Relocation(重定位)是一种将程序中的一些地址修正为运行时可用的实际地址的机制。在程序编译过程中,由于程序中使用了各种全局变量和函数,这些变量和函数的地址还没有确定,因此它们的地址只能暂时使用一个相对地址。当程序被加载到内存中运行时,这些相对地址需要被修正为实际的绝对地址,这个过程就是重定位。
在Windows操作系统中,程序被加载到内存中运行时,需要将程序中的各种内存地址进行重定位,以使程序能够正确地运行。Windows系统使用PE(Portable Executable)文件格式来存储可执行程序,其中包括重定位信息。当程序被加载到内存中时,系统会解析这些重定位信息,并将程序中的各种内存地址进行重定位。
重定位表一般出现在DLL
中,因为DLL
都是动态加载,所以地址不固定,DLL的入口点在整个执行过程中至少要执行2次,一次是在开始时执行初始化工作,一次则是在结束时做最后的收尾工作,重定位表则是解决DLL的地址问题,为了能找到重定位表首先我们需要使用PeView
工具查询DataDirectory
数据目录表,在其中找到Base relocation
字段,里面的0x00001800
则是重定位表基地址;
我们通过使用WinHex
工具定位到0x00001800
即可看到重定位表信息,如下图中的1000
代表的是重定位RVA
地址,绿色的0104
代表的则是重定位块的长度,后面则是每两个字节代表一个重定位块,0A是重定位地址,30则是重定位的类型,以此顺序向下排列。
重定位表也是分页排列的,每一页大小都是1000
字节,通过使用FixRelocPage
命令即可查询到当前程序中的重定位块信息,并以第一个为例,查询一下起始地址RVA为1000
的页上,有哪些重定位结构,如下图所示;
其中的重定位RVA
地址0000100A
是用标黄色的1000
加上标蓝色的0xA
得到的。而修正RVA地址00003000
加上模块基地址63FF0000+3000
得到的则是第一个被修正的内存地址,读者可使用x64dbg
跳转到该程序内自行确认。
重定位表的修复原理与IAT修复完全一致,我们需要分别读入脱壳前与脱壳后的两个程序,接着通过循环正确的重定位表信息,并依次覆盖到脱壳后的程序内,以此实现对重定位表的修复功能,实现代码如下所示;
#include
#include
struct TypeOffset
{
WORD Offset : 12; // 低12位代表重定位地址
WORD Type : 4; // 高4位代表重定位类型
};
DWORD Fil服务器托管网eSize = 0; // 定义文件大小
DWORD FileBase = 0; // 保存文件的基地址
// 定义全局变量,来存储DOS,NT,Section头
PIMAGE_DOS_HEADER DosHeader = nullptr;
PIMAGE_NT_HEADERS NtHeader = nullptr;
PIMAGE_FILE_HEADER FileHead = nullptr;
// 将RVA转换为FOA的函数
DWORD RVAtoFOA(DWORD rva)
{
auto SectionTables = IMAGE_FIRST_SECTION(NtHeader); // 获取区段表
WORD Count = NtHeader->FileHeader.NumberOfSections; // 获取区段数量
for (int i = 0; i = Section_Start && rva e_magic != IMAGE_DOS_SIGNATURE)
return false;
// 获取 NT 头并判断是不是一个有效的PE文件
NtHeader = (PIMAGE_NT_HEADERS)(FileBase + DosHeader->e_lfanew);
if (NtHeader->Signature != IMAGE_NT_SIGNATURE)
return false;
// 判断是不是一个32位文件
if (NtHeader->OptionalHeader.Magic != 0x010B)
return false;
CloseHandle(Handle);
return true;
}
// 修复重定位表
void RepairFixReloc(char new_file[])
{
DWORD base = NtHeader->OptionalHeader.ImageBase;
// 1. 获取重定位表的 rva
DWORD RelocRVA = NtHeader->OptionalHeader.DataDirectory[5].VirtualAddress;
// 2. 获取重定位表
auto Reloc = (PIMAGE_BASE_RELOCATION)(FileBase + RVAtoFOA(RelocRVA));
// 3. 遍历重定位表中的重定位块,以0结尾
while (Reloc->SizeOfBlock != 0)
{
// 3.1 输出分页基址
printf("[↓] 分页基址: 0x%08X nn", Reloc->VirtualAddress);
// 3.2 找到重定位项
auto Offset = (TypeOffset*)(Reloc + 1);
// 3.3 计算重定位项的个数
// Reloc->SizeOfBlock 保存的是整个重定位块的大小 结构体 + 重定位项数组
// Reloc->SizeOfBlock - sizeof(IMAGE_BASE_RELOCATION) 得到单个数组大小
// 上面的结果 2 = 重定位项的个数,原因是重定位项的大小为两个字节
DWORD Size = (Reloc->SizeOfBlock - sizeof(IMAGE_BASE_RELOCATION)) / 2;
// 3.4 遍历所有的重定位项
for (DWORD i = 0; i VirtualAddress; // 获取要重定位的地址所在的RVA
DWORD foa = RVAtoFOA(rva); // 获取要重定位的地址所在的FOA
DWORD fa = foa + FileBase; // 获取要重定位的地址所在的fa
DWORD addr = *(DWORD*)fa; // 获取要重定位的地址
DWORD new_addr = addr - base + 0x1500000; // 计算重定位后的数据: addr - oldbase + newbase
// 将重定位后的数据写回缓冲区(文件)
if (Offset[i].Type == 3)
*(DWORD*)fa = new_addr;
printf("t [->] 重定位RVA: 0x%08X | 重定位FOA: 0x%08X | 重定位地址: 0x%08X | 修正地址: 0x%08X n", rva, foa, addr, new_addr);
}
// 找到下一个重定服务器托管网位块
Reloc = (PIMAGE_BASE_RELOCATION)((DWORD)Reloc + Reloc->SizeOfBlock);
}
// 保存修正后的文件
NtHeader->OptionalHeader.ImageBase = 0x1500000;
// 打开一个新文件
HANDLE new_handle = CreateFileA(new_file, GENERIC_WRITE, NULL, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
if (new_handle == INVALID_HANDLE_VALUE)
return;
DWORD OperSize = 0;
// 保存修正好的程序
BOOL ret = WriteFile(new_handle, (LPVOID)FileBase, FileSize, &OperSize, NULL);
if (ret == TRUE)
{
printf("nn");
CloseHandle(new_handle);
printf("[*] 修复 %s 文件 t 写入基址: %08X t 总长度: %d t 写入长度: %d n", new_file, FileBase, FileSize, OperSize);
}
}
void Banner()
{
printf(" ____ _ _ _ ____ _ n");
printf("| __ ) _ _(_) | __| | | _ ___| | ___ ___ n");
printf("| _ | | | | | |/ _` | | |_) / _ |/ _ / __|n");
printf("| |_) | |_| | | | (_| | | _
运行上述程序,读者可自行传入脱壳前的程序与脱壳后的程序,此时则会实现自动替换,如下图所示;
本文作者: 王瑞
本文链接: https://www.lyshark.com/post/3c1b31b5.html
版权声明: 本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
服务器托管,北京服务器托管,服务器租用 http://www.fwqtg.net
机房租用,北京机房租用,IDC机房托管, http://www.fwqtg.net
今天来讲下参数化,具体是什么意思呢,举个例子 比如我们要测试登录功能,第一步会填写账号,第二步会填写密码,这是一条完整的操作,但是其中会有很多条用例比如账号错误、密码错误、账号为空、密码为空的各种情况,但是在输入账号、密码的操作都是一样的 我们不能一条…