重定位表的移动相比导出表就简单太多了,因为重定位表相当于只有一个大表。统计一下这个“大表”的大小,全部移动到新节就行了。因为是使用下一块 VirtualAddress 和 SizeOfBlock 是否全0 来判断重定位表是否结束的,所以拷贝的时候也把这8字节的全0内容也加上一起拷贝进新节。
最后修改数据目录中的 VirtualAddress 指向新节 RVA 即可。
代码有详细注释
#include "Currency.h"
#include "windows.h"
#include "stdio.h"
void h3262() //移动重定位表到新节
{
char FilePath[] = "CRACKME.EXE"; //CRACKME.EXE CrackHead.exe Dll1.dll R.DLL LoadDll.dll
char CopyFilePath[] = "CRACKMEcopy.EXE"; //CRACKMEcopy.EXE CrackHeadcopy.exe
LPVOID pFileBuffer = NULL; //会被函数改变的 函数输出之一
LPVOID* ppFileBuffer = &pFileBuffer; //传进函数的形参
int SizeOfFileBuffer;
int SizeOfNewFileBuffer;
LPVOID pNewFileBuffer = NULL;
LPVOID* ppNewFileBuffer = &pNewFileBuffer;
PIMAGE_DOS_HEADER pDosHeader = NULL;
PIMAGE_FILE_HEADER pFileHeader = NULL;
PIMAGE_SECTION_HEADER pSectionHeader = NULL;
PIMAGE_OPTIONAL_HEADER32 pOptionalHeader = NULL;
PIMAGE_BASE_RELOCATION pRelocationTable = NULL;
PIMAGE_SECTION_HEADER pNewSectionHeader = NULL; //指向最后一个节表的下一个节表,即不存在的节表作为新开辟的节表
DWORD nameFOA = NULL;
DWORD AddressOfNamesFOA = NULL;
SizeOfFileBuffer = ReadPEFile(FilePath, ppFileBuffer); //pFileBuffer即指向已装载到内存中的exe首部
/*pFileBuffer = *ppFileBuffer;*/
if (!SizeOfFileBuffer)
{
printf("文件读取失败\n");
return;
}
//Dos头
pDosHeader = (PIMAGE_DOS_HEADER)pFileBuffer; // 强转 DOS_HEADER 结构体指针
//可选PE头 简化后的处理
pOptionalHeader = (PIMAGE_OPTIONAL_HEADER32)((DWORD)pFileBuffer + pDosHeader->e_lfanew + 4 + IMAGE_SIZEOF_FILE_HEADER);
//重定向表
pRelocationTable = (PIMAGE_BASE_RELOCATION)((DWORD)pFileBuffer + RVA2FOA(pFileBuffer, pOptionalHeader->DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC].VirtualAddress));
printf("RelocationTable VirtualAddress:%x\n", pOptionalHeader->DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC].VirtualAddress);
if (!pOptionalHeader->DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC].VirtualAddress)
{
printf("该文件没有重定向表!\n");
return;
}
int i = 1;
int SizeOfRelocationTable = 0; //重定向表的大小,单位字节
while (pRelocationTable->VirtualAddress && pRelocationTable->SizeOfBlock)
{
printf("第%d个块VirtualAddress:%x\n", i, pRelocationTable->VirtualAddress);
printf("第%d个块SizeOfBlock:%x\n", i, pRelocationTable->SizeOfBlock);
SizeOfRelocationTable += pRelocationTable->SizeOfBlock;
printf("第%d个块项数:%d\n", i, (pRelocationTable->SizeOfBlock - 8) / 2);
pRelocationTable = (PIMAGE_BASE_RELOCATION)(pRelocationTable->SizeOfBlock + (DWORD)pRelocationTable);
i++;
}
SizeOfRelocationTable += 8; //加上最后全0的八字节
printf("重定位表的大小:%d\n", SizeOfRelocationTable);
//新增节,可能头抬升,因此全拷贝到pNewFileBuffer,之后上面的寻址得全部来一遍
SizeOfNewFileBuffer = AddNewSection(pFileBuffer, SizeOfFileBuffer, SizeOfRelocationTable, ppNewFileBuffer);
//重新对pNewFileBuffer来一遍
//Dos头
pDosHeader = (PIMAGE_DOS_HEADER)pNewFileBuffer; // 强转 DOS_HEADER 结构体指针
//PE头
pFileHeader = (PIMAGE_FILE_HEADER)((DWORD)pNewFileBuffer + pDosHeader->e_lfanew + 4); //NT头地址 + 4 为 FileHeader 首址
//可选PE头
pOptionalHeader = (PIMAGE_OPTIONAL_HEADER32)((DWORD)pFileHeader + IMAGE_SIZEOF_FILE_HEADER);
//首个节表
pSectionHeader = (PIMAGE_SECTION_HEADER)((DWORD)pOptionalHeader + pFileHeader->SizeOfOptionalHeader);
for (int i = 1; i < pFileHeader->NumberOfSections; i++, pSectionHeader++) //注意这里i从1开始 i < NumberOfSections
{
} //出循环后pSectionHeader指向最后一个节表,也即新的节表
pNewSectionHeader = pSectionHeader;
//旧重定向表绝对地址
pRelocationTable = (PIMAGE_BASE_RELOCATION)((DWORD)pNewFileBuffer + RVA2FOA(pNewFileBuffer, pOptionalHeader->DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC].VirtualAddress));
//拷贝重定位表 新filebuffer头+新节表中的文件偏移 作为拷贝目的地址 旧重定向表绝对地址为拷贝源
memcpy(PVOID((DWORD)pNewFileBuffer + pNewSectionHeader->PointerToRawData ), pRelocationTable, SizeOfRelocationTable);
//修改重定位表的地址值 节头 + 偏移
pOptionalHeader->DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC].VirtualAddress = pNewSectionHeader->VirtualAddress ;
//输出测试
//计算新的重定向表
pRelocationTable = (PIMAGE_BASE_RELOCATION)((DWORD)pNewFileBuffer + RVA2FOA(pNewFileBuffer, pOptionalHeader->DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC].VirtualAddress));
printf("RelocationTable VirtualAddress:%x\n", pOptionalHeader->DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC].VirtualAddress);
if (!pOptionalHeader->DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC].VirtualAddress)
{
printf("该文件没有重定向表!\n");
return;
}
i = 1;
while (pRelocationTable->VirtualAddress && pRelocationTable->SizeOfBlock)
{
printf("第%d个块VirtualAddress:%x\n", i, pRelocationTable->VirtualAddress);
printf("第%d个块SizeOfBlock:%x\n", i, pRelocationTable->SizeOfBlock);
printf("第%d个块项数:%d\n", i, (pRelocationTable->SizeOfBlock - 8) / 2);
pRelocationTable = (PIMAGE_BASE_RELOCATION)(pRelocationTable->SizeOfBlock + (DWORD)pRelocationTable);
i++;
}
MemeryToFile(pNewFileBuffer, SizeOfNewFileBuffer, CopyFilePath);
free(pNewFileBuffer);
free(pFileBuffer);
}
这里最后会再次根据新内存找到新的重定位表,然后打印重定位表内容,看是否和移动前打印的一致。用做一定的移动成功的验证。