在开始使用TEB/PEB
获取进程或线程ID之前,我想有必要解释一下这两个名词,PEB指的是进程环境块(Process Environment Block)
,用于存储进程状态信息和进程所需的各种数据。每个进程都有一个对应的PEB
结构体。TEB指的是线程环境块(Thread Environment Block)
,用于存储线程状态信息和线程所需的各种数据。每个线程同样都有一个对应的TEB
结构体。
PEB中包含了进程的代码、数据段指针、进程的环境变量、进程启动参数信息以及加载的dll信息等。PEB结构体中的FS段寄存器
通常被设置为0x30
,指向当前进程的PEB
结构体。其他进程可以通过访问自己的PEB结构体来获取自己的状态和信息。
TEB中包含了线程的堆栈指针、TLS(线程本地存储)指针、异常处理链表指针、用户模式分页表指针等信息。TEB中的FS段寄存器通常被设置为fs:[0]
,指向当前线程的TEB结构体。其他线程可以通过访问自己的TEB结构体来获取自己的状态和信息。
在创建进程时,操作系统会为每个线程分配一个TEB(线程环境块),并且该环境块中FS段寄存器总是被设置为fs:[0]
的位置上,也就是默认指向当前线程的TEB数据。因此,在进行代码分析时,可以通过通配符来找到TEB结构体的具体名称。
接着我们可通过dt -rv ntdll!_TEB
命令,查询ntdll!_TEB
结构,如下图所示,我们可以看到偏移为+0x018
的位置就是TEB结构头
指针,在该地址基础上向下偏移0x30
就可以得到PEB(进程环境块)
的基地址。
其中+0x000
的位置指向了NT_TIB
结构,+0x018
指向NT_TIB结构
TEB自身,+0x020+0x000
指向的是当前进程的PID,+0x020+0x004
则指向父进程的PPID,+0x030
指向了PEB结构体,其他字段读者可自行查阅官方文档;
接着再来验证一下,首先偏移地址0x18
是TEB结构基地址,也就是指向自身偏移fs:[0x18]
的位置,而!teb
地址加0x30
正是PEB
的位置,在teb的基础上加上0x30
就可以得到PEB的基地址,拿到PEB基地址就可以干很多事了。
在线程环境块内,fs:[0x18]
定位到TEB(线程环境块)
,加上0x20
得到ClientId
,此处存储的就是进程与线程ID的结构位置,通过+0x000
可得到UniqueProcess
也就是进程PID,通过+0x004
可得到UniqueThread
也就是线程TID,读者可输入如下所示的命令自行验证;
0:000> dd fs:[0x18] # 找到TEB基地址
0053:00000018 0081e000 00000000 0000139c 0000194c
0053:00000028 00000000 0081e02c 0081b000 00000000
0: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信息,其完整代码片段如下所示,当读者向GetSelfID()
传入1则表示读取PID,否则则读取TID信息;
#include <stdio.h>
#include <Windows.h>
// 传入1获取PID传入0获取TID
DWORD GetSelfID(DWORD isPid)
{
DWORD ref = 0;
if (isPid == 1)
{
__asm
{
mov eax, fs:[0x18] // 获取到PEB基地址
add eax, 0x20 // 加上20得到 _CLIENT_ID
add eax, 0x0 // 加上偏移0得到 UniqueProcess
mov eax, [eax] // 取出内存地址中的值
mov ref, eax
}
}
else
{
__asm
{
mov eax, fs:[0x18] // 获取到PEB基地址
add eax, 0x20 // 加上20得到 _CLIENT_ID
add eax, 0x04 // 加上偏移04得到 UniqueThread
mov 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;
}
读者可运行上述代码,并自行打开任务管理器验证是否可以正确获取到,此处执行效果图如下所示;
本文作者: 王瑞
本文链接: https://www.lyshark.com/post/f60a0f6f.html
版权声明: 本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!