在開始使用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 許可協議。轉載請註明出處!