重定位表:
程序的加载过程:
在双击一个exe可执行程序时,它的执行步骤是通过资源管理器(exploer,exe)定位到文件,得到文件后,使用CreateProcess(或CreateUserProcess)函数创建一个进程,但是需要注意的是,虽然创建了一个进程,但是未必跑的就一定是exe(可能是DLL或者sys)这俩步只是在三环或者0环挂上了所谓的物理页,分配了一块虚拟内存空间。然后通过文件的路径载入PE。
载入后会拉伸、各种重定位。最后是call 程序入口。
为什么要重定位呢?
在程序编译前的地址是Imagebase+ RVA。当这个地址被编译后,已经写入文件中了,那么假设,程序在编译时没有按照imagebase提供的基地址进行写入,那么在使用全局变量或者函数时,就会使用错误的数据,因为他们的偏移发生了变化。
也就是说如果程序按照imagebase提供的基地址进行加载,那么就不需要重定位。
重定位表:
数据目录的第6个结构就是重定位表:
typedef struct _IMAGE_DATA_DIRECTORY {
DWORD VirtualAddress; //重定位表的RVA
DWORD Size; //大小
} IMAGE_DATA_DIRECTORY, *PIMAGE_DATA_DIRECTORY;
1、通过IMAGE_DATA_DIRECTORY结构中的VirtualAaddress成员找到第一个IMGAE_BASE_RELOCATION
typedef struct _IMAGE_BASE_RELOCATION {
DWORD VirtualAddress; //需要定位的数据存在改地址中的低12位,高12位是3则表示改块需要修复
DWORD SizeOfBlock; //块的大小
} IMAGE_BASE_RELOCATION;
typedef IMAGE_BASE_RELOCATION ,* PIMAGE_BASE_RELOCATION;
2、判断一共有几块数据
IMAGE_BASE_RELOCATION结构中SizeOfBlock成员是该块的总大小。
计算一共存在多少块数据,则判断该结构中的两个成员都为0。
3、修复的具体项
具体的项占用2字节,那么通过当前块的总大小减掉两个成员的宽度就得到了需要修复的项,使用项在÷2 就得到了具体由多少项需要修复。计算公式如下:
具体需要修复多少项 = 块的总大小(SizeOfBlock - VirtualAddress - SizeOfBlock)/ 2
图解:
内存中页的大小是1000H(4KB),也就是2的12次方,就可以表示一个页内的所有偏移,具体项的宽度为2字节(16bit),高4位代表类型,值为3代表需要修复的数据,值为0代表的是用于数据对齐的,可以不用修改,所以我们只需要关注值为3的。
4、如何定位需要修复的数据、
具体项的高4位表示是否需要修复(也就是判断值是不是为3)如果为3,则该项的低12位+ VirtualAddres 才是真正需要修复数据的RVA。
实现代码:
/*定位头*/
PIMAGE_DOS_HEADER DosHeader = (PIMAGE_DOS_HEADER)FileBuffer;
PIMAGE_NT_HEADERS NtHeader = (PIMAGE_NT_HEADERS)((DWORD)DosHeader + DosHeader->e_lfanew);
PIMAGE_FILE_HEADER FileHeader = (PIMAGE_FILE_HEADER)((DWORD)NtHeader + 4);
PIMAGE_OPTIONAL_HEADER OptionalHeader = (PIMAGE_OPTIONAL_HEADER)((DWORD)FileHeader + IMAGE_SIZEOF_FILE_HEADER);
/*定位重定位表结构*/
PIMAGE_BASE_RELOCATION Relocation = (PIMAGE_BASE_RELOCATION)(RVAtoFOA(FileBuffer, OptionalHeader->DataDirectory[5].VirtualAddress) + (DWORD)FileBuffer);
/*遍历有几个块 和 每一块中存在多少项:具体项的数量 = (SizeOfBlock - 8)/2 */
int i = 0;
while (Relocation->VirtualAddress != 0 && Relocation->SizeOfBlock != 0)
{
int Item = (Relocation->SizeOfBlock - 8) / 2; //该块的具体项
char* SectionName = LocationSection(FileBuffer, RVAtoFOA(FileBuffer, Relocation->VirtualAddress)); //定位节名称
printf("%s\t 第%d块\t共%d项\tRVA:%x\n", SectionName, i + 1, Item, Relocation->VirtualAddress);
/*遍历要修复的块*/
short* test = (short*)((DWORD)Relocation + 8);
printf("Index\tRVA\tOffset\tType\t\n");
for (int j = 0; j < Item; j++)
{
if (*test & 0x3000)
{
DWORD addr = RVAtoFOA(FileBuffer, Relocation->VirtualAddress + *test - 0x3000);
DWORD xiufu = addr + (DWORD)FileBuffer;
printf("%d\t%x\t%x\t% d\t\n", j + 1, Relocation->VirtualAddress + *test - 0x3000, addr, 3);
}
test++;
}
Relocation = (PIMAGE_BASE_RELOCATION)((DWORD)Relocation + Relocation->SizeOfBlock); //定位下一块的位置
i++;
}