恶意代码分析之修改CRT的Dropper分析

 

0x00 前言

在上一小节,我们已经初步结合IDA和x64dbg对KimSuky的一个远控样本进行了一个比较完整的分析。

在这一小节,我们将会遇到一个 比较”奇怪”的样本,大部分的分析工作都需要在调试器中完成,此时,如果能够比较熟练的阅读汇编代码,将会大大的提升分析的速度。

此外,本节将是传统恶意代码分析基础教程的最后一小节。

在之后的文章中,将会更多的偏向于快速定位恶意代码功能、样本关联分析、溯源分析、恶意流量分析、移动平台样本分析、非PE样本分析等。

 

0x01 基本信息

首先,样本还是来源于app.any.run

样本hash:b7c3039203278bc289fd3756571bd468

沙箱连接为:https://app.any.run/tasks/7ea179ad-f812-40a3-b3e0-d2dfcbd11a65/

样本下载到本地之后,我们首先通过查壳工具对样本进行一个基础的检查。

使用的工具是Exeinfo,在这里可以看到,该样本无壳,由vc++的编译器编译。

 

0x02 行为分析

在之前,我们已经使用过了SysTracer.exe来分析样本的行为,在本节的样本中,我们将对比火绒剑和SysTracer.exe在行为分析中各自的优劣。

火绒剑行为分析

首先是使用火绒剑,我们启动火绒剑之后,设置好过滤条件,然后运行样本,样本运行最开始的10s左右没有任何行为,猜测是sleep了10s作为反检测的条件之一。等样本运行一会之后,我们可以在火绒剑的窗口中看到如下的界面:

即使在过滤掉了注册表行为的前提下,还是显示了高达15661的事件数,如此大量的事件数没有办法好好分析,我们可以在过滤窗口中查看一下每种动作的事件数:

通过日志过滤的动作过滤窗口,我们可以看到,样本有大量的文件行为(这里17225是包括了当前电脑中所有的文件操作行为),于是我们可以先将MT_filemon也过滤掉:

然后我们回到火绒剑的窗口中,文件操作过滤掉之后,这里就只显示了29个事件数,我们来看看这29个事件都做了什么。

首先是看到,样本多次调用了32为的cmd:C:WindowsSysWOW64cmd.exe执行cmd命令,目前还不清楚多次调用cmd都实现了些什么操作,此外,从图中可以看到样本访问了多个本地局域网的IP地址,如172.22.22.156 或是10.2.114.1这种。

我们选中NET_connect 172.22.22.156这一行,然后双击查看详情。

然后切换到<任务组>选项卡:

然后就可以看到,这些操作实际上都是通过32位的cmd程序来调用的,这也解释了之前我们在火绒剑主窗口中看到的多次cmd调用的实际用途。

在这里可以看到,cmd程序有15个文件操作,创建、更改了temp目录中部分文件的权限、然后删除了这些文件。

往下滑动,可以看到cmd还执行了一系列的注册表操作:

在最后可以看到,cmd访问了一些本地局域网的地址:

但是目前,我们并不知道cmd是通过何种方式去访问这些地址的,以及访问这些地址做什么。

到这里,通过火绒剑分析该样本的工作就基本完成,我们得知样本运行后会有一段sleep,然后会多次调用cmd执行一些奇怪的操作,包括创建文件、删除文件、注册表操作、访问本地局域网等等。

桥豆麻袋,我们刚才把样本的文件操作全过滤了,此时我们可以显示文件操作看看样本到底做了什么导致了一万五千多的文件操作,显示文件操作之后往下滑动,看到全是FILE_readdir的操作,然后观察路径,包括了一些用户个人的文件夹,此时,我们知道这里是在做文件夹遍历操作了。

通常来说,样本这种文件遍历操作有两种行为。

1是勒索样本遍历加密文件,这里运行了之后,文件一直没有被加密,可以知道应该不是勒索。

2是窃密样本在遍历查找文件,查找指定的文件或指定类型的文件。

目前来看,有可能属于后面这种情况。

SysTracer.exe

接下来,我们使用SysTracer.exe对该样本进行一个行为分析。

SysTracer.exe运行之后,相对于火绒剑,这里显示的操作就很少了,从左侧的窗口中,可以看到样本触发的文件行为只有24,注册表行为77,进程行为31。

从我选中的这一行中可以看到, 样本调用cmd执行ipconfig /all > C:DOCUME~1ADMINI~1LOCALS~1Temptempres.ip

将本地主机网卡的详细信息写入到temp目录下的res.ip文件。

SysTracer.exe的第一个优点出来了,在窗口的信息中,有着cmd操作的详细信息,我们就可以很直观的看到cmd到底做了些什么操作。

除了写入了ipconfig /all的信息之外,程序还通过tasklist将本地主机的所有进程信息写入到task.list。

滑到最后,我们还可以看到如下的关键信息:

这里可以看到,程序尝试通过IPC$将本地的一些信息上传到10.38.1.35这个机器上。

10.38.1.35这个地址很明显属于本地局域网的ip地址,这种操作我们基本可以确定,10.38.1.35这个地址已经被攻击者攻陷作为局域网横向渗透中的中转”服务器”。

至此,我们也成功使用SysTracer.exe对样本进行了行为分析。

通过SysTracer.exe我们可以知道,样本会通过cmd程序收集本地的一些基本信息,创建文件,然后上传到10.38.1.35这个机器,然后通过cmd删除那些文件。

我们无法直接说,哪个工具进行行为分析更好,只能说,在分析的时候结合多款行为分析工具,可能会有意想不到的惊喜。

 

0x03 代码分析

在IDA中加载之后,默认停留在wWinMain函数,而根据后面的调用代码我们可以得知该程序属于MFC的框架。

和普通VC编译的exe不同,MFC编写的程序有着自己独特的一套结构。

对于VC编译的exe,我们知道程序的入口点是在WinMain函数,但是MFC的程序却并不是这样。

对于MFC的程序来讲,程序一般首先会通过AfxGetThread()->PumpMessage()开始消息处理,然后经过一系列复杂的操作,最后才到用户的代码。

这里我们就不铺开讲MFC的分析原理了,之前进行行为分析的时候,我们也发现该程序没有运行界面,不能通过常规的MFC逆向思路来定位关键代码。

所以看样子只能在调试器中F7跟进到AfxGetThread函数,然后一直F8直到代码返回到用户空间了。

而我们在IDA中可以看到,程序进入到wWinMain之后,就会直接jmp然后去执行AfxGetThread

所以我们使用调试器加载该样本,然后将断点设置到wWinMain的起始地址。

od加载样本之后,默认停留在了start函数的起始地址:004381f8

我们之前在wWinMain起始地址0044AA20设置断点,然后F9跑过来:

这个时候,神奇的事情出现了,F9运行之后,程序并没有跑到wWinMain处,而是一直处在运行状态,跑了一会之后进程就结束了:

由于我们之前已经在该虚拟机中成功跑出了行为,证明样本在xp系统中是可以正常运行的,但是这里却完全没有跑到wWinMain函数,所以这里有两个可能,1是样本在wWinMain函数之前有反调试,且根据样本在调试器中的表现来看应该是一个长的sleep。2是样本的真实功能不再wWinMain中,这里MFC只是一个空壳。所以接下来,我们可以结合od和systrace来判断一下样本到底是属于哪种情况。

我们首先通过Systrace启动od,使用Systrace监视od的行为:

然后我们还是尝试在wWinMain处设置断点然后跑过去,可以看到,在Systrace的窗口中,出现了之前我们单独测试样本时的行为。

这里基本就可以确定,样本的根本就没有执行到wWinMain函数就退出了。

我们回到IDA中查看一下start函数:

start中首先call security_init_cookie,接着 jmp tmainCRTStartup

我们在调试器中,call security_init_cookie之后程序并没有反应,说明问题应该在jmp tmainCRTStartup这里。很明显,这里过去应该是CRT的代码(编译器生成),所以这里可以猜测,此样本应该是修改了CRT的。

我们在IDA中双击___tmainCRTStartup来到目标地址:

由于这里是CRT的代码,我们可以直接F5查看一下这里的伪代码(这种编译器生成的代码,一般都不是常见的语句,这种代码看汇编的话会很恼火):

这很明显和正常的CRT不同。

特别是在最后还return 了一个sub_438FE1,正常的CRT结尾应该如下:

而且在__tmainCRTStartup的开头,莫名其妙的调用了一个看起来像是用户代码的函数:

然后我们接着往后看,好像并没有其他的跳转或是函数调用,所以不出意外的话,肯定是能跑到后面的wWinMain的,这里就基本可以确定,样本是修改了CRT之后,在CRT代码中插入了sub_438F9C,然后通过sub_438F9C执行完所有的功能并退出程序。

00438F9C中一共有三个call 第一个call将两个0作为参数进行调用。

我们跟进到sub_43900C中,简要分析后注释如下:

在sub_43900C中,程序首先会通过VirtualAlloc分配ecx值大小的空间。如果分配成功,则通过GetModuleFileNameA获取当前进程的路径,然后作为参数传递到CreateFileA中,所以这里的CreateFile是用于打开当前的文件对象,而并不是创建一个文件。

我们接着往后看,成功获取到文件对象之后,程序会尝试通过SetFilePointer来设置一个读取位置,看到这里,我们基本可以知道,该程序中应该包含了一段数据,而程序在这里通过SetFilePointer的方式去读取数据。

果不其然,这里会按照指定的偏移,去读取文件数据存到到之前的lpBuffer中,然后程序就结束了,所以我们这里就分析完了sub_43900C的基本功能:从当前文件的指定位置读取数据到新分配的内存中

我们返回回去对该函数进行标注之后,在调试器中调试一下这个函数。

在调试器中直接在函数内的Virtual处设置断点,跑过来之后单步F8走一下,可以看到,成功分配了一篇内存,起始地址为:009A0000

GetModuleFileNameA成功获取:

通过CreateFileA打开当前句柄:

设置指定偏移:

然后调用ReadFile,成功将数据读取到009A0000

然后我们继续F8,执行到ret,结束该函数。

回到IDA中,看了下之前看到的那两个函数,都和之前看到的一样,没有实际功能,可以直接跳过。

且我们注意到,函数底部,出现了堆栈不平衡的错误。我们直接回到OD中,看具体运行到这里是怎样的。

果然,我们可以看到,两个call执行完之后,程序并没有像是IDA中看到的那样退出函数,而是接着往下运行,又分别在00438FF2和00438FFD这里执行了两个call指令。

我们F7跟进到0043911c函数中之后发现,该函数的指令通过动态解密的方式执行,且在执行一个大循环,代码可读性极差,经验告诉我们,这种不断的循环应该是在解密,结合我们之前分析到的read了片数据到VirtualAlloc分配的空间中,我们可以猜测该函数应该是在解密我们看到的数据。

于是我们重新运行程序,断点跑到这里,然后F8跑过去,对比一下函数执行后009A0000内存的数据:

诶 009A0000的值的确改变了,说明我们的推算是正确的,但是遗憾的是,这里我们还是没有看出来,解密后的内容是什么。只能继续往下走了。

我们双击第二个call ,可以知道这里指令实际上是call dword ptr ss:[ebp-0x10]

所以我们可以在内存窗口中看看ebp – 0x10 处的值是什么

跳转过来之后可以发现这里居然就是009A0000

也就是说程序通过第一个call解密了009A0000处的数据,然后通过call,跳转到009A0000处继续执行:

由于这里的009A0000是动态解密出来的,此时我们就没有办法在IDA中进行静态分析了,其实也可以,我们现在已经知道了009A0000这里是一个大函数,我们可以把这段二进制数据dump出来,使用IDA加载,就可以识别到一个函数。

我们选中这一大段数据,然后鼠标右键,选择保存到数据文件

然后通过IDA加载即可:

但是这里这样做的话,代码可读性还是比较差。

所以我们还是直接在od中边调试边看吧~ 我们回到OD中,可以看到,IDA没有识别出来的call,od已经自动识别出了一些系统API调用,

分别是SetErrorMode、GetSysColor、GetSystemDefaultLangID、GetAtomName
通过这几个API我们可以知道,这里是在对当前的操作系统环境进行一个初步判断,包括系统颜色、系统默认语言等

我们直接选中call 009A0B60这一行,然后回车进入该函数(不影响程序的EIP):

009A0B60进来之后,可以看到首先是对ebp – 0x1x 的地址进行赋值。

这里直接看十六进制的ASCII看不出来,我选择看看IDA是否有可用信息,你猜怎么着,IDA中很方便看

这里首先是获取来两个字符串:

%AppData%

systemprofile

然后调用一个call,我们在od中可以看到该call是expandenvironmentstrings,该函数可以扩展环境变量,使用当前用户定义的值来替换对应的环境变量字符串。

我们直接F4跑到这个函数,看一下参数的值。

从图中可以看到,该函数执行之后,? 将会代表环境变量%AppData%

接着程序call 0090340 ,参数分别是%Appdata%和systemprofile

同样的,为了节约分析时间,我们可以直接选中函数,然后回车进入到函数

进来之后有两个call,我们也选中第一个call,然后回车进去,就只是一个循环add计算。

按esc回到上一层,然后选中第二个函数,回车进来,发现和第一个call很相似

然后esc回到上层,往下滑动,发现没有其他call 了,就是一些跳转指令进行赋值的,所以我们可以直接按*号键,回到当前EIP,直接F8步过该函数,就不跟进进去了。

然后又解出一个字符串,使用GetModuleHandle获取Handle

通过IDA我们可以知道这里是获取User32.dll

然后通过GetProcAddres获取GetRawInputDeviceList,通过该函数,可以枚举连接到系统的原始输入设备。

然后调用GetRawInputDeviceList,成功获取到原始输入设备之后结束函数,如果获取失败则退出进程。

该函数执行完了之后,继续在后面一个call 009A00B0这一行回车进去:

进来之后发现又有一个VirtualAlloc,于是我们直接在VirtualAlloc这一行F4跑过来:

这次分配的的内存大小是CCC00

F8单步往下执行,eax中会出现新内存的起始地址:009B0000

继续往下走,又是熟悉的GetModuleFileNameA和CreateFile

打开的还是当前文件的句柄:

设置一个新的偏移点:

和之前一样的方式Read:

Read成功:

然后关闭句柄,然后退出函数。

根据之前的经验,返回出去之后,应该就会对这片内存进行解密了。

返回出来的两个call,通过快速分析发现没有什么功能,可以直接F8步过。

然后再下面的009A008C这里的call 009A01C0,回车进去可以看到有很多跳转指令,应该就是循环解密的地方了

我们F4运行过来,注意下面内存窗口汇总009B0000的值:

然后F8执行这个函数,不出意外就会成功解密:

惊喜来了,这里解密出了一个PE文件。

我们还是把这个PE文件dump出来并保存为dump_.exe.bin

我们将该程序拷贝到真实机器上,然后通过powershell计算该文件的MD5:

Get-FileHash -Algorithm md5 .dump_.exe.bin

得到hahs:4F8091A5513659B2980CB53578D3F798

然后我们尝试通过Systrace对该程序进行行为监控,神奇的事情再度出现,这里的行为,居然跟我们最开始对原始文件监控的时候,行为一模一样。这里说明,我们目前分析的这个样本的确是个Dropper。用于在内存中加载一个恶意样本。

然后发现有VT上有62家报毒。说明此文件就是我们分析样本Drop出的真实恶意样本。

od中还有一点点恶意代码么有分析完,但是我们基本可以知道,后面是用于加载执行该样本了。

这里只剩下最后一个call,我们直接回车进去,然后F4跑过

第三次VirtualAlloc,这里需要注意,这里的参数是004000,在VirtualAlloc中使用这样的参数,可以制作一个PE加载到自己内存中执行,很明显这里是为了加载执行Drop出来的文件做准备了。

然后又是VirtualAlloc分配00A80000

然后再下面执行一个call,将之前解密出来的PE赋值到00A80000中:

然后call 009A0710 这里需要注意,call的时候,push了eax作为参数,而eax==00A80000 所以该函数很有可能比较关键,我们F7跟进进去

然后发现是在循环判断节区信息,以确定是预设的PE文件

每次执行完之后,通过jmp跳转到开头执行

我们直接F4执行到retn处,执行完成本次的检查。然后F8退出函数。

然后紧接着的一个call,又将00A80000放在ecx中作为参数进行调用,没办法,我们只能继续跟进

通过LoadLibrary加载Kernel32.dll

然后又是一个查找,然后在call 009A07E0处对比,看样子应该还是用于检查将要加载的程序是否是预期程序。

所以我们还是直接在retn处F4,然后退出该函数。

继续往下看,又看到一个如下的函数调用,参数是00A80000

且后面有一个call eax,然后就释放内存,结束函数了。

说以这个call eax肯定是关键函数,且eax 的值就来源于我们看到的call 009A09E0

我们直接F4到 call eax,然后F7进去

这里面函数有点多,我们还是根据之前的方法快速分析,跟参数无关的call 直接F8运行过去,然后观察参数和eax的值即可。这里需要注意,我们通过此方法调试的时候请设置好快照,因为这样的调试方法经常会让程序跑飞,设置断点可以很好定位到跑飞点,然后定位到关键代码。

最后发现这里 call 00A83870,程序从这里,正式转入到了第二个PE文件中执行。

而第二个PE文件是标准VC编译的,无壳无混淆,非常好分析,有兴趣的读者可以试着继续跟进分析一下。

 

0x04 总结

在本小节中,主要是分析了一个被修改了CRT的Dropper样本。根据奇安信去年年底的报告来看,此样本应该是作为Lazarus攻击印度核电厂的Dropper样本。但是在报告中却一句话带过了,并没有详细的分析该样本的具体功能。所以本节中也以此样本为例,管中窥豹,学习学习非常规的攻击套路。

至此,恶意样本分析的基础系列算是完成,感谢大家的支持~

接下来的文章,将会分享一些更有特点的样本和分析手法。

(完)