CTS感染型病毒还原算法解密文件
前言
这是最近遇到的一个感染型病毒,感染可移动设备和前用户目录下的exe文件,被感染的文件,病毒母体会首先写入文件中,然后将原始文件加密后和感染标记、加***一同存放在附加数据上。对此,我决定分析算法还原文件。
样本信息
名称:CTS.exe
SHA1:cb54a305a566c00742fb972c4ee62266e880ea78
样本简要分析
样本的主函数很短
向windows目录和temp目录下释放CTS.exe文件,设置自启动
开启线程感染可移动设备文件
在本地只感染当前用户目录下的exe文件
判断是否是exe文件
获取0x10位**进行加密读取到缓冲区的文件,病毒母体会首先写入文件中,然后将原始文件加密后和感染标记、加***一同存放在附加数据上
使用010editor对比两个被感染的文件,发现两个文件的前0x6800都是相同的,并且结尾处都有相同的标志
经过分析,文件结尾处最后0x18个字节分别是标志,文件大小和**
加密算法分析
加密算法并不多,只有不到100行的汇编代码
.text:00405060 encrypt_file_buffer proc near ; CODE XREF: relase_file+55↓p
.text:00405060 ; infect_file+74↓p
.text:00405060
.text:00405060 var_109 = byte ptr -109h
.text:00405060 byte_100 = byte ptr -108h
.text:00405060 pe_start_addr = dword ptr -8
.text:00405060 pe_file_size = dword ptr -4
.text:00405060 crypt_key = dword ptr 8
.text:00405060
.text:00405060 push ebp
.text:00405061 mov ebp, esp
.text:00405063 sub esp, 108h
.text:00405069 mov [ebp+pe_file_size], edx
.text:0040506C mov [ebp+pe_start_addr], ecx
.text:0040506F xor eax, eax
.text:00405071
.text:00405071 loc_405071: ; CODE XREF: encrypt_file_buffer+1E↓j
.text:00405071 mov [ebp+eax+byte_100], al
.text:00405078 inc eax
.text:00405079 cmp eax, 100h
.text:0040507E jb short loc_405071
.text:00405080 push ebx
.text:00405081 mov ebx, [ebp+crypt_key]
.text:00405084 push esi
.text:00405085 push edi
.text:00405086 xor edi, edi
.text:00405088 xor esi, esi
.text:0040508A lea ebx, [ebx+0]
.text:00405090
.text:00405090 loc_405090: ; CODE XREF: encrypt_file_buffer+69↓j
.text:00405090 mov dl, [ebp+esi+byte_100]
.text:00405097 mov eax, esi
.text:00405099 and eax, 0Fh
.text:0040509C movzx ecx, dl
.text:0040509F movzx eax, byte ptr [eax+ebx]
.text:004050A3 add edi, eax
.text:004050A5 add edi, ecx
.text:004050A7 and edi, 0FFh
.text:004050AD inc esi
.text:004050AE mov al, [ebp+edi+byte_100]
.text:004050B5 mov [ebp+esi+var_109], al
.text:004050BC mov [ebp+edi+byte_100], dl
.text:004050C3 cmp esi, 100h
.text:004050C9 jb short loc_405090
.text:004050CB xor ebx, ebx
.text:004050CD xor esi, esi
.text:004050CF xor edi, edi
.text:004050D1 cmp [ebp+pe_file_size], ebx
.text:004050D4 jbe short loc_40512D
.text:004050D6
.text:004050D6 loc_4050D6: ; CODE XREF: encrypt_file_buffer+CB↓j
.text:004050D6 inc esi
.text:004050D7 and esi, 0FFh
.text:004050DD inc edi
.text:004050DE mov dl, [ebp+esi+byte_100]
.text:004050E5 movzx eax, dl
.text:004050E8 add ebx, eax
.text:004050EA and ebx, 0FFh
.text:004050F0 movzx eax, [ebp+ebx+byte_100]
.text:004050F8 mov [ebp+esi+byte_100], al
.text:004050FF mov [ebp+ebx+byte_100], dl
.text:00405106 movzx ecx, [ebp+esi+byte_100]
.text:0040510E movzx eax, dl
.text:00405111 add ecx, eax
.text:00405113 and ecx, 0FFh
.text:00405119 movzx eax, [ebp+ecx+byte_100]
.text:00405121 mov ecx, [ebp+pe_start_addr]
.text:00405124 xor [edi+ecx-1], al
.text:00405128 cmp edi, [ebp+pe_file_size]
.text:0040512B jb short loc_4050D6
.text:0040512D
.text:0040512D loc_40512D: ; CODE XREF: encrypt_file_buffer+74↑j
.text:0040512D pop edi
.text:0040512E pop esi
.text:0040512F pop ebx
.text:00405130 mov esp, ebp
.text:00405132 pop ebp
.text:00405133 retn 4
.text:00405133 encrypt_file_buffer endp
从代码上看,一共有三个循环
首先看第一个循环,直接看汇编指令就很明显的看出是在创建一个0x100字节大小的数组,并给数组赋值从0x00到0xFF
可转成C代码
char key_table[256] = { 0 };
for (int i = 0; i < 256; i++)
{
key_table[i] = i;
}
第二个循环,更新数组数据
这里主要关注谁给数组赋值,给数组的那个部分赋值即可
.text:004050B5 mov [ebp+esi+var_109], al
.text:004050BC mov [ebp+edi+byte_100], dl
首先看[ebp+esi+var_109]
,esi从1开始,每次循环只自增1,所以它是key_table[i]
al是从key_table[edi]([ebp+edi+byte_100])
中取出的,edi = (edi + key[i]+key_table[i])& 0xFF
dl是key_table[i+1]
可转化成C代码
int esi = 0;
int edi = 0;
int edx = 0;
int eax = 0;
int ecx = 0;
int ebx = 0;
for (int i = 0; i < 256; i++)
{
edx = key_table[esi];
eax = esi;
eax = eax & 0xF;
ecx = edx;
eax = key[eax];
edi = (edi + eax + ecx) & 0xFF;
esi += 1;
eax = key_table[edi];
key_table[esi - 1] = eax;
key_table[edi] = edx;
}
最后一个循环,异或加密数据
同样,只关注重点即可
xor [edi+ecx-1], al
然后得到C代码
ebx = 0;
esi = 0;
edi = 0;
for (int i = 0; i < ecrypt_file_size; i++)
{
esi += 1;
esi = esi & 0xFF;
edi += 1;
edx = key_table[esi];
eax = edx;
ebx = (ebx + eax) & 0xFF;
eax = key_table[ebx];
key_table[esi] = eax;
key_table[ebx] = edx;
ecx = key_table[esi];
eax = edx;
ecx = (ecx + eax) & 0xFF;
eax = key_table[ecx];
buffer[0x6800 + edi - 1] = buffer[0x6800 + edi - 1] ^ eax;
}
所以,完整的加解密代码如下:
void encrypt_file_buffer()
{
int esi = 0;
int edi = 0;
int edx = 0;
int eax = 0;
int ecx = 0;
int ebx = 0;
char key_table[256] = { 0 };
for (int i = 0; i < 256; i++)
{
key_table[i] = i;
}
for (int i = 0; i < 256; i++)
{
edx = key_table[esi];
eax = esi;
eax = eax & 0xF;
ecx = edx;
eax = key[eax];
edi = (edi + eax + ecx) & 0xFF;
esi += 1;
eax = key_table[edi];
key_table[esi - 1] = eax;
key_table[edi] = edx;
}
ebx = 0;
esi = 0;
edi = 0;
for (int i = 0; i < ecrypt_file_size; i++)
{
esi += 1;
esi = esi & 0xFF;
edi += 1;
edx = key_table[esi];
eax = edx;
ebx = (ebx + eax) & 0xFF;
eax = key_table[ebx];
key_table[esi] = eax;
key_table[ebx] = edx;
ecx = key_table[esi];
eax = edx;
ecx = (ecx + eax) & 0xFF;
eax = key_table[ecx];
buffer[0x6800 + edi - 1] = buffer[0x6800 + edi - 1] ^ eax;
}
}
总结
虽然写出的还原看起来有点绕,但其实这个算法并不难。刚开始还原这个算法的时候,我把变量的名字定义的规规矩矩,然后看着看着就把值给对应混了,磕磕绊绊,花了好久才写好。后来才感觉这样做是真的蠢,直接推平之前的还原代码,变量直接对应寄存器,无脑对应汇编,很快就搞定了。算是自己踩的小坑,分享给大家。
上一篇: 测试开发进阶(四十)
推荐阅读