淺談利用 TEB 實現的反跟蹤 (6千字)

看雪資料發表於2003-02-09

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

相關文章