0%

2018-01-10-利用硬件断点Hook法不脱壳爆破某VMP加壳程序

前几天得到一个VMP加壳的木马程序,要求bypass掉它的UKey弹框验证,虽然乍一看感觉挺麻烦的,当然最重要的是完成指标即可,没必要脱壳,能尽量减少工作量。因为不脱壳情况下代码未解密且存在文件校验,无法打文件补丁,最后考虑到这个程序的VMP壳没有开启反调试选项,所以最终通过模拟一个简单调试器完成了挂载程序爆破搞定,实际一共也就花了三四个小时。

分析

    首先查了下壳没有查出来,看了下有vmp0、vmp1的区段名字,因为实现告知是VMP,也没有再去验证了,直接运行程序看看能不能不脱壳破解,先运行程序弹出下图的提示字符串:

    还是从字符串信息入手,寻找弹框代码的位置,因为加壳了,直接在程序刚载入到OD的状态是搜不到这个字符串的,运行到弹出消息框再搜索这个字符串就行,共找到对这个字符串的两处引用:

    再来分析下这两处引用该字符串的代码,第一处代码可以看到有两个判断不通过会跳转到弹框流程:

    第二处代码的判断跟上面类似:

    先来试试直接爆破掉0x56FF47、0x56FF55、0x56FFBA这三个判断的结果,OD载入程序之后在0x56FF45地址下硬件执行断点,运行出发断点之后修改0x56FF47处指令为jz、0x56FF55及0x56FFBA处指令为jnz,再恢复运行即可看到程序正常运行到配置界面不再弹框。


编写Patch程序

    下面就是如何根据爆破的结果编写补丁程序了,因为程序被加壳了,需要Patch的三个地址0x56FF47、0x56FF55、0x56FFBA在程序刚载入的时候代码并未被解密,定位并修改被加密过的代码比较麻烦,而且壳里也会进行代码校验,就打算采取相比其它补丁方式来说最方便的硬件断点Hook办法,写一个简单的调试器程序,仿照上一步OD挂载程序手工爆破的步骤,设置0x56FF45地址的硬件断点,等代码解密断在0x56FF45地址之后在单步异常0x80000004的处理程序中再修改0x56FF47、0x56FF55、0x56FFBA这三个地址的数据内容。

    重新完成一个调试器程序比较麻烦,就在网上搜了下现成的代码,最后是根据http://blog.chinaunix.net/uid-20547722-id-1647182.html链接中的代码修改编译的补丁程序,下面介绍下补丁代码patch.exe的一些思路

  1. patch.exe以调试状态启动sample.exe,并进入循环接受调试事件状态。
  2. sample.exe在载入时会先给调试器patch.exe发送一个初始的断点事件,异常代码0x80000003,这也是patch.exe接受到的第一个调试异常,接收到该异常事件之后patch.exe修改sample.exe的入口点代码为int3中断指令的机器码0xCC,这一步是为了下一步在程序完全载入到内存时能够让调试器接管设置硬件断点。
  3. 继续运行sample.exe,patch.exe接收到第2步设置在入口点的断点异常,在异常处理代码中获取ThreadContext,设置Dr0、Dr7调试寄存器的值恢复运行。
  4. patch.exe接收到硬件断点触发的单步异常,在异常处理程序中对0x56FF47、0x56FF55、0x56FFBA处的代码进行修改,这里我其实是直接把EIP寄存器的值改为0x56FF49跳过了0x56FF47地址处的检查,0x56FF55、0x56FFBA地址指令改成jnz,最后恢复运行就行了。

一些问题

  1. 这个壳里没有使用一些反调试技术,所以使用这种方式还比较简单,如果是其它壳有些检测调试的话还需要做针对性的处理。
  2. 在patch.exe调试sample.exe的第2、3步骤中,之所以先修改入口点的代码为int3运行触发之后再设置硬件断点是因为初始断点不能设置硬件断点,见下图的示例:

  1. 最初调试的时候想根据程序运行时候触发的一个0x0EEDFADE的异常再做爆破的,直接在这个异常处理程序中设置爆破就行,结果写完测试程序第一次运行爆破成功之后再运行补丁程序就再也没有0x0EEDFADE这个异常了,就还改成了最终这样设置硬件断点的办法。
  2. 因为修改的网上代码,有一些冗余的变量等等都没有清除,代码也不是很清晰,最后测试能用也就没有细改了。

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
.586
.model flat, stdcall
option casemap :none
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
include windows.inc
include kernel32.inc
include user32.inc
include Comctl32.inc
include comdlg32.inc
;include macros.inc
include masm32.inc

includelib kernel32.lib
includelib user32.lib
includelib Comctl32.lib
includelib comdlg32.lib
includelib masm32.lib


dwOrgOEP equ 00a6ee02h ;程序原始入口
BREAK_POINT1 equ 0056FF45h ;第一个断点
;BREAK_POINT1 equ 0056FF45h ;第一个断点
.CONST
DR0_ENABLED EQU 000000001b
LOCAL_EXACT_BPM_ENABLED EQU 100000000b

.data
FileName db 'sample.exe',0,0,0,0,0,0,0,0,0,0,0,0,0,0
int3 db 0cch
value db 8 dup(?)
buffer db 8 dup(?)
oldbyte db 8 dup(?)
szFormat db "%X",0
dwCountSS dd 0
dwCountBP dd 0
Startup STARTUPINFO <>
processinfo PROCESS_INFORMATION <>

patchto90 db 2 dup(090h)
patchto75 db 2 dup(075h)
.data?
startinfo STARTUPINFO <>
pi PROCESS_INFORMATION <>
DBEvent DEBUG_EVENT <>
context CONTEXT <>

;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
.code
start:
pushad
;********************************************************************
; 创建进程
;********************************************************************
invoke CreateProcess, addr FileName, NULL, NULL, NULL, FALSE, DEBUG_PROCESS or DEBUG_ONLY_THIS_PROCESS, NULL, NULL, addr startinfo, addr pi
.if !eax
;invoke MessageBox,hDlg,CTXT("不能创建进程"),CTXT("错误!"),MB_OK
invoke ExitProcess,NULL
.endif
xor eax,eax
mov dwCountBP, eax
mov dwCountSS, eax
;********************************************************************
; 调试进程,进入循环调试
;********************************************************************
.while TRUE
invoke WaitForDebugEvent, addr DBEvent, INFINITE
.if DBEvent.dwDebugEventCode==EXIT_PROCESS_DEBUG_EVENT
;invoke MessageBox, 0, CTXT("退出进程..."), CTXT("提示!"), MB_OK+MB_ICONINFORMATION
.break
;********************************************************************
; 异常中断
;********************************************************************
.elseif DBEvent.dwDebugEventCode==EXCEPTION_DEBUG_EVENT
.if DBEvent.u.Exception.pExceptionRecord.ExceptionCode==EXCEPTION_BREAKPOINT
;********************************************************************
; 第一次中断时在原始入口点处设置断点
;********************************************************************
inc dwCountBP
.if dwCountBP==1
invoke ReadProcessMemory, pi.hProcess, dwOrgOEP, addr oldbyte, 1, 0 ;在dwOrgOEP中读出一个字节
invoke WriteProcessMemory, pi.hProcess, dwOrgOEP, addr int3, 1, 0 ;写入INT3断点
;********************************************************************
; 第二次中断,中断在起先设置的原始入口点,恢复代码,在机器码处设置硬件断点
;********************************************************************
.elseif dwCountBP==2
mov context.ContextFlags, CONTEXT_CONTROL
invoke GetThreadContext, pi.hThread, addr context
dec context.regEip
invoke WriteProcessMemory, pi.hProcess, dwOrgOEP, addr oldbyte, 1, 0 ;恢复入口代码
invoke SetThreadContext, pi.hThread, addr context
mov context.ContextFlags, CONTEXT_DEBUG_REGISTERS
invoke GetThreadContext, pi.hThread, addr context
mov context.iDr0, BREAK_POINT1 ;设置硬件断点
mov context.iDr7, LOCAL_EXACT_BPM_ENABLED + DR0_ENABLED
invoke SetThreadContext, pi.hThread, addr context
.endif
invoke ContinueDebugEvent, DBEvent.dwProcessId, DBEvent.dwThreadId, DBG_CONTINUE
.continue
.elseif DBEvent.u.Exception.pExceptionRecord.ExceptionCode==EXCEPTION_SINGLE_STEP;0eedfadeh ;EXCEPTION_SINGLE_STEP ;单步运行模式

inc dwCountSS
.IF dwCountSS == 1
; mov context.ContextFlags, CONTEXT_FULL
; invoke GetThreadContext, pi.hThread, addr context
; mov eax,context.regEax

invoke WriteProcessMemory,pi.hProcess,056ff47h,addr patchto90,02h,NULL ;
invoke WriteProcessMemory,pi.hProcess,056ff55h,addr patchto75,01h,NULL ;
invoke WriteProcessMemory,pi.hProcess,056ffbah,addr patchto75,01h,NULL ;

mov context.ContextFlags, CONTEXT_FULL
invoke GetThreadContext, pi.hThread, addr context
mov context.regEip ,056ff49h
;add EAX,01h

mov context.iDr0, 0
mov context.iDr7, 0
invoke SetThreadContext,pi.hThread, addr context
invoke ContinueDebugEvent, DBEvent.dwProcessId, DBEvent.dwThreadId, DBG_CONTINUE
.continue
.endif
invoke ContinueDebugEvent, DBEvent.dwProcessId,DBEvent.dwThreadId, DBG_CONTINUE
.endif
.endif
invoke ContinueDebugEvent, DBEvent.dwProcessId,DBEvent.dwThreadId, DBG_EXCEPTION_NOT_HANDLED
.endw
;********************************************************************
; 结束线程
;********************************************************************
invoke CloseHandle, pi.hThread
invoke CloseHandle, pi.hProcess

popad
ret
end start