在PE文件的字节码中有一些写死的地址,如:
使用这些内存地址需要在当前的模块下,如果该PE文件在其他模块下被加载,则其内存地址对应的内容并非当前的内容。
重定义表位于可选PE头的数据目录表的第六个元素,也就是索引值为5的结构体
获得重定义表
和导出表导入表相同,得到PE头的数据目录表后访问具体的某个数组元素后,进入IMAGE_DATA_DIRECTORY结构体,然后访问VirtualAddress字段来获取地址,然后强转使用。
重定义表结构体
typedef struct _IMAGE_BASE_RELOCATION {
DWORD VirtualAddress;
DWORD SizeOfBlock;
// WORD TypeOffset[1];
} IMAGE_BASE_RELOCATION;
typedef IMAGE_BASE_RELOCATION UNALIGNED * PIMAGE_BASE_RELOCATION;
sizeofBlock存的同一颜色的块的大小,VitualAddress存放地址RVA
重定义表存放数据流程
先将数据地址的第一个拿来用,比如说第一个数据地址是0x40001,那么第一个表的VirtualAddress 就是0x40000,然后第一个块下面存放0x1,最大的块的内存为0x1000,超过了就要换下一个块了
VirtualAddress + 存放的偏移地址 = 需要修复的地址RVA
判断地址是否需要修复
在重定义表结构体下面,都是WORD类型的数据,两个字节两个字节的16位存储,取16位的前四位,如果等于3表示该地址需要修复。
也就是说存放的地址一共16位,前四位是拿来判断该地址是否可以修复,后12位才是来存放地址,真正的地址RVA = 后12位+VirtualAddress
代码解析重定义表
1 拿到可选PE头的对应的数据目录表的第六个重定义表的指向结构体
2 拿到指向结构体对应地址的第一个重定义表的第一个区块
3 利用区块自增来访问每一个区块
4 利用区块里面的访问数据来一个一个访问数据
void CPeUtil::test()
{
//拿到数据目录表
IMAGE_DATA_DIRECTORY directory = pOptionalHeader->DataDirectory[5];
//拿到第一个重定位表
PIMAGE_BASE_RELOCATION RelocationTable = PIMAGE_BASE_RELOCATION(RvaToFoa(directory.VirtualAddress) + FileBuff);
//开始遍历重定位表
while (1)
{
if (RelocationTable->VirtualAddress == 0)
{
break;
}
//拿到重定义表的数据的大小
DWORD SizeNumber = (RelocationTable->SizeOfBlock - 8) / 2;
//开始遍历重定义表块的内容
//先拿到第一个数据的地址
WORD* Offset = (WORD*)RelocationTable + 4;
for (int i = 0; i < SizeNumber; i++)
{
if (*Offset & 0x3000 == 0x3000)
{
DWORD Rva = RelocationTable->VirtualAddress + *Offset&0x0FFF;
printf("Rva = %x\n",Rva);
}
Offset++;
}
RelocationTable = RelocationTable + RelocationTable->SizeOfBlock;
}
}