记一次复现勒索病毒分析

网上有个病毒分析,自己想好好的复现,断断续续分析了好几天才搞明白

样本概述

1
2
3
4
5
6
1.exe: 样本主体
shellcode.txt: 主体解密后的 shellcode
dump.exe: shellcode 执行后的新 PE 文件
a.bat: dump.exe 释放出的批处理文件,用于善后工作
a.txt..doc: 加密后的文件样本
Read___ME.html: 勒索信息

样本名: 1.exe
MD5: 612974dcb49adef982d9ad8d9cbdde36
SHA1: b817e361bd0cc1819d7f6a1189f0f5d56ed48721
下载(解压密码 x-encounter)
分析环境及工具:
win7 ida od

行为预览

1.exe (样本主体行为):

1
2
1、简单的反调试技术,调用无效循环及无效的 API 迷惑分析人员
2、从数据段解密出 shellcode,并显式调用 VirtualProtect 使 shellcode 可读可写可执行,接着运行 shellcode

shellcode.txt(shellcode 行为):

1
2
1、获取 Kernel32.dll 的基址、获取函数地址、解密新 PE 文件
2、使用进程替换技术,将老 PE 文件的内存空间替换为解密后的新 PE 文件,修复新 PE 文件的 IAT,接着跳向新 PE 文件的 OEP

dump.exe:(新 PE 文件的行为):

1
2
3
4
5
6
1、解密数据,获取系统信息,释放到系统路径,打开注册表添加启动项
2、创建文件,保存用户 RSA 公钥以及 用户 ID,并生成勒索信息
3、遍历所有进程,如果发现指定进程 kill 掉
4、创建加密线程,遍历文件并加密文件并将文件的后缀名改为 ..doc,创建勒索信息文件
5、释放 bat 文件到临时目录,创建进程并执行
6、自删除

a.bat:(批处理文件的行为):

1
2
3
1、删除卷影副本
2、删除指定的注册表项,添加指定的注册表项
3、删除日志信息

详细分析

1.exe(样本主体分析)

PEID查壳

ida加载找到主函数
首先呼叫SetProcessShutdownParameters,WinHttpOpen,WinHttpConnect,WinHttpQueryOption连接网络的函数
但是传入的参数都是0,可能是大量无关 API,可能是混淆代码,并没有什么用。接着

这里并且在 0040127D 处会进入一个循环,判断 var_8 的值等不等于 8B9A6F96h,里面就是呼叫一个GetWindowsDirectoryW。但是感觉没有什么用,而且循环n次。是无关的循环。

紧接着调用 GlobalMemoryStatus,获取内存信息,在 004013EF 处调用 LocalAlloc 分配了 8700h 的空间

接着偏移数据(加密的 shellcode )复制到刚刚分配的空间,之后进入右边的条件分支,对 shellcode 进行解密

解密函数如下

对加密的 shellcode 分组,通过一个循环,每 8 字节一组,对这 8 字节调用 sub_4015C0 进行解密,该函数的第三个参数为解密 key。

接着用od跟,shellcode还没有解密

解密后,

在解密完成后,dump下来shellcode保存为shellcode(等下会分析这个shellcode),再接着看看。

显示获取VirtualProtect的函数地址,使解密后的shellcode可读可写可执行。程序接着调用shellcode。

分析shellcode

将shellcode的代码dump出来。IDA 载入 shellcode.txt,发现 shellcode 只有两个 Call。

进入第一个 call

该函数通过 fs 寄存器获取 Kernel32.dll 的基址

接着通过解析 PE 文件,获取 PE 头,接着获取数据目录第 0 项也就是导出表的地址,接着在导出表中获取 GetProcAddress 和 LoadLibrary 的地址,然后返回

(用OD)进入 shellcode

shellcode的第二个 call,如图,观察此处的 eax 所指向的地址的值

很明显这是一个经过加密处理的 PE 文件,那么在 MZ 字符之前的值是什么呢?
经过后续的分析,我们可以了解到,这一段内存实际上是以 “新 PE 的参数+加密的新 PE” 这样的结构存储的,结构的详细分析如下图

然后通过之前获取的 GetProcAddress 和 LoadLibrary 的地址,来动态获取其他函数的的地址

函数地址获取完后,调用 GetVersionExA,

判断 dwMajorVersion 等不等于 6

接着分配内存,并调用解密函数

该函数的两个参数是通过 “新 PE 的参数” 这个结构获得的,解密函数大概是通过分组的形式,逐步对 PE 进行解密的,解密的关键一个是索引值,一个是 key 都在 `”新 PE 的参数” 这个结构中,解密后的 PE 文件如下图

紧接着调用 VirtualProtect,目标地址是 400000,也就是老 PE 文件的加载基址,将其属性改为可读可写可执行,为后面的替换做准备。然后将 400000 处的值清零,开始复杂的 PE 操作。

首先通过 DOS 头的 e_lfanew 获取 PE 头的偏移,找到 PE 头之后将 PE 头偏移四个字节的数据(也就是 Machine)存到局部变量中,然后通过 PE 头偏移 14h 处也就是 SizeOfOptionalHeader 获得 OptionalHeader 的长度,之所以计算各个段的长度是为了获取最后一个节区的偏移,通过偏移把解密后的新 PE 的 DOS 头,PE 头和节表头复制到 400000 的内存空间中,之后从 “新 PE 的参数” 中获取新 PE 的 EP,修正 EP,最后解析节表头获取节表的 pointerToRawData,准备进行节表数据的复制。

右边分支为复制节区数据,由于新PE文件只有一个节区所以循环只有一次,这里不做过多的解释,重点在 IAT 的修复上,当右边分支执行完后,会执行左边的分支,首先会释放掉解密后的新 PE 文件,通过 PE 头的偏移获取 DataDirectory 数据目录表,在表中接着获取 DataDirectory[IMAGE_DIRECTORY_ENTRY_LOAD_IMPORT] 的 RVA 也就是导入表的 RVA 和 Size,进行修复,而且还对 DataDirectory[IMAGE_DIRECTORY_ENTRY_RESOURCE] 资源目录项的 RVA 和 SIZE 也进行了修复,由于 DataDirectory[IMAGE_DIRECTORY_ENTRY_LOAD_IMPORT] 的 RVA 指向 IMAGE_IMPORT_DESCRIPTOR 结构体,接着比较该结构体的 Name字段是不是等于零,如果不为零,如下图

调用 LoadLibrary 载入导入的 DLL,之后判断 IMAGE_IMPORT_DESCRIPTOR 结构体中第一个字段,也就是 OriginalFirstThunk 是否为零,如果不为零,接着获取 IMAGE_IMPORT_DESCRIPTOR 结构的第 5 个字段,也就是 FirstThunk,通过 FirstThunk找到 IMAGE_THUNK_DATA32 结构体的 AddressofData 字段,通过 AddressofData 找到获得 _IMAGE_IMPORT_BY_NAME 结构体中的 Name字段,也就是该 DLL 导出函数的名称,之后调用 GetProcAddress 对其进行修复。修复 IAT 的操作一共有两层循环,外层循环遍历所有的导入的 DLL,内层循环对 DLL 导出的函数进行修复,
IDA中如下

在 OD 中如下

修复完 IAT 之后,调用 atexit 注册终止函数,最终跳转到新 PE 的 OEP

折腾了半天终于见到了病毒的真身

dump.exe:(新 PE 文件的分析)

我们通过 LoadPE 将此时病毒的内存,转存下来命名为 dump.exe,载入到 IDA 中,由于 dump.exe 可能用了一些对抗反汇编的技术,导致 IDA 无法判别哪些是数据哪些是代码,我们可以手动将其转换,但即使转换成功后也无法正常反编译,加大了我们的分析难度,
进入新的oep后

只能通过 OD(1.exe) 一点一点的单步进行分析。
我们进入函数入口处,步入第一个 call[],先分配了 5 个堆空间,接着获取病毒文件的完整地址和系统的临时目录

之后进行对相关字符串进行解密的操作

大概算法是解密了一个 256 位的 key (怀疑是黑客的 RSA 公钥),用对 key 进行操作然后与加密的字符串进行异或,两次解密后的字符串如下

接着调用 00409FE7 处的 call sub_409CB8 解密函数,将解密后的数据(一些 windows 上常用的软件名称)以.为分割符分为 43 个字符串的首地址存放在分配的内存地址中。
ida

解密函数call sub_409CB8

以同样的方式解密 401430 处的数据(系统环境之类的参数),之后通过系统环境变量获取 appdata 的目录,进行字符串拼接,

之后调用 CopyFile 将该病毒复制到 appdata 目录,这两次解密的目的是对某些特殊字符的文件夹进行绕过

在 C:\Documents and Settings\Administrator\Application Data 下可以找到该病毒的副本,接着在 0040A088 调用与注册表有关的函数,为了常驻系统。

继续获取系统目录 C:\Documents and Settings\All Users,并通过一个加密函数生成一个固定的 Hash 值,接着进行字符串拼接,最终调用 0040A11A 处的函数创建文件

进入 0040A11A 处的函数,创建文件,写入文件,文件内容是通过复杂的加密算法生成的用户 ID,

这里我们只要了解一下病毒的行为,具体加密流程在加密部分会详细介绍,创建的文件如下图

之后对勒索文档的内容进行解密,勒索文件的组合分为三部分
第一步先将解密后的数据前64E部分复制到分配好的缓冲区中
第二步将之前提到过的用户 ID的部分数据复制到第一部分的后面
第三步将剩下的勒索信息复制到第二部分后面,如图

在 0040A1FA 处调用 call sub_402354,该函数功能如下,

该函数用来关闭一些进程,如 word,excel,防止后面加密函数运行时,由于文件占用导致加密失败。
在最后连续调用了 3 个 call
第一个 call 寻找盘符,加密文件,这里不做过多分析,放到加密部分细讲
第二个 call 创建 bat,创建进程并运行,用于善后工作,从内存中 dump 出批处理文件,内容如下

1
2
3
4
5
6
7
8
@echo off..vssadmin.exe Delete Shadows /All /Quiet
reg delete "HKEY_CURRENT_USER\Software\Microsoft\Terminal Server Client\Default" /va /f
reg delete "HKEY_CURRENT_USER\Software\Microsoft\Terminal Server Client\Servers" /f
reg add "HKEY_CURRENT_USER\Software\Microsoft\Terminal ServerClient\Servers"
cd %userprofile%\documents\
attrib Default.rdp -s -h
del Default.rdp
for /F "tokens=*" %1 in ('wevtutil.exe el')DO wevtutil.exe cl "%1"

vssadmin.exe 删除卷影副本,wevtutil.execl 命令删除日志信息
第三个 call 用于自删除

加密部分

目前主流的勒索软件使用的加密算法为 RSA 和 AES。AES 属于对称密码体制,RSA 属于非对称密码体制,一般来讲 RSA 有两对,黑客有一对,用户生成一对,黑客的 RSA 私钥自己保留,将 RSA 公钥以硬编码(或加密)的方式存储在病毒中,用来加密用户的 RSA 私钥,用户的 RSA 公钥则用来加密 AES 密钥,AES 密钥用来加密文件。视病毒的情况而定,有些病毒每一个加密文件对应一个 AES 密钥,有些病毒所有加密文件只对应一个 AES 密钥。
之前已经提到过,该病毒主要有两次加密,第一次是写入 Hash 文件和勒索文档的用户 ID,第二次就是对文件进行加密

用户ID生成分析

在之前的分析中我刻意回避了一个函数 00409F43 处的 call sub_408F24,该函数的功能如下

a1 也就是第一个参数是 256 位的字符串(应该是未被加密),个人推测应该是黑客的 RSA 公钥,如下图

该函数内出现了 SHA256_K,ROL4 和 ROR4 的字样,推测该函数的目的是对黑客的 RSA 公钥进行 SHA256 加密,加密后的内容
是不是很熟悉……在对加密的字符串进行解密的时候作为解密的 key入栈,在创建 Hash 文件的时候作为文件名。转到生成用户 ID的函数空间,进入 sub_40A534,首先分配了 1024 大小的内存空间,在 sub_40A3DD 函数中的 sub_404336 函数生成了用户 RSA 的密钥,之后将该密钥写入 Hash 文件中。


接来下调用两次 sub_4024E8 生成 128 位的明文(该明文应该是与用户 RSA 私钥有关系的,不然用黑客 RSA 公钥加密就没有意义了,不过我没有分析出来这个拼接的 128 位明文是什么),在 sub_40A3F4 处用黑客的 RSA 公钥对明文进行加密,返回给 V26 变量,最后调用 sub_40267E 将 V26 中的十六进制数据转换为 ASCII 数据,如图

回到原来的函数中,接着调用 SetFilePoint 定位到 Hash 文件的末尾,使用 WriteFile 将上图的值写入 Hash 文件末尾(之后合成勒索文档时,中间插的那一段数据也是上图所示的数据)
至此用户 ID分析完毕

文件加密部分

来到 0040A20A 处的 call sub_409B9C 进入文件加密函数,注意调用该函数时的参数,第一个参数为之前生成的用户 RSA 的公钥

首先会调用 GetLogicalDrivers 函数判断宿主主机有几个盘,将返回值转换为二进制 10000000000000000000000100 从右向左数,一共有两个盘,C 盘和Z 盘,调用 GetDriveTypeA 获取相应磁盘的类型,接着进行判断如果属于硬盘驱动器、远程(网络驱动器)和移动驱动器中的一种,则对每一个符合要求的磁盘调用 CreateThread 创建一个加密线程,回调函数为 sub_40994F,回调参数为指向相应的盘符的指针,在回调函数处下断点,顺便在c盘根目录下创建 a.txt,内容为 123,以便分析加密方式

接着对各种字符串的操作,调用 FindFirstFileW,先寻找 C 盘根目录的文件,找到之后会进行一系列的判断
比较后缀名是不是 ..doc 判断是否被加密
比较文件名是不是 Read__ME.html(生成的勒索文档),比较文件名是不是 Hash 文件
比较文件名是不是比较是不是复制到 C:\Documents and Settings\Administrator\Application Data 目录的病毒副本
如果全部判断失败则进入 00409B01 处的 call sub_409081 文件加密函数,这里要留心该文件加密函数的三个参数
第一个参数为要加密的文件名称,也就是 a.txt
第二个参数是用户 RSA 的公钥
第三个参数是用户 ID的第二部分

函数会调用 UuidCreate 创建一个全世界独一无二的 uuid 值,uuid 的值如下

后调用 CreateFile 打开要加密的文件,获取文件大小,计算 uuid 的长度,调用 sub_407009 分配了两块内存空间,之后将要加密的文件大小与文件名称进行运算

之后将运算后的结果与 uuid 进行加密运算(应该是 SHA256 加密),而且加密了 2000h 次

经过运算之后的值为下图,从 D1BB98 到 D1BBB8,我们可以设这个值为 A

紧接着调用 004092EF 处的 call sub_402780 对 D1BB70 的值进行扩充,该函数有一个参数为 A,通过 A 的值生成 AES 加密的密钥,后面的分析也验证了这一点,扩充的数据中包含一个 256 位的 AES 密钥,从 D1BC10 到 D1BD20,设这个值为 B。

接着调用 ReadFile 将文件内容读取到缓冲区中,并在 00409489 处 call sub_408F7F 对文件内容进行加密,其中该函数的参数包含 B

这里可以推测一下 00409489 处的函数应该为 AES 加密,而该函数的第四个参数也就是 B (位于扩展数据中)作为 AES 的 key 进行加密。加密后的文件数据如下(该勒索病毒对每一个文件创建一个 AES 的密钥)

接着将加密后的文件数据复制到 D1BBB8 处,然后调用三次 WriteFile,分别将数据复制到文件中,使用用户 RSA 公钥将 uuid 进行加密,并写入文件,最后将用户 ID的第二部分写入文件中,加密后的 a.txt

分析完毕

通过上面的分析,我们可以了解到该病毒对每个文件的 uuid 进行运算,通过运算结果生成 AES 密钥,并使用用户 RSA 公钥对 uuid 进行加密放在了文件中,从用户的角度考虑要解密文件数据首先要获得 uuid 的值,由于 uuid 被用户 RSA 公钥进行加密了,所以我们要获得用户 RSA 的私钥,而用户 RSA 的私钥又被黑客 RSA 的公钥进行加密后写入用户 ID 中,所以我们要获得黑客 RSA 私钥,但是黑客的 RSA 私钥只有黑客一个人拥有,所以用户只能向黑客支付赎金并提供被加密的文件和在机器上生成的 Hash 文件才能解密

小结

1、加密技术掌握的不熟练,外加上该病毒是自己实现 RSA 和 AES 的加密的,没有调用微软提供的 CSP 容器,所以对分析造成很大的困难。
2、分析病毒要有耐心,切记分析加密部分的时候要一口气分析完,由于表哥只给了一个样本所以我们并不知道该病毒是如何传播的。
3、写文不易,且看且珍惜。
参考:
https://mp.weixin.qq.com/s/TznmkeU1zZU_wJSw0Jx6GQ

Donate
-------------本文结束感谢您的阅读-------------