是自己利用这些API函数,写一个简单的调试器,在目标程序断点发生时自动输出监视结果并且自动恢复目标程序的运行。
显然,用VC调试器作为监视器的话无需知道目标函数的原型就可以得到简单的输入输出参数和函数运行结果,而且,由于监视代码没有注入目标程序中,就不会出现监视目
标函数和监视代码的冲突。VC调试器显然可以跟踪递归函数,也可以跟踪DLL模块调用DLL本身的函数,以及EXE内部调用自身的函数。只要你知道目标函数的入口地址,就可以跟踪了(监视Exe自身的函数可以通过生成Exe模块时选择输出Map文件,就可以参考Map文件得到Exe内部函数的地址)。没有听说VC不能调试多线程的,最多是说调试多线程比较麻烦----证明多线程是可以调试的。显然,VC也可以调试DllMain中的代码。这些,已经可以证明通过调试函数可以实现我们的目标了。
如何编写实现我们目标的程序?需要哪些调试函数?
首先,让目标程序进入被调试状态:
对于一个已经启动的进程而言,利用DebugActiveProcess函数就可以捕获目标进程,将目标进程进入被调试状态。
BOOL DebugActiveProcess(DWORD dwProcessId);
参数dwProcessId是目标进程的进程ID。如何通过ToolHelp系列函数或Psapi库函数获得一个运行程序的进程ID在很多文章中介绍过,这里就不再重复。对于服务器程序而言,由于没有权限无法捕获目标进程,可以通过提升监视程序的权限得到调试权限进行捕获目标进程(用户必须拥有调试权限)。
对于启动一个新的程序而言,通过CreateProcess函数,设置必要的参数就可以将目标程序进入被调试状态。
BOOL CreateProcess(LPCTSTR lpApplicationName, LPTSTR lpCommandLine,
LPSECURITY_ATTRIBUTES lpProcessAttributes, LPSECURITY_ATTRIBUTES
lpThreadAttributes, BOOL bInheritHandles, DWORD dwCreationFlags, LPVOID
lpEnvironment, LPCTSTR lpCurrentDirectory, LPSTARTUPINFO lpStartupInfo,
LPPROCESS_INFORMATION lpProcessInformation );
该函数的具体说明请参考MSDN,在这里我仅介绍我们感兴趣的参数。这里和一般的用法不同,作为被调试程序dwCreationFlags必须设置为DEBUG_PROCESS或DEBUG_ONLY_THIS_PROCESS。这样启动的目标程序就会进入被调试状态。这里说明一下DEBUG_PROCESS和DEBUG_ONLY_THIS_PROCESS。DEBUG_ONLY_THIS_PROCESS就是只调试目标进程,而DEBUG_PROCESS参数则不仅调试目标进程,而且调试由目标进程启动的所有子进程。比如:在A.exe中启动B.exe,如果用DEBUG_ONLY_THIS_PROCESS启动,监视进程只调试A.exe不会调试B.exe,如果是DEBUG_PROCESS就会调试A.exe和B.exe。为简单起见,本文只讨论启动参数为DEBUG_ONLY_THIS_PROCESS的情况。
使用方法:
STARTUPINFO st = {0};
PROCESS_INFORMATION pro = {0};
st.cb = sizeof(st);
CreateProcess(NULL, pszCmd, NULL, NULL, FALSE,
DEBUG_ONLY_THIS_PROCESS,
NULL, szPath, &;st, &;pro));
// 关闭句柄---这些句柄在调试程序中不再使用,所以可以关闭
CloseHandle(pro.hThread);
CloseHandle(pro.hProcess);
其次,对进入被调试状态的
程序进行监视:
目标进程进入了被调试状态,调试程序(这里调试程序就是我们的监视程序,以后不再说明)就负责对被调试的程序进行调试操作的调度。调试程序通过WaitForDebugEvent函数获得来自被调试程序的调试消息,调试程序根据得到的调试消息进行处理,被调试进程将暂停操作,直到调试程序通过ContinueDebugEvent函数通知被调试程序继续运行。
BOOL WaitForDebugEvent(
LPDEBUG_EVENT lpDebugEvent, // debug event information
DWORD dwMilliseconds // time-out value
);
在参数lpDebugEvent中可以获得调试