前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >简单病毒样本分析

简单病毒样本分析

作者头像
码农UP2U
发布2020-08-26 15:06:35
1.9K0
发布2020-08-26 15:06:35
举报
文章被收录于专栏:码农UP2U码农UP2U

病毒样本分析分类

病毒样本分析大致分为两种,一种是行为分析,一种是逆向分析

行为分析主要是通过系统监控软件,监控系统中各资源或环境的变化,比如监控注册表、监控文件、监控进程,以及监控网络等。当然了,还可以通过 HIPS 软件来监控病毒的行为。各类系统监控工具或者是 HIPS 软件都是在系统的不同层面上进行了不同方式的 HOOK。比如说在内核层进行 HOOK,在应用层进行 HOOK。HOOK 的方式也是多样化的,比如直接 HOOK API 函数,或者 HOOK SSDT 表中的内核函数。

逆向分析主要是通过静态分析或者动态调试来查看病毒的反汇编代码,通过断点或者单步来观察病毒的内存数据、寄存器数据等相关内容。

行为分析可以快速的确定病毒的行为从而写出专杀工具,但是对于感染型的病毒是无法通过行为分析进行分析的,或者病毒需要某些触发条件才能执行相应的动作,这样因为系统环境的因素,也无法通过行为分析得到病毒的行为特征。逆向分析通过查看病毒的各个分支流程可以完整的、全面的查看病毒的各个流程,包括病毒需要在某些条件下才被触发的流程,都可以通过查看反汇编代码进行查看。但是,对于病毒的逆向分析需要有 Windows 的开发能力,需要有阅读汇编的代码。相对于行为分析来讲,要求的技术含量更高一些。

实例演示

我们通过一个真实的病毒样本,进行一次逆向分析,希望可以对病毒分析的入门者有一定的帮助。

下载到样本后,放置到虚拟机中,虚拟机最好也处于断网情况,因为我们不确定病毒到底有哪些行为。由于我们是逆向分析,即使一些需要连网后才有的病毒行为,我们也能通过反汇编代码一览无余。放置在虚拟机中后,我们例行用 PEID 查壳之。幸运的是这个病毒没有,而且是用VC编写的。

由于无壳,省去了脱壳的步骤。我们用 OD 载入病毒,由于是 VC 编写的直接跳过 VC 的启动函数,来到真正的病毒代码处,有点分析经验的人都能一眼看出哪些部分是 VC 的启动代码,关于如何跳过病毒的启动代码就不介绍了。我们直接来到病毒的代码处,病毒的代码如下所示(为了保证代码的美观,我去掉了 OD 中机器码的那一列,把注释列放到了对应代码的上一行):

代码语言:javascript
复制
00401457   PUSH EBP
00401458   MOV EBP,ESP
0040145A   SUB ESP,418
00401460   PUSH EBX
00401461   PUSH ESI
00401462   PUSH EDI
00401463   CALL 00401160

00401457 地址处是病毒代码的开始位置,也就是我们在用 VC 写代码是的 main() 函数处。在 00401463 地址处的代码 CALL 00401160 是调用了一个函数,我们查看被调用函数的代码,代码如下:

代码语言:javascript
复制
00401160   PUSH EBP
00401161   MOV EBP,ESP
00401163   SUB ESP,1C
00401166   LEA EAX,DWORD PTR SS:[EBP-4]
00401169   PUSH EAX
0040116A   PUSH 28
           ; kernel32.GetCurrentProcess
0040116C   CALL DWORD PTR DS:[<&KERNEL32.GetCurrent>]
00401172   PUSH EAX
           ; ADVAPI32.OpenProcessToken
00401173   CALL DWORD PTR DS:[<&ADVAPI32.OpenProces>]
00401179    TEST EAX,EAX
0040117B   JE SHORT 004011CC
0040117D   LEA EAX,DWORD PTR SS:[EBP-C]
00401180   PUSH EAX
           ; ASCII "SeDebugPrivilege"
00401181   PUSH 00403148
00401186   PUSH 0
           ; ADVAPI32.LookupPrivilegeValueA
00401188   CALL DWORD PTR DS:[<&ADVAPI32.LookupPriv>]
0040118E   TEST EAX,EAX
00401190   JNZ SHORT 0040119D
00401192   PUSH DWORD PTR SS:[EBP-4]
           ; kernel32.CloseHandle
00401195   CALL DWORD PTR DS:[<&KERNEL32.CloseHandl>]
0040119B   LEAVE
0040119C   RETN

从代码中我们可以看出,00401160 地址处的函数依次调用了如下的 API 函数,分别是:GetCurrentProcess()->OpenProcessToken()->LookupPrivilegeValueA()->CloseHandle()。从前三个 API 函数我们可以看出,这个函数的作用是调整当前进程的权限,我们配合 00401181 地址处入栈的字符串 “SeDebugPrivilege” 可以看出,这个函数将当前进程的权限调整为具有调试的权限。

回到前面继续看我们病毒的主流程,接着上面的代码如下:

代码语言:javascript
复制
00401468   MOV ESI,103
           ; [ebp - 418]保存系统目录
0040146D   LEA EAX,DWORD PTR SS:[EBP-418]
00401473   PUSH ESI
00401474   PUSH EAX
           ; kernel32.GetSystemDirectoryA
00401475   CALL DWORD PTR DS:[<&KERNEL32.GetSystemDirector>]
           ; [ebp - 314]保存Windows目录
0040147B   LEA EAX,DWORD PTR SS:[EBP-314]                 
00401481   PUSH ESI
00401482   PUSH EAX
           ; kernel32.GetWindowsDirectoryA
00401483   CALL DWORD PTR DS:[<&KERNEL32.GetWindowsDirecto>]
           ; [ebp - 10c]保存当前病毒文件的路径(包含病毒文件名)
00401489   LEA EAX,DWORD PTR SS:[EBP-10C]
0040148F   PUSH ESI
00401490   XOR EBX,EBX
00401492   PUSH EAX
00401493   PUSH EBX
           ; kernel32.GetModuleFileNameA
00401494   CALL DWORD PTR DS:[<&KERNEL32.GetModuleFileName>]
           ; MSVCRT.strrchr
0040149A   MOV EDI,DWORD PTR DS:[<&MSVCRT.strrchr>]
004014A0   LEA EAX,DWORD PTR SS:[EBP-10C]
004014A6   PUSH 5C
004014A8   PUSH EAX
           ; 从右找出第一个"\"的位置
004014A9   CALL EDI
004014AB   POP ECX
004014AC   CMP EAX,EBX
004014AE   POP ECX
004014AF   JE SHORT 004014B3
           ; 得到病毒的当前路径
004014B1   MOV BYTE PTR DS:[EAX],BL
004014B3   PUSH 1
           ; 此处是对麦克风设备进行设置,这里没有详细看
004014B5   CALL 004016AC
004014BA   POP ECX
004014BB   PUSH EBX

004014BC   PUSH EBX
004014BD   PUSH EBX
004014BE   PUSH 00401349
004014C3   PUSH 400
004014C8   PUSH EBX
           ; kernel32.CreateThread
004014C9   CALL DWORD PTR DS:[<&KERNEL32.CreateThread>]

看到该处反汇编代码处,我们可以看出,病毒获得了系统目录及 Windows 目录的路径,还有病毒的当前路径,以及病毒的文件名。在 004014c9 地址处,调用了 CreateThread() 函数,该函数用来创建一个线程。CreateThread() 函数的函数原型如下:

代码语言:javascript
复制
HANDLE CreateThread(
  LPSECURITY_ATTRIBUTESlpThreadAttributes, // SD
  DWORD dwStackSize,                        // initial stack size
  LPTHREAD_START_ROUTINElpStartAddress,    // thread function
  LPVOID lpParameter,                       // thread argument
  DWORD dwCreationFlags,                    // creation option
  LPDWORD lpThreadId                        // thread identifier
);

CreateThread() 函数的第三个参数是线程函数的地址,由于 WIN32 API 的调用方式是 stdcall 方式,因此函数的入栈方式是从右至左,那么线程的函数地址是在 004014be 的指令给出,该处的指令为 push 00401349。既然知道了线程函数的地址是 401349,那么我们就看一下这个线程函数的代码完成了什么样的功能,代码如下:

代码语言:javascript
复制
00401349   PUSH EBP
0040134A   MOV EBP,ESP
0040134C   PUSH ECX
0040134D   PUSH ECX
0040134E   PUSH EBX
0040134F   PUSH ESI
00401350   PUSH EDI
00401351   NOP
           ; USER32.SendMessageA
00401352   MOV ESI,DWORD PTR DS:[<&USER32.SendMessageA>]
……
00401367   PUSH EDI
           ; ASCII "AVP.AlertDialog"
00401368   PUSH 00403134
           ; USER32.FindWindowA
0040136D   CALL DWORD PTR DS:[<&USER32.FindWindowA>]
……
00401384   PUSH EDI
00401385   PUSH EAX
           ; USER32.FindWindowExA
00401386   CALL DWORD PTR DS:[<&USER32.FindWindowExA>]
……
004013AA   PUSH EDI
004013AB   PUSH EDI
004013AC   PUSH DWORD PTR SS:[EBP-8]
           ; USER32.FindWindowExA
004013AF   CALL DWORD PTR DS:[<&USER32.FindWindowExA>]
……
           ; USER32.FindWindowExA
004013D8    CALL DWORD PTRDS:[<&USER32.FindWindowExA>]
……
004013FD   PUSH EDI
004013FE   PUSH DWORD PTR SS:[EBP-8]
           ; USER32.FindWindowExA
00401401   CALL DWORD PTR DS:[<&USER32.FindWindowExA>]
……
00401420   PUSH EDI
           ; ASCII "AVP.Product_Notification"
00401421   PUSH 004030DC
           ; USER32.FindWindowA
00401426   CALL DWORD PTR DS:[<&USER32.FindWindowA>]
……
0040143C   PUSH EDI
           ; USER32.FindWindowA
0040143D   CALL DWORD PTR DS:[<&USER32.FindWindowA>]
……
0040144F   PUSH EAX
00401450   CALL ESI
00401452   JMP 0040135F

从该反汇编代码处我们能看出,整个线程函数处于一个死循环中,因为函数的最后一句代码 00401452 jmp 0040135f 是一个向上的跳转指令。在循环中基本上只完成了一个任务,也就是不断的查找(查找窗口调用的是 FindWindowEx() 函数)某个窗口,只要找到那个窗口就调用 SendMessage 函数向它发送消息。通过它查找的窗口是 “AVP.AlertDialog” 和 “AVP.Product_Notification”,说明这段代码是通过发送消息来躲过特定杀毒软件的主动防御。我们接着看病毒的主要流程,代码如下:

代码语言:javascript
复制
004014CF   LEA EAX,DWORD PTR SS:[EBP-314]
           ; windows目录
004014D5   PUSH EAX
004014D6   LEA EAX,DWORD PTR SS:[EBP-10C]
          ; 病毒当前目录
004014DC   PUSH EAX
          ; MSVCRT._stricmp
004014DD   CALL DWORD PTR DS:[<&MSVCRT._stricmp>]
004014E3   POP ECX
004014E4   TEST EAX,EAX
004014E6   POP ECX
          ; 这个分支很重要,它判断病毒的所在位置
004014E7   JNZ 00401612

这段代码是判断病毒的当前位置,病毒的当前位置决定了病毒的流程走向。这是在病毒中一个很重要的分支。由于目前病毒不处于 C:\windows 目录下,那么流程必然会跳走,我们先来看跳走的流程,返回来在继续查看未跳走的流程。

代码语言:javascript
复制
          ; MSVCRT.sprintf
00401612    MOV EDI,DWORD PTRDS:[<&MSVCRT.sprintf>]
00401618    LEA EAX,DWORD PTRSS:[EBP-314]
          ; ASCII "mppds.exe"
0040161E    PUSH 00403020                                     
00401623    PUSH EAX
00401624    LEA EAX,DWORD PTRSS:[EBP-210]
            ; ASCII"%s\%s"
0040162A    PUSH 0040307C
0040162F    PUSH EAX
00401630    CALL EDI
00401632    ADD ESP,10
            ; 保存病毒路径
00401635    LEA EAX,DWORD PTRSS:[EBP-10C]
0040163B    PUSH ESI
0040163C    PUSH EAX
0040163D    PUSH EBX
          ; kernel32.GetModuleFileNameA
0040163E    CALL DWORD PTRDS:[<&KERNEL32.GetModuleFileNameA>]
00401644    LEA EAX,DWORD PTRSS:[EBP-210]
0040164A    PUSH EBX
0040164B    PUSH EAX
0040164C    LEA EAX,DWORD PTRSS:[EBP-10C]
00401652    PUSH EAX
            ;kernel32.CopyFileA
00401653    CALL DWORD PTRDS:[<&KERNEL32.CopyFileA>]
          ; kernel32.GetCurrentProcessId
00401659    CALL DWORD PTRDS:[<&KERNEL32.GetCurrentProcessId>]
0040165F    PUSH EAX
00401660    LEA EAX,DWORD PTRSS:[EBP-10C]
00401666    PUSH 40
00401668    PUSH EAX
00401669    LEA EAX,DWORD PTRSS:[EBP-210]
0040166F    PUSH 40
00401671    PUSH EAX
00401672    8D85 ECFCFFFF   LEA EAX,DWORD PTR SS:[EBP-314]
          ; ASCII "%s %c%s%c%d"
00401678    PUSH 004030B4
0040167D    PUSH EAX
0040167E    CALL EDI
00401680    ADD ESP,1C
00401683    LEA EAX,DWORD PTRSS:[EBP-314]
00401689    PUSH EBX
          ; 以带参数的方式启动拷贝到C盘的mppds.exe文件
0040168A    PUSH EAX
          ; kernel32.WinExec
0040168B    CALL DWORD PTRDS:[<&KERNEL32.WinExec>]

上面的反汇编代码调用了 CopyFile() 函数将自身拷贝到 Windows 目录下,并且调用 WinExec() 函数启动拷贝到 Windows 目录下的 mppds.exe,在启动的时候为 mppds.exe 带了参数,对 WinExec() 的调用栈如下所示:

代码语言:javascript
复制
0012FAF8  0012FC10  |CmdLine ="C:\WINDOWS\mppds.exe @C:\Documents and Settings\Administrator\桌面\4sy.exe@1488"
0012FAFC  00000000  \ShowState = SW_HIDE

我们关闭当前 OD,不要让病毒以该参数启动,我们启动另外一个 OD 来调试 Windows 目录下的 mppds.exe ,并且调试时带上如上的参数。我们直接运行到那个关键的跳转处,继续往下执行,反汇编代码如下:

代码语言:javascript
复制
             ; kernel32.GetCommandLineA
004014ED    CALL DWORD PTRDS:[<&KERNEL32.GetCommand>]
……
            ; 省略了中间的代码是得到参数中病毒原来的位置
00401542    ADD ESP,18
00401545    MOV BYTE PTR SS:[EBP+EDI-10D],BL
0040154C    PUSH DWORD PTR SS:[EBP-4]
0040154F    PUSH EBX
00401550    PUSH 1F0FFF
            ; kernel32.OpenProcess
00401555    CALL DWORD PTRDS:[<&KERNEL32.OpenProces>]
……
00401561    PUSH EBX
00401562    PUSH EDI
            ; kernel32.TerminateProcess
00401563    CALL DWORD PTRDS:[<&KERNEL32.TerminateP>]
00401569    PUSH -1
0040156B    PUSH EDI
; kernel32.WaitForSingleObject
0040156C    CALL DWORD PTRDS:[<&KERNEL32.WaitForSin>]
00401572    LEA EAX,DWORD PTR SS:[EBP-10C]
00401578    PUSH EAX
            ; kernel32.DeleteFileA
00401579    CALL DWORD PTRDS:[<&KERNEL32.DeleteFile>]
0040157F    TEST EAX,EAX
00401581    JNZ SHORT 0040158D
00401583    PUSH 1
            ; kernel32.Sleep
00401585    CALL DWORD PTRDS:[<&KERNEL32.Sleep>]
0040158B    JMP SHORT 00401572

上面的代码是 GetCommandLine() 函数得到病毒的原来的位置,然后通过 OpenProcess() 和 TerminateProcess() 两个函数结束掉原来病毒的进程,然后调用 DeleteFile() 函数删除原来的病毒。

代码语言:javascript
复制
            ; ASCII "explorer.exe"
0040158D    PUSH 004030C4
            ; 该函数用来查找explorer.exe进程的PID
00401592    CALL 00401000
……
            ; 释放mppds.dll文件
004015CD    CALL 0040109C
……
             ; 注入mppds.dll到explorer.exe进程
004015E6    CALL 004011F9
……
            ; 将mppds.exe写入注册表的启动项
00401609    CALL 004017A5
……
            ; 病毒的另一个关键分支执行完毕
00401610    JMP SHORT 00401691

00401592 地址处的 CALL 是调用了一个函数,其函数的作用是获得 explorer.exe 进程的 PID,在其反汇编代码中依次调用了几个函数:

CreateToolhelp32Snapshot()->Process32First()->Process32Next()->CloseHandle(),这个是个典型的遍历系统进程的代码,而在遍历的过程中始终在拿进程名与 “explorer.exe” 进行比较,如果相等的会则结束遍历过程,并返回 explorer.exe 进程的 PID。

004015cd 处的 CALL 调用的函数是释放一个 DLL 文件到系统路径,其反汇编代码中依次调用了如下几个函数:

FindResource()->SizeofResource()->LoadResource()->SetHandleCount()->fopen()->fwrite()->fclose()。这也是一个典型的通过资源释放文件的过程,该过程从 mppds.exe 文件的资源中释放出 mppds.dll 文件到系统目录下。

004015e6 地址处的 CALL 调用的函数是将释放出来的 mppds.dll 文件注入到 explorer.exe 进程中,也就是说地址 401592 的 CALL 和地址 15cd 的 CALL,是为当前地址的注入做准备的。在分析代码时我们可以看一些主要的代码,整个功能也就明白了,004015e6 地址处 CALL 的函数中调用了 CreateRomoteThread() 函数,通过这一个函数我们就能确定该函数的目的是用来进行注入的。

00401609 地址处的 CALL 是将病毒的加入到注册表的启动项中,因为在这个 CALL 中调用了注册表操作相关的函数,另外配合几个字符串常量,我们就很容易的发现了该函数的作用了。

到这里,整个病毒的功能我们就分析完成了,至于释放出来的 mppds.dll 的功能这里就不再继续分析了。我们这里完整的分析了一个病毒,从我们分析的过程中可以看出,熟悉 Win32 API 和一些编程知识对于我们分析病毒是非常有帮助的。在反病毒厂商招聘病毒分析师时其中就要求熟悉 Win32 API、C 语言、汇编语言等知识。如果有对反病毒有兴趣的朋友,可以到一些反病毒网站参考一下他们关于病毒分析的招聘要求,就可以有方向性的进行学习了。希望这篇文章能给你带来帮助。

注:文章时很早以前写的,病毒样本已经找不到了,而且提供病毒样本也违法。

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2020-03-31,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 码农UP2U 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档