前言
今天练习脱Armadillo. 记录下流程点.
记录
查壳
PEID => Armadillo 1.xx - 2.xx -> Silicon Realms Toolworks [Overlay] DetectItEasy => Armadillo(6.X-9.X)[-] RDG => Armadillo
脱壳环境
目标程序在原版WinXpSp3虚拟机中启动不了, 可能有虚拟机检测吧. 在真机环境(Win7X64Sp1)中可以直接启动运行. 在52pj虚拟机(WinXpSp3)中可以直接启动运行. 因为是X86程序, 选择在52pj虚拟机中脱壳.
52pjOD太强了, 用异常计数法不行, 去掉OD的异常检测后, F9直接跑起来了. 使用看雪OD+hideOD插件可以用异常计数法脱壳.
查找OEP前的设置
异常检测的设置 重新运行程序, F9跑起来, 目标程序会不断制造异常,这时会停在OD中, 按SHIFT+F9, 将异常返回给目标程序处理, 记录”按SHIFT+F9”的次数, 我这里按了22次, 第23次程序跑起来了.
查找OEP的流程记录
0115CA3E
8900 mov dword ptr [eax], eax
这里写上
22次来这了,重新运行程序,下次来这里,就可以在Memory代码区下F2断点了.
第
23次(SHIFT+F9), 程序就跑起来了
第
22次(SHIFT+F9)返回后,再Memory代码区下F2断点, F9跑起来.
Memory map, 条目
24
地址=
00914000
大小=
00040000 (
262144.)
属主=RegMech
00400000
区段=
.text1
包含=代码
类型=Imag
01001002
访问=R
初始访问=RWE
还会有第
23次(异常来),再按下(SHIFT+F9)继续,这时会来到代码区中(因为对代码区下了F2断点).
0092A202
83C4
04 add esp,
4
F8往下走, 看看OEP在哪?
0092B76C FF15
54AA9600
call dword ptr [
96AA54]
在
0092B76C这里,下一个硬执行断点,重新跑程序,断在这后,F7进入看OEP在哪?
F7进入了
call dword ptr [
96AA54], 再F8找OEP在哪?
0115BF62 FFD7
call edi
去掉硬断点, 在
0115BF62处下一个硬执行断点, 重新跑程序来到这.
F7进入
CALL edi, F8往下走.
0041262C
68 1C364100
push 0041361C
00412631 E8 EEFFFFFF
call 00412624
去掉硬断点, 在
00412631下一个硬执行断点, 重新跑程序来到这(F9一次,
23次SHIFT+F9)
F7进入
call 00412624, F8往下走. 最后又走进
7xx函数去了(见下面的
2个片段<<
0041262C EIP处F7经过的
2个代码片段>>), 像是进了一个框架的接口.
看看
push 0041361C是压入得什么?
0041361C
56 42 35 21 F0
1F
2A
00 00 00 00 00 00 00 00 00 VB5!?*.........
在memory小窗口看到, 压入了
"VB5!"给一个函数, 在VB程序里,只有OEP处是这样的.
看看
call 00412624调用的是哪个函数?
dword ptr [
401358] =
733935A4
看看
733935A4在哪个DLL里面,是哪个函数?
在可执行模块列表中, 可以看到
733935A4在MSVBVM60
.DLL中。
Executable modules, 条目
10
基址=
73390000
大小=
00153000 (
1388544.)
入口=
73391AF8 MSVBVM60.<模块入口点>
名称=MSVBVM60 (系统)
文件版本=
6.00.9802
路径=C:\WINDOWS\system32\MSVBVM60
.DLL
用IDA将MsVBVM60
.dll拖进来,看看
733935A4是啥函数?
在IDA中看到,
733935A4 是 ThunRTMain
.text:733935A4 public ThunRTMain
.text:733935A4 ThunRTMain proc near
.text:733935A4
.text:733935A4 var_64 = dword ptr -
64h
.text:733935A4 StartupInfo = _STARTUPINFOA ptr -
60h
.text:733935A4 var_1C = dword ptr -
1Ch
.text:733935A4 var_18 = dword ptr -
18h
.text:733935A4 var_4 = dword ptr -
4
.text:733935A4 arg_0 = dword ptr
8
.text:733935A4
.text:733935A4
.text:733935A4
.text:733935A4
push ebp
.text:733935A5
mov ebp, esp
.text:733935A7
push 0FFFFFFFFh
.text:733935A9
push offset dword_733A97D0
push "VB5!"
call MSVBVM60
.ThunRTMain
这是VB程序的OEP代码了.
0041262C
68 1C364100
push 0041361C
00412631 E8 EEFFFFFF
call 00412624
最后得到结论
0041262C 是OEP.
去掉所有断点(硬件断点和F2断点), 在
0041262C处下硬件执行断点, 重新运行程序(F9 +
23次SHIFT+F), 来到OEP =
0041262C
0041262C EIP处F7经过的
2个代码片段
00412624 - FF25
58134000 jmp dword ptr [
401358]
733935A4
55 push ebp
733935A5
8BEC
mov ebp, esp
733935A7
6A FF
push -
1
733935A9
68 D0973A73
push 733A97D0
733935AE
68 FDBA4773
push 7347BAFD
733935B3
64:A1
00000000 mov eax, dword ptr fs:[
0]
733935B9
50 push eax
733935BA
64:
8925 0000000>
mov dword ptr fs:[
0], esp
733935C1
51 push ecx
733935C2
51 push ecx
733935C3
83EC
4C
sub esp,
4C
733935C6
53 push ebx
733935C7
56 push esi
733935C8
57 push edi
733935C9
8965 E8
mov dword ptr [ebp-
18], esp
733935CC
8B75
08 mov esi, dword ptr [ebp+
8]
733935CF
8935 70E84973 mov dword ptr [
7349E870], esi
733935D5
8365 FC
00 and dword ptr [ebp-
4],
0
733935D9
8D45 A0 lea eax, dword ptr [ebp-
60]
733935DC
50 push eax
733935DD FF15 A0103973
call dword ptr [
733910A0]
733935E3 0FB745 D0 movzx eax, word ptr [ebp-
30]
733935E7 A3
6CE84973
mov dword ptr [
7349E86C], eax
733935EC FF35 D8E74973
push dword ptr [
7349E7D8]
733935F2
56 push esi
733935F3 BE
70E44973 mov esi,
7349E470
733935F8
8BCE
mov ecx, esi
733935FA
90 nop
733935FB E8
5C000000
call 7339365C
脱壳
脱壳前的设置:
* 将异常检测选项恢复,都打上勾.
* 将hideod开启
* 在内存列表中,将目标程序的区段,全部改为全部可访问的属性.
用OllyDump脱2个版本(不带引入表修复和带引入表修复).
发现带引入表修复的dump操作运行后, OD的UI挂掉了(没反应)
根据这种场景,到了OEP之后, 只dump一个不带引入表修复的版本出来, 或者将OD最小化之后,等一会(1分钟左右), 等OD醒过来.
直接运行脱壳后的程序,报错:"不是有效的Win32程序"
修复导入表
使用ImpREC
* OEP填入 0001262C, 点击AutoSearch.
* 点击"Get Imports"
发现 rva = 00001048处的函数不识别, 其他函数都正常.
无效函数上面的函数是 : _
_vbaStrVarMove
无效函数下面的函数是 : __vbaFreeVarList
综合上面文的msvbvm60的API ord, 并不是按照顺序排列的, 也不好猜这个API的序号.
右击这个无效函数, 在菜单中选择反汇编, 看反汇编,也看不出是什么API.
选择HexView, 将函数的字节码抄下来,在msvbvm60.dll中搜索,看看是哪个函数?
有可能壳, 将这个API代码偷去了, 他不可能是自己写吧?
0113F261 55 8B EC 6A FF 68 D8 26 16 01 68 50 0D 16 01 64
0113F271 A1 00 00 00 00 50 64 89 25 00 00 00 00 83 EC 10
在OD中切换到msvbvm60.dll中,搜索这32个字节码(2进制字符串查找)
没找到这32个特征码
在目标程序中找到了几个vb函数的调用点
008F2C7E . FF15 14144000 call dword ptr [401414] ; MSVBVM60._
_vbaFreeStr
008F2C84 . C745 FC 04000>mov dword ptr [ebp-4], 4
008F2C8B . 68 00080000 push 800
008F2C90 . FF15 8C114000 call dword ptr [40118C] ; MSVBVM60.rtcSpaceBstr
008F2C96 . 8BD0 mov edx, eax
008F2C98 . 8D4D C4 lea ecx, dword ptr [ebp-3C]
008F2C9B . FF15 D0134000 call dword ptr [4013D0] ; MSVBVM60.__vbaStrMove
去IAT看一下.
00401040 734768DF MSVBVM60._
_vbaLenBstr
00401044 73471766 MSVBVM60.__vbaStrVarMove
00401048 0113F261
0040104C 73497262 MSVBVM60.__vbaFreeVarList
00401050 734800FA MSVBVM60._adj
_fdiv_m64
果真,00401048就是被偷了一个Vb的API.
重新跑程序,在00401048下硬件写入断点,看看被偷走之前,写的是哪个API?
现在只有2个断点
* OEP 硬件执行断点
* 00401048 硬件写入断点(DWORD)
重新跑程序.
no.1 00401048 660CBDA8
no.2 00401048 0113F261
当被写时,跟出去,发现了写IAT的代码.
01157ACD E8 14920000 call 01160CE6 ; jmp 到 msvcrt.memcpy
01157AD2 83C4 0C add esp, 0C
01157AD5 8D85 B4C4FFFF lea eax, dword ptr [ebp-3B4C]
01157ADB 50 push eax
01157ADC FFB5 B4C4FFFF push dword ptr [ebp-3B4C]
01157AE2 FFB5 BCC4FFFF push dword ptr [ebp-3B44]
01157AE8 8B85 00C7FFFF mov eax, dword ptr [ebp-3900]
01157AEE 0385 B8C4FFFF add eax, dword ptr [ebp-3B48]
01157AF4 50 push eax
01157AF5 FF15 5C211601 call dword ptr [116215C] ; kernel32.VirtualProtect
01157AFB 8B85 C0C4FFFF mov eax, dword ptr [ebp-3B40]
01157B01 8985 C09EFFFF mov dword ptr [ebp+FFFF9EC0], eax
01157B07 FFB5 C09EFFFF push dword ptr [ebp+FFFF9EC0]
01157B0D E8 CE910000 call 01160CE0 ; jmp 到 msvcrt.operator delete
011575AC 6A 01 push 1
为了跟踪00401048谁写的, 对00401044下硬件写入断点. 等00401044被写入时, 再仔细跟踪00401048怎么写.
01158D02 8B8D 58B9FFFF mov ecx, dword ptr [ebp+FFFFB958] ; 这里是系统API地址
堆栈 ss:[00128638]=0113F261
ecx=00127D28, (ASCII "_
_vbaEnd")
00127D28 5F 5F 76 62 61 45 6E 64 00 00 00 00 00 00 00 00 _
_vbaEnd........
00128638 61 F2 13 01 00 00 00 00 5F 5F 76 62 61 45 6E 64 a?...._
_vbaEnd
00128648 00 00 00 00 00 00 00 00 72 00 00 00 00 00 00 00 ........r.......
IAT中最终填的值是0113F261, 经过这里猜测, 对应的功能是_
_vbaEnd
00128638这里好像一个结构体, 前面放着函数指针,后面放着这个指针对应的函数名称.
先按IAT项 0113F261 为_
_vbaEnd,修复IAT试试.
在ImpREC中, 双击无效项,在弹出的API列表中选择__vbaEnd, 点击确定, 导入函数都有效了.
用ImpREC修复前面dump出来的2个版本.
ImpREC提示, 不能添加任何节.
用PETools重建PE(前面dump出来的2个版本).
再用ImpREC进行IAT修复, IAT修复成功.
测试脱壳后的程序
2个版本都可用, 开心啊:)
简便脱壳方法-CreateThread硬件执行断点
有的cm, 用异常计数法,次数非常多(e.g. 将近100次), 需要换CreateThread硬断点法. 这时, 对CreatThreaad下硬件断点, 返回用户领空后, 下面不远处,就是进入OEP的call.
做试验, 还是用的同一个cm, 注释都标注好了(做异常计数法时标注好的) 可以验证, 这种方法找OEP同样正确. 采用这种方法的原因是: 找个异常计数法次数少的cm(也是这种壳), 可以看到OEP处是执行了CreateThread之后过来的.
将OD异常检测选项都勾上, 对CreateThread下硬件执行断点. F9将程序跑起来, 断在CreateThread.
7C8106C7 kernel32
.CreateThread 8BFF
mov edi, edi
7C8106C9
55 push ebp
7C8106CA
8BEC
mov ebp, esp
7C8106CC FF75
1C
push dword ptr [ebp+
1C]
7C8106CF FF75
18 push dword ptr [ebp+
18]
7C8106D2 FF75
14 push dword ptr [ebp+
14]
7C8106D5 FF75
10 push dword ptr [ebp+
10]
7C8106D8 FF75
0C
push dword ptr [ebp+C]
7C8106DB FF75
08 push dword ptr [ebp+
8]
7C8106DE
6A FF
push -
1
7C8106E0 E8 D7FDFFFF
call CreateRemoteThread
7C8106E5
5D
pop ebp
7C8106E6 C2
1800 retn
18
011410FC FF15 A0211601
call dword ptr [11621A0] ; kernel32.CreateThread
01141102 5E pop esi ; RegMech.0096AA30 一路F8返回到调用点
01141103 C9 leave
01141104 C3 retn
0115BEC7 A1 D81E1701
mov eax, dword ptr [
1171ED8]
0115BECC
59 pop ecx
0115BECD
8B48
68 mov ecx, dword ptr [eax+
68]
0115BED0
3348 64 xor ecx, dword ptr [eax+
64]
0115BED3
3348 5C xor ecx, dword ptr [eax+
5C]
0115BED6 F6C1
40 test cl,
40
0115BED9
75 08 jnz short
0115BEE3
0115BEDB
6A
01 push 1
0115BEDD E8
15BEFDFF
call 01137CF7
0115BEE2
59 pop ecx
0115BEE3
53 push ebx
0115BEE4 C705 E0791601 A>
mov dword ptr [
11679E0],
11687A4
0115BEEE E8
22EAFDFF
call 0113A915
0115BEF3
59 pop ecx
0115BEF4 E8
8BE7FEFF
call 0114A684
0115BEF9
8BF8
mov edi, eax
0115BEFB A1 D81E1701
mov eax, dword ptr [
1171ED8]
0115BF00
8B48
7C
mov ecx, dword ptr [eax+
7C]
0115BF03
3348 68 xor ecx, dword ptr [eax+
68]
0115BF06
3348 10 xor ecx, dword ptr [eax+
10]
0115BF09
03F9
add edi, ecx
0115BF0B
8B0E
mov ecx, dword ptr [esi]
0115BF0D
3BCB cmp ecx, ebx
0115BF0F
75 2F jnz short
0115BF40
0115BF11
8B78
68 mov edi, dword ptr [eax+
68]
0115BF14 E8
6BE7FEFF
call 0114A684
0115BF19
8B0D D81E1701
mov ecx, dword ptr [
1171ED8]
0115BF1F FF76
14 push dword ptr [esi+
14]
0115BF22
8B51
7C
mov edx, dword ptr [ecx+
7C]
0115BF25 FF76
10 push dword ptr [esi+
10]
0115BF28
3351 10 xor edx, dword ptr [ecx+
10]
0115BF2B FF76
0C
push dword ptr [esi+C]
0115BF2E
33D7 xor edx, edi
0115BF30
03C2
add eax, edx
0115BF32
8B51
34 mov edx, dword ptr [ecx+
34]
0115BF35
3351 20 xor edx, dword ptr [ecx+
20]
0115BF38
33D7 xor edx, edi
0115BF3A
2BC2
sub eax, edx
0115BF3C FFD0
call eax
0115BF3E EB
24 jmp short
0115BF64
0115BF40
83F9
01 cmp ecx,
1
0115BF43
75 22 jnz short
0115BF67
0115BF45 FF76
04 push dword ptr [esi+
4]
0115BF48 FF76
08 push dword ptr [esi+
8]
0115BF4B
53 push ebx
0115BF4C E8
33E7FEFF
call 0114A684
0115BF51
50 push eax
0115BF52 A1 D81E1701
mov eax, dword ptr [
1171ED8]
0115BF57
8B48
68 mov ecx, dword ptr [eax+
68]
0115BF5A
3348 34 xor ecx, dword ptr [eax+
34]
0115BF5D
3348 20 xor ecx, dword ptr [eax+
20]
0115BF60
2BF9
sub edi, ecx
0115BF62 FFD7
call edi
0115BF64
8945 FC
mov dword ptr [ebp-
4], eax
0115BF67
8B45 FC
mov eax, dword ptr [ebp-
4]
0115BF6A
5F
pop edi
0115BF6B
5E
pop esi
0115BF6C
5B
pop ebx
0115BF6D C9 leave
0115BF6E C3 retn
0115BF62处的call edi, F7进去后就是OEP
0041262C
68 1C364100
push 0041361C
00412631 E8 EEFFFFFF
call 00412624
00412636 0000 add byte ptr [eax], al
后面的IAT修复,PE重建过程同异常计数法. 这壳用OllyDump脱的时候, 只能选非修复引用表方式. 如果选修复引用表方式, OD会挂掉, 不一定能恢复正常. 当ImpREC得到的导入函数没得到时,可以尝试在无效项上右击菜单 => Trace_Level1(Disasm) ImpREC按照导入函数反汇编去找导入函数名称时, 有时能找对几个. 像这个cm, 则找的不对(将vbApi找成了GetModuleHandleA),遇到这种情况, 那就要自己去找缺失的导入函数名称(异常计数法里,找缺失的导入函数的方法,是通过追踪IAT里填导入函数地址的反汇编实现来做的,这是最朴素,最靠谱的方法).