网友 发表于 2014-4-1 22:42:34

请教下利用FS 找 Kernel32.dll 基地址疑问?

原帖是http://bbs.fishc.com/home.php?mod=space&uid=9&do=blog&id=638小甲鱼发的,看到验证过程实在搞不懂,压根不知道从哪里找起。如果有会的人能否载图一步步示范下,劳驾了{:1_1:}

证明过程:1. 随便打开一个exe文件,内存中的KERNEL32.DLL基地址是不变的;
2. 获取PEB基地址,0:000> dd fs:30 L1003b:00000030 7ffd6000看到了,7ffd6000
3. 获取PEB_LDR_DATA结构地址7ffd6000+0cpeb的结构定义: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 L17ffd600c 00181ea0PEB_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 Void0:000> dd 00181ea0+1c L100181ebc 00181f58InInitializationOrderModuleList->00181f58
5. 获取kernel32的基地址0:000> dd 00181f58+8 L100181f60 7c9200007c920000就是了?check一下:0:000> dd kernel32 L17c800000 00905a4d啊!竟然不是啊,7c920000是ntdll.dll的,哈哈。
不过,算法命题仍然是正确的。因为在shellcode中模块列表的第一个就是kernel32了,当然可以通过镜像名称来check的,不过shellcode的空间不允许的,这就是shellcode的艺术了。我用来测试的exe恰好先加载了ntdll.dll。


向往青莲 发表于 2014-4-1 22:42:35

网友 发表于 2014-4-2 11:37 static/image/common/back.gif
能否图解吗?我就是不知道怎么用工具找出来到底对不对

不能。。。没时间。。。
ps: 你自己看嘛。。。。

向往青莲 发表于 2014-4-2 11:00:40

FS获取kernel32基址
FS寄存器指向当前活动线程的TEB结构(线程结构)

偏移说明

000指向SEH链指针

004线程堆栈顶部

008线程堆栈底部

00CSubSystemTib

010FiberData

014ArbitraryUserPointer

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

020进程PID

024线程ID

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

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

034上个错误号



在shellcode中用它来找KERNEL32.DLL基地址是常见的算法了,经典的三种算法都用到了FS寄存器!她们是:

1.       通过PEB(FS:)获取KERNEL32.DLL基地址

2.       通过TEB(FS:)获取KERNEL32.DLL基地址

3.       通过SEH(FS:)获取KERNEL32.DLL基地址

下面分别证明之。



命题一:通过PEB(FS:)获取KERNEL32.DLL基地址

算法描述:

mov eax,fs:   ;得到PEB结构地址

mov eax,;得到PEB_LDR_DATA结构地址

mov esi,

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

; InInitializationOrderModuleList地址

mov eax,;win7要加

mov edx,   ;得到BaseAddress,既Kernel32.dll基址



证明:

1.       随便open一个exe,内存中的KERNEL32.DLL基地址是不变的;

2.       获取PEB基地址,

0:000> dd fs:30 L1

003b:000000307ffd6000

看到了,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

7ffd600c00181ea0

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

00181ebc00181f58

InInitializationOrderModuleList->00181f58

5.       获取kernel32的基地址

0:000> dd 00181f58+8 L1

00181f607c920000

7c920000就是了?

check一下:

0:000> dd kernel32 L1

7c80000000905a4d

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

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



命题二:通过TEB(FS:)获取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:000000187ffdd000

TEB->7ffdd000

2.       找到栈顶指针:

0:000> dd 7ffdd000+4 L1

7ffdd00400070000

3.       进入Kernel32空间:

0:000> dd 00070000-1c L1

0006ffe47c839aa8



4.       Kernel32空间的大搜索:

0:000> db 7c839aa7 L4

7c839aa730 55 8b ec                                    0U..

......一直搞下去

0:000> db 7c800000 L4

7c8000004d 5a 90 00                                    MZ..

找到了吧,哈哈。有点效率问题,shellcode有时候是要牺牲效率的,没办法,还是艺术问题。



命题三:通过SEH(FS:)获取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:000000000006fedc

2.       找到最外层SEH:

round 1:

0:000> dd 0006fedc L1

0006fedc0006ffb0 ; esi

0:000> dd 0006ffb0 L1

0006ffb00006ffe0 ;

round 2:

0:000> dd 0006ffb0 L1

0006ffb00006ffe0 ; esi

0:000> dd 0006ffe0 L1

0006ffe0ffffffff ;

不错,第二趟就找到了!此时,eax=0006ffe0

3.       找到MZ:

0:000> dd 0006ffe0+4 L1

0006ffe47c839aa8



0:000> db 7c839aa7 L4

7c839aa730 55 8b ec                                    0U..

......又是一直搞下去

0:000> db 7c800000 L4

7c8000004d 5a 90 00                                    MZ..

找到!



知其然,更要知其所以然!



PEB获取kernel32基址
通过PEB枚举当前进程空间中用户模块列表也可以获取Kernel32模块的基地址,fs:指向TEB,fs:指向PEB,PEB偏移0ch是LDR指针,以下可以分别通过加载顺序、内存顺序、初始化顺序获取Kernel32模块的基地址,这里以初始化顺序为例:
未公开的LDR_MODULE数据结构如下:
typedef struct _LDR_MODULE
{
    LIST_ENTRY      InLoadOrderModuleList;            // +0x00
    LIST_ENTRY      InMemoryOrderModuleList;          // +0x08
    LIST_ENTRY      InInitializationOrderModuleList; // +0x10
    PVOID             BaseAddress;                      // +0x18
    PVOID             EntryPoint;                     // +0x1c
    ULONG             SizeOfImage;                      // +0x20
    UNICODE_STRING    FullDllName;                      // +0x24
    UNICODE_STRING    BaseDllName;                      // +0x2c
    ULONG             Flags;                            // +0x34
    SHORT             LoadCount;                        // +0x38
    SHORT             TlsIndex;                         // +0x3a
    LIST_ENTRY      HashTableEntry;                   // +0x3c
    ULONG             TimeDateStamp;                  // +0x44
                                                      // +0x48
} LDR_MODULE, *PLDR_MODULE;
以下是WinDbg显示的PEB_LDR_DATA的数据结构
+0x00c Ldr            : Ptr32 to struct _PEB_LDR_DATA, 7 elements, 0x28 bytes
      +0x000 Length         : Uint4B
      +0x004 Initialized      : UChar
      +0x008 SsHandle         : Ptr32 to Void
      +0x00c InLoadOrderModuleList : struct _LIST_ENTRY, 2 elements, 0x8 bytes
         +0x000 Flink            : Ptr32 to struct _LIST_ENTRY, 2 elements, 0x8 bytes
         +0x004 Blink            : Ptr32 to struct _LIST_ENTRY, 2 elements, 0x8 bytes
      +0x014 InMemoryOrderModuleList : struct _LIST_ENTRY, 2 elements, 0x8 bytes
         +0x000 Flink            : Ptr32 to struct _LIST_ENTRY, 2 elements, 0x8 bytes
         +0x004 Blink            : Ptr32 to struct _LIST_ENTRY, 2 elements, 0x8 bytes
      +0x01c InInitializationOrderModuleList : struct _LIST_ENTRY, 2 elements, 0x8 bytes
         +0x000 Flink            : Ptr32 to struct _LIST_ENTRY, 2 elements, 0x8 bytes
         +0x004 Blink            : Ptr32 to struct _LIST_ENTRY, 2 elements, 0x8 bytes
      +0x024 EntryInProgress : Ptr32 to Void
通过上面两个结构我们可以通过3种方式获取Kernel32模块的基地址
1)加载顺序的方式:
GetK32Base:
assume fs:nothing
push esi
xor eax,eax
mov eax,fs:    ;指向PEB的指针   
mov eax,    ;指向PEB_LDR_DATA的指针   
mov esi,    ;根据PEB_LDR_DATA得出InLoadOrderModuleList的Flink字段   
lodsd
mov eax,    ;指向下一个节点 ;第一个节点为ntdll.dll,第二个为kernel32.dll
mov eax,    ;Kernel.dll的基地址 + 距离baseaddress的距离
pop esi
ret
2)内存顺序的方式:
GetK32Base:
assume fs:nothing
push esi
xor eax,eax
mov eax,fs:    ;指向PEB的指针   
mov eax,    ;指向PEB_LDR_DATA的指针      
mov esi,    ;根据PEB_LDR_DATA得出InMemoryOrderModuleList的Flink字段
lodsd
mov eax,    ;指向下一个节点
mov eax,    ;Kernel.dll的基地址   
pop esi
ret
3)初始化顺序的方式:
GetK32Base:
assume fs:nothing
push esi
xor eax,eax
mov eax,fs:    ;指向PEB的指针   
mov eax,    ;指向PEB_LDR_DATA的指针   
mov esi,    ;根据PEB_LDR_DATA得出InInitializationOrderModuleList的Flink字段   
lodsd
mov    eax,    ;win7以上版本+这个
mov eax,    ;Kernel.dll的基地址   
pop esi
ret
3、通过遍历SEH链的方法,在SEH链中查找成员prev的值为0xFFFFFFFFh的EXCEPTION_REGISTER结构。该结构中的handler值是系统异常处理例程,总是位于kernerl32.dll中。当前线程的TIB保存在fs段选择器指定的数据段的0偏移处,所以fs: 的地方就是TIB结构中的ExceptionList字段。而ExceptionList指向一个EXCEPTION_REGISTRATION结构,SEH异常处理回调函数的入口地址就是由EXCEPTION_REGISTRATION结构指定。
TIB结构如下:
NT_TIB STRUCT
ExceptionList dd   ?
StackBase dd   ?
StackLimit dd   ?
SubSystemTib dd   ?
union
   FiberData dd   ?
   Version dd   ?
ends
ArbitraryUserPointer dd ?
Self dd ?
NT_TIB ENDS
   
EXCEPTION_REGISTRATION STRUCT
prev      dd      ?      ;前一个EXCEPTION_REGISTRATION结构的地址
handler   dd      ?      ;异常处理回调函数地址
EXCEPTION_REGISTRATION      ends
GetK32Base:
assume fs:nothing
xor esi, esi
mov esi, fs:
lodsd
@@:
inc eax
jz @f
dec eax
xchg esi, eax
lodsd
jmp @b
@@:
lodsd
@@:
dec eax
xor ax, ax
cmp DWORD ptr , 5A4D
jnz @b
ret

结束语:技术可以做正义的事,也可以做邪恶的事,关键是看什么人掌握这些技术

网友 发表于 2014-4-2 11:37:30

向往青莲 发表于 2014-4-2 11:00 static/image/common/back.gif
FS获取kernel32基址
FS寄存器指向当前活动线程的TEB结构(线程结构)



能否图解吗?我就是不知道怎么用工具找出来到底对不对

向往青莲 发表于 2014-4-2 21:10:08

网友 发表于 2014-4-2 11:37 static/image/common/back.gif
能否图解吗?我就是不知道怎么用工具找出来到底对不对

提示你。。。用OD随便打开一个程序。。然后把上面的汇编代码扔进去(首先得大致看懂上面的)。。。设置eip指向上面的汇编代码第一句。。然后你就慢慢边单步运行。。边看结果嘛。。我是这样弄得。。。对了。。。可能有些事错误的。。因为上面有些事基于XP。。反正你自己调试就知道了。。。貌似WIN7 X64的我备注了一下的。。好像是。。。自己调试吗。。。配合着OD

青玄 发表于 2014-4-2 22:40:27

嗯!可以用OD或者IDA载入一个EXE文件,我觉得OD会好一点,再就是在里面进行单步调试,就这样,如果汇编还可以的话!应该能找到!

网友 发表于 2014-4-3 12:52:19

向往青莲 发表于 2014-4-2 21:10 static/image/common/back.gif
提示你。。。用OD随便打开一个程序。。然后把上面的汇编代码扔进去(首先得大致看懂上面的)。。。设置eip指 ...

谢谢!!!!!!!!!1

向往青莲 发表于 2014-4-3 18:28:28

网友 发表于 2014-4-3 12:52 static/image/common/back.gif
谢谢!!!!!!!!!1

那选我为最佳答案被。。。(*^__^*) 嘻嘻……

那年`逝去 发表于 2014-4-8 00:01:34

看大神回答学习学习
页: [1]
查看完整版本: 请教下利用FS 找 Kernel32.dll 基地址疑问?