加密壳

8、阶段合集

1、课程目标

  1. 完成完整的加壳/脱壳系统
  2. 实现壳代码的完整功能
  3. 添加高级特性和优化
  4. 测试和验证加壳效果

2、名词解释

名词 全称 解释
Complete Packer 完整加壳器 包含所有功能的加壳工具
Unpacker 脱壳器 还原加壳程序的工具
Memory Dump 内存转储 从内存提取已解密的代码
IAT Rebuild 导入表重建 恢复被破坏的导入表

3、使用工具

  • Visual Studio 2022
  • x64dbg
  • Scylla(IAT重建)
  • PE-bear

4、技术原理

4.1、完整加壳系统架构

+------------------+     +------------------+     +------------------+
|    加壳器        |     |    壳代码        |     |    脱壳器        |
|    (Packer)      |     |    (Stub)        |     |    (Unpacker)    |
+------------------+     +------------------+     +------------------+
        |                        |                        |
        v                        v                        v
+------------------+     +------------------+     +------------------+
| - 解析目标PE     |     | - 获取API        |     | - 运行程序       |
| - 加密代码段     |     | - 反调试检测     |     | - 等待OEP        |
| - 提取壳代码     |     | - 解密代码       |     | - 内存转储       |
| - 修复重定位     |     | - 恢复导入表     |     | - 重建导入表     |
| - 添加壳区段     |     | - 跳转OEP        |     | - 修复PE头       |
| - 修改入口点     |     +------------------+     | - 保存文件       |
| - 保存新PE       |                              +------------------+
+------------------+

5、代码实现

5.1、完整的壳代码

// stub.c - 完整的壳代码实现
// 编译选项:/GS- /NODEFAULTLIB /ENTRY:ShellEntry

#include <windows.h>

// 哈希定义
#define HASH_KERNEL32           0x6A4ABC5B
#define HASH_GETPROCADDRESS     0x7C0DFCAA
#define HASH_LOADLIBRARYA       0xEC0E4E8E
#define HASH_VIRTUALPROTECT     0x7946C61B

// 壳配置
#pragma pack(push, 1)
typedef struct _SHELL_CONFIG {
    DWORD   magic;
    DWORD   originalOEP;
    DWORD   packedDataRVA;
    DWORD   packedDataSize;
    DWORD   originalDataSize;
    DWORD   encryptionType;
    DWORD   key;
    DWORD   shellCrc;
    DWORD   dataCrc;
    DWORD   flags;
} SHELL_CONFIG;
#pragma pack(pop)

#define SHELL_MAGIC         0xDEADC0DE
#define ENCRYPT_XOR         1
#define FLAG_ANTI_DEBUG     0x0001

// ROR13哈希
DWORD Ror13Hash(const char* str) {
    DWORD hash = 0;
    while (*str) {
        hash = ((hash >> 13) | (hash << 19));
        hash += *str++;
    }
    return hash;
}

// 通过哈希获取模块
HMODULE GetModuleByHash(DWORD hash) {
    #ifdef _WIN64
    PPEB peb = (PPEB)__readgsqword(0x60);
    #else
    PPEB peb;
    __asm {
        mov eax, fs:[0x30]
        mov peb, eax
    }
    #endif
    
    PPEB_LDR_DATA ldr = peb->Ldr;
    PLIST_ENTRY head = &ldr->InMemoryOrderModuleList;
    PLIST_ENTRY entry = head->Flink;
    
    while (entry != head) {
        PLDR_DATA_TABLE_ENTRY module = CONTAINING_RECORD(
            entry, LDR_DATA_TABLE_ENTRY, InMemoryOrderLinks);
        
        if (module->BaseDllName.Buffer) {
            DWORD modHash = 0;
            WCHAR* name = module->BaseDllName.Buffer;
            while (*name) {
                WCHAR c = *name;
                if (c >= L'A' && c <= L'Z') c += 0x20;
                modHash = ((modHash >> 13) | (modHash << 19));
                modHash += (char)c;
                name++;
            }
            
            if (modHash == hash) {
                return (HMODULE)module->DllBase;
            }
        }
        entry = entry->Flink;
    }
    return NULL;
}

// 通过哈希获取函数
FARPROC GetFunctionByHash(HMODULE hModule, DWORD funcHash) {
    LPBYTE base = (LPBYTE)hModule;
    
    PIMAGE_DOS_HEADER dos = (PIMAGE_DOS_HEADER)base;
    PIMAGE_NT_HEADERS nt = (PIMAGE_NT_HEADERS)(base + dos->e_lfanew);
    
    DWORD exportRVA = nt->OptionalHeader.DataDirectory[0].VirtualAddress;
    if (exportRVA == 0) return NULL;
    
    PIMAGE_EXPORT_DIRECTORY exp = (PIMAGE_EXPORT_DIRECTORY)(base + exportRVA);
    
    DWORD* eat = (DWORD*)(base + exp->AddressOfFunctions);
    DWORD* ent = (DWORD*)(base + exp->AddressOfNames);
    WORD* eot = (WORD*)(base + exp->AddressOfNameOrdinals);
    
    for (DWORD i = 0; i < exp->NumberOfNames; i++) {
        char* name = (char*)(base + ent[i]);
        if (Ror13Hash(name) == funcHash) {
            return (FARPROC)(base + eat[eot[i]]);
        }
    }
    return NULL;
}

// 检测调试器
BOOL IsBeingDebugged() {
    #ifdef _WIN64
    return *(PBYTE)(__readgsqword(0x60) + 2);
    #else
    BOOL result;
    __asm {
        mov eax, fs:[0x30]
        movzx eax, byte ptr [eax + 2]
        mov result, eax
    }
    return result;
    #endif
}

// XOR解密
void XorDecrypt(LPBYTE data, DWORD size, DWORD key) {
    LPBYTE keyBytes = (LPBYTE)&key;
    for (DWORD i = 0; i < size; i++) {
        data[i] ^= keyBytes[i % 4];
    }
}

// 获取当前模块基址
LPBYTE GetCurrentImageBase() {
    #ifdef _WIN64
    PPEB peb = (PPEB)__readgsqword(0x60);
    return *(LPBYTE*)(((LPBYTE)peb) + 0x10);
    #else
    LPBYTE base;
    __asm {
        mov eax, fs:[0x30]
        mov eax, [eax + 0x08]
        mov base, eax
    }
    return base;
    #endif
}

// 定位壳配置
SHELL_CONFIG* FindShellConfig(LPBYTE imageBase) {
    // 配置在壳代码末尾,通过搜索魔数定位
    PIMAGE_DOS_HEADER dos = (PIMAGE_DOS_HEADER)imageBase;
    PIMAGE_NT_HEADERS nt = (PIMAGE_NT_HEADERS)(imageBase + dos->e_lfanew);
    
    // 找到.shell区段
    PIMAGE_SECTION_HEADER sections = IMAGE_FIRST_SECTION(nt);
    for (WORD i = 0; i < nt->FileHeader.NumberOfSections; i++) {
        if (sections[i].Name[0] == '.' && sections[i].Name[1] == 's') {
            LPBYTE sectionEnd = imageBase + sections[i].VirtualAddress + 
                               sections[i].Misc.VirtualSize;
            
            // 配置在区段末尾
            SHELL_CONFIG* config = (SHELL_CONFIG*)(sectionEnd - sizeof(SHELL_CONFIG));
            if (config->magic == SHELL_MAGIC) {
                return config;
            }
        }
    }
    return NULL;
}

// 壳入口
void __stdcall ShellEntry() {
    // 获取kernel32
    HMODULE hKernel32 = GetModuleByHash(HASH_KERNEL32);
    if (!hKernel32) return;
    
    // 获取API
    typedef BOOL (WINAPI* PFN_VirtualProtect)(LPVOID, SIZE_T, DWORD, PDWORD);
    PFN_VirtualProtect pVirtualProtect = 
        (PFN_VirtualProtect)GetFunctionByHash(hKernel32, HASH_VIRTUALPROTECT);
    
    if (!pVirtualProtect) return;
    
    // 获取基址
    LPBYTE imageBase = GetCurrentImageBase();
    
    // 定位配置
    SHELL_CONFIG* config = FindShellConfig(imageBase);
    if (!config || config->magic != SHELL_MAGIC) return;
    
    // 反调试
    if (config->flags & FLAG_ANTI_DEBUG) {
        if (IsBeingDebugged()) {
            return;
        }
    }
    
    // 修改内存保护
    LPBYTE packedData = imageBase + config->packedDataRVA;
    DWORD oldProtect;
    pVirtualProtect(packedData, config->packedDataSize, 
                   PAGE_EXECUTE_READWRITE, &oldProtect);
    
    // 解密
    if (config->encryptionType == ENCRYPT_XOR) {
        XorDecrypt(packedData, config->packedDataSize, config->key);
    }
    
    // 恢复保护
    pVirtualProtect(packedData, config->packedDataSize, 
                   PAGE_EXECUTE_READ, &oldProtect);
    
    // 跳转到OEP
    LPBYTE oep = imageBase + config->originalOEP;
    ((void(*)())oep)();
}

5.2、简单脱壳器

// unpacker.c - 简单脱壳器
#include <windows.h>
#include <stdio.h>
#include <tlhelp32.h>

// 等待程序到达OEP
BOOL WaitForOEP(HANDLE hProcess, LPVOID oepAddress) {
    BYTE breakpoint = 0xCC;
    BYTE original;
    SIZE_T bytesRead, bytesWritten;
    
    // 读取OEP原始字节
    ReadProcessMemory(hProcess, oepAddress, &original, 1, &bytesRead);
    
    // 设置断点
    WriteProcessMemory(hProcess, oepAddress, &breakpoint, 1, &bytesWritten);
    FlushInstructionCache(hProcess, oepAddress, 1);
    
    // 继续执行
    DEBUG_EVENT debugEvent;
    
    while (1) {
        if (!WaitForDebugEvent(&debugEvent, 10000)) {
            printf("[-] 等待超时\n");
            break;
        }
        
        if (debugEvent.dwDebugEventCode == EXCEPTION_DEBUG_EVENT) {
            if (debugEvent.u.Exception.ExceptionRecord.ExceptionCode == 
                EXCEPTION_BREAKPOINT) {
                
                LPVOID exAddr = debugEvent.u.Exception.ExceptionRecord.ExceptionAddress;
                
                if (exAddr == oepAddress) {
                    printf("[+] 到达OEP: 0x%p\n", oepAddress);
                    
                    // 恢复原始字节
                    WriteProcessMemory(hProcess, oepAddress, &original, 1, &bytesWritten);
                    
                    return TRUE;
                }
            }
        }
        
        ContinueDebugEvent(debugEvent.dwProcessId, debugEvent.dwThreadId, 
                          DBG_CONTINUE);
    }
    
    return FALSE;
}

// 转储内存
BOOL DumpProcess(HANDLE hProcess, LPVOID imageBase, const char* outputPath) {
    printf("[*] 转储进程内存...\n");
    
    // 读取DOS头
    IMAGE_DOS_HEADER dosHeader;
    ReadProcessMemory(hProcess, imageBase, &dosHeader, sizeof(dosHeader), NULL);
    
    // 读取NT头
    IMAGE_NT_HEADERS ntHeaders;
    ReadProcessMemory(hProcess, (LPBYTE)imageBase + dosHeader.e_lfanew, 
                     &ntHeaders, sizeof(ntHeaders), NULL);
    
    DWORD imageSize = ntHeaders.OptionalHeader.SizeOfImage;
    
    // 分配缓冲区
    LPBYTE buffer = (LPBYTE)VirtualAlloc(NULL, imageSize, 
                                         MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
    
    // 读取整个映像
    ReadProcessMemory(hProcess, imageBase, buffer, imageSize, NULL);
    
    // 修复区段(内存对齐 -> 文件对齐)
    PIMAGE_DOS_HEADER pDos = (PIMAGE_DOS_HEADER)buffer;
    PIMAGE_NT_HEADERS pNt = (PIMAGE_NT_HEADERS)(buffer + pDos->e_lfanew);
    PIMAGE_SECTION_HEADER sections = IMAGE_FIRST_SECTION(pNt);
    
    DWORD fileAlignment = pNt->OptionalHeader.FileAlignment;
    
    // 计算新文件大小
    DWORD newFileSize = pNt->OptionalHeader.SizeOfHeaders;
    for (WORD i = 0; i < pNt->FileHeader.NumberOfSections; i++) {
        sections[i].PointerToRawData = newFileSize;
        sections[i].SizeOfRawData = (sections[i].Misc.VirtualSize + fileAlignment - 1) & 
                                    ~(fileAlignment - 1);
        newFileSize += sections[i].SizeOfRawData;
    }
    
    // 分配新缓冲区
    LPBYTE fileBuffer = (LPBYTE)VirtualAlloc(NULL, newFileSize, 
                                              MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
    
    // 复制头部
    memcpy(fileBuffer, buffer, pNt->OptionalHeader.SizeOfHeaders);
    
    // 复制区段
    PIMAGE_NT_HEADERS pNewNt = (PIMAGE_NT_HEADERS)(fileBuffer + pDos->e_lfanew);
    PIMAGE_SECTION_HEADER newSections = IMAGE_FIRST_SECTION(pNewNt);
    
    for (WORD i = 0; i < pNewNt->FileHeader.NumberOfSections; i++) {
        memcpy(fileBuffer + newSections[i].PointerToRawData,
               buffer + newSections[i].VirtualAddress,
               newSections[i].Misc.VirtualSize);
    }
    
    // 保存文件
    HANDLE hFile = CreateFileA(outputPath, GENERIC_WRITE, 0, NULL, 
                               CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
    DWORD written;
    WriteFile(hFile, fileBuffer, newFileSize, &written, NULL);
    CloseHandle(hFile);
    
    VirtualFree(buffer, 0, MEM_RELEASE);
    VirtualFree(fileBuffer, 0, MEM_RELEASE);
    
    printf("[+] 转储完成: %s (%d bytes)\n", outputPath, newFileSize);
    
    return TRUE;
}

// 脱壳主函数
BOOL Unpack(const char* packedPath, const char* outputPath, DWORD oepRVA) {
    printf("[*] 开始脱壳: %s\n", packedPath);
    
    // 创建调试进程
    STARTUPINFOA si = { sizeof(si) };
    PROCESS_INFORMATION pi;
    
    if (!CreateProcessA(packedPath, NULL, NULL, NULL, FALSE, 
                       DEBUG_PROCESS | DEBUG_ONLY_THIS_PROCESS,
                       NULL, NULL, &si, &pi)) {
        printf("[-] 创建进程失败\n");
        return FALSE;
    }
    
    printf("[+] 进程创建: PID=%d\n", pi.dwProcessId);
    
    // 获取基址
    DEBUG_EVENT debugEvent;
    LPVOID imageBase = NULL;
    
    while (1) {
        WaitForDebugEvent(&debugEvent, INFINITE);
        
        if (debugEvent.dwDebugEventCode == CREATE_PROCESS_DEBUG_EVENT) {
            imageBase = debugEvent.u.CreateProcessInfo.lpBaseOfImage;
            printf("[+] 映像基址: 0x%p\n", imageBase);
            break;
        }
        
        ContinueDebugEvent(debugEvent.dwProcessId, debugEvent.dwThreadId, 
                          DBG_CONTINUE);
    }
    
    // 计算OEP地址
    LPVOID oepAddress = (LPBYTE)imageBase + oepRVA;
    printf("[*] OEP地址: 0x%p (RVA: 0x%08X)\n", oepAddress, oepRVA);
    
    // 等待到达OEP
    ContinueDebugEvent(debugEvent.dwProcessId, debugEvent.dwThreadId, DBG_CONTINUE);
    
    if (WaitForOEP(pi.hProcess, oepAddress)) {
        // 转储内存
        DumpProcess(pi.hProcess, imageBase, outputPath);
    }
    
    // 终止进程
    TerminateProcess(pi.hProcess, 0);
    CloseHandle(pi.hThread);
    CloseHandle(pi.hProcess);
    
    return TRUE;
}

int main(int argc, char* argv[]) {
    if (argc < 4) {
        printf("用法: %s <加壳程序> <输出文件> <OEP_RVA>\n", argv[0]);
        printf("示例: unpacker.exe packed.exe dumped.exe 1000\n");
        return 1;
    }
    
    DWORD oepRVA = strtoul(argv[3], NULL, 16);
    
    Unpack(argv[1], argv[2], oepRVA);
    
    return 0;
}

5.3、测试和验证

// 验证加壳效果
void TestPackedFile(const char* originalPath, const char* packedPath) {
    printf("\n===== 加壳效果测试 =====\n");
    
    // 比较文件大小
    WIN32_FILE_ATTRIBUTE_DATA origAttr, packAttr;
    GetFileAttributesExA(originalPath, GetFileExInfoStandard, &origAttr);
    GetFileAttributesExA(packedPath, GetFileExInfoStandard, &packAttr);
    
    printf("原始文件大小: %d bytes\n", origAttr.nFileSizeLow);
    printf("加壳文件大小: %d bytes\n", packAttr.nFileSizeLow);
    printf("体积变化: %+d bytes\n", 
           (int)packAttr.nFileSizeLow - (int)origAttr.nFileSizeLow);
    
    // 运行测试
    printf("\n[*] 运行加壳程序测试...\n");
    
    STARTUPINFOA si = { sizeof(si) };
    PROCESS_INFORMATION pi;
    
    if (CreateProcessA(packedPath, NULL, NULL, NULL, FALSE, 0, 
                       NULL, NULL, &si, &pi)) {
        
        DWORD exitCode;
        WaitForSingleObject(pi.hProcess, 5000);
        GetExitCodeProcess(pi.hProcess, &exitCode);
        
        if (exitCode == STILL_ACTIVE) {
            printf("[+] 程序正常运行中\n");
            TerminateProcess(pi.hProcess, 0);
        } else {
            printf("[+] 程序退出,代码: %d\n", exitCode);
        }
        
        CloseHandle(pi.hThread);
        CloseHandle(pi.hProcess);
    } else {
        printf("[-] 运行失败: %d\n", GetLastError());
    }
}

// 主程序
int main(int argc, char* argv[]) {
    printf("===========================================\n");
    printf("         Complete Packer System           \n");
    printf("===========================================\n\n");
    
    if (argc < 2) {
        printf("用法:\n");
        printf("  加壳: %s pack <目标> <壳> <输出> [密钥]\n", argv[0]);
        printf("  脱壳: %s unpack <加壳程序> <输出> <OEP>\n", argv[0]);
        printf("  测试: %s test <原始> <加壳>\n", argv[0]);
        return 1;
    }
    
    if (strcmp(argv[1], "pack") == 0 && argc >= 5) {
        PACKER_CONTEXT ctx;
        if (InitPacker(&ctx, argv[2], argv[3])) {
            strcpy(ctx.outputPath, argv[4]);
            ctx.encryptionType = ENCRYPT_XOR;
            ctx.encryptionKey = (argc > 5) ? strtoul(argv[5], NULL, 16) : 0x12345678;
            PackExecutable(&ctx);
            CleanupPacker(&ctx);
        }
    }
    else if (strcmp(argv[1], "unpack") == 0 && argc >= 5) {
        Unpack(argv[2], argv[3], strtoul(argv[4], NULL, 16));
    }
    else if (strcmp(argv[1], "test") == 0 && argc >= 4) {
        TestPackedFile(argv[2], argv[3]);
    }
    
    return 0;
}

6、课后作业

  1. 完善加壳系统

    • 添加压缩功能
    • 支持DLL加壳
    • 添加更多加密算法
  2. 增强反分析能力

    • 添加多种反调试
    • 添加反虚拟机
    • 添加代码混淆
  3. 开发脱壳工具

    • 自动查找OEP
    • 重建IAT
    • 修复各种PE问题