很明显,当被调试进程在函数入口产生中断调试信息时,调试程序只能得到函数的输入参数,而不能得到我们希望的输出参数及返回值!为了实现我们的目标,我们必须在函数调用结束时,再次产生中断,取得函数的输出参数和返回值。在处理函数入口中断时,就必须设置好函数的返回地址的断点。这样,在函数返回时,就可以得到函数的输出参数和返回值了。关于这里的实现说明请参考附录的源代码。
你完全可以参照附录的源代码写出你自己的简单的调试监视程序。当然,有几个问题因为比较复杂,我没有在这里进行说明。一个就是函数返回断点的处理,比如TRY、CATCH的处理,就必须重新设计好RETURN_FUN_STACK的结构,考虑一些除错处理还是可以解决这个问题的。另外一个问题就是函数的入口断点和返回断点没有任何关系。这个问题更好解决,只需重新设计RETURN_FUN,FUN_BREAK_MAP等结构体就可以将它们关联起来。由于我在这里只要是分析如何实现中断调试处理的过程,这些完善程序的工作就由读者自行跟踪改造了。
关于Win9X系统
细心的读者在上面可以发现一个问题,那就是在SetBreakPoint函数中有一个限制,就是函数的入口地址不能大于0x80000000。确实如此,我们知道0x80000000以上的空间是系统共有的空间,我们一般不能修改这些空间的程序,否则将影响系统的工作。在NT环境下,所有的DLL都被加载在0x80000000下,修改0x80000000以下空间的代码不会对其它进程产生影响。所以在NT下可以用上面的方案监视所有的DLL函数。然而,在Win9X下,kernel32.dll,user32.dll,gdi32.dll等系统DLL都被加载到0x80000000以上的空间,修改这些空间的代码将破坏系统工作。那么,在9X下就不能监视这些DLL模块的函数吗?
的确,在Win9X平台下不能利用在函数入口处设置断点的方法实现监视。我们必须采用另外的方法实现该功能。在前面讨论中知道,通过API HOOK修改模块导入表的方法可以实现将API的入口修改为自己监视程序的入口,也可以实现监视功能。如果采用API HOOK的方法有限制,即必须知道函数原型,对每一个函数都必须编写相应的监视代码,灵活性受到限制。而我们的目标是不管有多少个DLL,不管DLL有多少个导出函数,在不修改我们的程序前提下都可以实现我们的监视功能。所以,API HOOK是不可以完成我们的目标,但我们可以利用修改导入表的方案实现目标。首先,修改导入表,将函数的调用地址指向我们的监视代码,在监视代码中,我们无需对函数编程,只是简单调用jmp XXXX就可以了。然后,设置断点时,不是设置在函数的入口点,而是设置在我们的监视代码上。这样,当我们的模块调用系统API函数时,就可以实现监视功能了。修改原理如图:

如图所示,假设我们的监视代码在目标进程的的0x20000000空间,我们在分析DLL导出表的同时,将导出表函数的地址经过计算,在监视代码中设置为jmp xxxx的代码。这样我们在修改EXE模块的导入表时写入的地址为监视代码的地址。当目标程序调用MessageBox函数是,程序将首先跳转到监视代码中执行jmp指令到user32.dll的MessageBox入口地址中。经过这样处理后,我们希望监视MessageBox函数的调用时,只需在监视代码的0x20000000处设置断点,就达到了监视的目的。限于篇幅原因,这里不再讨论。
扩展应用
你可以很轻松的在此基础上进行扩展你的监视跟踪功能。只需要修改一下记录输入输出函数结果的程序,就得到一个新的功能:
1.在记录输入输出参数的地方加入取得当前时刻的功能,就实现了监视函数调用性能的功能。(相当于Numega的TrueTime功能)由于采用了Debug技术,得到的时间将包括调试函数导致产生进程的切换时间。等到的时间只是一个参考价值,但对分析性能而言一般足够。
2.在记录输入输出参数的地方加入函数调用的计数器,就实现了Numega的TrueCoverage功能。
3.监视malloc, free, realloc函数的输入输出值,并进行统计,就实现了简单的内存泄漏检查功能。关键的是你可以通过Map文件得到Release版本的malloc等函数的地址,实现对Release版的跟踪。
4.在记录输入参数处理中加入StackWalk函数可以实现call stack功能,分析是由哪个函数调用了自己。在jmp方案中也可以实现这个功能,但是你必须确保StackWalk关联的函数没有调用被你监视的函数。在Hook API(IAT)的方案中到是不用保证,但得出的调用列表中有可能包含你的监视代码。
有一点需要注意的是,我们的目标是监视程序的运行路径,并不是改变参数和修改结果,所以,在jmp和Hook Api(IAT)中可以实现的修改参数和运行路径的做法在这里不能实现。
其他:
本文附录的代码TestDebug.zip就是实现了一个简单的调试监视器,自动输出监视函数的4个输入参数的地址内容和函数调用返回值。该代码只是表明通过监视函数可以实现对API的跟踪,所以没有实现9X下对系统DLL的监视。
DebugApi.zip是一个利用这个方案编写的应用程序DebugApiSpy.exe,它实现了这个方案中的最基本的跟踪监视函数的输入输出参数功能,也实现了9X下对系统DLL的监视支持。该程序支持Win9X/NT/W2K/XP上的运用。
参考资料:
1.《Windows核心编程》, Jeffrey Richter,机械工业出版社
2.微软的MSDN
3.detours 可以在http://research.microsoft.com/sn/detours/ 上得到源代码。detours功能在WinNT和W2K下有效,对9X不支持。
原文链接:http://www.bccn.net/Article/kfyy/vc/jszl/200709/6208.html
相关推荐
用Debug函数实现API函数的跟踪 大家知道,VC可以用来调试程序,除了调试Debug程序,当然也可以调试Release程 序(调试Release程序时为汇编代码)。如果知道函数的入口地址,只需在函数入口上设置断点,当程序调用了...
### 使用Debug函数实现API函数跟踪 #### 概述 在软件开发过程中,有时我们需要对特定的应用程序接口(API)函数进行跟踪,以便更好地理解和调试程序的行为。本文将介绍一种利用Debug函数来实现API函数跟踪的方法。...
Debug API主要包括DebugActiveProcess、DebugBreak、ContinueDebugEvent、WaitForDebugEvent等函数,它们使得开发者可以在目标进程中设置断点、检查内存、跟踪执行流程等。在进行网络截取时,我们需要对与网络通信...
《精通Windows API函数接口编程实例源代码》是一个深入学习Windows API编程的重要资源,它涵盖了大量实践案例,旨在帮助开发者熟练掌握API接口的使用技巧。Windows API是微软为开发者提供的一个平台,通过它,程序员...
使用 API 函数 `SetWindowPos` 可以实现让窗体总是在最前面的效果。首先,我们需要在模块中声明 `SetWindowPos` 函数: `Declare Function SetWindowPos Lib "user32" (ByVal hwnd As Long, ByVal hwndInsertAfter ...
《Windows API函数大全》中文版是一本针对Windows操作系统编程的核心参考资源,对于深入学习和理解高级编程至关重要。Windows API(应用程序接口)是微软提供的一系列函数、常量、数据类型和结构,允许开发者直接与...
API函数是软件开发中的一个重要概念,它是一组预先定义好的函数,允许开发者调用这些函数来实现特定功能,而无需理解底层实现细节。在汇川机器人的上下文中,API函数使得程序员能够更方便地编写控制逻辑,控制...
- `debugfs_create_x8`、`debugfs_create_x16`、`debugfs_create_x32`、`debugfs_create_x64`:与上述函数类似,但使用`x`前缀表示数据以十六进制格式显示。 - `debugfs_create_size_t`:用于创建表示`size_t`类型...
本文档提供了关于Oracle数据库API接口函数的详细设计说明,包括如何设置项目环境以及如何使用提供的接口函数进行数据库操作。通过学习本文档,开发者能够有效地利用这些API接口函数完成与Oracle数据库的交互任务。...
同时,还需要熟悉C或C++语言,因为大多数API函数都是用这两种语言的语法进行调用的。对于初学者来说,理解参数的意义、函数的返回值以及错误处理机制是关键。 在实践中,开发者往往需要借助诸如调试器(如DebugView...
除了使用`EnableDebugPrivilege` API外,还可以借助其他Windows API函数来尝试结束进程,例如通过`CreateToolhelp32Snapshot`和`Process32First/Next`遍历进程,然后使用`OpenProcess`和`TerminateProcess`。...
在嵌入式系统开发中,uC/...总之,理解和熟练掌握uCOS的API函数以及LWIP网络库的使用,是开发高效、可靠的嵌入式系统的关键。通过深入学习和实践,开发者可以充分利用这些工具实现复杂的功能,满足各种应用场景的需求。
在"基于STM32_C8T6的Debug函数项目"中,我们关注的是如何在STM32开发过程中实现有效的调试功能。Debug功能对于任何软件开发都至关重要,它帮助开发者追踪代码执行、定位错误、理解程序运行状态,从而优化代码性能。...
这个单元可能包含了`DebugPrint`函数的声明和实现,以及如何在项目中使用它的示例。通过查看此文件,我们可以学习如何自定义调试工具,以提高我们的开发效率和调试体验。 总的来说,`DebugPrint`是为了解决`...
在分析VistaLKD的驱动程序时,我们发现了一个名为GetProcessNameOffset()的函数,它通过IoGetCurrentProcess和_strnicmp等API来识别当前进程,这在内核编程中常见于识别关键系统进程的场景。GetProcessNameOffset()...
WebGL是一种基于OpenGL标准的JavaScript API,用于在任何兼容的Web浏览器中实现硬件加速的2D和3D图形渲染。这个“WebGL编程指南函数库”包含了一系列辅助脚本,帮助开发者更方便地进行WebGL编程。以下是这些脚本的...
### 在VB中使用API函数(之四):深入探讨回调函数与窗口过程 #### 标题解析 本篇文章主要讨论了如何在Visual Basic (VB) 编程环境中使用API函数,特别是聚焦于第四部分的内容——回调函数的应用以及窗口过程的...
WebGL是一种基于OpenGL标准的JavaScript API,用于在任何兼容的Web浏览器中实现2D和3D图形渲染。在WebGL编程中,为了提高代码的可重用性和简化复杂任务,通常会使用各种公用函数库。这里提到的"cuon-matrix.js"、...
使用钩子函数时,开发者需要通过`SetWindowsHookEx`函数注册钩子,并指定钩子类型、处理函数、所属线程以及模块句柄。当钩子被触发时,系统会调用提供的处理函数。需要注意的是,为了确保钩子能够正确工作,必须确保...
Debugviewer是一款专为C语言API开发设计的DLL(动态链接库)查看工具,它提供了对公有函数的详细查看功能,对于深入理解底层开发和Windows API编程的开发者来说,是不可或缺的辅助工具。通过Debugviewer,程序员能够...