四、重定位表(Relocation Table)解析
1、重定位表的作用
重定位表(Relocation Table)用於在程式載入到記憶體中時,進行記憶體地址的修正。為什麼要進行記憶體地址的修正?我們舉個例子來說:test.exe可執行程式需要三個動態連結庫dll(a.dll,b.dll,c.dll),假設test.exe的ImageBase為400000H,而a.dll、b.dll、c.dll的基址ImageBase均為1000000H。
那麼作業系統的載入程式在將test.exe載入進記憶體時,直接複製其程式到400000H開始的虛擬記憶體中,接著一一載入a.dll、b.dll、c.dll:假設先載入a.dll,如果test.exe的ImageBase + SizeOfImage + 1000H不大於1000000H,則a.dll直接複製到1000000H開始的記憶體中;當b.dll載入時,雖然其基址也為1000000H,但是由於1000000H已經被a.dll佔用,則b.dll需要重新分配基址,比如載入程式經過計算將其分配到1200000H的地址,c.dll同樣經過計算將其載入到150000H的地址。如下圖所示:
但是b.dll和c.dll中有些地址是根據ImageBase固定的,被寫死了的,而且是絕對地址不是相對偏移地址。比如b.dll中存在一個call 0X01034560,這是一個絕對地址,其相對於ImageBase的地址為δ = 0X01034560 - 0X01000000 = 0X34560H;而此時的記憶體中b.dll存在的地址是1200000H開始的記憶體,載入器分配的ImageBase和b.dll中原來預設的ImageBase(1000000H)相差了200000H,因此該call的值也應該加上這個差值,被修正為0X01234560H,那麼δ = 0X01234560H - 0X01200000H = 0X34560H則相對不變。否則call的地址不修正會導致call指令跳轉的地址不是實際要跳轉的地址,獲取不到正確的函式指令,程式則不能正常執行。
由於一個dll中的需要修正的地址不止一兩個,可能有很多,所以用一張表記錄那些“寫死”的地址,將來載入進記憶體時,可能需要一一修正,這張表稱作為重定位表,一般每一個PE檔案都有一個重定位表。當載入器載入程式時,如果載入器為某PE(.exe、.dll)分配的基址與其自身預設記錄的ImageBase不相同,那麼該程式檔案載入完畢後就需要修正重定位表中的所有需要修正的地址。如果載入器分配的基址和該程式檔案中記錄預設的ImageBase相同,則不需要修正,重定位表對於該dll也是沒有效用的。比如test.exe和a.dll的重定位表都是不起作用的(由於一般情況.exe執行時被第一個載入,所以exe檔案一般沒有重定位表,但是不代表所有exe都沒有重定位表
2、重定位表的結構與解析:
知道了重定位表的作用,現在我們來分析一個重定位表在PE檔案中是如何存在的,首先來看看描述重定位表的結構:
typedef struct _IMAGE_BASE_RELOCATION {
DWORD VirtualAddress;//RVA
DWORD SizeOfBlock;
} IMAGE_BASE_RELOCATION,* PIMAGE_BASE_RELOCATION;
#define IMAGE_SIZEOF_BASE_RELOCATION 8
- 1
- 2
- 3
- 4
- 5
該結構體有兩個成員:一個是地址,一個是大小。如下圖所示:一個重定位表由多個大小SizeOfBlock的Block組成,(不同塊的SizeOfBlock大小不一)。每一個塊記錄了1000H即4KB大小的記憶體中需要重定位資訊的地址(一頁大小),這些地址以VirtualAdress為該頁的基址,偏移地址佔兩個位元組(1000H最多需要12bit即可:0~FFFH)。所以兩個位元組的低12位為偏移地址,而高4位就是一個標記,當此標記為0011(3)時低12為才有效,否則該2個位元組可能是為了對齊而產生的,並且為對齊而產生的位元組其值全為0。
由於重定位表的SizeOfBlock大小不確定,新的Block的重定位資訊的結構體接著上一個Block4位元組對齊後開始,而當出現一個_IMAGE_BASE_RELOCATION結構體的值全為0時,表明重定位表結束。
3、程式碼解析PE的Relocation Table :
//將解析出來的重定位表資訊寫入檔案
void PETool::print_BaseRelocation()
{
fprintf(fp_peMess, "重定位表(Relocation):\n");
if(dataDir[5].VirtualAddress == 0){
fprintf(fp_peMess, "\t不存在重定位表!\n");
return;
}
BYTE secName[9] = {0};
//rec指向重定位表第一個IMAGE_BASE_RELOCATION結構體
IMAGE_BASE_RELOCATION * rec = (IMAGE_BASE_RELOCATION *)(pFileBuffer + RVAToFOA(dataDir[5].VirtualAddress));
for(int i = 1; rec->SizeOfBlock && rec->VirtualAddress; i++){
DWORD foa = RVAToFOA(rec->VirtualAddress);
DWORD size = (rec->SizeOfBlock - 8) / 2;
//確定該結構體所處節,並獲取節名稱
IMAGE_SECTION_HEADER * section = section_header;
for(int t = 0; t < sectionNum; t++){
DWORD lower = RVAToFOA(section[t].VirtualAddress);
DWORD upper = RVAToFOA(section[t].VirtualAddress) + section[t].Misc.VirtualSize;
if(foa >= lower && foa <= upper){
memcpy(secName, section[t].Name, 8);
break;
}
}
//列印該頁主要資訊
fprintf(fp_peMess, "\tIndex[%d]\tsection[%s]\tOffset[%08X]\tItems[%dD:%XH]\t【Block】\n", i, secName, foa, size, size);
//列印一個頁中所有重定位地址與資訊
WORD * recAddr = (WORD *)((BYTE *)rec + 8);//recAddr指向重定位表結構體後的首位元組
fprintf(fp_peMess, "\t\tindex\toffset\t\ttype\t【Block Items】\n");
for(DWORD j = 0; j < size; j++){
DWORD offset = (recAddr[j] & 0X0FFF) + foa;//低四位是偏移地址
WORD type = recAddr[j] >> 12;//高四位是有效判斷位
if(type == 0){
fprintf(fp_peMess, "\t\t[%d] \t[--------]\tABSOLUTE[%d]\n", j+1, type);
continue;
}
fprintf(fp_peMess, "\t\t[%d] \t[%08X]\tHIGHLOW[%d]\n", j+1, offset, type);
}
memset(secName, 0, 9);
rec = (IMAGE_BASE_RELOCATION *)((BYTE *)rec + rec->SizeOfBlock);//進行下一頁的判斷
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
由於列印的資訊過長(一個只包含加減乘除四個簡單函式的庫,其重定位資訊的地址就有2500多條),這裡只提出一部分比較短的Block資訊,可以很明顯地看到當需要重定位資訊的記錄長度是4Byte的倍數時,不存在高四位為0000的情況,當其不為4的倍數時,就有一個因對齊而產生的資料:
//…
Index[21] section[.text] Offset[00015000] Items[16D:10H] 【Block】
index offset type 【Block Items】
[1] [00015002] HIGHLOW[3]
[2] [00015008] HIGHLOW[3]
[3] [0001500E] HIGHLOW[3]
[4] [00015014] HIGHLOW[3]
[5] [0001501A] HIGHLOW[3]
[6] [00015020] HIGHLOW[3]
[7] [00015026] HIGHLOW[3]
[8] [0001502C] HIGHLOW[3]
[9] [00015032] HIGHLOW[3]
[10] [00015038] HIGHLOW[3]
[11] [0001503E] HIGHLOW[3]
[12] [00015044] HIGHLOW[3]
[13] [0001504A] HIGHLOW[3]
[14] [00015050] HIGHLOW[3]
[15] [00015056] HIGHLOW[3]
[16] [——–] ABSOLUTE[0]
//…
Index[23] section[.rdata] Offset[0002D000] Items[12D:CH] 【Block】
index offset type 【Block Items】
[1] [0002D804] HIGHLOW[3]
[2] [0002D808] HIGHLOW[3]
[3] [0002D810] HIGHLOW[3]
[4] [0002D814] HIGHLOW[3]
[5] [0002D874] HIGHLOW[3]
[6] [0002D878] HIGHLOW[3]
[7] [0002D884] HIGHLOW[3]
[8] [0002D888] HIGHLOW[3]
[9] [0002D8E4] HIGHLOW[3]
[10] [0002D8E8] HIGHLOW[3]
[11] [0002D8F0] HIGHLOW[3]
[12] [0002D8F4] HIGHLOW[3]
//…
Index[25] section[.data] Offset[00031000] Items[4D:4H] 【Block】
index offset type 【Block Items】
[1] [00031CC0] HIGHLOW[3]
[2] [00031CC8] HIGHLOW[3]
[3] [00031CCC] HIGHLOW[3]
[4] [——–] ABSOLUTE[0]
//…
相關推薦
四、重定位表(Relocation Table)解析
1、重定位表的作用 重定位表(Relocation Table)用於在程式載入到記憶體中時,進行記憶體地址的修正。為什麼要進行記憶體地址的修正?我們舉個例子來說
PE檔案學習筆記(四):重定位表(Relocation Table)解析
1、重定位表的作用 重定位表(Relocation Table)用於在程式載入到記憶體中時,進行記憶體地址的修正。為什麼要進行記憶體地址的修正?我們舉個例子來說:test.exe可執行程式需要三個動態連結庫dll(a.dll,b.dll,c.dll),假設te
PE檔案學習筆記(三):匯出表(Export Table)解析
資料目錄(Data Directory)有16個_IMAGE_DATA_DIRECTORY結構體元素,該結構體陣列是可選PE頭中最後一個成員。這十六個元素分別儲存了不同資訊,分別是:匯入表、匯出表、資源、異常資訊、安全證書、重定位表、除錯資訊、版權所有、全域性指
四、PTA實驗作業(指針)
流程 描述 .com 接下來 小數 ets ngs 字母 移動 6-1 計算兩數的和與差(10 分) 本題要求實現一個計算輸入的兩數的和與差的簡單函數。 函數接口定義: void sum_diff( float op1, float op2, float *psum,
Postgresql中臨時表(temporary table)的特性和用法
.net 他會 acl tmp 就會 fonts 功能 不能 聲明 熟悉Oracle的人,相比對臨時表(temporary table)並不陌生,很多場景對解決問題起到不錯的作用,開源庫Postgresql中,也有臨時表的概念,雖然和Oracle中臨時表名字相同,使用方法和
資料結構:雜湊表(Hash Table)
雜湊表定義 雜湊表是一種根據關鍵碼去尋找值的資料對映結構,該結構通過把關鍵碼對映的位置去尋找存放值的地方。 本質是一個數組,陣列中每一個元素稱為一個箱子(bin),箱子中存放的是鍵值對。 雜湊表的儲存過程如下: 根據 key 計算出它的雜湊值 h。 假設箱子的個數
雜湊表(hash table)及其應用舉例
雜湊表最主要的有點在於我們利用它能夠在 O(1) (直接根據線性空間的下標進行索引)時間查詢某一元素,是效率最高的查詢方式,其缺點是需要額外的空間實現雜湊表。 1. 百分制成績 建立成績與出現
JVM之卡表(Card Table)
我們知道,JVM在進行垃圾收集時,需要先標記所有可達物件,然後再清除不可達物件,釋放記憶體空間。那麼,如何快速的找到所有可達物件呢? 最簡單粗暴的實現,就是每次進行垃圾收集時,都對整個堆中的所有物件進行掃描,找到所有存活物件。邏輯是簡單,但效能比較差。 簡單粗暴的實現方式,通常都是不可取的。那JVM是如何
程式的重定位問題(程式裝入)
在多道程式環境下,要使程式執行,必須先為程式建立程序。而建立程序的第一件事就是:將程式和資料裝入記憶體。如何將一個使用者源程式變成可在記憶體中執行的程式,通常都要進過幾個步驟:1.編譯:由compiler將源程式編譯成若干個目標模組(Object Module);2.連結
工具介面標準(TIS)可執行連結格式(ELF)規範-卷I-字串表(String Table)
本文是對Tool Interface Standard (TIS) Executable and Linking Format (ELF) Specification Version 1.2的翻譯 工具介面標準(TIS)可執行連結格式(ELF)規範版本 1.2翻譯以中英對照
UE4中使用資料表(Data Table)
做過遊戲的應該都清楚,如果遊戲稍微有點規模,那麼使用資料驅動來做遊戲一般是必不可少的一步,一般也就是策劃通過本表的方式來解決。下面我們來簡單說一下UE4中如何使用DataTable來實現資料驅動開發。 顧名思義,資料表就是以有意義且有用的方式將各種相關的資料歸類的表格, 其中,資料欄位可以是任何有效的 U
Java:方法的虛分派(virtual dispatch)和方法表(method table)
本文通過介紹 Java 方法呼叫的虛分派,來加深對 Java 多型實現的理解。需要預先理解 Java 位元組碼和 JVM 的基本框架。 虛分配(Virtual Dispatch) 首先從位元組碼中對方法的呼叫說起。Java 的 bytecode 中方法的呼叫實現分為四種指令: 1.invokevir
什麼是重定位?為什麼需要重定位?(嵌入式下)
一、必須知道的幾個概念。 1、連結地址和執行地址。 ①執行地址,顧名思義就是程式執行的時候的地址,也就是你用工具將程式碼下載到RAM的那個地址,也叫載入地址。 ②連結地址,由連結指令碼指定的地址。為什麼需要連結指令碼指定地址呢?你想一下,在c語言程式設計中
PE檔案格式學習(八):基址重定位表
1.簡介 基址重定位表位於資料目錄表中的第六個,它位於安全表的後面。 這個表的作用是用來索引那些需要重定位的資料的。當系統發現DLL的真實載入基址跟PE檔案中的ImageBase中的值不一樣時,就會啟用基址重定位表修復一些資料的地址。我們知道一個程式中可能包含多個DLL,因此有可
PE檔案解析-輸入表、輸出表與重定位表
一、 輸入表 1、輸入表地址定位 PE檔案頭可選映像頭中資料目錄表的第二成員指向輸入表,輸入表以一個 IAMGE_IMPORT_DESCRITPTOR 陣列開始,每個被PE檔案隱式地連結進來的DLL都有一個IID,在這個陣列中沒有欄位指出該結構陣列的項數,但他最後一
四、內核啟動(二)
clu 更新 mage onf 指針 內核編譯 try ext 內存空間 4.1 MMU設置續 上一節分析到調用 __armv4_mmu_cache_on,執行如下,這裏我們要分析 set_mmu 函數 4.1.1 __setup_mmu 前文已經分析過在內
解析PE資源表與重定位表
PE #include<Windows.h> #include<iostream> #include<stdio.h> #include<stdlib.h> #include<commdlg.h> using namespace std; DWORD
四、保護工作表
.com 鎖定 bsp 輸入 TP image bubuko 分享 技術分享 選中允許修改的單元格,右擊“設置單元格格式”,在保護一欄將“鎖定取消”;“審閱”——》保護工作表——》輸入密碼——》再次輸入密碼。這樣獎金就無法修改 四、保護工作表
銳捷7716、7708抓流表(抓包)
end http -- rec any 表示 字符 系列 int 一、流表的含義:Pr SrcAddr DstAddr SrcPort DstPort Vrf SendBytes RecvBytes St6 172.18.15
PE知識復習之PE的重定位表
exe ati 全局 很多 擴展 寫到 技術分享 準備 知識 PE知識復習之PE的重定位表 一丶何為重定位 重定位的意思就是修正偏移的意思. 如一個地址位 0x401234 ,Imagebase = 0x400000 . 那麽RVA就是 1