首页
社区
课程
招聘
[原创]分析实战读书笔记5_实战分析恶意样本
发表于: 2020-12-24 12:32 8988

[原创]分析实战读书笔记5_实战分析恶意样本

2020-12-24 12:32
8988

本章记录一下<恶意代码分析实战>Lab 7-3样本, 这份样本对新人分析恶意程序有很多帮助, 可以说是一个很有代表性的例子. 分析本章样本的过程中可疑积累到很多对日后分析非常有帮助的技巧. 让分析 ≠ 体力活.

本文篇幅可能会略长, 各位见谅.

随书文件我已经在第一篇文章就上传了, 各位可以去我主页下载. 切记不要在实体机中运行

这一章的练习是exe+dll的一个组合, 按照常理来说会是exe加载dll来完成恶意行为. 那么拿到样本先来一波初级静态分析查壳 提串 查导入导出表, 以此来推断样本会具有那些行为, 当然仅限于推断. (查壳就省略了, 都是无壳的, PEID看一下即可)

提串

通过工具对exe提串, 可以发现以下一些有趣的字符串

这些字符串可以让我们进行初步的推测, 样本具有遍历文件 复制文件 修改文件 伪装系统文件, 再仔细思考一下, 将行为进一步具体 : 遍历C盘下所有exe文件 可能将dll文件复制到系统目录下伪装为kernel32 ,而MapViewOfFile官方说明叫做创建PE文件镜像, 这个函数相当于让windows替我们拉伸PE文件,模拟PE加载过程, 将拉伸后的数据返回.

如果对PE的知识掌握牢固的同学, 应该都经历过PE拉伸、还原的痛苦, 所以我们这里有理由猜测样本使用这个API用来更方便的修改某些可执行文件的PE数据, 到这里对于字符串的价值基本都压榨出来了 , 剩余的WARNING...字符串不知道有什么用, 先记住有这么个东西, 然后进行下一步.

查导入导出表

image-20201223161507514

这里的API我们大致都分析完了 , 但是我们最关注的一点却没有, Lab 7-3.dll在哪? 按照我们之前的猜测, 这可能是一个exe导入dll来完成恶意功能的样本, 但是我们在exe的导入表中未看到相关dll, 并且也不包含LoadLibrary + GetProcAddress的动态加载组合, 目前看来这份样本的工作方式可能略显不同. 我们在后续的分析中要留意.

导出表无数据...

exe大致初步分析完毕了, 现在开始初步分析下这个看起来不知道有啥用的dll

提串

对于dll的提串要明显少于exe, 但是却包含更多敏感信息, 初步分析功能包括休眠 创建进程 互斥体操作 网络相关API exec动态执行? 一个IP地址 一段不像是乱码的未知字符串, 进一步将功能细化 : 休眠不知道干了啥 创建进程配合exec常用于动态执行命令 互斥体用于检测多开 WS232加上IP用于网络交互

导入表

image-20201223162714239

导入表没什么额外的东西, 在字符串中已经基本分析完了

导出表

image-20201223162805479

无导出函数, 只有一个入口函数, 这说明后面的分析中只要一路冲着dllmain走下去就行了

楼主在尝试多个虚拟机进行运行exe与dll, 都没有任何行为, 这里就不贴图了, 各位自己练习的时候会发现的.

初级动态分析已经无法为我们提供帮助, 所幸样本无壳子, 我们可以直接步入高级静态分析环节来"扒光"它的衣服

在上文中我们发现dll的字符串和导入表都相对较少, 因此我觉得从dll开始分析, 各位也可以从exe开始分析, 毕竟这份样本中两个文件的耦合度很低

将dll拖入IDA, 定位至main函数0x10001010处准备开始分析

如果想要分析一个样本, 一行一行的读汇编是不现实的, 时间成本体力成本都过高, 因此在面对一个样本, 想要快速分析且不会漏掉一些重点的时候, 找对技巧就很关键, 这里我们使用书中提供的技巧, 也是我在工作中最常用的技巧 忽略掉除call外的所有指令,只有当需要追溯参数或返回值时才关注其他汇编语句, 分析时应当遵循从外层到内层的顺序,而不是遇见一个call就进入

image-20201223164218123

鼠标单击任意call指令, IDA会将所有call指令高亮, 方便观察

这段汇编可以发现程序在对互斥体SADFHUHF进行操作以进行防止多开, 其中jnz 100011E8跳转到了退出部分

如果未多开, 则通过WSAStartup初始化一个网络套接字, 这是在进行网络交互时必要的初始化步骤, 如果初始化失败,则通过jnz 100011E8跳到返回, 结束程序

image-20201223165235068

然后创建一个socket , 向127.26.152.13发起connect交互, 期间任何操作失败都会跳到退出

如果建立链接成功, 则通过send函数向127.26.152.13发送hello消息, 若发送失败则关闭网络交互(shutdown)后退出程序

若发送成功, 则开始通过调用recv接受127.26.152.13传回的消息, 接收的长度为1000H, 接收的位置为buf通过lea指令传入eax, 进而传入堆栈作为out类型参数. 若recv函数调用失败, 则向上跳转(jz 100010E9), 而唯一跳出这段循环的方式则是成功接收或发送失败, 这是典型的while语句结构

到目前为止, 一切都是正常的C/S交互, 下面的代码就会开始样本的恶意行为

image-20201223165852690

image-20201223171312971

可以看到, 当recv成功接收后, 将会执行到1000113C处的代码, 将buf中的内容(也就是接收到的内容)sleep字符串进行比较, 比较的长度为5 -> MaxCount ,IDA已经为我们做出了清晰的注释, 如果判断成功, 则执行Sleep函数, 休眠60秒后再次向上跳到100010E9处, 再次send recv等待下次接收内容

当字符串比较为满足前5个字符串为sleep时, 将会通过jnz 10001161跳到下一个判断, 同样的结构 ,这次是判断buf(接受到的内容)前4个字符是否为exec, 如果判断成功则通过CreateProcessA创建一个进程

如果判断失败则继续判断buf 第一个字节是否为q, 如果判断成功则通过jz 100011D0执行清理然后退出

如果所有判断都不满足,则通过jmp 100010E9继续while循环

目前看来, 这一段是明显的通过远程下发的指令来执行不同功能 :sleep...执行休眠 exec...执行创建进程 q...执行清理退出, 这里我们需要重点关注下100011AF处的 CreateProcessA

image-20201223171559082

通过查阅MSDN官方文档, 我们可以知道当lpApplicationNameNULL时, 被创建的程序将由lpCommandLine中的路径决定, 根据追数据的方法我们开始追edx来源 , 发现来自于局部变量 CommandLine , 但是继续追这个局部变量的来源 , 我们发现并未有任何处对于CommandLine的赋值, 这违背我们的常识, 仿佛程序在使用一个从未初始化的变量. 接下来我们将通过细节来判断CommandLine的来源

image-20201223172039115

回去观察函数的布局变量分布, 我们可以发现一个特点 : 程序在栈内用掉了很大一部分空间来存储从CommandLine往上的局部变量, 而我们已知buf用于存储recv接收到的字符串, 而在局部变量中存储字符串主要有两种方式,我将其通过画图的方式展示出来:

image-20201223173925261

当选择堆存储字符串时, 栈中只需要保存一个堆中的地址, 而在栈中存储字符串,则是将整个字符串按字节存在一段栈中, 栈中存储的是字符而非地址, 两者的显著差距则是对栈的占用空间差距

回到样本分析中, 可以看到几个局部变量占据了1000H大小的栈

image-20201223174213068

而在所有对buf的操作中, 所有指令都是通过lea指令传递栈的地址当作字符串指针而并非通过mov传递栈中存储的堆地址作为字符串指针

因此我们可以得出, buf的存储方式为栈存储

image-20201223174844282

recv处接受的数据长度为0x1000H长度

而buf在栈中位置为ebp-0x1000, commandLine在栈中存储位置为ebp-0x0FFB,字符串存储方式为低地址->高地址, 对应在栈结构则是靠近ESP的部分为字符串头部

因此我们可以推断出CommandLine来源于远程服务器下发的数据,在exec后面

到这里,整个main函数已经分析完毕了

通过观察, 没有第二级的call代码, 那么整个dll的全部逻辑则为:

将exe文件拖入IDA,定位至main函数0x00401440

image-20201224105801695

上来就看到一大段内存操作, 想要分析这段代码干了什么就需要关注每行汇编的细节, 我们不应浪费时间在读汇编上, 但我们在这段代码中发现了一个熟悉的名字 argv ,对于控制台程序来说, argv[0]是自身程序路径 argv[1]是第一个参数 ,而这一整段代码其实是一段标准的取命令行参数 + 字符串比较

这一段代码就是在比较命令行参数是否和WARNING_THIS_WILL_DESTROY_YOUR_MACHINE相同, 如果不相同则通过jnz 401488设置eax不为0, 然后通过jnz 401813退出程序

这也解释了为什么我们在初步动态分析时程序会直接结束, 目前看起来需要传入一个 WARNING_THIS_WILL_DESTROY_YOUR_MACHINE 参数, 以后类似这种控制台程序验证命令行参数的样本会有很多, 因此这里要多留心.

绕过参数验证的代码,我们继续向下分析

image-20201224111106187

image-20201224111213445

接下来这段代码, 我们按照之前的方法, 只需要关注call指令

我们可以发现程序在做kernel32.dll 与 lab7-3.dll的文件内存映射, 根据我们之前的猜测, 认为在完成内存映射后, 程序可能会对PE进行操作

继续向下分析

image-20201224111608952

发现了一大段对call的调用, 我们先记下这两个函数, 一会回头分析401040 401070

继续向下分析, 可以发现直到4017D4之前 全部是内存操作, 这让我们很难直接看出程序的行为, 除非选择细致化的分析汇编, 但我们并不这样做.

先忽略这一段内存操作的汇编, 直接定位至4017D4, 我们只需记住程序在创建完两个dll的内存映射后进行了一些内存上的操作

image-20201224111947380

继续向下看 可以看到一个清理句柄的操作, 然后将样本中的dll复制为kerne132.dll完成伪装隐藏

然后在调用了4011E0后程序退出

到这里整个main函数已经大致分析完成了,行为包括 对kernel32.dll和lab7-3.dll进行内存映射, 然后一些未知的内存操作后将lab7-3.dll改名为kerne132.dll复制到kernel32的目录下,然后执行4011E0后退出

剩余的三个函数401040 401070 4011E0我们接下来逐个分析

image-20201224112852691

可以看到401040内部调用了401000 ,没有其他明显的行为, 我们进入401000看一下

image-20201224113131284

可以看到401000内部没有任何call的调用, 但是多了一些数学运算 内存操作 循环结构

根据前文我们的猜测, 这里可能会操作PE结构, 我们留心这些立即数18 14 28

结合PE结构 我们可以发现0x18可能代表着可选PE头地址 0x14可能代表着标准PE头中的SizeOfOptionalHeader 0x28可能代表一个目录项的大小 循环结构+0x28可能在遍历目录项

当然到这里我们也只是猜测, 如果想进一步验证需要分析汇编语句了. 到这我们401040函数可以停止分析了, 有个猜测即可

image-20201224114106931

401070内部同样调用了401000, 我们就不用分析了, 接下来关注4011E0

image-20201224114220273

在分析4011E0之前, 我们需要注意函数参数 C:\\*, 这个参数代表C盘根路径下的所有内容

在进行初级静态分析时我们发现了样本具有FindFirstFile FindNextFile这种遍历文件的API, 但目前为止我们的分析都未发现任何地方调用了这两个API, 结合4011E0的参数, 我们在分析前就应意识到这个函数可能在遍历C盘下的所有文件, 在任何程序中遍历C盘所有文件都是一件极度敏感的行为!

进入4011E0

image-20201224114554660

进入函数内部首先就调用了FindFirstFileA, 这是遍历文件的初始化操作, 下面紧跟着一大段未知操作, 几乎都是内存操作, 我们依然无法快速判断

但我们发现了在0040134F处调用了call 4011E0, 这种调用自身的递归写法在遍历文件操作中也很常见, 用来区分文件夹与文件的不同处理方式.

004013A1处的.exe字符串告诉我们程序可能在遍历所有exe文件

image-20201224114925139

在函数尾部我们发现了循环结构以及标准的文件遍历写法,其中夹杂了一个call 4010A0,整个4011E0可以先停止分析了, 我们已经知道这个函数就是在遍历C盘所有的exe文件, 接下来需要关注一下4010A0函数的操作

函数的参数IDA已经很贴心的帮我们标注出来了 lpFileName, 说明传入的是遍历到的exe文件

进入4010A0观察代码

image-20201224115309331

可以看到程序依然创建了文件的内存映射然后调用了401040, 一个我们已经分析过并猜测为PE结构修改的函数

image-20201224115757844

继续向下可以看到一个对于kernel32.dll的字符串比较,如果没有包含这个字符串则会跳过, 继续下个循环

repne scasb + rep movsd常见于字符串拷贝, 对于同时出现了字符串拷贝、相似字符串这种操作, 我们就需要稍微细致的分析一下

通过简单的分析我们可以发现程序在将kernel32.dll替换为kerne132.dll

循环结束后程序退出

我们现在来整理一下这个函数的信息, 函数进来就通过创建文件内存映像来执行一个处理PE结构的函数, 然后程序开始一个循环用于替换字符串

到这里基本上整个exe大致行为已经差不多分析结束了, 程序首先判断命令行参数是否正确, 然后对kernel32.dll和lab7-3.dll进行一些PE操作,再将lab7-3.dll复制到系统目录下并改名为kerne132.dll. 紧接着遍历C盘所有exe文件进行一些PE操作, 将字符串kernel32.dll替换为kerne132.dll

在知道了exe的运行方法后,我们尝试运行exe, 在行为监控中会看到大量的对C盘exe文件的修改操作,与我们静态分析的结果相吻合

查看伪装的kerne132.dll文件, 你会发现文件的导出表从原来的没有导出函数变成了与kernel32.dll相同的导出函数, 作用为转发至正常的kernel32.dll的导出函数

到这里整套样本的行为已经清晰了:

如果样本已经成功运行, 那么C盘中所有曾导入了kernel32.dll的exe文件都会被修改, 这类似感染型的行为危害极大
而清除方法也比较困难

这套样本很适合练习病毒分析, 其中的PE相关技术也能帮助分析者巩固PE知识, 对于PE知识不牢固的同学来讲, 只能分析出dll的功能, 而exe的功能大部分在操作PE来保证dll文件的持久化运行

 
 
UnmapViewOfFile
IsBadReadPtr
MapViewOfFile
CreateFileMappingA
CreateFileA
FindClose
FindNextFileA
FindFirstFileA
CopyFileA
.exe
C:\*
C:\windows\system32\kerne132.dll        //kerne132这里是数字1   不是字母l
Kernel32.
Lab07-03.dll
C:\Windows\System32\Kernel32.dll
WARNING_THIS_WILL_DESTROY_YOUR_MACHINE
UnmapViewOfFile
IsBadReadPtr
MapViewOfFile
CreateFileMappingA
CreateFileA
FindClose

[注意]传递专业知识、拓宽行业人脉——看雪讲师团队等你加入!

最后于 2020-12-24 12:37 被SSH山水画编辑 ,原因:
收藏
免费 3
支持
分享
最新回复 (1)
雪    币: 49
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
2
分析得很详细。
2021-9-30 10:56
0
游客
登录 | 注册 方可回帖
返回
//