typedef struct _IMAGE_BASE_RELOCATION {
DWORD VirtualAddress; // 分页基址(RVA)
DWORD SizeOfBlock; // 重定位块的大小
//WORD TypeOffset[1]; // 动态数组,每项大小为16位,前高4位是类型,后低12位是偏移
} IMAGE_BASE_RELOCATION,* PIMAGE_BASE_RELOCATION;
TypeOffset 高位字节的定义:
#include <Windows.h>
#include <stdio.h>
static PCH LoadFileT(PCSTR FilePath) {
// 获取文件对象
HANDLE hFile = CreateFileA(FilePath, GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
DWORD dwFileSize = GetFileSize(hFile, NULL); // 获取文件大小
PCH szBuffer = new CHAR[dwFileSize]; // 用于接收从文件读取的数据
memset(szBuffer, 0, dwFileSize); // 初始化内存
BOOL Result = ReadFile(hFile, szBuffer ,dwFileSize, NULL, NULL); // 读取数据
CloseHandle(hFile); // 关闭文件句柄
if (Result)
{
return szBuffer;
}
else {
return NULL;
}
}
// RVA 转 FOA
static DWORD RvaToFoa(DWORD dwRva, PCH szbuffer)
{
PIMAGE_DOS_HEADER pDos = PIMAGE_DOS_HEADER(szbuffer); // DOS Header
PIMAGE_NT_HEADERS64 pNt = PIMAGE_NT_HEADERS64(szbuffer + pDos->e_lfanew); // NT Header
PIMAGE_SECTION_HEADER pSection = IMAGE_FIRST_SECTION(pNt); // 节
if (dwRva < pSection->VirtualAddress)return dwRva; // 判断是否落在头部
for (size_t i = 0; i < pNt->FileHeader.NumberOfSections; i++)
{
// 区段的开始 = VirtualAddress
// 区段的结束 = VirtualAddress + VirtualSize
// 判断是否在区段
if (dwRva >= pSection[i].VirtualAddress && dwRva <= pSection[i].VirtualAddress + pSection[i].Misc.VirtualSize)
{
// 数据的 RVA - 区段的 RVA = 数据的 FOA - 区段的 FOA
// 数据的 FOA = 数据的 RVA(传参) - 区段的 RVA + 区段的 FOA
return dwRva - pSection[i].VirtualAddress + pSection[i].PointerToRawData;
}
}
}
// 从定位表相对性的区段
static PCH GetSelectName(DWORD dwRva, PCH szbuffer)
{
PIMAGE_DOS_HEADER pDos = PIMAGE_DOS_HEADER(szbuffer); // DOS Header
PIMAGE_NT_HEADERS64 pNt = PIMAGE_NT_HEADERS64(szbuffer + pDos->e_lfanew); // NT Header
PIMAGE_SECTION_HEADER pSection = IMAGE_FIRST_SECTION(pNt); // 节
if (dwRva < pSection->VirtualAddress)return NULL;
for (size_t i = 0; i < pNt->FileHeader.NumberOfSections; i++)
{
if (dwRva >= pSection[i].VirtualAddress && dwRva <= pSection[i].VirtualAddress + pSection[i].Misc.VirtualSize)
{
PCH selectName = new CHAR[IMAGE_SIZEOF_SHORT_NAME+1]{0};
memcpy(selectName, pSection[i].Name, IMAGE_SIZEOF_SHORT_NAME);
return selectName;
}
}
}
// 基址重定位表
static VOID BaseRelocationT(PCH szbuffer, PIMAGE_DATA_DIRECTORY pDataDirectory, PIMAGE_SECTION_HEADER pSection) {
PIMAGE_DATA_DIRECTORY pBaseRelocDir = pDataDirectory + IMAGE_DIRECTORY_ENTRY_BASERELOC; // 定位到重定位表
// 使用重定位表结构体指针指向重定位表所在的内存地址
PIMAGE_BASE_RELOCATION pBaseReloc = PIMAGE_BASE_RELOCATION(RvaToFoa(pBaseRelocDir->VirtualAddress, szbuffer) + szbuffer);
PWORD pwRelocData{}; // 用于接收 TypeOffset
DWORD dwItems{}; // 用于存储块个数
DWORD dwRva{}; // 用于存储TypeOffset的偏移
while (pBaseReloc->VirtualAddress && pBaseReloc->SizeOfBlock) // 判断是否存在重定位表
{
dwItems =( pBaseReloc->SizeOfBlock - 8) / 2; // 个数 = (块大小 - IMAGE_BASE_RELOCATION 大小) / TypeOffset 字节数
// TypeOffset 是个数组,每个占 2 字节大小,所以除以 2
printf("Page RVA: %X, Block Size: %X, 项目数: %d, 区段: %sn",
pBaseReloc->VirtualAddress, pBaseReloc->SizeOfBlock, dwItems, GetSelectName(pBaseReloc->VirtualAddress, szbuffer));
pwRelocData = PWORD((PCH)pBaseReloc + 0x8); // +8 是因为 TypeOffset 在两个 DWORD 之后
for (size_t i = 0; i < dwItems; i++)
{
// TypeOffset 16位, 前4位类型,后12位偏移
// 右移12位,移出去的位丢弃,空缺位用0填充,只剩下前4位
if (*(pwRelocData + i) >> 12 == IMAGE_REL_BASED_DIR64) {
dwRva = *(pwRelocData + i) & 0x0FFF; // 清空前四位,只取偏移
printf("TypeOffset: %X, 类型: DIR64, Reloc RVA: %X, Reloc FOA: %Xn",
*(pwRelocData + i), dwRva+ pBaseReloc->VirtualAddress, RvaToFoa(dwRva, szbuffer));
}
else {
printf("TypeOffset: %X, 类型: Padding(skipped)n",*(pwRelocData + i));
}
}
printf("n");
pBaseReloc = PIMAGE_BASE_RELOCATION(PCH(pBaseReloc) + pBaseReloc->SizeOfBlock); // 指向下一个重定位块PRIN
}
}
int main() {
PCH buffer = LoadFileT("C:/project/PE/EXE/x64/Debug/EXE.exe");
PIMAGE_DOS_HEADER pDos = PIMAGE_DOS_HEADER(buffer); // DOS Header
PIMAGE_NT_HEADERS64 pNt = PIMAGE_NT_HEADERS64(buffer + pDos->e_lfanew); // NT Header
PIMAGE_SECTION_HEADER pSection = IMAGE_FIRST_SECTION(pNt); // 节
PIMAGE_OPTIONAL_HEADER64 pOptionalHeader = &pNt->OptionalHeader; // 扩展文件头
PIMAGE_DATA_DIRECTORY pDataDirectory = pOptionalHeader->DataDirectory; // 获取目录表头指针
BaseRelocationT(buffer, pDataDirectory, pSection);
}
原文始发于微信公众号(走在网安路上的哥布林):基址重定位表解析(x64)
免责声明:文章中涉及的程序(方法)可能带有攻击性,仅供安全研究与教学之用,读者将其信息做其他用途,由读者承担全部法律及连带责任,本站不承担任何法律及连带责任;如有问题可邮件联系(建议使用企业邮箱或有效邮箱,避免邮件被拦截,联系方式见首页),望知悉。
- 左青龙
- 微信扫一扫
-
- 右白虎
- 微信扫一扫
-
评论