12558网页游戏私服论坛

 找回密码
 立即注册
游戏开服表 申请开服
游戏名称 游戏描述 开服状态 游戏福利 运营商 游戏链接
攻城掠地-仿官 全新玩法,觉醒武将,觉醒技能 每周新区 经典复古版本,长久稳定 进入游戏
巅峰新版攻 攻城掠地公益服 攻城掠地SF 新兵种、新武将(兵种) 进入游戏
攻城掠地公 散人玩家的天堂 新开 进入游戏
改版攻城掠 上线即可国战PK 稳定新区 全新改版,功能强大 进入游戏
少年江山 高福利高爆率 刚开一秒 江湖水落潜蛟龙 进入游戏
太古封魔录 开服送10亿钻石 福利多多 不用充钱也可升级 进入游戏
神魔之道 签到送元宝 稳定开新区 送豪华签到奖励 进入游戏
神奇三国 统帅三军,招揽名将 免费玩新区 激情国战,征战四方 进入游戏
龙符 三日豪礼领到爽 天天开新区 助你征战无双 进入游戏
王者之师 免费领豪华奖励 免费玩新区 6元送6888元宝 进入游戏
查看: 396|回复: 0

PE文件笔记十一 新增节

[复制链接]

52

主题

52

帖子

114

积分

实习版主

Rank: 7Rank: 7Rank: 7

积分
114
发表于 2021-5-7 23:35:20 | 显示全部楼层 |阅读模式
继续PE系列条记的更新
PE别的条记索引可前去:
PE文件条记一 PE介绍
前面在PE文件条记十 扩大节学习了关于节的操纵之扩大节,接下来继续学习节的操纵之新增节
新增节

为什么要新增节

新增节和扩大节一样,都是用来解决空白区不敷的问题
但既然扩大节已经能够解决问题了,为什么还要新增节呢?或者说新增节比扩大节幸亏哪里
在前面的扩大节中,只是扩大了节,并没有关注扩大出来的空白区的权限问题;每个节都有其对应的权限,由节.Characteristics决定
有关节的权限问题已经在PE文件条记六 节表和节中说过,这里不再赘述
如果扩大出来的空白区盼望能够被用于实行代码,那么被扩大的节就必须具备IMAGE_SCN_CNT_CODE权限(该节包含可实行代码)
如果被扩大的节不具备这个权限,还得为此将整个节的权限修改
除此之外,扩大节还会使得本来的数据和我们扩大的空白区混在一起
相比之下新增节则完全拥有自己的权限,不依附于要扩大的节的权限,可以自己指定想要的权限
扩大节和新增节的差异

扩大节:权限取决于要被扩大的节的本来权限,如果不满足权限还需要去修改;本来数据和扩大的空白区混在一起
新增节:权限由自己来指定;空间独立没有数据混杂
新增节涉及的布局体成员

涉及的节表成员含义Name节名称VirtualAddress节在内存中的偏移 (RVA)Misc节的现实大小SizeOfRawData节在文件中对齐后的尺寸PointerToRawData节区在文件中的偏移Characteristics节的属性涉及的尺度PE头成员含义NumberOfSections节的个数涉及的扩展PE头成员含义SizeOfImageImage(PE文件)大小新增节的位置

和扩大节一个道理,为了避免新增节后影响先前的节,选择在本来的最后一个节后面新增节
新增节的流程


  • 判断是否有充足空间用于添加节表
  • 修改尺度PE头中节的数目
  • 在节表中新增一个成员
  • 修正SizeOfImage的大小
  • 分配新空间
按流程新增节

此次依旧以先前的EverEdit.exe为例进行新增节的演示
判断是否有空间能添加节表

用WinHex打开EverEdit.exe,找到最后一个节表,判断最后一个节表后面的40个字节(节表的大小)是否全为0

可以看到最后一个节区后40个字节全为0,因此是可以添加节表的
修改尺度PE头中节的数目

之前都是利用WinHex来进行修改演示的,是为了更好地学习本质;到了如今对PE文件比较熟悉的时间,就可以用PE工具:DIE工具来代替WinHex进行修改了(看起来更直观)




在节表中新增一个成员

回到先前PE 根本信息的地方,点击节来查看节表信息




确定要修改的数值

涉及的节表成员值要求含义Name.lyl610小于等于8位节名称Misc.VirtualSize0x1000要新增的节的大小节的现实大小VirtualAddress0x298000上一个节.VirtualAddress+上一个节内存对齐后的大小节在内存中的偏移 (RVA)SizeOfRawData0x1000要新增的节的大小节在文件中对齐后的尺寸PointerToRawData0x258200上一个节.PointerToRawData+上一个节.SizeOfRawData节区在文件中的偏移Characteristics0xe0000000该节具备的权限,这里指定为节可读、可写、可实行节的属性这里主要讲一下VirtualAddress和PointerToRawData的计算,其余的都是自己指定的
VirtualAddress:
VirtualAddress为该节在内存中的偏移 = 上一个节.VirtualAddress+上一个节内存对齐后的大小 = 0x281000 + 0x17000 = 0x298000
关于节内存对齐后的大小如何计算在PE文件条记十 扩大节中已经说明,这里也就不再赘述
PointerToRawData:
PointerToRawData为该节在文件中的偏移 = 上一个节在文件中的偏移 + 上一个节文件对齐后的大小
即PointerToRawData = 上一个节.PointerToRawData + 上一个节.SizeOfRawData = 0x241c00+0x16600=0x258200
填充要修改的数值


修正SizeOfImage的大小


这里将SizeOfImage增加0x1000(与前面新增节中的Misc.VirtualSize和SizeOfRawData对应)

分配新空间

分配新空间还是得交给WinHex,步骤和扩大节中的分配新空间没什么不同o( ̄▽ ̄)ブ
利用WinHex打开EverEdit,直接拉到文件的末尾,并选中最后一个字节

然后 编辑→粘贴0字节


按"是"

选择插入的大小为:0x1000(与前面新增节中的Misc.VirtualSize和SizeOfRawData对应),对应十进制为4096
添加完成

生存测试

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

代码实现新增节

//新增一个节//第一个参数为指向dos头的指针//第二个参数为指向nt头的指针//第三个参数为存储指向节指针的数组//第四个参数为文件句柄//第五个参数为要新增的节的大小//第六个参数为新增节的名称//第七个参数为新增节的权限void addSection(_IMAGE_DOS_HEADER* dos, _IMAGE_NT_HEADERS* nt, _IMAGE_SECTION_HEADER** sectionArr, HANDLE hFile, UINT addSize, BYTE Name[IMAGE_SIZEOF_SHORT_NAME], DWORD Characteristics) {    //判断最后一个节表的后40个字节是否全为0    //通过最后一个节表+节表大小 到达新节表    _IMAGE_SECTION_HEADER* newSection = (_IMAGE_SECTION_HEADER*)((UINT)&sectionArr[nt->FileHeader.NumberOfSections - 1]->Name + sizeof(_IMAGE_SECTION_HEADER));    //判断新节表是否有被填充    UINT* tmp = (UINT*)newSection;    int i;    //标记 判断新节表是否全为0    BOOL flag = false;    for (i = 0; i < sizeof(_IMAGE_SECTION_HEADER) / sizeof(INT); i++) {        if (*tmp != 0) {            flag = true;        }        tmp++;    }    if (flag) {        printf("空间不敷,无法新增节\n");        return;    }    //Name赋值    for (i = 0; i < IMAGE_SIZEOF_SHORT_NAME; i++) {        newSection->Name = Name;    }    //大小赋值    newSection->Misc.VirtualSize = addSize;    newSection->SizeOfRawData = addSize;    //权限赋值    newSection->Characteristics = Characteristics;    //获得最后一个节的现实大小    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;    //RVA赋值    newSection->VirtualAddress = sectionArr[nt->FileHeader.NumberOfSections - 1]->VirtualAddress + SizeInMemory;    printf("newSection->VirtualAddress:%X\n", newSection->VirtualAddress);    //FOA赋值    newSection->PointerToRawData = sectionArr[nt->FileHeader.NumberOfSections - 1]->PointerToRawData + sectionArr[nt->FileHeader.NumberOfSections - 1]->SizeOfRawData;    printf("newSection->PointerToRawData:%X\n", newSection->PointerToRawData);    //分配新空间    //根据节在文件中的偏移 + 文件对齐后的大小 得到节的末尾    UINT end = sectionArr[nt->FileHeader.NumberOfSections - 1]->PointerToRawData + sectionArr[nt->FileHeader.NumberOfSections - 1]->SizeOfRawData;    printf("end:%X\n", end);    //设置要写入的所在为节末尾    SetFilePointer(hFile, end, NULL, FILE_BEGIN);    //申请要填充的空间    INT* content = (INT*)malloc(addSize);    //初始化为0    ZeroMemory(content, addSize);    DWORD dwWritenSize = 0;    BOOL bRet = WriteFile(hFile, content, addSize, &dwWritenSize, NULL);    if (bRet)    {        //修改尺度PE头中节的数目        nt->FileHeader.NumberOfSections += 1;        //修正SizeOfImage大小        nt->OptionalHeader.SizeOfImage += addSize;        printf("add Section success!\n");    }    else {        printf("分配新空间失败\n");    }}int main(int argc, char* argv[]){    //创建DOS对应的布局体指针    _IMAGE_DOS_HEADER* dos;    //读取文件,返回文件句柄    HANDLE hFile = CreateFileA("C:\\Users\\sixonezero\\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);        }        BYTE Name[IMAGE_SIZEOF_SHORT_NAME] = ".lyl610";        addSection(dos, nt, sectionArr, hFile, 0x1000, Name, 0xe0000000);        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;
总结


  • 新增节的好处就是可以自己指定想要的权限,代码更加具有独立性
  • 无论是新增节还是扩大节都要注意文件对齐和内存对齐;分配的新空间大小最好为内存对齐的整数倍
附件

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

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

本帖子中包含更多资源

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

x
楼主热帖
回复

使用道具 举报

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

本版积分规则

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

GMT+8, 2024-3-29 22:59 , Processed in 0.140625 second(s), 31 queries .

Powered by Discuz! X3.4

© 2001-2017 Comsenz Inc.

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