PE文件操作-末尾添加节

有时候为了某些原因,需要在PE末尾添加节,比如加壳,补丁等等,需要对PE文件进行扩展。
在末尾添加节是最简单的方式,只需要做如下修改:

  • 修改FILE_HEADER中的节数目字段
  • 修改OPTIONAL_HEADER中映像大小字段
  • 在节描述符数组中添加新描述符
  • 在文件末尾添加新节数据

首先找到文件最后一个节并计算出PE范围内的文件结尾,这里的末尾是指PE描述范围内的文件结尾,即最后一个节的结束:

    //找到文件末尾的节,计算出文件结束位置
    PIMAGE_SECTION_HEADER last_sec_hdr;
    get_section_entry_by_index(ctx, 0, &last_sec_hdr);
    for (int i = 1;i < file_hdr->NumberOfSections;i++)
    {
        PIMAGE_SECTION_HEADER sec_hdr;
        get_section_entry_by_index(ctx, i, &sec_hdr);
        if (sec_hdr->PointerToRawData > last_sec_hdr->PointerToRawData)
            last_sec_hdr = sec_hdr;
    }
    DWORD file_end = ALIGN_PAGE((last_sec_hdr->PointerToRawData + last_sec_hdr->SizeOfRawData), FileAlignment);

然后去初始化新节描述符:

    //创建新节描述并初始化
    IMAGE_SECTION_HEADER sec_hdr;
    memset(&sec_hdr, 0, sizeof(IMAGE_SECTION_HEADER));

    sec_hdr.Characteristics = IMAGE_SCN_MEM_READ | IMAGE_SCN_MEM_WRITE;
    sec_hdr.SizeOfRawData = size;
    sec_hdr.PointerToRawData = file_end;
    sec_hdr.Misc.VirtualSize = size;
    sec_hdr.VirtualAddress = ALIGN_PAGE((last_sec_hdr->VirtualAddress + last_sec_hdr->Misc.VirtualSize), SectionAlignment);
    memcpy(sec_hdr.Name, name, 8);

在某些PE文件中,可能有些PE描述范围外的数据,需要先将这些数据备份出来:

    //备份pe文件未定义的文件数据
    DWORD useless_size = GetFileSize(ctx->file, &ret) - file_end;
    PUCHAR useless_buf;
    if (useless_size > 0)
    {
        useless_buf = (PUCHAR)malloc(useless_size);
        if (useless_buf == NULL)
            return FALSE;

        ReadFile(ctx->file, useless_buf, useless_size, &ret, NULL);
        if (ret != useless_size)
            return FALSE;
    }

用0去在文件末尾填充新节:

    //在文件中填充新节
    DWORD need_size = ALIGN_PAGE(size, FileAlignment);
    PVOID new_sec = malloc(need_size);
    sec_hdr.SizeOfRawData = need_size;
    SetFilePointer(ctx->file, file_end, 0, FILE_BEGIN);
    WriteFile(ctx->file, new_sec, need_size, &ret, NULL);
    free(new_sec);
    if (ret != need_size)
        return FALSE;

再将刚刚备份的PE描述范围外的数据拷回去:

    //将pe文件定义外的数据拷会
    if (useless_size)
    {
        SetFilePointer(ctx->file, 0, 0, FILE_END);
        WriteFile(ctx->file, useless_buf, useless_size, &ret, NULL);
    }

最后将PE头的信息进行修正,由于这里用的内存映射文件,所以只要内存操作就能映射到文件中:

    //将新节描述写到最后一个节描述符的后面
    get_section_entry_by_index(ctx, file_hdr->NumberOfSections - 1, &last_sec_hdr);
    memcpy(last_sec_hdr + 1, &sec_hdr, sizeof(IMAGE_SECTION_HEADER));

    //修正文件头信息
    if (ctx->is_x64)
    {
        PIMAGE_OPTIONAL_HEADER64 opt_hdr;
        get_optional_header64(ctx, &opt_hdr);
        opt_hdr->SizeOfImage+= ALIGN_PAGE(size, SectionAlignment);
    }
    else
    {
        PIMAGE_OPTIONAL_HEADER32 opt_hdr;
        get_optional_header32(ctx, &opt_hdr);
        opt_hdr->SizeOfImage += ALIGN_PAGE(size, SectionAlignment);
    }

    file_hdr->NumberOfSections++;

其中用到对齐操作,即将地址对齐到页表边界:

#define ALIGN_PAGE(_addr_in_page_,_page_align_)                             \
    (((_addr_in_page_)+(_page_align_-1))&~((_page_align_-1)))              \
  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
.版本 2 .局部变量 段, 段, , "0" .局部变量 表目录, 表目录, , "0" .局部变量 现行位置 .局部变量 段数 .局部变量 段内存地址, , , "0" .局部变量 段占用文件大小 .局部变量 代码入口地址 .局部变量 表的数量, 整数型 .局部变量 计次, 整数型 .局部变量 计次2, 整数型 .局部变量 代码段地址, 整数型 .局部变量 文件对齐度 .局部变量 DOS数据, 字集 .局部变量 首选装载地址, 整数型 .局部变量 控制台程序, 逻辑型 现行位置 = 取字集数据 (文件, #整数型, #MZ头长度 - 3) + 1 ' DOS数据 = 取字集中间 (文件, #MZ头长度 + 1, 现行位置 - #MZ头长度 - 1) .如果真 (取字集数据 (文件, #整数型, 现行位置) ≠ #PE署名) 返回 () .如果真结束 现行位置 = 现行位置 + 2 段数 = 取字集数据 (文件, #短整数型, 现行位置) 重定义数组 (段, 假, 段数) 重定义数组 (段内存地址, 假, 段数) 现行位置 = 现行位置 + 32 代码入口地址 = 取字集数据 (文件, #整数型, 现行位置) 代码段地址 = 取字集数据 (文件, #整数型, 现行位置) 现行位置 = 现行位置 + 4 首选装载地址 = 取字集数据 (文件, #整数型, 现行位置) 现行位置 = 现行位置 + 4 文件对齐度 = 取字集数据 (文件, #整数型, 现行位置) 现行位置 = 现行位置 + 28 控制台程序 = 取字集中间 (文件, 现行位置, 2) = { 3, 0 } 现行位置 = 现行位置 + 24 表的数量 = 取字集数据 (文件, #整数型, 现行位置) 重定义数组 (表目录, 假, 表的数量) .计次循环首 (表的数量, 计次) 表目录 [计次].地址 = 取字集数据 (文件, #整数型, 现行位置) 表目录 [计次].大小 = 取字集数据 (文件, #整数型, 现行位置) .计次循环尾 () .计次循环首 (段数, 计次) ' 段 [计次].名称 = 取字集中间 (文件, 现行位置, 8) 现行位置 = 现行位置 + 8 段 [计次].内存大小 = 取字集数据 (文件, #整数型, 现行位置) 段内存地址 [计次] = 取字集数据 (文件, #整数型, 现行位置) 段占用文件大小 = 取字集数据 (文件, #整数型, 现行位置) 段 [计次].数据 = 字集删尾空 (取字集中间 (文件, 取字集数据 (文件, #整数型, 现行位置) + 1, 段占用文件大小)) 现行位置 = 现行位置 + 12 段 [计次].属性 = 取字集数据 (文件, #整数型, 现行位置) .计次循环尾 () .计次循环首 (表的数量, 计次) .变量循环首 (段数, 1, -1, 计次2) .如果真 (表目录 [计次].地址 ≥ 段内存地址 [计次2]) 表目录 [计次].段号 = 计次2 表目录 [计次].地址 = 表目录 [计次].地址 - 段内存地址 [计次2] 跳出循环 () .如果真结束 .变量循环尾 () .计次循环尾 () .计次循环首 (段数, 计次) .如果真 (段内存地址 [计次] = 代码段地址) 代码入口地址 = 代码入口地址 - 代码段地址 生成PE文件 (首选装载地址, 段, 计次, 代码入口地址, 文件对齐度, 表目录, DOS数据, 控制台程序) 跳出循环 () .如果真结束 .计次循环尾 ()
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值