當我們找到OEP後,用ImportREC的“IATAutoSearch”按鈕,一般情況下ImportREC可以自動識別出IAT地址與大小。但如果不能自動識別,就必須手動確定IAT地址與大小,然後將IAT的RVA與Size填進ImportREC,點擊“GetImport”按鈕就可得到輸入表。
比如我們用OD打開一個notepad.upx.exe文件,來到OEP處:
隨便找一個API函數調用語句,如:
其中地址4063E4就是IAT中的一部分,在數據窗口下命令:D4063E4,顯示如下:
上圖每一組數據都是指向一個API函數,如8D2C817C就是地址:7C812C8D,在OD裏按Ctrl+G,輸入7C812C8D跳到這個地址就會發現是kernel32.GetCommandLineA函數:
IAT是一塊連續排列的數據,因此在數據窗口向上翻屏,直到出現00數據,尋找IAT起始地址:
比如我們用OD打開一個notepad.upx.exe文件,來到OEP處:
隨便找一個API函數調用語句,如:
004010D3 FF15E4634000 call [4063E4] ;kernel32.GetCommandLineA
其中地址4063E4就是IAT中的一部分,在數據窗口下命令:D4063E4,顯示如下:
上圖每一組數據都是指向一個API函數,如8D2C817C就是地址:7C812C8D,在OD裏按Ctrl+G,輸入7C812C8D跳到這個地址就會發現是kernel32.GetCommandLineA函數:
IAT是一塊連續排列的數據,因此在數據窗口向上翻屏,直到出現00數據,尋找IAT起始地址:
然後向下翻屏,尋找IAT結束地址:
爲了直觀些,你也可以這樣讓數據窗口直接顯示這些API函數,以確定IAT是否正確,在數據窗口點擊鼠標右鍵:
調整顯示格式後的數據窗口:
這樣就直觀了,IAT中每組數據指向一個API函數,各DLL之間是以000000分開的。
因此IAT範圍:0x4062E4~0x406524,大小爲0x406524-0x4062E4=0x240
如果IAT加密了,此時IAT中的地址不是指向系統DLL中的API函數了,可能指向外殼。這就十分有必要找到外殼處理IAT的代碼了,前面己說過,外殼加載時,會模擬Windows加載器,向IAT裏填充當前操作系統API函數的實際地址。所以,在IAT裏設個內存寫斷點,就可中斷到這段代碼處。
重新加載notepad.upx.exe,在IAT某個地址下內存寫斷點,這裏選擇0x4062E4這個地址設內存寫斷點,先在數據窗口下命令:D4062E4
然後選擇一個地址,點擊鼠標右鍵,下“內存寫斷點”。
此時只要有數據寫入4062E4地址處,OD就會中斷,按F9運行OD,會中斷這裏:
0040E96D >/8A02 mov al,[edx]
0040E96F .|42 inc edx
0040E970 .|8807 mov [edi],al
0040E972 .|47 inc edi
0040E973 .|49 dec ecx
0040E974 .^75F7 jnz short0040E96D
0040E96F .|42 inc edx
0040E970 .|8807 mov [edi],al
0040E972 .|47 inc edi
0040E973 .|49 dec ecx
0040E974 .^75F7 jnz short0040E96D
這段還不是處理IAT,按F9繼續執行程序,會中斷這裏:
0040E9E9 >/8A07 mov al,[edi]
0040E9EB .|47 inc edi
0040E9EC .|08C0 or al,al
0040E9EE .^|74DC je short0040E9CC
0040E9F0 .|89F9 mov ecx,edi
0040E9F2 .|57 push edi //函數名字符串
0040E9F3 .|48 dec eax
0040E9F4 . F2:AE repne scasbyteptres:[edi]
0040E9F6 .|55 push ebp //DLL模塊句柄
0040E9F7 . FF96A4EC0000call [esi+ECA4] ; kernel32.GetProcAddress
0040E9FD .|09C0 or eax,eax
0040E9FF .|7407 je short0040EA08
0040EA01 .|8903 mov [ebx],eax //EBX指向IAT,將取得的API地址填充進IAT
0040EA03 .|83C304 add ebx,4 //指向下一個地址
0040EA06 .^EBE1 jmp short0040E9E9
0040EA08 > FF96A8EC0000call [esi+ECA8]
0040E9EB .|47 inc edi
0040E9EC .|08C0 or al,al
0040E9EE .^|74DC je short0040E9CC
0040E9F0 .|89F9 mov ecx,edi
0040E9F2 .|57 push edi //函數名字符串
0040E9F3 .|48 dec eax
0040E9F4 . F2:AE repne scasbyteptres:[edi]
0040E9F6 .|55 push ebp //DLL模塊句柄
0040E9F7 . FF96A4EC0000call [esi+ECA4] ; kernel32.GetProcAddress
0040E9FD .|09C0 or eax,eax
0040E9FF .|7407 je short0040EA08
0040EA01 .|8903 mov [ebx],eax //EBX指向IAT,將取得的API地址填充進IAT
0040EA03 .|83C304 add ebx,4 //指向下一個地址
0040EA06 .^EBE1 jmp short0040E9E9
0040EA08 > FF96A8EC0000call [esi+ECA8]
上面這段就是UPX外殼填充IAT的全過程,感興趣的,動態跟蹤一下就明白了。這裏用GetProcAddress函數獲得函數地址:
FARPROCGetProcAddress(
HMODULEhModule, //DLL模塊句柄
LPCSTRlpProcName //函數名
);
HMODULEhModule, //DLL模塊句柄
LPCSTRlpProcName //函數名
);