DLL注入指的是向運作中的其他程序強制插入特定的DLL檔案。
可以通過修改靜态的PE檔案,修改輸入表結構,使得程式執行時載入特定的DLL檔案。
通常可執行檔案需要使用其他DLL檔案中的代碼或資料,這些DLL檔案相關的資訊會儲存在輸入表中,是以我們通過修改PE檔案中輸入表相應的資訊,即可實作PE檔案在運作時自動載入特定的DLL檔案。
pe結構分析之手工段表建構--該實驗,明确段結構在windows PE程式中的作用,掌握段在檔案和記憶體中加載的過程。實操請複制下方連結
https://www.hetianlab.com/expc.do?ce=d5bc0c11-a182-474b-8ac1-251194174436?pk_campaign=weixin-wemedia#stu
使用PEview工具可以清晰的看到程式輸入表的資訊
【——全網最全的網絡安全學習資料包分享給愛學習的你,關注我,私信回複“領取”擷取——】
1.網絡安全多個方向學習路線
2.全網最全的CTF入門學習資料
3.一線大佬實戰經驗分享筆記
4.網安大廠面試題合集
5.紅藍對抗實戰技術秘籍
6.網絡安全基礎入門、Linux、web安全、滲透測試方面視訊
輸入表
由于需要修改輸入表資訊,是以這裡簡單介紹一下輸入表的結構。
在PE檔案的可選頭中存在這資料目錄項, 裡面記載了輸出表、輸入表等關鍵資訊的偏移及大小。那麼理所當然的PE檔案在執行時也會通過資料目錄項裡的資訊去找尋輸入表。
輸入表是由IMAGE_IMPORT_DESCRIPTOR結構的數組組成,簡稱IID,沒有特定的成員指出IID項數,但是會由全為0的IID結構作為結束。
上圖可以看出輸入表的起始位址為0x1B1C4,這是RVA(相對偏移位址)位址,我們需要轉化為檔案的偏移位址才能夠在檔案中找到相應的内容。工具中提供了RVA與檔案偏移位址的轉換或者自行計算。
從上圖可以看出IID結構确實是由全為0的IID結構作為結束。
輸入表結構
IID結構的字段成員如下,其中OriginalFirstThunk、Name以及FirstThunk成員是我們添加DLL檔案的關鍵。
IMAGE_IMPORT_DESCRIPTOR
union
characteristics DWORD
OriginalFirstThunk DWORD //指向IMAGE_THUNK_DATA結構的數組
ends
TimeDateStamp DWORD //時間标志
ForwarderChain DWORD //一般為0
Name DWORD //指向DLL名稱的指針
FirstThunk DWORD//指向IMAGE_THUNK_DATA結構的數組
IMAGE_IMPORT_DESCRIPTOR
在PE檔案尚未執行過時,OriginalFirstThunk與FirstThunk字段指向相同的結構,差別在于OriginalFirstThunk不可以重寫,而FirstThunk可以被重寫,當PE檔案執行後FirstThunk指向的結構會用于存放輸入函數的真實位址。是以我們修改時将OriginalFirstThunk與FirstThunk字段指向同個位址即可。而Name字段存放的是指向DLL檔案名稱的指針。
OriginalFirstThunkFirstThunkName指向IMAGE_THUNK_DATA結構的數組指向IMAGE_THUNK_DATA結構的數組指向DLL名稱的指針
IMAGE_THUNK_DATA
IMAGE_THUNK_DATA
union u1
ForwarderString DWORD //指向一個轉向者字元串的RVA
Function DWORD //被輸入的函數的記憶體位址
Oridinal DWORD //被輸入的API的序數值
AddressOfData DWORD //指向IMAGE_IMPORT_BY_NAME
IMAGE_THUNK_DATA
IMAGE_THUNK_DATA結構在不同情況下的成員不同,但是重點關注AddresOfData字段,該字段指向IMAGE_IMPORT_BY_NAME結構,該結構記錄的輸入函數的名稱。當IMAGE_THUNK_DATA值的雙字的最高位為0時,表示函數以字元串類型的函數名方式輸入。是以構造時高兩個位元組為0,低兩個位元組為IMAGE_IMPORT_BY_NAME結構位址即可。
IMAGE_IMPORT_BY_NAME
IMAGE_IMPORT_BY_NAME STRUCT
Hint WORD //忽略設定為0
Name BYTE //輸入函數名稱
IMAGE_IMPORT_BY_NAME
IMAGE_IMPORT_BY_NAME結構的高兩位元組的值忽略,後門跟着的資料直接填入DLL檔案中輸出的函數名稱,即PE檔案運作時會使用到DLL檔案中函數的名稱。
修改PE檔案
這裡準備兩個檔案
- 檔案一:HelloWorld.exe,該檔案僅僅是簡單在螢幕輸出HelloWorld!!!的字元
- 檔案二:待注入的DLL檔案,show.dll,該DLL檔案的功能可以根據實際情況而定,這裡我準備的DLL檔案可以簡單的彈出一個對話框。
将HelloWorld.exe檔案拖入PEview工具中,檢視輸入表内容。可以看到并沒有載入show.dll檔案。
運作HelloWorld.exe檔案
開始修改HelloWorld.exe檔案的思路
- 需要在輸入表中添加額外的IID結構,該IID成員的資訊為show.dll檔案的資訊
- 由于需要添加IID成員,需要觀察原始輸入表是否由額外的空間可以容納新的IID結構,若沒有則可以選擇
- 檔案中的空白區域
- 檔案末尾添加新節區
現在觀察HelloWorld.exe檔案的輸入表,可以看見在輸入表的結尾處緊跟着的是一串資料,并且大機率不是無用的資料,若我們直接在輸入表結尾處添加新的IID結構必定會破壞原檔案的結構,導緻程式無法正常運作。
是以選擇在找空白處,因為PE檔案需要對齊,是以會使用大量的空字元進行填充。空白區域可以任取,但是需要記住選取的位址因為後續需要用到。并且我們需要觀察該空白區域是否會被載入到記憶體中去。我們這裡選擇的是idata段末尾位置,是以需要去查詢idata段資訊。
如下圖所示,檔案中idata段的大小比映射到記憶體中的大小更大,是以我們可以利用這段內插補點填充僞造輸入表。這裡選擇檔案偏移0x8960作為輸入表的起始位址。不能将0x8950作為其實位址,這樣KERNEL32.DLL字元串會缺失截斷符,運作時會提示找不到該DLL檔案。
首先将原輸入表的資料複制下來,寫入檔案偏移0x8960處,新增一個IID結構,Name字段填入DLL檔案的名字,即show.dll,而OriginalFirstThunk與FirstThunk字段填入填入IMAGE_IMPORT_BY_NAME結構體的位址,IMAGE_IMPORT_BY_NAME的内容填入輸入函數的名稱,并且高兩個位元組需要為0。這裡所有填入的位址都為RVA位址,是以需要将檔案位址轉化為RVA位址填入。
接着需要改寫idata的權限,前面說到FirstThunk在PE檔案運作後是會被改寫的,是以輸入表所在的區段需要具有寫權限。可以看到idata不具備寫權限,是以需要将寫權限加上。
0x80000000為寫權限的标志位,是以将原來的資料或上0x80000000即可
修改後為0xC0000000
最後由于修改了輸入表結構以及所在位址并且新增了一個IID結構是以需要去資料目錄項的位置修改輸入表的位址及大小。
使用PEView工具檢視修改後的檔案,能夠發現修改後的檔案使用工具依然能夠識别出來,證明沒有把檔案修壞。
最後執行程式,發現show.dll檔案成功注入
參考文章
- 加密與解密
- 逆向工程核心原理