通用ShellCode的編寫與呼叫
首先,我們的ShellCode程式碼需要自定位,因為我們的程式碼並不是一個完整的EXE可執行程式,他沒有匯入表無法定位到當前系統中每個函式的虛擬地址,所以我們直接獲取到Kernel32.dll的基地址,裡面的GetProcAddr這個函式,獲取的方式有很多,第一種是暴力搜尋,第二種通過遍歷程序的TEB結構來實現,我們使用第二種方式嘗試,一旦獲取到該函式,就可以動態的呼叫任何想要的函數了。
獲取DLL模組基地址
首先開啟WinDbg載入符號連結檔案,輸入 srv*https://www.blib.cn/symbols
1.首先FS暫存器裡面儲存的是TEB結構,TEB是執行緒環境快,裡面的PET。
TEB的偏移位置30h處,存放的是PEB執行緒環境快。
接著解析一下 dt _peb 0026b000
裡面的0C欄位是LDR,一個指向_PEB_LDR_DATA
的結構陣列。
PEB_LDR_DATA 結構體偏移位置為 0x1c 的地方存放著指向模組初始化連結串列的頭指標 InInitializationOrderModuleList,該指標指向了一個雙向連結串列。
模組初始化連結串列 InInitializationOrderModuleList 中按順序存放著PE裝入執行時初始化模組的資訊,第一個連結串列節點是 ntdll.dll,第二個連結串列結點就是kernel32.dll可以先看看 InInitializationOrderModuleList 中的內容。
上圖中的 004e3278 儲存的是第一個連結串列節點的指標,通過dd 004e3278解析這個結點,可發現如下地址0x773a0000就是ntdll.dll的基地址,而 004e3b20 則是下一個模組的指標
繼續跟隨 004e3b20 跟進後的76a90000就是kernel32.dll的基地址,而下一個地址的指標則是004e3760以此類推來遍歷。
最後我們通過!peb命令來驗證一下,如下會發現第一個對上了,這裡的kerlel32.dll其實是kernelbase.dll 這個dll是轉向dll中轉到kernel32.dll中,64位系統特有的。
通過上方的除錯我們可得到公式,接著通過編寫一段彙編程式碼來實現自動的遍歷出 kernel32.dl 的基址。
include windows.inc
include kernel32.inc
includelib kerbcli.lib
assume fs:nothing
.code
main PROC
xor eax,eax
xor edx,edx
mov eax,fs:[30h] ; 得到PEB結構地址
mov eax,[eax + 0ch] ; 得到PEB_LDR_DATA結構地址
mov esi,[eax + 1ch] ; 得到 InInitializationOrderModuleList
lodsd ; 得到KERNEL32.DLL所在LDR_MODULE結構的
mov eax,[eax] ; Windows 7 以上要將這裡開啟
mov edx,[eax + 8h] ; 得到BaseAddress,既Kernel32.dll基址
ret
main ENDP
END main
通過使用C語言也可以實現拿到Kernel32的基地址.
#include <windows.h>
#include <stdio.h>
int main(int argc, char * argv[])
{
DWORD *PEB = NULL;
DWORD *Ldr = NULL;
DWORD *Init = NULL;
DWORD *Kernel32 = NULL;
__asm
{
mov eax, fs:[0x30]
mov PEB,eax
}
printf("得到PEB指標 = %x \n", PEB);
Ldr = *(DWORD **)((unsigned char *)PEB + 0x0c);
printf("得到LDR結構指標 = %x \n", Ldr);
Init = *(DWORD **)((unsigned char *)Ldr + 0x1c);
printf("得到InInitializationOrderModuleList結構指標 = %x \n", Init);
Kernel32 = *(DWORD **)((unsigned char *)Init + 0x08);
printf("得到Kernel32的基地址 = %x \n", Kernel32);
system("pause");
return 0;
}
獲得映象基地址: 我們來擴充套件一個知識點,首先我們這次想要獲得映象基地址,如何解析結構?
首先映象基地址,在PEB結構中,我們先來獲取到其偏移地址。
此時我們知道TEB結構中 指向 PEB,則 0026b000
接著來解析TEB結構,只需要執行 dt _PEB 0026b000 即可得到該地址。
直接彙編實現,也非常簡單,如下。
列舉程序模組
1.我們來拓展一個知識點,通過PEB/TEB找到自身程序的所有載入模組資料,首先獲取 TEB,也就是執行緒環境塊。在程式設計的時候,TEB 始終儲存在暫存器 FS 中。
先來得到LDR結構:Ldr = *( ( DWORD ** )( ( unsigned char * )PEB + 0x0c ) );
先找到TEB
然後再找到PEB結構 偏移為 0x30 從該命令的輸出可以看出,PEB 結構體的地址位於 TEB 結構體偏移0x30 的位置
找到了PEB也就可以找到_PEB_LDR_DATA結構 其位於 PEB 偏移 0c的位置上。
Ldr = *( ( DWORD ** )( ( unsigned char * )PEB + 0x0c ) );
從輸出結果可以看出,LDR 在 PEB 結構體偏移的 0x0C 處,該地址儲存的地址是 0x77bf0c40 通過該地址來解析 LDR 結構體。
WinDBG 輸出如下內容:
Flink = *( ( DWORD ** )( ( unsigned char * )Ldr + 0x14 ) );
位於LDR偏移14的位置就是InLoadOrderModuleList其所指向的就是模組名稱表。
現在來手動遍歷第一條連結串列,輸入命令 0x4e3370
在連結串列偏移 0x18 的位置是模組的對映地址。
即 ImageBase;在連結串列偏移 0x28 的位置是模組的路徑及名稱的地址;
在連結串列偏移 0x30 的位置是模組名稱的地址。
的確是模組的名稱。既然是連結串列,就來下一條連結串列的資訊,004e3268 儲存著下一個連結串列結構。依次遍歷就是了。
我們找到下一個連結串列位置,然後同樣的方法來驗證一下。
沒錯了吧,下一個是 ntdll.dll
上面介紹的結構,是微軟保留結構,只能從網上找到一個結構定義,然後自行看著解析就好了。
typedef struct _LDR_DATA_TABLE_ENTRY {
PVOID Reserved1[2];
LIST_ENTRY InMemoryOrderLinks;
PVOID Reserved2[2];
PVOID DllBase;
PVOID EntryPoint;
PVOID Reserved3;
UNICODE_STRING FullDllName;
BYTE Reserved4[8];
PVOID Reserved5[3];
union {
ULONG CheckSum;
PVOID Reserved6;
};
ULONG TimeDateStamp;
} LDR_DATA_TABLE_ENTRY, *PLDR_DATA_TABLE_ENTRY;
列舉模組的方法就是:得到TEB -> PEB ->LDR ->遍歷 0x18與0x28中的內容即可。
未完待續。。。
獲取指定API地址
222