清除硬件断点的方法有哪些?
修改调试寄存器,CONTEXT结构体
1.对API下断点,测试是否会断下
应该是存在判断敏感API的机器码,检测第一个字节是否是0xCC,故此处导致程序崩溃了,破解方法就是在第二行反汇编下断点。
尝试在下一行重新下断点,结果程序直接运行了?并没有断下。
再次尝试在API对应的底层函数调用的位置下断点
结果发现程序还是运行起来了,并没有断下,所以应该不是API,而是异常
2.搭建异常触发环境
首先我们需要对OD进行一些配置,在菜单中找到调试选项,将一些异常取消忽略,按照以下配置。
除了OD调试选项的配置之外,一些反反调试插件也需要做些配置,这里我们使用的是StrongOD插件,在插件中找到StrongOD,取消插件对一些异常的跳过。按照以下配置对插件进行设置。
程序运行起来,发现一共有7个异常,需要排查出对硬件断点清0的地方,这里采用在异常点设置硬件断点的方法,尝试看能不能断下来,如果能断下来说明当前异常点前面的异常处理函数中没有对硬件断点清0。
第一个异常:
第二个异常:
第三个异常:
第四个异常:
第五个异常:
第六个异常:
第七个异常:
按照上面的思路,首先在第二个异常点设置硬件执行断点,然后动态调试程序,如果硬件执行断点有效(在状态栏中可以发现硬件断点命中的提示信息)说明第一个异常点的异常回调函数中没有清除硬件断点的代码。
硬件执行断点的特点是到了这个地址就会停下来,而异常是必须触发,即必须执行了这段代码,执行了这条指令才能够触发,如果先触发了硬件断点,就意味着这个异常前面的异常处理函数是没有清除硬件断点的,
按照这样的方式,依次测试每一个异常的回调函数。
第二个异常:
这条指令还没有执行之前就已经触发了硬件断点,说明第一个函数内没有清除硬件断点
清除第二个异常处的硬件断点,依次向下一个异常继续下硬件执行断点
第三个异常
这条指令还没有执行之前就已经触发了硬件断点,说明第二个函数内没有清除硬件断点
第四个异常
这条指令直接触发了异常,说明第三个函数内有清除硬件断点的代码
综上所述,发现第三个异常,即第一个STI特权指令异常回调是清除硬件断点的代码。
打开SEH链观察
在0043AF42的位置下断点
继续shift+F9进入第三个异常处理函数
上面代码由于是和异常、清除硬件断点有关的,所以其中肯定有异常相关的结构体和寄存器相关的Context结构,直接分析代码不太容易看出来,这个时候我们就可以将其直接在这个地方进行DUMP,这里DUMP不是为了脱壳,只是单纯的DUMP,然后方便在IDA中查看其中的代码,使用IDA特有的解析结构体的功能,解析可能存在的结构体。
进入IDA,我直接一手F5......
在结构体中Inser添加一个Context结构
在伪代码中转换结构体
继续回到OD,分析这段代码应该就是清除硬件断点的代码
初步思路:在0043AF59的位置设置一个断点,到了这个断点之后将清除硬件断点的这段代码清除一下,将其NOP掉,后面的硬件断点就能正常的触发了
将之前更改的设置还原,把异常重新跳过
在OEP和清除硬件断点的位置下硬件断点,然后重新运行程序
当程序运行至清除硬件断点的位置时将其NOP掉
再运行就发现硬件断点正常的触发了
继续向下分析,发现IAT加密了
所以就需要解密IAT,照常设置硬件写入断点
重新运行程序,NOP清除硬件断点的位置,然后程序断在此处,一翻完全看不清代码啊
点击EIP回到原来的位置,按Ctrl+↑(微调反汇编引擎,向上一个字节),找到正确的代码,这里看起来不是填充IAT的位置
继续运行,仍然看不出来,继续按Ctrl+↑,找到正确的代码,这里看起来应该就是填充IAT的位置
填充IAT的循环
再接着在写入IAT附件寻找获取API的位置
可以使用RUN跟踪的方法,设置EIP == 0043918C 即可(只需要翻一万两千七百多行就可以找到了)
获取API地址(通过RUN跟踪找到)
继续单步跟踪分析
继续跟踪发现将EAX进行比较,说明在判断hash,不相等的时候会跳转,所以直接在00438F81的位置下断点,让程序运行至此
继续跟踪,发现到这个位置后EAX中就变成API地址了
后来发现在这个位置填充IAT的话会有问题,这里就要提到导入表中的特殊API
有些API 在Kernel32导出,实际代码却在ntdll
比如:Kernel32.HeapAlloc,实际代码是在ntdll.RtlAllocateHeap
这种情况下从kernel32中获取HeapAlloc函数的地址,地址里面保存的是函数字符串,NTDLL.RtlAllocateHeap
遇到这种情况,需要特殊处理
①先判断地址处是不是NTDLL打头,是的话就需要二次获取API地址
②解析字符串,获取模块名以及API地址
NTDLL
RtlAllocateHeap
③加载模块获取模块地址,获取API地址
拿到这四个地址后就可以编写脚本了。
编写脚本的思路:
①设置必要的断点
②构建运行循环
③第一个断下的是清除硬件断点的异常回调函数,将其NOP掉
④再次循环会断到获取API的点,保存API地址
⑤再次循环会断到写入IAT的点,将保存的API填充IAT
⑥一直循环,最终到达原始OEP
观察数据窗口,发现每一个API地址中间都用00隔开了
而且观察地址,发现咋跑到壳这个区段来了,正常来说VC6.0的程序应该在第二个区段才对
查看以下422000,发现也有IAT
查看一下模块间调用,发现有43的也有42的,这里的原因上文已经提到过了
因为这里的IAT比较诡异,所以我们在内存中重新规划IAT表
先申请一块内存
复制地址
以此类推,我们把所有的调用都复制粘贴过来就可以了。
还是用工具吧,这也太累了...顺便修复一下输入表
修复完成了
再看一下,都填充好了,确实比我手工快一点点
接下来就是日常DUMP,修复,完活儿
① 循环读取API字符串,判断第一个字节是否指定字符
00438F44
8
>MOV EDI,DWORD PTR DS:[EBX]
00438F46
0
>ADD EDI,DWORD PTR SS:[EBP
+
0x40421E
] ; kernel32.
75E20000
00438F4C
8
>
CMP
BYTE PTR DS:[EDI],
0x48
00438F4F
7
>JNZ SHORT
06.00438FA1
00438FA1
8
>ADD EBX,
0x4
00438FA4
4
>INC ECX
00438FA5
8
>
CMP
ECX,
0x54F
00438FAB
^
7
>JNZ SHORT
06.00438F44
② 循环读取API字符串中的字符,求
HASH
0043A70E
8
>MOV AL,BYTE PTR DS:[EDI]
0043A710
0
>OR AL,AL
0043A712
7
>JE SHORT
06.0043A746
;退出循环
0043A725
4
>INC EDI ; kernel32.
75EDBBDF
0043A726
3
>XOR DL,AL
0043A728
B>MOV AL,
0x8
0043A72A
E>JMP SHORT
06.0043A72D
0043A736
D>SHR EDX,
1
0043A738
7
>JNB SHORT
06.0043A740
0043A73A
8
>XOR EDX,
0xEDB88320
0043A740
F>DEC AL
0043A742
^
7
>JNZ SHORT
06.0043A72A
③ 判断
hash
值是否与当前一致,不一致继续循环,一致进行下一步
00438F7A
3
>
CMP
EAX,
0xA124E28D
00438F7F
7
>JNZ SHORT
06.00438FA1
00438F81
8
>MOV EAX,DWORD PTR SS:[EBP
+
0x40421A
] ; 已经寻找到正确的
hash
④ 根据
hash
值,寻找API偏移,求出API地址
00438F81
8
>MOV EAX,DWORD PTR SS:[EBP
+
0x40421A
] ; kernel32.
75ED7848
00438F87
D>SHL ECX,
1
00438F89
0
>ADD EAX,ECX
00438F8B
0
>MOVZX EAX,WORD PTR DS:[EAX]
00438F8E
C>SHL EAX,
0x2
00438F91
0
>ADD EAX,DWORD PTR SS:[EBP
+
0x404226
] ; kernel32.
75ED4DD0
00438F97
8
>MOV EAX,DWORD PTR DS:[EAX]
00438F99
0
>ADD EAX,DWORD PTR SS:[EBP
+
0x40421E
] ; 计算完之后,EAX是API地址
特殊情况,需要特殊处理一下
⑤ 读取API中的代码,生成加密API代码
代码特别长,只复制了一部分
00439333
AC LODS BYTE PTR DS:[ESI]
00439334
8AF8
MOV BH,AL
00439336
8A27
MOV AH,BYTE PTR DS:[EDI]
00439338
47
INC EDI
00439339
C0EC
04
SHR AH,
0x4
0043933C
2AC4
SUB AL,AH
0043933E
^
73
F6 JNB SHORT
06.00439336
⑥ 填充IAT
① 循环读取API字符串,判断第一个字节是否指定字符
00438F44
8
>MOV EDI,DWORD PTR DS:[EBX]
00438F46
0
>ADD EDI,DWORD PTR SS:[EBP
+
0x40421E
] ; kernel32.
75E20000
00438F4C
8
>
CMP
BYTE PTR DS:[EDI],
0x48
00438F4F
7
>JNZ SHORT
06.00438FA1
00438FA1
8
>ADD EBX,
0x4
00438FA4
4
>INC ECX
00438FA5
8
>
CMP
ECX,
0x54F
00438FAB
^
7
>JNZ SHORT
06.00438F44
② 循环读取API字符串中的字符,求
HASH
0043A70E
8
>MOV AL,BYTE PTR DS:[EDI]
0043A710
0
>OR AL,AL
0043A712
7
>JE SHORT
06.0043A746
;退出循环
0043A725
4
>INC EDI ; kernel32.
75EDBBDF
0043A726
3
>XOR DL,AL
0043A728
B>MOV AL,
0x8
0043A72A
E>JMP SHORT
06.0043A72D
0043A736
D>SHR EDX,
1
0043A738
7
>JNB SHORT
06.0043A740
0043A73A
8
>XOR EDX,
0xEDB88320
0043A740
F>DEC AL
0043A742
^
7
>JNZ SHORT
06.0043A72A
③ 判断
hash
值是否与当前一致,不一致继续循环,一致进行下一步
00438F7A
3
>
CMP
EAX,
0xA124E28D
00438F7F
7
>JNZ SHORT
06.00438FA1
00438F81
8
>MOV EAX,DWORD PTR SS:[EBP
+
0x40421A
] ; 已经寻找到正确的
hash
④ 根据
hash
值,寻找API偏移,求出API地址
00438F81
8
>MOV EAX,DWORD PTR SS:[EBP
+
0x40421A
] ; kernel32.
75ED7848
00438F87
D>SHL ECX,
1
00438F89
0
>ADD EAX,ECX
00438F8B
0
>MOVZX EAX,WORD PTR DS:[EAX]
00438F8E
C>SHL EAX,
0x2
00438F91
0
>ADD EAX,DWORD PTR SS:[EBP
+
0x404226
] ; kernel32.
75ED4DD0
00438F97
8
>MOV EAX,DWORD PTR DS:[EAX]
00438F99
0
>ADD EAX,DWORD PTR SS:[EBP
+
0x40421E
] ; 计算完之后,EAX是API地址
特殊情况,需要特殊处理一下
⑤ 读取API中的代码,生成加密API代码
代码特别长,只复制了一部分
00439333
AC LODS BYTE PTR DS:[ESI]
00439334
8AF8
MOV BH,AL
00439336
8A27
MOV AH,BYTE PTR DS:[EDI]
00439338
47
INC EDI
00439339
C0EC
04
SHR AH,
0x4
[注意]传递专业知识、拓宽行业人脉——看雪讲师团队等你加入!