危险漫步博客
新鲜的“黑客思维”就是从全新的角度看待黑客技术,从更高的层面去思考;专注于黑客精神及技术交流分享的独立博客。
文章2306 浏览20580960

深入浅出谈OD

都说“工欲善其事,必先利其器”,相信那些经常制作免杀、破解的朋友,对于用户态下的动态调试器Ollydbg(以下简称为OD)都不陌生,但如果讨论到其各个功能的实现机制可能就不怎么清楚了,本文我将结合实例来讨论一下0D中某些功能的具体实现,希望能对大家有所帮助!

一、OD的基本知识

OD运行后,正如大家所看到的,OD的界面分为了反汇编窗口、注释窗口、内存窗口、栈窗口和寄存器窗口。反汇编窗口是对程序进行的汇编指令集的分析,我们知道,每个Windows程序都是一个PE文件,此处的指令开始为IMAGE_OPTIONAL_HEADER中指明的EntryPoint(程序入口点处的汇编指令),寄存器窗口包括了8个通用寄存器-EAX、EBX、ECX、EDX、ESI、EDI、EBP、ESP、EFALG状态寄存器、EIP指令寄存器、St(0)——St(7)浮点数组寄存器组,其中通用寄存器ESP和EBP标识栈区的栈底与栈顶,而EIP寄存器始终指向下一条要执行的指令。注释窗口会显示每条指令操作数的地址等信息,在调试Debug版的程序时,此处还可以注释出与之对应的c++源代码信息,内存窗口对应于当前进程的内存信息,可以通过dd xxx,查看xxx地址处的内存数据,xxx为0x00000000-0xFFFFFFFF范围内的内存地址。

对于OD来说,让人往往难以把握的是F9、ALT+F9、Ctrl+F9的不同,F9是运行程序,ALT+F9是让程序执行到返回,这通常是对于一个函数(Call指令对应的语句)来说的,Ctrl+F9是从系统领空执行到程序领空,所谓领空指的是系统或程序当前的执行指令,比如OD标题栏显示的是“模块API”,那么就表示处于程序的领空中,大家再来看标题栏显示的是“模块NTDLL”,我们知道NTDLL为system32文件夹下的一个DLL文件,那么此时就为系统领空。

二、如何开始调试

当我们要免杀或破解程序时,会先通过OD“文件”中的“打开”或“附加”命令来调试或反汇编程序,这个过程分别调用了两个系统API函数——CreateProcess()和DebugActiveProcess(),下面的程序就演示了上面的过程:

即OD中的“打开”命令通过向CreateProcess()函数传递DEBUG_PROCESS参数来实现,而“附加”命令则是通过向DehugActiveProcess()函数传递ProcessID以将正在运行的程序附加到OD中。

三、bp xxxx命令的机制

在破解时,我们经常会使用bp命令下断点来进行脱壳,这个过程实际上下的是int3断点,对应的机器码为0xcc,它与int 3(对应的机器码为0x3d0c)断点完全是两回事,int3断点只占一个字节,这有利于断点机制的实现,而int 3为两个字节,在一些源码编程中使用的。int3断点在CPU执行时会引发一个硬件中断,该中断在中断处理器转换为软异常,即0x80000003,如果进程不在调试器中,Windows就会抛出该异常,如果进程在OD中,Windows就将这些异常发送给调试器,调试器接收到消息后会将进程中所有的线程挂起,等待用户交互。下面的程序演示了上面的过程:

这个程序通过TF标志来判断程序是否在调试器中,如果不在调试器中,就引发一个单步跟踪异常,从而b_indebug为FALSE,否则抛出异常,告诉我们详细的异常信息,在程序中就是输出相关的异常信息。如果是在调试器中,那么就将bjndebug设置为TRUE,调试嚣接收到信息就会自动挂起线程,等待用户交互。

bp命令既然为int 3断点,那么OD是如何利用它进行工作的呢?当我们bp XXX时,OD会将此处的指令替换为0x3c,此时程序中断执行,等待用户判断,当我们按F2取消此断点时,调试器就会将此处的断点替换为原来的指令。

四、hr、hw、he命令的机制

在使用ESP定律脱刻时,我们习惯使用“hr esp”,在esp寄存器所保存的地址处设置一个硬件访问断点,它实际上是一个内存读取断点,即程序读取此地址的数据时,产生中断异常,同样hw是在断点处写入数据时产生中断异常,而he是执行断点处的指令时产生中断异常,它们对应于OD中的“断点”和“内存断点”。

X86指令集的CPU包括8个调试寄存器,DRO-DR3为断点地址寄存器,顾名思义就是保存断点地址的寄存器,正因为有四个断点地址寄存器,所以OD中的硬件断点只有4个位置,我们在OD中打开“调试”一“硬件断点”,只有4个断点位置;DR4和DR5保留,DR7保存相关断点的控制信息,称为断点控制寄存器,DR6保存引起断点的原因。下面的程序则演示了hr、hw、he命令的中断过程:

这个程序会先挂起主线程,然后设置主线程CONTEXT上下文的调试寄存器的内容,然后再让主线程执行,在OD中,跟踪到WaitForSignleObject()函数后面的位置时,会引发一个硬件访问异常。

五、F8单步步过与F7单步步入

在脱壳中有一条经验—一近“call F7”远“call F8”,它的意思就是说当碰到call xxx的指令时,如果xxx的地址与当前指令EIP值比较近,则使用F7步入,比较远则使用F8步过。F8实际上是一个单步执行过程,那么OD是如何实现单步执行的呢?就本质来说,这是由调试循环与调试异常处理函数结合硬件CPU(TF标志)所决定的。

首先,我们要分清这个过程中参于的几个成员——用户、OD调试器、操作系统内核,调试与反汇编的过程实际上是调试器在用户与操作系统之间提供了一层媒介,即操作系统内核将调试信息比如动态链接库的加载(在OD中中可以通过ALT+M查看到)返回给用户态下的OD调试器,调试器通过调试处理函数作出相应的设置,用户通过调试器的信息进行分析,同时用户可以通过调试器设置断点、跟踪程序等,调试器将用户的操作保存,然后让程序执行,执行发生异常,则操作系统内核再次将信息发送给调试器,重复上面的过程。

上面的调试循环过程通过WaitForDebugEvent()来实现,当调试器得到异常信息后通过调试异常处理函数返回不同的调试参数,这个参数将用于调用ContinueDebugEvent(),以便继续执行程序。

每一次的F8都将重复上面的过程,下面的程序就演示了上面的整个过程:

程序首先创建调试目标的进程,使用DEBUG_PROCESS标识要调试的程序,然后循环通过WaitForDebugEvent()等待系统返回调试信息,然后将信息传递给调试异常处理函数,通过调试异常处理函数再分析其为何种异常,用户分析完成后,调试器继续执行。

到这里,相信大家对于OD主要的功能机制都有所了解了,在此基础上再来使用它相信就一定能做到得心应手了!

相关推荐