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(); }
結果:
