恶意代码分析实战 第十三章 实验部分
实验
Q
Lab13-1
1、比较恶意代码中的字符串(字符串命令的输出)与动态分析提供的有用信息,基于这些比较,哪些元素可能被加密?
2、使用IDAPro
搜索恶意代码中字符串xor
,以此来查找潜在的加密,你发现了哪些加密类型?
3、恶意代码使用什么密钥加密,加密了什么内容?
4、使用静态工具FindCrypt2
、Krypto ANALyzer(KANAL)
以及 IDA熵插件识别一些其他类型的加密机制,你发现了什么?
5、什么类型的加密被恶意代码用来发送部分网络流量?
6、Base64
编码函数在反汇编的何处?
7、恶意代码发送的 Base64
加密数据的最大长度是什么?加密了什么内容?
8、恶意代码中,你是否在 Base64
加密数据中看到了填充字符(=或者==)?
9、这个恶意代码做了什么?
Lab13-2
1、使用动态分析,确定恶意代码创建了什么?
2、使用静态分析技术,例如 xor
指令搜索、FindCrypt2
、KANAL
以及IDA熵插件,查找潜在的加密,你发现了什么?
3、基于问题1的回答,哪些导入函数将是寻找加密函数比较好的一个证据?
4、加密函数在反汇编的何处?
5、从加密函数追溯原始的加密内容,原始加密内容是什么?
6、你是否能够找到加密算法?如果没有,你如何解密这些内容?
7、使用解密工具,你是否能够恢复加密文件中的一个文件到原始文件?
Lab13-3
1、比较恶意代码的输出字符串和动态分析提供的信息,通过这些比较,你发现哪些元素可能被加密?
2、使用静态分析搜索字符串xor
来查找潜在的加密。通过这种方法,你发现什么类型的加密?
3、使用静态工具,如FindCrypt2
、KANAL
以及 IDA嫡插件识别一些其他类型的加密机制。发现的结果与搜索字符XOR结果比较如何?
4、恶意代码使用哪两种加密技术?
5、对于每一种加密技术,它们的密钥是什么?
6、对于加密算法,它的密钥足够可靠吗?另外你必须知道什么?
7.恶意代码做了什么?
8、构造代码来解密动态分析过程中生成的一些内容,解密后的内容是什么?
A
Lab13-01
先拖进IDA Pro中观察,导入表中有网络连接有关的函数,怀疑有网络连接行为。还有资源相关的函数,怀疑资源节中有东西。
01_import1.png进行网络连接函数为sub_4011c9
。
a1
来自sub_401300
,就是资源节中的数据用sub_401190
进行了解码。
解码逻辑就是和0x3B
进行异或操作。
资源节中数据如下:
01rs.png解码后如下:
01rs2.png所以恶意代码访问的域名是www.practicalmalwareanalysis.com
。
继续看a8
来源,拿到了主机名的前12个字节,然后将第十三字节置为0,截断。使用sub_4010B1
进行处理。
继续跟进sub_4010B1
,看着比较麻烦,调用了sub_401000
。
继续跟进sub_401000
,调用了byte_4050E8
。
该处是一个标准的Base64
索引,所以怀疑sub_4010B1
应该是一个Base64
编码函数,实际看代码确实是这样。
使用PEID
的插件寻找加密,可以看到这里找到了一个Base64
表,也给出了详细位置。
下面动态分析一下,开启wireshark
抓包,我的wireshark
在宿主机,恶意代码运行在win10
虚拟机中,注意选择正确接口。抓到流量如下:
对REVTS1RPUC0zMVVN
进行Base64
解码,解码结果为,正是我的主机名。这也与静态分析结果相符。
如果前面的今天分析没有将逻辑分析的很清楚,其实可以动态调试,看看sprintf
格式化的url
,直接提取IOC
然后看看浏览器返回的数据,因为后面要判断浏览器返回的数据的第一个字母是o
,这里我们提取数据如下
就是拿到了一些注释,程序的基本流程就分析结束
01content.pngLab13-02
直接运行文件,同时打开Process Monitor
,可以看到在和Lab13-03.exe
同级的文件夹下创建了很多新文件,而且数量还在不断增加,大小都是7042950
字节。
将文件拖进IDA Pro中进行分析。
可以看到创建的文件名是GetTickCount
的返回值,返回值是系统启动以来经过的毫秒数。
使用PEID的插件寻找加密,没有任何发现。
03kanal.pngIDA Pro的插件FindCrypt
也没有找到相关信息。在IDA Pro中搜索XOR的结果如下,sub_401739
函数大量使用xor
,查看引用
查看引用,可以判断sub_401851
可能是加密相关的函数。
sub_401070
函数如下,主要作用是对桌面截屏。
void *__cdecl sub_401070(void **a1, _DWORD *a2)
{
void *result; // eax
HGLOBAL hMem; // [esp+0h] [ebp-78h]
UINT dwBytes; // [esp+8h] [ebp-70h]
char pv[4]; // [esp+Ch] [ebp-6Ch] BYREF
LONG v6; // [esp+10h] [ebp-68h]
UINT cLines; // [esp+14h] [ebp-64h]
HGLOBAL v8; // [esp+24h] [ebp-54h]
void *v9; // [esp+28h] [ebp-50h]
HDC hdc; // [esp+2Ch] [ebp-4Ch]
struct tagBITMAPINFO bmi; // [esp+30h] [ebp-48h] BYREF
int v12; // [esp+5Ch] [ebp-1Ch]
HGDIOBJ h; // [esp+60h] [ebp-18h]
__int16 Src[7]; // [esp+64h] [ebp-14h] BYREF
int cy; // [esp+74h] [ebp-4h]
v12 = GetSystemMetrics(0);
cy = GetSystemMetrics(1);
hWnd = GetDesktopWindow();
hDC = GetDC(hWnd);
hdc = CreateCompatibleDC(hDC);
h = CreateCompatibleBitmap(hDC, v12, cy);
SelectObject(hdc, h);
BitBlt(hdc, 0, 0, v12, cy, hDC, 0, 0, 0xCC0020u);
GetObjectA(h, 24, pv);
bmi.bmiHeader.biSize = 40;
bmi.bmiHeader.biWidth = v6;
bmi.bmiHeader.biHeight = cLines;
bmi.bmiHeader.biPlanes = 1;
bmi.bmiHeader.biBitCount = 32;
bmi.bmiHeader.biCompression = 0;
bmi.bmiHeader.biSizeImage = 0;
bmi.bmiHeader.biXPelsPerMeter = 0;
bmi.bmiHeader.biYPelsPerMeter = 0;
bmi.bmiHeader.biClrUsed = 0;
bmi.bmiHeader.biClrImportant = 0;
dwBytes = cLines * 4 * ((32 * v6 + 31) / 32);
hMem = GlobalAlloc(0x42u, dwBytes);
bmi.bmiColors[0] = (RGBQUAD)GlobalLock(hMem);
GetDIBits(hDC, (HBITMAP)h, 0, cLines, *(LPVOID *)bmi.bmiColors, &bmi, 0);
*(_DWORD *)&Src[5] = 54;
*(_DWORD *)&Src[1] = dwBytes + 54;
Src[0] = 19778;
v8 = GlobalAlloc(0x42u, dwBytes + 54);
v9 = GlobalLock(v8);
memcpy(v9, Src, 0xEu);
memcpy((char *)v9 + 14, &bmi, 0x28u);
memcpy((char *)v9 + 54, *(const void **)bmi.bmiColors, dwBytes);
GlobalUnlock(hMem);
GlobalFree(hMem);
ReleaseDC(hWnd, hDC);
DeleteDC(hdc);
DeleteObject(h);
result = v9;
*a1 = v9;
*a2 = dwBytes + 54;
return result;
}
关于此部分代码的分析可以参考MSDN
https://docs.microsoft.com/en-us/windows/win32/gdi/capturing-an-image
开始动态调试,在0x401880
位置打上断点,这时栈上是缓冲区地址(0x030F5020
)和大小(0x006B7786
)。
查阅资料可知此处格式为BMP,详细信息参考wiki:https://zh.wikipedia.org/wiki/BMP
偏移量 | 大小 | 用途 |
---|---|---|
0000h |
2字节 | 用于标识BMP和DIB文件的魔数,一般为0x42 0x4D ,即ASCII的BM。以下为可能的取值:BM – Windows 3.1x, 95, NT, ... etc.BA – OS/2 struct Bitmap ArrayCI – OS/2 struct Color IconCP – OS/2 const Color PointerIC – OS/2 struct IconPT – OS/2 Pointer |
0002h |
4字节 | BMP文件的大小(单位为字节) |
0006h |
2字节 | 保留;实际值因创建程序而异 |
0008h |
2字节 | 保留;实际值因创建程序而异 |
000Ah |
4字节 | 位图数据(像素数组)的地址偏移,也就是起始地址。 |
再看缓冲区的内容,第一二个字节为0x42
和0x4D
,后面四个字节为0x6B7786
就是十进制的7,042,950
,和最初发现的temp文件大小是一致的。
接下来到解密环节,可以直接将未加密的文件给dump出来,脚本如下:
from idc import *
RunTo(0x401880)
GetDebuggerEvent(WFNE_SUSP, -1)
stack = GetRegValue("esp")
buffer_addr = Dword(stack)
print("buffer_addr:{}".format(hex(buffer_addr)))
buffer_length = Dword(stack + 4)
print("buffer_length:{}".format(hex(buffer_length)))
tmp_list = []
for byte in idc.GetManyBytes(buffer_addr, buffer_length):
tmp_list.append(byte)
with open("dump.bmp", "wb") as f:
f.write(bytes(tmp_list))
print("success")
02dump.png
dump出的文件如下,可以看到是一个全屏截图。
bmp文件渲染不了。。。
假设此加密函数是可逆的(可以对加密后的数据再次运行加密函数得到加密前的数据),可以使用手动或者自动化的方式解密。
使用x32dbg进行解密
使用winhex
打开加密后的文件,以16进制形式复制。再使用x32dbg
打开Lab13-02.exe
,在0x401880
和0x40190A
两处下断点,复制加密后的数据到buffer地址,再F9
运行,即可得到解密后的文件,将后缀名改为.bmp
即可打开
使用脚本对上面操作自动化
//使用PathchByte()对内存数据进行修改(不适合修改大量数据,时间非常久)
eg:对XOR的数据进行解密
addr = 0xaaaa
for i in range(xxxx):
PatchByte(addr +i, Byte(addr + i)^ 0x123)
eg:读取文件数据进行Patch
with open(file_path, "rb") as f:
buffer = f.read()
for index, byte in enumerate(buffer):
PatchByte(buffer_addr +index, byte)
回答:
1、在当前目录创建了大小为7,042,950
byte的文件,文件名为temp加8个十六进制数
2、XOR大量出现在sub_401739
,其他检测加密的插件没有发现什么有效信息。
3、WriteFile
调用之前可能会出现加密逻辑。
4、加密函数是sub_40181F
5、原内容是屏幕截图
6、可以在文件未加密之前dump出来,也可以对加密的文件再次执行加密函数进行解密
7、还原文件不再赘述
Lab13-03
使用IDA Pro打开文件,查看strings窗口,可以看到可能使用了自定义的Base64
加密。
可以看到函数sub_4015B7
可能使用了AES
加密数据。
使用PEID的插件KANAL也发现了AES加密的特征。
03kanal1111.pngsub_401AC2
和sub_4015B7
两个函数都和加密相关,中间部分有网络连接行为。
修改Lab13-03.exe
运行机器的hosts
文件,将对应IP
改为我们自己的机器,使用NC
查看连接
捕获的数据包如下,根据行为可以判断应该是一个反弹shell程序,但是对应输出为乱码,应该是进行了加密。
03connect_pcap.png开始分析代码,查找xor
指令,去掉清空寄存器和库函数相关的指令,重命名相关函数。(快捷键N
)
重命名函数名 | 函数地址 |
---|---|
s_xor1 | 401AC2 |
s_xor2 | 40223A |
s_xor3 | 4027ED |
s_xor4 | 402DA8 |
s_xor5 | 403166 |
s_xor6 | 403990 |
使用IDA Pro
的插件寻找加密,可以看到运用了AES
加密,对应有三个常量,分别查看引用。
0x40C908
地址上的数据在s_xor1
, s_xor2
, s_xor4
中被使用
0x40CA08
地址上的数据在s_xor3
, s_xor5
中被使用
0x40CB08
地址上的数据在s_xor2
, s_xor4
中被使用
查看0x40CB08
位置上的数据,可以看到这是硬编码的S Box,可以重命名为s_box
。与此数据关联的函数与AES
加密相关。
查看0x40CA08
位置上的数据,可以看到这是硬编码的逆 S Box,可以重命名为reverse-s_box
。与此数据关联的函数与AES
解密相关。
查看s_xor6
的交叉引用,函数sub_40352D
调用了s_xor6
。
查看sub_40352D
调用了那些函数。可以判断s_xor6
也是和AES
加密相关,将sub_40352D
重命名为s_AES_encrypt
查看s_xor5
的交叉引用,该函数在0x4037EE
和0x40392D
位置被调用了,但是这两个位置并没有被识别为代码,说明这个解密函数可能没有被调用执行。
查看函数s_xor1
首先有几个判断条件,条件不正确时给出错误提示,而且包含以下部分,所以判断这应该是AES加密函数的初始部分。
密钥扩展
字循环
字节代换,涉及到S盒
轮常量异或 (byte_40FB08
)
_main
函数中调用了s_xor1
函数,在调用此函数之前,使用了mov ecx, offset unk_412EF8
,查看unk_412EF8
的交叉引用,发现几个调用的地方都是这样使用,可以判断unk_412EF8
是一个初始化的对象AES
。(这里的调用方式是thiscall
,第一个参数this保存在ecx
中32位)
参考:https://zh.wikipedia.org/wiki/X86%E8%B0%83%E7%94%A8%E7%BA%A6%E5%AE%9A#thiscall
查看AES
加密函数sub_40352D
的交叉引用,发现是在ReadFile
和WriteFile
之间被调用的。该函数是sub_40132B
。
继续向上查找交叉引用,发现这是一个新的线程,重命名为aes_thread
。
同理可以重命名Base64
相关函数,Base64
解密函数和AES
加密函数都出现在函数sub_4015B7
中,这两个新线程与两个管道和一个cmd
的输入输出相关联:
- 使用自定义的
Base64
解密反向shell传回的命令,并发给cmd
执行 - 将
cmd
标准输出和标准错误通过AES
加密以后传给反向shell控制端。
分析到这里基本流程已经清晰,将命令dir
使用自定义Base64
加密,加密以后为BInaEi==
,重新抓取流量。
对上述response流量进行AES
解密,结果符合预期。
使用AES
解密脚本进行解密,模式为CBC
,
# pip install pycryptodome
from Crypto.Cipher import AES
import binascii
raw = 'eb 63 85 35 e8 45 cc e9 5c 92 36 9d 31 d5 3c 01' + \
'8e bf e5 8b 06 e1 48 3f 42 5c 5a 0a 4c 75 b2 fa' + \
'7a c5 3c 27 f2 04 d2 25 d8 e6 c5 d5 78 03 34 98' + \
'49 5e 4f fd f7 dd 63 a6 91 0e 81 06 cc a8 62 ac' + \
'74 f2 4a 26 e7 b2 55 03 d3 71 c8 a9 4c 61 c0 65'
ciphertext = binascii.unhexlify(raw.replace(' ', ''))
obj = AES.new('ijklmnopqrstuvwx', AES.MODE_CBC)
print obj.decrypt(ciphertext)
练习题目
分析恶意文件 f622a03f9c746e551b6467930413fff9
,分析该程序是如何加密恶意负载的。提取相应IOC
此文件为真实病毒,注意防护
参考
【解决IDA Python报错】https://bbs.pediy.com/thread-269644.htm
【对称密码体制和非对称密码体制】https://www.cnblogs.com/Leo_wl/p/10714011.html
【AES加密算法的详细介绍与实现】https://blog.csdn.net/qq_28205153/article/details/55798628
【AES C语言实现】https://www.ghostscript.com/doc/base/aes.c