用调试函数跟踪API
用调试函数跟踪API
作者:彭春华
我们知道,当一个目标程序运行时,或多或少的都会调用一下API函数。当我们在调试该目标程序进行除错时,非常希望知道某些API的输入输出参数以及运行结果,对API的调用路径及参数的跟踪监视,在分析研究目标程序的内部调用机制时是非常有帮助的。在这里所指的API,不仅包括狭义上的Windows系统函数,还包括广义上第三方(及自身)提供DLL的输出函数。如果从跟踪监视的需求来讲,跟踪监视的API就不仅仅包括广义的API,更希望包括EXE和DLL中的内部未导出的函数。
对某一目标程序进行API函数的跟踪监视分析时,一般来讲是没有源代码和调试版本,更多的情况是只有EXE和DLL的发行版。跟踪的目标就是通过运行目标程序,得到调用API函数的参数和运行结果,而不希望改变目标程序的运行路径。
跟踪API函数 跟踪监视方案概览
当我们对某一目标程序进行API函数的跟踪监视分析时,根据跟踪监视的目标,基本上有以下几种途径实现对API函数的跟踪监视:
写Log记录分析
如果拥有目标程序的源代码,就可以在关键的API函数的入口点和出口点记录API的参数和运行结果。在除错程序中是经常可以看到这种方法的。该方法的缺点就是必须拥有
源代码,每次修改Log时必须重新编译源代码。由于该方案和我们要讨论的目标不同,在此不作讨论。
将监视代码注入目标程序
该方案的原理是在目标程序运行时,将监视代码注入到目标程序的进程空间。监视代码通过前期的准备
工作,修改目标程序的运行代码,使得目标程序调用指定的API函数时,运行指令会跳转到监视代码中,这样,监视代码就可以记录下API函数的运行参数,然后,监视代码在运行用来的API函数代码。在用来的API函数代码运行完后,再回到监视代码中,将来下运行结果,再返回目标程序。其运行原理图为:
在32位Windows平台上,各进程空间是独立的。要在目标程序中运行监视代码就必须将监视代码注入到目标进程。一种
常用的方法就是将监视代码编译成一个DLL,再将该DLL注入到目标进程中。关于将DLL注入目标进程的文章有很多,比如在《Windows核心编程》上就详细介绍过CreateRemoteThread和SetWindowsHookEx的方法,关于DLL的注入不是本文的重点,在这里就不进行介绍,请读者参考其它相关的文章。
利用在目标程序中注入监视代码实现监视的
方案,常见以下几种方式:
1.在目标函数写入跳转指令(jmp),跳转至监视代码实现监视
2.和第1种方式相同,在目标函数入口写入跳转指令,但在监视代码中实现监
视的机制不同。
3.利用API Hook功能,修改EXE和DLL的导入表(Import Address Table),将监视代码中函数入口地址写入导入表中,当EXE或DLL调用其它DLL中API函数时,就可以跳转到监视代码中实现跟踪监视。
这里介绍的1~3种方案在很多资料上都介绍过,这里不再重复,他们都有一个最大的缺点:监视某个函数时必须知道函数的原型(即参数个数和调用方式——WINAPI调用还是其它?)。在写监视代码时必须确保监视代码函数的原型和被监视函数的原型一致。希望增加一个监视函数时必须增加一个监视代码。详细可以参考Detours和API Hook。
用代理DLL实现API函数的监视
用代理DLL实现API函数的监视就是将原来的DLL改名,用一个新的DLL代替原来的DLL。这个新的DLL的导出函数和用来DLL的导出函数相同,并且导出函数的顺序和原来的DLL一样。新的DLL名字和原来DLL的名字也一样。在新DLL中的每个函数实现代码中,就负责记录运行参数和运行结果,同时调用原来DLL的函数。其运行方式如图所示:
比如:A.exe调用B.DLL,希望监视B.DLL的导出函数时,就将B.DLL改名为C.DLL,生成一个新的监视