重定位表

概述

数据目录项的第6个结构,就是重定位表。

结构

重定位表的结构

typedef struct _IMAGE_BASE_RELOCATION {
    DWORD   VirtualAddress;
    DWORD   SizeOfBlock;
} IMAGE_BASE_RELOCATION;
typedef IMAGE_BASE_RELOCATION ,* PIMAGE_BASE_RELOCATION;

该结构体有两个成员:一个是地址,一个是大小。如下图所示:一个重定位表由多个大小SizeOfBlock的Block组成。(Block在SizeOfBlock结构之后)

VirtualAddress 当前的RVA

宽度:四字节
真正的RVA = VirtualAddress + 具体项的低12位

SizeOfBlock

宽度:四字节
当前块的总大小

Block

Block的内的数据为需要重定位的数据的情况。其中高4位表示重定位的类型。低12位表示需要重定位的数据的偏移。
重定位表_第1张图片
Block结构每个成员四个字节,2^12位就可以完全表示4GB的内存空间。所以可以用低12位表示大偏移位置。高4位用来表示重定位数据的类型。

打印重定位表

思路: 用重定位表的FOA定位各项结构,遍历重定位表的各项结构。遍历的过程中,需要判断重定位表在哪个节。然后定位重定位表内的数据的地址与信息:
定位12位偏移

DWORD offset = (recAddr[j] & 0x0FFF) + FOA ;//12位偏移

定位高四位

WORD type = recAddr[j] >> 12;//四位

演示代码

#include 
#include 

unsigned char* FileBuffer(const char* FileName);
void PrintReloadTb();
DWORD RVA2Offset(PIMAGE_NT_HEADERS pNTHeader, DWORD dwRVA);


int main(int argc, char* argv[])
{
	PrintReloadTb();
	return 0;
}

//打印重定位表
void PrintReloadTb()
{

	PIMAGE_DOS_HEADER pDosHeader;
	PIMAGE_NT_HEADERS pNtHeaders;
	PIMAGE_BASE_RELOCATION  pBaseRec;
	PIMAGE_SECTION_HEADER pSectionHeader;


	unsigned char* fileBuffer = FileBuffer("D:\\test\\Dym.dll");
	pDosHeader = (PIMAGE_DOS_HEADER)fileBuffer;
	pNtHeaders = (PIMAGE_NT_HEADERS)(fileBuffer+pDosHeader->e_lfanew);

	printf("重定位表的相对虚拟地址:%x\n",pNtHeaders->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC].VirtualAddress);
	printf("重定位表的大小:%x\n",pNtHeaders->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC].Size);

	DWORD pBaseRecOffset = RVA2Offset(pNtHeaders, pNtHeaders->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC].VirtualAddress); //注意这个VirtualAddress是VA
	pBaseRec = (PIMAGE_BASE_RELOCATION)(pBaseRecOffset+fileBuffer);//重定位表文件偏移

	pSectionHeader = (PIMAGE_SECTION_HEADER)(pNtHeaders+1);
	BYTE secName[9] = {0};
	int i =0;
	for(i; pBaseRec->SizeOfBlock && pBaseRec->VirtualAddress; i++)
	{
		DWORD FOA = RVA2Offset(pNtHeaders, pBaseRec->VirtualAddress);
		DWORD size = (pBaseRec->SizeOfBlock - 8 )/2; // VirtualAddress SizeOfBlock共占8字节,每个SizeOfBlock项2字节

		//确定该结构所属的节
		 DWORD j=0;
		 for(j=0;j<pNtHeaders->FileHeader.NumberOfSections;j++)
		 {
			 DWORD lower =RVA2Offset(pNtHeaders, pSectionHeader[j].VirtualAddress);
			 DWORD upper =RVA2Offset(pNtHeaders, pSectionHeader[j].VirtualAddress+pSectionHeader[j].Misc.VirtualSize);

			if(FOA>=lower && FOA<=upper )
			{
				memcpy(secName,pSectionHeader[j].Name,8);
				break;
			}
		 }
		 printf("%d.RELOCATION\n VirtualAddress %x(%s)\n SizeOfBlock %x\n", i, pBaseRec->VirtualAddress,secName, size); //打印本页的主要信息
		 printf("RVA,TYPE\n");

		 //打印一个页中,所有重定位地址与信息
		 WORD * recAddr = (WORD *)((BYTE *)pBaseRec+8); //指向第一个目录项

		 for(j=0; j<size; j++)//每个结构的内容进行遍历
		 {
			DWORD offset = (recAddr[j] & 0x0FFF) + FOA ;//12位偏移
			WORD type = recAddr[j] >> 12;//三位

			if(type!=0)
			{
				 printf("%08X,  %x\n",offset+pBaseRec->VirtualAddress,type);
			}
		 }
        memset(secName, 0, 9);
        pBaseRec = (PIMAGE_BASE_RELOCATION )((BYTE *)pBaseRec + pBaseRec->SizeOfBlock);//移到下一页的内容
	}


}


//将PE文件读到FileBuffer
unsigned char* FileBuffer(const char* FileName)
{
     unsigned char* Heap = NULL;
     FILE* Stream;

     //打开文件
     Stream = fopen(FileName,"rb");
     //计算文件大小
     fseek(Stream,0,SEEK_END);
     long FileSize = ftell(Stream);
     fseek(Stream,0,SEEK_SET);

     //分配堆空间
     Heap = (unsigned char*)malloc(sizeof(char)*FileSize);
     //将文件拷到堆
     fread(Heap,sizeof(char),FileSize,Stream);
     fclose(Stream);

	 return Heap;
}

DWORD RVA2Offset(PIMAGE_NT_HEADERS pNTHeader, DWORD dwRVA)//内存对齐转化成文件对齐
{
    PIMAGE_SECTION_HEADER pSection = (PIMAGE_SECTION_HEADER)((DWORD)pNTHeader + sizeof(IMAGE_NT_HEADERS));

    for(int i = 0; i < pNTHeader->FileHeader.NumberOfSections; i++)
    {
        if(dwRVA >= pSection[i].VirtualAddress && dwRVA < (pSection[i].VirtualAddress + pSection[i].SizeOfRawData))
        {
            return pSection[i].PointerToRawData + (dwRVA - pSection[i].VirtualAddress);
        }
    }

    return 0;
}

你可能感兴趣的:(重定位表)