☆ 手工脱Armadillo https://scz.617.cn/misc/200802251522.txt Milescan Web Security Auditor Trial 1.1 (auditor_1.1_trial.exe) http://www.milescan.com spider_trial.exe(4,313,088 bytes) auditor_trial.exe(4,313,088 bytes) Milescan是一款商用WWW扫描器,不是太出名,其主站为http://www.milescan.com。 支持SQL注入扫描。它有一个兄弟产品更出名些,Paros Proxy,这是一款开源软件, 可以当普通代理使用,但更重要的是作为WWW扫描器使用,从名字上可能看不出后一 点来,我实测过其扫描功能,相当不错,其主站为http://www.parosproxy.org/。既 然开源的不错,想必商用的更不错,这是我关注Milescan的最初原因。 Milescan在其主站提供试用版下载,需要免费注册进入下载页面,文件较大(24.7M)。 这个试用版本身包含了完整的功能性代码,但通过其它手段做了如下两个限制: . 30天试用期 . 只能扫描如下IP: 10.0.0.0 - 10.255.255.255 172.16.0.0 - 172.31.255.255 192.168.0.0 - 192.168.255.255 127.0.0.0 - 127.255.255.255 第一个限制是通过Armadillo实现的,第二个限制是软件本身的。软件本身是用Java 开发的,最终编译成native code,没有保持在字节码状态。 运气不太好,终于碰上自己关心的程序需要脱壳,之前从未接触过脱壳。PEiD报 Armadillo壳,版本信息不太对,ArmaGUI.rar、mm_dillodie1.6.zip都可以成功脱掉 这个版本的壳。就本例而言,手脱虽然是个无意义的举动,但还是记录备忘一下手脱 过程,艺多不压身。 因为第一次接触手工脱壳,很多观点、操作相当稚嫩,贻笑大方。 用PEiD查origspider_trial.exe,报: Armadillo 1.xx - 2.xx -> Silicon Realms Toolworks 用Armadillo find protected查origspider_trial.exe,报: Debug-Blocker Enable Strategic Code Splicing Armadillo一般有类似这样的程序逻辑: -------------------------------------------------------------------------- HANDLE mutex; mutex = OpenMutex( ..., ..., "%X::DA%08X" ); if ( !mutex ) { /* * 父进程流程 */ CreateProcess(); DebugActiveProcess(); SuspendThread(); while ( 1 ) { WaitForDebugEvent( lpDebugEvent, ... ); switch ( lpDebugEvent->dwDebugEventCode ) { /* * 1 */ case EXCEPTION_DEBUG_EVENT: switch ( lpDebugEvent->u.Exception.ExceptionRecord.ExceptionCode ) { /* * STATUS_GUARD_PAGE_VIOLATION,0x80000001 */ case EXCEPTION_GUARD_PAGE: ... ... WriteProcessMemory(); ... ... break; /* * STATUS_ACCESS_VIOLATION,0xC0000005 */ case EXCEPTION_ACCESS_VIOLATION: ... ... break; /* * STATUS_BREAKPOINT,0x80000003 */ case EXCEPTION_BREAKPOINT: ... ... break; case ... } /* end of switch */ ... ... break; /* * 3 */ case CREATE_PROCESS_DEBUG_EVENT: ... ... /* * 确保子进程不会再走父进程流程,否则无限递归了 */ CreateMutex( ..., ..., "%X::DA%08X" ); /* * 与DebugActiveProcess()之后的那个SuspendThread()配对 */ ResumeThread(); ... ... break; case ... } /* end of switch */ ContinueDebugEvent( lpDebugEvent->dwProcessId, lpDebugEvent->dwThreadId, ... ); } /* end of while */ } else { /* * 子进程流程 * * 子进程自己负责处理引入表 */ } -------------------------------------------------------------------------- 单纯的Debug-Blocker只是靠DebugActiveProcess()确保无法用Ring 3调试器attach 子进程,从而无法调试子进程。 更复杂的则是子进程有意识地触发EXCEPTION_GUARD_PAGE、EXCEPTION_BREAKPOINT异 常,父进程捕获异常进行相应处理之后子进程再继续执行。 我碰上的这个是单纯的Debug-Blocker。 Armadillo在反调试器,一定要用HideOD隐藏OD。 就本例而言,只需对付IsDebuggerPresent()即可,下面是一个cdb.exe调试示例: -------------------------------------------------------------------------- cdb.exe -hd -noinh origspider_trial.exe g $exentry ba e 1 IsDebuggerPresent "g poi(esp);r eax=0;g" -------------------------------------------------------------------------- OD载入origspider_trial.exe,停在如下位置: -------------------------------------------------------------------------- 0092FBFB >/$ 55 push ebp 0092FBFC |. 8BEC mov ebp, esp 0092FBFE |. 6A FF push -1 0092FC00 |. 68 28829500 push origspid.00958228 0092FC05 |. 68 70EF9200 push origspid.0092EF70 ; SE handler installation 0092FC0A |. 64:A1 0000000>mov eax, dword ptr fs:[0] 0092FC10 |. 50 push eax 0092FC11 |. 64:8925 00000>mov dword ptr fs:[0], esp -------------------------------------------------------------------------- he OpenMutexA F9 -------------------------------------------------------------------------- 7C830607 > 8BFF mov edi, edi // OpenMutexA入口 7C830609 55 push ebp -------------------------------------------------------------------------- 断在OpenMutexA入口时查看stack: -------------------------------------------------------------------------- 0006F798 0091B205 /CALL to OpenMutexA from origspid.0091B1FF 0006F79C 001F0001 |Access = 1F0001 0006F7A0 00000000 |Inheritable = FALSE 0006F7A4 0006FDD8 \MutexName = "520::DAE5F88F3F" // 记下字符串指针0x0006FDD8 -------------------------------------------------------------------------- 在IDA中看看相关汇编代码(没有实际意义,仅记录备忘): -------------------------------------------------------------------------- 0091B1CA A1 B8 23 95 00 mov eax, ds:dword_9523B8 ; 0xAF7D5A38 0091B1CF 33 05 D0 23 95 00 xor eax, ds:dword_9523D0 ; 0xAF7D5A38 ^ 0x4A85D507 = 0xE5F88F3F 0091B1D5 50 push eax ; 0xE5F88F3F 0091B1D6 FF 15 B8 21 95 00 call ds:GetCurrentProcessId 0091B1DC 50 push eax ; PID 0091B1DD 68 D4 26 95 00 push offset aXDa08x ; "%X::DA%08X" 0091B1E2 8D 8D D8 FE FF FF lea ecx, [ebp+Name] 0091B1E8 51 push ecx ; Dest 0091B1E9 E8 0C 3C 01 00 call _sprintf 0091B1EE 83 C4 10 add esp, 10h 0091B1F1 8D 95 D8 FE FF FF lea edx, [ebp+Name] 0091B1F7 52 push edx ; lpName 0091B1F8 6A 00 push 0 ; bInheritHandle 0091B1FA 68 01 00 1F 00 push 1F0001h ; dwDesiredAccess 0091B1FF FF 15 A8 20 95 00 call ds:OpenMutexA 0091B205 85 C0 test eax, eax -------------------------------------------------------------------------- 00921400 8B 15 B8 23 95 00 mov edx, ds:dword_9523B8 00921406 33 15 D0 23 95 00 xor edx, ds:dword_9523D0 0092140C 52 push edx 0092140D A1 6C 97 95 00 mov eax, ds:child_pi 00921412 8B 48 08 mov ecx, [eax+8] 00921415 51 push ecx 00921416 68 D4 26 95 00 push offset aXDa08x ; "%X::DA%08X" 0092141B 8D 95 F8 FD FF FF lea edx, [ebp-208h] 00921421 52 push edx 00921422 E8 D3 D9 00 00 call _sprintf 00921427 83 C4 10 add esp, 10h 0092142A 8D 85 F8 FD FF FF lea eax, [ebp-208h] 00921430 50 push eax 00921431 6A 00 push 0 00921433 6A 00 push 0 00921435 FF 15 A4 20 95 00 call ds:CreateMutexA -------------------------------------------------------------------------- 我没有动态拦截CreateMutexA(),只是在IDA中查看"%X::DA%08X"的交叉引用关系定 位0x00921416的。 at 0x00401000 -------------------------------------------------------------------------- 00401000 0000 add byte ptr [eax], al 00401002 0000 add byte ptr [eax], al 00401004 0000 add byte ptr [eax], al 00401006 0000 add byte ptr [eax], al 00401008 0000 add byte ptr [eax], al 0040100A 0000 add byte ptr [eax], al 0040100C 0000 add byte ptr [eax], al 0040100E 0000 add byte ptr [eax], al 00401010 0000 add byte ptr [eax], al 00401012 0000 add byte ptr [eax], al 00401014 0000 add byte ptr [eax], al 00401016 0000 add byte ptr [eax], al -------------------------------------------------------------------------- 这是一片空闲的有效内存(可以另找空闲区域),手工输入如下汇编代码: -------------------------------------------------------------------------- 00401000 60 pushad 00401001 9C pushfd 00401002 68 D8FD0600 push 6FDD8 ; ASCII "520::DAE5F88F3F" 00401007 33C0 xor eax, eax 00401009 50 push eax 0040100A 50 push eax 0040100B E8 9783427C call kernel32.CreateMutexA 00401010 9D popfd 00401011 61 popad 00401012 - E9 F0F5427C jmp kernel32.OpenMutexA -------------------------------------------------------------------------- 60 9C 68 D8 FD 06 00 33 C0 50 50 E8 97 83 42 7C 9D 61 E9 F0 F5 42 7C -------------------------------------------------------------------------- 这段代码模拟0x00921435处的CreateMutexA(),以此欺骗0x0091B1FF处的OpenMutexA(), 强制走子进程流程。这次是单纯的Debug-Blocker,可以一开始就强制走子进程流程, 如果父进程需要处理EXCEPTION_GUARD_PAGE、EXCEPTION_BREAKPOINT异常,处理时机 要复杂些。 在理解这个壳的时候重点参考了两份文档: Armadillo 2.52加壳原理分析和改进的脱壳方法 - leo_cyl http://blog.csdn.net/compiler/articles/91123.aspx 试玩armadillo3.50a一点心得 - mysqladm [2004-03-06] http://www.pediy.com/bbshtml/BBS6/pediy6837.htm 这两份文档解释了一些重要原理,适合对Win32本身较熟但对脱壳不熟的程序员参看。 其中mysqladm原创了欺骗OpenMutexA()的方案,致敬。 手工修改EIP指向0x00401000,F9之后再次断在OpenMutexA入口: -------------------------------------------------------------------------- 7C830607 > 8BFF mov edi, edi // OpenMutexA入口 7C830609 55 push ebp -------------------------------------------------------------------------- 先撤消0x00401000附近的所有修改。 hd OpenMutexA Alt-M -------------------------------------------------------------------------- 00400000 00001000 origspid PE header Imag R RWE 00401000 00163000 origspid .text Imag R RWE // 在.text上设内存访问断点 00564000 00084000 origspid .data Imag R RWE 005E8000 00004000 origspid .bss Imag R RWE 005EC000 002D0000 origspid .rdata Imag R RWE 008BC000 0000D000 origspid .jidata data Imag R RWE 008C9000 00001000 origspid .idata Imag R RWE 008CA000 00010000 origspid .jedata Imag R RWE 008DA000 00017000 origspid .reloc Imag R RWE 008F1000 00001000 origspid .config Imag R RWE 008F2000 00050000 origspid .text1 code Imag R RWE 00942000 00010000 origspid .adata code Imag R RWE 00952000 00010000 origspid .data1 imports Imag R RWE 00962000 00010000 origspid .reloc1 relocations Imag R RWE 00972000 000F0000 origspid .pdata Imag R RWE 00A62000 00003000 origspid .rsrc resources Imag R RWE -------------------------------------------------------------------------- F9运行,中间如果有异常抛出(注意OD下方状态栏)就用Shift-F9继续,直至内存访问 断点命中: -------------------------------------------------------------------------- 00FDEBA7 0FBE00 movsx eax, byte ptr [eax] // 此时EAX=00401000 -------------------------------------------------------------------------- 00401000 6A 00 push 0 00401002 E8 43F91500 call origspid.0056094A 00401007 50 push eax 00401008 6A 01 push 1 0040100A 50 push eax 0040100B E8 24000000 call origspid.00401034 00401010 85C0 test eax, eax 00401012 74 16 je short origspid.0040102A 00401014 68 00000000 push 0 -------------------------------------------------------------------------- TNND,都不知道什么时候0x00401000已被写了新数据(原来全零),OD的内存访问断点 还真是不可靠,太容易被VirtualProtect()系列函数干挠。若想定位向0x00401000写 入新数据的代码,可用硬件断点。 清除.text上的内存访问断点。 he 00401000 F9 断在0x00401000之后清除所有断点。 在.text上设内存访问断点,不知是谁的原创,我看到的文档都在引别人的,却没有 看到源头。其本意是假设OEP位于.text中,在.text上设内存访问断点可快速定位OEP。 0x00401000即本例的OEP。现在来对付"Enable Strategic Code Splicing"。运行 ArmInline v0.96 Final,在Processs里选中origspider_trial.exe,会自动获取如 下信息: (Slave)Process ID Start Of Target Code 0x401000 Length Of Target Code 0x163000 Start Of Spliced Code 0x3C60000 Lenght Of Spliced Code 0x1000 用Alt-M查看内存信息加深理解: -------------------------------------------------------------------------- Memory map Address Size Owner Section Contains Type Access Initial Mapped as 00400000 00001000 origspid Imag R RWE 00401000 00163000 origspid .text Imag R RWE // Target Code 00564000 00084000 origspid .data Imag R RWE 005E8000 00004000 origspid .bss Imag R RWE 005EC000 002D0000 origspid .rdata Imag R RWE 008BC000 0000D000 origspid .jidata data Imag R RWE 008C9000 00001000 origspid .idata Imag R RWE 008CA000 00010000 origspid .jedata Imag R RWE 008DA000 00017000 origspid .reloc Imag R RWE 008F1000 00001000 origspid .config Imag R RWE 008F2000 00050000 origspid .text1 code Imag R RWE 00942000 00010000 origspid .adata code Imag R RWE 00952000 00010000 origspid .data1 imports Imag R RWE 00962000 00010000 origspid .reloc1 relocations Imag R RWE 00972000 000F0000 origspid .pdata Imag R RWE 00A62000 00003000 origspid .rsrc resources Imag R RWE 00A70000 00103000 Map R R 00B80000 000C8000 Map R E R E 00E80000 00001000 Priv RW RW 00E90000 00001000 Priv RW RW 00EA0000 0000E000 Priv RW RW 00EB0000 00001000 Priv RW RW 00FB0000 00056000 Priv RW RW 01010000 00002000 Map R R 01020000 00002000 Map R R 01030000 00005000 Priv RW RW 0116D000 00001000 Priv RW Guar RW 0116E000 00002000 stack of thr Priv RW Guar RW 01170000 00018000 Priv RW RW 01190000 000A4000 Priv RW RW 01253000 00002000 Priv RW RW 01270000 00007000 Priv RW RW 012B0000 0000C000 Priv RW RW 012C0000 00006000 Priv RW RW 012D0000 00004000 Priv RW RW 012E0000 00001000 Map RW RW 012F0000 00002000 Map R R 01300000 00003000 Priv RW RW 01340000 00001000 Map RW RW 01350000 00022000 Priv RW RW 01391000 00026000 Priv RW RW 01550000 00001000 Priv RW RW 01560000 0003E000 Priv RW RW 01660000 00001000 Priv RW RW 016E0000 00001000 Priv RW RW 0172D000 00001000 Priv RW Guar RW 0172E000 00002000 stack of thr Priv RW Guar RW 03C60000 00011000 Priv R E RWE // Spliced Code -------------------------------------------------------------------------- 在ArmInline里点"Remove Splices",得到如下提示信息: 73 splices repaired. Splice repairing complete. Patching process... Patch succesful. 最后那个succesful不是我写错的,是它自己写错的。关于ArmInline参看: Armadillo客户版Code Splicing+Import Table Elimination的简便修复方法 - fly [2005-09-28] http://bbs.pediy.com/showthread.php?t=17253 (介绍了一些Armadillo保护机制的术语以及ArmInline的使用方法) fly这里用的是个低版本的ArmInline,现在的0.96版用起来更省事。 后面的操作回头来看可能有些稚嫩,但我还是忠实地记录原始学习过程,见笑。 运行LordPE选中origspider_trial.exe,dump full,保存成dumped.exe。 运行ImportREC,选中origspider_trial.exe,将OEP改成00001000,注意,一定不要 直接写成00401000,这里减去的00400000即ImageBase。自动查找IAT,现在RVA变成 004C9078。 0x004C9078 + 0x00400000 = 0x008C9078 回到OD,查看相关信息: -------------------------------------------------------------------------- 00401000 6A 00 push 0 00401002 E8 43F91500 call origspid.0056094A -------------------------------------------------------------------------- 00560908 - FF25 A8908C00 jmp near dword ptr [8C90A8] ; ADVAPI32.DeregisterEventSource 0056090E - FF25 A4908C00 jmp near dword ptr [8C90A4] ; ADVAPI32.RegCloseKey 00560914 - FF25 A0908C00 jmp near dword ptr [8C90A0] 0056091A - FF25 9C908C00 jmp near dword ptr [8C909C] ; ADVAPI32.RegSetValueExA 00560920 - FF25 98908C00 jmp near dword ptr [8C9098] ; ADVAPI32.RegisterEventSourceA 00560926 - FF25 94908C00 jmp near dword ptr [8C9094] ; ADVAPI32.ReportEventA 0056092C - FF25 18918C00 jmp near dword ptr [8C9118] 00560932 - FF25 14918C00 jmp near dword ptr [8C9114] ; kernel32.FindClose 00560938 - FF25 10918C00 jmp near dword ptr [8C9110] 0056093E - FF25 0C918C00 jmp near dword ptr [8C910C] 00560944 - FF25 08918C00 jmp near dword ptr [8C9108] ; kernel32.GetModuleFileNameA 0056094A - FF25 04918C00 jmp near dword ptr [8C9104] ; from 0x00401002 00560950 - FF25 00918C00 jmp near dword ptr [8C9100] 00560956 - FF25 FC908C00 jmp near dword ptr [8C90FC] 0056095C - FF25 F8908C00 jmp near dword ptr [8C90F8] ; kernel32.GetStdHandle 00560962 - FF25 F4908C00 jmp near dword ptr [8C90F4] ; ntdll.RtlAllocateHeap 00560968 - FF25 F0908C00 jmp near dword ptr [8C90F0] ; ntdll.RtlFreeHeap 0056096E - FF25 EC908C00 jmp near dword ptr [8C90EC] 00560974 - FF25 E8908C00 jmp near dword ptr [8C90E8] 0056097A - FF25 28918C00 jmp near dword ptr [8C9128] ; USER32.OemToCharA ... ... 0056367A FF25 D4878C00 jmp near dword ptr [8C87D4] 00563680 FF25 00888C00 jmp near dword ptr [8C8800] 00563686 FF25 08888C00 jmp near dword ptr [8C8808] 0056368C FF25 10888C00 jmp near dword ptr [8C8810] 00563692 FF25 18888C00 jmp near dword ptr [8C8818] 00563698 FF25 20888C00 jmp near dword ptr [8C8820] 0056369E FF25 30888C00 jmp near dword ptr [8C8830] 005636A4 FF25 38888C00 jmp near dword ptr [8C8838] 005636AA FF25 40888C00 jmp near dword ptr [8C8840] 005636B0 FF25 48888C00 jmp near dword ptr [8C8848] -------------------------------------------------------------------------- 008C9078 004C9192 origspid.004C9192 008C907C 004C917A origspid.004C917A 008C9080 004C9168 origspid.004C9168 008C9084 004C9156 origspid.004C9156 008C9088 004C9148 origspid.004C9148 008C908C 004C9130 origspid.004C9130 008C9090 00000000 008C9094 77F3F9D8 ADVAPI32.ReportEventA 008C9098 77F3B857 ADVAPI32.RegisterEventSourceA 008C909C 77F3EBD7 ADVAPI32.RegSetValueExA 008C90A0 00FCB725 008C90A4 77F56CCE ADVAPI32.RegCloseKey 008C90A8 77F3F160 ADVAPI32.DeregisterEventSource 008C90AC 00FC7E3F 008C90B0 004C926E origspid.004C926E 008C90B4 004C925E origspid.004C925E 008C90B8 004C9252 origspid.004C9252 008C90BC 004C9246 origspid.004C9246 008C90C0 004C9236 origspid.004C9236 008C90C4 004C9224 origspid.004C9224 008C90C8 004C9212 origspid.004C9212 008C90CC 004C91FE origspid.004C91FE 008C90D0 004C91E8 origspid.004C91E8 008C90D4 004C91CE origspid.004C91CE 008C90D8 004C91BC origspid.004C91BC 008C90DC 004C91B0 origspid.004C91B0 008C90E0 004C91A2 origspid.004C91A2 008C90E4 00000000 008C90E8 00FC9931 008C90EC 00FC7E85 008C90F0 7C959E17 ntdll.RtlFreeHeap 008C90F4 7C959FD6 ntdll.RtlAllocateHeap 008C90F8 7C82B437 kernel32.GetStdHandle 008C90FC 00FCBD32 008C9100 00FC72A7 008C9104 00FCB213 008C9108 7C8245FF kernel32.GetModuleFileNameA 008C910C 00FC86CD 008C9110 00FCACCD 008C9114 7C82BFB3 kernel32.FindClose 008C9118 00FC8BC6 008C911C 00FC7E49 008C9120 004C927A origspid.004C927A 008C9124 00000000 008C9128 77E519CC USER32.OemToCharA 008C912C 00FC7D67 -------------------------------------------------------------------------- 获取输入表,显示无效函数,剪切指针,修复转存文件,这将生成一个dumped_.exe。 退出OD,双击执行dumped_.exe,出错信息表明0x00FCB213内存无效。这是Armadillo 的一种反dump机制,针对IAT中部分Entry进行特殊处理,使之指向更难理解、不易 dump的hook code,后者最终调用完成实际功能的Win32 API。前面dump时IAT已被处 理过,IAT中出现了指向hook code的Entry: 008C9104 00FCB213 而0x00FCB213处的代码不在dump范围内,所以执行dumped_.exe时出错。 -------------------------------------------------------------------------- 00FCB213 55 push ebp 00FCB214 8BEC mov ebp, esp 00FCB216 51 push ecx 00FCB217 53 push ebx 00FCB218 56 push esi 00FCB219 57 push edi 00FCB21A E8 81270000 call 00FCD9A0 00FCB21F FF75 08 push dword ptr [ebp+8] 00FCB222 E8 61CAFFFF call 00FC7C88 00FCB227 85C0 test eax, eax 00FCB229 59 pop ecx 00FCB22A 8945 FC mov dword ptr [ebp-4], eax 00FCB22D 75 2A jnz short 00FCB259 00FCB22F 60 pushad 00FCB230 8B15 18B7FF00 mov edx, dword ptr [FFB718] ; kernel32.7C82196D 00FCB236 83C2 64 add edx, 64 ; 注意这个加0x64 00FCB239 FFD2 call near edx ; kernel32.GetTickCount 00FCB23B 8B15 B4B6FF00 mov edx, dword ptr [FFB6B4] ; kernel32.7C8246E6 00FCB241 83C2 64 add edx, 64 ; 注意这个加0x64 00FCB244 B9 05000000 mov ecx, 5 00FCB249 803A CC cmp byte ptr [edx], 0CC 00FCB24C 74 07 je short 00FCB255 00FCB24E ^ E2 F9 loopd short 00FCB249 00FCB250 FF75 08 push dword ptr [ebp+8] 00FCB253 FFD2 call near edx ; kernel32.GetModuleHandleA,这是完成实际功能的Win32 API 00FCB255 8945 FC mov dword ptr [ebp-4], eax 00FCB258 61 popad 00FCB259 8B45 FC mov eax, dword ptr [ebp-4] 00FCB25C 5F pop edi 00FCB25D 5E pop esi 00FCB25E 5B pop ebx 00FCB25F C9 leave 00FCB260 C2 0400 retn 4 -------------------------------------------------------------------------- 关于针对IAT中部分Entry进行特殊处理,参看: Armadillo标准壳完全扫盲 - [2007-05-17] http://hi.baidu.com/%CC%EC%CD%E2%C3%AB%B3%E6/blog/item/91313df5a0483e24bc3109b4.html (解释了一些原理,适合新手,但有些术语并不正确,自己修正着理解吧) 重新用OD调试origspider_trial.exe,欺骗OpenMutexA()之后清除所有断点。 hw 008C912C F9 dd 008C9078 -------------------------------------------------------------------------- 008C9078 004C9192 origspid.004C9192 008C907C 004C917A origspid.004C917A 008C9080 004C9168 origspid.004C9168 008C9084 004C9156 origspid.004C9156 008C9088 004C9148 origspid.004C9148 008C908C 004C9130 origspid.004C9130 008C9090 00000000 008C9094 004C9192 origspid.004C9192 008C9098 004C917A origspid.004C917A 008C909C 004C9168 origspid.004C9168 008C90A0 004C9156 origspid.004C9156 008C90A4 004C9148 origspid.004C9148 008C90A8 004C9130 origspid.004C9130 008C90AC 00000000 008C90B0 004C926E origspid.004C926E 008C90B4 004C925E origspid.004C925E 008C90B8 004C9252 origspid.004C9252 008C90BC 004C9246 origspid.004C9246 008C90C0 004C9236 origspid.004C9236 008C90C4 004C9224 origspid.004C9224 008C90C8 004C9212 origspid.004C9212 008C90CC 004C91FE origspid.004C91FE 008C90D0 004C91E8 origspid.004C91E8 008C90D4 004C91CE origspid.004C91CE 008C90D8 004C91BC origspid.004C91BC 008C90DC 004C91B0 origspid.004C91B0 008C90E0 004C91A2 origspid.004C91A2 008C90E4 00000000 008C90E8 004C926E origspid.004C926E 008C90EC 004C925E origspid.004C925E 008C90F0 004C9252 origspid.004C9252 008C90F4 004C9246 origspid.004C9246 008C90F8 004C9236 origspid.004C9236 008C90FC 004C9224 origspid.004C9224 008C9100 004C9212 origspid.004C9212 008C9104 004C91FE origspid.004C91FE 008C9108 004C91E8 origspid.004C91E8 008C910C 004C91CE origspid.004C91CE 008C9110 004C91BC origspid.004C91BC 008C9114 004C91B0 origspid.004C91B0 008C9118 004C91A2 origspid.004C91A2 008C911C 00000000 008C9120 004C927A origspid.004C927A 008C9124 00000000 008C9128 004C927A origspid.004C927A 008C912C 00000000 -------------------------------------------------------------------------- 清除所有断点。与处理后的IAT比较一下,设置如下数据断点: hw 008C90A0 -------------------------------------------------------------------------- 00FDDB8E 8908 mov dword ptr [eax], ecx ; 数据断点命中 00FDDB90 8B85 10D9FFFF mov eax, dword ptr [ebp-26F0] ; origspid.008C90A0 00FDDB96 83C0 04 add eax, 4 00FDDB99 8985 10D9FFFF mov dword ptr [ebp-26F0], eax 00FDDB9F ^ E9 4DFCFFFF jmp 00FDD7F1 -------------------------------------------------------------------------- 单步跟踪这附近的代码执行情况,可以定位如下代码: -------------------------------------------------------------------------- 00FDD9D2 50 push eax ; 比较Win32 API的名字 00FDD9D3 FF15 7873FE00 call near dword ptr [FE7378] ; msvcrt._stricmp 00FDD9D9 59 pop ecx 00FDD9DA 59 pop ecx 00FDD9DB 85C0 test eax, eax 00FDD9DD /75 11 jnz short 00FDD9F0 ; 改成jmp,否则开始针对IAT中部分Entry进行特殊处理 00FDD9DF 8B85 58C2FFFF mov eax, dword ptr [ebp-3DA8] 00FDD9E5 8B40 08 mov eax, dword ptr [eax+8] 00FDD9E8 8985 68CAFFFF mov dword ptr [ebp-3598], eax 00FDD9EE EB 02 jmp short 00FDD9F2 00FDD9F0 ^ EB 9C jmp short 00FDD98E -------------------------------------------------------------------------- 0x00FDD9D3处进行名字比较,检查是否是需要特殊处理的Win32 API。0x00FDD9DD处 不跳转表明找到一个需要特殊处理的Win32 API,最终向IAT中写入hook code入口地 址。若不需要特殊处理则直接在IAT中写入Win32 API入口地址。 重新用OD调试origspider_trial.exe,欺骗OpenMutexA()之后清除所有断点。 hw 008C912C F9 dd 008C9078 清除所有断点。 hw 008C9094 F9 at 00FDD9DD 将0x00FDD9DD处的jnz改成jmp。 hw 008C912C F9 dd 008C9078 -------------------------------------------------------------------------- 008C9078 004C9192 origspid.004C9192 008C907C 004C917A origspid.004C917A 008C9080 004C9168 origspid.004C9168 008C9084 004C9156 origspid.004C9156 008C9088 004C9148 origspid.004C9148 008C908C 004C9130 origspid.004C9130 008C9090 00000000 008C9094 77F3F9D8 ADVAPI32.ReportEventA 008C9098 77F3B857 ADVAPI32.RegisterEventSourceA 008C909C 77F3EBD7 ADVAPI32.RegSetValueExA 008C90A0 77F46A17 ADVAPI32.RegCreateKeyExA 008C90A4 77F56CCE ADVAPI32.RegCloseKey 008C90A8 77F3F160 ADVAPI32.DeregisterEventSource 008C90AC 00FC7E3F 008C90B0 004C926E origspid.004C926E 008C90B4 004C925E origspid.004C925E 008C90B8 004C9252 origspid.004C9252 008C90BC 004C9246 origspid.004C9246 008C90C0 004C9236 origspid.004C9236 008C90C4 004C9224 origspid.004C9224 008C90C8 004C9212 origspid.004C9212 008C90CC 004C91FE origspid.004C91FE 008C90D0 004C91E8 origspid.004C91E8 008C90D4 004C91CE origspid.004C91CE 008C90D8 004C91BC origspid.004C91BC 008C90DC 004C91B0 origspid.004C91B0 008C90E0 004C91A2 origspid.004C91A2 008C90E4 00000000 008C90E8 7C825529 kernel32.WriteFile 008C90EC 7C801DC6 kernel32.LoadLibraryA 008C90F0 7C959E17 ntdll.RtlFreeHeap 008C90F4 7C959FD6 ntdll.RtlAllocateHeap 008C90F8 7C82B437 kernel32.GetStdHandle 008C90FC 7C823EC7 kernel32.GetProcessHeap 008C9100 7C823D7A kernel32.GetProcAddress 008C9104 7C82474A kernel32.GetModuleHandleA // 对比前述0x00FCB253处的代码 008C9108 7C8245FF kernel32.GetModuleFileNameA 008C910C 7C830BE4 kernel32.GetEnvironmentVariableA 008C9110 7C831FE1 kernel32.FindFirstFileA 008C9114 7C82BFB3 kernel32.FindClose 008C9118 7C8268F1 kernel32.ExitProcess 008C911C 00FC7E49 008C9120 004C927A origspid.004C927A 008C9124 00000000 008C9128 77E519CC USER32.OemToCharA 008C912C 00FC7D67 -------------------------------------------------------------------------- 务必撤消0x00FDD9DD处的修改,将jmp改回成jnz,清除所有断点。 he 00401000 F9 断在0x00401000之后清除所有断点。现在我们有未被处理过的IAT。 运行ArmInline v0.96 Final,选中origspider_trial.exe,Remove Splices。 运行LordPE选中origspider_trial.exe,dump full,保存成dumped.exe。 运行ImportREC,选中origspider_trial.exe,将OEP改成00001000,自动查找IAT, 现在RVA变成004C9078。获取输入表,显示无效函数,剪切指针,修复转存文件,这 将生成一个dumped_.exe。 退出OD,双击执行dumped_.exe,搞定。脱壳后30天试用期自动去除。破解IP限制与 脱壳无关,与本文无关。注意origauditor_trial.exe也加了壳,手脱原理同上。 本来还有个脱壳后的优化,照着下文简单折腾了一下: 脱壳后软件减肥大法 - yesky1 [2003-11-20] http://www.pediy.com/bbshtml/BBS6/pediy6313.htm 但未成功,不打算深究这一步了。前面有几次提到撤消某某处的修改,是为了躲避内 存中的自校验过程。 曾参看过一些手脱Armadillo壳的文章,不过不太适合我入门,主要原因是之前我完 全不了解该壳的一些保护机制,如果照着TA们的步骤做,会有很多细节对不上号,比 如那个所谓的"magic jmp",照葫芦画瓢只会让我更困惑,还好我一开始也没打算照 葫芦画瓢。由于这次的保护机制只用到两个: Debug-Blocker Enable Strategic Code Splicing 所以像我这样的脱壳新手还可以承受,再复杂的以后有需求时再学习吧。 没有技术含量的操作备忘录。 我把这次对我产生原理性的直接帮助的几篇文章列在正文中了。后面的参考资源则是 一个良莠不齐的总收集,亦可参看。 ☆ 参考资源 [ 2] Armadillo壳相关文档 脱壳入门初级教学 http://bbs.pediy.com/showthread.php?t=20366 Armadillo 2.52加壳原理分析和改进的脱壳方法 - leo_cyl http://blog.csdn.net/compiler/articles/91123.aspx (解释了一些重要原理,适合喜欢折腾的Win32程序员) 试玩armadillo3.50a一点心得 - mysqladm [2004-03-06] http://www.pediy.com/bbshtml/BBS6/pediy6837.htm (解释了一些重要原理,适合喜欢折腾的Win32程序员) Armadillo标准壳完全扫盲 - [2007-05-17] http://hi.baidu.com/%CC%EC%CD%E2%C3%AB%B3%E6/blog/item/91313df5a0483e24bc3109b4.html (解释了一些原理,适合新手,但有些术语并不正确,自己修正着理解吧) Armadillo客户版Code Splicing+Import Table Elimination的简便修复方法 - fly [2005-09-28] http://bbs.pediy.com/showthread.php?t=17253 (介绍了一些Armadillo保护机制的术语以及ArmInline的使用方法) Armadillo 3.6主程序脱壳 - tDasm [2004-03-13] http://www.pediy.com/bbshtml/BBS6/pediy6444.htm (一些原创脱壳技巧) Armadillo COPYMEMEII之DUMP的一个LOADPE小插件 - jwh51 [2004-03-15] http://www.pediy.com/bbshtml/BBS6/pediy6443.htm Armadillo 双进程标准壳快速脱壳 - fly [2004-03-16] http://www.pediy.com/bbshtml/BBS6/pediy6446.htm Blaze Media Pro5.05脱壳+基本修复CC(int3)+破解 - pyzpyz [2004-04-22] http://www.pediy.com/bbshtml/BBS6/pediy6499.htm (在tDasm文章基础上新增更多原创脱壳技巧) 手动脱Armadillo CopyMem-ll +Debug-Blocker壳全过程 - [2004-05-04] http://www.cnblogs.com/f4ncy/archive/2005/01/22/95674.html http://www.pediy.com/bbshtml/BBS6/pediy6732.htm iRider.exe 2.20BETA主程序脱壳 - wxhing [2004-11-12] http://wxhing.blogcn.com/diary,204358883.shtml 浅谈Armadillo V.3.75 与 V.3.78的保护 - C-pen [2004-12-06] http://www.pediy.com/bbshtml/BBS6/pediy6906.htm (不适合学习阶段的人看,基本是熟手的自我总结小片段,不成体系) Armadillo V4.0输入表乱序的简便修复方法 - fly [2004-12-31] http://bbs.pediy.com/showthread.php?t=9193 (介绍了ImportREC) 脱Armadillo 1.xx - 2.xx的壳教程 - [2005-01-20] http://bbs.pediy.com/showthread.php?t=10131 用Ollydbg手脱Armadillo加壳的DLL - jameshero [2005-05-31] http://bbs.pediy.com/showthread.php?t=14098 Armadillo几个版本脱壳总结 - wynney [2005-06-26] http://bbs.pediy.com/showthread.php?t=14753 ArmadilloCopyMem-ll+Debug-Block - wynney [2005-06-27] http://bbs.pediy.com/showthread.php?t=14778 Armadillo双进程标准壳快速脱壳 - KuNgBiM [2005-08-18] http://bbs.pediy.com/showthread.php?t=16342 (这个基本照搬前文pediy6446.htm) Patch 修复 Armadillo 的IAT乱序 - [2005-12-07] http://bbs.pediy.com/showthread.php?t=19202 (有创意,可参考) 脱带KEY的ARM双进程的壳 http://bbs.pediy.com/showthread.php?t=29177 用OD手脱 Armadillo v4.40 DLL壳 - regkiller https://forum.eviloctal.com/read-htm-tid-19169.html Armadillo 1.xx - 2.xx脱壳 - layper http://www.juntuan.net/pjjs/pjjs/n/2005-07-22/6420.html 老生常谈Armadillo CopyMem-II+Debug-Blocker 保护方式脱壳 - daxia2002 http://www.hacker.com.cn/forum/view_120213.html 脱壳后软件减肥大法 - yesky1 [2003-11-20] http://www.pediy.com/bbshtml/BBS6/pediy6313.htm 浅谈程序脱壳后的优化 - CCDebuger [2006-07-03] http://bbs.pediy.com/showthread.php?t=28402 Armadillo壳相关工具 http://www.pediy.com/tools/unpacker.htm http://www.pediy.com/tools/unpack/Armadillo/armafp/armafp.zip http://www.pediy.com/tools/unpack/Armadillo/ArmaGUI/ArmaGUI.rar http://www.pediy.com/tools/unpack/Armadillo/ArmInline/ArmInlinev0.96f.zip http://www.pediy.com/tools/PE_tools/Lordpe/LPE-DLX.rar http://www.pediy.com/tools/PE_tools/Rebuilder/Import%20REC/ucfir16f.rar