12558网页游戏私服论坛

 找回密码
 立即注册
游戏开服表 申请开服
游戏名称 游戏描述 开服状态 游戏福利 运营商 游戏链接
攻城掠地-仿官 全新玩法,觉醒武将,觉醒技能 每周新区 经典复古版本,长久稳定 进入游戏
巅峰新版攻 攻城掠地公益服 攻城掠地SF 新兵种、新武将(兵种) 进入游戏
攻城掠地公 散人玩家的天堂 新开 进入游戏
改版攻城掠 上线即可国战PK 稳定新区 全新改版,功能强大 进入游戏
少年江山 高福利高爆率 刚开一秒 江湖水落潜蛟龙 进入游戏
太古封魔录 开服送10亿钻石 福利多多 不用充钱也可升级 进入游戏
神魔之道 签到送元宝 稳定开新区 送豪华签到奖励 进入游戏
神奇三国 统帅三军,招揽名将 免费玩新区 激情国战,征战四方 进入游戏
龙符 三日豪礼领到爽 天天开新区 助你征战无双 进入游戏
王者之师 免费领豪华奖励 免费玩新区 6元送6888元宝 进入游戏
三国霸业 战车-珍宝-觉醒-攻城掠地SF-全新玩法 免费玩新区 攻城掠地私服 进入游戏
手游私服盒子 各类免费游戏 0.1折送海量资源 各类手游私服 进入游戏
皇家MU2 《奇迹 2:传奇》韩国网禅公司《奇迹》正统续作。 3D锁视角Mmrpg 暗黑3+传奇+流放之路+奇迹 进入游戏
查看: 328|回复: 0

PE文件笔记十二 修正内存对齐

[复制链接]
发表于 2021-7-18 18:24:37 | 显示全部楼层 |阅读模式
继续PE系列笔记的更新
PE别的笔记索引可前往:
PE文件笔记一 PE介绍
在前面PE文件笔记十 扩大节中,需要修正节表成员;修正节表成员时要把Misc(现实大小)和 SizeOfRawData(文件对齐后的大小)修正为内存对齐后的大小
但在扩大节中,只针对最后一个节举行修正,没有影响到别的的成员,接下来学习将所有节表都修正为内存对齐
此篇笔记为学习节操作之修正内存对齐
修正内存对齐

为什么要修正内存对齐

修正内存对齐 使得 节文件对齐后的大小和内存对齐后的大小同等,方便后续归并节
修正内存对齐涉及的结构体成员

涉及的节表成员寄义Misc节的现实大小SizeOfRawData节在文件中对齐后的尺寸PointerToRawData节区在文件中的偏移修正内存对齐的流程


  • 计算节内存对齐后的大小
  • 计算差值 = 节内存对齐后的大小 - 节文件对齐后的大小
  • 计算节在文件中的末尾位置 = 节在文件中的偏移 + 节文件对齐后的大小
  • 在节的文件中的末尾位置后填充新空间,新空间的大小为 前面计算的差值
  • 修正Misc和SizeOfRawData为节内存对齐后的大小
  • 在该节后面的节在文件中的偏移增加差值
按流程修正内存对齐

此次依旧以先前的EverEdit.exe为例举行修正内存对齐的演示,这次选择修正倒数第二个节
节信息


这里只关注最后两个节:
节名称MiscSizeOfRawDataPointerToRawData.rsrc0x620080x622000x1dfa00.reloc0x1643a0x166000x241c00计算节内存对齐后的大小

计算节内存对齐后的大小在先前的笔记PE文件笔记十 扩大节中已经说明过,这里不再赘述,直接计算
节内存对齐后的大小 = ( max{Misc,SizeOfRawData} ÷ SectionAlignment)向上取整 × SectionAlignment
即节内存对齐后的大小 = ( max{0x62008,0x62200}÷0x1000)向上取整 × 0x1000
即节内存对齐后的大小 = (0x62200 ÷ 0x1000)向上取整 × 0x1000 = 0x63000
计算差值

差值 = 节内存对齐后的大小 - 节文件对齐后的大小  = 节内存对齐后的大小 - SizeOfRawData
即 差值 = 0x63000 - 0x62200 = 0xE00
计算节在文件中的末尾位置

节在文件中的末尾位置 = 节在文件中的偏移 + 节文件对齐后的大小
即节在文件中的末尾位置 = PointerToRawData + SizeOfRawData
即节在文件中的末尾位置 = 0x1dfa00 + 0x62200 = 0x241C00
填充新空间

用WinHex打开EverEdit.exe,找到前面计算出来的节在文件中的末尾位置

选中末尾后 编辑→粘贴0字节


选择插入的大小为:0xE00(对应十进制为3584),即插入前面计算出来的差值
插入后,保存

修正节成员

修正Misc和SizeOfRawData为节内存对齐后的大小:0x63000
这里为了省事,直接使用PE工具:DIE举行修改

修正后:

修正后面的节

在该节后面的节在文件中的偏移增加差值
该节后面只有一个
节名称MiscSizeOfRawDataPointerToRawData.reloc0x1643a0x166000x241c00修改其PointerToRawData = PointerToRawData + 差值
即 PointerToRawData = 0x241c00 + 0xe00 = 242A00
同样使用PE工具:DIE举行修正

修改后

修正前后对比

执行完上一步,倒数第二个节就已经修正完了
对比一下修改前后节的信息
修改前

节名称MiscSizeOfRawDataPointerToRawData.rsrc0x620080x622000x1dfa00.reloc0x1643a0x166000x241c00
修改后

节名称MiscSizeOfRawDataPointerToRawData.rsrc0x630000x630000x1dfa00.reloc0x1643a0x166000x242a00
测试运行

发现仍然能够正常运行

代码实现修正内存对齐

#include #include #include #include #include //在VC6这个比较旧的情况里,没有界说64位的这个宏,需要本身界说,在VS2019中无需本身界说#define IMAGE_FILE_MACHINE_AMD64  0x8664//向文件中指定位置追加数据//第一个参数为文件路径//第二个参数为要追加的数据指针//第三个参数为要追加的数据大小//第四个参数为位置偏移//第五个参数为hMap的指针//第六个参数为pFile的指针BOOL appendFile(LPCSTR filePath, PVOID writeData, DWORD sizeOfWriteData, DWORD offset, HANDLE* phMap, PVOID* ppFile) {    HANDLE hFile = CreateFileA(filePath, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, 0);    char newPath[100];    strcpy(newPath, filePath);    strcat(newPath, ".exe");    HANDLE hFile2 = CreateFileA(newPath, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_WRITE, NULL, OPEN_ALWAYS, 0, 0);    //WriteFile用于吸收现实写入的大小的参数    DWORD dwWritenSize = 0;    //根据文件句柄创建映射    HANDLE hMap = CreateFileMappingA(hFile, NULL, PAGE_READWRITE, 0, 0, 0);    //映射内容    LPVOID pFile = MapViewOfFile(hMap, FILE_SHARE_WRITE, 0, 0, 0);    BYTE* content = (BYTE*)pFile;    content += offset;    //写入要插入数据前的数据    DWORD size = SetFilePointer(hFile, NULL, NULL, FILE_END);    BOOL bRet;    bRet = WriteFile(hFile2, pFile, offset, &dwWritenSize, NULL);    if (!bRet)return false;    //写入要插入的数据    SetFilePointer(hFile, NULL, NULL, FILE_END);    bRet = WriteFile(hFile2, writeData, sizeOfWriteData, &dwWritenSize, NULL);    if (!bRet)return false;    //写入要插入数据后的数据    SetFilePointer(hFile, NULL, NULL, FILE_END);    bRet = WriteFile(hFile2, content, size - offset, &dwWritenSize, NULL);    if (!bRet)return false;    //在删除文件前要先关闭句柄和映射    CloseHandle(hFile);    CloseHandle(hMap);    CloseHandle(*phMap);    UnmapViewOfFile(pFile);    UnmapViewOfFile(*ppFile);    bRet = DeleteFileA(filePath);    if (!bRet)return false;    hFile = CreateFileA(filePath, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_WRITE, NULL, OPEN_ALWAYS, 0, 0);    //根据文件句柄创建映射    hMap = CreateFileMappingA(hFile2, NULL, PAGE_READWRITE, 0, 0, 0);    //映射内容    pFile = MapViewOfFile(hMap, FILE_SHARE_WRITE, 0, 0, 0);    SetFilePointer(hFile, NULL, NULL, FILE_BEGIN);    bRet = WriteFile(hFile, pFile, sizeOfWriteData + size, &dwWritenSize, NULL);    if (!bRet)return false;    //在删除文件前要先关闭句柄和映射    CloseHandle(hFile);    CloseHandle(hFile2);    CloseHandle(hMap);    UnmapViewOfFile(pFile);    bRet = DeleteFileA(newPath);    if (!bRet)return false;    hFile = CreateFileA(filePath, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_WRITE, NULL, OPEN_ALWAYS, 0, 0);    //根据文件句柄创建映射    hMap = CreateFileMappingA(hFile, NULL, PAGE_READWRITE, 0, 0, 0);    //映射内容    *ppFile = MapViewOfFile(hMap, FILE_SHARE_WRITE, 0, 0, 0);    *phMap = hMap;    CloseHandle(hFile);    return true;}//根据pFile获取PE文件结构void GetPeStruct32(LPVOID pFile, _IMAGE_DOS_HEADER* dos, _IMAGE_NT_HEADERS* nt, _IMAGE_SECTION_HEADER** sectionArr) {    dos = (_IMAGE_DOS_HEADER*)pFile;    //创建指向PE文件头标志的指针    DWORD* peId;    //让PE文件头标志指针指向其对应的地址=DOS首地址+偏移    peId = (DWORD*)((UINT)dos + dos->e_lfanew);    //创建指向可选PE头的第一个成员magic的指针    WORD* magic;    //让magic指针指向其对应的地址=PE文件头标志地址+PE文件头标志大小+标准PE头大小    magic = (WORD*)((UINT)peId + sizeof(DWORD) + sizeof(_IMAGE_FILE_HEADER));    //根据magic判断为32位步伐还是64位步伐    //让PE文件头指针指向其对应的地址    nt = (_IMAGE_NT_HEADERS*)peId;    //创建指向块表的指针    _IMAGE_SECTION_HEADER* sectionHeader;    //让块表的指针指向其对应的地址    sectionHeader = (_IMAGE_SECTION_HEADER*)((UINT)nt + sizeof(_IMAGE_NT_HEADERS));    //计数,用来计算块表地址    int cnt = 0;    //比较 计数 和 块表的个数,即遍历所有块表    while (cnt < nt->FileHeader.NumberOfSections) {        //创建指向块表的指针        _IMAGE_SECTION_HEADER* section;        //让块表的指针指向其对应的地址=第一个块表地址+计数*块表的大小        section = (_IMAGE_SECTION_HEADER*)((UINT)sectionHeader + sizeof(_IMAGE_SECTION_HEADER) * cnt);        //将得到的块表指针存入数组        sectionArr[cnt++] = section;    }}//修正节表的Misc和SizeOfRawData//第一个参数为指向dos头的指针//第二个参数为指向nt头的指针//第三个参数为存储指向节指针的数组//第四个参数为文件路径//第五个参数为文件映射//第六个参数为文件映射内容指针//第七个参数为要修正的节表在数组中的下标void sectionAlignment(_IMAGE_DOS_HEADER* dos, _IMAGE_NT_HEADERS* nt, _IMAGE_SECTION_HEADER** sectionArr, LPCSTR filePath, HANDLE* phMap,LPVOID* ppFile, int n) {    //获得最后一个节的现实大小    DWORD VirtualSize = sectionArr[n]->Misc.VirtualSize;    //获得最后一个节的文件对齐后的大小    DWORD SizeOfRawData = sectionArr[n]->SizeOfRawData;    //计算上一个节内存对齐后的大小    UINT SizeInMemory = (UINT)ceil((double)max(VirtualSize, SizeOfRawData) / (double)nt->OptionalHeader.SectionAlignment) * nt->OptionalHeader.SectionAlignment;    printf("%X\n", SizeInMemory);    //计算差值= 内存对齐后大小 - 文件对齐后大小    UINT offset = SizeInMemory - sectionArr[n]->SizeOfRawData;    printf("%X\n", offset);    //根据节在文件中的偏移 + 文件对齐后的大小 得到节的末尾    UINT end = sectionArr[n]->PointerToRawData + sectionArr[n]->SizeOfRawData;    printf("end:%X\n", end);    //申请要填充的空间    INT* content = (INT*)malloc(offset);    //初始化为0    ZeroMemory(content, offset);    //WriteFile用于吸收现实写入的大小的参数    DWORD dwWritenSize = 0;    BOOL bRet=appendFile(filePath, (PVOID)content, offset, end,phMap,ppFile);    GetPeStruct32(*ppFile, dos, nt, sectionArr);    if (bRet) {        //开始修正Misc和SizeOfRawData        sectionArr[n]->Misc.VirtualSize = SizeInMemory;        sectionArr[n]->SizeOfRawData = SizeInMemory;        //修正后面受到影响的节的PointerOfRawData和VirtualAddress        int i;        while (n + 1 FileHeader.NumberOfSections - 1) {            n++;            sectionArr[n]->PointerToRawData += offset;        }    }}int main(int argc, char* argv[]){    //创建DOS对应的结构体指针    _IMAGE_DOS_HEADER* dos;    //读取文件,返回文件句柄    HANDLE hFile = CreateFileA("C:\\Users\\lyl610abc\\Desktop\\EverEdit\\EverEdit.exe", GENERIC_READ | GENERIC_WRITE, FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, 0);    //根据文件句柄创建映射    HANDLE hMap = CreateFileMappingA(hFile, NULL, PAGE_READWRITE, 0, 0, 0);    //映射内容    LPVOID pFile = MapViewOfFile(hMap, FILE_SHARE_WRITE, 0, 0, 0);    //类型转换,用结构体的方式来读取    dos = (_IMAGE_DOS_HEADER*)pFile;    //输出dos->e_magic,以十六进制输出    printf("dos->e_magic:%X\n", dos->e_magic);    //创建指向PE文件头标志的指针    DWORD* peId;    //让PE文件头标志指针指向其对应的地址=DOS首地址+偏移    peId = (DWORD*)((UINT)dos + dos->e_lfanew);    //输出PE文件头标志,其值应为4550,否则不是PE文件    printf("peId:%X\n", *peId);    //创建指向可选PE头的第一个成员magic的指针    WORD* magic;    //让magic指针指向其对应的地址=PE文件头标志地址+PE文件头标志大小+标准PE头大小    magic = (WORD*)((UINT)peId + sizeof(DWORD) + sizeof(_IMAGE_FILE_HEADER));    //输出magic,其值为0x10b代表32位步伐,其值为0x20b代表64位步伐    printf("magic:%X\n", *magic);    //根据magic判断为32位步伐还是64位步伐    switch (*magic) {    case IMAGE_NT_OPTIONAL_HDR32_MAGIC:    {        printf("32位步伐\n");        //确定为32位步伐后,就可以使用_IMAGE_NT_HEADERS来吸收数据了        //创建指向PE文件头的指针        _IMAGE_NT_HEADERS* nt;        //让PE文件头指针指向其对应的地址        nt = (_IMAGE_NT_HEADERS*)peId;        printf("Machine:%X\n", nt->FileHeader.Machine);        printf("Magic:%X\n", nt->OptionalHeader.Magic);        //创建一个指针数组,该指针数组用来存储所有的节表指针        //这里相当于_IMAGE_SECTION_HEADER* sectionArr[nt->FileHeader.NumberOfSections],声明白一个动态数组        _IMAGE_SECTION_HEADER** sectionArr = (_IMAGE_SECTION_HEADER**)malloc(sizeof(_IMAGE_SECTION_HEADER*) * nt->FileHeader.NumberOfSections);        //创建指向块表的指针        _IMAGE_SECTION_HEADER* sectionHeader;        //让块表的指针指向其对应的地址        sectionHeader = (_IMAGE_SECTION_HEADER*)((UINT)nt + sizeof(_IMAGE_NT_HEADERS));        //计数,用来计算块表地址        int cnt = 0;        //比较 计数 和 块表的个数,即遍历所有块表        while (cnt < nt->FileHeader.NumberOfSections) {            //创建指向块表的指针            _IMAGE_SECTION_HEADER* section;            //让块表的指针指向其对应的地址=第一个块表地址+计数*块表的大小            section = (_IMAGE_SECTION_HEADER*)((UINT)sectionHeader + sizeof(_IMAGE_SECTION_HEADER) * cnt);            //将得到的块表指针存入数组            sectionArr[cnt++] = section;            //输出块表名称            printf("%s\n", section->Name);        }        CloseHandle(hFile);        int i;        //sectionAlignment(dos, nt, sectionArr, "C:\\Users\\lyl610abc\\Desktop\\EverEdit\\EverEdit.exe",hMap, pFile,2);        for (i = 0; i < nt->FileHeader.NumberOfSections; i++) {            sectionAlignment(dos, nt, sectionArr, "C:\\Users\\lyl610abc\\Desktop\\EverEdit\\EverEdit.exe", &hMap, &pFile,i);        }        break;    }    case IMAGE_NT_OPTIONAL_HDR64_MAGIC:    {        printf("64位步伐\n");        //确定为64位步伐后,就可以使用_IMAGE_NT_HEADERS64来吸收数据了        //创建指向PE文件头的指针        _IMAGE_NT_HEADERS64* nt;        nt = (_IMAGE_NT_HEADERS64*)peId;        printf("Machine:%X\n", nt->FileHeader.Machine);        printf("Magic:%X\n", nt->OptionalHeader.Magic);        //创建一个指针数组,该指针数组用来存储所有的节表指针        //这里相当于_IMAGE_SECTION_HEADER* sectionArr[nt->FileHeader.NumberOfSections],声明白一个动态数组        _IMAGE_SECTION_HEADER** sectionArr = (_IMAGE_SECTION_HEADER**)malloc(sizeof(_IMAGE_SECTION_HEADER*) * nt->FileHeader.NumberOfSections);        //创建指向块表的指针        _IMAGE_SECTION_HEADER* sectionHeader;        //让块表的指针指向其对应的地址,区别在于这里加上的偏移为_IMAGE_NT_HEADERS64        sectionHeader = (_IMAGE_SECTION_HEADER*)((UINT)nt + sizeof(_IMAGE_NT_HEADERS64));        //计数,用来计算块表地址        int cnt = 0;        //比较 计数 和 块表的个数,即遍历所有块表        while (cnt < nt->FileHeader.NumberOfSections) {            //创建指向块表的指针            _IMAGE_SECTION_HEADER* section;            //让块表的指针指向其对应的地址=第一个块表地址+计数*块表的大小            section = (_IMAGE_SECTION_HEADER*)((UINT)sectionHeader + sizeof(_IMAGE_SECTION_HEADER) * cnt);            //将得到的块表指针存入数组            sectionArr[cnt++] = section;            //输出块表名称            printf("%s\n", section->Name);        }        break;    }    default:    {        printf("error!\n");        break;    }    }    return 0;}运行结果




代码说明

这次的代码比较复杂,由于C语言没有提供对文件指定位置插入数据的函数;只能本身手动实现了一个appendFile
着实就是:

  • 把要插入数据之前的数据写出到新文件
  • 将要插入数据写入到前面写出到文件末尾
  • 把要插入数据之后的数据写出到文件末尾
  • 删除原本的文件
  • 将新文件复制为原本的文件
  • 删除旧的文件
此中要注意,删除文件的时候要将先前获得的句柄和映射,即hFile,hMap,pFile关闭
关闭hFile,hMap,pFile后,需要重新获得新的PE结构,即更新dos、nt、sectionArr
说明


  • 修正内存对齐会影响后面的节,要修正后面的节在文件中的偏移
  • 对文件指定位置插入数据需要本身实现
  • 删除文件前要关闭相关的句柄和映射
  • 这次代码实现关于PE部分着实并没有什么难点,主要麻烦在要对文件指定位置插入数据
附件

附上本笔记中分析的EverEdit文件:点我下载
此次附件中添加了 修正完内存对齐后的exe


来源:http://www.12558.net
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有帐号?立即注册

x
楼主热帖
回复

使用道具 举报

*滑块验证:
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

QQ|Archiver|手机版|小黑屋|12558网页游戏私服论坛 |网站地图

GMT+8, 2024-11-28 06:04 , Processed in 0.078125 second(s), 32 queries .

Powered by Discuz! X3.4

© 2001-2017 Comsenz Inc.

快速回复 返回顶部 返回列表