Acprotect学习笔记
本帖最后由 demoscene 于 2010-5-31 22:53 编辑垃圾前言
这个壳早就已经被各位大牛肢解了,而 且也不更新了,现在的软件基本上不会用它来加壳了吧。我写这篇文章只是来过下手瘾
其实Acprotect以前就分析过 了(主要是分析IAT处理),现在写篇文章来作个记录吧。
同样来给 MM.exe加个壳吧,这里我用的是1.41版的来加壳。
好吧,运行一下,MM1.exe能运 行。OD载入。跟一下,发现全是花指令
既然它这么多花指令,那我们就不具体 F7了。我们直接我们的目标,IAT处理分析
忽略所有异常访问,SOD全选
1.1找OEP+修复
先到 OEP去吧.怎么到OEP?
方法很多哈.理解了原理,用哪种方法 看大家的爱好,
这里我用两次内存断点法。在.rsrc 段下内存访问断点.f9运行,断下
现在代码段应该解码了吧
到 00401000去看下
Boolean都出来了,证明已经解 码了
那么接下来程序要到OEP就应该会到 代码段去执行代码了吧
在代码段00401000下内存访问 断点,F9。断下。
咦这里是Delphi程序的笫一个 Call吧。为什么不是断在OEP,看看堆栈
返回到 004A3071 , 这 个004A3071是在哪里呢看看区段信息
原来是在壳段中,那么为什么不会断下 OEP应该知道了吧。
它把程序的OEP移动到壳段而且进行 了变形。我们不管它的变形,我们主要分析它的IAT哈
既然找到了笫一个call那就在 004A306B这里下个硬件执行断点吧。然后重载程序。F9。断下。
我们来修复下OEP,
找个未回味加壳的delphi程序
下面就直接patch代码来修复 OEP
eax的值看下断下后的 eax的值就行了
004A3060新建EIP。取消两 个nop的修改。
OEP修复完毕
1.2,修复IAT
现在跟进去笫一个CALL
004A3060 55 push ebp
004A3061 8BEC mov ebp, esp
004A3063 83C4 F0 add esp, -10
004A3066 B8 4C2B4600 mov eax, 462B4C
004A306B FF15 B5D84800 call dword ptr ; F7跟进
004A3071 A1 E8484600 mov eax, dword ptr
-----------------------------------------------------------------------------
00405C34 53 push ebx ;到这,
00405C35 8BD8 mov ebx, eax
00405C37 33C0 xor eax, eax
00405C39 A3 9C304600 mov dword ptr , eax
00405C3E 6A 00 push 0
00405C40 E8 2BFFFFFF call 00405B70 ; 继续F7跟进
00405C45 A3 64564600 mov dword ptr , eax
00405C4A A1 64564600 mov eax, dword ptr
------------------------------------------------------------------------------
到这里 就是iat 重定向了吧,都定向到壳段去了
先数据窗口中跟随内存地址
0046611000000000
0046611400000000
004661180048D010 MM1.0048D010
0046611C0048D01D MM1.0048D01D
004661200048D02A MM1.0048D02A
004661240048D037 MM1.0048D037
004661280048D044 MM1.0048D044
0046612C0048D051 MM1.0048D051
004661300048D05E MM1.0048D05E
004661340048D06B MM1.0048D06B
004661380048D078 MM1.0048D078
0046613C0048D085 MM1.0048D085
004661400048D092 MM1.0048D092
004661440048D09F MM1.0048D09F
004661480048D0AC MM1.0048D0AC
0046614C0048D0B9 MM1.0048D0B9
004661500048D0C6 MM1.0048D0C6
004661540048D0D3 MM1.0048D0D3
004661580048D0E0 MM1.0048D0E0
0046615C0048D0ED MM1.0048D0ED
004661600048D0FA MM1.0048D0FA
004661640048D107 MM1.0048D107
004661680048D114 MM1.0048D114
0046616C0048D121 MM1.0048D121
004661700048D12E MM1.0048D12E
004661740048D13B MM1.0048D13B
004661780048D148 MM1.0048D148
0046617C0048D155 MM1.0048D155
004661800048D162 MM1.0048D162
004661840048D16F MM1.0048D16F
004661880048D17C MM1.0048D17C
0046618C0048D189 MM1.0048D189
004661900048D196 MM1.0048D196
004661940048D1A3 MM1.0048D1A3
004661980048D1B0 MM1.0048D1B0
0046619C0048D1BD MM1.0048D1BD
004661A000000000
004661A40048D1CA MM1.0048D1CA
004661A80048D1D7 MM1.0048D1D7
004661AC0048D1E4 MM1.0048D1E4
004661B00048D1F1 MM1.0048D1F1
004661B400000000
004661B877DA7ABB advapi32.RegQueryValueExA
004661BC77DA7852 advapi32.RegOpenKeyExA
004661C077DA6C27 advapi32.RegCloseKey
004661C400000000
004661C8770F4880 oleaut32.SysFreeString
004661CC770FA3EC oleaut32.SysReAllocStringLen
004661D0770F4B39 oleaut32.SysAllocStringLen
004661D400000000
004661D80048D1FE MM1.0048D1FE
004661DC0048D20B MM1.0048D20B
这就是被处理后的IAT的,看到有一 些函数被处理了,有一些函数没被处理。
我们跟一个看看吧。这里选择笫一个 0048D010。到0048D010去新建个EIP
看到这是是一段重复的差 不多的代码
我们F7单步走
当我们走到 0048D01C这里的时候我们发现栈顶中出现了真实的API地址
它这里是先push了一个值支栈顶, 然后把这个值和另外一个值进行异或不得到了真实函数的地址.
我们再跟一个函数看下是不是也是这 样,我们就跟笫二个0048D01D,吧,在0048D01D新建EIP,然后跟
发现当走到retn的时候又出现了真 实API的函数地址。
那么我们只要每个函数都这个跟一下到 retn的时候再把栈顶的真实地址写回IAT里,这样就能得找正确的IAT了。
这样手动一个个跟估计我 会xx掉,那 么我们就用脚本来帮我人来完成这个工作
var Old_esp
var cur_addr
var old_eip
mov old_esp,esp ;保存当前的eip,和esp,脚本运行完后我们要还原它们
mov old_eip,eip
mov cur_addr,00466114 ;00450114是IAT的起始地址-4
loop:
mov esp,old_esp ;还原一下esp,在解密真实函数地址的时候会push一个值,我们要还原它
add cur_addr,4 ;IAT 笫一个地址
cmp cur_addr,00466708 ;004516d4是IAT末尾+4
jae exit ;到达 IAT末尾就结束
cmp ,10000000 ;因为有一些函数没有被壳处理掉,所以我们不需要还原
jae loop
cmp ,0 ; 有一些是作为一个dll的结束标志,其值为0,我们不需要处理
je loop
mov eip, ;在目的地址新建EIP
sti ;F7两步到 retn,栈顶会出现真实函数地址
sti
mov ,[esp] ;把真实函数地址写回去
jmp loop
exit:
mov esp,old_esp ;还原esp,eip
mov eip,old_eip
ret
好运行脚本,脚本 跑完后我们看下IAT
发现函数基本都修复完了,不过还有两个还没修复
004661AC0049BE4B MM1.0049BE4B
004664DC0049BE4B MM1.0049BE4B
这两个函数都是指 向 0049BE4B,就是执行同一个函数了,那个我们去0049BE4B新建个EIP,跟一下
0049BE4B 60 pushad
0049BE4C 85DE test esi, ebx
0049BE4E 45 inc ebp
0049BE4F F9 stc
0049BE50 03DE add ebx, esi
0049BE52 50 push eax
0049BE53 E8 01000000 call 0049BE59 ; 0049BE59
0049BE58 - E9 83C40458 jmp 584E82E0
发现这里和刚才的加密方式不同,F7来跟,我这里就不贴代码给大家看了,因为这个加密的没刚
才的那么简单,跟几步就出来了。
这里要跟很久,解密完后,在EAX和 ECX中会出现函数地址
EAX 77D507EA USER32.MessageBoxA
ECX 0012FFC8
EDX 8054C6ED
EBX 77D507EA USER32.MessageBoxA
ESP 0012FFAC
那么我们知道了这个函数就是 MessageBoxA.我们就修改还没修复的两个函数为
MessageBoxA就行了。
IAT修复完毕。
由于在跟MessageBoxA的时 候程序的一些环境已经变了,所以我们重来,这一次就不用跟MessagBoxA了,直接修复。
Lordpe来 dump.Import Rec来修复,直接自动搜索IAT会找不到,需要我们自己填写IAT地址和大小
最后程序运行成功。
最后上个图,此图只为增加人气用,没 其它目的,要不我的文章没人看就不好了
2,IAT处理过程分析
根据Kissy牛的提示,分析了一下它具体处理IAT的过程,谢谢Kissy牛:)
004A147F 03F2 add esi, edx ; esi =00466000 - >IID
0046600000 00 00 00 00 00 00 00 00 00 00 00 0C 67 06 00.............g.
0046601018 61 06 00 00 00 00 00 00 00 00 00 00 00 00 00a.............
0046602084 69 06 00 A4 61 06 00 00 00 00 00 00 00 00 00刬..........
0046603000 00 00 00 CA 69 06 00 B8 61 06 00 00 00 00 00....蔵.竌.....
0046604000 00 00 00 00 00 00 00 0A 6A 06 00 C8 61 06 00.........j.萢.
0046605000 00 00 00 00 00 00 00 00 00 00 00 52 6A 06 00............Rj.
00466060D8 61 06 00 00 00 00 00 00 00 00 00 00 00 00 00豠.............
004660709E 6A 06 00 EC 61 06 00 00 00 00 00 00 00 00 00瀓.靉.........
0046608000 00 00 00 DE 6A 06 00 FC 61 06 00 00 00 00 00....辥.黙.....
0046609000 00 00 00 00 00 00 00 04 6F 06 00 F8 62 06 00........o.鴅.
004660A000 00 00 00 00 00 00 00 00 00 00 00 52 6F 06 00............Ro.
004660B008 63 06 00 00 00 00 00 00 00 00 00 00 00 00 00c.............
004660C0C4 73 06 00 10 64 06 00 00 00 00 00 00 00 00 00膕.d.........
004660D000 00 00 00 B6 7D 06 00 84 66 06 00 00 00 00 00....秨.刦.....
004660E000 00 00 00 00 00 00 00 CC 7D 06 00 8C 66 06 00........蘿.宖.
;这里是被加密了的输入表,不过它的输入表结构是完整的
004A1481 8B46 0C mov eax, dword ptr [esi+C] ; ;eax -> IID.Name
004A1484 0BC0 or eax, eax
004A1486 0F84 25020000 je 004A16B1 ; 004A16B1
004A148C 8366 0C 00 and dword ptr [esi+C], 0 ; ;断在这,用0填充充掉IID.Name
004A1490 03C2 add eax, edx ; ;+00400000 ->IID.Name VA
004A1492 8BD8 mov ebx, eax
004A1494 56 push esi
004A1495 57 push edi
004A1496 50 push eax
004A1497 8BF3 mov esi, ebx
004A1499 8BFB mov edi, ebx
004A149B AC lods byte ptr [esi] ; 下面这几句是解密DLL的名字,并且写回原来的地方
004A149C C0C0 03 rol al, 3
004A149F AA stos byte ptr es:[edi]
004A14A0 803F 00 cmp byte ptr [edi], 0
004A14A3^ 75 F6 jnz short 004A149B ; 循环解密DLL名字
004A14A5 58 pop eax
004A14A6 5F pop edi
004A14A7 5E pop esi
004A14A8 50 push eax
004A14A9 FF95 20854100 call dword ptr [ebp+418520] ; GetModuleHandleA获取DLL模块句柄
004A14AF 0BC0 or eax, eax
004A14B1 75 43 jnz short 004A14F6 ; 获取成功跳
004A14F6 60 pushad ; ;下面这一段会把刚才解密出来的DLL名字用0填充掉
004A14F7 2BC0 sub eax, eax
004A14F9 8803 mov byte ptr [ebx], al
004A14FB 43 inc ebx
004A14FC 3803 cmp byte ptr [ebx], al
004A14FE^ 75 F9 jnz short 004A14F9 ; ;循环填充
004A1500 61 popad
004A1501 8985 3EF84000 mov dword ptr [ebp+40F83E], eax ; ;保存DLL模块句柄
004A1507 C785 42F84000 0>mov dword ptr [ebp+40F842], 0
004A1511 8B95 46F84000 mov edx, dword ptr [ebp+40F846] ; ;00400000
004A1517 8B06 mov eax, dword ptr [esi] ; ;eax ->OriginalFirstThunk
004A1519 0BC0 or eax, eax ; ;OriginalFirstThunk是否为空?
004A151B 75 07 jnz short 004A1524 ; ;不为空就直接使用它,为空就使用FirstThunk
004A151D 90 nop
004A151E 90 nop
004A151F 90 nop
004A1520 90 nop
004A1521 8B46 10 mov eax, dword ptr [esi+10] ; ;FirstThunk->IMAGE_THUNK_DATA
004A1524 03C2 add eax, edx ; ;00400000
004A1526 0385 42F84000 add eax, dword ptr [ebp+40F842]
004A152C 8B18 mov ebx, dword ptr [eax] ; ;IMAGE_IMPORT_BY_NAME
004A152E 8B7E 10 mov edi, dword ptr [esi+10]
004A1531 03FA add edi, edx
004A1533 03BD 42F84000 add edi, dword ptr [ebp+40F842]
004A1539 85DB test ebx, ebx ; ;是否已经处理完一个DLL?
004A153B 0F84 62010000 je 004A16A3 ; 004A16A3
004A1541 F7C3 00000080 test ebx, 80000000 ; ;Hint的高位是否为1?
004A1547 75 1D jnz short 004A1566 ; 004A1566
004A1549 90 nop
004A154A 90 nop
004A154B 90 nop
004A154C 90 nop
004A154D 03DA add ebx, edx ; ;00400000
004A154F 83C3 02 add ebx, 2 ; ;跳过Hint
004A1552 56 push esi
004A1553 57 push edi
004A1554 50 push eax
004A1555 8BF3 mov esi, ebx
004A1557 8BFB mov edi, ebx
004A1559 AC lods byte ptr [esi] ; ;下面是解密API的名字,写回原处
004A155A C0C0 03 rol al, 3
004A155D AA stos byte ptr es:[edi]
004A155E 803F 00 cmp byte ptr [edi], 0
004A1561^ 75 F6 jnz short 004A1559 ; ;循环解密
004A1563 58 pop eax
004A1564 5F pop edi
004A1565 5E pop esi
004A1566 3B9D 46F84000 cmp ebx, dword ptr [ebp+40F846] ; ;00400000
004A156C 7C 11 jl short 004A157F ; 004A157F
004A156E 90 nop
004A156F 90 nop
004A1570 90 nop
004A1571 90 nop
004A1572 83BD 1A204000 0>cmp dword ptr [ebp+40201A], 0
004A1579 75 0A jnz short 004A1585 ; 004A1585
004A157B 90 nop
004A157C 90 nop
004A157D 90 nop
004A157E 90 nop
004A157F 81E3 FFFFFF0F and ebx, 0FFFFFFF
004A1585 53 push ebx
004A1586 FFB5 3EF84000 push dword ptr [ebp+40F83E]
004A158C FF95 1C854100 call dword ptr [ebp+41851C] ; ;GetProcAddress获取函数地址
004A1592 3B9D 46F84000 cmp ebx, dword ptr [ebp+40F846] ; ;00400000
004A1598 7C 0F jl short 004A15A9 ; 004A15A9
004A159A 90 nop
004A159B 90 nop
004A159C 90 nop
004A159D 90 nop
004A159E 60 pushad ; ;下面又会循环用0填充掉刚才解密出来的API名字
004A159F 2BC0 sub eax, eax
004A15A1 8803 mov byte ptr [ebx], al
004A15A3 43 inc ebx
004A15A4 3803 cmp byte ptr [ebx], al
004A15A6^ 75 F9 jnz short 004A15A1 ; 004A15A1
004A15A8 61 popad
004A15A9 0BC0 or eax, eax ; ;获取地址是否成功?
004A15AB^ 0F84 15FFFFFF je 004A14C6 ; 004A14C6
004A15B1 3B85 2C854100 cmp eax, dword ptr [ebp+41852C] ; ;判断这个API是否是 MessageBoxA?
004A15B7 74 20 je short 004A15D9 ; 004A15D9
004A15B9 90 nop
004A15BA 90 nop
004A15BB 90 nop
004A15BC 90 nop
004A15BD 3B85 C4FD4000 cmp eax, dword ptr [ebp+40FDC4] ; ;判断是不是RegisterHotKey?
004A15C3 74 09 je short 004A15CE ; 004A15CE
004A15C5 90 nop
004A15C6 90 nop
004A15C7 90 nop
004A15C8 90 nop
004A15C9 EB 14 jmp short 004A15DF ; ;跳
004A15CB 90 nop
004A15CC 90 nop
004A15CD 90 nop
004A15CE 8D85 31FE4000 lea eax, dword ptr [ebp+40FE31]
004A15D4 EB 09 jmp short 004A15DF ; 004A15DF
004A15D6 90 nop
004A15D7 90 nop
004A15D8 90 nop
004A15D9 8D85 4BFE4000 lea eax, dword ptr [ebp+40FE4B]
004A15DF 56 push esi
004A15E0 FFB5 3EF84000 push dword ptr [ebp+40F83E]
004A15E6 5E pop esi
004A15E7 39B5 12204000 cmp dword ptr [ebp+402012], esi ; ;是不是Kernel32.dll?
004A15ED 74 15 je short 004A1604 ; 004A1604
004A15EF 90 nop
004A15F0 90 nop
004A15F1 90 nop
004A15F2 90 nop
004A15F3 39B5 16204000 cmp dword ptr [ebp+402016], esi
004A15F9 74 09 je short 004A1604 ; 004A1604
004A15FB 90 nop
004A15FC 90 nop
004A15FD 90 nop
004A15FE 90 nop
004A15FF EB 63 jmp short 004A1664 ; 004A1664
004A1601 90 nop
004A1602 90 nop
004A1603 90 nop
004A1604 80BD 16564100 0>cmp byte ptr [ebp+415616], 0
004A160B 74 57 je short 004A1664 ; 004A1664
004A160D 90 nop
004A160E 90 nop
004A160F 90 nop
004A1610 90 nop
004A1611 EB 07 jmp short 004A161A ; 004A161A
004A1613 90 nop
004A1614 90 nop
004A1615 90 nop
004A1616 0100 add dword ptr [eax], eax ; ;下面的一段生成一个函数,API会重定向到这个函数
004A1618 0000 add byte ptr [eax], al
004A161A 8BB5 0BF94000 mov esi, dword ptr [ebp+40F90B]
004A1620 83C6 0D add esi, 0D
004A1623 81EE 02184000 sub esi, 401802
004A1629 2BF5 sub esi, ebp
004A162B 83FE 00 cmp esi, 0
004A162E 7F 34 jg short 004A1664 ; 004A1664
004A1630 90 nop
004A1631 90 nop
004A1632 90 nop
004A1633 90 nop
004A1634 8BB5 0BF94000 mov esi, dword ptr [ebp+40F90B]
004A163A 53 push ebx
004A163B 50 push eax
004A163C E8 8DB2FFFF call 0049C8CE ; 0049C8CE
004A1641 8BD8 mov ebx, eax
004A1643 58 pop eax
004A1644 33C3 xor eax, ebx
004A1646 C606 68 mov byte ptr [esi], 68
004A1649 8946 01 mov dword ptr [esi+1], eax
004A164C C746 05 8134240>mov dword ptr [esi+5], 243481
004A1653 895E 08 mov dword ptr [esi+8], ebx
004A1656 C646 0C C3 mov byte ptr [esi+C], 0C3
004A165A 5B pop ebx ; ;到这里后我们到esi的地址去看看,发面这就是解密API地址的函数。
0048D010 68 F02096ED push ED9620F0
0048D015 813424 8A330591 xor dword ptr [esp], 9105338A
0048D01C C3 retn
004A165B 8BC6 mov eax, esi ; eax = 自成的函数地址
004A165D 8385 0BF94000 0>add dword ptr [ebp+40F90B], 0D
004A1664 5E pop esi
004A1665 60 pushad
004A1666 8BD0 mov edx, eax
004A1668 2BBD 46F84000 sub edi, dword ptr [ebp+40F846] ; ;edi->IAT,存放函数地址的RVA
004A166E 8BC7 mov eax, edi
004A1670 B9 01010000 mov ecx, 101
004A1675 8DBD EBEC4000 lea edi, dword ptr [ebp+40ECEB]
004A167B F2:AF repne scas dword ptr es:[edi]
004A167D 0BC9 or ecx, ecx
004A167F 74 13 je short 004A1694 ; ;跳
004A1681 90 nop
004A1682 90 nop
004A1683 90 nop
004A1684 90 nop
004A1685 81E9 01010000 sub ecx, 101
004A168B F7D1 not ecx
004A168D 89948D EBE84000 mov dword ptr [ebp+ecx*4+40E8EB], edx
004A1694 61 popad
004A1695 8907 mov dword ptr [edi], eax ; 用生成的函数地址来填充掉IAT中的函数地址
004A1697 8385 42F84000 0>add dword ptr [ebp+40F842], 4 ; ;加4,处理下一个API
004A169E^ E9 6EFEFFFF jmp 004A1511 ; ;循环处理API
004A16A3 83C6 14 add esi, 14 ; ;加20,esi指向下一个IID
004A16A6 8B95 46F84000 mov edx, dword ptr [ebp+40F846]
004A16AC^ E9 D0FDFFFF jmp 004A1481 ; ;处理下一个DLL
004A16B1 8DBD EBEC4000 lea edi, dword ptr [ebp+40ECEB]
004A16B7 33C0 xor eax, eax
;我们直接在004A16B1,F4,那么IAT的处理就完毕了,我们到0048D010去看看,
0048D010 68 F02096ED push ED9620F0
0048D015 813424 8A330591 xor dword ptr [esp], 9105338A
0048D01C C3 retn
0048D01D 68 0453A0B0 push B0A05304
0048D022 813424 E44332CC xor dword ptr [esp], CC3243E4
0048D029 C3 retn
0048D02A 68 4A8B1C69 push 691C8B4A
0048D02F 813424 4A9B8E15 xor dword ptr [esp], 158E9B4A
0048D036 C3 retn
0048D037 68 1BCC1C7C push 7C1CCC1B
0048D03C 813424 8A539C00 xor dword ptr [esp], 9C538A
0048D043 C3 retn
0048D044 68 34E1457C push 7C45E134
0048D049 813424 B07AC500 xor dword ptr [esp], 0C57AB0
0048D050 C3 retn
0048D051 68 414BDC7E push 7EDC4B41
0048D056 813424 B0D15C02 xor dword ptr [esp], 25CD1B0
0048D05D C3 retn
发现就是生成的一系统解密函数,而且IAT中地函数地址都指向了这里。
我们来总结一下它的流程吧
用0填充IID.Name -> 解密DLL名字 -> 获取DLL模块句柄 -> 销毁DLL名字 -> 解密API 名字 ->获取API地址 ->
销毁API名字 ->处理特殊函数 -> 生成一个函数 ->修改IAT,把API重定向到这个函数
好吧,分析完了,我们知道刚开始的时候输入表的结构是完整的,不过DLL和API名字都是被加密了的。
它在处理IAT的时候又会解密出DLL和API名字并且写回原来的输入表中,不过它后面又会销毁掉,。
那么我们想要它还原正确的输入表就不能让它销毁掉解密出来的DLL和API名字。
004A148C 是销毁IID.Name NOP掉
004A14F6 - 004A1500 是销毁解密出来的DLL名字NOP掉
004A159E - 004A15A8 是销毁解密出来的API名字 NOP掉
004A1695 是API重定向 NOP掉
然后用:LordPe dump出来,修正OEP。IAT为66000,大小为1000就行了,这样的程序就能直接运行了,不用Import Rec修复
我就不演示这个过程了
demoscene写的很详细,加精鼓励! 太帅了,有个地方没明白 ,百度去, 写的很详细 你写的那么详细,下次能不能写个vmp的啊!越详细越好... 我不会写脚本。。。郁闷。。。
导致现在很多壳都玩不了了。。。。
顺便膜拜一下lz 还是看不懂啊 好东西啊, 谢谢 写的很详细
只能看看大牛。。。
慢慢学呢