FS寄存器指向当前活动线程的TEB结构(线程结构) 偏移 说明 000 指向SEH链指针 004 线程堆栈顶部 008 线程堆栈底部 00C SubSystemTib 010 FiberData 014 ArbitraryUserPointer 018 FS段寄存器在内存中的镜像地址 020 进程PID 024 线程ID 02C 指向线程局部存储指针 030 PEB结构地址(进程结构) 034 上个错误号 在shellcode中用它来找KERNEL32.DLL基地址是常见的算法了,经典的三种算法都用到了FS寄存器!她们是: 1. 通过PEB(FS:[30])获取KERNEL32.DLL基地址 2. 通过TEB(FS:[18])获取KERNEL32.DLL基地址 3. 通过SEH(FS:[00])获取KERNEL32.DLL基地址 下面分别证明之。 命题一:通过PEB(FS:[30])获取KERNEL32.DLL基地址 算法描述: mov eax,fs:[30h] ;得到PEB结构地址 mov eax,[eax + 0ch] ;得到PEB_LDR_DATA结构地址 mov esi,[eax + 1ch] lodsd ; 得到KERNEL32.DLL所在LDR_MODULE结构的 ; InInitializationOrderModuleList地址 mov edx,[eax + 8h] ;得到BaseAddress,既Kernel32.dll基址 证明: 1. 随便open一个exe,内存中的KERNEL32.DLL基地址是不变的; 2. 获取PEB基地址, 0:000> dd fs:30 L1 003b:00000030 7ffd6000 看到了,7ffd6000 3. 获取PEB_LDR_DATA结构地址7ffd6000+0c peb的结构定义: ntdll!_PEB +0x000 InheritedAddressSpace : UChar +0x001 ReadImageFileExecOptions : UChar +0x002 BeingDebugged : UChar +0x003 SpareBool : UChar +0x004 Mutant : Ptr32 Void +0x008 ImageBaseAddress : Ptr32 Void +0x00c Ldr : Ptr32 _PEB_LDR_DATA +0x010 ProcessParameters : Ptr32 _RTL_USER_PROCESS_PARAMETERS +0x014 SubSystemData : Ptr32 Void +0x018 ProcessHeap : Ptr32 Void +0x01c FastPebLock : Ptr32 _RTL_CRITICAL_SECTION ...... 0:000> dd 7ffd6000+0c L1 7ffd600c 00181ea0 PEB_LDR_DATA-> 00181ea0 4. 获取InInitializationOrderModuleList的地址 说一下这个PEB_LDR_DATA,她是ntdll.dll中的u ndocumented 的一个结构,PEB_LDR_DATA的结构定义: 0:000> dt _PEB_LDR_DATA +0x000 Length : Uint4B +0x004 Initialized : UChar +0x008 SsHandle : Ptr32 Void +0x00c InLoadOrderModuleList : _LIST_ENTRY +0x014 InMemoryOrderModuleList : _LIST_ENTRY +0x01c InInitializationOrderModuleList : _LIST_ENTRY +0x024 EntryInProgress : Ptr32 Void 0:000> dd 00181ea0+1c L1 00181ebc 00181f58 InInitializationOrderModuleList->00181f58 5. 获取kernel32的基地址 0:000> dd 00181f58+8 L1 00181f60 7c920000 7c920000就是了? check一下: 0:000> dd kernel32 L1 7c800000 00905a4d 啊!竟然不是啊,7c920000是ntdll.dll的,哈哈。 不过,算法命题仍然是正确的。因为在shellcode中模块列表的第一个就是kernel32了,当然可以通过镜像名称来check的,不过shellcode的空间不允许的,这就是shellcode的艺术了。我用来测试的exe恰好先加载了ntdll.dll。 命题二:通过TEB(FS:[18])获取KERNEL32.DLL基地址 算法描述: 本地线程的栈里偏移18H的指针指向kernel32.dll内部,而fs :[ 0x18 ] 指向当前线程而且往里四个字节指向线程栈,结合栈顶指针进行对齐遍历,找到PE文件头(DLL的文件格式)的“MZ”MSDOS标志,就拿到了kernel32.dll基址。 xor esi , esi mov esi , fs :[ esi + 0x18 ] // TEB mov eax , [ esi + 4 ] // 这个是需要的栈顶 mov eax , [ eax - 0x1c ] // 指向Kernel32.dll内部 find_kernel32_base : dec eax // 开始地毯式搜索Kernel32空间 xor ax , ax cmp word ptr [ eax ], 0x5a4d // "MZ" jne find_kernel32_base // 循 环遍 历 ,找到 则 返回 eax 证明: 1. 找到TEB,这个好办: 0:000> dd fs:18 L1 003b:00000018 7ffdd000 TEB->7ffdd000 2. 找到栈顶指针: 0:000> dd 7ffdd000+4 L1 7ffdd004 00070000 3. 进入Kernel32空间: 0:000> dd 00070000-1c L1 0006ffe4 7c839aa8 4. Kernel32空间的大搜索: 0:000> db 7c839aa7 L4 7c839aa7 30 55 8b ec 0U.. ......一直搞下去 0:000> db 7c800000 L4 7c800000 4d 5a 90 00 MZ.. 找到了吧,哈哈。有点效率问题,shellcode有时候是要牺牲效率的,没办法,还是艺术问题。 命题三:通过SEH(FS:[00])获取KERNEL32.DLL基地址 算法描述: 注意:FS:[ 0 ] 指向的是SHE,它指向kernel32.dll内部链,这样就可以顺藤摸瓜了。FS:[ 0 ] 指向的是SHE的内层链,为了找到顶层异常处理,我们向外遍历找到prev成员等于 0xffffffff 的EXCEPTION_REGISTER结构,该结构的handler值就是系统 默认的处理例程;这里有个细节,DLL的装载是64K边界对齐的,所以需要利用遍历到的指向最后的异常处理的指针进行页查找,再结合PE文件MSDOS标志部分,只要在每个 64K 边界查找 “MZ ”字符就能找到kernel32.dll基址。 xor ecx , ecx mov esi , fs :[ ecx ] find_seh : mov eax ,[ esi ] mov esi , eax cmp [ eax ], ecx jns find_seh // 0xffffffff mov eax , [ eax + 0x04 ] // handler find_kernel32_base : dec eax xor ax , ax cmp word ptr [ eax ], 0x5a4d jne find_kernel32_base 证明: 1. &n bsp; 找到当前SEH: 0:000> dd fs:0 L1 003b:00000000 0006fedc 2. 找到最外层SEH: round 1: 0:000> dd 0006fedc L1 0006fedc 0006ffb0 ; esi 0:000> dd 0006ffb0 L1 0006ffb0 0006ffe0 ; [eax] round 2: 0:000> dd 0006ffb0 L1 0006ffb0 0006ffe0 ; esi 0:000> dd 0006ffe0 L1 0006ffe0 ffffffff ; [eax] 不错,第二趟就找到了!此时,eax=0006ffe0 3. 找到MZ: 0:000> dd 0006ffe0+4 L1 0006ffe4 7c839aa8 0:000> db 7c839aa7 L4 7c839aa7 30 55 8b ec 0U.. ......又是一直搞下去 0:000> db 7c800000 L4 7c 800000 4d 5a 90 00 MZ.. 找到! 知其然,更要知其所以然! |
======================================== |
SHE搜索: __inline __declspec(naked) unsigned int GetKernel32() int main() PEB: TEB: 这里再说一下当前技术防止找到kernel32.dll地址的方法:一个是摘链,即将kernel32.dll在LDR中的链摘掉,即防止了PEB方法,第二个是抹掉MZ,因为TEB与SHE方法都是搜索MZ特征,所以将其抹掉即可。 |