- 第一章 - OD 的各个窗口介绍
- 第二章 - 数值系统
- 第三章 - 寄存器
- 第四章 - 汇编指令
- 第五章 - 数学指令
- 第六章 - 比较和条件跳转
- 第七章 - call ret
- 第八章 - 循环 字符串指令和寻址方式
- 第九章 - 基本概念
- 第十章 - 断点
- 第十一章:硬件断点与条件断点
- 第十二章 - 消息断点
- 第十三章 - 硬编码序列号寻踪-Part1
- 第十四章 - 硬编码序列号寻踪-Part2
- 第十五章 - 硬编码序列号寻踪-Part3
- 第十六章 - 序列号生成算法分析-Part1
- 第十七章 - 序列号生成算法分析-Part2
- 第十八章 - 序列号生成算法分析-Part3
- 第十九章 - OllyDbg 反调试之 IsDebuggerPresent
- 第二十章 - OllyDbg 反调试之检测 OD 进程名
- 第二十一章 - OllyDbg 反调试之检测 OD 进程名,窗口类名,窗口标题名
- 第二十二章 - OllyDbg 反调试之 UnhandledExceptionFilter,ZwQueryInformationProcess
- 第二十三章 - OllyDbg 反调试之 ProcessHeap,NTGlobalFlag,OutputDebugStringA
- 第二十四章 - OllyDbg 反调试之综合 CrackMe
- 第二十五章 - 异常处理
- 第二十六章 - Visual Basic 程序的破解-Part1
- 第二十七章 - Visual Basic 程序的破解-Part2
- 第二十八章 - Visual Basic 程序的破解-Part3
- 第二十九章 - P-CODE-Part1
- 第三十章 - P-CODE-Part2
- 第三十一章 - 脱壳简介
- 第三十二章 - OEP 寻踪
- 第三十三章 - 神马是 IAT 如何修复
- 第三十四章 - 手脱 UPX,修复 IAT
- 第三十五章 - 手脱 ASPack V2.12
- 第三十六章 - IAT 重定向
- 第三十七章 - 论 IAT 重定向之修复
- 第三十八章 - 手脱 Yoda's Protector v1.3(Yoda's Crypter)
- 第三十九章 - 神马是 stolen bytes
- 第四十章 - OllyDbg 脚本的编写
- 第四十一章 - 神马是 AntiDump
- 第四十二章 - ACProtect V1.09 脱壳(寻找 OEP 绕过硬件断点的检测 修复 Stolen code)
- 第四十三章 - ACProtect V1.09 脱壳(编写脚本修复 IAT)
- 第四十四章 - ACProtect V1.09 脱壳(修复 AntiDump)
- 第四十五章补充章节-ReCrypt v0.80 脱壳(续)
- 第四十六章 - Patrick 的 CrackMe-Part1
- 第四十七章 - Patrick 的 CrackMe-Part2
- 第四十八章 - PeSpin V1.3.04 脱壳-Part1
- 第四十九章 - PeSpin V1.3.04 脱壳-Part2
- 第五十章 - 再谈 ReCrypt v.0.80 脱壳(调戏 OutputDebugString)
- 第五十一章 - ASProtect v2.3.04.26 脱壳-Part1
- 第五十二章 - ASProtect v2.3.04.26 脱壳-Part2
- 第五十三章 - TPPpack 脱壳
- 第五十四章 - EXECryptor v2.2.50.a 脱壳-Part1
- 第五十五章 - ExeCryptor v2.2.50.a-Part2
- 第五十六章 - EXECryptor v2.2.50.b 脱壳
- 第五十七章 - ExeCryptor v2.2.50.c/d/e/f/g 脱壳
- 第五十八章 - ExeCryptor v2.2.50
第四十二章 - ACProtect V1.09 脱壳(寻找 OEP 绕过硬件断点的检测 修复 Stolen code)
本章开始,我们将讨论更加复杂的壳-ACProtect 1.09。
我们一步步的来剖析这个壳的保护机制。
该例子的名称为 UnPackMe_ACProtect 1.09g.f。配合好反反调试插件,用 OD 加载之。
我们直接运行起来,看看会发生什么。
.
这里我用的是 Parcheado_4(西班牙语:Patched_4) 这个 OD,接着按照下图来配置 HideOD 0.12 反反调试插件,这样就可以正常运行了,该壳会使用 Process32Next 这类 API 函数来检测 OD 进程,反反调试插件可以绕过。
下面我们来定位 OEP。
前面章节我们介绍了很多定位 OEP 的方法,比如说可以使用 OD 自带的跟踪功能就能定位到 OEP,打开主菜单项 Debugging options-SFX,选择 Trace real entry blockwise(inaccurate)。接着重启 OD,不一会儿断在了这里。
现在的问题是这里是否是真正的 OEP,或者说是不是存在 stolen bytes。我们来看一看堆栈的情况。
现在我们不勾选自动定位 OEP 的选项,重启 OD。
我们可以看到比开始的时候多了两个值。一般来说,这样情况存在 stolen bytes。下面我们来定位 stolen bytes。
在定位 stolen bytes 之前,先来给大家介绍一下如何利用 HBP.txt 这个脚本来设置硬件断点,这个方法我们在脚本的编写那一章中介绍过了,大家应该还记得吧!通过脚本来设置硬件断点可以绕过很多壳对于硬件断点的检测。假如我们现在想通过设置硬件断点让程序断在 OEP 处的话,我们会发现程序并不会断下来,我们可以通过 HPB.txt 这个脚本来解决这个问题。下面就来给大家详细讲解硬件断点为什么不触发的原因,以及 HPB.txt 脚本如何解决这个问题的。
下面我们再次重启 OD,分别给 KiUserExceptionDispatcher 以及其内部调用的 ZwContinue 处设置断点。
下面我们来给 OEP 处设置一个硬件执行断点。
此时在壳的入口处,单击鼠标右键选择-Goto-Expression,输入地址 4271B5。
在这里我们设置一个硬件执行断点。
单击鼠标右键选择 Breakpoint-Hardware,on execution,接着运行起来。
断了下来,我们来看下堆栈情况。
此时断在了 KiUserExceptionDispatcher 的入口处,堆栈中偏移 4 的单元中存放了 context 的指针,下面我们到数据窗口中定位该 context。
这里我暂时不解释 CONTEXT 结构体各个字段的含义,等后面用到的时候再解释。
在寄存器窗口中,单击鼠标右键选择 View debug registers,切换到调试寄存器显示模式,如果没有该选项的话,说明当前已经是调试寄存器显示模式了。
这些就是调试寄存器组,Dr0 ~ Dr7。Dr0,Dr1,Dr2,Dr3 是用于设置硬件断点的,由于只有 4 个硬件断点寄存器,所以同时最多只能设置 4 个硬件断点。产生的异常是 STATUS_SINGLE_STEP(单步异常)。Dr4,Dr5 是系统保留的。Dr7 是一些控制位,用于控制断点的方式,Dr6 是用于显示哪个硬件调试寄存器引发的断点,如果是 Dr0 ~ Dr3 的话,相应位会被置 1。即如果是 Dr0 引发的断点,则 Dr6 的第 0 位被置 1,如果是 Dr1 引发的断点,则 Dr6 的第 1 位被置 1,依次类推。因为硬件断点同时只会触发一个,所以 Dr6 的低 4 位最多只有一位被置 1,所以在进入单步后,我们可以通过检测 Dr6 的低 4 位是否有 1 的位,就可以判断该单步是否是因为硬件断点被断下的。如果是硬件断点被断下的,进而可以通过 Dr6 的哪一位为 1 来判断是由 Dr0~Dr3 中的哪个断点断下的。
调试控制寄存器 Dr7 比较重要,其 32 位结构如下:
位 0 L0 和位 1 G0:用于控制 Dr0 是全局断点还是局部断点,如果 G0 为 1 则是全局断点,如果 L0 为 1 则为局部断点。G0,L0 ~G3,L3 分别用于控制 Dr1~Dr3。
LE 和 GE:P6 family 和之后的 IA32 处理器都不支持这两位。当设置时,使得处理器会检测触发数据断点的精确的指令。当其中一个被设置的时候,处理器会放慢执行速度,这样当命令执行的时候可以通知这些数据断点。建议在设置数据断点时需要设置其中一个。切换任务时 LE 会被清除而 GE 不会被清除。为了兼容性,Intel 建议使用精确断点时把 LE 和 GE 都设置为 1。
LEN0 到 LEN3:指定 调试 地址 寄存器 DR0 到 DR3 对应断点所下断的长度。如果 R/Wx 位为 0(表示执行断点),则 LENx 位也必须为 0(表示 1 字节),否则会产生不确定的行为。LEN0 到 LEN3 其可能的取值如下:
(1)00 1 字节
(2)01 2 字节
(3)10 保留
(4)11 4 字节
R/W0 到 R/W3:指定各个断点的触发条件。它们对应于 DR0 到 DR3 中的地址以及 DR6 中的 4 个断点条件标志。可能的取值如下:
(1) 00 只执行
(2) 01 写入数据断点
(3) 10 I/O 端口断点(只用于 pentium+,需设置 CR4 的 DE 位,DE 是 CR4 的第 3 位 )
(4) 11 读或写数据断点
GD 位:用于保护 DRx,如果 GD 位为 1,则对 Drx 的任何访问都会导致进入 1 号调试陷阱(int 1)。即 IDT 的对应入口,这样可以保证调试器在必要的时候完全控制 Drx。
此时 Dr0 为 4271B5,表示 4271B5 地址处被设置了硬件断点。现在我们来看看 CONTEXT 结构。
这里我们可以看到 context 结构中 Dr0~Dr3 寄存器的内容。黄色标注的 4271B5 是我们设置了硬件断点的地址。其他三个粉红色标注的位零,因为我们只设置了一个硬件断点,所以它们是空的。
当异常处理完毕以后,context 中的值将被清零,我们一起来看一看,直接运行起来,断在第二个断点处。
我们可以看到经过了异常处理以后,Dr0 被清零了,我们可以编写脚本来恢复它。这里我们有两种方案,第一种方案:在断在 KiUserExceptionDispatcher 入口处时将 context 中调试寄存器的值保存一份。当断在下面的 ZwContinue 的调用处时将调试寄存器的值恢复。第二种方案:在 context 中定位到程序的返回地址,在返回地址处设置一个断点,当断在返回地址处时恢复硬件断点,相当于重新设置了一次硬件断点。
好,这里我们采用第二种方案,但是现在的问题是程序的返回地址保存在 context 结构中的哪里呢?
Context 的起始地址偏移 0B8 地址处就保存了返回地址,我的机器上是:
12FC8C + 0B8 = 12FD44
12FD44 内存单元中就保存着程序的返回地址,在脚本中可以对返回地址设置一个断点,当程序返回以后,再次设置硬件断点,我们来看一看脚本是怎么写的。
脚本的开头我们定义一个变量
var 命令是用来声明一个变量,这个变量 RetAddr 我们用来保存由异常返回的地址。
这就是恢复硬件断点的一部分,首先将 ESP 寄存器的值保存到 RetAddr 变量中。
此时 RetAddr 保存 ESP 寄存器的值,接着再取其内容,也就是 context 结构的首地址,我这里 context 的首地址为 12FC8C,接着加上 0B8 等于 12FD44,12FD44 内存单元中就保存了异常的返回地址。
最后再次取内容就得到了异常的返回地址,接着就对该返回地址设置断点。然后运行起来。
这里是判断断下来的地址是 KiUserExceptionDispatcher 的入口处,还是 ZwContinue 的调用处,还是异常的返回地址。
这里是删除异常返回地址处的断点,接着重新设置硬件断点。
好了,下面我们来看看脚本执行的效果,重启程序。
我们通过这个脚本来断到 OEP 处。
我们现在来运行脚本,不要忘了设置 KiUserExceptionDispatcher 入口处以及下面 ZwContinue 调用处的断点,还要记得将忽略异常的选项都勾选上。
这里我们可以看到断在了硬件断点处,我们选择 NO,不继续。
好,我们现在断在了 OEP 处。
有些壳会通过一些方法来清除硬件断点,现在我们就可以很完美的给 stolen bytes 设置硬件断点了。
首先我们来看看有没有异常,我们从最后一次异常处开始跟踪。
在日志窗口中我们可以看到在到达 OEP 之前产生了一次异常,我们可以看到类型是 Access violation when reading。我们来清空掉相应的异常忽略选项。
禁用掉脚本中用到的断点。
现在我们重新启动,然后运行起来。
断在了最后一次异常处,给当前区段设置内存访问断点,让其断在异常处理程序中。
按 Shift + F9 忽略异常运行起来。
按 F7 键单步几下到达异常处理函数的 RET 处。接着运行起来,我们返回到了这里
下面我们可以对 OEP 处设置断点,因为此时壳已经解密完毕了。
现在我们就可以从 471090 处开始跟踪了,不一会儿跟踪完毕了,我们现在到达了 OEP 处。
我们来看一看跟踪的指令列表。
这里跟踪的指令的最后几行,这里我们可以搜索 PUSH EBP,一般是从上往下搜索,但这里前面位于系统 DLL 中的 PUSH EBP 过多,所以这里我们从下往下搜索。(PS:由于这里记录的 TXT 又几十 M,所以这里我使用的是 EditPlus)
这里我们可以看到 stolen bytes,接着是 NOP 指令,然后使用 PUSHAD 指令保存寄存器环境,那么在跳往假的 OEP 之前会使用 POPAD 指令来恢复寄存器环境的。
这里我们可以看到 stolen bytes 的数值部分是被保存在栈中的。
EBP 寄存器可能在其间会赋予一个随机值,但是随后通过 MOV EBP,ESP 指令又可以修正。
我们可以看到记录文件中显示 EBP 的值为 12FFC0,该值刚好与假 OEP 处的 EBP 的值相等。说明我们定位到的 stolen bytes 是正确的。
好了,这里我们就找到了 stolen bytes,关于前面的通过脚本设置硬件断点的技巧我们将在下一章节中使用。
Trace 之前记住删除内存断点,不会记录到文件的可以后面跟帖提问... 这部分作者没有详细说明...
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论