前言

在本系列的文章中,对每一个病毒分析的最后一个部分,若无特殊情况,我都会采用逆向分析的手段来为读者彻底剖析目标病毒。但是之前的“熊猫烧香”病毒,我用了三篇文章的篇幅(每篇2500字左右)也仅仅分析了病毒的三分之一,而且还没分析到病毒的核心部分。主要也是因为那是我这个系列为大家分析的第一个病毒,为了将一些原理性的东西说清楚,所以文章略显冗长,也主要是照顾一下初学的朋友,摒弃那些高大上的东西,将我的实际分析过程完整地呈现出来。相信大家在认真阅读完那三篇文章后,都能够掌握基本的分析方法,那么我在以后的文章中,只会讨论病毒中的一些比较重要的部分,会跳跃式地讲解,略去无关紧要的内容。当然,我依旧会配上详尽的图片与文字进行说明,让大家仅仅是看这些文章,就宛如亲自动手了一样。

一般来说,病毒分析不会涉及到算法问题,如果是要分析算法(如我之前对于CM4注册机制的分析),那么我们更多地是需要关注程序的流程与逻辑,一般不深究CALL的具体内容。而病毒分析则往往需要搞清楚各个不同的CALL的意义,才能够弄清楚病毒的行为。所以本文的第一部分着重讲述对这些CALL的剖析。而第二部分则简单讨论一下进程守护技术的实现。

逆向分析

这里我们跳过程序的初始化部分,来到第一个API函数的位置:

图1

我们能够直接看到的第一个API函数是GetModuleFileName,这个函数用于获取当前进程已加载模块的文件的完整路径,该模块必须由当前进程加载。而该函数的返回值则是文件路径长度,由截图可见,该返回值保存在了EAX中,为2B,也就是说路径长度为2B个字符。可以看一下所返回的路径是什么。路径保存在图1中的“PathBuffer”中,跟踪该地址查看:

图2

可见程序已正确获取了当前文件的地址。然后继续分析下一个API函数:

图3

这里出现了ShellExecute这个函数,它的功能是运行一个外部程序(或者是打开一个已注册的文件、打开一个目录、打印一个文件等等),并对外部程序有一定的控制。具体到本程序,ShellExecute会运行Explorer.exe程序来打开“d:\”,其实也就是使用程序管理器打开D盘根目录。但是执行这个API函数是有条件的,它需要根据图3中第二行CALL语句的结果进行判定,那么有必要进入这个CALL,看看需要满足什么条件才能够执行ShellExecute。

进入oso.00403C48这个函数,可以看到如下代码:

图4

程序会对EAX和EDX中的内容进行比对,其中EAX保存的字符串就是我们之前使用GetModuleFileName所获取的当前文件的路径,只不过被转化成了大写字符。而EDX保存的是D盘根目录下的OSO.EXE这个文件路径。二者在这里很明显是不同的。所以图4中黄色高亮显示的条件跳转语句也就不成立,程序会继续顺序执行:

图5

这里需要说明的是,由于本病毒是由Delphi编写的,那么字符串首地址减去4后,取出的4字节内容便是此字符串的长度。因此图5中的前两句代码意思就是获取两个路径的字符数,然后通过相减进行比较。这里很明显当前路径的字符数量是要大的,因此黄色高亮显示的条件跳转成立,来到oso.00403C6B的位置:

图6

这里依旧是字符的比较,由于二者不相等,所以上图中最后一句的条件跳转成立,来到oso.00403CD1的位置:

图7

这里比较的是盘符,也是不相等的,所以条件跳转成立,本函数也就执行完毕了。综合上述分析,这段函数的功用是判断当前所执行的文件是不是位于D盘的根目录下,如果是,则执行图3中的ShellExecute这个函数,反之则跳过这个函数执行。由于我们的这个程序是位于桌面上的,因此不执行ShellExecute函数。

接下来程序还会继续判断当前程序是否位于E、F、G、H、I盘根目录下,如果不是,那么也就不执行相应的ShellExecute函数。

之后程序会调用名为oso.004050F0的函数,进入其内部分析:

图8

可见病毒程序调用了GetSystemDirectory函数用于获取系统目录。一般来说,恶意程序使用这个函数的目的就是要将自身复制到系统目录中,以迷惑用户(详见《反病毒攻防研究第001篇:自我复制与自删除》)。之后病毒程序会将字符“severe.exe”与上面获得的系统目录字符串进行组合,新的路径也就是病毒程序需要隐藏的位置:

图9

之后可以看到CreateFile函数:

图10

但是这个CreateFile函数的执行是有条件的,它取决于上图中第一行代码中的CALL的返回值。进入这个CALL进行分析,可以找到:

图11

程序调用了FindFirstFile函数来查找系统目录中有没有severe.exe这个文件,如果没有(返回值为-1)则不执行图10中的CreateFile函数。由此可见,CreateFile函数在这里的作用不是创建文件,而是打开文件。接下来就是文件的复制操作:

图12

之后又是一系列的文件复制,病毒会将自身改名为tfidma.exe,并复制到系统目录中。还会将自身改名为conime.exe,复制到系统目录的drivers文件夹中。类似的操作不再赘述。之后程序就会再次调用ShellExecute函数,以执行所创建出来的这些程序。因此在“资源管理器”中就会出现severe.exe、conime.exe与tfidma.exe等进程。可以说在这个时候,我们的计算机就已经中病毒了。接下来我们会遇到线程的创建函数:

图13

CreateThread函数往往与Sleep或者WaitForSingleObject函数相配合使用。因为每个线程都有自己的CPU时间片,当主线程创建了新线程后,它的CPU时间片有时并没有完,它还可以继续执行。有时候如果主线程的代码非常少,那么在CPU指定的CPU时间片中主线程执行完后就退出了。主线程结束,那么意味着程序也就结束了,所以在这种情况下,我们自己创建的线程根本就没有被执行到。所以我们需要让主线程等待我们创建的线程,就需要使用Sleep或者WaitForSingleObject函数。本程序所采用的就是Sleep函数(等待1.3秒)。

回到图13中,根据OD对CreateThread函数的解析可以知道,该线程所调用的是oso.00404958这个函数。分析这个函数可以知道,它主要是创建了名为“hx1.bat”的批处理文件并执行,而该批处理的内容为:

@echo off
set date=2004-1-22
ping ** localhost > nul
date %date%
del %0

其主要作用就是修改系统时间,测试本地网络系统并删除自身。病毒的常规分析部分就是这些。

进程守护技术原理

进程的守护技术最早应该是源于“中国黑客病毒(worm.runouce)”,它开创性地采用了“三线程”结构。创建三线程就是为了更好地保护程序自身不被关闭和删除。我们可以将想要执行的代码放在主线程里,然后再生成两个辅助线程,它们的功能就是实现对程序的保护,防止程序被用户关闭或删除。在此,称我们的可执行文件的进程为主进程。两个辅助线程相互实时监视,如果监视对象被关闭了,就重新创建线程或进程。比如病毒程序可以选择Explorer.exe和Taskmgr.exe作为远程进程驻体。如果用户知道了远程线程的驻体为资源管理器后,就会打开任务管理器来结束Explorer,这时我们再把远程线程驻入到任务管理器中。也就是说,只要Explorer或Taskmgr有一个存在,就不可能结束主进程。如果有其它结束进程的工具,你就可以将其关闭掉,只要资源管理器和任务管理器均不存在时,就没有驻体来维持远程进程。不过,如果我们选择的远程进程为随机的,或者就是病毒自创的,这就不容易发现了。

这种三线程结构的程序框架大致如下(代码来自《浅析三线程程序开发思路与实现》):

// 1.主线程:main
// 获得操作系统的系统目录
GetSystemDirectory(syspath,MAX_PATH);
// 查询系统目录下病毒是否存在
FindFirstFile(virusname,&fdata);
// 如果系统目录下没有,在将正在运行的程序复制到系统目录下
CopyFile(curname,tname,TRUE);
// 在查询完毕后,关闭相关句柄
FindClose(ffhandle);
// 打开系统目录下的文件
CreateFile(kname,GENERIC_WRITE,FILE_SHARE_WRITE,NULL,OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL,NULL);
// 修改时间
SetFileTime(fchandle,&ftime,NULL,&ftime);
// 设置属性
SetFileAttributes(kname,FILE_ATTRIBUTE_READONLY|FILE_ATTRIBUTE_HIDDEN|FILE_ATTRIBUTE_SYSTEM );
// 创建驻留在主进程内的辅助监视线程
CreateThread(NULL,0,watch,(LPVOID)rthread,0,NULL); // 2.本地辅助监视线程:watch
// 以查询方式打开注册表的HKEY_LOCAL_MACHINE\Software\Microsoft\Windows\CurrentVersion\Run
RegOpenKeyEx(HKEY_LOCAL_MACHINE,rgspath,0,KEY_QUERY_VALUE,&hkey);
// 查询是否存在virusname的键值
RegQueryValueEx(hkey,_T("virusname"),NULL,NULL,(LPBYTE)lpdata,&dwbuflen);
// 如果没有相关键值,就以写方式再次打开注册表
RegOpenKeyEx(HKEY_LOCAL_MACHINE,rgspath,0,KEY_WRITE,&hkey);
// 写入我们想要的东西,系统每次启动都会运行我们的可执行文件;
RegSetValueEx(hkey,_T("virusname"),NULL,type,(const byte *)wtname,dwbuflen);
// 获得远程线程的运行情况,看是否为STILL_ACTIVE,如果不是则创建远程线程
GetExitCodeThread(wethread,&exitcode); // 3.远程线程:remote
// 以所有可能的访问方式打开主进程,以便监视主进程的运行情况
tOpenProcess(PROCESS_ALL_ACCESS,FALSE,erp->rpmousepid);
// 等待直到主进程结束
tWaitForSingleObject(erp->rpprocesshandle,INFINITE);
// 重新启动我们的可执行文件
tWinExec(erp->rpwinexecname, 0); // 4.获得进程ID:processtopid
// 列举所有的进程
EnumProcesses(lpidprocesses,sizeof(lpidprocesses),&cbneeded);
// 以查询信息和读取的方式打开进程
OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ,FALSE,lpidprocesses[i]);
//获得进程模块的句柄
EnumProcessModules(hprocess,&hmodule,sizeof(hmodule),&cbneeded);
// 获得特定模块的名字,以备比较
GetModuleBaseName(hprocess,hmodule,normalname,sizeof(normalname)); // 5.创建远程线程:createremote
// PROCESS_CREATE_THREAD for CreateRemoteThread
// PROCESS_VM_OPERATION for VirtualAllocEx
// PROCESS_VM_WRITE for WriteProcessMemory
OpenProcess(PROCESS_CREATE_THREAD|PROCESS_VM_OPERATION|PROCESS_VM_WRITE,FALSE,remotepid);
// 在远程进程中分配空间,以备将线程代码置入其中
VirtualAllocEx(rphandle,NULL,cb,MEM_COMMIT,PAGE_EXECUTE_READWRITE);
// 将远程线程remote的代码写入到远程进程的地址空间中
WriteProcessMemory(rphandle,remotethr,(LPVOID)remote,cb,NULL);
// 将远程线程所需的参数也写入到远程进程的地址空间中
WriteProcessMemory(rphandle,remotepar,(LPVOID)&rp,cb,NULL);
// 创建远程监视线程
CreateRemoteThread(rphandle,NULL,0,(LPTHREAD_START_ROUTINE)remotethr,(LPVOID)remotepar,0,NULL);

毕竟本系列不是教大家编写病毒,而是剖析病毒的大概思路,所以上述程序大家有个大概的印象即可,这样在以后的实际分析中,就能有大概的应对思路。

小结

至此,QQ盗号木马(oso.exe)病毒程序的分析就到这里。其实这几篇文章也就主要讨论了进程守护技术的实现与杀除。在以后的文章中,我只会讨论病毒中一些有特色的部分,也会与时俱进,研究一些目前流行的病毒,希望大家喜欢。

病毒木马查杀实战第012篇:QQ盗号木马之逆向分析的更多相关文章

  1. 病毒木马查杀实战第025篇:JS下载者脚本木马的分析与防御

    前言 这次我与大家分享的是我所总结的关于JS下载者脚本木马的分析与防御技术.之所以要选择这样的一个题目,是因为在日常的病毒分析工作中,每天都会遇到这类病毒样本,少则几个,多则几十个(当然了,更多的样本 ...

  2. 病毒木马查杀实战第009篇:QQ盗号木马之手动查杀

    前言 之前在<病毒木马查杀第002篇:熊猫烧香之手动查杀>中,我在不借助任何工具的情况下,基本实现了对于"熊猫烧香"病毒的查杀.但是毕竟"熊猫烧香" ...

  3. 病毒木马查杀实战第011篇:QQ盗号木马之专杀工具的编写

    前言 由于我已经在<病毒木马查杀第004篇:熊猫烧香之专杀工具的编写>中编写了一个比较通用的专杀工具的框架,而这个框架对于本病毒来说,经过简单修改也是基本适用的,所以本文就不讨论那些重叠的 ...

  4. 病毒木马查杀实战第010篇:QQ盗号木马之十六进制代码分析

    前言 按照我的个人习惯,在运用诸如IDA Pro与OllyDBG对病毒进行逆向分析之前,我都会利用一些自动化的工具,通过静态或动态的分析方法(参见<病毒木马查杀第008篇:熊猫烧香之病毒查杀总结 ...

  5. 病毒木马查杀实战第017篇:U盘病毒之专杀工具的编写

    前言 经过前几次的讨论,我们对于这次的U盘病毒已经有了一定的了解,那么这次我们就依据病毒的行为特征,来编写针对于这次U盘病毒的专杀工具. 专杀工具功能说明 因为这次是一个U盘病毒,所以我打算把这次的专 ...

  6. 病毒木马查杀实战第020篇:Ring3层主动防御之基本原理

    前言 假设说我们的计算机中安装有杀毒软件,那么当我们有意或无意地下载了一个恶意程序后.杀软一般都会弹出一个对话框提示我们,下载的程序非常可能是恶意程序,建议删除之类的.或者杀软就不提示.直接删除了:或 ...

  7. 病毒木马查杀实战第015篇:U盘病毒之脱壳研究

    前言 因为我们的终于目标是编写出针对于这次的U盘病毒的专杀工具.而通过上次的分析我们知道,病毒有可能在不同的计算机中会以不同的名称进行显示.假设真是如此,那么就有必要在此分析出病毒的命名规律等特征,然 ...

  8. 病毒木马查杀实战第013篇:一个基于.NET的“敲竹杠”病毒研究

    前言 恶意程序发展至今,其功能已经从最初的单纯破坏,不断发展为隐私的窥探,信息的盗取,乃至如今非常流行的"敲竹杠"病毒,用于勒索.可见随着时代的发展,病毒的作者们往往也是想利用自己 ...

  9. 病毒木马查杀实战第016篇:U盘病毒之逆向分析

    比对脱壳前后的程序 我们这次所要研究的是经过上次的脱壳操作之后,所获取的无壳病毒样本.其实我们这里可以先进行一下对比,看看有壳与无壳的反汇编代码的区别.首先用IDA Pro载入原始病毒样本: 图1 可 ...

随机推荐

  1. 利用CORDIC算法计算三角函数

    这里主要先介绍如何利用CORDIC算法计算固定角度\(\phi\)的\(cos(\phi)\).\(sin(\phi)\)值.参考了这两篇文章[1].[2]. 一般利用MATLAB计算三角函数时,用\ ...

  2. 基于Hi3559AV100的视频采集(VDEC-VPSS-VO)整体框图设计

    下面给出基于Hi3559AV100的视频采集整体设计,具体设计将在后续给出: 图形采集端整体设计 Hi3559AV100软件程序按结构划分可分为4层,第一层是硬件驱动层,第二层是操作系统层,第三层是媒 ...

  3. Google单元测试框架gtest之官方sample笔记3--值参数化测试

    1.7 sample7--接口测试 值参数不限定类型,也可以是类的引用,这就可以实现对类接口的测试,一个基类可以有多个继承类,那么可以测试不同的子类功能,但是只需要写一个测试用例,然后使用参数列表实现 ...

  4. CCF(再卖菜60分)爆搜+记忆化搜索+差分约束

    201809-4 再卖菜 我使用的是爆搜解决,只得了60分. 记忆化搜索 差分约束 #include<iostream> #include<cstdio> #include&l ...

  5. Linux速通 随笔整理

    Linux速通 随笔整理 为了方便阅读,特整理了相关的学习笔记 零.大纲 一.系统安装 二.命令格式 三.文件管理 四.用户群组 五.文件处理 六.系统初始化及监控 七.硬盘初始化 八.网络原理

  6. 关于PHP的__construct()函数

    1 <?php 2 header("Content-Type:text/html;charset=utf-8");//调整为utf-8编码格式 3 class car 4 { ...

  7. idea配置struts2.5环境

    struts2不是struts1的下一代产品,是在struts1和WebWork技术的基础上进行合并后的全新框架,虽然两个名字相似,但是设计思想却有很大的不同. 使用本地的l ib 或者downloa ...

  8. POJ_2452 Sticks Problem 【ST表 + 二分】

    一.题目 Sticks Problem 二.分析 对于$i$和$j$,并没有很好的方法能同时将他们两找到最优值,所以考虑固定左端点$i$. 固定左端点后,根据题意,$a[i]$是最小值,那么现在的问题 ...

  9. python模块的打包和安装

    假设需要打包的模块文件名是mm.py,代码如下: a = 2 在mm.py文件的同目录下新建一个setup.py文件,代码如下: from distutils.core import setup se ...

  10. 对控制器类型“StudentController”的操作“Edit”的当前请求在下列操作方法之间不明确:

    "/"应用程序中的服务器错误. 对控制器类型"StudentController"的操作"Edit"的当前请求在下列操作方法之间不明确:类型 ...