Windows PE结构

8、阶段合集

1、课程目标

  1. 综合运用PE结构知识
  2. 实现完整的PE解析器
  3. 实现PE加载器和修改器
  4. 掌握PE在安全开发中的应用

2、名词解释

名词 全称 解释
PE Loader PE加载器 手动将PE映射到内存并执行
PE Packer PE加壳器 压缩/加密PE文件
PE Injector PE注入器 向目标进程注入PE
Reflective DLL 反射式DLL 自加载的DLL
RunPE - 镂空进程执行技术
PE Parser PE解析器 分析PE结构的工具

3、使用工具

  • Visual Studio 2022
  • CFF Explorer
  • x64dbg
  • IDA Pro
  • PE-bear

4、技术原理

4.1、PE结构综合图

+--------------------------------------------------+
|                    PE文件布局                      |
+--------------------------------------------------+
|  DOS Header (64 bytes)                           |
|    - e_magic: MZ                                 |
|    - e_lfanew: -> PE Signature                   |
+--------------------------------------------------+
|  DOS Stub (可变长度)                              |
|    - "This program cannot be run in DOS mode"    |
+--------------------------------------------------+
|  PE Signature (4 bytes): "PE\0\0"                |
+--------------------------------------------------+
|  File Header (20 bytes)                          |
|    - Machine, NumberOfSections                   |
|    - TimeDateStamp, SizeOfOptionalHeader         |
|    - Characteristics                             |
+--------------------------------------------------+
|  Optional Header (PE32: 224 / PE32+: 240 bytes)  |
|    - Magic, EntryPoint, ImageBase                |
|    - SectionAlignment, FileAlignment             |
|    - SizeOfImage, SizeOfHeaders                  |
|    - Subsystem, DllCharacteristics               |
|    - DataDirectory[16]                           |
|        [0] Export Table                          |
|        [1] Import Table                          |
|        [2] Resource Table                        |
|        [5] Base Relocation Table                 |
|        [12] IAT                                  |
+--------------------------------------------------+
|  Section Headers (每个40 bytes)                   |
|    - .text  (代码)                               |
|    - .data  (已初始化数据)                        |
|    - .rdata (只读数据)                           |
|    - .rsrc  (资源)                               |
|    - .reloc (重定位)                             |
+--------------------------------------------------+
|  Section Data                                    |
|    - 代码、数据、资源等                           |
+--------------------------------------------------+

5、代码实现

5.1、完整PE解析器

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

#pragma comment(lib, "imagehlp.lib")

// PE解析器上下文
typedef struct _PE_CONTEXT {
    LPBYTE      fileData;
    DWORD       fileSize;
    BOOL        is64;
    
    PIMAGE_DOS_HEADER       dosHeader;
    PIMAGE_NT_HEADERS32     ntHeaders32;
    PIMAGE_NT_HEADERS64     ntHeaders64;
    PIMAGE_FILE_HEADER      fileHeader;
    PIMAGE_SECTION_HEADER   sections;
    WORD                    sectionCount;
    
    // 通用访问
    ULONGLONG   imageBase;
    DWORD       entryPoint;
    DWORD       sizeOfImage;
    DWORD       sizeOfHeaders;
    DWORD       sectionAlignment;
    DWORD       fileAlignment;
    WORD        subsystem;
    WORD        dllCharacteristics;
    
    // 数据目录
    PIMAGE_DATA_DIRECTORY   dataDirectories;
    DWORD                   dataDirectoryCount;
} PE_CONTEXT, *PPE_CONTEXT;

// 初始化PE上下文
BOOL InitPEContext(PPE_CONTEXT ctx, LPBYTE fileData, DWORD fileSize) {
    ZeroMemory(ctx, sizeof(PE_CONTEXT));
    ctx->fileData = fileData;
    ctx->fileSize = fileSize;
    
    // 验证DOS头
    ctx->dosHeader = (PIMAGE_DOS_HEADER)fileData;
    if (ctx->dosHeader->e_magic != IMAGE_DOS_SIGNATURE) {
        printf("[-] 无效的DOS签名\n");
        return FALSE;
    }
    
    // 验证PE签名
    PIMAGE_NT_HEADERS ntHeaders = (PIMAGE_NT_HEADERS)(
        fileData + ctx->dosHeader->e_lfanew
    );
    
    if (ntHeaders->Signature != IMAGE_NT_SIGNATURE) {
        printf("[-] 无效的PE签名\n");
        return FALSE;
    }
    
    ctx->fileHeader = &ntHeaders->FileHeader;
    ctx->sectionCount = ctx->fileHeader->NumberOfSections;
    
    // 判断位数
    ctx->is64 = (ntHeaders->OptionalHeader.Magic == IMAGE_NT_OPTIONAL_HDR64_MAGIC);
    
    if (ctx->is64) {
        ctx->ntHeaders64 = (PIMAGE_NT_HEADERS64)ntHeaders;
        PIMAGE_OPTIONAL_HEADER64 opt = &ctx->ntHeaders64->OptionalHeader;
        
        ctx->imageBase = opt->ImageBase;
        ctx->entryPoint = opt->AddressOfEntryPoint;
        ctx->sizeOfImage = opt->SizeOfImage;
        ctx->sizeOfHeaders = opt->SizeOfHeaders;
        ctx->sectionAlignment = opt->SectionAlignment;
        ctx->fileAlignment = opt->FileAlignment;
        ctx->subsystem = opt->Subsystem;
        ctx->dllCharacteristics = opt->DllCharacteristics;
        ctx->dataDirectories = opt->DataDirectory;
        ctx->dataDirectoryCount = opt->NumberOfRvaAndSizes;
        
        ctx->sections = IMAGE_FIRST_SECTION(ctx->ntHeaders64);
    } else {
        ctx->ntHeaders32 = (PIMAGE_NT_HEADERS32)ntHeaders;
        PIMAGE_OPTIONAL_HEADER32 opt = &ctx->ntHeaders32->OptionalHeader;
        
        ctx->imageBase = opt->ImageBase;
        ctx->entryPoint = opt->AddressOfEntryPoint;
        ctx->sizeOfImage = opt->SizeOfImage;
        ctx->sizeOfHeaders = opt->SizeOfHeaders;
        ctx->sectionAlignment = opt->SectionAlignment;
        ctx->fileAlignment = opt->FileAlignment;
        ctx->subsystem = opt->Subsystem;
        ctx->dllCharacteristics = opt->DllCharacteristics;
        ctx->dataDirectories = opt->DataDirectory;
        ctx->dataDirectoryCount = opt->NumberOfRvaAndSizes;
        
        ctx->sections = IMAGE_FIRST_SECTION(ctx->ntHeaders32);
    }
    
    return TRUE;
}

// RVA转FOA
DWORD PERvaToFoa(PPE_CONTEXT ctx, DWORD rva) {
    for (WORD i = 0; i < ctx->sectionCount; i++) {
        DWORD start = ctx->sections[i].VirtualAddress;
        DWORD end = start + ctx->sections[i].Misc.VirtualSize;
        
        if (rva >= start && rva < end) {
            return ctx->sections[i].PointerToRawData + (rva - start);
        }
    }
    return rva;
}

// 获取数据指针
LPVOID PEGetDataByRva(PPE_CONTEXT ctx, DWORD rva) {
    if (rva == 0) return NULL;
    DWORD foa = PERvaToFoa(ctx, rva);
    return ctx->fileData + foa;
}

// 打印完整PE信息
void PrintFullPEInfo(PPE_CONTEXT ctx) {
    printf("\n");
    printf("================================================================================\n");
    printf("                           PE File Analysis Report\n");
    printf("================================================================================\n");
    
    // 基本信息
    printf("\n[基本信息]\n");
    printf("  文件大小:       %d bytes (0x%X)\n", ctx->fileSize, ctx->fileSize);
    printf("  PE类型:         %s\n", ctx->is64 ? "PE32+ (64-bit)" : "PE32 (32-bit)");
    printf("  映像基址:       0x%llX\n", ctx->imageBase);
    printf("  入口点:         0x%08X (RVA)\n", ctx->entryPoint);
    printf("  映像大小:       0x%08X (%d KB)\n", ctx->sizeOfImage, ctx->sizeOfImage / 1024);
    printf("  头部大小:       0x%08X\n", ctx->sizeOfHeaders);
    printf("  区段对齐:       0x%08X (%d bytes)\n", ctx->sectionAlignment, ctx->sectionAlignment);
    printf("  文件对齐:       0x%08X (%d bytes)\n", ctx->fileAlignment, ctx->fileAlignment);
    
    // 安全特性
    printf("\n[安全特性]\n");
    WORD dll = ctx->dllCharacteristics;
    printf("  ASLR:           %s\n", (dll & IMAGE_DLLCHARACTERISTICS_DYNAMIC_BASE) ? "启用" : "禁用");
    printf("  DEP/NX:         %s\n", (dll & IMAGE_DLLCHARACTERISTICS_NX_COMPAT) ? "启用" : "禁用");
    printf("  CFG:            %s\n", (dll & IMAGE_DLLCHARACTERISTICS_GUARD_CF) ? "启用" : "禁用");
    printf("  高熵ASLR:       %s\n", (dll & IMAGE_DLLCHARACTERISTICS_HIGH_ENTROPY_VA) ? "启用" : "禁用");
    printf("  SEH:            %s\n", (dll & IMAGE_DLLCHARACTERISTICS_NO_SEH) ? "禁用" : "启用");
    
    // 区段信息
    printf("\n[区段信息] (%d个区段)\n", ctx->sectionCount);
    printf("  %-8s %-10s %-10s %-10s %-10s %-10s\n",
           "Name", "VirtAddr", "VirtSize", "RawAddr", "RawSize", "Flags");
    printf("  %s\n", "------------------------------------------------------------");
    
    for (WORD i = 0; i < ctx->sectionCount; i++) {
        char name[9] = {0};
        memcpy(name, ctx->sections[i].Name, 8);
        
        printf("  %-8s 0x%08X 0x%08X 0x%08X 0x%08X 0x%08X\n",
               name,
               ctx->sections[i].VirtualAddress,
               ctx->sections[i].Misc.VirtualSize,
               ctx->sections[i].PointerToRawData,
               ctx->sections[i].SizeOfRawData,
               ctx->sections[i].Characteristics);
    }
    
    // 数据目录
    printf("\n[数据目录]\n");
    const char* dirNames[] = {
        "Export", "Import", "Resource", "Exception", "Security",
        "BaseReloc", "Debug", "Architecture", "GlobalPtr", "TLS",
        "LoadConfig", "BoundImport", "IAT", "DelayImport", "CLR", "Reserved"
    };
    
    for (DWORD i = 0; i < ctx->dataDirectoryCount && i < 16; i++) {
        if (ctx->dataDirectories[i].VirtualAddress != 0) {
            printf("  [%2d] %-12s RVA: 0x%08X  Size: 0x%08X\n",
                   i, dirNames[i],
                   ctx->dataDirectories[i].VirtualAddress,
                   ctx->dataDirectories[i].Size);
        }
    }
    
    printf("\n================================================================================\n");
}

5.2、完整PE加载器

// PE加载器
typedef struct _LOADED_PE {
    LPBYTE      imageBase;
    DWORD       imageSize;
    DWORD       entryPoint;
    BOOL        is64;
    BOOL        isDll;
} LOADED_PE, *PLOADED_PE;

// 完整的PE加载器
BOOL LoadPEIntoMemory(LPBYTE peData, DWORD peSize, PLOADED_PE loaded) {
    PE_CONTEXT ctx;
    if (!InitPEContext(&ctx, peData, peSize)) {
        return FALSE;
    }
    
    // 分配内存
    loaded->imageBase = (LPBYTE)VirtualAlloc(
        NULL,
        ctx.sizeOfImage,
        MEM_COMMIT | MEM_RESERVE,
        PAGE_EXECUTE_READWRITE
    );
    
    if (!loaded->imageBase) {
        printf("[-] 无法分配内存: %d bytes\n", ctx.sizeOfImage);
        return FALSE;
    }
    
    loaded->imageSize = ctx.sizeOfImage;
    loaded->entryPoint = ctx.entryPoint;
    loaded->is64 = ctx.is64;
    loaded->isDll = (ctx.fileHeader->Characteristics & IMAGE_FILE_DLL) != 0;
    
    printf("[+] 映像基址: 0x%p\n", loaded->imageBase);
    
    // 复制头部
    memcpy(loaded->imageBase, peData, ctx.sizeOfHeaders);
    
    // 复制区段
    for (WORD i = 0; i < ctx.sectionCount; i++) {
        if (ctx.sections[i].SizeOfRawData > 0) {
            memcpy(
                loaded->imageBase + ctx.sections[i].VirtualAddress,
                peData + ctx.sections[i].PointerToRawData,
                ctx.sections[i].SizeOfRawData
            );
        }
        
        char name[9] = {0};
        memcpy(name, ctx.sections[i].Name, 8);
        printf("[+] 复制区段: %-8s -> 0x%p\n", 
               name, loaded->imageBase + ctx.sections[i].VirtualAddress);
    }
    
    // 执行重定位
    LONGLONG delta = (LONGLONG)loaded->imageBase - (LONGLONG)ctx.imageBase;
    if (delta != 0) {
        printf("[*] 执行重定位 (Delta: 0x%llX)\n", delta);
        
        PIMAGE_DATA_DIRECTORY relocDir = &ctx.dataDirectories[IMAGE_DIRECTORY_ENTRY_BASERELOC];
        if (relocDir->VirtualAddress == 0) {
            printf("[-] 没有重定位表,无法加载到其他地址\n");
            VirtualFree(loaded->imageBase, 0, MEM_RELEASE);
            return FALSE;
        }
        
        PIMAGE_BASE_RELOCATION reloc = (PIMAGE_BASE_RELOCATION)(
            loaded->imageBase + relocDir->VirtualAddress
        );
        
        int fixupCount = 0;
        
        while (reloc->VirtualAddress != 0) {
            DWORD numEntries = (reloc->SizeOfBlock - sizeof(IMAGE_BASE_RELOCATION)) / sizeof(WORD);
            WORD* entries = (WORD*)(reloc + 1);
            
            for (DWORD j = 0; j < numEntries; j++) {
                WORD type = entries[j] >> 12;
                WORD offset = entries[j] & 0x0FFF;
                
                LPBYTE target = loaded->imageBase + reloc->VirtualAddress + offset;
                
                if (type == IMAGE_REL_BASED_HIGHLOW) {
                    *(DWORD*)target += (DWORD)delta;
                    fixupCount++;
                } else if (type == IMAGE_REL_BASED_DIR64) {
                    *(ULONGLONG*)target += delta;
                    fixupCount++;
                }
            }
            
            reloc = (PIMAGE_BASE_RELOCATION)((LPBYTE)reloc + reloc->SizeOfBlock);
        }
        
        printf("[+] 重定位完成: %d 处修复\n", fixupCount);
    }
    
    // 解析导入表
    PIMAGE_DATA_DIRECTORY importDir = &ctx.dataDirectories[IMAGE_DIRECTORY_ENTRY_IMPORT];
    if (importDir->VirtualAddress != 0) {
        printf("[*] 解析导入表\n");
        
        PIMAGE_IMPORT_DESCRIPTOR importDesc = (PIMAGE_IMPORT_DESCRIPTOR)(
            loaded->imageBase + importDir->VirtualAddress
        );
        
        while (importDesc->Name != 0) {
            char* dllName = (char*)(loaded->imageBase + importDesc->Name);
            
            HMODULE hDll = LoadLibraryA(dllName);
            if (!hDll) {
                printf("[-] 无法加载: %s\n", dllName);
                VirtualFree(loaded->imageBase, 0, MEM_RELEASE);
                return FALSE;
            }
            
            printf("[+] 加载DLL: %s -> 0x%p\n", dllName, hDll);
            
            // 填充IAT
            PIMAGE_THUNK_DATA originalThunk = (PIMAGE_THUNK_DATA)(
                loaded->imageBase + (importDesc->OriginalFirstThunk ?
                                     importDesc->OriginalFirstThunk :
                                     importDesc->FirstThunk)
            );
            PIMAGE_THUNK_DATA firstThunk = (PIMAGE_THUNK_DATA)(
                loaded->imageBase + importDesc->FirstThunk
            );
            
            while (originalThunk->u1.AddressOfData != 0) {
                FARPROC func;
                
                if (IMAGE_SNAP_BY_ORDINAL(originalThunk->u1.Ordinal)) {
                    func = GetProcAddress(hDll, 
                            MAKEINTRESOURCEA(IMAGE_ORDINAL(originalThunk->u1.Ordinal)));
                } else {
                    PIMAGE_IMPORT_BY_NAME hint = (PIMAGE_IMPORT_BY_NAME)(
                        loaded->imageBase + originalThunk->u1.AddressOfData
                    );
                    func = GetProcAddress(hDll, hint->Name);
                }
                
                if (!func) {
                    printf("[-] 无法获取函数地址\n");
                }
                
                firstThunk->u1.Function = (ULONGLONG)func;
                
                originalThunk++;
                firstThunk++;
            }
            
            importDesc++;
        }
    }
    
    // 设置区段保护属性
    for (WORD i = 0; i < ctx.sectionCount; i++) {
        DWORD protect = PAGE_READONLY;
        DWORD chars = ctx.sections[i].Characteristics;
        
        if (chars & IMAGE_SCN_MEM_EXECUTE) {
            if (chars & IMAGE_SCN_MEM_WRITE) {
                protect = PAGE_EXECUTE_READWRITE;
            } else {
                protect = PAGE_EXECUTE_READ;
            }
        } else if (chars & IMAGE_SCN_MEM_WRITE) {
            protect = PAGE_READWRITE;
        }
        
        DWORD oldProtect;
        VirtualProtect(
            loaded->imageBase + ctx.sections[i].VirtualAddress,
            ctx.sections[i].Misc.VirtualSize,
            protect,
            &oldProtect
        );
    }
    
    printf("[+] PE加载完成\n");
    
    return TRUE;
}

// 执行加载的PE
BOOL ExecuteLoadedPE(PLOADED_PE loaded) {
    if (loaded->entryPoint == 0) {
        printf("[-] 没有入口点\n");
        return FALSE;
    }
    
    LPBYTE entry = loaded->imageBase + loaded->entryPoint;
    printf("[*] 执行入口点: 0x%p\n", entry);
    
    if (loaded->isDll) {
        typedef BOOL (WINAPI* DllMain_t)(HINSTANCE, DWORD, LPVOID);
        DllMain_t DllMain = (DllMain_t)entry;
        return DllMain((HINSTANCE)loaded->imageBase, DLL_PROCESS_ATTACH, NULL);
    } else {
        typedef int (*EntryPoint_t)(void);
        EntryPoint_t EntryPoint = (EntryPoint_t)entry;
        return EntryPoint();
    }
}

5.3、PE修改器

// 添加区段
BOOL PEAddSection(PPE_CONTEXT ctx, DWORD* newFileSize,
                  const char* name, DWORD size, DWORD characteristics) {
    // 检查空间
    DWORD headersEnd = ctx->sizeOfHeaders;
    DWORD newSectionHeaderOffset = (DWORD)((LPBYTE)&ctx->sections[ctx->sectionCount] - ctx->fileData);
    
    if (newSectionHeaderOffset + sizeof(IMAGE_SECTION_HEADER) > headersEnd) {
        printf("[-] 没有空间添加区段头\n");
        return FALSE;
    }
    
    // 获取最后一个区段
    PIMAGE_SECTION_HEADER lastSection = &ctx->sections[ctx->sectionCount - 1];
    PIMAGE_SECTION_HEADER newSection = &ctx->sections[ctx->sectionCount];
    
    // 计算新区段位置
    DWORD alignedVA = (lastSection->VirtualAddress + lastSection->Misc.VirtualSize + 
                       ctx->sectionAlignment - 1) & ~(ctx->sectionAlignment - 1);
    DWORD alignedRaw = (lastSection->PointerToRawData + lastSection->SizeOfRawData +
                        ctx->fileAlignment - 1) & ~(ctx->fileAlignment - 1);
    DWORD alignedSize = (size + ctx->fileAlignment - 1) & ~(ctx->fileAlignment - 1);
    
    // 填充新区段头
    ZeroMemory(newSection, sizeof(IMAGE_SECTION_HEADER));
    strncpy((char*)newSection->Name, name, 8);
    newSection->Misc.VirtualSize = size;
    newSection->VirtualAddress = alignedVA;
    newSection->SizeOfRawData = alignedSize;
    newSection->PointerToRawData = alignedRaw;
    newSection->Characteristics = characteristics;
    
    // 更新PE头
    ctx->fileHeader->NumberOfSections++;
    
    DWORD newImageSize = (alignedVA + size + ctx->sectionAlignment - 1) & 
                         ~(ctx->sectionAlignment - 1);
    
    if (ctx->is64) {
        ctx->ntHeaders64->OptionalHeader.SizeOfImage = newImageSize;
    } else {
        ctx->ntHeaders32->OptionalHeader.SizeOfImage = newImageSize;
    }
    
    *newFileSize = alignedRaw + alignedSize;
    
    printf("[+] 添加区段: %s\n", name);
    printf("    VirtualAddress:  0x%08X\n", alignedVA);
    printf("    PointerToRawData: 0x%08X\n", alignedRaw);
    printf("    Size: 0x%08X\n", alignedSize);
    
    return TRUE;
}

// 修改入口点
void PESetEntryPoint(PPE_CONTEXT ctx, DWORD newEntryPoint) {
    if (ctx->is64) {
        ctx->ntHeaders64->OptionalHeader.AddressOfEntryPoint = newEntryPoint;
    } else {
        ctx->ntHeaders32->OptionalHeader.AddressOfEntryPoint = newEntryPoint;
    }
    printf("[+] 入口点修改为: 0x%08X\n", newEntryPoint);
}

// 禁用ASLR
void PEDisableASLR(PPE_CONTEXT ctx) {
    if (ctx->is64) {
        ctx->ntHeaders64->OptionalHeader.DllCharacteristics &= 
            ~IMAGE_DLLCHARACTERISTICS_DYNAMIC_BASE;
    } else {
        ctx->ntHeaders32->OptionalHeader.DllCharacteristics &= 
            ~IMAGE_DLLCHARACTERISTICS_DYNAMIC_BASE;
    }
    printf("[+] ASLR已禁用\n");
}

// 清除调试信息
void PEClearDebugInfo(PPE_CONTEXT ctx) {
    ctx->dataDirectories[IMAGE_DIRECTORY_ENTRY_DEBUG].VirtualAddress = 0;
    ctx->dataDirectories[IMAGE_DIRECTORY_ENTRY_DEBUG].Size = 0;
    printf("[+] 调试信息已清除\n");
}

5.4、PE分析工具主程序

int main(int argc, char* argv[]) {
    if (argc < 2) {
        printf("PE Analyzer v1.0\n");
        printf("用法: %s <PE文件路径> [选项]\n", argv[0]);
        printf("选项:\n");
        printf("  -i    显示完整信息\n");
        printf("  -e    显示导出表\n");
        printf("  -m    显示导入表\n");
        printf("  -r    显示资源表\n");
        printf("  -l    显示重定位表\n");
        printf("  -x    执行PE文件\n");
        return 1;
    }
    
    // 加载PE文件
    HANDLE hFile = CreateFileA(argv[1], GENERIC_READ, FILE_SHARE_READ,
                               NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
    if (hFile == INVALID_HANDLE_VALUE) {
        printf("[-] 无法打开文件: %s\n", argv[1]);
        return 1;
    }
    
    DWORD fileSize = GetFileSize(hFile, NULL);
    LPBYTE fileData = (LPBYTE)VirtualAlloc(NULL, fileSize, 
                                           MEM_COMMIT | MEM_RESERVE, 
                                           PAGE_READWRITE);
    
    DWORD bytesRead;
    ReadFile(hFile, fileData, fileSize, &bytesRead, NULL);
    CloseHandle(hFile);
    
    // 初始化PE上下文
    PE_CONTEXT ctx;
    if (!InitPEContext(&ctx, fileData, fileSize)) {
        VirtualFree(fileData, 0, MEM_RELEASE);
        return 1;
    }
    
    // 处理选项
    BOOL showInfo = TRUE;
    
    for (int i = 2; i < argc; i++) {
        if (strcmp(argv[i], "-i") == 0) {
            PrintFullPEInfo(&ctx);
            showInfo = FALSE;
        } else if (strcmp(argv[i], "-e") == 0) {
            ParseExportTable(fileData);
            showInfo = FALSE;
        } else if (strcmp(argv[i], "-m") == 0) {
            ParseImportTable(fileData);
            showInfo = FALSE;
        } else if (strcmp(argv[i], "-r") == 0) {
            ParseResourceTable(fileData);
            showInfo = FALSE;
        } else if (strcmp(argv[i], "-l") == 0) {
            ParseRelocationTable(fileData);
            showInfo = FALSE;
        } else if (strcmp(argv[i], "-x") == 0) {
            LOADED_PE loaded;
            if (LoadPEIntoMemory(fileData, fileSize, &loaded)) {
                ExecuteLoadedPE(&loaded);
            }
            showInfo = FALSE;
        }
    }
    
    if (showInfo) {
        PrintFullPEInfo(&ctx);
    }
    
    VirtualFree(fileData, 0, MEM_RELEASE);
    return 0;
}

6、课后作业

  1. 编写完整PE分析工具

    • 支持所有数据目录解析
    • 支持导出到JSON/XML
    • 支持PE对比功能
  2. 实现反射式DLL加载器

    • 无文件落地加载DLL
    • 自动处理导入和重定位
    • 支持卸载功能
  3. 研究PE保护技术

    • 分析常见加壳方式
    • 研究反调试技术
    • 了解代码混淆方法