鱼C论坛

 找回密码
 立即注册

利用FS 找 Kernel32.dll 基地址

热度 7已有 1461 次阅读2011-4-13 16:33 |个人分类:病毒与反病毒技术|

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. 随便打开一个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的文件格式)的“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. 找到当前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搜索: 

FS:[ 0 ] 指向的是SHE,它指向kernel32.dll内部链,遍历SHE链找到next成员等于 0xffffffff EXCEPTION_REGISTER结构,该结构的handler值就是系统默认的异常处理例程;因为这个handlerkernel32.dll中,所以就可以向低地址搜索MZ字符,找到MZ就证明找到了kernel32.dll的基址。这里有个细节,DLL的装载是64K边界对齐的,所以需要利用遍历到的指向最后的异常处理的指针进行页查找,再结合PE文件MSDOS标志部分,只要在每个 64K 边界查找 “MZ ”字符就能找到kernel32.dll基址。 


#include <stdio.h> 

#include <conio.h> 

//#include <windows.h>

__inline __declspec(naked) unsigned int GetKernel32() 

__asm{ 

    mov esi,fs:[0] 

Find_System_SEH : 

    lodsd      将 _EXCEPTION_REGISTRATION_RECORD.next值装入eax 

    cmp[eax],0xFFFFFFFF    _EXCEPTION_REGISTRATION_RECORD.next == 0xFFFFFFFF时,

   ; 此结构中Handler为系统默认异常处理例程 

    jne Find_System_SEH    没找到系统默认异常处理例程则继续找 

    mov eax,[eax+0x04]    _EXCEPTION_REGISTRATION_RECORD.Handler装入eax

   ; 用windbg查看这个地址可知是:kernel32!_except_handler3 

Find_Kernel32_Base: 

    dec eax      ; 从系统默认异常处理例程开始向低地址搜索,因为这个例程在kernel32.dll中 

    xor ax,ax     ;根据PE执行文件的64K字节齐特征,加快查找速度,相当于and eax,0xffff0000 

    cmp word ptr[eax],0x5A4D   ;比较MZ 

    jne Find_Kernel32_Base 

    mov ebx,[eax+0x3C]    ;IMAGE_DOS_HEADER.e_lfanew装入ebx,即装入PE头的RVA 

    add ebx,eax     ;计算PE头的VA 

    cmp word ptr[ebx],0x4550  ;比较PE 

    jne Find_Kernel32_Base 

    ret      ;找到! eax 中即是 kernel32.dll 地址 

   } 

}


int main() 

printf("KERNEL32.DLL:\t%0.8X\n",GetKernel32()); 

getch(); 

return 0 ; 

}

PEB: 

这种方法是通常链表来查找kernel32.dll地址的,结构_TEB -> _PEB -> _PEB_LDR_DATA -> InInitializationOrderModuleList字段是一个_ENTRY_LIST结构,这个结构中的Flink指向_LDR_MODULE.InInitializationOrderLinks 


#pragma comment(linker,"/ENTRY:main") 

#pragma warning(disable:4035) 

#include<stdio.h> 

__forceinline void* GetKernel32() 

{__asm{ 

    mov eax,fs:[30h] ; FS:[30]_TEB._PEB 

mov eax,[eax+0ch] ; _PEB._PEB_LDR_DATA 

mov eax,[eax+1ch] ; _PEB_LDR_DATA. InInitializationOrderModuleList.Flink,指向的是_LDR_MODULE.InInitializationOrderLinks 

mov eax,[eax]   ;_LDR_MODULE.InInitializationOrderLinks.Flink 

mov eax,[eax+08h] ;_LDR_MODULE.DllBase,此处即是kernel32.dll的基址 

}} 

void main() 

    printf("KERNEL32.DLL:%08X\n",GetKernel32()); 

}


TEB: 

本地线程的堆栈中偏移1CH(或者18H)的指针指向kernel32.dll内部,根据这个特点,我们可以得到这个指向kernel32.dll内部的指针,并向底地址搜索MZ字符即可查找到kernel32.dll的基址。_NT_TIB .StackBase指向堆栈基址,让它送去1CH18H即可得到指向kernel32.dll内部的指针。 


#pragma comment(linker,"/ENTRY:main") 

#pragma warning(disable:4035) 

#include<stdio.h> 

__forceinline void* GetKernel32() 

__asm{ 

   xor esi , esi 

   mov esi , fs :[ esi + 0x18 ] ;_TEB. _NT_TIB.Self,指向自身_TIB结构地址,即指向_NT_TIB自己 

   mov eax , [ esi + 4 ]    ; _NT_TIB .StackBase,堆栈基址,即栈顶(栈顶为底地址) 

   mov eax , [ eax - 0x1c ] ; 指向Kernel32.dll内部 

Find_Kernel32_Base: 

   dec eax ; 向底地址搜索 

   xor ax , ax    ;根据PE执行文件的64K字节齐特征,加快查找速度,相当于and eax,0xffff0000 

   cmp word ptr [ eax ], 0x5a4d ; "MZ" 

   jne Find_Kernel32_Base 

   mov ebx,[eax+0x3C]   ;IMAGE_DOS_HEADER.e_lfanew装入ebx,即装入PE头的RVA 

   add ebx,eax    ;计算PE头的VA 

   cmp word ptr[ebx],0x4550 ;比较PE 

   jne Find_Kernel32_Base 

   ret     ;找到,eax中即是kernel32.dll地址 

void main() 

    printf("KERNEL32.DLL:%08X\n",GetKernel32()); 

}

这里再说一下当前技术防止找到kernel32.dll地址的方法:一个是摘链,即将kernel32.dllLDR中的链摘掉,即防止了PEB方法,第二个是抹掉MZ,因为TEBSHE方法都是搜索MZ特征,所以将其抹掉即可。

1

路过

鸡蛋
1

鲜花
5

握手

雷人

刚表态过的朋友 (7 人)

发表评论 评论 (1 个评论)

回复 网友 2014-4-1 22:36
还是不懂怎么找

facelist

您需要登录后才可以评论 登录 | 立即注册

小黑屋|手机版|Archiver|鱼C工作室 ( 粤ICP备18085999号-1 | 粤公网安备 44051102000585号)

GMT+8, 2024-5-20 05:36

Powered by Discuz! X3.4

© 2001-2023 Discuz! Team.

返回顶部