启动目标程序。
实时监控目标程序的运行,并做出相应的应对。
我们要打造自己的Debugger程序,实际上也只需要完成这两个功能就可以了。当然,要完成这两个特定的功能,我们不可能从头开始造轮子,要首先看看操作系统给我们提供了什么样的基础设施:
由于我们是在Windows平台上工作,自然离不开微软公司提供的文档大全——MSDN。翻开MSDN,定位到“Debugging and Error Handling”,一些最基本的Windows Debug信息都在这里面。不过与其他栏目相比,这个栏目的信息明显显得单薄许多——也许越是底层、强大的技术,微软越不想公开吧。
初步浏览之后,我们可以确定,对于我们的Debugger而言,最重要的Debug API有如下几个:
CreateProcess —— 用于创建被调试进程
WaitForDebugEvent —— Debug Loop(调试循环)的主要构成函数
ContinueDebugEvent —— 用于构成Debug Loop
GetThreadContext —— 得到被调试进程的寄存器信息
SetThreadContext —— 设置被调试进程的寄存器信息
ReadProcessMemory —— 得到被调试进程的内存内容
WriteProcessMemory —— 设置被调试进程的内存内容
最重要的数据结构有如下几个:
CONTEXT —— 寄存器结构
STARTUPINFO —— Start信息
PROCESS_INFORMATION —— 进程相关信息
DEBUG_EVENT —— Debug Event(调试事件)结构
可以说,我们的Debugger程序就是利用这几个API函数结合下面的几个数据结构,完成我们指定的功能。那么下面就让我们先来看看这几个API和数据结构的具体含义:
Debug API解析
在这里,我们将对上面所述的几个Debug调试API做一个检阅式的考察,大概介绍一下每个API的应用领域。而将这些API应用到具体实践中去,将会在下一部分“实例解析”中,给出详细的说明。
1.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 // 进程信息
);
函数解析:该函数是Windows平台提供的最基本的创建进程的函数。每当我们双击一个EXE可执行文件,Windows内核就会自动调用该函数创建我们双击的文件所对应的进程。该函数中,最重要的参数有三个:一个是进程模块名,指明了要创建哪个进程;一个是进程创建选项,指明了要如何创建目标进程;对于Debugger程序而言,最常用的创建选项就是:DEBUG_PROCESS|DEBUG_ONLY_THIS_PROCESS。最后还有一个就是进程信息,我们调用CreateProcess创建了进程以后,Windows会将新创建的进程的相关信息全部放到ProcessInfo信息块中,我们在Debug Loop调试循环中使用进程信息块中的数据与目标进程交互,监视和控制目标进程的动作。
2.WaitForDebugEvent。
函数原型:BOOL WaitForDebugEvent(
LPDEBUG_EVENT lpDebugEvent, // Debug Event(调试事件指针)
DWORD dwMilliseconds // 超时设置
);
函数解析:该函数构成了Debug Loop调试循环的主体,一个Debugger程序在创建出目标进程后,一般都会紧接着循环调用该函数等待目标进程的各种调试信息,这个循环调用WaitForDebugEvent的过程,我们就称之为Debug Loop调试循环。调试循环是所有Debugger程序的主