FS寄存器资料

来源:互联网 发布:linux shell 等待输入 编辑:程序博客网 时间:2024/05/01 07:48

FS寄存器指向当前活动线程的TEB结构(线程结构)

偏移  说明

000  指向SEH链指针

004  线程堆栈顶部

008  线程堆栈底部

00C  SubSystemTib

010  FiberData

014  ArbitraryUserPointer

018  FS段寄存器在内存中的镜像地址

020  进程PID

024  线程ID

02C  指向线程局部存储指针

030  PEB结构地址(进程结构)

034  上个错误号

 

 

得到KERNEL32.DLL基址的方法

assume fs:nothing             ;打开FS寄存器

mov eax,fs:[30h]            ;得到PEB结构地址

mov eax,[eax + 0ch]        ;得到PEB_LDR_DATA结构地址

mov esi,[eax + 1ch]        ;InInitializationOrderModuleList

lodsd                      ;得到KERNEL32.DLL所在LDR_MODULE结构的InInitializationOrderModuleList地址

mov edx,[eax + 8h]         ;得到BaseAddress,既Kernel32.dll基址

 

实例分析:如何用FS寄存器查找KERNEL32.DLL

 

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中的undocumented的一个结构,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

 

啊!竟然不是啊,7c920000ntdll.dll的,哈哈。

 

不过,算法命题仍然是正确的。因为在shellcode中模块列表的第一个就是kernel32了,当然可以通过镜像名称来check的,不过shellcode的空间不允许的,这就是shellcode的艺术了。我用来测试的exe恰好先加载了ntdll.dll

 

 

 

命题二:通过TEB(FS:[18])获取KERNEL32.DLL基地址

 

算法描述:

 

本地线程的栈里偏移18H的指针指向kernel32.dll内部,而fs :[ 0x18 ] 指向当前线程而且往里四个字节指向线程栈,结合栈顶指针进行对齐遍历,找到PE文件头(DLL的文件格式)的“MZMSDOS标志,就拿到了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.       找到当前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

 

7c800000  4d 5a 90 00                                      MZ..

 

找到!