看了下看雪的一篇文章,加深下对重定位的理解,其实这是一个程序
https://bbs.pediy.com/thread-76638.htm
这个程序需要用户输入的是加载基址,还有文件的路径
开始首先通过MZ头,还有e_lfanew偏移是否是PEx00x00 来判断这是否是一个PE文件
BOOL VerifyPE( PVOID pFile )
{
PIMAGE_DOS_HEADER pDosHeader;
PIMAGE_NT_HEADERS pNtHeader;
pDosHeader = (PIMAGE_DOS_HEADER)pFile;
if ( pDosHeader->e_magic != 0x5A4D ) // compare with 'MZ'
return FALSE;
pNtHeader = (PIMAGE_NT_HEADERS)((PCHAR)pFile + pDosHeader->e_lfanew); // e_lfanew + 0x3c
if ( pNtHeader->Signature != 0x00004550 ) // compare with 'PE '
return FALSE;
return TRUE;
}
重定位
计算镜像基址与真正加载基址的差值
pDosHeader = (PIMAGE_DOS_HEADER)pFile;
pNtHeader = (PIMAGE_NT_HEADERS)((PCHAR)pFile + pDosHeader->e_lfanew); // e_lfanew + 0x3c
dwImageBase = pNtHeader->OptionalHeader.ImageBase;
dwDiffer = dwImageBase - BaseAddr; // pay attention to the order
获取重定位表RVA,并计算出File Offset
// Get reloc table RVA
PIMAGE_DATA_DIRECTORY pRelocTable = (PIMAGE_DATA_DIRECTORY)pNtHeader->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC].VirtualAddress;
// Get reloc table File Offset
pRelocBlock = (PIMAGE_RELOCATION)( (PCHAR)pFile + RvaToFileOff( pFile, (DWORD)pRelocTable ) ); // the pRelocTable is file offset?
之后根据重定位表循环计算真正的地址,具体如下
1、一次获取一条记录,获取其RVA,算出File Offset
2、通过与pRelocAddr相加得到储存原始地址的地方(即要修正的地址)
3、将这个要修正的地址减去之前算出来的基址的差别即可
4、最后将结果写回文件即可
pRelocAddr = pRelocBlock->VirtualAddress + (*pType & 0x0fff);
// pRelocAddr += dwImageBase;
// Get reloc address's File Offset
pRelocAddr = RvaToFileOff( pFile, pRelocAddr );
// DWORD test = pRelocAddr;
// Go to the Buffer offset
pRelocAddr += (DWORD)pFile;
// Get the reloc address
dwRelocAddr = *(PDWORD)pRelocAddr;
// Calculate the new address
dwRelocAddr -= dwDiffer;
// Copy to the file
*(PDWORD)pRelocAddr = dwRelocAddr;