加密壳
7、添加区段完成加壳流程
1、课程目标
- 实现向PE添加新区段
- 完成原始代码加密
- 嵌入壳代码和配置
- 生成完整的加壳PE
2、名词解释
| 名词 | 全称 | 解释 |
|---|---|---|
| Section Addition | 区段添加 | 向PE添加新区段 |
| Code Encryption | 代码加密 | 加密原始代码段 |
| Config Embed | 配置嵌入 | 将配置信息嵌入壳 |
| PE Rebuild | PE重建 | 生成新的PE文件 |
3、使用工具
- Visual Studio 2022
- CFF Explorer
- PE-bear
- x64dbg
4、技术原理
4.1、加壳PE构建流程
原始PE 加壳后PE
+---------------+ +---------------+
| DOS Header | | DOS Header |
| PE Header | | PE Header | (修改)
| Section Hdrs | | Section Hdrs | (增加)
+---------------+ +---------------+
| .text (代码) | 加密 | .packed |
| .data | --------> | (加密的原始 |
| .rdata | | 区段数据) |
| .rsrc | +---------------+
+---------------+ | .shell |
| (壳代码+配置) |
+---------------+
修改项:
1. NumberOfSections + 2 (或合并为1个)
2. AddressOfEntryPoint -> 壳入口
3. SizeOfImage 更新
4. 添加新区段头
5、代码实现
5.1、加密原始代码
// 加密原始PE的代码段
BOOL EncryptTargetSections(PPACKER_CONTEXT ctx) {
printf("\n===== 加密目标PE =====\n");
PPE_FILE_INFO target = &ctx->targetPE;
// 找到需要加密的区段
for (WORD i = 0; i < target->sectionCount; i++) {
PIMAGE_SECTION_HEADER sec = &target->sectionHeaders[i];
// 加密代码段和数据段
BOOL shouldEncrypt = FALSE;
if (sec->Characteristics & IMAGE_SCN_CNT_CODE) {
shouldEncrypt = TRUE;
}
if (sec->Characteristics & IMAGE_SCN_CNT_INITIALIZED_DATA) {
// 可选:加密初始化数据
// shouldEncrypt = TRUE;
}
// 跳过资源段(通常需要保留)
if (memcmp(sec->Name, ".rsrc", 5) == 0) {
shouldEncrypt = FALSE;
}
// 跳过重定位段
if (memcmp(sec->Name, ".reloc", 6) == 0) {
shouldEncrypt = FALSE;
}
if (shouldEncrypt && sec->SizeOfRawData > 0) {
LPBYTE sectionData = target->fileData + sec->PointerToRawData;
DWORD sectionSize = sec->SizeOfRawData;
printf("[*] 加密区段: %-8s (0x%08X bytes)\n",
sec->Name, sectionSize);
// 应用加密
switch (ctx->encryptionType) {
case ENCRYPT_XOR:
XorCrypt(sectionData, sectionSize,
(BYTE*)&ctx->encryptionKey, 4);
break;
case ENCRYPT_RC4: {
RC4_STATE rc4;
Rc4Init(&rc4, (BYTE*)&ctx->encryptionKey, 4);
Rc4Crypt(&rc4, sectionData, sectionSize);
break;
}
case ENCRYPT_TRANSFORM:
TransformXor(sectionData, sectionSize, ctx->encryptionKey);
break;
}
}
}
printf("[+] 加密完成\n");
return TRUE;
}
5.2、添加新区段
// 添加壳区段
BOOL AddShellSection(PPACKER_CONTEXT ctx, LPBYTE* newPE, DWORD* newPESize) {
printf("\n===== 添加壳区段 =====\n");
PPE_FILE_INFO target = &ctx->targetPE;
// 准备壳配置
SHELL_CONFIG config = {0};
config.magic = SHELL_MAGIC;
config.originalOEP = target->entryPoint;
config.encryptionType = ctx->encryptionType;
config.key = ctx->encryptionKey;
config.flags = FLAG_ANTI_DEBUG | FLAG_INTEGRITY;
// 计算壳区段大小(代码 + 配置 + 填充)
DWORD shellDataSize = ctx->stubCodeSize + sizeof(SHELL_CONFIG);
DWORD alignedShellSize = (shellDataSize + target->fileAlignment - 1) &
~(target->fileAlignment - 1);
// 计算新区段位置
PIMAGE_SECTION_HEADER lastSection =
&target->sectionHeaders[target->sectionCount - 1];
// 新区段的文件偏移
DWORD newSectionFOA = lastSection->PointerToRawData + lastSection->SizeOfRawData;
newSectionFOA = (newSectionFOA + target->fileAlignment - 1) &
~(target->fileAlignment - 1);
// 新区段的RVA
DWORD newSectionRVA = lastSection->VirtualAddress + lastSection->Misc.VirtualSize;
newSectionRVA = (newSectionRVA + target->sectionAlignment - 1) &
~(target->sectionAlignment - 1);
// 更新配置中的打包数据信息
// 这里假设整个原始.text段被加密
for (WORD i = 0; i < target->sectionCount; i++) {
if (target->sectionHeaders[i].Characteristics & IMAGE_SCN_CNT_CODE) {
config.packedDataRVA = target->sectionHeaders[i].VirtualAddress;
config.packedDataSize = target->sectionHeaders[i].SizeOfRawData;
break;
}
}
printf("[*] 新区段位置:\n");
printf(" FOA: 0x%08X\n", newSectionFOA);
printf(" RVA: 0x%08X\n", newSectionRVA);
printf(" Size: 0x%08X\n", alignedShellSize);
// 计算新PE大小
*newPESize = newSectionFOA + alignedShellSize;
// 分配新PE内存
*newPE = (LPBYTE)VirtualAlloc(
NULL, *newPESize,
MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE
);
if (!*newPE) {
printf("[-] 分配内存失败\n");
return FALSE;
}
// 复制原始PE
memcpy(*newPE, target->fileData, target->fileSize);
// 填充到新大小
ZeroMemory(*newPE + target->fileSize, *newPESize - target->fileSize);
// 更新PE头
PIMAGE_DOS_HEADER newDos = (PIMAGE_DOS_HEADER)*newPE;
PIMAGE_NT_HEADERS newNt = (PIMAGE_NT_HEADERS)(*newPE + newDos->e_lfanew);
// 增加区段数量
newNt->FileHeader.NumberOfSections++;
// 添加新区段头
PIMAGE_SECTION_HEADER newSecHeader = IMAGE_FIRST_SECTION(newNt) +
target->sectionCount;
ZeroMemory(newSecHeader, sizeof(IMAGE_SECTION_HEADER));
memcpy(newSecHeader->Name, ".shell", 7);
newSecHeader->Misc.VirtualSize = shellDataSize;
newSecHeader->VirtualAddress = newSectionRVA;
newSecHeader->SizeOfRawData = alignedShellSize;
newSecHeader->PointerToRawData = newSectionFOA;
newSecHeader->Characteristics = IMAGE_SCN_CNT_CODE |
IMAGE_SCN_MEM_EXECUTE |
IMAGE_SCN_MEM_READ;
// 更新SizeOfImage
DWORD newImageSize = newSectionRVA + shellDataSize;
newImageSize = (newImageSize + target->sectionAlignment - 1) &
~(target->sectionAlignment - 1);
if (target->is64Bit) {
PIMAGE_OPTIONAL_HEADER64 opt = (PIMAGE_OPTIONAL_HEADER64)&newNt->OptionalHeader;
opt->SizeOfImage = newImageSize;
// 修改入口点到壳
opt->AddressOfEntryPoint = newSectionRVA; // 壳代码开始处
} else {
PIMAGE_OPTIONAL_HEADER32 opt = (PIMAGE_OPTIONAL_HEADER32)&newNt->OptionalHeader;
opt->SizeOfImage = newImageSize;
opt->AddressOfEntryPoint = newSectionRVA;
}
// 复制壳代码
memcpy(*newPE + newSectionFOA, ctx->stubCode, ctx->stubCodeSize);
// 复制配置(放在壳代码末尾)
memcpy(*newPE + newSectionFOA + ctx->stubCodeSize, &config, sizeof(config));
printf("[+] 壳区段添加完成\n");
printf(" 新入口点: 0x%08X\n", newSectionRVA);
printf(" 新映像大小: 0x%08X\n", newImageSize);
return TRUE;
}
5.3、完整加壳流程
// 执行加壳
BOOL PackExecutable(PPACKER_CONTEXT ctx) {
printf("\n========== 开始加壳 ==========\n");
// 1. 准备壳代码
if (!PrepareStubCode(ctx)) {
printf("[-] 准备壳代码失败\n");
return FALSE;
}
// 2. 加密目标PE
if (!EncryptTargetSections(ctx)) {
printf("[-] 加密失败\n");
return FALSE;
}
// 3. 构建新PE
LPBYTE packedPE;
DWORD packedSize;
if (!AddShellSection(ctx, &packedPE, &packedSize)) {
printf("[-] 添加壳区段失败\n");
return FALSE;
}
// 4. 保存输出文件
if (!SaveFile(ctx->outputPath, packedPE, packedSize)) {
printf("[-] 保存文件失败\n");
VirtualFree(packedPE, 0, MEM_RELEASE);
return FALSE;
}
printf("\n========== 加壳完成 ==========\n");
printf("输出文件: %s\n", ctx->outputPath);
printf("原始大小: %d bytes\n", ctx->targetPE.fileSize);
printf("加壳大小: %d bytes\n", packedSize);
printf("增加: %d bytes (%.1f%%)\n",
packedSize - ctx->targetPE.fileSize,
(float)(packedSize - ctx->targetPE.fileSize) / ctx->targetPE.fileSize * 100);
VirtualFree(packedPE, 0, MEM_RELEASE);
return TRUE;
}
// 保存文件
BOOL SaveFile(const char* path, LPBYTE data, DWORD size) {
HANDLE hFile = CreateFileA(
path,
GENERIC_WRITE,
0,
NULL,
CREATE_ALWAYS,
FILE_ATTRIBUTE_NORMAL,
NULL
);
if (hFile == INVALID_HANDLE_VALUE) {
return FALSE;
}
DWORD written;
BOOL result = WriteFile(hFile, data, size, &written, NULL);
CloseHandle(hFile);
return result && written == size;
}
5.4、加壳器主程序
// 使用示例
int main(int argc, char* argv[]) {
printf("========================================\n");
printf(" Simple PE Packer v1.0 \n");
printf("========================================\n\n");
if (argc < 4) {
printf("用法: %s <目标PE> <壳文件> <输出文件> [密钥]\n", argv[0]);
printf("\n示例: packer.exe target.exe stub.exe packed.exe 12345678\n");
return 1;
}
PACKER_CONTEXT ctx;
// 初始化
if (!InitPacker(&ctx, argv[1], argv[2])) {
printf("[-] 初始化失败\n");
return 1;
}
// 设置输出路径
strcpy(ctx.outputPath, argv[3]);
// 设置加密参数
ctx.encryptionType = ENCRYPT_XOR;
ctx.encryptionKey = (argc > 4) ? strtoul(argv[4], NULL, 16) : 0x12345678;
printf("[*] 加密类型: XOR\n");
printf("[*] 加密密钥: 0x%08X\n", ctx.encryptionKey);
// 执行加壳
BOOL success = PackExecutable(&ctx);
// 清理
CleanupPacker(&ctx);
return success ? 0 : 1;
}
6、课后作业
-
实现完整的加壳工具
- 整合所有功能
- 添加命令行参数
- 支持多种加密选项
-
测试加壳效果
- 测试各种PE文件
- 验证加壳后能正常运行
- 测试免杀效果
-
优化加壳工具
- 压缩原始代码
- 添加更多混淆
- 减小增加的体积