概述
当我们在编写一个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。
|