《加密与解密》_2_动态调试技术_OllyDbg
⍢⃝ ⍤⃝ ⍥⃝ ⍨⃝ ⌓̈⃝ ◡̈⃝ ⠒̫⃝ ʚ◡̈⃝ɞ ༛̥⃝ʺ̤
动态分析技术
动态调试器:
- 用户模式:工作在Ring3。
- OllyDbg、x64dbg、MDebug、Visual C++等。
- 内核模式:工作在Ring0,操作系统内核调试器。
- WinDbg
1 调试窗口
OllyDbg的5个窗口:
详细可查看《OD窗口介绍》
反汇编窗口:
- Address 列:显示被双击行地址的相对地址,再次双击返回标准地址模式(栈区也可双击地址)。虚拟地址,在一般情况下,同一程序的同一条指令在不同系统环境下此值相同。
- Hex dump 列:设置或取消无条件断点,快捷键:F2键。
- Disassembly 列:调用汇编器,可直接修改汇编代码,快捷键:空格键。
- Comment 列:允许增加或编辑注释,快捷键:;键。
信息窗口:在进行动态跟踪时,信息面板窗口(Information window)将显示以下等信息:
- 寄存器的值;
- API 函数调用提示;
- 跳转提示。
数据窗口:以十六进制和字符方式显示文件在内存中的数据。可使用数据窗口跟随或在数据窗口按“
Ctl+G”
快捷键,打开地址窗口,输入地址。寄存器窗口:显示 CPU 各寄存器的值,支持浮点、MMX 和 3DNow!寄存器。可以单击右键或窗口标题切换显示寄存器的方式。修改寄存器的值可直接双击(EIP除外)。EIP寄存器,不能直接修改,需要在反汇编窗口选择新的指令起始地址,在弹出的快捷菜单中选择“New origin here”(此处为新的EIP)。
栈面板窗口:显示栈的内容。栈窗口非常重要,各 API 函数和子程序都利用它传递参数和变量等。
- OD所在目录下的UDD 文件是 Ollydbg 的工程文件,用于保存当前调试的一些状态,例如断点、注释等,以
便下次调试时继续使用。
- 插件用于扩充功能。路径设置正确后,将插件复制到
plugin
目录里,相应的选项就会在Ollydbg 的主菜单Plugin
(插件)里显示出来。
2 基本操作
大部分程序在启动时都会停在入口点(EntryPoint)。通过一些特殊的修改方式,有些程序可以在启动时不停在入口点,以达到反调试的目的。
快捷键:
Call的跟进与回退查看:
- 按
F7
进入子程序的过程后,若想回看之前单步跟踪的代码,可以按-(减号)键。(除了反汇编窗口中能会退看之前的代码外,其余窗口数据不会变化); - 若想让光标回到当前EIP所指向的语句,可以单击快捷窗口C按钮或双击EIP寄存器。
通过屏蔽程序的某些功能或改变程序流程使程序保护方式失效的方法称为“爆破”。
代码分析:
- 如
mov bl,[eax+405030]
:若eax每次自增inc
或自减dec
,则0x405030
有很大可能是一个数组集合,构成两个循环自变量,0x405030
为大循环自变量。 - 函数
wsprintf()
是唯一需要需要手动平衡栈的API函数。
3 常用断点
常用的断点有:
- INT3 断点
- 硬件断点
- 内存断点
- 消息断点 ...
3.1 INT 3 断点
在Ollydbg中可以使用如下快捷键来设置/取消断点:
- bp
- F2
原理:当执行一个 INT3 断点时,该地址处的机器码被调试器用 INT3 指令替换了,此时 Ollydbg 将 INT3 隐藏,显示出来的仍是中断前的指令。如在004013A5h
处设置int3 断点,则该地址处的机器码被替换成CC
,当CC
执行完之后,再去执行该地址处的指令。
这个 INT 3 指令,因其机器码是0xCC
,也常被称为CC 指令,故一般缓冲区经常填充为C。
使用上的一些技巧:
一些软件会对自身较为敏感的API函数进行检测,为了防范 API 被下断,一些软件会检测 API 的首地址是否为0xCC
(以此判断是否被下断)。
技巧:躲过检测的方法是将断点设在函数内部或末尾,例如将断点设在函数入口的下一行等。
3.2 硬件断点
硬件断点优势:
- INT 3 断点容易被检测到;
- 硬件断点执行速度快。
缺点:最多只能使用 4 个断点。
硬件断点可以在代码区、数据区下断点。在数据区下断点也可以实现与内存断点相同的效果。
硬件断点和DRx调试寄存器有关。在 Intel CPU 体系架构手册中可以找到对 DRx 调试寄存器的介绍,如图 2.24 所示。
DRx 调试寄存器共有 8 个(DRO~DR7),每个寄存器的特性如下。
- DR0~DR3:调试地址寄存器,用于保存需要监视的地址,例如设置硬件断点。
- DR4~DR5:保留,未公开具体作用。
- DR6:调试寄存器组状态寄存器。
- DR7:调试寄存器组控制寄存器。
硬件断点的原理是使用 DRO、DR1、DR2、DR3 设定地址,并使用 DR7 设定相应的控制位,因此最多设置 4 个断点 。硬件执行断点与CC
断点的作用一样,但因为硬件执行断点不会将指令首字节修改为CC
,所以更难检测。
硬件断点设断方法:
- 在指定的代码行单击右键,执行快捷菜单中的“Breakpoint” →”Hardware, on execution”(“断点”→“硬件执行”)命令。
- HE地址。
硬件断点删除方法:
- 单击菜单栏“调试”→”硬件断点”;
- 在有硬件断点的地址处右键删除硬件断点。
硬件断点的运用:Ollydbg 提供了一个快捷键F4,可以执行到光标所在的行。这也是利用调试寄存器的原理 --- 在中断后自动删除,相当于执行了一次性硬件断点。
硬件断点的优点是速度快,在 INT 3 断点容易被发现的地方使用硬件断点会有很好的效果,缺点是最多只能使用 4 个断点。
3.3 内存断点
内存断点:只能下1个内存断点。
- 内存访问断点;“断点”→“内存访问”/“删除内存断点”)
- 内存写入断点。(“断点”→“内存写入”/“删除内存断点”)
内存断点与INT 3
断点的区别:
内存断点不修改原始代码,不会像 INT3 断点那样因为修改代码被程序校验而导致下断失败。因此,在遇到代码校验且硬件断点失灵的情况下,可以使用内存断点。
Ollydbg可以设置内存访问断点或内存写断点。
原理:对所设的地址赋予不可访/不可写属性,这样当访问/写的时候就会产生异常。Ollydbg 截获异常后,比较异常地址是不是断点地址,如果是就中断,让用户继续操作。
程序运行时有 3 种状态,分别是读取、写入和执行(被CPU执行)。写入和读取的示例代码如下
1 | mov dword ptr ds:[405528], edx ;对【405528] 处的内存进行写入 |
硬件断点与内存断点:
硬件断点在数据区下断也可以实现与内存断点相同的效果。单个硬件写入访问断点可以设置为 1 字节、2 字节或 4 字节,而且不论选择的数据范围有多大,只有前 4 个字节会起作用。
硬件访问/写入断点是在触发硬件断点的下一条指令处下断,而内存断点是在触发断点的指令处下断。
3.4 内存访问一次性断点
该类型的断点是一次性的,它可以对内存块的整个段下断点。由于Windows使用段页式的内存管理方法,每个段都有不可访问、读、写、执行的属性,如果对某一个区块下断点之后,当这个区块被访问或者执行时就会中断。
简称为段断点,在脱壳时捕捉调用或返回某个模块很有用。
使用方法:
- 在OD里按
Alt+M
/快捷按钮M→在相应的段上单击右键→在访问上设置断点(Set break-on-access)
段断点与内存断点是有区别的,段断点(在访问上设置中断)是一次性的,而内存断点不是一次性的。
3.5 消息断点
Windows 本身是由消息驱动的,如果调试时没有合适的断点,可以尝试使用消息断点。当某个特定窗口函数接收到某个特定消息时,消息断点将使程序中断。
消息断点与 INT 3 断点的区别在于:INT 3 断点可以在程序启动之前设置,消息断点只有在窗口被创建之后才能被设置并拦截消息。
当用户单击一个按钮、移动光标或者向文本框中输入文字时(一个事件),一条消息就会发送给当前窗体。所有发送的消息都有 4 个参数,分别是 1 个窗口句柄(hwnd)、1 个消息编号(msg)和 2 个 32 位长(long)的参数。Windows 通过句柄来标识它所代表的对象。例如,在单击某个按钮时,Windows 通过句柄来判断单击了哪一个按钮,然后发送相应的消息来通知程序。
事件→发送消息(4个参数)→当前窗体。
举例:
将TraceMe.exe程序拖进OllyDbg → 点击W或输入
Alt+W
打开“窗口”(实时显示窗口上相关信息包括按钮、文本输入框的相关信息的重要参数) → 右键刷新,无任何内容,因为窗口还没有被创建;F9运行,出现窗口 → 打开“窗口” → 右键刷新;
对“Check”按钮下断点,即当单击该按钮时程序中断。在“Check”条目上单击右键 → 在“ClassProc”上设置消息断点 → 选择消息类型。有文本控件、按钮、鼠标等类型的消息。如果选择第 1 项“Any Message“,将拦截所有消息。我们在这里关注的消息属于“Button”(按钮)这一项,当单击按钮并松开时,会发送“WM_LBUTTONUP“这个消息。单击下拉菜单,选择“202 WM_LBUMTONUP”选项,再单击“OK”按钮,消息断点就设置好了。
输入用户名、序列号后消息断点将程序断在系统领空,但是按
Ctrl+F9
(运行到ret
)或Alt+F9
(返回到程序领空)或F9
都不管用,则需要设置段断点(在访问上设置中断,一次性断点)来让程序返回到程序领空并断下来。点击运行
/F9
,程序断在程序领空。
3.6 条件断点
条件断点会在满足一定条件时才会中断,这类断点称为条件断点。OllyDbg的条件断点可以按寄存器存储器消息等设断。条件断点是一个带有条件表达式的普通INT 3断点。当调试器遇到这类断点时,断点将计算表达式的值,如果结果非零或者表达式有效,则断点生效(暂停被调试程序)。
如循环、数组等场景中使用起来分析很方便。
(1)按寄存器条件中断
- 快捷键:Shift+F2
- CmdBar:bp 0x40XXXXXX EAX==??
(2)按存储器条件中断
在实际应用中程序可能会成百上千次调用CreateFileA
函数,因此让OllyDbg在CreateFileA
函数打开所需文件时中断就显得十分有必要了。CreateFile函数的定义如下:
1 | HANDLE CreateFile( |
3.7 条件记录断点
条件记录断点除了具有条件断点的作用,还能记录断点处函数表达式或参数的值。也可以设置通过断点的次数,每次符合暂停条件时,计数器的值都将减1。
快捷键:Shift+F4
- ①在“Condition:(条件)域中输入要设置的条件表达式。如
[STRING [esp+4]]=="c:\\1212.txt"
。 - ②在“Explanation”(说明)域中设置一个名称(自定义)。
- ③“Expression”(表达式)域中是要记录的内容的条件,只能设置1个表达式,例如要记录EAX的值,可以输入“EAX”。
- ④在“Decode value of expression as”(解码表达式的值)下拉列表中可以对记录的数据进行分析。例如,在条件记录窗口中,如果“Expression”域中填写的是 “【esp+4】”,则要在该下拉列表中选择“Pointer to ASCII String”(指向ASCII字符串的指针)选项,才能得到正确的结果,其功能相当于“STRING”前缀。
- ⑤对这3个域,可以根据需要设置“Never”(从不)、“On condition”(按条件)或“Always”(永远)。
- “Pause program”(暂停程序)域用于设置OllyDbg遇到断点时是否中断。
- “Log value of expression”(记录表达式的值)域用于设置遇到断点时是否记录表达式的值。
- “Log function arguments”(记录函数参数)域用于设置遇到断点时是否记录函数的参数。
- ⑥通过断点的次数。
- ⑦条件记录断点允许向插件传递1个或多个命令。当应用程序因条件断点暂停,并且断点中包含传递给插件的命令时,都会调用回调函数
ODBG_Plugincmd(int reason,t_reg *registers,char *cmd)
。例如,当程序暂停时,传送命令d esp
给CmdBar插件,只要在如图⑦窗口的文本框中输入.d esp
(注意,命令前有一个点字符.
),当条件断点断下时,就会执行“desp”命令。这时,我们就可以在数据窗口中看到ESP地址处的数据了。 - ⑧设置好条件记录断点,单击实例Conditional_bp的“OpenTest”按钮,运行后,OllyDbg会在“Logdata”窗口(快捷键
Alt+L
)记录数据,如图⑧所示。
4 插件
将插件复制到Plugin这个目录下,重新运行OllyDbg,就可以加载插件了。由于0llyDbg默认只能加载32个插件,而且插件之间有可能存在冲突,建议在插件目录下仅放置常用的插件,以减少各类问题的出现。
常用插件:
- 命令行插件CmdBar如下图。
- OllyScript插件:OllyDbg的脚本插件可以通过OllyScript脚本完成一些复杂的或重复性的操作,具体使用方法可参考其帮助文档。
5 Run trace
Run trace (Run跟踪)可以把被调试程序执行过的指令保存下来,以便了解以前发生的事件。该功能将地址、寄存器的内容、消息等记录到Run trace缓冲区中。
-待补充-
6 Hit trace
Hit trace能够让调试者辨别哪一部分代码被执行了,哪一部分没有。
原理:在选中区域的每一条命令处设置一个INT 3断点,当中断发生时,OllyDbg便把它去除。在使用Hit trace时,不能在数据中设置断点,否则程序可能会崩溃。如下图。
使用方法:当遇到一段跳转分支比较多的代码,需要了解程序的执行线路时,可以使用Hit trace。
- 选中这段代码 → 单击右键快捷菜单中的“Hit trace”(Hit 跟踪)→ “Add selection”(添加选择部分)命令,将需要监视的代码选中 → 然后按“F9”键让程序运行,OllyDbg就会在已被执行的指令前用不同的颜色添加标记。
- 注意:如果右键快捷菜单中没有Hit trace的相关命令,则必须打开相关的菜单选项进行代码分析。例如,可以按“Ctrl+A”快捷键或执行右键快捷菜单中的“Analysis”一“Analyse code”(“分析”十“分析代码”)命令重新分析代码。
7 Ollydbg常见问题
常见问题 | 快捷键 | 原因分析及解决方法 |
---|---|---|
乱码问题 | ◡̈⃝: Ctrl+A ◡̈⃝: “Analysis” → “Analyse code”(“分析” → “分析代码”) ◡̈⃝:“Analysis” → “Remove analysis from module”(“分析” → “从模块中删除分析”) |
原因:这是因为OllyDbg将这段代码当成了数据,没有进行反汇编识别。 解决方法:此时,只要执行0llyDbg右键快捷菜单中的“Analysis” → “Analyse code”(“分析” → “分析代码”)命令或按“Ctrl+A”快捷键,强迫OllyDbg重新分析代码即可。如果还是无法识别,可以尝试执行右键快捷菜单中的“Analysis” → “Remove analysis from module”(“分析” → “从模块中删除分析”)命令或在UDD目录中删除相应的UDD文件。 |
快速回到当前程序领空 | ◡̈⃝:双击寄存器面板中的EIP ◡̈⃝:单击如下按钮。 |
原因:在OllyDbg中查看代码时可能会翻页或者定位到其他地方。 解决方法:如果想快速回到当前CPU所在的指令处,可以双击寄存器面板中的EIP或单击C按钮。 |
OllyDbg修改EIP | ◡̈⃝:Ctrl+* ◡̈⃝:右键 → “New origin here”(在此处新建EIP) |
原因:修改EIP。 解决方法:将光标移到需要修改的地址上,执行右键快捷菜单中的“New origin here”(在此处新建EIP)命令或使用快捷键 Ctrl+* 即可修改EIP。 |
UDD | ◡̈⃝:查看程序目录udd文件夹 | 原因:清除调试痕迹。 解决方法:OllyDbg把所有与程序或模块相关的信息保存在单独的文件中,以便在模块重新加载时继续使用。这些信息包括标签、注释、断点、监视、分析数据、条件等。 |
已经删除了断点,llyDbg重新加载时这些断点重新出现 | ◡̈⃝:修改ollydbg.ini |
将配置文件ollydbg.ini 中的相应内容改成Backup UDD files=1 即可解决。 |
OllyDbg出现“假死”现象 | ◡̈⃝:修改ollydbg.ini |
用OllyDbg调试一些加壳程序,程序运行到断点(包括硬件断点)时,OllyDbg会出现“假死”现象。解决方法是:打开配置文件ollydbg.ini ,如果Restore windows 是一个很大的值,就设置Restore windows 0 。 |
微调窗口显示 | ◡̈⃝:Crl+↑ ◡̈⃝:Ctrl+↓ |
可以通过“Crl+↑”或“Ctrl+↓”快捷键对反汇编窗口或数据窗口翻动1字节。 |
执行复制到可执行文件时,提示错误信息“Unable to locate data in executable file” | ◡̈⃝:修改RawSize =VirtualSize |
这里要修改的地方不在RawSize范围内。修改PE文件,使“RawSize =VirtualSize”,具体方法参见第11章。 或者添加的代码所在区段属性不是 CODE/TEXT |
把call调用改成函数名的形式 | ◡̈⃝:Shift+; | 例如call 401496 ,假设401496h 处是amsg_exit 函数,将光标停在该处,按Shift+; 快捷键,会弹出一个标签框,在其中输入字符amsg_exit ,所有调用401496h 处的call指令都会变成call <amsg_exit> 的形式。 |