Loading... 重定位表: 当程序使用全局变量的语句时。生成的OPCODE中会直接包含绝对地址。一旦发生重定位,使用这个地址就会产生问题。所以需要通过重定位表保存所有需要重定位的地址,在程序运行时交由 PE 加载器进行修复。 ``` 重定位后的VA = 重定位前的VA – 默认加载基址 + 实际加载基址 ``` PE把文件中所有可能需要修改的地址放入一个数组冲。如果PE文件不在首选的地址,那么文件中每一个定位都需要被修正。 基址重定位表(Base Relocation Table)位于一个.reloc区块内。目录表的 IMAGE_DIRECTORY_ENTRY_BASERLOC 条目查找。基址重定位数据采用按页分割的方式组织,有许多重定位快串接而成,每个块中存放4KB的重定位信息,4字节大小对其(DWORD),IMAGE_BASE_RELOCATIOn结构开始。 ``` IMAGE_BASE_RELOCATION STRUCT { VirtualAddress DWORD ? ;重定位数据的开始RVA地址 SizeofBlock DWORD ? ;重定位块的长度 TypeOffset WORD ? ;重定位项数组 } ``` VirtualAddress:重定位数据开始的RVA。各重定向的地址加上这个值才是完整RVA SizeofBlock :当前重定位结构大小,因为VirtualAddress和SizeOfBlock大小都是固定4字节,所以值-8 就是TypeOffset数组的大小。 TypeOffset :数组每项为2字节,共16位。16位分为高四位,和第四位。高四位代表重定位类型;第四位代表重定位地址,与VirtualAddress相加就是指向PE映像中需要修改的地址数据指针。 | 类型 | Winnt.h里的预定义值 | 含义 | | - | - | - | | 0 | IMAGE_REL_BASED_ABSOLUTE | 没有具体含义,只是为了让每段4字节对齐 | | 3 | IMAGE_REL_BASED_HIGHLOW | 重定位指向整个地址都需要修正,实际大小部分情况下都是这样 | | 10 | IMAGE_REL_BASED_DIR64 | 出现在64位文件中,对指向的整个地址进行修正 | ![image.png](http://www.irohane.top/usr/uploads/2021/01/332353049.png) 偏移地址为00000E00h,其IMAGE_BASE_RELOCATION构造如图。 ![image.png](http://www.irohane.top/usr/uploads/2021/01/3462494296.png) Virtual: 00 00 10 00 SizeOfBlock: 00 00 00 10 Rec1: 30 0F Rec2: 30 23 Rec3: 00 00 (用于对齐) Rec4: 00 00 (用于对齐) | 项目 | 重定位数据1 | 重定位数据2 | 重定位数据3 | 重定位数据4 | | - | - | - | - | - | | 原始数据 | 0F30h | 2330h | 00 00h | 00 00h | | 转换到内存 | 300Fh | 3023h | ---- | ---- | | TypeOffset高四位 | 3h | 3h | ---- | ---- | | TtpeOffset低四位 | 00fh | 023h | ---- | ---- | | 低12位加virtualaddress | 100fh(RVA) | 1023h(RVA) | ---- | ---- | | 文件偏移 | | | | | 这时候算出来的文件偏移,可以在文件中查看的 --- ``` /修复DLL重定位 BOOL Tool::FixDllStub() { //DLL 中的地址或者变量 加载到文件PE 中那么偏移也应该进行相应的改变。所以说需要修复重定位表 DWORD OldImageBase = GetOptionHandle(DllBase)->ImageBase; //旧的基址 DWORD NewImageBase = GetOptionHandle(FileBase)->ImageBase; //新的基址 //区段 DWORD OldSection = GetSectionHandle(DllBase, ".text")->VirtualAddress; //以.text段基址开始的RVA DWORD NewSection = GetSectionHandle(FileBase, ".sakura")->VirtualAddress;//以.sakura段开始的RVA //获取需要重定位DLL的重定位表 auto pRel = (PIMAGE_BASE_RELOCATION)(GetOptionHandle(DllBase)->DataDirectory[5].VirtualAddress + DllBase); //遍历重定位块 while (pRel->SizeOfBlock) { DWORD OldProtect = 0; //重定位的地址可能所在代码段所以对属性进行修改 //记录之前的属性 //修改属性 VirtualProtect(LPVOID(DllBase+pRel->VirtualAddress),0x1000, PAGE_EXECUTE_READWRITE, &OldProtect); // 03 AB CD EF 内存中 EF CD AB 03 //获取重定位数据的偏移 typedef struct _TYPEOFFSET { // USHORT Offset : 12; //内存中以小端存储 USHORT Type : 4; }TYPEOFFSET, * PTYPEOFFSET; //获取 重定位元素的个数 和地址 PTYPEOFFSET TypeOffset = (PTYPEOFFSET)(pRel + 1); DWORD Count = ((pRel->SizeOfBlock - sizeof(IMAGE_BASE_RELOCATION)) / 2); //遍历重定位项 for (int i = 0;i < Count;++i) { //如果超出代码段 指向一个无用区段 if (pRel->VirtualAddress >= (GetSectionHandle(DllBase, ".text") + 1)->VirtualAddress) { DWORD _OldProtect; VirtualProtect((LPVOID)pRel, 0x1, PAGE_EXECUTE_READWRITE, &_OldProtect); //更新区段 指向一个无用区段 pRel->VirtualAddress = (GetSectionHandle(DllBase, ".text")->VirtualAddress + GetSectionHandle(DllBase, ".text")->SizeOfRawData); //计算出重定位项的地址 算出来的是地址需要 DWORD指针作为接受 DWORD* addr = (DWORD*)(TypeOffset[i].Offset + pRel->VirtualAddress + DllBase); //更新重定位表地址 解引用将地址转换为所对应需要更新的DWORD 地址实际地址 *addr = *addr - OldImageBase - OldSection + pRel->VirtualAddress+ NewImageBase; VirtualProtect((LPVOID)pRel, 0x1, _OldProtect, &_OldProtect); } //如果类型是重定位的 if (TypeOffset->Type == 3); { //计算出重定位项的地址 DWORD* addr = (DWORD*)(TypeOffset[i].Offset + pRel->VirtualAddress + DllBase); //更改原来的地址 *addr = *addr - OldImageBase - OldSection + NewImageBase + NewSection; } } //还原属性 VirtualProtect(LPVOID(DllBase + pRel->VirtualAddress), 0x1000, OldProtect, &OldProtect); //计算出下一个 重定位块的大小 pRel = (PIMAGE_BASE_RELOCATION)((DWORD)pRel + pRel->SizeOfBlock); } return TRUE; } ``` 最后修改:2021 年 03 月 05 日 © 允许规范转载 赞 0 如果觉得我的文章对你有用,请随意赞赏