C/C++ 導入表與IAT內存修正


本章教程中,使用的工具是上次制作的PE結構解析器,如果還不會使用請先看前一篇文章中對該工具的介紹,本章節內容主要復習導入表結構的基礎知識點,並通過前面編寫的一些小案例,實現對內存的轉儲與導入表的脫殼修復等。

關於Dump內存原理,我們可以使用調試API啟動調試事件,然后再程序的OEP位置寫入CC斷點讓其暫停在OEP位置,此時程序已經在內存解碼,同時也可以獲取到程序的OEP位置,轉儲就是將程序原封不動的讀取出來並放入臨時空間中,然后對空間中的節表和OEP以及內存對齊進行修正,最后將此文件在內存保存出來即可。

脫殼修復:輸入表一般分為IAT與INT,由於加殼后程序可能會加密或者破壞IAT結構,導致脫殼后IAT不一致了,脫殼修復就是使用未脫殼的源程序的輸入表覆蓋到新程序中,就這麽簡單。

解析器下載與使用:https://www.cnblogs.com/LyShark/p/12960816.html

解析 IMAGE_IMPORT_DESCRIPTOR

數據目錄表第二個成員指向輸入表,該指針在PE開頭位置向下偏移80H處,PE開始位置就是B0H , B0H+80H= 130H處。

此處存放着一個指針,00002040 即輸入表在內存中的偏移量為 2040,使用前面制作的工具可以快速定位到此處。

2040是一個RVA,需要將其轉換為磁盤文件FOA偏移才能定位到輸入表在文件中的位置,使用工具快速完成計算任務,轉換為文件偏移為 00000640

也可以這樣來找到640的位置,首先2040位於rdata,rdata的虛擬偏移是2000h,而實際偏移是600h 使用 2000h - 600h = 1a00h

將相對偏移地址2040轉為文件偏移,使用2040-1a00同樣可得出640h 用winhex打開后跳轉過去看看。

下面將重點解析一下這幾個結構的含義。

如上就是導入表中的IID數組,每個IID結構包含一個裝入DLL的描述信息,現在有兩個DLL,第三個是一個全部填充為0的結構,標志着IID數組的結束。

結構定義如下。

typedef struct _IMAGE_IMPORT_DESCRIPTOR {
    union {
        DWORD   Characteristics;            // 0 for terminating null import descriptor
        DWORD   OriginalFirstThunk;         // RVA to original unbound IAT (PIMAGE_THUNK_DATA)
    } DUMMYUNIONNAME;
    DWORD   TimeDateStamp;                  // 0 if not bound,
                                            // -1 if bound, and real date\time stamp
                                            //     in IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT (new BIND)
                                            // O.W. date/time stamp of DLL bound to (Old BIND)

    DWORD   ForwarderChain;                 // -1 if no forwarders
    DWORD   Name;
    DWORD   FirstThunk;                     // RVA to IAT (if bound this IAT has actual addresses)
} IMAGE_IMPORT_DESCRIPTOR;
typedef IMAGE_IMPORT_DESCRIPTOR UNALIGNED *PIMAGE_IMPORT_DESCRIPTOR;

以第一個字段為例:

0000 208C => OrignalFirstThunk => 指向輸入名稱表INT的RVA
0000 0000 => TimeDateStamp => 指向一個32位時間戳,默認此處為0
0000 0000 => ForwardChain => 轉向API索引,默認為0
0000 2174 => Name => 指向DLL名字的指針
0000 2010 => FirstThunk => 指向輸入地址表IAT的RVA

每個IID結構的第四個字段指向的是DLL名稱的地址,以第一個為例,其RVA是0000 2174 將其減去1a00得到文件偏移774,跳轉過去看看,調用的是USER32.dll庫。

使用工具同樣可以快速轉換出來。

上方的兩個字段OrignalFirstThunkFirstThunk都可以指向導入結構,在實際裝入中,當程序中的OrignalFirstThunk值為0時,則就要看FirstThunk里面的數據了,FirstThunk常被叫做IAT他是在程序初始化時被動態填充的,而OrignalFirstThunk常被叫做INT,他是不可改變的,之所以會保留兩份是因為,有些時候會存在反查的需求,保留兩份是為了更方便的實現。

解析 IMAGE_THUNK_DATA32

如上,我們找到了User32.dll的OrignalFirstThunk,其地址為208C,使用該值減去1A00h 得到 68Ch,在偏移為68Ch處保存的就是一個IMAGE_THUNK_DATA32數組,他存儲的內容就是指向 IMAGE_IMPORT_BY_NAME 結構的地址,最后一個元素以一串0000 0000作為結束標志,先來看一下IMAGE_THUNK_DATA32的定義規范。

typedef struct _IMAGE_THUNK_DATA32 {
    union {
        DWORD ForwarderString;      // PBYTE 
        DWORD Function;             // PDWORD
        DWORD Ordinal;
        DWORD AddressOfData;        // PIMAGE_IMPORT_BY_NAME
    } u1;
} IMAGE_THUNK_DATA32;
typedef IMAGE_THUNK_DATA32 * PIMAGE_THUNK_DATA32;

直接使用WinHex定位到68Ch處,此處就是OrignalFirstThunk中保存的INT的內容,如下圖中,出去最后一個00000000以外,一共有11個四字節,則說明User32.dll中導入了11個API函數。

再來看一下FirstThunk也就是IAT中的內容,由於User32的FirstThunk字段默認值是2010h,使用該值減去1a00h即可得到610h,此處就是IAT的內容,定位過去看看,完全一致的。

我們以第一個導入RVA地址00002110h,用該值減去1a00h得到710h,定位過去正好是LoadIconA的字符串。
接着來看第二個導入RVA地址0000211ch,用該值減去1a00h得到71c0 定位過去正好是PostQuitMessage的字符串。

如上圖,以第二個為例,前兩個字節表示的是Hint值,后面的藍色部分則是PostQuitMessage字符串,最后的0標志結束標志。

當程序被運行前,它的FirstThunk值與OrignalFirstThunk字段都指向同一片INT中,如下使用上次編寫的MyDump工具對其內存進行dump轉儲,觀察內存變化。

觀察發現 OrignalFirstThunk字段並不會發生變化,但是FirstThunk值的指向已經改變,系統在裝入內存時會自動將FirstThunk指向的偏移轉化為一個個真正的函數地址,並回寫到原始空間中,定位到輸入表RVA地址處2040h查看。

可以發現,黃色的INT並沒有變化,但是綠色的IAT則相應的發生了變化,以第一個0x766bd680則是載入內存后LoadIconA的內存地址,我們使用X64DBG跟過去看看,沒錯吧!

當系統裝入內存后,其實只會用到IAT中的地址解析,輸入表中的INT啥的就已經不需要了,此地址每個系統之間都會不同,該地址是操作系統動態計算后填入的,這也是為什么會存在導入表這個東西的原因,就是為了解決不同系統間的互通問題的。

有時我們在拖殼時,由於IAT發生了變化,所以程序會無法被正常啟動,我們Dump出來的文件可能收入表已經被破壞了,導入表不一致,我們可以使用原始的未脫殼的導入表地址對脫殼后的導入表地址進行覆蓋,來修復文件,使用修復工具修復即可。

例如dump前導入表是這樣的。

dump 后變成了這樣。

由於導入表錯誤導致dump文件無法正常運行,這是需要使用修復工具來對導入表進行修正。

修正后文件就可以正常被打開了,我們來看一下dump后的文件導入表。

是不是很清晰了,就是將原來的導入函數的RVA拷貝過來,就這麽簡單。


工具學習篇

lyshark.exe 是一個加過UPX殼的程序,現在演示如何流程化脫殼處理。

先查節表,發現UPX

定位到數據目錄表中第二個字段,也就是輸入表的存儲位置,直接使用工具計算出foa地址。

加過殼就是這樣 442cc

將內存文件轉儲出來,保存到dump.exe

跳過去看看,空的

嘗試打開文件,出現錯誤。

使用buid工具修正即可。脫殼后文件體積變大了

不過我自己搗鼓的這些脫殼工具只是用來學習的,很多殼還得借助專業的脫殼工具進行修復,我這個修不了。

既然到這份上了,來演示一下專業脫殼,先查殼,upx

壓縮殼,使用ESP定律脫掉。F8一次,ESP右擊內存窗口轉到

斷點設置硬件訪問斷點,四字節,選擇,讓程序跑起。

然后運行到jmp 即可到達OEP

獲取OEP刪除無效函數,直接dump轉儲文件。

文件轉儲打不開

使用工具修復buitIAT即可。

脫殼完成,程序可運行起來。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM