调试目标程序时,你得有相应的权限。用下面这个函数得到它:
//--------------------------------------------------------------------------
// EnableDebugAccessCtl
// Description: Enable or disable debug access control
// Return value: 0 means succeed
DWORD EnableDebugAccessCtl(BOOL bEnable)
{
HANDLE hTokenHandle = NULL;
if( !OpenProcessToken(GetCurrentProcess(),
TOKEN_ADJUST_PRIVILEGES,
&hTokenHandle ) )
{
return GetLastError();
}
// Lookup the privilege value
TOKEN_PRIVILEGES tp;
tp.PrivilegeCount = 1;
if( !LookupPrivilegeValue( NULL, SE_DEBUG_NAME, &tp.Privileges[0].Luid ) )
{
CloseHandle(hTokenHandle);
return -1;
}
// Enable/disable the privilege
tp.Privileges[0].Attributes = bEnable ? SE_PRIVILEGE_ENABLED : 0;
if( !AdjustTokenPrivileges( hTokenHandle, FALSE, &tp, sizeof(tp), NULL, NULL ) )
{
CloseHandle(hTokenHandle);
return -1;
}
CloseHandle(hTokenHandle);
return 0;
}
我们可以attach一个正在运行的进行或重新启动一个进程进行调试。DebugActiveProcess可以实现第一种情况;第二种情况可以用CreateProcess,下面是代码,注意创建标志。
// Create Process
STARTUPINFO si;
PROCESS_INFORMATION pi;
ZeroMemory( &si, sizeof(si) );
si.cb = sizeof(si);
ZeroMemory( &pi, sizeof(pi) );
if( !CreateProcess(NULL, csCmdParms.GetBuffer(),
NULL, NULL, FALSE,
CREATE_NEW_CONSOLE | DEBUG_ONLY_THIS_PROCESS,
NULL, NULL,
&si, &pi ) )
{
return GetLastError();
}
这下初调试的程序就跑起来了,接下来一般来用一个循环来获取目标进行的Debug事件。主循环看起来应该是这样的,嘿,MSDN给了你一个模板,那就用吧:
DEBUG_EVENT DebugEv; // debugging event information
DWORD dwContinueStatus = DBG_CONTINUE; // exception continuation
for(;;) { // Wait for a debugging event to occur. The second parameter indicates // that the function does not return until a debugging event occurs. WaitForDebugEvent(&DebugEv, INFINITE); // Process the debugging event code. switch (DebugEv.dwDebugEventCode) { case EXCEPTION_DEBUG_EVENT: // Process the exception code. When handling // exceptions, remember to set the continuation // status parameter (dwContinueStatus). This value // is used by the ContinueDebugEvent function. switch (DebugEv.u.Exception.ExceptionRecord.ExceptionCode) { case EXCEPTION_ACCESS_VIOLATION: // First chance: Pass this on to the system. // Last chance: Display an appropriate error. break; case EXCEPTION_BREAKPOINT: // First chance: Display the current // instruction and register values. break; case EXCEPTION_DATATYPE_MISALIGNMENT: // First chance: Pass this on to the system. // Last chance: Display an appropriate error. break; case EXCEPTION_SINGLE_STEP: // First chance: Update the display of the // current instruction and register values. break; case DBG_CONTROL_C: // First chance: Pass this on to the system. // Last chance: Display an appropriate error. break; default; // Handle other exceptions. break; } case CREATE_THREAD_DEBUG_EVENT: // As needed, examine or change the thread's registers // with the GetThreadContext and SetThreadContext functions; // and suspend and resume thread execution with the // SuspendThread and ResumeThread functions. break; case CREATE_PROCESS_DEBUG_EVENT: // As needed, examine or change the registers of the // process's initial thread with the GetThreadContext and // SetThreadContext functions; read from and write to the // process's virtual memory with the ReadProcessMemory and // WriteProcessMemory functions; and suspend and resume // thread execution with the SuspendThread and ResumeThread // functions. Be sure to close the handle to the process image // file with CloseHandle. break; case EXIT_THREAD_DEBUG_EVENT: // Display the thread's exit code. break; case EXIT_PROCESS_DEBUG_EVENT: // Display the process's exit code. break; case LOAD_DLL_DEBUG_EVENT: // Read the debugging information included in the newly // loaded DLL. Be sure to close the handle to the loaded DLL // with CloseHandle. break; case UNLOAD_DLL_DEBUG_EVENT: // Display a message that the DLL has been unloaded. break; case OUTPUT_DEBUG_STRING_EVENT: // Display the output debugging string. break; } // Resume executing the thread that reported the debugging event. ContinueDebugEvent(DebugEv.dwProcessId, DebugEv.dwThreadId, dwContinueStatus); }可以从CASE语句中看到可以获取什么样的事件。注意CREATE_PROCESS_DEBUG_EVENT事件,我们设置断点是在这里,也就是进行被创建的时候。
说到这里就要讲讲断点是怎么设置的了。通常我们设置的断点称为软件断点。通俗点说这个断点其实并不是在原代码中写的,它的基本工作是:调试器跟据用户的设置对代码进行了修改,也即是在指地址处更换一个字节的值为CC,当程序运行到用户指定地址时,CC被处理器装入执行,因为CC对应用int 3指令,这是处理器的一个断点异常,于是处理暂停当前程序,并发出相应异常,通过异常处理程序和debug驱动程序,调试器Host端得到这个事件,此时用户就可以对被debugging的程序进行一些访问了,比如寄存器,局部变量等各种信息,可以对目标程序的指定代码块进行反汇编或进行源码级的查看。
注意:这里指出的是当程序被断点所中断时,调试器获得此断点事件后第一件是要将有被改写为CC的内存单元重新改会为原来程序的代码,以保证程序的完整性。另一点提示,被捕获的第一个断点异常应该被丢掉,呵呵,那不关你的事,它是规定的一个步骤,当然,你是可以用它做点什么事的。
当然继续调试的时候,目标程序会根据EIP来执行下一条指令,而这时EIP实际指向的是CC后的位置,因此必须令EIP指针值减1,这样程序就对用户实现了透明,用户是不知道调试器作了什么手脚的,当然别忘,程序继续跑时,其实的断点还要改成CC的。
写目标进程可以用WriteProcessMemory进行,写完后别忘了用FlushInstructionCache指令flush一下目标进行的指令的cache。如果页为只读时,可以使用VirtualProtectEx改其为读写属性然后再写。
修改目标线程EIP的值可以用SetThreadContext,当然EIP的值请先用GetThreadContext来获得。啊,这样就可以了。好热,写了40多分钟......要吃冰棒去了。
本文介绍目标程序调试相关内容。调试需相应权限,可通过特定函数获取。可attach或重启进程调试,用循环获取Debug事件。还讲解了软件断点设置,包括工作原理、中断处理及EIP指针调整,也提及写目标进程和修改线程EIP值的方法。
2446

被折叠的 条评论
为什么被折叠?



