首页
社区
课程
招聘
[原创]PEB结构:获取模块kernel32基址技术及原理分析
发表于: 2021-3-26 14:42 23723

[原创]PEB结构:获取模块kernel32基址技术及原理分析

2021-3-26 14:42
23723

在学习《恶意代码分析实战》第19章shellcode分析时,提到了一个根据PEB结构搜索kernel32基址的方法。书中描述的很晦涩难懂,于是花了点时间详细了解了一下这个技术。整理成笔记方便日后查找。

Windows是一个强大的操作系统。为了方便开发者,微软将进程中的每个线程都设置了一个独立的结构数据。这个结构体内存储着当前线程大量的信息。这个结构被称为TEB(线程环境块)。通过TEB结构内的成员属性向下扩展,可以得到很多线程信息。这其中还包含大量的未公开数据。

TEB结构的其中一个成员为PEB(进程环境块),顾名思义,这个结构中存储着整个进程的信息。通过对PEB中成员的向下扩展可以找到一个存储着该进程所有模块数据的链表。

本文共涉及两个资料:

神图:image-20210326095430084

VergiliusProject结构体查询网站:(下文简称VP)

https://www.vergiliusproject.com/kernels

微软未公开数据:(本文没有用到,但是收藏起来以后会用到,包含API,结构体等信息)

http://undocumented.ntinternals.net/

对模块的遍历是从PEB结构开始的,因此我们需要获取PEB结构指针。PEB的结构指针存储在TEB中。TEB结构指针存储在fs寄存器中。那么如何在内存中定位TEB结构的位置呢?有三种方法:

查看OD等调试器的寄存器窗口,fs段寄存器的后面会接着TEB结构指针。直接在内存窗口跳过去即可,如下:

image-20210326103016090

通过fs的值拆分成段选择子,通过GDT表查找段描述符,得到一个3环的调用门....#*#%$()#.....

显然这种方式很笨重,而且对于像我这种没学过内核的彩笔来说根本不现实,更别说后面落实代码了。

第三种是最方便的,fs:[0x18]存储着TEB结构指针,fs:[0x30]存储着PEB结构指针。

网上很多文章都会说:fs:[0x30]是PEB fs:[0x18]是TEB,虽然确实方便,但我们还是要看下原理。

目前我们已经知道了fs就是TEB结构指针,但是不巧,在OD中尝试直接跳转到fs处,OD无法直接找到TEB。这是因为fs只存储了段选择子,而通过段选择子找到的东西才是真正的TEB结构指针(内核知识)。如下图:

image-20210326103955352

这时我们回到VP上,看看TEB结构的具体成员:

这里注意下,系统为64时,VP首页kernels记得选X64,但是当你在64位系统上运行32位程序时,我们要查询的TEB结构为_TEB32

切记不要32位程序去查64位的TEB,会迷失自己。

image-20210326104524136

image-20210326104603616

可以看到TEB结构中第一个成员是另一个结构体TIB(线程信息块),它占了1C个字节,我们点击黄色的结构体名可以直接跳到TIB结构处,看看它的成员。

image-20210326105010855

可以看到,偏移0x18处有个Self成员,它存储着这个TIB结构的首地址。也就是SEH指针

回到TEB结构,我们知道TIB是TEB中的第一个成员,那么可以理解为TIB的首地址就是TEB的首地址。所以TIB的0x18偏移等于TEB的0x18偏移,TIB的Self成员同时也指向TEB首地址

至此,我们得出了一个结论,pTEB->0x18 == *pTEB

还记得上文说的fs就是TEB指针吗,带入公式,得到:

fs:[0x18] == TEB

同理,我们看到TEB结构0x30偏移处为PEB结构(图上没缩写,我淦),那么同理:

pTEB->0x30 == fs:[0x30] == PEB

SEH:pTEB->0 == fs:[0] == SEH链

===========================================================================

知道了fs:[0x30]的来历那就好办了,我们继续模块遍历的学习。VP上看下PEB结构(我这里直接看X86下的PEB了,不然PEB32里面没有缩写,不能直接跳过去很麻烦):

image-20210326105759321

image-20210326110204730

可以看到PEB偏移为C处存储着LDR指针,它指向一个_PEB_LDR_DATA结构,我们点进去看看:

image-20210326110515918

结构中提供了三个链表,链表内的节点都是一样的,只是排序不同。由于我们要寻找kernel32的基址,所以我们选择第三个InInitializationOrderModuleList,这样kernel32的链表节点会比较靠前。其实选其他两个也一样,就是要找久一点。我们看下这个链表入口结构_LIST_ENTRY的信息:

image-20210326110744971

可以看到这个结构有两个成员,第一个成员Flink指向下一个节点,Blink指向上一个节点。所以这是一个双向链表。接下来的概念很重要:

当我们从_PEB_LDR_DATA结构中取到InInitializationOrderModuleList结构时,这个结构中的Flink指向真正的模块链表,这个真正的链表的每个成员都是一个LDR_DATA_TABLE_ENTRY结构。

之前的_PEB_LDR_DATA只是一个入口这个结构只有一个,它不是链表节点,真正的链表节点结构如下图:

image-20210326111443725

_LDR_DATA_TABLE_ENTRY结构中的 _LIST_ENTRY 结构对应下一个 _LDR_DATA_TABLE_ENTRY 节点中的 _LIST_ENTRY 结构。

如:第一个 _LDR_DATA_TABLE_ENTRY 结构中的 InInitializationOrderModuleList 中的 Flink 指向的是第二个 _LDR_DATA_TABLE_ENTRY 结构中 InInitializationOrderModuleList 的首地址。而不是另外两个 _LIST_ENTRY 结构。

第一个 _LDR_DATA_TABLE_ENTRY 结构中的 Blink 指向 PEB_LDR_DATA 中对应成员的 Blink

最后一个 _LDR_DATA_TABLE_ENTRY 结构中的 Flink 指向 PEB_LDR_DATA 中对应的成员的 Flink

PEB_LDR_DATA 结构中的 Blink 指向最后一个 _LDR_DATA_TABLE_ENTRY 中对应成员的 Blink

PEB_LDR_DATA 结构中的 Flink 指向第一个 _LDR_DATA_TABLE_ENTRY 中对应的成员的 Flink

看不懂上面这段话? 我们转换成图:

image-20210326135732359

宏观的体现为下图:

image-20210326140642056

可以看到这是一个以PEB_LDR_DATA为起点的一个闭合环形双向链表。

每个_LDR_DATA_TABLE_ENTRY节点结构中偏移为0x30处的成员为dllName,偏移为0x18处的成员为DllBase

通过遍历链表,比较dllName字符串内容可以找到目标模块的所属节点。

通过节点成员DllBase可以定位该模块的DOS头起始处。

通过对PE结构的解析可以搜索导出表,从而可以取到指定的导出函数地址。

理论讲的差不多了,下面我们在OD中实战演练一下,手动寻找kernel32的基地址。

打开OD,随便载入一个PE文件。在内存窗口按Ctrl+G跳转到fs:[0x30]处。PEB的首地址为0x7EFDE000

image-20210326141506780

对照VP上的结构体信息,我们知道PEB偏移0xC处为成员LDR。继续跳转到LDR结构首地址0x7DF70200

image-20210326142252722

MD 属性名搞错了,不是list 是link, 懒得改了

三个成员任选一个,我们这里使用InInitializationOrderModuleList成员,Flink代表第一个节点,我们跳转到0x5E2590,由于我们是从InInitializationOrderModuleList中的Flink跳过来的,因此我们向上拉一点,从0x005E2580处开始为_LDR_DATA_TABLE_ENTRY结构

image-20210326143239523

我们看到BaseDllName并不是我们想要的kernel32.dll,因此我们继续去看下一个节点。

继续跳转到Flink(0x5E29B8)处

image-20210326143550283

可以看到此时的Blink已经指向了第一个节点的Blink,dllname依然不是kernel32.dll


[招生]科锐逆向工程师培训(2024年11月15日实地,远程教学同时开班, 第51期)

收藏
免费 19
支持
分享
最新回复 (15)
雪    币: 8753
活跃值: (5215)
能力值: ( LV4,RANK:50 )
在线值:
发帖
回帖
粉丝
2
学习了!
2021-3-27 16:22
0
雪    币: 8880
活跃值: (5101)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
jgs
3
收藏,学习
2021-3-27 17:54
0
雪    币: 300
活跃值: (2452)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
4
mark
2021-3-28 09:52
0
雪    币: 29
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
5
感谢,理解了
2021-3-28 11:12
0
雪    币: 9872
活跃值: (6706)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
6

学习学习!

最后于 2021-3-31 13:45 被我的小拇指啊编辑 ,原因: 。。。眼瞎没看到楼主已经贴出来了
2021-3-31 13:42
0
雪    币: 353
活跃值: (840)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
7
mark
2021-3-31 13:59
0
雪    币: 26588
活跃值: (63257)
能力值: (RANK:135 )
在线值:
发帖
回帖
粉丝
8
感谢分享~
2021-3-31 14:02
0
雪    币: 50
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
9
太强了,get到了
2021-7-25 12:56
0
雪    币: 50
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
10
三个成员任选一个,我们这里使用InInitializationOrderModuleList成员,Flink代表第一个节点,我们跳转到0x5E2590,由于我们是从InInitializationOrderModuleList中的Flink跳过来的,因此我们向上拉一点,从0x005E2580处开始为_LDR_DATA_TABLE_ENTRY结构

解释:由于InInitializationOrderModuleList是_LDR_DATA_TABLE_ENTRY的第三个成员,偏移是0x10,前三个结构:
     LIST_ENTRY InLoadOrderLinks;                     0x0
     LIST_ENTRY InMemoryOrderLinks;               0x8
     LIST_ENTRY InInitializationOrderLinks;         0x10
2021-7-26 14:48
0
雪    币:
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
11
2021-8-6 17:21
0
雪    币: 522
活跃值: (4821)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
12
用vc2010里的结构体不一样:
typedef struct _PEB_LDR_DATA {
  BYTE       Reserved1[8];
  PVOID      Reserved2[3];
  LIST_ENTRY InMemoryOrderModuleList;
} PEB_LDR_DATA, *PPEB_LDR_DATA;

typedef struct _LDR_DATA_TABLE_ENTRY {
    PVOID Reserved1[2];
    LIST_ENTRY InMemoryOrderLinks;
    PVOID Reserved2[2];
    PVOID DllBase;
    PVOID EntryPoint;
    PVOID Reserved3;
    UNICODE_STRING FullDllName;
    BYTE Reserved4[8];
    PVOID Reserved5[3];
    union {
        ULONG CheckSum;
        PVOID Reserved6;
    };
    ULONG TimeDateStamp;
} LDR_DATA_TABLE_ENTRY, *PLDR_DATA_TABLE_ENTRY;
2022-3-22 00:04
0
雪    币: 522
活跃值: (4821)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
13
vs2010下,只有选择InLoadOrderLinks才正确,远第二第三个,解不出来,或者必须网上回滚8或者16个字节才可以解出来正确的基址
2022-3-22 00:07
0
雪    币: 522
活跃值: (4821)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
14
wx_大可爱 三个成员任选一个,我们这里使用InInitializationOrderModuleList成员,Flink代表第一个节点,我们跳转到0x5E2590,由于我们是从InInitializationOr ...
vs2010下,只有选择InLoadOrderLinks才正确,远第二第三个,解不出来,或者必须网上回滚8或者16个字节才可以解出来正确的基址。

这一点查表网络,都没有讲
2022-3-22 00:08
0
雪    币: 922
活跃值: (1798)
能力值: ( LV4,RANK:40 )
在线值:
发帖
回帖
粉丝
15
只能一句:牛逼
2023-1-26 17:17
0
雪    币: 27
活跃值: (35)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
16

11111

最后于 2024-11-5 16:13 被汉唐魂魄编辑 ,原因:
2024-11-5 15:59
0
游客
登录 | 注册 方可回帖
返回
//