Simple Exe Packing

本文章介绍最简单的32位exe程序加壳流程。本来是在寒假就已经写好了很大一部分代码,但是由于vs属性配置不起作用的原因,导致写的shellcode没法使用,也是最近才解决掉这个问题。

通过编写一个壳,可以对pe结构和程序执行过程有更深入的了解,而且这个壳的思路都是最简单的一种,只要shellcode厉害,壳就可以更能隐藏程序的信息。

代码已经上传到github上,https://github.com/The-Itach1/Simple-Exe-Packing

前置知识

PE结构

对pe结构体还是必须要有个大概的了解,不说能背,至少在peview工具中看到名称知道一些关机的结构体和其成员代表什么需要知道,还有exe在内存和在磁盘的区别已经如何转换。特别是对于IMAGE_EXPORT_DIRECTORY导出表和IMAGE_BASE_RELOCATION重定位表,实际上我在Windows-Hack-Programming的编程练习中,已经对这个很熟悉了。IMAGE_EXPORT_DIRECTORY导出表可能还没提到过,一般就是dll程序的导出函数的信息存储地方,由于我们要编写shellcode,所以需要获得kernel32.dll的一些导出函数,需要用到这个表。

如何编写shellcode

首先shellcode是可以在其他设备(windows)上也可以直接使用的,因为不会涉及到全局变量,并且调用的函数也必须是从kernel32.dll中直接寻找到,这是因为重定位的关系,程序加载的api函数的地址都是不固定的,一般是先找到GetProcAddress和GetModuleHandle和LoadLibrary,这样就可以基本上获取到其他所有函数。

通过peb获取kernel32基址:
https://bbs.pediy.com/thread-266678.htm?msclkid=51ec2fddaf6711ec9ca04b75cc507387

解决vs的栈检测和优化

在关闭了vs的安全检测和优化后,还是发现了栈检测的问题,并且在release编译环境下,仍可能会将简单的异或运算进行优化,优化为128bit的异或运算,并且将异或值设置为全局变量。最后的解决方法就是使用Debug模式编译,然后在ida中手动删除掉其中的栈检测汇编代码。

加壳思路

算是最基础的一种加壳思路了。

  • 添加一个节区,用来装载解密.text节区的shellcode。
  • 加密.text节区,实际上应该还可以加密.idata节区。
  • 编写shellcode,用来解密.text节区。
  • 将shellcode写入添加的节区。

当然,shellcode的编写需要另外写一个项目。

代码展示

这里也不详细介绍过程了,完全可以通过代码和代码中的注释来弄清流程。

shellcode

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
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215

#include<stdio.h>
#include<windows.h>
#include <winternl.h>

typedef FARPROC(WINAPI* GETPROCADDRESS)(
HMODULE hModule,
LPCSTR lpProcName
);

typedef FARPROC(WINAPI* GETMODULEHANDLEA)(
LPCSTR lpModuleName
);

typedef FARPROC(WINAPI* VIRTUALPROTECT)(
LPVOID lpAddress,
SIZE_T dwSize,
DWORD flNewProtect,
PDWORD lpflOldProtect
);

//将shellcode装到另一个节区中,不影响text节区的同时还方便找到对应代码。
#pragma code_seg(".SMC");

void Myshellcode()
{
//获取PEB结构体的地址
PPEB PebBaseAddress;
_asm
{
mov eax, dword ptr fs : [0x18]
mov eax, dword ptr ds : [eax + 0x30]
mov PebBaseAddress, eax;
}

//通过PEB来找到kernel32的基地址
PPEB_LDR_DATA pPebLdr = PebBaseAddress->Ldr;
PLDR_DATA_TABLE_ENTRY pLdrDataHeader = (PLDR_DATA_TABLE_ENTRY)pPebLdr->InMemoryOrderModuleList.Flink->Flink->Flink;
//wprintf(L"%s\n", pLdrDataHeader->FullDllName.Buffer);

//偏移0x10的地址即为指向kernel.dll基址的指针
//printf("%x\n", *(pLdrDataHeader->Reserved2));

//测试是否一样
//HMODULE libhandle = LoadLibraryA("kernel32.dll");
//printf("%x ", libhandle);

PBYTE hkernel = *(PBYTE*)(pLdrDataHeader->Reserved2);
PIMAGE_DOS_HEADER pIDH = (PIMAGE_DOS_HEADER)hkernel;
PIMAGE_OPTIONAL_HEADER pIOH = (PIMAGE_OPTIONAL_HEADER)(hkernel + pIDH->e_lfanew + 0x18);
PIMAGE_EXPORT_DIRECTORY PIED = (PIMAGE_EXPORT_DIRECTORY)((DWORD)hkernel + pIOH->DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress);

DWORD NumOfFunction = PIED->NumberOfFunctions;
PIMAGE_IMPORT_BY_NAME FuctionName = NULL;
GETPROCADDRESS MyGetProcAddress = NULL;


PIMAGE_THUNK_DATA RVAofNameArrays = (PIMAGE_THUNK_DATA)((DWORD)hkernel + PIED->AddressOfNames);
PIMAGE_THUNK_DATA RVAofEotArrays = (PIMAGE_THUNK_DATA)((DWORD)hkernel + PIED->AddressOfFunctions);

//通过比较导出表的函数名称获取GetProcAddress的地址
DWORD strName[3] = { 0x50746547,0x41636f72,0x65726464 };
for (int i = 0; i < NumOfFunction; i++)
{
FuctionName = (PIMAGE_IMPORT_BY_NAME)((DWORD)hkernel + RVAofNameArrays[i].u1.AddressOfData);

if (strName[0] == *(DWORD*)((DWORD)FuctionName) && strName[1] == *(DWORD*)((DWORD)FuctionName + 4) && strName[2] == *(DWORD*)((DWORD)FuctionName + 8))
{
//这里有个+2的偏移,是通过查看本电脑的kernerl32.dll得到的,确实导出表的函数地址需要偏移2
MyGetProcAddress = (GETPROCADDRESS)((DWORD)hkernel + RVAofEotArrays[i + 2].u1.AddressOfData);
break;
}
}

//char GetModuleHandleAFunctionName[] = { 0x47,0x65,0x74,0x4d,0x6f,0x64,0x75,0x6c,0x65,0x48,0x61,0x6e,0x64,0x6c,0x65,0x41,0x00 };
DWORD GetModuleHandleAFunctionName[6] = { 0 };
GetModuleHandleAFunctionName[0] = 0x4d746547;
GetModuleHandleAFunctionName[1] = 0x6c75646f;
GetModuleHandleAFunctionName[2] = 0x6e614865;
GetModuleHandleAFunctionName[3] = 0x41656c64;
GetModuleHandleAFunctionName[4] = 0;

//获取GetModuleHandleAFunction函数地址
GETMODULEHANDLEA MyGetModuleHandleA = (GETMODULEHANDLEA)MyGetProcAddress((HMODULE)hkernel, (char*)GetModuleHandleAFunctionName);

PBYTE BaseAddress = (PBYTE)MyGetModuleHandleA(NULL);


PIMAGE_DOS_HEADER pExeIDH = (PIMAGE_DOS_HEADER)BaseAddress;
PIMAGE_OPTIONAL_HEADER pExeIOH = (PIMAGE_OPTIONAL_HEADER)(BaseAddress + pExeIDH->e_lfanew + 0x18);
PIMAGE_FILE_HEADER pExeIFH = (PIMAGE_FILE_HEADER)(BaseAddress + pExeIDH->e_lfanew + 4);
PIMAGE_SECTION_HEADER pExeISH = (PIMAGE_SECTION_HEADER)(BaseAddress + pExeIDH->e_lfanew + sizeof(IMAGE_NT_HEADERS));

DWORD OldSectionNumber = pExeIFH->NumberOfSections - 1;
DWORD dwTextsize;
DWORD* pTextStart = NULL;
DWORD TextMax = pExeISH->Misc.VirtualSize + pExeISH->VirtualAddress;

//寻找.text节区,获取其起始RVA和大小,解密会用到
for (int i = 0; i < OldSectionNumber; pExeISH++)
{
if ((DWORD) * (DWORD*)pExeISH->Name == 0x7865742e)
{
dwTextsize = pExeISH->Misc.VirtualSize;
pTextStart = (DWORD*)(pExeISH->VirtualAddress + (DWORD)pExeIDH);
break;
}
}

//获取VirtualProtectFunction地址,修改.text节区为可写可读可执行
char VirtualProtectFunctionName[15] = { 0x56,0x69,0x72,0x74,0x75,0x61,0x6c,0x50,0x72,0x6f,0x74,0x65,0x63,0x74, 0x00 };
VIRTUALPROTECT MyVirtualProtect = (VIRTUALPROTECT)MyGetProcAddress((HMODULE)hkernel, VirtualProtectFunctionName);

DWORD flOldProtect;
MyVirtualProtect((LPVOID)pTextStart, dwTextsize, PAGE_EXECUTE_READWRITE, &flOldProtect);


PIMAGE_BASE_RELOCATION pExeIBR = (PIMAGE_BASE_RELOCATION)((DWORD)BaseAddress + pExeIOH->DataDirectory[5].VirtualAddress);
DWORD dwImageBase = *(DWORD*)(pExeIOH->ImageBase + pExeIOH->AddressOfEntryPoint + 4);

DWORD Round = NULL;
Round = dwTextsize / 4;

if (pExeIOH->DataDirectory[5].VirtualAddress == 0)
{
//没有重定位表

for (int i = 0; i < Round; i++)
{
*(unsigned int*)(pTextStart + i) = *(unsigned int*)(pTextStart + i) ^ 0x12345678;
}
}
else
{
//将text节区还原为重定位表之前的情况。
while (pExeIBR->VirtualAddress != 0)
{

DWORD RvaofBlock = pExeIBR->VirtualAddress;
//定义成一个WORD的指针,方便取值计算
WORD* RvaArrays = (WORD*)((DWORD)pExeIBR + sizeof(IMAGE_BASE_RELOCATION));
//算出这个重定位表块需要修改多少个RAV
int NumofRva = (pExeIBR->SizeOfBlock - sizeof(IMAGE_BASE_RELOCATION)) / sizeof(WORD);

//修改每个块中的重定位表信息
for (int i = 0; i < NumofRva; i++)
{
if ((RvaArrays[i] & 0xfff) + RvaofBlock > TextMax)
{
continue;
}
//32位程序一般Type就是3,也就是IMAGE_REL_BASED_HIGHLOW
DWORD* pAddr = (DWORD*)((DWORD)BaseAddress + (RvaArrays[i] & 0xfff) + RvaofBlock);
*pAddr = *pAddr + dwImageBase - (DWORD)BaseAddress;
}

pExeIBR = (PIMAGE_BASE_RELOCATION)((DWORD)pExeIBR + pExeIBR->SizeOfBlock);
}

//解密
for (int i = 0; i < Round; i++)
{
*(DWORD*)(pTextStart + i) = *(DWORD*)(pTextStart + i) ^ 0x12345678;
}

//再次进行重定位表。
pExeIBR = (PIMAGE_BASE_RELOCATION)((DWORD)BaseAddress + pExeIOH->DataDirectory[5].VirtualAddress);

while (pExeIBR->VirtualAddress != 0)
{

DWORD RvaofBlock = pExeIBR->VirtualAddress;
//定义成一个WORD的指针,方便取值计算
WORD* RvaArrays = (WORD*)((DWORD)pExeIBR + sizeof(IMAGE_BASE_RELOCATION));
//算出这个重定位表块需要修改多少个RAV
int NumofRva = (pExeIBR->SizeOfBlock - sizeof(IMAGE_BASE_RELOCATION)) / sizeof(WORD);

//修改每个块中的重定位表信息
for (int i = 0; i < NumofRva; i++)
{
if ((RvaArrays[i] & 0xfff) + RvaofBlock > TextMax)
{
continue;
}
//32位程序一般Type就是3,也就是IMAGE_REL_BASED_HIGHLOW
DWORD* pAddr = (DWORD*)((DWORD)BaseAddress + (RvaArrays[i] & 0xfff) + RvaofBlock);
*pAddr = *pAddr - dwImageBase + (DWORD)BaseAddress;
}

pExeIBR = (PIMAGE_BASE_RELOCATION)((DWORD)pExeIBR + pExeIBR->SizeOfBlock);
}
}

}
void func2() {
}

#pragma code_seg()
#pragma comment(linker, "/SECTION:.SMC,ERW")
int main()
{
//用来导出shellcode的,但是由于只有Debug模式下的才好修改,所以还是在ida里面提取shellcode吧。
PBYTE Start, End;
Start = (PBYTE)Myshellcode;
End = (PBYTE)func2;
int lenth = 0;

for (; Start < End; Start++)
{
printf("\\x%02x", *Start);
lenth++;
}
printf("\n%x", lenth);
Myshellcode();
}

然后编译好了后就是去ida里面删除栈检测了,以及提取出shellcode。

ExeShell

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
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240

#include<stdio.h>
#include<windows.h>
#include "BlowFish.h"
#pragma warning(disable:4996)

unsigned char shellcode[] = "\x55\x8B\xEC\x81\xEC\xB8\x03\x00\x00\x53\x56\x57\x8D\xBD\x48\xFC\xFF\xFF\xB9\xEE\x00\x00\x00\xB8\xCC\xCC\xCC\xCC\xF3\xAB\x64\xA1\x18\x00\x00\x00\x3E\x8B\x40\x30\x89\x45\xF8\x8B\x45\xF8\x8B\x48\x0C\x89\x4D\xEC\x8B\x45\xEC\x8B\x48\x14\x8B\x11\x8B\x02\x89\x45\xE0\x8B\x45\xE0\x8B\x48\x10\x89\x4D\xD4\x8B\x45\xD4\x89\x45\xC8\x8B\x45\xC8\x8B\x48\x3C\x8B\x55\xD4\x8D\x44\x0A\x18\x89\x45\xBC\xB8\x08\x00\x00\x00\x6B\xC8\x00\x8B\x55\xBC\x8B\x45\xD4\x03\x44\x0A\x60\x89\x45\xB0\x8B\x45\xB0\x8B\x48\x14\x89\x4D\xA4\xC7\x45\x98\x00\x00\x00\x00\xC7\x45\x8C\x00\x00\x00\x00\x8B\x45\xB0\x8B\x4D\xD4\x03\x48\x20\x89\x4D\x80\x8B\x45\xB0\x8B\x4D\xD4\x03\x48\x1C\x89\x8D\x74\xFF\xFF\xFF\xC7\x85\x60\xFF\xFF\xFF\x47\x65\x74\x50\xC7\x85\x64\xFF\xFF\xFF\x72\x6F\x63\x41\xC7\x85\x68\xFF\xFF\xFF\x64\x64\x72\x65\xC7\x85\x54\xFF\xFF\xFF\x00\x00\x00\x00\xEB\x0F\x8B\x85\x54\xFF\xFF\xFF\x83\xC0\x01\x89\x85\x54\xFF\xFF\xFF\x8B\x85\x54\xFF\xFF\xFF\x3B\x45\xA4\x73\x72\x8B\x85\x54\xFF\xFF\xFF\x8B\x4D\x80\x8B\x55\xD4\x03\x14\x81\x89\x55\x98\xB8\x04\x00\x00\x00\x6B\xC8\x00\x8B\x55\x98\x8B\x84\x0D\x60\xFF\xFF\xFF\x3B\x02\x75\x45\xB8\x04\x00\x00\x00\xC1\xE0\x00\x8B\x4D\x98\x8B\x94\x05\x60\xFF\xFF\xFF\x3B\x51\x04\x75\x2E\xB8\x04\x00\x00\x00\xD1\xE0\x8B\x4D\x98\x8B\x94\x05\x60\xFF\xFF\xFF\x3B\x51\x08\x75\x18\x8B\x85\x54\xFF\xFF\xFF\x8B\x8D\x74\xFF\xFF\xFF\x8B\x55\xD4\x03\x54\x81\x08\x89\x55\x8C\xEB\x05\xE9\x74\xFF\xFF\xFF\x33\xC0\x89\x85\x34\xFF\xFF\xFF\x89\x85\x38\xFF\xFF\xFF\x89\x85\x3C\xFF\xFF\xFF\x89\x85\x40\xFF\xFF\xFF\x89\x85\x44\xFF\xFF\xFF\x89\x85\x48\xFF\xFF\xFF\xB8\x04\x00\x00\x00\x6B\xC8\x00\xC7\x84\x0D\x34\xFF\xFF\xFF\x47\x65\x74\x4D\xB8\x04\x00\x00\x00\xC1\xE0\x00\xC7\x84\x05\x34\xFF\xFF\xFF\x6F\x64\x75\x6C\xB8\x04\x00\x00\x00\xD1\xE0\xC7\x84\x05\x34\xFF\xFF\xFF\x65\x48\x61\x6E\xB8\x04\x00\x00\x00\x6B\xC8\x03\xC7\x84\x0D\x34\xFF\xFF\xFF\x64\x6C\x65\x41\xB8\x04\x00\x00\x00\xC1\xE0\x02\xC7\x84\x05\x34\xFF\xFF\xFF\x00\x00\x00\x00\x90\x90\x8D\x85\x34\xFF\xFF\xFF\x50\x8B\x4D\xD4\x51\xFF\x55\x8C\x90\x90\x90\x90\x90\x90\x90\x89\x85\x28\xFF\xFF\xFF\x90\x90\x6A\x00\xFF\x95\x28\xFF\xFF\xFF\x90\x90\x90\x90\x90\x90\x90\x89\x85\x1C\xFF\xFF\xFF\x8B\x85\x1C\xFF\xFF\xFF\x89\x85\x10\xFF\xFF\xFF\x8B\x85\x10\xFF\xFF\xFF\x8B\x48\x3C\x8B\x95\x1C\xFF\xFF\xFF\x8D\x44\x0A\x18\x89\x85\x04\xFF\xFF\xFF\x8B\x85\x10\xFF\xFF\xFF\x8B\x48\x3C\x8B\x95\x1C\xFF\xFF\xFF\x8D\x44\x0A\x04\x89\x85\xF8\xFE\xFF\xFF\x8B\x85\x10\xFF\xFF\xFF\x8B\x48\x3C\x8B\x95\x1C\xFF\xFF\xFF\x8D\x84\x0A\xF8\x00\x00\x00\x89\x85\xEC\xFE\xFF\xFF\x8B\x85\xF8\xFE\xFF\xFF\x0F\xB7\x48\x02\x83\xE9\x01\x89\x8D\xE0\xFE\xFF\xFF\xC7\x85\xC8\xFE\xFF\xFF\x00\x00\x00\x00\x8B\x85\xEC\xFE\xFF\xFF\x8B\x48\x08\x8B\x95\xEC\xFE\xFF\xFF\x03\x4A\x0C\x89\x8D\xBC\xFE\xFF\xFF\xC7\x85\xB0\xFE\xFF\xFF\x00\x00\x00\x00\xEB\x0F\x8B\x85\xEC\xFE\xFF\xFF\x83\xC0\x28\x89\x85\xEC\xFE\xFF\xFF\x8B\x85\xB0\xFE\xFF\xFF\x3B\x85\xE0\xFE\xFF\xFF\x73\x36\x8B\x85\xEC\xFE\xFF\xFF\x81\x38\x2E\x74\x65\x78\x75\x26\x8B\x85\xEC\xFE\xFF\xFF\x8B\x48\x08\x89\x8D\xD4\xFE\xFF\xFF\x8B\x85\xEC\xFE\xFF\xFF\x8B\x48\x0C\x03\x8D\x10\xFF\xFF\xFF\x89\x8D\xC8\xFE\xFF\xFF\xEB\x02\xEB\xAD\xC6\x85\x98\xFE\xFF\xFF\x56\xC6\x85\x99\xFE\xFF\xFF\x69\xC6\x85\x9A\xFE\xFF\xFF\x72\xC6\x85\x9B\xFE\xFF\xFF\x74\xC6\x85\x9C\xFE\xFF\xFF\x75\xC6\x85\x9D\xFE\xFF\xFF\x61\xC6\x85\x9E\xFE\xFF\xFF\x6C\xC6\x85\x9F\xFE\xFF\xFF\x50\xC6\x85\xA0\xFE\xFF\xFF\x72\xC6\x85\xA1\xFE\xFF\xFF\x6F\xC6\x85\xA2\xFE\xFF\xFF\x74\xC6\x85\xA3\xFE\xFF\xFF\x65\xC6\x85\xA4\xFE\xFF\xFF\x63\xC6\x85\xA5\xFE\xFF\xFF\x74\xC6\x85\xA6\xFE\xFF\xFF\x00\x90\x90\x8D\x85\x98\xFE\xFF\xFF\x50\x8B\x4D\xD4\x51\xFF\x55\x8C\x90\x90\x90\x90\x90\x90\x90\x89\x85\x8C\xFE\xFF\xFF\x90\x90\x8D\x85\x80\xFE\xFF\xFF\x50\x6A\x40\x8B\x8D\xD4\xFE\xFF\xFF\x51\x8B\x95\xC8\xFE\xFF\xFF\x52\xFF\x95\x8C\xFE\xFF\xFF\x90\x90\x90\x90\x90\x90\x90\xB8\x08\x00\x00\x00\x6B\xC8\x05\x8B\x95\x04\xFF\xFF\xFF\x8B\x85\x1C\xFF\xFF\xFF\x03\x44\x0A\x60\x89\x85\x74\xFE\xFF\xFF\x8B\x85\x04\xFF\xFF\xFF\x8B\x48\x1C\x8B\x95\x04\xFF\xFF\xFF\x8B\x42\x10\x8B\x4C\x01\x04\x89\x8D\x68\xFE\xFF\xFF\xC7\x85\x5C\xFE\xFF\xFF\x00\x00\x00\x00\x8B\x85\xD4\xFE\xFF\xFF\xC1\xE8\x02\x89\x85\x5C\xFE\xFF\xFF\xB8\x08\x00\x00\x00\x6B\xC8\x05\x8B\x95\x04\xFF\xFF\xFF\x83\x7C\x0A\x60\x00\x75\x54\xC7\x85\x50\xFE\xFF\xFF\x00\x00\x00\x00\xEB\x0F\x8B\x85\x50\xFE\xFF\xFF\x83\xC0\x01\x89\x85\x50\xFE\xFF\xFF\x8B\x85\x50\xFE\xFF\xFF\x3B\x85\x5C\xFE\xFF\xFF\x73\x26\x8B\x85\x50\xFE\xFF\xFF\x8B\x8D\xC8\xFE\xFF\xFF\x8B\x14\x81\x81\xF2\x78\x56\x34\x12\x8B\x85\x50\xFE\xFF\xFF\x8B\x8D\xC8\xFE\xFF\xFF\x89\x14\x81\xEB\xBD\xE9\x51\x02\x00\x00\x8B\x85\x74\xFE\xFF\xFF\x83\x38\x00\x0F\x84\xE3\x00\x00\x00\x8B\x85\x74\xFE\xFF\xFF\x8B\x08\x89\x8D\x44\xFE\xFF\xFF\x8B\x85\x74\xFE\xFF\xFF\x83\xC0\x08\x89\x85\x38\xFE\xFF\xFF\x8B\x85\x74\xFE\xFF\xFF\x8B\x48\x04\x83\xE9\x08\xD1\xE9\x89\x8D\x2C\xFE\xFF\xFF\xC7\x85\x20\xFE\xFF\xFF\x00\x00\x00\x00\xEB\x0F\x8B\x85\x20\xFE\xFF\xFF\x83\xC0\x01\x89\x85\x20\xFE\xFF\xFF\x8B\x85\x20\xFE\xFF\xFF\x3B\x85\x2C\xFE\xFF\xFF\x7D\x6F\x8B\x85\x20\xFE\xFF\xFF\x8B\x8D\x38\xFE\xFF\xFF\x0F\xB7\x14\x41\x81\xE2\xFF\x0F\x00\x00\x03\x95\x44\xFE\xFF\xFF\x3B\x95\xBC\xFE\xFF\xFF\x76\x02\xEB\xBD\x8B\x85\x20\xFE\xFF\xFF\x8B\x8D\x38\xFE\xFF\xFF\x0F\xB7\x14\x41\x81\xE2\xFF\x0F\x00\x00\x03\x95\x1C\xFF\xFF\xFF\x03\x95\x44\xFE\xFF\xFF\x89\x95\x14\xFE\xFF\xFF\x8B\x85\x14\xFE\xFF\xFF\x8B\x08\x03\x8D\x68\xFE\xFF\xFF\x2B\x8D\x1C\xFF\xFF\xFF\x8B\x95\x14\xFE\xFF\xFF\x89\x0A\xE9\x74\xFF\xFF\xFF\x8B\x85\x74\xFE\xFF\xFF\x8B\x8D\x74\xFE\xFF\xFF\x03\x48\x04\x89\x8D\x74\xFE\xFF\xFF\xE9\x0E\xFF\xFF\xFF\xC7\x85\x08\xFE\xFF\xFF\x00\x00\x00\x00\xEB\x0F\x8B\x85\x08\xFE\xFF\xFF\x83\xC0\x01\x89\x85\x08\xFE\xFF\xFF\x8B\x85\x08\xFE\xFF\xFF\x3B\x85\x5C\xFE\xFF\xFF\x73\x26\x8B\x85\x08\xFE\xFF\xFF\x8B\x8D\xC8\xFE\xFF\xFF\x8B\x14\x81\x81\xF2\x78\x56\x34\x12\x8B\x85\x08\xFE\xFF\xFF\x8B\x8D\xC8\xFE\xFF\xFF\x89\x14\x81\xEB\xBD\xB8\x08\x00\x00\x00\x6B\xC8\x05\x8B\x95\x04\xFF\xFF\xFF\x8B\x85\x1C\xFF\xFF\xFF\x03\x44\x0A\x60\x89\x85\x74\xFE\xFF\xFF\x8B\x85\x74\xFE\xFF\xFF\x83\x38\x00\x0F\x84\xE3\x00\x00\x00\x8B\x85\x74\xFE\xFF\xFF\x8B\x08\x89\x8D\xFC\xFD\xFF\xFF\x8B\x85\x74\xFE\xFF\xFF\x83\xC0\x08\x89\x85\xF0\xFD\xFF\xFF\x8B\x85\x74\xFE\xFF\xFF\x8B\x48\x04\x83\xE9\x08\xD1\xE9\x89\x8D\xE4\xFD\xFF\xFF\xC7\x85\xD8\xFD\xFF\xFF\x00\x00\x00\x00\xEB\x0F\x8B\x85\xD8\xFD\xFF\xFF\x83\xC0\x01\x89\x85\xD8\xFD\xFF\xFF\x8B\x85\xD8\xFD\xFF\xFF\x3B\x85\xE4\xFD\xFF\xFF\x7D\x6F\x8B\x85\xD8\xFD\xFF\xFF\x8B\x8D\xF0\xFD\xFF\xFF\x0F\xB7\x14\x41\x81\xE2\xFF\x0F\x00\x00\x03\x95\xFC\xFD\xFF\xFF\x3B\x95\xBC\xFE\xFF\xFF\x76\x02\xEB\xBD\x8B\x85\xD8\xFD\xFF\xFF\x8B\x8D\xF0\xFD\xFF\xFF\x0F\xB7\x14\x41\x81\xE2\xFF\x0F\x00\x00\x03\x95\x1C\xFF\xFF\xFF\x03\x95\xFC\xFD\xFF\xFF\x89\x95\xCC\xFD\xFF\xFF\x8B\x85\xCC\xFD\xFF\xFF\x8B\x08\x2B\x8D\x68\xFE\xFF\xFF\x03\x8D\x1C\xFF\xFF\xFF\x8B\x95\xCC\xFD\xFF\xFF\x89\x0A\xE9\x74\xFF\xFF\xFF\x8B\x85\x74\xFE\xFF\xFF\x8B\x8D\x74\xFE\xFF\xFF\x03\x48\x04\x89\x8D\x74\xFE\xFF\xFF\xE9\x0E\xFF\xFF\xFF\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x5F\x5E\x5B\x81\xC4\xB8\x03\x00\x00\x90\x90\x90\x90\x90\x90\x90\x8B\xE5\x5D";

DWORD ShellCodeLenth = 1760;



BOOL ReadExeFile(CHAR* szNewFilePath, BYTE** buffer)
{
DWORD dwNumberOfBytesRead = 0;
HANDLE hFile;
DWORD dwFileSize = 0;

hFile = CreateFileA(szNewFilePath, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
if (hFile == INVALID_HANDLE_VALUE)
{
printf("CreateFileA Failed !!! GetLastError: %d", GetLastError());
return FALSE;
}

dwFileSize = GetFileSize(hFile, NULL);
//这里申请的空间+多少需要根据生成的shellcode长度来确定
*buffer = new BYTE[dwFileSize+2000];

if (ReadFile(hFile, *buffer, dwFileSize, &dwNumberOfBytesRead, NULL) == FALSE)
{
printf("ReadFile Failed !!! GetLastError: %d", GetLastError());
return FALSE;
}
return TRUE;

}

BOOL AddNewSectionHeader(PBYTE pbuf)
{
PIMAGE_DOS_HEADER pIDH = (PIMAGE_DOS_HEADER)pbuf;
PIMAGE_NT_HEADERS INH = (PIMAGE_NT_HEADERS)(pbuf + pIDH->e_lfanew);
PIMAGE_FILE_HEADER pIFH = (PIMAGE_FILE_HEADER)(pbuf + pIDH->e_lfanew + 4);
PIMAGE_OPTIONAL_HEADER pIOH = (PIMAGE_OPTIONAL_HEADER)(pbuf + pIDH->e_lfanew + 0x18);
PIMAGE_SECTION_HEADER pISH = (PIMAGE_SECTION_HEADER)(pbuf + pIDH->e_lfanew + sizeof(IMAGE_NT_HEADERS));

DWORD OldSectionNumber = pIFH->NumberOfSections;

//节区数加1
pIFH->NumberOfSections = OldSectionNumber + 1;

//修改PMAGE_OPTIONAL_HEADER中的Size of Image,添加大小必须满足Section Alignment的倍数关系
printf("%x ", (ShellCodeLenth / pIOH->SectionAlignment + 1));
pIOH->SizeOfImage = (ShellCodeLenth / pIOH->SectionAlignment + 1) * pIOH->SectionAlignment+ pIOH->SizeOfImage;


//获取最后一个节区的一些数据
PIMAGE_SECTION_HEADER pLastSectionHeader = (PIMAGE_SECTION_HEADER)(pISH + OldSectionNumber - 1);
DWORD LastVirtualSize = pLastSectionHeader->Misc.VirtualSize;
DWORD LastVirtualAddress = pLastSectionHeader->VirtualAddress;
DWORD LastSizeOfRawData = pLastSectionHeader->SizeOfRawData;
DWORD LastPointerToRawData = pLastSectionHeader->PointerToRawData;

//添加新节区
PIMAGE_SECTION_HEADER pNewSectionHeader = (PIMAGE_SECTION_HEADER)(pISH + OldSectionNumber);
memcpy(&pNewSectionHeader->Name, ".Shell", 6);
pNewSectionHeader->Misc.VirtualSize = ShellCodeLenth;
pNewSectionHeader->VirtualAddress = (LastVirtualSize / pIOH->SectionAlignment + 1) * pIOH->SectionAlignment + LastVirtualAddress;
pNewSectionHeader->SizeOfRawData = (ShellCodeLenth / pIOH->FileAlignment + 1) * pIOH->FileAlignment;
pNewSectionHeader->PointerToRawData = LastSizeOfRawData + LastPointerToRawData;
pNewSectionHeader->PointerToRelocations = 0;
pNewSectionHeader->PointerToLinenumbers = 0;
pNewSectionHeader->NumberOfLinenumbers = 0;
pNewSectionHeader->NumberOfRelocations = 0;
pNewSectionHeader->Characteristics = IMAGE_SCN_CNT_CODE | IMAGE_SCN_MEM_EXECUTE | IMAGE_SCN_MEM_READ;

return TRUE;

}

BOOL EncodeTextSection(PBYTE pbuf)
{

PIMAGE_DOS_HEADER pIDH = (PIMAGE_DOS_HEADER)pbuf;
PIMAGE_NT_HEADERS pINH = (PIMAGE_NT_HEADERS)(pbuf + pIDH->e_lfanew);
PIMAGE_FILE_HEADER pIFH = (PIMAGE_FILE_HEADER)(pbuf + pIDH->e_lfanew + 4);
PIMAGE_OPTIONAL_HEADER pIOH = (PIMAGE_OPTIONAL_HEADER)(pbuf + pIDH->e_lfanew + 0x18);
PIMAGE_SECTION_HEADER pISH = (PIMAGE_SECTION_HEADER)(pbuf + pIDH->e_lfanew + sizeof(IMAGE_NT_HEADERS));

DWORD OldSectionNumber = pIFH->NumberOfSections - 1;
DWORD dwTextsize;
DWORD* pTextStart=NULL;

for (int i = 0; i < OldSectionNumber; pISH++)
{
if (memcmp(pISH->Name, ".text", 6) == 0)
{
dwTextsize = pISH->Misc.VirtualSize;
pTextStart = (DWORD*)(pISH->PointerToRawData + (DWORD)pIDH);
break;
}
}

DWORD Round=0;
if (dwTextsize % 4 == 0)
{
Round = dwTextsize / 4;
}
else
{
Round = dwTextsize/4 + 1;
}

for (int i = 0; i < Round; i++)
{
*(unsigned int*)(pTextStart + i ) = *(unsigned int*)(pTextStart + i ) ^ 0x12345678;
}



//本来想用blowfish加密方式加密的,但是写shellcode解密比较麻烦,就放弃了。
//BLOWFISH_CTX ctx;
//BlowFishInit(&ctx, (unsigned char*)"Shell", strlen("Shell"));

//DWORD Round;
//if (dwTextsize / 8 == 0)
//{
// Round = dwTextsize / 8;
//}
//else
//{
// Round = dwTextsize + 1;
//}
//printf("%x ", dwTextsize);
////BlowFish_Encry(&ctx, &L, &R);
//for (int i = 0; i < Round; i++)
//{
// BlowFish_Encry(&ctx, (unsigned int*)(pTextStart + i * 8), (unsigned int*)(pTextStart + i * 8 + 4));
//}

return TRUE;

}

BOOL AddShellCode(PBYTE pbuf)
{
PIMAGE_DOS_HEADER pIDH = (PIMAGE_DOS_HEADER)pbuf;
PIMAGE_OPTIONAL_HEADER pIOH = (PIMAGE_OPTIONAL_HEADER)(pbuf + pIDH->e_lfanew + 0x18);
PIMAGE_FILE_HEADER pIFH = (PIMAGE_FILE_HEADER)(pbuf + pIDH->e_lfanew + 4);
PIMAGE_SECTION_HEADER pISH = (PIMAGE_SECTION_HEADER)(pbuf + pIDH->e_lfanew + sizeof(IMAGE_NT_HEADERS));
PIMAGE_SECTION_HEADER pNewSectionHeader = (PIMAGE_SECTION_HEADER)(pISH + pIFH->NumberOfSections - 1);

PBYTE ShellSectionStart = pbuf + pNewSectionHeader->PointerToRawData;

//添加绝对跳转,并且空出4byte空间存储原来的ImageBase,用于shellcode还原到重定位前。
*(DWORD*)ShellSectionStart = 0x4750674;
ShellSectionStart = ShellSectionStart+ 4;

*(DWORD*)ShellSectionStart = pIOH->ImageBase;
ShellSectionStart = ShellSectionStart + 4;

//添加shellcode到新节区
memcpy(ShellSectionStart, shellcode, ShellCodeLenth);
ShellSectionStart = ShellSectionStart + ShellCodeLenth;

//执行完shellcode解密后,跳转到原EP,需要算一下偏移。
*(char*)ShellSectionStart = 0xe9;
ShellSectionStart = ShellSectionStart + 1;
DWORD offset = pIOH->AddressOfEntryPoint - pNewSectionHeader->VirtualAddress - ShellCodeLenth-13;
memcpy(ShellSectionStart, &offset, 4);

//修改EP为shellcode的起始地址,也就是OEP
DWORD OldEntryPoint = pIOH->AddressOfEntryPoint;
pIOH->AddressOfEntryPoint = pNewSectionHeader->VirtualAddress;


//将修改后的数据写入新文件中
FILE* fp = NULL;
fp = fopen("test_shell.exe", "wb");
DWORD NewFileSize = pNewSectionHeader->PointerToRawData + pNewSectionHeader->SizeOfRawData;
fwrite(pbuf, sizeof(unsigned __int8), NewFileSize, fp);
fclose(fp);
return TRUE;
}
BOOL Blowfishtest()
{
unsigned int L = 0x546F4EBF, R = 0xB4ED937B;
unsigned char enc[] = { 0xBF, 0x4E, 0x6F, 0x54, 0x7B, 0x93, 0xED, 0xB4, 0x7E, 0xA0,
0xD2, 0x82, 0xDD, 0xEF, 0xD3, 0x13, 0x0F, 0xAE, 0x09, 0x22,
0x61, 0xDF, 0x4E, 0x59, 0x2C, 0x78, 0x33, 0xB9, 0x32, 0xE5,
0x07, 0x1C };

BLOWFISH_CTX ctx;
//BlowFish_Encry(&ctx, &L, &R);
for (int i = 0; i < 4; i++)
{
BlowFish_Decrypt(&ctx, (unsigned int*)(enc + i * 8), (unsigned int*)(enc + i * 8 + 4));
}
printf("%s", enc);
return TRUE;
}

BOOL ExeShell(PBYTE pbuf)
{
//添加一个新节区,用来装解密的shellcode。
AddNewSectionHeader(pbuf);


//采用异或加密text节区。
EncodeTextSection(pbuf);

//添加shellcode
AddShellCode(pbuf);

//Blowfishtest();
return TRUE;

}

int main(int argc, char* argv[])
{
//CHAR szExePath[MAX_PATH] = "D:\\xor.exe";
PBYTE buf;
if (argc == 2)
{
//申请空间,并读取目标exe文件
ReadExeFile(argv[1], &buf);

//加壳
ExeShell(buf);

printf("Packing Success!!!");
}
else
{
printf("USAGE : %s FilePath\n\n",argv[0]);
}

system("pause");
}

成品实验

The_Itach1.exe是一个弹窗程序。

然后加壳生成test_shell.exe,运行一下。

可以看到,仍然正常运行。

总结

上面没有详细的讲解过程,但是看代码应该就完全可以明白了,而且这是最简单的一种壳的实现方式,如果shellcode写的更好,完全可以做到像upx壳完全隐藏原文件信息,将原文件信息和解密代码都放在upx1的节区中。可以通过编写一个壳来熟悉PE结构。