///////////////////////////////////////////////////////////////////////////////////////////////
文件名称:病毒“wuauclt.exe”所加未知壳的手脱方法
目标程序:病毒样本
操作环境:Windows XP-SP2
使用工具:Ollydbg 1.10版
编写作者:Coderui
编写时间:2008年10月14日(更新)
联系方式:coderui@163.com
作者博客:http://hi.baidu.com/coderui
///////////////////////////////////////////////////////////////////////////////////////////////
---------------------------------------------------------------------------------------------
前言:
本想努力工作来着,好把日程进度再往前赶赶。但很不幸,一不小心发现了个比较诱人的壳,看来今天又要把大部分时间压在它的身上了。
其实这个病毒的其它变种样本我在很早以前就见过多次,也分析过多次了。只是它所加的壳实在是变态得很,以至于我一直都没能把它完全征服,所以在每次分析它的时候都只能是取其运行后的内存映像来进行研究。
使用PEID对样本进行扫描,显示出的壳名为“PEtite 2.1 -> Ian Luck”。我脱完壳后在网上搜索了一下,发现有好多该壳的脱文,并且内容都是寥寥几句,一个ESP定律就把壳给搞定了。仔细看了一下,怎么感觉完全不是同一个壳呢?也不知道问题出在哪里了。“PEtite 2.1 -> Ian Luck”这个壳以前我没接触过,具体什么样子我也不知道,这里就把它当未知壳来讲吧。
该壳应该是一个加密壳,里边有3处异常(通向光明的关键,其中的后2处异常加了暗桩手段)、输入表加密、代码动态解密执行,有N多迷惑性的花指令代码、压缩算法、反动态跟踪调试等技术。如果想脱掉这个壳的话,别说刚入门的新手了,就算是有经验的人都未必能很快的搞定。
今天仔细分析了下,用了2个小时左右,终于把这个壳的框架思路分析清楚了。下面准备分两部分来讲述:第一部分-手脱方法、第二部分-难点分析。
---------------------------------------------------------------------------------------------
第一部分:手脱方法:
说明:([F2]:下软断点、[F4]:执行到当前代码处、[F7]:单步步入、[F8]:单步步过、[F9]:运行。)
(01):使用“Ollydbg”打开病毒主程序,设置“Ollydbg”为“不忽略任何异常”,然后[F9]运行。
(02):“Ollydbg”提示“访问违规”,然后停在代码“MOV BYTE PTR DS:[EDI],AL”处(代码如下)。
13149217 8807 MOV BYTE PTR DS:[EDI],AL ; 访问违规
13149219 81EC D8BA0000 SUB ESP,0BAD8
1314921F 8D8D 887FFFFF LEA ECX,DWORD PTR SS:[EBP+FFFF7F88]
13149225 834D EC FF OR DWORD PTR SS:[EBP-14],FFFFFFFF
13149229 894D 90 MOV DWORD PTR SS:[EBP-70],ECX
1314922C 8D8D 887FFFFF LEA ECX,DWORD PTR SS:[EBP+FFFF7F88]
13149232 894D 8C MOV DWORD PTR SS:[EBP-74],ECX
13149235 8B4D 08 MOV ECX,DWORD PTR SS:[EBP+8]
13149238 8D45 88 LEA EAX,DWORD PTR SS:[EBP-78]
1314923B 53 PUSH EBX
(03):使用[Shift + F9]来忽略异常,“Ollydbg”停在下一个异常代码“POP EDX”处,并提示“单步异常”(代码如下)。
131438F1 5A POP EDX ; 单步异常
131438F2 64:8F05 0000000>POP DWORD PTR FS:[0]
131438F9 58 POP EAX
131438FA 6A 00 PUSH 0
131438FC 53 PUSH EBX
131438FD 33DB XOR EBX,EBX
131438FF 68 4C030000 PUSH 34C
13143904 8B0C24 MOV ECX,DWORD PTR SS:[ESP]
13143907 0FBAE3 00 BT EBX,0
1314390B 72 16 JB SHORT virus.13143923
(04):在代码“POP DWORD PTR FS:[0]”处下软断点(如地址“131438F2”)。使用[Shift + F9]来忽略异常,停在代码“POP DWORD PTR FS:[0]”处(代码如下)。
131438F1 5A POP EDX ; 断点
131438F2 64:8F05 0000000>POP DWORD PTR FS:[0]
131438F9 58 POP EAX
131438FA 6A 00 PUSH 0
131438FC 53 PUSH EBX
131438FD 33DB XOR EBX,EBX
131438FF 68 4C030000 PUSH 34C
13143904 8B0C24 MOV ECX,DWORD PTR SS:[ESP]
13143907 0FBAE3 00 BT EBX,0
1314390B 72 16 JB SHORT virus.13143923
(05):向下翻阅代码,你会找到具有明显特征的如下代码,然后在代码“CALL 74DBD43D”处下断点(如地址“13143BB2”),[F9]运行(代码如下)。
13143BA8 59 POP ECX
13143BA9 5E POP ESI
13143BAA FD STD
13143BAB 33C0 XOR EAX,EAX
13143BAD B9 65030000 MOV ECX,365
13143BB2 E8 8698C761 CALL 74DBD43D ; 下断点
13143BB7 0000 ADD BYTE PTR DS:[EAX],AL
13143BB9 0000 ADD BYTE PTR DS:[EAX],AL
13143BBB 0000 ADD BYTE PTR DS:[EAX],AL
13143BBD 0000 ADD BYTE PTR DS:[EAX],AL
13143BBF 0000 ADD BYTE PTR DS:[EAX],AL
13143BC1 0000 ADD BYTE PTR DS:[EAX],AL
.
.
13143FF5 0000 ADD BYTE PTR DS:[EAX],AL
13143FF7 0000 ADD BYTE PTR DS:[EAX],AL
13143FF9 0000 ADD BYTE PTR DS:[EAX],AL
13143FFB 0000 ADD BYTE PTR DS:[EAX],AL
13143FFD 0000 ADD BYTE PTR DS:[EAX],AL
(06):停在代码“CALL 74DBD43D”处后,使用[F7]跟进去。进去后来到这里。然后一直[F8]单步向下走,在代码“JMP”处跳(如地址“1314910B”)(代码如下)。
13149102 5F POP EDI
13149103 F3:AA REP STOS BYTE PTR ES:[EDI]
13149105 61 POPAD
13149106 66:9D POPFW
13149108 83C4 0C ADD ESP,0C
1314910B >- E9 F2EEFFFF JMP virus.13148002 ; 跳转(这里是分界线。听一位网友说前面属于PEtite壳的部分,那么后面的代码可能就属于另一个壳,或是该壳以被改为变形壳了。)
13149110 - E9 1A6B6C69 JMP kernel32.GlobalFree
13149115 - E9 5D796C69 JMP kernel32.GetFileSize
1314911A - E9 44826D69 JMP kernel32.GetWindowsDirectoryA
1314911F - E9 540C7169 JMP kernel32.OutputDebugStringA
13149124 - E9 E6906B69 JMP kernel32.WriteProcessMemory
13149129 - E9 F7846B69 JMP kernel32.DeviceIoControl
1314912E - E9 A73C6D69 JMP kernel32.ExitProcess
13149133 - E9 69256C69 JMP kernel32.GetModuleHandleA
13149138 - E9 631C6C69 JMP kernel32.GetProcAddress
1314913D - E9 16AE7169 JMP kernel32.Process32Next
13149142 - E9 E48FBE64 JMP USER32.GetWindowTextA
13149147 - E9 ECA0C764 JMP ADVAPI32.StartServiceA
1314914C - E9 02E6C564 JMP ADVAPI32.OpenProcessToken
13149151 - E9 DE33C864 JMP ADVAPI32.AdjustTokenPrivileges
13149156 - E9 857D4C6A JMP SHELL32.ShellExecuteA
1314915B - E9 98BCAB64 JMP msvcrt._onexit
13149160 - E9 CC67AC64 JMP msvcrt.sprintf
(07):跳到这里后,一直[F8]单步向下走,走到代码“INT 2D”处时停下,看堆栈,在堆栈中找到所要执行的异常处理代码位置(代码如下)。
13148002 3BC0 CMP EAX,EAX ; 跳到这里
13148004 9B WAIT
13148005 43 INC EBX
13148006 4B DEC EBX
13148007 87E6 XCHG ESI,ESP
13148009 87E6 XCHG ESI,ESP
1314800B 60 PUSHAD
1314800C 8B7424 20 MOV ESI,DWORD PTR SS:[ESP+20]
13148010 BF 5F801413 MOV EDI,virus.1314805F
13148015 A4 MOVS BYTE PTR ES:[EDI],BYTE PTR DS:[ESI]
13148016 4F DEC EDI
13148017 87FA XCHG EDX,EDI
13148019 87FA XCHG EDX,EDI
1314801B 8037 6E XOR BYTE PTR DS:[EDI],6E
1314801E 68 42801413 PUSH virus.13148042
13148023 64:FF35 0000000>PUSH DWORD PTR FS:[0]
1314802A 64:8925 0000000>MOV DWORD PTR FS:[0],ESP
13148031 CD 2D INT 2D ; 停在这里看堆栈,寻找异常处理代码,该处的异常“Ollydbg”不能自动处理。
13148033 C3 RETN
(08):例如找到的异常处理代码所在的位置是“13148042”,那么我们就需要手动把当前调试的EIP值重新设置为“13148042”(需要手动设置的原因是OD没有识别这个异常,我们只能自己去定向这个跳转)。EIP设置完毕后,一直[F8]单步向下走,在代码“RETN”处返回(代码如下)。
13148042 3BFF CMP EDI,EDI
13148044 9B WAIT
13148045 90 NOP
13148046 3BFF CMP EDI,EDI
13148048 3BFF CMP EDI,EDI
1314804A 8BC0 MOV EAX,EAX
1314804C B8 65801413 MOV EAX,virus.13148065
13148051 50 PUSH EAX
13148052 C2 0000 RETN 0
(09):返回后来到这里,然后一直[F8]单步向下走,在代码“RETN”处返回(代码如下)。
13148065 B9 00101413 MOV ECX,virus.13141000
1314806A BB 00200000 MOV EBX,2000
1314806F 68 7C801413 PUSH virus.1314807C
13148074 68 59801413 PUSH virus.13148059
13148079 C2 0000 RETN 0
(10):返回后来到这里,向下翻阅代码,找到无效指定代码“ADD BYTE PTR DS:[EAX],AL”前的最后一个返回跳转代码“RETN”,这个返回跳转就是壳的结尾处了(代码如下)。
13148059 85DB TEST EBX,EBX
1314805B 74 07 JE SHORT virus.13148064
1314805D 8031 3E XOR BYTE PTR DS:[ECX],3E
13148060 4B DEC EBX
13148061 41 INC ECX
13148062 ^ EB F5 JMP SHORT virus.13148059
13148064 C3 RETN
13148065 B9 00101413 MOV ECX,virus.13141000
1314806A BB 00200000 MOV EBX,2000
1314806F 68 7C801413 PUSH virus.1314807C
13148074 68 59801413 PUSH virus.13148059
13148079 C2 0000 RETN 0
1314807C 42 INC EDX
1314807D 4A DEC EDX
1314807E B9 00401413 MOV ECX,virus.13144000
13148083 BB 00200000 MOV EBX,2000
13148088 68 95801413 PUSH virus.13148095
1314808D 68 59801413 PUSH virus.13148059
13148092 C2 0000 RETN 0
13148095 83EB 01 SUB EBX,1
13148098 43 INC EBX
13148099 9B WAIT
1314809A 60 PUSHAD
1314809B 61 POPAD
1314809C 9B WAIT
1314809D B9 00601413 MOV ECX,virus.13146000
131480A2 BB 00100000 MOV EBX,1000
131480A7 68 B4801413 PUSH virus.131480B4 ; ASCII "ah"
131480AC 68 59801413 PUSH virus.13148059
131480B1 C2 0000 RETN 0
131480B4 61 POPAD
131480B5 68 00221413 PUSH virus.13142200
131480BA 47 INC EDI
131480BB 4F DEC EDI
131480BC C3 RETN
131480BD 0000 ADD BYTE PTR DS:[EAX],AL
131480BF 0000 ADD BYTE PTR DS:[EAX],AL
131480C1 0000 ADD BYTE PTR DS:[EAX],AL
131480C3 0000 ADD BYTE PTR DS:[EAX],AL
131480C5 0000 ADD BYTE PTR DS:[EAX],AL
131480C7 0000 ADD BYTE PTR DS:[EAX],AL
131480C9 0000 ADD BYTE PTR DS:[EAX],AL
.
.
13148FF8 0000 ADD BYTE PTR DS:[EAX],AL
13148FFA 0000 ADD BYTE PTR DS:[EAX],AL
13148FFC 0000 ADD BYTE PTR DS:[EAX],AL
13148FFE 0000 ADD BYTE PTR DS:[EAX],AL
(11):返回后来到这里,这个就是病毒样本的真正OEP入口了,看入口代码的结构像汇编写的。使用OD插件DUMP出来就可以了,输入表在DUMP过程中被脱壳插件自动修复了,脱壳后的样本可以正常运行(代码如下)。
13142200 55 PUSH EBP ; ntdll.7C930738
13142201 8BEC MOV EBP,ESP
13142203 81EC 0C050000 SUB ESP,50C
13142209 57 PUSH EDI
1314220A 6A 00 PUSH 0
1314220C 68 68531413 PUSH virus.13145368 ; ASCII "YINWSAFDWPEIRPWIEIHENANRENQUANSHILAJIHAOGUOAZHINALDAOJFAGZHIXIANLFALFJAAUFOPWLAJLFJALJFA"
13142211 68 34531413 PUSH virus.13145334 ; ASCII "TIANYAWEISHENMEHENANLAOCHUPIANZIPEPWUAPREPAJIERPW"
13142216 6A FF PUSH -1
13142218 FF15 28311413 CALL DWORD PTR DS:[13143128] ; USER32.MessageBoxA
1314221E E8 4DFFFFFF CALL virus.13142170
13142223 85C0 TEST EAX,EAX
13142225 74 13 JE SHORT virus.1314223A
13142227 68 00531413 PUSH virus.13145300 ; ASCII "FINDINFILESFINDINFILESFNAJIUGANDIAOTAMENBAHEHEHE"
1314222C FF15 84301413 CALL DWORD PTR DS:[13143084] ; virus.1314911F
13142232 6A 00 PUSH 0
13142234 FF15 BC301413 CALL DWORD PTR DS:[131430BC] ; virus.1314912E
---------------------------------------------------------------------------------------------
第二部分:难点分析
其实这个壳只要了解了其原理,脱起来就简单多了。关键点就是要找出它内部出现的三处异常处理代码的位置,这是脱整个壳的关键。
大概说下“PEtite”壳的原理:
(1):壳在一开始运行时先是在内存中解密要执行的代码,解密完毕后使用“内存访问异常”(第一处异常)来离开解密循环,然后在异常处理代码中执行后面的所有操作。
(2):接着壳大概设置了一部分堆栈和寄存器环境变量,然后创建了一个“单步异常”(第二处异常)。这个异常的异常处理代码中有另一部分对堆栈和寄存器环境变量的设置,而“单步异常”可以使用[F8]单步直接执行过去,并且不会执行内部的异常处理代码,所以这样就做到反动态单步跟踪了。如果这样执行过去的话,由于一部分堆栈和寄存器环境变量没有得到有效的设置,所以就无法对程序去进行正确的动态跟踪了。
(3):在内存中动态解密并修复输入表。
(4):创建一个“INT 2D异常”(第三处异常),这个异常不会被“Ollydbg”自动识别并执行。也就是说不管你怎么做,“Ollydbg”都不会去自动寻找并执行“INT 2D异常”所产生的异常处理代码。这里就需要我们自己去看堆栈并寻找异常处理代码位置,自己去设置代码当前的EIP地址,然后从“INT 2D异常”的异常处理代码入口处开始执行剩下的操作,这样这个壳就脱完了(好象只有1到3点属于“PEtite”壳,这个第4点是被后加进来的)。
原理看似简单,但如果自己去跟的话,如果不细心或经验不足,很多细节都会被忽略掉的。其中,“单步异常”的异常处理代码我是用内存断点定位出来的,不知道堆栈中为什么就看不到对它设置的地址呢?可能同样是在某处被秘密的隐藏起来了。就像第一处异常的异常处理代码就是在壳入口的附近创建的,看样子作者在设计这个壳时可是花去了不少的心思啊,呵呵!
---------------------------------------------------------------------------------------------
///////////////////////////////////////////////////////////////////////////////////////////////
[招生]科锐逆向工程师培训(2024年11月15日实地,远程教学同时开班, 第51期)