标 题:Unpack Acprotect 1.21 (一) dump
发信人:jingulong
时 间:2004年2月25日 01:31
详细信息:
这几天比较忙,看雪这里也来得少了。看见偶成为“斑竹”,恐慌之余想想,唯有努力吧。
看到本版关于Acpr的帖子不少,也把自己的一点心得拿出来献丑,也算祝贺“脱壳论坛”的成立。
本文计划分四部分:一. dump、二. IAT 修复(不添加段)、三. 程序修复、四. 跨系统问题,由于时间的关系,打算一段一段的贴出。
(系统 WIN2K)
一、dump
1.躲过acpr的检查
用od(1.1b)载入程序,Command处输入:d fs:[30]+2,回车后将Hex dump窗口中左上角的 1改成 0(这就相当于插件IsDebugPresent选
hide啦);在Command处又输入 bp Process32First+1,回车。F9执行程序,断点处中断后去掉断点,Alt+F9返回程序“领空”,F8两下后来到
006E1A4C 3B95 B3D44000 CMP EDX,DWORD PTR SS:[EBP+40D4B3]
这里[EBP+40D4B3]处存放的是程序的ProcessId,我们把它改成其父进程(OllDbg)的ProcessId,(用工具pupe)找到OllDbg的Id后替换该处
数据。
(当然,如果使用 Fly的修改版这一步就省了,不过知其所以不是更好吗!再加上od升级后 Fly的新版还在加工中吧。)
2.到伪oep
这步就更快了,按下Alt+M,在地址401000 (size 00CC000)处下F2,F9执行程序!
中断了,呵呵,在406EDC处!这里是非常熟悉的代码!!我们正处于Delphi程序的第一个call之中!!!
00406EDC PUSH EBX
00406EDD MOV EBX,EAX
00406EDF XOR EAX,EAX
00406EE1 MOV DWORD PTR DS:[4F370C],EAX
00406EE6 PUSH 0
00406EE8 CALL ACProtec.00406E18
........ .........
3.修复oep
Acpr 1.21主程序这次把几乎整个初始化主干代码都偷掉了,不过找回这部分代码也很容易。
停在406edc后看看stack窗口,返回到6ed109 ?!鼠标在stack窗口里选中这行,回车,我们看见了经Acpr处理后“
主干”代码:
006ED0E3 PUSH ESI
006ED0E4 MOV DWORD PTR SS:[ESP],ECX
006ED0E7 PUSH EAX
006ED0E8 MOV EAX,ACProtec.00406EDC
006ED0ED MOV ECX,EAX
006ED0EF POP EAX
006ED0F0 PUSH ECX
006ED0F1 POP DWORD PTR DS:[6D4F0F] ; ACProtec.00406EDC
006ED0F7 POP DWORD PTR DS:[6D4EFF]
006ED0FD MOV ECX,DWORD PTR DS:[6D4EFF]
006ED103 CALL DWORD PTR DS:[6D4F0F] ; ACProtec.00406EDC
006ED109 MOV EAX,DWORD PTR DS:[4F258C]
006ED10E MOV EAX,DWORD PTR DS:[EAX]
006ED110 MOV DWORD PTR DS:[6D4EFB],ACProtec.004622F8
006ED11A PUSH DWORD PTR DS:[6D4EFB]
006ED120 POP DWORD PTR DS:[6D4F0B]
006ED126 CALL DWORD PTR DS:[6D4F0B]
006ED12C MOV DWORD PTR DS:[6D4EF7],EDI
........ ......
006ED1AA MOV DWORD PTR SS:[ESP],ACProtec.00462310
006ED1B1 POP DWORD PTR DS:[6D4F07]
006ED1B7 CALL DWORD PTR DS:[6D4F07]
006ED1BD PUSH DWORD PTR DS:[4F228C] ; ACProtec.004F45E8
006ED1C3 POP DWORD PTR DS:[6D4EE3]
006ED1C9 MOV ECX,DWORD PTR DS:[6D4EE3]
006ED1CF MOV EAX,DWORD PTR DS:[4F258C]
006ED1D4 MOV EAX,DWORD PTR DS:[EAX]
006ED1D6 PUSH DWORD PTR DS:[47F7CC] ; ACProtec.0047F818
006ED1DC POP EDX
006ED1DD PUSH ACProtec.00462310
006ED1E2 POP DWORD PTR DS:[6D4F03] ; ACProtec.004CBFF8
006ED1E8 CALL DWORD PTR DS:[6D4F03] ; ACProtec.004CBFF8
006ED1EE MOV EAX,DWORD PTR DS:[4F258C]
006ED1F3 MOV EAX,DWORD PTR DS:[EAX]
从这段代码容易分析出被“偷”的代码如下:
PUSH EBP
MOV EBP,ESP
ADD ESP,-0C
XOR EAX,EAX
PUSH EAX ; 这条语句可改成 push 0 等
MOV [EBP-4],EAX
MOV EAX,004CBFF8
CALL 00406EDC
MOV EAX,[4F258C]
MOV EAX,[EAX]
CALL 004622F8
MOV ECX,[4F2354]
MOV EAX,[4F258C]
MOV EAX,[EAX]
MOV EDX,[47FB5C]
CALL 00462310
MOV ECX,[4F228C]
MOV EAX,[4F258C]
MOV EAX,[EAX]
MOV EDX,[47F7CC]
CALL 00462310
MOV EAX,[4F258C]
MOV EAX,[EAX]
在代码段尾部的空闲处键入上面的代码,再加上Acpr未偷的两条语句,Oep就算修复了。这两条语句是:
004CC263 CALL ACProtec.00462390
004CC268 CALL ACProtec.004049D8
现在可以 dump 了。
后面将看到,这段“标准”的 oep代码还要根据我们的需要加以修改。
(时间问题,今天就这一段吧)。
标 题:Unpack Acprotect 1.21 (二)修复 IAT
发信人:jingulong
时 间:2004年2月25日 11:15
详细信息:
由于Acpr把ImageSize作为“敏感”数据,因此这一步就不能完全交给ImportRec了,当然,这么好的工具还是要用的。这里分为两步,第一是常规修复 IAT;第二就是移植Import Table。
这其中第一步的确很简单,除了一个地方,Acpr对原来的入口地址仅仅分解为两个值的异或运算结果。找 IAT也很容易,因为是Delphi程序,所以406EE8这行就应该是调用GetModuleHandle函数了,鼠标选中该行后回车,见到:
00406E18 JMP DWORD PTR DS:[691234] ; ACProtec.006D42A7
再回车,又见:
006D42A7 PUSH 66D2D09B
006D42AC XOR DWORD PTR SS:[ESP],11358640
006D42B3 RETN
呵呵,异或后转到 api(这也算加密?有点晕)
OK,根据这里的[691234],知道这应该是Thunk值表的位置,将Dump窗口转到这里稍作调整看到这个表:
00691140 10 40 6D 00 1D 40 6D 00 2A 40 6D 00 37 40 6D 00 始于691140
00691150 44 40 6D 00 51 40 6D 00 5E 40 6D 00 6B 40 6D 00
00691160 78 40 6D 00 85 40 6D 00 92 40 6D 00 9F 40 6D 00
........ ......
00691800 28 26 79 71 5A 8A 79 71 B7 42 79 71 E9 15 78 71
00691810 00 00 00 00 72 C3 5F 77 00 00 00 00 E6 A4 AF 76
00691820 CD A5 AF 76 00 00 00 00 00 00 00 00 00 00 00 00 止于691824
利用异或还原数据吧,程序中空闲处键入:
pusha
mov esi,691140
mov edi,esi
again: lodsd ;这里的行标在od中是地址,下面有行标处一样
test eax,eax
je OK
cmp eax,70000000
ja OK
mov edx,eax
mov eax,[edx+1] ;[edx+1]指向push 的数据
xor eax,[edx+8] ;[edx+8]处是原来xor的数据
cmp eax,70000000 ;应该大于70000000
jb check_it
OK: stosd
cmp esi,691824
jbe again
jmp over
check_it: nop ; <-- 这里设断,若停在这里,记下edi和eax的值。并在 eax处设断。
over : popa ; <-- 这里设断
以上代码与tDasm的不同之处在于1.还原的是Thunk表以保证所有的api无遗漏;2.有一个是Acpr借用了的Api,必须找出来。
好,执行这程序段吧。过程中两次停在check_it,其edi和eax的值分别是:
edi eax
6911FC 6E084D
691588 6E084D
记下这两组数据(注意eax都一样)修复程序时使用。
下面轮到ImportRec上场了,启动它选中目标后RVA处设为291140,Size处设为700,GetImports!
乖乖,除了有两处外全部OK!这两处就是刚才记下的啦,这个函数无论你用追踪层次几都无济于事。看看吧,都位于user32组中,特别是291588处附近的Api函数以“姓氏笔画为序”,很整齐的。这个未解析出来的函数排在MessageBeep的上方哦,是不是...它?一个商品化的程序不可能没有它!把结果存为文本文件放到记事本中搜索一下,果然没有它---MessageBoxA啊!!!多半是了。也许你要说“又在猜了!”,好吧,刚才不是设断了吗,跟一下那个断点就清楚了,如果这样你会发现这个函数有四个参数(当然咯),跟到最后看到函数有两个出口1.retn 2.jmp eax,这个时候eax的值正好指向MessageBox,至此恢复完毕。(注意,这个就是Acpr借用的“Api”入口,运行时还要填入6E084D这个值的。)这时用ImportRec以通常方式修复IAT(修复后的程序丧失加密功能),结果就是dumped_.exe了。
第二步,利用上面的dumped_.exe移植Impotr Table。这一步操作起来有点考耐心的。
用LordPe查一下Impotr Table的RVA---2F6000,记下后关闭LordPe,od载入dumped_.exe,把Hex dump窗口转到6F6000,把6F6000~6F6130的数据复制到691000~691130,再把6F6140~6F7EF0处的数据复制到691840~6935F0,记下位置差6F6140-691840=64900,用Winhex打开dumped_exe把291140~291830的数据块复制下来,用Winhex的RamEditor把它(以ASCII-Hex方式)粘贴到od载入的dumped_.exe,位置:691140~691830。(希望不要说昏了)
关闭Winhex,在od中找“空地”键入:
pusha
mov esi,691000
mov edi,esi
again: lodsd ;这里的行标在od中是地址,下面有行标处一样
cmp eax,2F0000
jb OK
sub eax,64900 ;64900是位置差哦
OK: stosd
cmp esi,691824
jb again
popa
over: nop ;<- 这里设断,执行这段代码
呵呵,移植完毕。
现在,你可以把内存中的dumped_.exe再dump一次就啦,(dump前最好清除自己的工作代码),然后用LordPe打开新dump的文件把Import Table的RVA改成291000,Save后检查Import Table的内容,会发现最后一个api“看不见”,这是因为文件头中定义该段的RSize小了一点的缘故,用LordPe修改一下就好,RSize和VSize都从25D4改成2600吧。再次Save后检查,allright!
最后轮到去掉ImportRec添加的段了,这用LordPe就可简单搞定。用OD载入试试,是不是有点爽!
(老板叫出差,看来第三篇要耽搁几天了。见谅)
标 题:Unpack Acprotect
1.21 (三)程序修复
发信人:jingulong
时 间:2004年2月29日 09:46
详细信息:
呵呵,又回家了。各位兄弟好!
该说说这第三个问题了。前有shinegood的脱壳版,后有tDasm的修复文章,我这个不会又...?管他的,一则说过的话总要兑现,二则各有各的方法,更何况他们的不是都还有点问题吗。(二者使用了相同的方法来修复)
修复中主要要解决的是Replace Code的地址,一般情况下,程序把Replace Code放到堆中执行,堆的结构由壳代码初
始化时建立,这样,去了壳的程序就会出错。这里采用的方法就是修改放置Replace Code的地址。
od载入已修复iat的程序,隐藏od、忽略所有异常、eip定位到修复的oep、在6d540c处设断,执行程序。断下光标移到6b55b7,按F4,看到:
006D55B7 CALL Acprotect.006E1460
006D55BC MOV EAX,DWORD PTR SS:[ESP+20] ;获得返回地址
006D55C0 XOR ECX,ECX
006D55C2 MOV EBX,DWORD PTR SS:[EBP+ECX*4+4026E2] ;取表中元素
006D55C9 ADD EBX,DWORD PTR SS:[EBP+40D2AA] ;加Image Base
006D55CF CMP EAX,EBX ;
006D55D1 JE SHORT Acprotect.006D55DA ;查表
006D55D3 NOP
006D55D4 NOP
006D55D5 NOP
006D55D6 NOP
006D55D7 INC ECX
006D55D8 JMP SHORT Acprotect.006D55C2 ;再找
006D55DA MOV DWORD PTR SS:[EBP+ECX*4+4026E2],0 ;找到后把这个元素从表中删除
006D55E5 LEA ESI,DWORD PTR SS:[EBP+4055C2] ;取(变形)Replace Code表基地址
006D55EB MOV EAX,0A
006D55F0 MUL ECX
006D55F2 ADD ESI,EAX ;得到本次应取(变形)Replace Code地址
006D55F4 PUSH ESI
006D55F5 PUSH ECX
006D55F6 MOV AL,BYTE PTR SS:[EBP+4023FC] ;取还原Replace Code的操作数
006D55FC OR AL,AL
006D55FE JNZ SHORT Acprotect.006D5628 ;跳,则操作数已经存在
006D5600 NOP
006D5601 NOP
006D5602 NOP
006D5603 NOP
006D5604 MOV EAX,DWORD PTR SS:[EBP+40D2AA] ;
006D560A MOV ESI,DWORD PTR DS:[EAX+3C] ;
006D560D ADD ESI,DWORD PTR SS:[EBP+40D2AA] ;
006D5613 ADD ESI,28 ;
006D5616 LODS DWORD PTR DS:[ESI] ;得到Entry Point值
006D5617 MOV BL,AL
006D5619 ADD BL,AH
006D561B SHR EAX,10
006D561E ADD BL,AL
006D5620 ADD BL,AH
006D5622 MOV BYTE PTR SS:[EBP+4023FC],BL ;BL中为还原Replace Code的操作数
006D5628 POP ECX
006D5629 POP ESI
006D562A PUSHAD
006D562B MOV EAX,2
006D5630 CALL Acprotect.006E11EA
006D5635 OR EAX,EAX ;
006D5637 JNZ SHORT Acprotect.006D565D ;跳,表示把Replace Code放在原来变形码的位置
006D5639 NOP ; .
006D563A NOP ; .
006D563B NOP ; .
006D563C NOP ; .
006D563D POPAD ; .
006D563E MOV EDI,DWORD PTR SS:[EBP+40D2AE] ;否则放到位于[ebp+40d2ae]的指针指向的位置
006D5644 MOV EAX,0A ;一般情况下该指针指向堆中
006D5649 MUL ECX
006D564B ADD EDI,EAX
006D564D MOV ECX,0A
006D5652 MOV BL,BYTE PTR SS:[EBP+4023FC] ;取还原Replace Code的操作数
006D5658 JMP SHORT Acprotect.006D566B
006D565A NOP
006D565B NOP
006D565C NOP
006D565D POPAD
006D565E MOV EDI,ESI
006D5660 MOV ECX,0A
006D5665 MOV BL,BYTE PTR SS:[EBP+4023FC]
006D566B LODS BYTE PTR DS:[ESI] ;取变形Replace Code
006D566C XOR AL,BL ;还原
006D566E STOS BYTE PTR ES:[EDI] ;放置
006D566F LOOPD SHORT Acprotect.006D566B
006D5671 SUB EDI,0A ;得本次Replace Code入口地址
006D5674 PUSH EDI
006D5675 MOV ESI,DWORD PTR SS:[ESP+24]
006D5679 SUB ESI,4
006D567C LODS DWORD PTR DS:[ESI]
006D567D SUB EDI,Acprotect.0040240C
006D5683 SUB EDI,EBP
006D5685 ADD EAX,EDI
006D5687 MOV DWORD PTR DS:[ESI-4],EAX
006D568A POP EDI
006D568B PUSH EDI
006D568C XOR ECX,ECX
006D568E CMP ECX,8
006D5691 JE SHORT Acprotect.006D56A1
006D5693 NOP
006D5694 NOP
006D5695 NOP
006D5696 NOP
006D5697 MOV EAX,DWORD PTR SS:[ESP+ECX*4+4]
006D569B MOV DWORD PTR SS:[ESP+ECX*4],EAX
006D569E INC ECX
006D569F JMP SHORT Acprotect.006D568E
006D56A1 MOV DWORD PTR SS:[ESP+ECX*4],EDI
006D56A4 PUSHAD ;以下是把以上代码变形
006D56A5 CALL Acprotect.006D56AA
006D56AA POP ESI
006D56AB SUB ESI,6
006D56AE MOV ECX,0ED
006D56B3 SUB ESI,ECX
006D56B5 MOV EDX,4923BF69
006D56BA SHR ECX,2
006D56BD SUB ECX,2
006D56C0 CMP ECX,0
006D56C3 JL SHORT Acprotect.006D56DF
006D56C5 MOV EAX,DWORD PTR DS:[ESI+ECX*4]
006D56C8 MOV EBX,DWORD PTR DS:[ESI+ECX*4+4]
006D56CC ADD EAX,EBX
006D56CE ROR EAX,15
006D56D1 XOR EAX,EDX
006D56D3 ADD EDX,3BC3E0C
006D56D9 MOV DWORD PTR DS:[ESI+ECX*4],EAX
006D56DC DEC ECX
006D56DD JMP SHORT Acprotect.006D56C0
006D56DF POPAD
006D56E0 POPAD
006D56E1 RETN ;“回”到Replace Code入口地址
从以上注解可以看出,为了避免程序把Replace Code放到堆中,修改6D5637的jnz 为 jmp就可以了,但是这段代码是变了形的,所以必须作如下处理:
1.6D5637的jnz 为 jmp
2.改6D56A4的代码为 jmp 6d56e0,禁止程序作代码变形
3.把这个函数入口处第二条语句改成 jmp 6d55b7
保存以上修改。
F9执行程序,我这里看到程序出错!F12停下,检查后知是程序试图向堆中不存在的地址作串传输,于是重新载入,修改程序入口处代码,以加大堆的size
PUSH 5EA20
PUSH 40
CALL DWORD PTR DS:[691230] ; kernel32.LocalAlloc
执行上面三条语句后再转到修复的oep处执行,正常了,加壳,测试都没有问题。再加另一个壳,在压缩程序时程序不动了!这个现象和shinegood兄的一样。
想到在修复iat时碰到的MessageBox问题(至今我还这么笨,就没去看看看看Acpr的文档!),想想应该还原这两个函数吧?于是在上面的语句后又加上:
MOV EAX,Acprotect.006E084D
MOV DWORD PTR DS:[6911fc],EAX
MOV DWORD PTR DS:[691588],EAX
再试一试,没问题啦!(就是不能跨系统)
(注意:shinegood脱壳版不能按此修改)