
概述
當我們在編寫一個Shellcode Payload時,我們總是擁有無限的可能性,尤其是在Windows平臺上。但我們要知道的是,想要編寫高質量的Shellcode其實并非易事,因此我才決定要通過這篇文章跟大家聊一聊我對此的看法。就我個人而言,我比較喜歡用C語言(用Visual Studio編譯源碼)來完成我的工作。因為C語言的源代碼非常優美,而且編譯器可以最大程度地優化源碼,如果你需要的話,也可以通過LLVM實現你自己的代碼混淆器。
為了方便演示,我將以x86 Shellcode作為樣例進行講解。當然了,你想將其用于x64平臺也是可以的。
查找基本的DLL
介紹
當一個Shellcode Payload在Windows平臺中加載之后,第一步就是要定位我們所需要使用到的功能函數。首先,我們要搜索保存了系統功能函數的動態鏈接庫(DLL)。為了實現這個目標,我們將會使用到多種不同的結構體,請大家往下看。
線程環境塊(TEB)
TEB是Windows系統用于描述一個線程所使用到的一種結構。每一個線程都可以通過FS寄存器(x86平臺)以及GS寄存器(x86_64平臺)來訪問自己的TEB。TEB的結構如下:
0:000> dt ntdll!_TEB
+0x000 NtTib : _NT_TIB
+0x01c EnvironmentPointer: Ptr32 Void
+0x020ClientId : _CLIENT_ID
+0x028ActiveRpcHandle : Ptr32 Void
+0x02cThreadLocalStoragePointer : Ptr32 Void
+0x030ProcessEnvironmentBlock : Ptr32 _PEB
...
+0xff0 EffectiveContainerId :_GUID
因此,如果你想要訪問進程環境塊(PEB)的話,你只需要使用下面這段代碼即可:
PEB* getPeb() {
__asm {
mov eax,fs:[0x30];
}
}
進程環境塊(PEB)
如果TEB提供的是有關一個線程的信息,那么PEB提供的就是進程本身的信息了。而我們所需要的信息是那些基本的DLL所處的位置。實際上,當Windows在內存中加載一個進程時,至少會映射出兩個DLL:
1. ntdll.dll:它包含有可以進行系統調用(syscall)的功能函數。所有以Nt*為前綴的dll文件都可以調用相應的內核函數(以Zw*開頭)。
2. kernel32.dll:它是一個內核級文件,它可以調用高層的NTDLL函數。比如說,kernel32!CreateFileA可以調用ntdll!NtCreateFileW,而ntdll!NtCreateFileW又可以調用ntoskrnl!ZwCreateFileW。
在Windows平臺下,其他的DLL可能已經在內存中加載了,但是為了方便講解,我們可以假設目前內存中只加載了上述兩個DLL。
接下來,讓我們一起看一看下面這個TEB結構:
0:000> dt nt!_PEB
+0x000InheritedAddressSpace : UChar
+0x001ReadImageFileExecOptions : UChar
+0x002BeingDebugged : UChar
+0x003BitField : UChar
+0x003ImageUsesLargePages : Pos 0, 1 Bit
+0x003IsProtectedProcess : Pos 1, 1 Bit
+0x003IsImageDynamicallyRelocated : Pos 2, 1 Bit
+0x003SkipPatchingUser32Forwarders : Pos 3, 1 Bit
+0x003IsPackagedProcess : Pos 4, 1 Bit
+0x003IsAppContainer : Pos 5, 1 Bit
+0x003IsProtectedProcessLight : Pos 6, 1 Bit
+0x003IsLongPathAwareProcess : Pos 7, 1 Bit
+0x004 Mutant : Ptr32 Void
+0x008ImageBaseAddress : Ptr32 Void
+0x00c Ldr : Ptr32 _PEB_LDR_DATA
...
+0x25c WaitOnAddressHashTable :[128] Ptr32 Void
你可以看到其中有一個PEB.BeingDebugged(第4行),這部分信息是IsDebuggerPresent()所要使用到的信息。但有趣的地方就在于上面的PEB.Ldr(第16行),它所對應的結構如下:
0:000> dt nt!_PEB_LDR_DATA
+0x000 Length : Uint4B
+0x004Initialized : UChar
+0x008SsHandle : Ptr32 Void
+0x00cInLoadOrderModuleList : _LIST_ENTRY
+0x014InMemoryOrderModuleList : _LIST_ENTRY
+0x01cInInitializationOrderModuleList : _LIST_ENTRY
+0x024EntryInProgress : Ptr32 Void
+0x028ShutdownInProgress : UChar
+0x02c ShutdownThreadId : Ptr32Void
我們可以看到,PEB.Ldr->In*OrderModuleList(第5、6、7行)都是鏈表結構(LIST_ENTRY),其中包含內存中所有已經加載的DLL。這三個鏈表指向的是相同的對象,但順序不同。我更愿意使用InLoadOrderModuleList,因為我可以直接把它當作指針(指向_LDR_DATA_TABLE_ENTRY)來使用。比如說,如果你想使用InMemoryOrderModuleList,那么_LDR_DATA_TABLE_ENTRY就直接位于_InMemoryOrderModuleList.Flink-0×10,因為InMemoryOrderModuleList.Flink指向的是下一個InMemoryOrderModuleList。
|