深入分析PE结构(三)
字数 1357 2025-08-07 08:22:20

PE结构深入分析:新增节与扩大节技术详解

0x0 新增节技术

前言

手动新增一个节表和节,保证修改后的程序能正确执行。

PE结构整体过程

  • SizeOfHeaders:DOS头 + DOS stub(垃圾数据) + NT头(PE标记 + 标准PE头 + 可选PE头) + 已存在节表 --> 对齐之后的大小
  • 重要原则:SizeOfHeaders不能随便改变,代价太大

新增节步骤

  1. 新增一个节
  2. 新增一个节表(40个字节)

判断条件:确保新增节表后有足够空间(至少80字节)

计算公式:

SizeOfHeader - (DOS + DOS stub + PE标记 + 标准PE头 + 可选PE头 + 已存在节表) >= 2个节表的大小(80字节)
  1. 需要修改的数据
    1. 添加一个新节(可以复制现有节)
    2. 在新增节后面填充一个节大小的000
    3. 修改标准PE头中的节数量(NumberOfSections参数)
    4. 修改内存中整个PE文件的映射尺寸(可选PE头中sizeOfImage参数)
    5. 在原有数据的最后,新增一个节的数据(内存对齐的整数倍)
    6. 修正新增节表的属性

手动分析-1:常规情况

  1. 复制要操作的exe作为参考
  2. 判断空间是否足够
  3. 计算现有节表数量(每个节表40字节)
  4. 在空白区域添加新节表(复制.text节表)
  5. 修改NumberOfSections参数
  6. 修改sizeOfImage参数(在可选PE头后56字节)
  7. 填充新节(1000h字节=4096字节)
  8. 修改新增节表的属性:
    • Name(8字节)
    • VirtualSize(内存中大小,对齐前长度)
    • VirtualAddress(内存中偏移/RVA)
    • SizeOfRawData(文件中大小,对齐后长度)
    • PointerToRawData(文件中偏移)
    • Characteristics(节属性)

手动分析-2:极端情况(节表后无足够空间)

当节表后跟了一堆有用数据时:

  1. 不能移动或删除这些数据
  2. 解决方案:利用PE结构中间的垃圾数据
    • 将PE标记上移
    • 修改DOS头中的e_lfanew参数指向新位置
    • 原位置数据补0

核心代码实现

DWORD CopyFileBufferToNewImageBuffer(IN LPVOID pFileBuffer, IN size_t fileSize, OUT LPVOID* pNewImageBuffer)
{
    // 检查空间是否足够
    okAddSections = (DWORD)(pOptionHeader->SizeOfHeaders - (pDosHeader->e_lfanew + 0x04 + 
                  sizeof(PIMAGE_FILE_HEADER) + pPEHeder->SizeOfOptionalHeader + 
                  sizeof(PIMAGE_SECTION_HEADER) * pPEHeder->NumberOfSections));
    
    if (okAddSections < 2 * sizeof(PIMAGE_SECTION_HEADER)) {
        printf("这个exe文件头不剩余空间不够\r\n");
        free(pTempNewImageBuffer);
        return 0;
    }
    
    // 修改节数量
    *pNumberOfSection = pPEHeder->NumberOfSections + 1;
    
    // 修改SizeOfImage
    *pSizeOfImage = pOptionHeader->SizeOfImage + 0x1000;
    
    // 设置新节表属性
    memcpy(pSecName, ".newSec", 8);
    *pSecMisc = 0x1000;
    *pSecVirtualAddress = pLastSectionHeader->VirtualAddress + add_size;
    *pSecSizeOfRawData = 0x1000;
    *pSecPointToRawData = pLastSectionHeader->PointerToRawData + pLastSectionHeader->SizeOfRawData;
    *pSecCharacteristics = 0xFFFFFFFF;
}

0x2 扩大节技术

前言

当空白区域无法满足新增节要求时,需要扩大现有节。

整体流程

  1. 拉伸到内存
  2. 分配新空间:SizeOfImage + Ex(要扩大的大小)
  3. 修改最后一个节的SizeOfRawData和VirtualSize:
    • 取两者中较大的值N
    • 设置SizeOfRawData = VirtualSize = N + Ex
  4. 修改SizeOfImage大小:SizeOfImage = SizeOfImage + Ex

核心代码实现

DWORD FileBufferToModifyImageBuffer(IN LPVOID pFileBuffer, OUT LPVOID* pNewImageBuffer)
{
    // 扩大VirtualSize
    *pVirtualSize = AlignLength(*pVirtualSize, pOptionHeader->SectionAlignment) + 0x1000;
    
    // 扩大SizeOfRawData
    *pSizeOfRawData = AlignLength(*pSizeOfRawData, pOptionHeader->SectionAlignment) + 0x1000;
    
    // 修改SizeOfImage
    *pSizeOfImage += 0x1000;
}

对齐计算函数

DWORD AlignLength(DWORD Actuall_size, DWORD Align_size)
{
    if (Actuall_size % Align_size == 0) {
        return Actuall_size;
    } else {
        DWORD n = Actuall_size / Align_size;
        return Align_size * (n + 1);
    }
}

关键点总结

  1. 新增节表空间计算:必须确保有至少80字节连续空间
  2. 节表属性设置
    • VirtualAddress = 上一个节的VirtualAddress + max(上一个节的VirtualSize, SizeOfRawData)
    • PointerToRawData = 上一个节的PointerToRawData + 上一个节的SizeOfRawData
  3. 对齐处理:所有地址和大小必须按照SectionAlignment和FileAlignment对齐
  4. 极端情况处理:当节表后无空间时,可调整PE标记位置
  5. 扩大节技术:当无法新增节时,可扩大最后一个节的空间

通过以上技术,可以灵活地修改PE文件结构,为后续的代码注入、数据添加等操作提供空间基础。

PE结构深入分析:新增节与扩大节技术详解 0x0 新增节技术 前言 手动新增一个节表和节,保证修改后的程序能正确执行。 PE结构整体过程 SizeOfHeaders :DOS头 + DOS stub(垃圾数据) + NT头(PE标记 + 标准PE头 + 可选PE头) + 已存在节表 --> 对齐之后的大小 重要原则 :SizeOfHeaders不能随便改变,代价太大 新增节步骤 新增一个节 新增一个节表(40个字节) 判断条件 :确保新增节表后有足够空间(至少80字节) 计算公式: 需要修改的数据 : 添加一个新节(可以复制现有节) 在新增节后面填充一个节大小的000 修改标准PE头中的节数量(NumberOfSections参数) 修改内存中整个PE文件的映射尺寸(可选PE头中sizeOfImage参数) 在原有数据的最后,新增一个节的数据(内存对齐的整数倍) 修正新增节表的属性 手动分析-1:常规情况 复制要操作的exe作为参考 判断空间是否足够 计算现有节表数量(每个节表40字节) 在空白区域添加新节表(复制.text节表) 修改NumberOfSections参数 修改sizeOfImage参数(在可选PE头后56字节) 填充新节(1000h字节=4096字节) 修改新增节表的属性: Name(8字节) VirtualSize(内存中大小,对齐前长度) VirtualAddress(内存中偏移/RVA) SizeOfRawData(文件中大小,对齐后长度) PointerToRawData(文件中偏移) Characteristics(节属性) 手动分析-2:极端情况(节表后无足够空间) 当节表后跟了一堆有用数据时: 不能移动或删除这些数据 解决方案:利用PE结构中间的垃圾数据 将PE标记上移 修改DOS头中的e_ lfanew参数指向新位置 原位置数据补0 核心代码实现 0x2 扩大节技术 前言 当空白区域无法满足新增节要求时,需要扩大现有节。 整体流程 拉伸到内存 分配新空间:SizeOfImage + Ex(要扩大的大小) 修改最后一个节的SizeOfRawData和VirtualSize: 取两者中较大的值N 设置SizeOfRawData = VirtualSize = N + Ex 修改SizeOfImage大小:SizeOfImage = SizeOfImage + Ex 核心代码实现 对齐计算函数 关键点总结 新增节表空间计算 :必须确保有至少80字节连续空间 节表属性设置 : VirtualAddress = 上一个节的VirtualAddress + max(上一个节的VirtualSize, SizeOfRawData) PointerToRawData = 上一个节的PointerToRawData + 上一个节的SizeOfRawData 对齐处理 :所有地址和大小必须按照SectionAlignment和FileAlignment对齐 极端情况处理 :当节表后无空间时,可调整PE标记位置 扩大节技术 :当无法新增节时,可扩大最后一个节的空间 通过以上技术,可以灵活地修改PE文件结构,为后续的代码注入、数据添加等操作提供空间基础。