1.创建一个用来试验的目标dll
头文件:
#if !defined(AFX_HELLO_H__87AA4900_2935_4604_AFB2_7CD004B103D8__INCLUDED_) #define AFX_HELLO_H__87AA4900_2935_4604_AFB2_7CD004B103D8__INCLUDED_ #if _MSC_VER > 1000 #pragma once #endif #include "stdio.h" #define ONE "hello" #define TWO "Jim" extern "C" _declspec(dllexport) void __stdcall hello(); //导出函数 #endif
实现:
#include "Hello.h" #define THREE "nice to meet you" void __stdcall hello(){ printf("%s %s %s",ONE,TWO,THREE); }
该dll提供一个函数hello();作用是输出“hello jim nice to meet you”;
在main中测试dll:
复制编译后生成的.lib和.dll文件到测试工程目录下;
导入dll,这里使用静态导入;
使用dll中的函数;
代码:
#include <stdio.h> #pragma comment(lib, "HelloDll.lib") //加载dll extern "C" __declspec(dllimport) void __stdcall hello(); //引入dll中的函数 int main(){ hello(); //使用dll中的函数 getchar(); }
结果:

2.手动查重定向表
用winhex打开dll;
重定向表在可选pe头最后的一个结构数组中;
该结构数组有16个结构,每个结构各两个元素,各占4位;前4位表示RVA地址;
重定向表为第6个结构;
查找技巧:可以看到右边的注释,找到.text很明显是节表开始的地方;
.text前面就是可选pe头的结尾;可选pe头最后的结构数组也就是数据目录占8X16个字节,也就是往上数8行即可找到数据目录开始的地方;
找到第6个结构,可以看到:重定向表的RVA是37000;

将RVA转为FOA;
用pe工具打开节表信息,可以看到37000对应的FOA正好是35000;
因为37000是.reloc节的开始处;也就是找文件中.reloc节偏移为0的地方,可以看到是35000;

继续看winhex,35000处就是重定向表开始的地方:
可以看到重定向表有多个块;
第一个块的VirtualAddress是1000;SizeOfBlock是D8;
也就是说,到350D8之后就是下一个重定位表,VirtualAddress为2000,SizeOfBlock是1000;
以此类推,直到VirtualAddress和SizeOfBlock都是0的时候也就是最后一个重定位表;

3.用程序解析重定位表
大体思路:
将dll读入内存;
通过可选pe头获取重定位表的RVA,因为实在文件镜像中操作,需要将RVA转为FOA;
通过FOA找到重定位表;
循环解析重定位表的每一个块,判断最后一个重定位表的条件是VirtualAddress和SizeOfBlock都是0;
在每个块中循环输出每个可能需要修改的地址的RVA;RVA=的VirtualAddress + 具体项的后12位;
具体项的个数 = (SizeOfBlock - 8)/2,也就是循环这些次数;
具体项的高4位表示是否为有效具体项,如果是3则表示有效,需要标记出来;
代码:
#include "stdafx.h" #include "PeTool.h" #define SRC "C:\\Users\\Administrator\\Desktop\\HelloDll.dll" //打印重定位表信息 void printRelocInfo(){ //定义pe头结构指针 PIMAGE_DOS_HEADER dosHeader = NULL; //dos头指针 PIMAGE_FILE_HEADER peHeader = NULL; //pe头指针 PIMAGE_OPTIONAL_HEADER32 opHeader = NULL; //可选pe头指针 PIMAGE_DATA_DIRECTORY dataDir = NULL; //数据目录指针 PIMAGE_BASE_RELOCATION relocDir= NULL; //重定位表指针 //1.将dll读入内存 LPVOID pFileBuffer = NULL; ReadPEFile(SRC, &pFileBuffer); if(!pFileBuffer){ printf("读取Dll失败\n"); return; } //2.找到重定位表 dosHeader = (PIMAGE_DOS_HEADER)pFileBuffer; if(dosHeader->e_magic != IMAGE_DOS_SIGNATURE){ printf("不是有效MZ标记\n"); free(pFileBuffer); return; } if(*((PDWORD)((DWORD)pFileBuffer + dosHeader->e_lfanew)) != IMAGE_NT_SIGNATURE){ printf("不是有效PE标记\n"); free(pFileBuffer); return; } peHeader = (PIMAGE_FILE_HEADER) ((DWORD)pFileBuffer + dosHeader->e_lfanew + 4); opHeader = (PIMAGE_OPTIONAL_HEADER32) ((DWORD)peHeader + IMAGE_SIZEOF_FILE_HEADER); dataDir = opHeader ->DataDirectory; if(!((dataDir + 5)->VirtualAddress)){ printf("该pe文件没有重定位表\n"); free(pFileBuffer); return; } relocDir = (PIMAGE_BASE_RELOCATION)((DWORD)pFileBuffer + RvaToFileOffset(pFileBuffer, (dataDir + 5)->VirtualAddress)); //3.循环解析重定位表 int i = 0; while(relocDir->SizeOfBlock){ //重定位表的块大小为0时是最后一个块 printf("*************第%d个重定位表块************\n", i+1); printf("VirtualAddress:%x\n",relocDir->VirtualAddress); printf("SizeOfBlock:%x\n",relocDir->SizeOfBlock); printf("#####要修改的地址#####\n"); printf("RVA\t前4位的值\n"); //获取具体项的数量 int j = (relocDir->SizeOfBlock -8)/2; //循环解析每个具体项 PWORD item = (PWORD)((DWORD)relocDir + 8); for(int k=0;k<j;k++){ //获取具体项低12位 WORD low = item[k] & 0x0fff; //获取高4位的值 WORD hig = (item[k] & 0xf000) >> 12; DWORD rva = (DWORD)low + relocDir->VirtualAddress; printf("%x\t%d\n",rva,hig); } //找到下一个块 relocDir = (PIMAGE_BASE_RELOCATION) ((DWORD)relocDir + relocDir->SizeOfBlock); i++; } //4.释放内存 free(pFileBuffer); return; } int main(int argc, char* argv[]) { printRelocInfo(); getchar(); }
结果:
