重定位表:

当程序使用全局变量的语句时。生成的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

偏移地址为00000E00h,其IMAGE_BASE_RELOCATION构造如图。

image.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 日 03 : 56 PM