加密壳

7、添加区段完成加壳流程

1、课程目标

  1. 实现向PE添加新区段
  2. 完成原始代码加密
  3. 嵌入壳代码和配置
  4. 生成完整的加壳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、课后作业

  1. 实现完整的加壳工具

    • 整合所有功能
    • 添加命令行参数
    • 支持多种加密选项
  2. 测试加壳效果

    • 测试各种PE文件
    • 验证加壳后能正常运行
    • 测试免杀效果
  3. 优化加壳工具

    • 压缩原始代码
    • 添加更多混淆
    • 减小增加的体积