a>
首先宣告一下,本文所講的內容已經是老掉牙的東西了,而且本文所涉及的程式碼似乎只能在 NT 核心下正常發揮作用,如有不對的地方,請各位高手給我指點,謝謝。
:)
我們先來看看本文中最重要的概念―― TEB 。
TEB(Thread Environment Block) 在 Windows
9x 系列中被稱為 TIB(Thread Information Block),它記錄了執行緒的重要資訊,而且每一個執行緒都會對應一個 TEB 結構。 Matt
Pietrek 大牛已經給我們列出了它的結構,我就不多說啦,見下:(摘自 Matt Pietrek 的 Under The Hood - MSJ 1996)
//===========================================================
//
file: TIB.H
// Author: Matt Pietrek
// From: Microsoft Systems Journal
"Under the Hood", May 1996
//===========================================================
#pragma pack(1)
typedef struct _EXCEPTION_REGISTRATION_RECORD
{
struct _EXCEPTION_REGISTRATION_RECORD * pNext;
FARPROC
pfnHandler;
} EXCEPTION_REGISTRATION_RECORD, *PEXCEPTION_REGISTRATION_RECORD;
typedef struct _TIB
{
PEXCEPTION_REGISTRATION_RECORD pvExcept;
// 00h Head of exception record list
PVOID pvStackUserTop;
// 04h Top of user stack
PVOID pvStackUserBase;
// 08h Base of user stack
union
// 0Ch (NT/Win95
differences)
{
struct // Win95 fields
{
WORD pvTDB;
// 0Ch TDB
WORD pvThunkSS;
// 0Eh SS selector used for thunking to 16 bits
DWORD unknown1; // 10h
} WIN95;
struct // WinNT fields
{
PVOID
SubSystemTib; // 0Ch
ULONG FiberData;
// 10h
} WINNT;
} TIB_UNION1;
PVOID pvArbitrary; // 14h Available
for application use
struct _tib *ptibSelf;
// 18h Linear address of TIB structure
union
// 1Ch (NT/Win95
differences)
{
struct // Win95 fields
{
WORD TIBFlags;
// 1Ch
WORD Win16MutexCount;
// 1Eh
DWORD DebugContext;
// 20h
DWORD pCurrentPriority; // 24h
DWORD pvQueue;
// 28h Message Queue selector
} WIN95;
struct
// WinNT fields
{
DWORD unknown1;
// 1Ch
DWORD processID;
// 20h
DWORD threadID;
// 24h
DWORD unknown2;
// 28h
} WINNT;
} TIB_UNION2;
PVOID* pvTLSArray; //
2Ch Thread Local Storage array
union
// 30h (NT/Win95 differences)
{
struct // Win95 fields
{
PVOID* pProcess; // 30h Pointer to owning process
database
} WIN95;
} TIB_UNION3;
} TIB, *PTIB;
#pragma pack()
呵呵,看到那麼多的結構,是不是有點頭暈了?不要緊,我來給大家找出最重要的部分加以解釋。
在 MASM/TASM 下編寫過 SEH 程式碼的朋友,一定會對這三句程式碼非常熟悉:
push offset _SEH_Handler
push fs:[0]
mov fs:[0], esp
這是標準的
SEH 異常處理函式的註冊方法,可是各位朋友有沒有想過為什麼是 fs:[0] 呢?
讓我們抬頭看看上面的 Matt Pietrek 的程式碼,其中有這麼一行:
PEXCEPTION_REGISTRATION_RECORD pvExcept; // 00h Head of exception record
list
注意到 PEXCEPTION_REGISTRATION_RECORD 這個定義,它表示 pvExcept 這個變數正是 exception
record list 的入口,這個入口位於整個結構的 0 偏移處。同時,在 M$ 的 Intel i386 Windows NT/2K/XP 核心中,每當建立一個執行緒,
OS 均會為每個執行緒分配 TEB ,而且 TEB 永遠放在 fs 段選擇器指定的資料段的 0 偏移處。
這樣一來,你就明白了 SEH 註冊的偏移為什麼是在
fs:[0] 了吧?
事實上 Windows 系統都是透過這種方法來為應用程式提供資訊的,比如有這樣的例子:
struct
_tib *ptibSelf; // 18h Linear address of TIB
structure
DWORD threadID;
// 24h
Windows 提供了一個 API :GetCurrentThreadID(),它的內部工作原理其實是這樣的:(利用了上面的這兩個地址)
mov eax, fs:[18h] ;因為 18h 偏移處是 TIB 結構的線性偏移地址
mov eax,
[eax + 24h] ;因為 24h 偏移處是 threadID 的地址
ret
;把 eax 中儲存的 threadID 地址返回
明白了上面的例子,就可以繼續往下看了(羅羅嗦嗦講了一大堆真不好意思……但是上面所說的其實並不是廢話,因為這是理論基礎)。
OK,接下來讓我們看看 30h 處的偏移:
PVOID* pProcess;
// 30h Pointer to owning process database
這個偏移地址處的內容非常有用,它指向本執行緒的擁有者的
PDB(Process Database) 的線性地址。聽起來好像挺高深的,又是“擁有者”,又是“PDB”什麼的……等等,聰明的你是不是已經想到了――“擁有者”意味著什麼?
當你用動態偵錯程式,例如 OllyDbg 的時候,偵錯程式是把除錯的物件作為一個子執行緒進行跟蹤的,在這種情況下,被除錯的物件的“擁有者”就是偵錯程式本身,也就是說,它的
TEB 的 30h 處的偏移指向的內容肯定不為 0 ,這樣,我們就可以利用這一點,判斷 30h 偏移指向的內容,來判斷是否有偵錯程式跟蹤。
原理就說到這裡了,本文只是拋磚引玉,其實 TEB 的內容非常豐富,可以利用的地方還有不少!相信聰明的你一定能對它進行更為深入的挖掘,不過一旦有了什麼研究心得,記得要告訴小弟一聲啊!
:)
最後給出一個 Anti-Debug 的例子程式,用 MASM 編譯完成後,請用 OllyDbg 來載入除錯一下,看看與正常的執行結果有什麼不同。
:)
;*********************************************************
;程式名稱:演示利用
TEB 結構進行 Anti-Debug
; 請用 OllyDbg 進行除錯
;適用OS:Windows NT/2K/XP
;作者:羅聰
;日期:2003-2-9
;出處:http://www.LuoCong.com(老羅的繽紛天地)
;注意事項:如欲轉載,請保持本程式的完整,並註明:
;轉載自“老羅的繽紛天地”(http://www.LuoCong.com)
;*********************************************************
.386
.model flat, stdcall
option casemap:none
include \masm32\include\windows.inc
include \masm32\include\kernel32.inc
include \masm32\include\user32.inc
includelib \masm32\lib\kernel32.lib
includelib \masm32\lib\user32.lib
.data
szCaption db "Anti-Debug Demo by LC, 2003-2-9",
0
szDebugged db "Hah, let me guess... U r dEBUGGINg me! :)",
0
szFine db "Good boy, no dEBUGGEr detected!",
0
.code
main:
assume fs:nothing
mov
eax, fs:[30h] ;指向 PDB(Process
Database)
movzx eax, byte ptr [eax + 2h]
or
al, al
jz _Fine
_Debugged:
push MB_OK or MB_ICONHAND
push
offset szCaption
push offset szDebugged
jmp
_Output
_Fine:
push MB_OK or MB_ICONINformATION
push offset szCaption
push offset
szFine
_Output:
push NULL
call
MessageBoxA
invoke ExitProcess, 0
end
main
老羅
2003-2-9