Windows中的PEB,TEB等的研究

在研究0day安全漏洞这本书的过程中,对windows线程和进程块的学习过程(但是发现资料是在是不多。。只能先针对性的整理一下了)

Windows中利用TEB找到指定的dll起始地址

64 bit 下的研究

这里windbg中查询到的结构体本身不知为何,显示的是32bit的结构体的大小,所以这里我们需要将所有的偏移量*2。

TEB的定义

TEB(thread environment block),也就是线程环境变量块,是在用户态下对线程的一种表示。在操作系统的分级下,其对于运行在最外层的用户态下的线程拥有最少的信息,对于运行在在最高级的内核态中的线程拥有最高级的信息。如果当前线程没有任何用户态的对象使用的话,那么就不会有TEB。原则上,如果一个线程在用户态下就能够完成处理,并且暂时不需要和内核态交互的话,那么这个信息就会放在TEB中。说白了,TEB是一个存储了线程基本属性的结构体。并且可以通过API来获得。

TEB的查找方式

TEB存放的位置在执行中使能够找到对的。但是在32bit和64bit下存在差异:

1
2
3
4
5
+-------------+--------------+
| 32 bit | 64 bit |
+-------------+--------------+
| fs | gs |
+-------------+--------------+

如上,32bit下是通过fs段寄存器来定位的,而64bit下则是通过gs段寄存器进行定位的。

利用windbg指令:

dt _teb

我们能够查看此时的TEB的结构:

关注红色的框框处(+0x030),这个位置存放的是指向当前PEB指针的。

PEB的定义

PEB(Process environment blocck),也就是进程环境变量块。这个块运行用户在用户态下获得很多当前进程的基本信息,比如说读入了的dll的名字,进程开始处的参数,堆地址,检查当前进程是否在调试状态下以及dll的镜像基地址等等。
当前的内容我们可以看到如下


红框处圈出来的(+0xc0)就是指向一个叫做PEB_LDR_DATA的结构体。大部分的内容并没有公开,但是这个模块已经被公开了。我们接下来研究一下这个结构体

PEB_LDR_DATA\LDR_DATA_TABLE_ENTRY的定义

PRE_LDR_DATA结构体记录了当前进程中所有载入了的模块。本质上是LDR_DATA_TABLE_ENTRY结构的三个双链表的头,每个表示一个加载的模块。
然后直接查看内部的结构,如下:

能够看出,这个结构体就是一个链表,链接的对象就是结构体LDR_DATA_TABLE_ENTRY。这个对象中存放了一些和模块加载相关的内容。我们这里首先查看一下:

1
2
3
4
5
+0x000 InLoadOrderLinks : _LIST_ENTRY
+0x008 InMemoryOrderLinks : _LIST_ENTRY
+0x010 InInitializationOrderLinks : _LIST_ENTRY
+0x018 DllBase : Ptr32 Void
+0x01c EntryPoint : Ptr32 Void

这个对象中前三个属性也是存放了当前所有模块中链表的基本信息的值。然后在偏移量为0x18的地方存放了当前DllBase。

接下来我们尝试去寻找当前的kernel32.dll所存放的位置。
首先假设我们能够获取到gs寄存器中的内容,然后我们此时找到了teb的位置,此处定义为teb_address:

也就是

1
peb_address = [teb_address + 0x60]

然后我们查看peb中的ldr的位置:

此时得到的Ldr的所在的位置就是

1
Ldr = [peb_address + 0x18]

然后此时我们选取指定的链表InLoadOrderLinks,此时指向的地址就是LDR_DATA_TABLE_ENTRY:

1
ldr_data_table = [Ldr + 0x20]

最后我们从这个ld_data_table中能够找到当前所查找的目标内容:

1
2
dllbase = [ldr_data_table + 0x20]
next_ldr = [ldr_data_table]

然后我们可以知道,载入dll的顺序为

1
2
3
+-----------+ +-----------+ +-----------+ +--------------+
| test.exe | ---> | ntdll.dll | ---> | wow64.dll | ---> | wow64win.dll |
+-----------+ +-----------+ +-----------+ +--------------+

那么我们想要知道dll载入地址的话,就能够按照顺序来访问next,从而拿到指定的dll起始地址。