当前位置: 首页 > 科技观察

网络安全编程:为调试API函数生成断点

时间:2023-03-18 01:25:05 科技观察

Windows中有些API函数是专门用来调试的,称为DebugAPI,或调试API。这些函数可用于开发调试器。调试器可以通过创建具有调试关系的父子进程来进行调试。可获取被调试进程的底层信息、实时寄存器、指令等信息,用于分析。OllyDbg调试器的功能非常强大。虽然它有很多功能,但它的基本实现依赖于调试API。调试API函数虽然数量不多,但合理使用会有很大的效果。调试器依赖于具有非常复杂结构的调试事件。调试器有一个固定的过程。因为它需要实时等待调试事件的发生,所以它的过程是一个调试循环体,很像SDK开发程序中的消息循环。无论是调试事件还是调试循环,对于调试或者调试器来说,最基础最核心的部分就是中断,或者说最核心的部分就是捕获中断的能力。产生中断的方法是设置断点。常见的产生中断的断点方式有中断断点、内存断点和硬件断点三种。下面介绍这三个断点之间的区别。中断断点,这里通常指的是汇编语言中的int3指令。当CPU执行这条指令时,会产生一个断点,因此常被称为INT3断点。现在演示如何使用int3产生断点,代码如下:intmain(intargc,char*argv[]){__asmint3return0;}代码中使用了asm,asm之后可以使用汇编指令。如果要添加汇编指令,方法是asm{}。内联汇编语言可以通过asm在C语言中实现。__asm之后直接使用int3指令,会产生一个异常,称为断点中断异常。编译并链接这个简单的代码,然后运行它。运行后出现错误对话框,如图1所示。图1异常对话框图1显示的异常对话框通过链接“点击这里”打开详细的异常报告。如果计算机与此处显示的对话框不同,请依次进行以下设置:右击“我的电脑”,在弹出的菜单中选择“属性”,打开“属性”对话框,选择在“高级”选项卡中,选择“错误报告”按钮,打开“错误报告”界面,在此界面上选择“启用错误报告”单选按钮,然后单击确定。通过这样的设置,您可以启动“异常对话框”。对于分析程序bug、挖掘软件漏洞,弹出异常对话框界面非常有用。这个对话框大家可能经常看到,看到它估计会很郁闷。通常,您只需单击“不发送”按钮并关闭对话框。这里,这个异常是int3引起的,别忙着关掉。通常,如果在编写自己的软件时出现此类错误,您应该寻求更多的帮助信息来更正错误。单击“单击此处”链接,出现如图2所示的对话框。图2“异常基本信息”对话框弹出“异常基本信息”对话框,由于该对话框给出的信息太少,继续点击“请点击此处查看有关错误报告的技术信息”链接打开如图3所示的对话框图3“ErrorReportContent”对话框通常这个报告只关注两个内容,一个是Code,一个是Address,图3中Code后面的值为0x80000003,Address后面的值为0x0000000000401028,Code的值为产生异常的异常代码,Address为异常发生的地址,Code的值定义在Winnt.h中,其中0x80000003定义为STATUS_BREAKPOINT,即断点中断,Winnt.h中的定义是:#defineSTATUS_BREAKPOINT((DWORD)0x80000003L)可以看出这里给出的Address是一个VA(虚拟地址),用OD打开这个程序,直接按F9运行它,如图4和图45。图4OD运行后断了图5OD状态栏提示从图4可以看出,程序执行停止在00401029的位置。从图5可以看出,INT3指令位于00401028。再看一遍在图3中Address后面的值为00401028。这也证明了系统的错误报告中可以给出正确的错误地址(或者产生异常的地址)。这样,在以后编写程序的过程中,很容易定位到程序中的错误位置。在OD中运行自己的int3程序时,可能OD不会停在地址00401029,也不会给出类似图4的提示。在实验本例时,需要设置OD,选择“Options”→“Debug”Settings”菜单,打开“DebugOptions”对话框,选择“Exception”选项卡,取消勾选“INT3Interrupt”复选框状态,即可仿照示例进行测试。回到中断断点的话题,中断断点是由int3产生的,那么如何通过调试器(调试进程)在被调试进程中设置中断断点呢?看图4中地址00401028,在地址值后面反汇编代码之前,中间一栏的内容是汇编指令对应的机器码。可以看出INT3对应的机器码是0xCC。如果想通过调试器在被调试的进程中设置一个INT3断点,只需要将需要中断的位置的机器码改为0xCC即可。当调试器捕捉到断点异常时,修改为原来的值即可。内存断点的方法也是通过异常产生的。在Win32平台下,内存是按页划分的,每页大小为4KB。每页内存都有自己的内存属性。常见的内存属性包括只读、读写、可执行和可共享。内存断点的原理是通过修改内存属性,使得本应允许的操作无法执行,从而引发异常。OD中有两种内存断点,一种是内存访问,一种是内存写。使用OD随机打开一个应用程序,在其“转储窗口”(或“数据窗口”)中选择一些数据点,单击鼠标右键,在弹出的菜单中选择“断点”命令。"子命令,你会看到两个断点,"内存访问"和"内存写入",如图6所示。图6内存断点的类型让我们用一个简单的例子来看看如何产生内存访问异常。代码如下:#include#defineMEMLEN0x100intmain(intargc,char*argv[]){PBYTEpByte=NULL;pByte=(PBYTE)malloc(MEMLEN);if(pByte==NULL){return-1;}DWORDdwProtect=0;VirtualProtect(pByte,MEMLEN,PAGE_READONLY,&dwProtect);BYTEbByte='\xCC';memcpy(pByte,(constchar*)&bByte,MEMLEN);免费(p字节);return0;}这个程序使用了VirtualProtect()函数,和VirtualProtectEx()函数类似,但是VirtualProtect()用于修改当前进程的内存属性。编译并链接这个程序,然后运行它。熟悉的错误界面再次出现,如图7所示。图7“基本异常信息”对话框按照上述步骤打开“错误报告内容”对话框,如图8所示。图8“错误报告内容”》对话框根据上面的分析方法,我们来看一下Code和Address这两个值。Code后面的值为0xc0000005,在Winnt.h中定义如下:#defineSTATUS_ACCESS_VIOLATION((DWORD)0xC0000005L)表示访问冲突。Address后面的值为0x0000000000403093。这个值是一个地址,但是这里的地址是根据程序来考虑的。该值与malloc()函数一起应用,用于保存数据的堆地址,而不是用于保存代码的地址。这个地址就不测试了,因为是动态申请,很可能每次都不一样,大家可以理解。硬件断点是由硬件支持的,硬件是硬件提供的一组调试寄存器。通过这些硬件寄存器设置相应的值,然后让硬件在需要设置断点的地址断点。CPU上有一组特殊的寄存器,称为调试寄存器。有8个调试寄存器,分别为DR0-DR7,用于设置和管理硬件断点。调试寄存器DR0-DR3用于存放设置的硬件断点的内存地址。由于只有4个调试寄存器可以用来存放地址,所以最多只能设置4个硬件断点。寄存器DR4和DR5由系统保留,它们的用途未公开。调试寄存器DR6称为调试状态寄存器,记录了上次断点触发产生的调试事件类型信息。调试寄存器DR7用于设置硬件断点的触发条件,如硬件读断点、硬件访问断点或硬件执行断点。