12558网页游戏私服论坛

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

PE文件笔记十 扩大节

[复制链接]
发表于 2021-7-18 18:12:31 | 显示全部楼层 |阅读模式
继承PE系列笔记的更新
PE其它笔记索引可前往:
PE文件笔记一 PE介绍
经过前面的实战PE文件笔记八 实战之HOOK程序添加弹窗和PE文件笔记九 实战之HOOK程序添加弹窗续,对PE文件结构有了进一步的了解之后继承来学习对节的操作
此篇笔记为学习节操作之扩大节
扩大节

为什么要扩大节

在先前的实战中,通过在节表和节之间的空白区写入代码并执行来到达了弹窗的效果,但是如果当想要执行的代码量较大时,即空白区的空间不够时,就可以通过扩大节来解决空白区不敷的问题
扩大节涉及的结构体成员

涉及的节表成员寄义VirtualAddress节在内存中的偏移 (RVA)Misc节的实际大小SizeOfRawData节在文件中对齐后的尺寸PointerToRawData节区在文件中的偏移涉及的扩展PE头成员寄义SizeOfImageImage(PE文件)大小扩大哪个节

既然要扩大节,那么选择哪个节举行扩大比较好?
实际上每个节都可以扩大,但是一般来说是选取最后一个节举行扩大,为什么?
因为节是顺序存储的,如果扩大了前面的节就意味着背面的节全部都要相应地修改偏移,比较贫苦
选取最后一个节举行扩大就不会影响到背面的节了
扩大节流程


  • 将最后一个节的SizeOfRawData和VirtualSize改成N,N = 节内存对齐后的大小 + 要扩大的大小
  • 修改 SizeOfImage大小=SizeOfImage大小+要扩大的大小
  • 分配一块新的空间,大小为:节内存对齐增长的大小+要扩大的大小
按流程扩大节

此次依旧以先前的EverEdit.exe为例举行扩大节的演示
修正节表成员

由于选择的是最后一个节举行扩大,于是不涉及VirtualAddress和PointerToRawData的修改,只需修改Misc和SizeOfRawData即可
先用PE工具DIE查看一下最后一个节的成员

得到了
涉及的节表成员值寄义Misc0x1643a节的实际大小SizeOfRawData0x16600节在文件中对齐后的尺寸于是可以计算出在节内存对齐后的大小:
节内存对齐后大小 = (max{Misc,SizeOfRawData}÷SectionAlignment)向上取整 × SectionAlignment
即节内存对齐后大小 (= max{0x1643a,0x16600}÷0x1000)向上取整 × 0x1000 =(0x16600÷0x1000)向上取整 × 0x1000 = 0x17000
计算 节内存对齐后大小的原理在PE文件笔记六 节表和节中已经说明,这里不再赘述
得到的节内存对齐后的大小后再加上要扩大的大小:0x1000,即 0x17000+0x1000=0x18000
于是要将Misc和SizeOfRawData都修改为0x00018000
此时节内存对齐增长的大小 = 节内存对齐后大小 - SizeOfRawData(节文件对齐后的大小)
节内存对齐增长的大小 = 0x17000 - 0x16600 = 0x17000 - 0x16600 =0xA00
用WinHex找到对应的位置

修改数据,注意小端存储

修正SizeOfImage

用PE工具:DIE查看一下扩展PE头中SizeOfImage

得到本来SizeOfImage的大小为0x00298000
要修改为 本来大小+扩大的大小= 0x00298000 + 0x1000 = 0x00299000
在WinHex中找到SizeOfImage对应的位置

修改为:

分配新空间

使用WinHex打开EverEdit,直接拉到文件的末尾,并选中最后一个字节

然后 编辑→粘贴0字节


按"是"

选择插入的大小为:节内存对齐增长的大小 + 要扩大的大小 = 0xA00 + 0x1000=0x1A00 对应十进制为6656
添加完成

生存测试

将文件生存后再打开,发现仍然能够正常运行

代码实现扩大节

//扩大最后一个节//第一个参数为指向dos头的指针//第二个参数为指向nt头的指针//第三个参数为存储指向节指针的数组//第四个参数为文件句柄//第五个参数为要扩大的节的大小void expandSection(_IMAGE_DOS_HEADER* dos, _IMAGE_NT_HEADERS* nt, _IMAGE_SECTION_HEADER** sectionArr, HANDLE hFile, UINT expandSize) {        //得到最后一个节的实际大小        DWORD VirtualSize = sectionArr[nt->FileHeader.NumberOfSections - 1]->Misc.VirtualSize;        //得到最后一个节的文件对齐后的大小        DWORD SizeOfRawData = sectionArr[nt->FileHeader.NumberOfSections - 1]->SizeOfRawData;        //算出最后一个节内存对齐后的大小        UINT SizeInMemory = (UINT)ceil((double)max(VirtualSize, SizeOfRawData) / double(nt->OptionalHeader.SectionAlignment)) * nt->OptionalHeader.SectionAlignment;        printf("Last Section SizeInMemory:%X\n", SizeInMemory);        //根据内存对齐后大小 - 文件对齐后大小 得到 节内存对齐增长的大小        UINT offset = SizeInMemory - sectionArr[nt->FileHeader.NumberOfSections - 1]->SizeOfRawData;        printf("offset:%X\n", offset);        //根据节在文件中的偏移 + 文件对齐后的大小 得到节的末尾        UINT end = sectionArr[nt->FileHeader.NumberOfSections - 1]->PointerToRawData + sectionArr[nt->FileHeader.NumberOfSections - 1]->SizeOfRawData;        printf("end:%X\n", end);        //根据要扩大的节的大小 + 节内存对齐增长的大小 得到 要分配的新空间大小        UINT size = offset + expandSize;        //设置要写入的地点为节末尾        SetFilePointer(hFile, end, NULL, FILE_BEGIN);        //申请要填充的空间        INT* content = (INT*)malloc(expandSize + offset);        //初始化为0        ZeroMemory(content, expandSize + offset);        //WriteFile用于接收实际写入的大小的参数        DWORD dwWritenSize = 0;        BOOL bRet = WriteFile(hFile, content, expandSize + offset, &dwWritenSize, NULL);        if (bRet)        {                printf("expand Section success!\n");                //修正节表成员                sectionArr[nt->FileHeader.NumberOfSections - 1]->Misc.VirtualSize = SizeInMemory + expandSize;                sectionArr[nt->FileHeader.NumberOfSections - 1]->SizeOfRawData = SizeInMemory + expandSize;                //修正SizeOfImage                nt->OptionalHeader.SizeOfImage += expandSize;        }        else {                printf("%d\n", GetLastError());        }}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);                }                expandSection(dos, nt, sectionArr, hFile, 0x1000);                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;}运行结果



可以看到代码执行以后,到达了和前面手动操作一样的效果,并且程序仍然能够正常运行( &#8226;&#768; ω &#8226;&#769; )&#10023;
代码说明

代码中修改了先前打开文件的权限,修改其拥有写权限
//读取文件,返回文件句柄        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);在扩大节的相干代码中用到了WriteFile和SetFilePointer;其它的就是单纯的PE内容了,代码中都有注释,这里也就不再赘述了
总结


  • 扩大节一般选取最后一个节举行扩大
  • 扩大最后一个节必要修正的节表成员:Misc和SizeOfRawData 以及修正扩展PE头中的SizeOfImage
  • 扩大节后要分配的空间除了要扩大的大小外还要加上节内存对齐增长的大小(内存对齐后的大小-文件对齐后的大小)
附件

附上本笔记中分析的EverEdit文件:点我下载

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

本帖子中包含更多资源

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

x
楼主热帖
回复

使用道具 举报

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

本版积分规则

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

GMT+8, 2024-11-24 09:33 , Processed in 0.078125 second(s), 31 queries .

Powered by Discuz! X3.4

© 2001-2017 Comsenz Inc.

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