8.1 TEB与PEB概述

在开始使用TEB/PEB获取进程或线程ID之前 , 我想有必要解释一下这两个名词 , PEB指的是进程环境块(Block) , 用于存储进程状态信息和进程所需的各种数据 。每个进程都有一个对应的PEB结构体 。TEB指的是线程环境块(Block) , 用于存储线程状态信息和线程所需的各种数据 。每个线程同样都有一个对应的TEB结构体 。
PEB中包含了进程的代码、数据段指针、进程的环境变量、进程启动参数信息以及加载的dll信息等 。PEB结构体中的FS段寄存器通常被设置为0x30 , 指向当前进程的PEB结构体 。其他进程可以通过访问自己的PEB结构体来获取自己的状态和信息 。
TEB中包含了线程的堆栈指针、TLS(线程本地存储)指针、异常处理链表指针、用户模式分页表指针等信息 。TEB中的FS段寄存器通常被设置为fs:[0] , 指向当前线程的TEB结构体 。其他线程可以通过访问自己的TEB结构体来获取自己的状态和信息 。
在创建进程时 , 操作系统会为每个线程分配一个TEB(线程环境块) , 并且该环境块中FS段寄存器总是被设置为fs:[0]的位置上 , 也就是默认指向当前线程的TEB数据 。因此 , 在进行代码分析时 , 可以通过通配符来找到TEB结构体的具体名称 。

8.1 TEB与PEB概述

文章插图
接着我们可通过dt -rv ntdll!_TEB命令 , 查询ntdll!_TEB结构 , 如下图所示 , 我们可以看到偏移为+0x018的位置就是TEB结构头指针 , 在该地址基础上向下偏移0x30就可以得到PEB(进程环境块)的基地址 。
其中+0x000的位置指向了结构 , +0x018指向结构TEB自身 , +0x020+0x000指向的是当前进程的PID , +0x020+0x004则指向父进程的PPID , +0x030指向了PEB结构体 , 其他字段读者可自行查阅官方文档;
接着再来验证一下 , 首先偏移地址0x18是TEB结构基地址 , 也就是指向自身偏移fs:[0x18]的位置 , 而!teb地址加0x30正是PEB的位置 , 在teb的基础上加上0x30就可以得到PEB的基地址 , 拿到PEB基地址就可以干很多事了 。
【8.1 TEB与PEB概述】在线程环境块内 , fs:[0x18]定位到TEB(线程环境块) , 加上0x20得到 , 此处存储的就是进程与线程ID的结构位置 , 通过+0x000可得到也就是进程PID , 通过+0x004可得到也就是线程TID , 读者可输入如下所示的命令自行验证;
0:000> dd fs:[0x18]# 找到TEB基地址0053:000000180081e000 00000000 0000139c 0000194c0053:0000002800000000 0081e02c 0081b000 000000000:000> dt _teb 0081e000 ntdll!_TEB+0x000 NtTib: _NT_TIB+0x01c EnvironmentPointer : (null) +0x020 ClientId: _CLIENT_ID# 将TEB+0x20定位到这里(进程与线程信息)+0x028 ActiveRpcHandle: (null) 0:004> dt _CLIENT_ID 0081e000# 查看进程详细结构ntdll!_CLIENT_ID+0x000 UniqueProcess: 0x00b3f774 Void# 获取进程PID+0x004 UniqueThread: 0x00b40000 Void# 获取线程TID
有了上述分析结果那么读者可以很容易的获取到当前自身进程的PID以及TID信息 , 其完整代码片段如下所示 , 当读者向()传入1则表示读取PID , 否则则读取TID信息;
#include #include // 传入1获取PID传入0获取TIDDWORD GetSelfID(DWORD isPid){DWORD ref = 0;if (isPid == 1){__asm{mov eax, fs:[0x18]// 获取到PEB基地址add eax, 0x20// 加上20得到 _CLIENT_IDadd eax, 0x0// 加上偏移0得到 UniqueProcessmov eax, [eax]// 取出内存地址中的值mov ref, eax}}else{__asm{mov eax, fs:[0x18]// 获取到PEB基地址add eax, 0x20// 加上20得到 _CLIENT_IDadd eax, 0x04// 加上偏移04得到 UniqueThreadmov eax, [eax]// 取出内存地址中的值mov ref, eax}}return ref;}int main(int argc, char* argv[]){printf("进程 Pid = %d \n", GetSelfID(1));printf("线程 Tid = %d \n", GetSelfID(0));system("pause");return 0;}