参考:姜晔老师的病毒分析
一.行为分析
1.
在运行病毒前,先打开任务管理器看看,在运行代码后,任务管理器会自动关掉,且无法再打开。
在dos命令行下,使用tasklist命令,可查看当前进程。
发现可以进程spoclsv.exe,可以推断一下,这就是病毒打开的进程.输入命令taskkill /r /im pid可以杀死该进程。
2.
打开进程管理工具process monitor,在筛选器中选择process name,输入setup.exe,添加,筛选,
在Tool选项卡中找到process tree,在里面找到setup.exe的进程,可以发现,他创建了spoclsv.exe的进程,并且打开命令行窗口,执行‘cmd.exe /c net share C$ /del /y’(删除C盘的网络共享,执行完后关闭cmd.exe),“cmd.exe /c net share admin$ /del /y”(关闭共享)。我只有一个C盘,可以推断,病毒会删除所有盘的共享。
改变筛选器的值,选择options RegSetValue,查看setup.exe进程对注册表的更改。
他改变了seed的值,seed用于设置随机数,无关紧要,不管他。
接着来看spoclsv.exe的行为。
修改筛选器的选项,processname设置为spoclsv.exe,options选择regsetvaluse和regcreatkeys,
查看他对注册表做了什么。
首先,他给自己创建了自启动项,
其次,修改系统注册表的值,使某些文件变为隐藏。
再改变筛选器,选择regdeletevalue
查一下这个进程
所以,他关掉了安全软件的进程。这应该是病毒们通用的策略,可以推断,其他的病毒也可能具有这样的特征。
再改筛选器,
他会将自身复制,并且创建.ini的文件。
3. 总结
综上可以总结出病毒的几点特征
- 关闭并禁止启动任务管理器
- 创建spoclsv.exe的进程,该进程文件的路径为“C:\WINDOWS\system32\drivers\spoclsv.exe”
- 创建自启动项,将位于“C:\WINDOWS\system32\drivers\spoclsv.exe”的病毒程序,设置为自启动
- 删除安全类软件的启动项
- 将自身拷贝到根目录,并命名为“setup.exe”,同时创建“autorun.inf”用于病毒的启动,这两个文件的属性都是“隐藏”。
- 在一些目录中创建名为“Desktop_.ini”的隐藏文件
- 取消系统共享
- 修改注册表,使得隐藏文件无法通过普通的设置进行显示
二.逆向分析
IDA打开,开头部分由编译器自主添加,不需要管,我们从sub_403C98开始分析(在姜晔老师教程上,此处IDA会自动分析出一串字符,但我的IDA没有),在OD里跟随,od跳转到此处后可以看到一句话,
F7进入函数查看,
CODE:00403C98 test edx, edx ; 判断edx是否为零,由于edx存的是字符串,所以不为零,下一个跳转不成立
CODE:00403C9A jz short loc_403CC0
CODE:00403C9C mov ecx, [edx-8] ; 将edx-8,地址的内容拷贝到ecx中,现在其值是FFFFFFF
CODE:00403C9F inc ecx ; ecx自加1,ecx变为0,同时,ZF标志位变1
CODE:00403CA0 jg short loc_403CBC ; 只有SF=0且ZF=0时跳转,但上一步ZF被置为1,所以不会跳转
CODE:00403CA2 push eax
CODE:00403CA3 push edx
CODE:00403CA4 mov eax, [edx-4] ; edx-4地址的内容赋值给eax,现在其值为20
CODE:00403CA7 call sub_403D08 ; 经od调试分析,他层层调用,最后调用locallocl函数,分配内存
CODE:00403CAC mov edx, eax ; 现在,edx中存放着刚分配的内存地址
CODE:00403CAE pop eax
CODE:00403CAF push edx
CODE:00403CB0 mov ecx, [eax-4]
CODE:00403CB3 call sub_402650 ; 将字符串“武汉男生感染下载者”拷贝到刚分配的内存里
CODE:00403CB8 pop edx
CODE:00403CB9 pop eax
CODE:00403CBA jmp short loc_403CC0 ; 跳转到
在IDA Pro中的sub_403C98重命名为Allocstackandcopystring,
第二个sub_403C98就不分析了,字符串是这个
再看下一个函数,函数执行前放入寄存器的是‘xboy’和一串乱码,可以推测一下,这是个解密过程。
跟进函数,可以找到一个循环,经过od跟踪,发现他就是个解密循环,将这堆乱码翻译成 “***武 * 汉 * 男 * 生 * 感 * 染 * 下 * 载 * 者 ***”,
mov eax, [ebp+var_14]
CODE:004053D4 call Getlen
CODE:004053D9 push eax
CODE:004053DA mov eax, ebx
CODE:004053DC pop edx
CODE:004053DD mov ecx, edx
CODE:004053DF cdq
CODE:004053E0 idiv ecx
CODE:004053E2 mov edi, edx
CODE:004053E4 inc edi
CODE:004053E5 mov eax, [ebp+var_14]
CODE:004053E8 movzx eax, byte ptr [eax+edi-1] ; 取出xboy中的一个字符,进行计算,第一次是‘b’,再是‘o’,最后是‘x’
CODE:004053ED mov ecx, 0Ah ; 将0xA赋给ecx,在下面的除法中当除数
CODE:004053F2 xor edx, edx ; edx清零
CODE:004053F4 div ecx ; eax除以ecx,商储存在eax中,余数在edx中
CODE:004053F6 mov eax, [ebp+var_4]
CODE:004053F9 movzx eax, byte ptr [eax+ebx-1] ; 从乱码中取值
CODE:004053FE xor edx, eax ; 上面除法的余数,与刚从乱码中取出的值异或,存于edx中
CODE:00405400 lea eax, [ebp+var_18]
CODE:00405403 call sub_403E2C
CODE:00405408 mov edx, [ebp+var_18]
CODE:0040540B lea eax, [ebp+var_10]
CODE:0040540E call stringcat
CODE:00405413 inc ebx
CODE:00405414 dec esi
CODE:00405415 jnz short loc_4053D1
给他更名decodestring,
再看下一个函数,
decodesting函数解密完的字符串赋给edx,第一个函数中出现的"***武 * 汉 * 男 * 生 * 感 * 染 * 下 * 载 * 者 ***",被赋值给了eax,跟进这个函数,
0040401B |. 89C6 mov esi,eax
0040401D |. 89D7 mov edi,edx
0040401F |. 39D0 cmp eax,edx ; 比较两个字符串是否相等
00404021 |. 0F84 8F000000 je setup.004040B6
00404027 |. 85F6 test esi,esi ; 查看esi是否为0
00404029 |. 74 68 je short setup.00404093
0040402B |. 85FF test edi,edi ; 查看edi是否为0
0040402D |. 74 6B je short setup.0040409A ; 以上两个比较杜绝了两串字符都是0的情况
0040402F |. 8B46 FC mov eax,dword ptr ds:[esi-0x4]
00404032 |. 8B57 FC mov edx,dword ptr ds:[edi-0x4]
00404035 |. 29D0 sub eax,edx ; 比较两个字符串的长度
00404037 |. 77 02 ja short setup.0040403B
00404039 |. 01C2 add edx,eax
0040403B |> 52 push edx
0040403C |. C1EA 02 shr edx,0x2
0040403F |. 74 26 je short setup.00404067
00404041 |> 8B0E /mov ecx,dword ptr ds:[esi] ; 该循环依次比较两个字符串的每一位
00404043 |. 8B1F |mov ebx,dword ptr ds:[edi] ; setup.0040E500
00404045 |. 39D9 |cmp ecx,ebx
00404047 |. 75 58 |jnz short setup.004040A1
00404049 |. 4A |dec edx
0040404A |. 74 15 |je short setup.00404061
0040404C |. 8B4E 04 |mov ecx,dword ptr ds:[esi+0x4]
0040404F |. 8B5F 04 |mov ebx,dword ptr ds:[edi+0x4] ; setup.0040E5F4
00404052 |. 39D9 |cmp ecx,ebx
00404054 |. 75 4B |jnz short setup.004040A1
00404056 |. 83C6 08 |add esi,0x8
00404059 |. 83C7 08 |add edi,0x8
0040405C |. 4A |dec edx
0040405D |.^ 75 E2 \jnz short setup.00404041
该函数用于比较两串字符串是否相等,改个名,cmpstring。
接着,程序跳转到loc_40CBBC,
这里的函数已经出现过了,不再过多研究,接着向下走,到40CBE6,
此处连着三个函数调用,先看第一个,
进入函数,首先是一个循环,循环0x84次,一次压入2个0x0,从而开辟出一块全是0的空间,推测一会还有用。
继续向下看,遇见第一个函数,进入,可以看到,
调用了系统API,从名字可以看出,返回文件路径。给他重命名,getpathandname。
再看下一个函数,最主要的便是这个循环
,在遇到0x5c,0x2f,0x3a时,跳出循环,而0x5c,0x2f都是‘/’,0x3a是‘:’,所以判断这个函数是用来取文件名或是不含文件名的路径,在结尾处查看内存地址可以看到
所以,该函数实现的是去掉文件名,取不带文件名的路径,改名:getfilepath。
再往下看函数00403ED4,忽略过程,看看结果,
上一步取出的路径后面多了“Desktop_.ini”,所以,该函数的功能应该是连接两个字符串,改个名,stringcat。
再向下,将上一步处理的字符串赋值给eax,进入看看,再进入第一个函数,发现
FindFirstFileA函数用于查找是否存在这个文件,那么sub_4057A4函数的作用就在于检测文件是否存在,可将其改名为:CheckFileExist。
再往下看,这一部分由于函数以前出现过,简单点,
CODE:0040808B lea edx, [ebp+var_3C0]
CODE:00408091 xor eax, eax
CODE:00408093 call getpathandname
CODE:00408098 mov eax, [ebp+var_3C0]
CODE:0040809E lea edx, [ebp+var_3BC]
CODE:004080A4 call getfilepath
CODE:004080A9 lea eax, [ebp+var_3BC]
CODE:004080AF mov edx, offset aDesktop__ini ; "Desktop_.ini"
CODE:004080B4 call stringcat
CODE:004080B9 mov eax, [ebp+var_3BC]
CODE:004080BF call checkpath
CODE:004080C4 push eax ; lpFileName
CODE:004080C5 call SetFileAttributesA
CODE:004080CA push 1 ; dwMilliseconds
CODE:004080CC call Sleep
CODE:004080D1 lea edx, [ebp+var_3C8]
CODE:004080D7 xor eax, eax
CODE:004080D9 call getpathandname
CODE:004080DE mov eax, [ebp+var_3C8]
CODE:004080E4 lea edx, [ebp+var_3C4]
CODE:004080EA call getfilepath
CODE:004080EF lea eax, [ebp+var_3C4]
CODE:004080F5 mov edx, offset aDesktop__ini ; "Desktop_.ini"
CODE:004080FA call stringcat
CODE:004080FF mov eax, [ebp+var_3C4]
CODE:00408105 call checkpath
CODE:0040810A push eax ; lpFileName
CODE:0040810B call DeleteFileA
首先根据上一段代码的查找结果进行判定,即如果当前目录下Desktop_.ini文件存在,那么就将该文件的文件属性调整为NORMAL,暂停一毫秒,将文件删除。如果从上面开始文件就不存在会直接跳转到00408110,下一个未知函数,004078E0,直接看他执行的结果,
由截图可以发现,在获取了当前文件的路径后,该函数利用了很大的一片区域来写入了大量看似无意义的字符。结合右边分析出的ASCII码,发现这些可以理解为是一个暴力破解字典,病毒编写者企图利用暴力破解的方式,来攻破计算机中的某些验证机制。那么这里可以将sub_4078E0函数重命名为:WriteVirusInfoToMem.
下一个函数比较迷,
给edx赋了0,再判断他是不是0,一定会跳转了,那么不妨将这个操作理解为某种标志的设置,将sub_403C44重命名为:SetZeroFlags.
再看下一个函数,函数00403ECC,
由于使用Delphi写的,首地址减4就是文件长度,可以知道,该函数返回文件的长度,重命名Getlen。
接着跳转到00408163处
首先会验证文件长度是不是0,再验证文件末端是不是0,尾端值是0,不跳转,继续向下运行。
到这里,他会先比较两个值,由于都是0,所以不会跳转,接着调用函数0040277C,该函数以前分析过,用于输出文件的路径,所以此时病毒文件的路径储存于eax中。
跟进下面的0040532C函数,
他调用了一个API函数,该函数用于将缓冲区的字符变成大写字符,所以该函数的功能应该就是将字符变成大写,改一下名,“ToUpper”。
跟进004054BC函数,他调用API GetSystemDirectoryA,这个函数的作用是返回系统路径,所以可以改名,GetSystemDir
执行下一个函数,在执行前,系统路径被赋值给edx
函数执行完
最后出现字符串,可知,他将两个字符串拼起来了,改名“Twostringcat”,再往下是
结合OD动态分析可知,这段程序首先将之前连接好的路径字符串转换为大写字母,然后将该字符串与之前所转换的病毒程序路径的大写字符串作对比操作,这里执行时,二者是不同的,所以接下来的跳转不成立(如果想要理解这里为什么要利用比对的操作,可以结合之前文章中对于病毒的行为分析,病毒会将自身改名为“spoclsv.exe”,并复制到“drivers”目录中,这里的对比就是要确认病毒到底有没有被复制到该目录下,如果没有,即对比结果不为零,那么就执行接下来的病毒复制操作)。dword_408658中保存的是字符串“spoclsv.exe”。再往下
三个一样的函数,他们用来查找进程并关闭。
再往下
他调用了WinExec(用于运行指定程序),
这里运行的是复制之后的程序,也就是说,该程序到此为止,剩下的功能由复制后的程序完成。