浅谈Armadillo V.3.75 与 V.3.78的保护
阔别脱壳已有四年了,没想到Armadillo与ASprotect都变得如此变态,这是四年多来第一篇脱文,有不对的地方,还请各位多加指教,这篇文章只是心得分享.
首先ArmadilloV.3.75与V.3.78最大的不同点是V.3.75是由子进程自行解压而父进程只处理CC,而V.3.78是子进程解压后又会再度加密,然后在执行时产生异常,而由父进程处理异常,处理子进程的解压,所以在父进程在此时的角色变为处理子进程解码与CC。我们主要要处理几个部分:
(1) 得到IAT
(2) Dump程序与修复IAT
(3) 找OEP
(4) 修复CC
我们分别来谈:
[得到IAT] 想要得到IAT一般是修改Magic Jmp 在 Armadillo中有两处,如下:
00E49C2A 8B0D 74B7E700 MOV ECX,DWORD PTR DS:[E7B774]
00E49C30 89040E MOV DWORD PTR DS:[ESI+ECX],EAX
00E49C33 A1 74B7E700 MOV EAX,DWORD PTR DS:[E7B774]
00E49C38 391C06 CMP DWORD PTR DS:[ESI+EAX],EBX
00E49C3B 75 16 JNZ SHORT 00E49C53
00E49C3D 8D85 B4FEFFFF LEA EAX,DWORD PTR SS:[EBP-14C]
00E49C43 50 PUSH EAX
00E49C44 FF15 DC00E700 CALL DWORD PTR DS:[E700DC] ; KERNEL32.LoadLibraryA
00E49C4A 8B0D 74B7E700 MOV ECX,DWORD PTR DS:[E7B774]
00E49C50 89040E MOV DWORD PTR DS:[ESI+ECX],EAX
00E49C53 A1 74B7E700 MOV EAX,DWORD PTR DS:[E7B774]
00E49C58 391C06 CMP DWORD PTR DS:[ESI+EAX],EBX
00E49C5B 0F84 32010000 JE 00E49D93 ;Magic Jmp1
00E76BE8 3985 90C4FFFF CMP DWORD PTR SS:[EBP-3B70],EAX ; Armadill.00400000
00E76BEE 75 0F JNZ SHORT 00E76BFF
00E76BF0 C785 8CC4FFFF 80>MOV DWORD PTR SS:[EBP-3B74],0E85180
00E76BFA E9 C4000000 JMP 00E76CC3
00E76BFF 83A5 68C2FFFF 00 AND DWORD PTR SS:[EBP-3D98],0
00E76C06 C785 64C2FFFF C0>MOV DWORD PTR SS:[EBP-3D9C],0E857C0
00E76C10 EB 1C JMP SHORT 00E76C2E
00E76C12 8B85 64C2FFFF MOV EAX,DWORD PTR SS:[EBP-3D9C]
00E76C18 83C0 0C ADD EAX,0C
00E76C1B 8985 64C2FFFF MOV DWORD PTR SS:[EBP-3D9C],EAX
00E76C21 8B85 68C2FFFF MOV EAX,DWORD PTR SS:[EBP-3D98]
00E76C27 40 INC EAX
00E76C28 8985 68C2FFFF MOV DWORD PTR SS:[EBP-3D98],EAX
00E76C2E 8B85 64C2FFFF MOV EAX,DWORD PTR SS:[EBP-3D9C]
00E76C34 8338 00 CMP DWORD PTR DS:[EAX],0
00E76C37 0F84 86000000 JE 00E76CC3 ;Magic Jmp2
00E76C3D 8B85 64C2FFFF MOV EAX,DWORD PTR SS:[EBP-3D9C]
00E76C43 8B40 08 MOV EAX,DWORD PTR DS:[EAX+8]
00E76C46 83E0 01 AND EAX,1
00E76C49 85C0 TEST EAX,EAX
00E76C4B 74 25 JE SHORT 00E76C72
00E76C4D A1 2800E900 MOV EAX,DWORD PTR DS:[E90028]
00E76C52 8B0D 2800E900 MOV ECX,DWORD PTR DS:[E90028] ; Armadill.004B3310
00E76C58 8B40 30 MOV EAX,DWORD PTR DS:[EAX+30]
00E76C5B 3341 20 XOR EAX,DWORD PTR DS:[ECX+20]
00E76C5E 8B0D 2800E900 MOV ECX,DWORD PTR DS:[E90028] ; Armadill.004B3310
00E76C64 3341 70 XOR EAX,DWORD PTR DS:[ECX+70]
00
个人觉得第一处比较好。既可得到完整的IAT又可以避开时间的检验,至于如何找到IAT与Dump出IAT,我想大家应该没有问题。
[ Dump出程序与修复IAT ] 在这个步骤,首先你需要一个观念,你必须时时刻刻问自己程序在做什么?为什么这么做?这时IAT乱序已完成,程序在以下的代码段写下CALL的位置.即CALL(Address)
00E67E16 8B85 BCB0FFFF MOV EAX,DWORD PTR SS:[EBP+FFFFB0BC]
00E67E1C 40 INC EAX
00E67E1D 8985 BCB0FFFF MOV DWORD PTR SS:[EBP+FFFFB0BC],EAX
00E67E23 8B85 BCB0FFFF MOV EAX,DWORD PTR SS:[EBP+FFFFB0BC]
00E67E29 8B8D 18C8FFFF MOV ECX,DWORD PTR SS:[EBP-37E8]
00E67E2F 833C81 00 CMP DWORD PTR DS:[ECX+EAX*4],0
00E67E33 0F84 90000000 JE 00E67EC9
00E67E39 8B85 BCB0FFFF MOV EAX,DWORD PTR SS:[EBP+FFFFB0BC]
00E67E3F 8B8D 18C8FFFF MOV ECX,DWORD PTR SS:[EBP-37E8]
00E67E45 8B95 00C7FFFF MOV EDX,DWORD PTR SS:[EBP-3900]
00E67E4B 031481 ADD EDX,DWORD PTR DS:[ECX+EAX*4]
00E67E4E 8995 ACB0FFFF MOV DWORD PTR SS:[EBP+FFFFB0AC],EDX
00E67E54 8B85 ACB0FFFF MOV EAX,DWORD PTR SS:[EBP+FFFFB0AC]
00E67E5A 8B00 MOV EAX,DWORD PTR DS:[EAX]
00E67E5C 8985 A8B0FFFF MOV DWORD PTR SS:[EBP+FFFFB0A8],EAX
00E67E62 81BD A8B0FFFF 90>CMP DWORD PTR SS:[EBP+FFFFB0A8],90909090
00E67E6C 74 56 JE SHORT 00E67EC4
00E67E6E 8B85 A8B0FFFF MOV EAX,DWORD PTR SS:[EBP+FFFFB0A8]
00E67E74 2B85 B8B0FFFF SUB EAX,DWORD PTR SS:[EBP+FFFFB0B8]
00E67E7A 8985 A8B0FFFF MOV DWORD PTR SS:[EBP+FFFFB0A8],EAX
00E67E80 FFB5 A8B0FFFF PUSH DWORD PTR SS:[EBP+FFFFB0A8]
00E67E86 8B85 BCB0FFFF MOV EAX,DWORD PTR SS:[EBP+FFFFB0BC]
00E67E8C 33D2 XOR EDX,EDX
00E67E8E 6A 10 PUSH 10
00E67E90 59 POP ECX
00E67E91 F7F1 DIV ECX
00E67E93 FF1495 7807E700 CALL DWORD PTR DS:[EDX*4+E70778]
00E67E9A 59 POP ECX
00E67E9B 8985 A8B0FFFF MOV DWORD PTR SS:[EBP+FFFFB0A8],EAX
00E67EA1 8B85 A8B0FFFF MOV EAX,DWORD PTR SS:[EBP+FFFFB0A8]
00E67EA7 8B8D D8C6FFFF MOV ECX,DWORD PTR SS:[EBP-3928]
00E67EAD 8D0481 LEA EAX,DWORD PTR DS:[ECX+EAX*4]
00E67EB0 8985 A8B0FFFF MOV DWORD PTR SS:[EBP+FFFFB0A8],EAX
00E67EB6 8B85 ACB0FFFF MOV EAX,DWORD PTR SS:[EBP+FFFFB0AC]
00E67EBC 8B8D A8B0FFFF MOV ECX,DWORD PTR SS:[EBP+FFFFB0A8]
00E67EC2 8908 MOV DWORD PTR DS:[EAX],ECX
00E67EC4 ^E9 4DFFFFFF JMP 00E67E16
[Address]是指同一个API Armadillo为什么会留下这个最容易被攻击的地方呢?那是因为他的IAT乱序表的地址是动态产生的,它不得不在这个时候写入正确的地址,所以在这个时候他的属性就非得变为可读可写了.嘻嘻!也因此你就能使用Lordpe等工具去Dump出子进程来,这时你可以在Memory中找到IAT乱序表与CALL的位置表,它们是紧连着的.你把它们Dump出来,配合之前得到的IAT你就能修复他,或是直接写入一些代码,让CALL指向你自己的IAT,随便你,在这里你可以为所欲为.
[得到OEP] 在Armadillo中OEP倒有许多方法可找到.你可以He GetCurrenthreadId去找那个CALL EDI V.3.75与V.3.78均同.
[修复CC]:在这个阶段大部分是手工活了,也是最花时间的Armadillo 的 Nanomites保护,其特征代码如下:
0048F1F7 C785 94EBFFFF 00>MOV DWORD PTR SS:[EBP-146C],0
0048F201 6A FF PUSH -1
0048F203 6A 04 PUSH 4
0048F205 8D95 50ECFFFF LEA EDX,DWORD PTR SS:[EBP-13B0]
0048F20B 52 PUSH EDX
0048F20C E8 7F7DFEFF CALL Armadill.00476F90
0048F211 83C4 0C ADD ESP,0C
0048F214 8985 68EEFFFF MOV DWORD PTR SS:[EBP-1198],EAX
0048F21A 8B85 68EEFFFF MOV EAX,DWORD PTR SS:[EBP-1198]
0048F220 33D2 XOR EDX,EDX
0048F222 B9 10000000 MOV ECX,10
0048F227 F7F1 DIV ECX
0048F229 8995 64EEFFFF MOV DWORD PTR SS:[EBP-119C],EDX
0048F22F 8B95 50ECFFFF MOV EDX,DWORD PTR SS:[EBP-13B0]
0048F235 52 PUSH EDX
0048F236 8B85 64EEFFFF MOV EAX,DWORD PTR SS:[EBP-119C]
0048F23C FF1485 A8784B00 CALL DWORD PTR DS:[EAX*4+4B78A8]
0048F243 83C4 04 ADD ESP,4
0048F246 8985 94EBFFFF MOV DWORD PTR SS:[EBP-146C],EAX
0048F24C C785 90EBFFFF 00>MOV DWORD PTR SS:[EBP-1470],0
0048F256 8B8D 64EEFFFF MOV ECX,DWORD PTR SS:[EBP-119C]
0048F25C 8B148D 88994B00 MOV EDX,DWORD PTR DS:[ECX*4+4B9988]
0048F263 8995 70EEFFFF MOV DWORD PTR SS:[EBP-1190],EDX
0048F269 8B85 90EBFFFF MOV EAX,DWORD PTR SS:[EBP-1470]
0048F26F 3B85 70EEFFFF CMP EAX,DWORD PTR SS:[EBP-1190]
0048F275 7D 5C JGE SHORT Armadill.0048F2D3
0048F277 8B85 70EEFFFF MOV EAX,DWORD PTR SS:[EBP-1190]
0048F27D 2B85 90EBFFFF SUB EAX,DWORD PTR SS:[EBP-1470]
0048F283 99 CDQ
0048F284 2BC2 SUB EAX,EDX
0048F286 D1F8 SAR EAX,1
至于怎么到这里不用我说了吧!He GetThreadContext在这个部分你需要找到四个表《INT3地址+1表(Armadillo利用这来计算跳转的,记做Table1),跳转类型表(JMP,JZ,JNZ…)记做 Table2,跳的话距离是多少记做Table3,不跳的话距离又是多少记做Table4。为什么需要这四个表呢?举个例子:
00401D1B CC INT3
00401D1C 56 push ESI
我们可以在Armadillo中找到
Table1:00401D1C
Table2:Jmp
Table3:04
Table4: 04
这是个绝对的跳转JMP,但他只跳04Byte这其实是一个NOP怎么说,你看00401D1C+04=00401D20你改为
00401D1B 90 NOP
00401D1C 90 NOP
00401D1D 90 NOP
00401D1E 90 NOP
00401D1F 90 NOP
00401D20 8B45 08 MOV EAX,DWORD PTR SS:[EBP+8]
或直接改为:
00401D1B E900000000 JMP00401D20
00401D20 8B4508 MOV EAX,DWORD PTR SS:[EBP+8]
都必须可实现,或你不放心那可改为:
00401D1B EB 03 JMP 00401D20
00401D1D 105678 ADC BYTE DS:[EST+78],DL
00401D20 8B4508 MOV EAX,DWORD PTR SS:[EB+8]
也可以,另外还有一些修复技巧,后面再详述.
首先找Table1亦就是发生INT3的下一个地址,它就在上述的0048F205 8D95 50ECFFFF LEA EDX,DWPRO PTR SS:[EBP-13BO]就在[EBP-13BO]中,V.3.75你可以用点击所有的功能表来获得大部分有用的地址.但V.3.78就难了.还有一个方法是使用IDA去反汇编你已Dump出来的程序,IDA就会帮你做记号“Trap to Debugger”利用它你几乎可以得到所有INT3地址,但有许多是用不到的,因为你不会走到那里.你或许会问为什么不用Armadillo的运算表去还原地址呢?旧版的或许可以,但新版的他不只把跳转做成表,也把不是的也加在里面,那就算你能还原它,也不一定是你需要的。
再来就是Table2的处理了,其代码如下:
0048F372 8B85 64EEFFFF MOV EAX,DWORD PTR SS:[EBP-119C]
0048F378 8B0C85 C8994B00 MOV ECX,DWORD PTR DS:[EAX*4+4B99C8]
0048F37F 8B95 90EBFFFF MOV EDX,DWORD PTR SS:[EBP-1470]
0048F385 33C0 XOR EAX,EAX
0048F387 8A0411 MOV AL,BYTE PTR DS:[ECX+EDX]
0048F38A 8985 74EBFFFF MOV DWORD PTR SS:[EBP-148C],EAX
0048F390 8B85 74EBFFFF MOV EAX,DWORD PTR SS:[EBP-148C]
0048F396 99 CDQ
0048F397 83E2 0F AND EDX,0F
0048F39A 03C2 ADD EAX,EDX
0048F39C C1F8 04 SAR EAX,4
0048F39F 8985 7CEBFFFF MOV DWORD PTR SS:[EBP-1484],EAX
0048F3A5 8B8D 74EBFFFF MOV ECX,DWORD PTR SS:[EBP-148C]
0048F3AB 81E1 0F000080 AND ECX,8000000F
0048F3B1 79 05 JNS SHORT Armadill.0048F3B8
0048F3B3 49 DEC ECX
0048F3B4 83C9 F0 OR ECX,FFFFFFF0
0048F3B7 41 INC ECX
0048F3B8 898D 78EBFFFF MOV DWORD PTR SS:[EBP-1488],ECX
0048F3BE 8B95 7CEBFFFF MOV EDX,DWORD PTR SS:[EBP-1484]
0048F3C4 3B95 78EBFFFF CMP EDX,DWORD PTR SS:[EBP-1488]
0048F3CA 75 1B JNZ SHORT Armadill.0048F3E7
0048F3CC 8B85 78EBFFFF MOV EAX,DWORD PTR SS:[EBP-1488]
0048F3D2 83C0 01 ADD EAX,1
0048F3D5 25 0F000080 AND EAX,8000000F
0048F3DA 79 05 JNS SHORT Armadill.0048F3E1
0048F3DC 48 DEC EAX
0048F3DD 83C8 F0 OR EAX,FFFFFFF0
0048F3E0 40 INC EAX
0048F3E1 8985 78EBFFFF MOV DWORD PTR SS:[EBP-1488],EAX
0048F3E7 8B8D 74EBFFFF MOV ECX,DWORD PTR SS:[EBP-148C]
0048F3ED 8B95 7CEBFFFF MOV EDX,DWORD PTR SS:[EBP-1484]
0048F3F3 8B048D 388F4B00 MOV EAX,DWORD PTR DS:[ECX*4+4B8F38]
0048F3FA 330495 AC324B00 XOR EAX,DWORD PTR DS:[EDX*4+4B32AC]
0048F401 8B8D 78EBFFFF MOV ECX,DWORD PTR SS:[EBP-1488]
0048F407 33048D AC324B00 XOR EAX,DWORD PTR DS:[ECX*4+4B32AC]
0048F40E 8985 84EBFFFF MOV DWORD PTR SS:[EBP-147C],EAX
0048F414 8B95 58ECFFFF MOV EDX,DWORD PTR SS:[EBP-13A8]
0048F41A 81E2 D70F0000 AND EDX,0FD7
0048F420 52 PUSH EDX
0048F421 8B85 74EBFFFF MOV EAX,DWORD PTR SS:[EBP-148C]
0048F427 0FBE88 A0774B00 MOVSX ECX,BYTE PTR DS:[EAX+4B77A0]
0048F42E FF148D A8784B00 CALL DWORD PTR DS:[ECX*4+4B78A8]
0048F435 83C4 04 ADD ESP,4
0048F438 8985 88EBFFFF MOV DWORD PTR SS:[EBP-1478],EAX
0048F43E 8B95 44ECFFFF MOV EDX,DWORD PTR SS:[EBP-13BC]
0048F444 52 PUSH EDX
0048F445 8B85 88EBFFFF MOV EAX,DWORD PTR SS:[EBP-1478]
0048F44B 50 PUSH EAX
0048F44C FF95 84EBFFFF CALL DWORD PTR SS:[EBP-147C]
0048F452 83C4 08 ADD ESP,8
0048F455 50 PUSH EAX
0048F456 8B8D 74EBFFFF MOV ECX,DWORD PTR SS:[EBP-148C]
0048F45C 0FBE91 A0774B00 MOVSX EDX,BYTE PTR DS:[ECX+4B77A0]
0048F463 FF1495 E8784B00 CALL DWORD PTR DS:[EDX*4+4B78E8]
0048F46A 83C4 04 ADD ESP,4
0048F46D 8985 80EBFFFF MOV DWORD PTR SS:[EBP-1480],EAX
0048F473 8B85 80EBFFFF MOV EAX,DWORD PTR SS:[EBP-1480]
0048F479 83E0 01 AND EAX,1
0048F47C 85C0 TEST EAX,EAX
0048F47E 0F84 AE000000 JE Armadill.0048F532
0048F484 60 PUSHAD
0048F485 33C0 XOR EAX,EAX
0048F487 75 02 JNZ SHORT Armadill.0048F48B
0048F489 EB 15 JMP SHORT Armadill.0048F4A0
0048F48B EB 33 JMP SHORT Armadill.0048F4C0
这段代码就是利用EFL值与前面运算得出的值做运算,算出子进程是跳或不是跳,意思即0048F47C TEST EAX,EAX; EAX=1,子进程跳EAX=0子进程不跳,我们利用它来模拟进而得到跳转类型。如何做呢?let’s go!
我们用到的旗标有CF、PF、ZF、SF、OF
CF=1→1h
PF=1→4h
ZF=1→40h
SF=1→80h
OF=1→800h
走到0048F420 Push EDX,此时EDX=EFL值,我们把EDX值改为1意即只有CF旗标有作用,结果0048F47C的EAX值为1用笔记下来,再至0048F372重建EIP,如此依序更改为EDX的值,我们发现一个有趣的现象:
EFL=1 EAX=1
EFL=4 EAX=1
EFL=40 EAX=1
EFL=80 EAX=1
EFL=800 EAX=1
意即所有的旗标都无法影响到跳转,它肯定是一个绝对的跳转,意即JMP。
再看下一个例子:
EFL=1 EAX=0
EFL=4 EAX=0
EFL=40 EAX=1
EFL=80 EAX=0
EFL=800 EAX=0
此时只有零旗标ZF会影响跳转,意即等于零跳转,那就是JZ or JE了
那JNZ呢?如下:
EFL=1 EAX=1
EFL=4 EAX=1
EFL=40 EAX=0
EFL=80 EAX=1
EFL=800 EAX=1
跟JZ相反,等于零不跳,意即不等于零才会跳
归纳出:
(1) 11111→JMP(EB)
(2) 00100→JZ(74)
(3) 11011→JNZ(75)
(4) 00011→JL(7C) 漏掉了许多跳转,这只是告诉你原理,你可以查阅跳转的资料
(5) 00111→JLE(7E) 去编你自己的跳转类型判断表,自由发挥吧!
(6) 11100→JGE(7D)
(7) 10000→JB(72)
(8) 01111→JAE(73)
接下来是Table3其代码如下:
0048F4B0 8B0C8D E8984B00 MOV ECX,DWORD PTR DS:[ECX*4+4B98E8]
0048F4B7 8B85 90EBFFFF MOV EAX,DWORD PTR SS:[EBP-1470]
0048F4BD 33D2 XOR EDX,EDX
0048F4BF BE 10000000 MOV ESI,10
0048F4C4 F7F6 DIV ESI
0048F4C6 8B85 90EBFFFF MOV EAX,DWORD PTR SS:[EBP-1470]
0048F4CC 8B0C81 MOV ECX,DWORD PTR DS:[ECX+EAX*4]
0048F4CF 338C95 8CEEFFFF XOR ECX,DWORD PTR SS:[EBP+EDX*4-1174]
0048F4D6 8B95 50ECFFFF MOV EDX,DWORD PTR SS:[EBP-13B0]
0048F4DC 03D1 ADD EDX,ECX
0048F4DE 8995 50ECFFFF MOV DWORD PTR SS:[EBP-13B0],EDX
走过0048F4CF后这个ECX的值就是我们要的了。
最后是Table4了,其代码如下:
0048F53D 8B85 64EEFFFF MOV EAX,DWORD PTR SS:[EBP-119C]
0048F543 8B0C85 109A4B00 MOV ECX,DWORD PTR DS:[EAX*4+4B9A10]
0048F54A 8B95 90EBFFFF MOV EDX,DWORD PTR SS:[EBP-1470]
0048F550 33C0 XOR EAX,EAX
0048F552 8A0411 MOV AL,BYTE PTR DS:[ECX+EDX]
0048F555 8B8D 50ECFFFF MOV ECX,DWORD PTR SS:[EBP-13B0]
0048F55B 03C8 ADD ECX,EAX
0048F55D 898D 50ECFFFF MOV DWORD PTR SS:[EBP-13B0],ECX
走过0048F552后这个EAX的值就是我们要的了。
在程序内写入代码,依序去得到想要的Table,然后用它来修复CC,修复CC时注意事项
例1:
Table1 00401213
Table2 JZ
Table3 55FFFFFF
Table4 05
改00401212 OF 84 50FFFFFF
例2:
Table1 00401213
Table2 JZ
Table3 EFFFFFFF
Table4 01
改00401212 74 EE
例3:
Table1 00401213
Table2 JZ
Table3 28
Table4 01
改00401212 74 27
例4:
Table1 00401213
Table2 JZ
Table3 B5
Table4 05
改00401212 0F 84 B0000000
[后记]:许久没有脱壳与写脱壳文了,甚至连电脑都很少碰,最近脱了新版本ASprotect 与Armadillo好象又能找到当年的一点感觉,我脱壳总喜欢慢慢的去品尝保护程序带给我的惊喜。脱这些硬壳,需要的是除了知识外,还需要耐心与恒心。最重要的是对脱壳的热爱,希望本文能对你有所帮助。
[培训]内核驱动高级班,冲击BAT一流互联网大厂工作,每周日13:00-18:00直播授课