ASPROTECT 2.x 脱壳系列(一)
【目 标】:DVD Fab Gold 2.9.4.2
【工 具】:Olydbg1.1(diy版)、LORDPE、ImportREC1.6F
【任 务】:简单的脱壳
【操作平台】:Windows XP sp2
【作 者】: LOVEBOOM[DFCG][FCG][US]
【相关链接】: 自己搜索下
【简要说明】: 时间在不断的减少,人越靠近死亡的边缘.人在死亡的边缘线上挣扎.寻求人间仅有的希望,骤觉得到了,又已失去了:-(.再虚幻的网络都要回到现实,但愿大家的现实比虚幻过的更充实.
ASPROTECT比起以前的版本可算是进步不少,脱壳难度大了不少,回头看看自己,感觉太失败了,别人进步了这么多,自己却还在原地踏步走:-(
【详细过程】:
想脱这个壳的话,建议对他的历史版本有所了解,那样对看起文章来不会那么吃力.asprotect 2.x惯用的伎俩:2.x版本anti-debug方面没有什么新变化,很多人说这是这个壳的失败之外,我倒觉得说其是失败之处,倒不如说是作者别有用心:-).代码混淆处理方面做了很大功夫,变形处理和1.23可以说基本不是一个层次了,新版不再是只抽程序入口代码,程序入口代码那一个段基本上都抽光了,其它地方也是抽的很利害.输入表也处理的很难修复.我这次挑的目标是没有抽入口代码的程序.所以相对简单些.
用OD载入目标,去除调试标志.打开内存异常项,通过N个异常后到程序的入口:
00ADAF43 64:8921 MOV DWORD PTR FS:[ECX], ESP
00ADAF46 C601 A6 MOV BYTE PTR DS:[ECX], 0A6 ; 最后一次异常
00ADAF49 5B POP EBX ; 00B4AC90
.......
00ADB09B E8 807AFEFF CALL 00AC2B20
00ADB0A0 E8 73CAFFFF CALL 00AD7B18 ; 直接在这里下断然后直接这里f2断点
00ADB0A5 83C4 2C ADD ESP, 2C
00ADB0A8 5D POP EBP ; 00B4AC90
最后一个异常发生时,在00adb0a0处下f2断点,然后shift+f9,断下后f7进去,进去之后CTRL+F9执行到RETURN
00D20000 68 1F1C0627 PUSH 27061C1F ; 进到这里
00D20005 66:81C0 96FC ADD AX, 0FC96
.......
00D2011C /0F85 0F000000 JNZ 00D20131
00D20122 |B0 6A MOV AL, 6A
00D20124 |E9 3E000000 JMP 00D20167 ; f4运行到这里
00D20129 |40 INC EAX
......
00D20192 2BC3 SUB EAX, EBX
00D20194 5C POP ESP ; DVDFabGo.00400000
00D20195 - FFE0 JMP EAX ; 这里跳去OEP
00D20197 40 INC EAX
当然如果你只是想直接到
oep的话,最后一个异常发生后直接在CODE段下f2断点,再shift+f9就可以了:
0047572E 55 PUSH EBP ; 程序OEP
0047572F 8BEC MOV EBP, ESP
00475731 6A FF PUSH -1
00475733 68 80F14700 PUSH 0047F180
.....
到了OEP后,我们先定位下IAT,观察后发现相关的API函数并没有什么加密处理,IAT:47A000 SIZE:BC4.
api没有什么特别处理,并不表示容易的,在程序里随便找一下就会看到call api到壳里去了。
00475832 E8 C9A78C00 CALL 00D40000
搜索一下发现很多地方都改成这样子了,一个一个去改??,我以前是试过一个一个去改。但那样效率太低了,且容易出错。跟过aspr 2.x的应该知道壳运行时会改地址的,因此在475832处下写入断点,运行中断:
00AE6265 8B45 F4 MOV EAX, DWORD PTR SS:[EBP-C]
00AE6268 8B40 3C MOV EAX, DWORD PTR DS:[EAX+3C]
00AE626B 8B55 FC MOV EDX, DWORD PTR SS:[EBP-4] ; 传递正确的API到EDX中
00AE626E E8 D1130000 CALL 00AE7644 ; 因是壳会检测相关代码,进去这里面后有地方不会被检测的
--------------------进入第一层----------------------------
00AE7644 55 PUSH EBP
00AE7645 8BEC MOV EBP, ESP
00AE7647 83C4 E4 ADD ESP, -1C
00AE764A 53 PUSH EBX
00AE764B 56 PUSH ESI
00AE764C 57 PUSH EDI
00AE764D 894D F4 MOV DWORD PTR SS:[EBP-C], ECX
00AE7650 8955 F8 MOV DWORD PTR SS:[EBP-8], EDX ; kernel32.GetModuleHandleA
00AE7653 8945 FC MOV DWORD PTR SS:[EBP-4], EAX
00AE7656 33C0 XOR EAX, EAX
00AE7658 8945 F0 MOV DWORD PTR SS:[EBP-10], EAX
00AE765B B8 00070000 MOV EAX, 700 ; 这里还有代码检测的,再次进入
00AE7660 E8 DFAEFDFF CALL 00AC2544
00AE7665 8945 E4 MOV DWORD PTR SS:[EBP-1C], EAX
----------------------------------进入第二层------------------------------------------
00AC2544 85C0 TEST EAX, EAX
00AC2546 74 0A JE SHORT 00AC2552 ; 经跟踪发现这里的条件判断在这个程序里是不会跳转的
00AC2548 FF15 18A0AE00 CALL DWORD PTR DS:[AEA018] ; 这里的代码也不会被检测到,因此我把这里做为我们的突破点
00AC254E 09C0 OR EAX, EAX
00AC2550 74 01 JE SHORT 00AC2553
00AC2552 C3 RETN
00AC2553 B0 01 MOV AL, 1
---------------------------------------------------------------------------------------
--------------------END----------------------------
00AE6273 8945 FC MOV DWORD PTR SS:[EBP-4], EAX
00AE6276 8B45 E0 MOV EAX, DWORD PTR SS:[EBP-20]
00AE6279 8B00 MOV EAX, DWORD PTR DS:[EAX]
00AE627B E8 D0E6FFFF CALL 00AE4950
00AE6280 8BD0 MOV EDX, EAX
00AE6282 0255 DF ADD DL, BYTE PTR SS:[EBP-21]
00AE6285 8B4D FC MOV ECX, DWORD PTR SS:[EBP-4]
00AE6288 8B45 F4 MOV EAX, DWORD PTR SS:[EBP-C]
00AE628B E8 80040000 CALL 00AE6710
00AE6290 8945 FC MOV DWORD PTR SS:[EBP-4], EAX
00AE6293 8B45 F4 MOV EAX, DWORD PTR SS:[EBP-C]
00AE6296 8B40 24 MOV EAX, DWORD PTR DS:[EAX+24]
00AE6299 8B55 F4 MOV EDX, DWORD PTR SS:[EBP-C]
00AE629C 0382 E0000000 ADD EAX, DWORD PTR DS:[EDX+E0]
00AE62A2 0145 1C ADD DWORD PTR SS:[EBP+1C], EAX
00AE62A5 8B45 FC MOV EAX, DWORD PTR SS:[EBP-4]
00AE62A8 2B45 1C SUB EAX, DWORD PTR SS:[EBP+1C] ; DVDFabGo.00475832
00AE62AB 83E8 05 SUB EAX, 5
00AE62AE 8B55 1C MOV EDX, DWORD PTR SS:[EBP+1C] ; DVDFabGo.00475832
00AE62B1 42 INC EDX ; DVDFabGo.00475833
00AE62B2 8902 MOV DWORD PTR DS:[EDX], EAX ; 这里断下,写入call的地址
00AE62B4 EB 01 JMP SHORT 00AE62B7
00AE62B6 E8 8B45F883 CALL 84A6A846
找到突破点后,再找这段代码的出口,tc command is:POPFD,条件符合后中断:
00D500B0 9D POPFD ; 条件中断在这里
00D500B1 5C POP ESP
00D500B2 F3: PREFIX REP: ; Superfluous prefix
00D500B3 EB 02 JMP SHORT 00D500B7
00D500B5 CD 20 INT 20
00D500B7 FF6424 FC JMP DWORD PTR SS:[ESP-4] ; 这里跳去执行原程序的call api,注意这里已经处理过了,不再是简单的call api了
00D500BB F3: PREFIX REP: ; Superfluous prefix
00D500BC EB 02 JMP SHORT 00D500C0
00D500BE CD 20 INT 20
再找一个空闲的地方写上修复代码,考虑到代码可能会长一点,选择代码后的空白地址479bc0修复代码起始地址。
总结一下大概为:
OEP: 47572E
IAT: 47A000
SIZE: BC4
Patch点一: 00AC2548
Patch点二: 00D500B7(这个地址每次运行都会改变的)
Patch起始地址: 479BC0
现在唯一没有解决的问题就是,都有哪些地方改成了CALL 00D40000,当然,可以通过比较死的方法,直接去搜索,我觉得那样可能没有那么准确,所以我选择了直接记录的方式。
重新加载,载入后,两次GetModuleHandleA中断后,让壳的代码完全解出来:
00AF25C2 /75 08 JNZ SHORT 00AF25CC
00AF25C4 |B8 01000000 MOV EAX, 1
00AF25C9 |C2 0C00 RETN 0C
00AF25CC \68 A08FAE00 PUSH 0AE8FA0 ; 壳的执行代码执行点
00AF25D1 C3 RETN
......
00AE8FA0 55 PUSH EBP ; 壳的代码
00AE8FA1 8BEC MOV EBP, ESP
00AE8FA3 83C4 B4 ADD ESP, -4C
壳的代码完全解出来后,搜索命令
MOV [EBP],EAX
PUSH 0A
找到相关位置后下硬件执行断点。执行后中断:
00AE653F 8945 00 MOV DWORD PTR SS:[EBP], EAX ; 找到这里,ebp-1就正好是call d40000的地址
00AE6542 6A 0A PUSH 0A
00AE6544 E8 63C4FEFF CALL 00AD29AC
00AE6549 8BC8 MOV ECX, EAX
现在要要找的就是存放这些地址的空间,ASPR加壳的程序,最后一个段是空的,我们可以利用下,我选择514100开始保存相关数据:
514100保存将要保存相关call地址的实际地址,514108保存call [address]中的address,514100开始保存相关call 00d40000的实际地址。好了,现在写上一段代码来保存相关数据:
00AE653F /EB 43 JMP SHORT 00AE6584 ; 跳去执行我们的代码
00AE6541 |90 NOP
00AE6542 |6A 0A PUSH 0A
00AE6544 |E8 63C4FEFF CALL 00AD29AC
00AE6549 |8BC8 MOV ECX, EAX
00AE654B |038B E4000000 ADD ECX, DWORD PTR DS:[EBX+E4]
00AE6551 |8BD6 MOV EDX, ESI
00AE6553 |8BC3 MOV EAX, EBX
00AE6555 |E8 9EE5FFFF CALL 00AE4AF8
00AE655A |FF0C24 DEC DWORD PTR SS:[ESP]
00AE655D |03B3 E4000000 ADD ESI, DWORD PTR DS:[EBX+E4]
00AE6563 |833C24 00 CMP DWORD PTR SS:[ESP], 0
00AE6567 ^|0F87 55FEFFFF JA 00AE63C2
00AE656D |53 PUSH EBX ; 写好代码后直接在这里F2断点
00AE656E |E8 5D000000 CALL 00AE65D0
00AE6573 |0183 EC000000 ADD DWORD PTR DS:[EBX+EC], EAX
00AE6579 |B0 01 MOV AL, 1
00AE657B |83C4 24 ADD ESP, 24
00AE657E |5D POP EBP ; DVDFabGo.004033AD
00AE657F |5F POP EDI
00AE6580 |5E POP ESI
00AE6581 |5B POP EBX
00AE6582 |C3 RETN
00AE6583 |90 NOP
00AE6584 \53 PUSH EBX ; 保存堆栈
00AE6585 51 PUSH ECX
00AE6586 B9 00415100 MOV ECX, 514100 ; 起始地址
00AE658B 8339 00 CMP DWORD PTR DS:[ECX], 0
00AE658E 75 06 JNZ SHORT 00AE6596
00AE6590 C701 10415100 MOV DWORD PTR DS:[ECX], 514110 ; 如果是第一次则写入相关数据
00AE6596 8B19 MOV EBX, DWORD PTR DS:[ECX]
00AE6598 4D DEC EBP ; DVDFabGo.004033AD
00AE6599 892B MOV DWORD PTR DS:[EBX], EBP ; 保存call的地址
00AE659B 83C3 04 ADD EBX, 4
00AE659E 8919 MOV DWORD PTR DS:[ECX], EBX ; 保存下次保存数据的地址
00AE65A0 45 INC EBP ; DVDFabGo.004033AD
00AE65A1 59 POP ECX
00AE65A2 5B POP EBX
00AE65A3 8945 00 MOV DWORD PTR SS:[EBP], EAX ; 执行壳原来的代码
00AE65A6 ^ EB 9A JMP SHORT 00AE6542
写完代码后直接在00AE656D下F2断点,断下后,还原patch代码,514100处的数据保存起来(主要是方便一次操作不成功,第二次不用再写代码获取相关数据,当然其实完全可以一次操作成功的)。
获取到了相关的数据后,运行到OEP处,然后就可以直接写上完整的修复代码了,在前面总结的479BC0处写上修复代码:
00479BC0 . 60 PUSHAD ; 保护堆栈,直接定位到这里
00479BC1 . B9 10415100 MOV ECX, 00514110 ; 把call 00d40000改成call ds:[addr]
00479BC6 > 8B19 MOV EBX, DWORD PTR DS:[ECX] ; 取出相关地址
00479BC8 . 83FB 00 CMP EBX, 0 ; 判断是否处理完了
00479BCB . 74 15 JE SHORT 00479BE2
00479BCD .- FFE3 JMP EBX ; 执行原call
00479BCF . 8B15 08415100 MOV EDX, DWORD PTR DS:[514108] ; [514108]就是保存call [address] 中的address
00479BD5 . 66:C703 FF15 MOV WORD PTR DS:[EBX], 15FF ; 修复成call ds:[address]
00479BDA . 8953 02 MOV DWORD PTR DS:[EBX+2], EDX ; 填上实际的address
00479BDD . 83C1 04 ADD ECX, 4
00479BE0 .^ EB E4 JMP SHORT 00479BC6
00479BE2 > 33C0 XOR EAX, EAX ; 这里开始修复call [addr]中实际上是jmp [addr]的部分
00479BE4 . B0 E8 MOV AL, 0E8
00479BE6 . BF 00104000 MOV EDI, <ModuleEntryPoint> ; 代码段的起始地址
00479BEB . B9 B89B0600 MOV ECX, 69BB8 ; 大小
00479BF0 > F2:AE REPNE SCAS BYTE PTR ES:[EDI]
00479BF2 . 83F9 00 CMP ECX, 0
00479BF5 . 74 3C JE SHORT 00479C33 ; 如果处理完则结束过程
00479BF7 . 8B1F MOV EBX, DWORD PTR DS:[EDI]
00479BF9 . 8D5C3B 04 LEA EBX, DWORD PTR DS:[EBX+EDI+4]
00479BFD . 81FB 00104000 CMP EBX, <ModuleEntryPoint>
00479C03 .^ 72 EB JB SHORT 00479BF0
00479C05 . 81FB BA9B4700 CMP EBX, 00479BBA
00479C0B .^ 77 E3 JA SHORT 00479BF0
00479C0D . 66:813B FF15 CMP WORD PTR DS:[EBX], 15FF
00479C12 .^ 75 DC JNZ SHORT 00479BF0
00479C14 . 817B 02 00A04>CMP DWORD PTR DS:[EBX+2], 0047A000 ; 再次准确的判断是否为真的要修改的代码
00479C1B .^ 72 D3 JB SHORT 00479BF0
00479C1D . 817B 02 C0AB4>CMP DWORD PTR DS:[EBX+2], 0047ABC0
00479C24 .^ 77 CA JA SHORT 00479BF0
00479C26 . 66:C703 FF25 MOV WORD PTR DS:[EBX], 25FF
00479C2B . 83C7 04 ADD EDI, 4
00479C2E . 83E9 04 SUB ECX, 4
00479C31 .^ EB BD JMP SHORT 00479BF0
00479C33 > 61 POPAD ; 还原现场
00479C34 . 00 DB 00
00479C35 . 00 DB 00 ; 因为我是操作边写的,这里留多点空间方便修改代码
00479C36 . 00 DB 00
00479C37 . 00 DB 00
00479C38 . 00 DB 00
00479C39 . 00 DB 00
00479C3A . 00 DB 00
00479C3B . 00 DB 00
00479C3C . 00 DB 00
00479C3D . 00 DB 00
00479C3E . 00 DB 00
00479C3F . 00 DB 00
00479C40 . 00 DB 00
00479C41 . 00 DB 00
00479C42 . 00 DB 00
00479C43 . 00 DB 00
00479C44 . 00 DB 00
00479C45 . 00 DB 00
00479C46 . 00 DB 00
00479C47 . 00 DB 00
00479C48 . 00 DB 00
00479C49 . 00 DB 00
00479C4A . 00 DB 00
00479C4B . 00 DB 00
00479C4C . 00 DB 00
00479C4D . 00 DB 00
00479C4E . 00 DB 00
00479C4F . 00 DB 00
00479C50 . 00 DB 00
00479C51 . 00 DB 00
00479C52 . 00 DB 00
00479C53 . 00 DB 00
00479C54 . 00 DB 00
00479C55 . 00 DB 00
00479C56 . 00 DB 00
00479C57 . 00 DB 00
00479C58 . 00 DB 00
00479C59 . 00 DB 00
00479C5A . 00 DB 00
00479C5B . 00 DB 00
00479C5C . 00 DB 00
00479C5D . 00 DB 00
00479C5E . 00 DB 00
00479C5F . 00 DB 00
00479C60 . 00 DB 00
00479C61 . 00 DB 00
00479C62 . 00 DB 00
00479C63 . 00 DB 00
00479C64 . 00 DB 00
00479C65 . 00 DB 00
00479C66 . 00 DB 00
00479C67 . 00 DB 00
00479C68 . 00 DB 00
00479C69 . 00 DB 00
00479C6A . 00 DB 00
00479C6B . 00 DB 00
00479C6C . 00 DB 00
00479C6D . 00 DB 00
00479C6E . 00 DB 00
00479C6F . 00 DB 00
00479C70 789C4700 DD DVDFabGo.00479C78
00479C74 00 DB 00
00479C75 90 NOP
00479C76 90 NOP
00479C77 90 NOP
00479C78 . 60 PUSHAD
00479C79 . 8BC2 MOV EAX, EDX
00479C7B . B9 C80B0000 MOV ECX, 0BC8 ; iat大小
00479C80 . BF 00A04700 MOV EDI, 0047A000 ; iat起始地址
00479C85 . F2:AF REPNE SCAS DWORD PTR ES:[EDI]
00479C87 . 83EF 04 SUB EDI, 4
00479C8A . 893D 08415100 MOV DWORD PTR DS:[514108], EDI ; 保存地址
00479C90 . 61 POPAD
00479C91 . FF15 18A0AE00 CALL DWORD PTR DS:[AEA018] ; 执行程序的原代码
00479C97 . C3 RETN
00479C98 90 NOP
再把这里的代码改一下:
00AC2548 FF15 709C4700 CALL DWORD PTR DS:[479C70] ; DVDFabGo.00479C78
写到这里,完了吗?当然没有了,认真看就会发前的的jmp ebx那里是个call,直接这样操作就回不来了,这也就是为什么前面还用一个patch2,
直接到00D500B7看看去,看看现在在哪里了。
00D500E1 9D POPFD
00D500E2 5C POP ESP ; 00A80000
00D500E3 - FF6424 FC JMP DWORD PTR SS:[ESP-4] ; 这次在这里了
00D500E7 CC INT3
直接在D500E3处下硬件断点,没错了,我想在这里的时候把返回地址改成跳去00479BCF,直接修改代码?当然不行了,壳会检测的,找到地方去除检测?我偷懒处理下,用脚本就很简单的搞定了:
//fixed aspr 2.x
var addr
start:
run
l1:
cmp eip,00D500E3
jne l2
mov addr,esp
sub addr,4
mov [addr],479bcf
jmp start
l2:
ret
写好后,记得在479c33处下个f2断,再运行,否则运行脚本后会"飞"的。
脚本运行完毕,赶快dump完,修复收工吧(做完后要记得把修复代码和保存的数据给清除掉哦,做事要有头有尾才行的).嗯,脱壳完毕,当然这里实际上还是有点小问题的,问题解决方法请参考我的第二篇ASPR文章
Greetz:
Fly.Jingulong,yock,tDasm.David.hexer,hmimys,ahao.UFO(brother).alan(sister).all of my
friends and you!
By loveboom[DFCG][FCG][US]
Email:loveboom#163.com