ch.3 TEB & PEB

calling convention

stdcall這種呼叫約定32bit是由callee進行stack的清理,參數由右至左push進入stack,返回值放在EAX,x64的話參數進入依序是RCX, RDX, R8, R9, stack。
calling convention主要就是決定參數放哪?是由caller還是callee清理stack?返回值存哪?等等

如果可以不靠導入表與Win32 API,就找到載入的系統模組的base address與函數的address,並且依照calling convention把參數擺好,然後call function,那就等於是成功寫出並執行shellcode了,這就是接下來的目標。

TEB

每個thread有一個TEB來保存自己的狀態,例如stack等。
一個process只會有一個PEB,但可以有多個TEB。

TEB

+0x4和+0x8的地方是當前thread的stack的範圍,+0x20是process和thread的ID,+0x18指向TEB自己,+0x30指向PEB。

TEB在32bit組語中,所有的TEB欄位值都可以通過FS段暫存器加上對應的offset來取得,例如可以從fs:[0x18]來取得TEB起始位址,fs:[0x04]取得StackBase,x64中是用GS段暫存器來取得欄位值。
(另外,這並不代表說fs:[0x0]就是TEB的位址)

PEB

每個process有且僅有一個PEB用來儲存process的狀態。

PEB

+0x2是使用WinAPI IsDebuggerPresent檢查是否正在被debug時返回的值,我目前的理解是,惡意軟體可以用這個來anti-debug。
+0x8是當前process主要執行的PE模組基址。

PEB+0x10的ProcessParameters是當前process被父行程喚醒時繼承的參數資訊。

ProcessParameters

ConsoleHandle,繼承父行程的console,這樣printf的時候會出現在父行程的黑窗中。
CurrentDirectoryPath紀錄被父行程指定的工作目錄,沒指定就跟父行程當前目錄相同。
ImagePathName紀錄當前exe的完整路徑。
CommandLine紀錄被父行程喚醒時給的參數。

PEB+0x0c的Ldr會將當前process所有已載入的模組資訊儲存起來。
NtDLL!LdrpInitializeProcess為執行程式裝載器,它會去修正PE模組引用到的函數指標,還會加載PE模組需要使用的其他系統模組到memory space中。

Ldr

Length代表當前Ldr的大小。
Initialized若為true代表系統模組已經載入、初始化完成,可供查詢。
後面有三個LIST_ENTRY結構的成員,這個LIST_ENTRY是double linked list結構,被這三個linked list串起來的就是已載入的各個模組資訊。

LIST_ENTRY

這三條linked list,InLoadOrderModuleList、InMemoryOrderModuleList、InInitializationOrderModuleList,都是串著當前加載的模組資訊,只是差在串的順序不同而已。
InLoadOrderModuleList按照各個模組被載入的順序排列。
InMemoryOrderModuleList按照各個模組的映象基址低到高排列。
InInitializationOrderModuleList按照初始化各個函數模組入口的順序排列,此串不含本身的exe模組資訊。
假設有個test.exe,然後載入了ntdll.dll和Kernel32.dll兩個模組,在前兩個list中會有這三個模組的資訊,最後一個list只會有兩個dll模組的資訊。

其中被串起來的這些模組資訊,是一個個叫LDR_DATA_TABLE_ENTRY的結構。

LDR_DATA_TABLE_ENTRY

裡面包含模組映像基址、入口點、SizeOfImage等重要資訊
從這三個list都可以找尋已載入模組的映象基址,遍歷哪一個list都無所謂。

模組資訊中除了模組映像基址、入口點、SizeOfImage外,還有個Flags,紀錄了當前模組載入的狀態。
若是LDRP_STATIC_LINK代表它是process生成時就被載入的模組,可能是導入表紀錄需要被載入的模組。若是LDRP_IMAGE_DLL代表它是被載入的dll模組。若是LDRP_ENTRY_PROCESSED代表不只被載入了,而且入口函數已被呼叫過(初始化完成)。

LoadCount代表當前模組被載入的次數,可能是導入表說要載入它,或者動態呼叫LoadLibrary來載入它,多一次載入,LoadCount就+1,歸零時會被釋放記憶體空間。

FullDllName代表此模組完整路徑,BaseDllName是此模組檔案名稱
例如FullDllName是「C:\Windows\System32\Kernel32.dll」,而BaseDllName就是「Kernel32.dll」。

整個關係圖大概長這樣,但通常第一個載入的模組是exe本人,它其實是不會出現在InInitializationOrderModuleList中的。

three-lists