WINDOWS系統隱含了不少內部資料結構,其記錄著與系統相關的所有重要訊息如線程、進程、核心調用等等,具體如Windows NT/2000模組ntoskrnl.exe中的NtBuildNumber與KeServiceDescriptorTable等(用SoftICE或Visual Studio所帶的Dependency Walker之類的可以看到),前者只是指出當前Windows的Build號(如SoftICE下可用dw命令查出我的機器中為0893h 即十進位2195);後者是指向如下資料結構的指標:
struct _ServiceDescriptorEntry {
unsigned int *ServiceTableBase;
unsigned int *ServiceCounterTableBase;
unsigned int NumberOfServices;
unsigned char *ParamTableBase;
}ServiceDescriptorTableEntry
其典型應用為Mark Russinovich與Bryce Cogswell的Regmon,具體可以參閱www.sysinternals.com.
本文僅在Intel i386的Windows 2000 Server(Build 2195)中對TEB(Thread Environment Block)作初步介紹.
TEB在Windows 9x系列中稱為TIB(Thread Information Block),她紀錄著線程的重要訊息,每一個線程對應一個TEB結構。其格式如下(摘自Matt Pietrek的Under the Hood專欄-MSJ 1996):
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;
在Windows 2000 DDK中定義為:
typedef struct _NT_TIB
{
struct _EXCEPTION_REGISTRATION_RECORD *ExceptionList;
PVOID StackBase;
PVOID StackLimit;
PVOID SubSystemTib;
union {
PVOID FiberData;
ULONG Version;
};
PVOID ArbitraryUserPointer;
struct _NT_TIB *Self;
} NT_TIB;
慶幸的是,Windows在調入進程,建立線程時,作業系統均會為每個線程分配TEB,而且都將FS段選取器(i386)指向當前線程的TEB資料(單CPU機器在任何時刻系統中只有一條線程在執行),這就為我們提供了存取TEB資料的途徑。實際上Windows都是通過這種方法來為你的應用程式提供資訊的,讓我們來看一個例子吧!大家都知道用GetCurrentThreadID API來獲得當前線程ID的,其在Kernel32.dll是如下實現的:
GetCurrentThreadID:
mov eax, FS:[00000018] ; 18h Linear address of TIB structure(TIB結構線性地址)
mov eax, [eax+24] ; 24h ThreadID
ret ; 將EAX中的值返回給調用者
由於TEB結構過於龐大,我現在只來談談位移量為00h的struct _EXCEPTION_REGISTRATION_RECORD *ExceptionList,並結合CIH 1.3源碼來說說它具體用處。ExceptionList主要用於處理SEH(Structured Exception Handling)的。如果你連C語言中新增的_try,_except與_finally也不熟悉的話,建議請先看看Jeffery Richter的<<Advanced Windows NT>>或之類的。
首先讓我們來看看_EXCEPTION_REGISTRATION_RECORD結構,在CRT(C++ RunTime library)源碼中它如下定義:
// Exsup.INC ---Microsoft Visual C++ CRT 源檔案
_EXCEPTION_REGISTRATION struc
prev dd ?
handler dd ?
_EXCEPTION_REGISTRATION ends
其中prev是指向前一_EXCEPTION_REGISTRATION的指標,形成一鏈狀結構,這樣才會在EXCPT.H中有EXCEPTION_CONTINUE_SEARCH這樣的定義(參閱&t;<Advanced Windows NT>>);handler指向異常處理代碼。
CIH正是利用了這一機制,將handler指向它自己程式中。在它入口處有如下代碼:
.
.
.
; *********************************************************
; * Ring3 Virus Game Initial Program *
; *********************************************************
MyVirusStart: ; Ring3代碼進入點
push ebp
; *************************************
; * Let's Modify Structured Exception *
; * Handing, Prevent Exception Error *
; * Occurrence, Especially in NT. *
; *************************************
lea eax, [esp-04h*2] ;在棧中分配8位元組存放_EXCEPTION_REGISTRATION結構
;相當於C中基於棧的資料,即局部變數(C編譯器中完成)
;這樣EAX即指向_EXCEPTION_REGISTRATION的指標,但此時
;_EXCEPTION_REGISTRATION結構未初始化
;具體實現機制可翻閱編譯原理書籍和Matt Pietrek大師文章
xor ebx, ebx ;0->EBX
xchg eax, fs:[ebx] ;FS:[0]<->EAX ;此時EAX存放的是原來異常處理代碼,FS:[0]指向TEB中
;ExceptionList(FS指向TEB,ExceptionList位移為0,即FS:[0])
call @0
@0:
pop ebx ;此三行計算代碼入口,此時ebx就是@0的地址
lea ecx, StopToRunVirusCode-@0[ebx] ;將ecx指向自己內部代碼處
push ecx ;填充_EXCEPTION_REGISTRATION結構的handler
;在發生異常時,作業系統會自動調用,此時為CIH代碼
push eax ;EAX為原來異常處理代碼
;填充_EXCEPTION_REGISTRATION結構的prev
.
.
.
這其後CIH調用int 3使系統發生異常,仍能進入自已的代碼,這可從CIH原始碼中的如下注釋得到證實:
; *************************************
; * Generate Exception to Get Ring0 *
; *************************************
int HookExceptionNumber ; GenerateException
HookExceptionNumber定義為3,此段代碼會產生異常,具體請參閱CIH原始碼。
因為如上代碼比較抽象,我特意將它稍加修改,以便於理解(PE格式可直接在Windows下執行):
// TestCIH.C 有任何問題聯絡tsu00@263.net
#include <windows.h>
#include <stdio.h>
EXCEPTION_DISPOSITION __cdecl _except_handler( //例外處理常式段
struct _EXCEPTION_RECORD *ExceptionRecord,
void * EstablisherFrame,
struct _CONTEXT *ContextRecord,
void * DispatcherContext )
{
printf( "CIH Run Here.../n" );
exit(0); //由於堆棧已被程式打亂,有興趣的可以自己將它恢複,這兒我只簡單的退出
}
void main(void)
{
_asm
{
push ebp
mov eax, esp
sub eax, 8 //這兩行相當於lea eax, [esp-04h*2]
xor ebx, ebx
xchg eax, fs:[ebx]
call next
next:
pop ebx //這三行在這沒實在意義,只是為了與CIH對比
lea ecx, _except_handler //將_except_handler設為異常處理入口
push ecx
push eax
}
_asm
{
mov eax,0
mov [eax],0 //發生STATUS_ACCESS_VIOLATION異常讓作業系統調用_except_handler
}
}
_except_handler回呼函數原形可參閱EXCPT.H
在main函數中第一個_asm段與前面討論的CIH代碼基本一致,而第二個_asm段則試圖寫系統保留記憶體位址,發生異常。
使用Visual C++如下編譯:
c:>Cl testCIH.c
c:>testCIH
CIH Run Here...
在Windows 2000中,運行此段代碼時,出現異常後作業系統將控制權交給_except_handler執行,這樣CIH代碼在NT/2000環境下在系統修改被其保護的記憶體位址時(IDT地區),不至於出現非法操作等提示,以達到保護自己的目的!
我總覺得瞭解系統安全,首先必須對這個系統有足夠的瞭解,就像瞭解CIH病毒一樣,而目前國內在這方面的資料可真謂少之又少,本文僅在這方面說出我自己的一些切身實踐,錯誤之處,在所難免。如果您有任何發現,如果您對這方面有比較有興趣,請聯絡tsu00@263.net.
最後很感謝綠盟高手的指點與協助!
參考文獻:
1.Jeffrey Richter <<Advanced Windows NT>>
2.Matt Pietrek <<A Crash Course on the Depths of Win32 Structured Exception Handling>>
3.CIH 1.3原始碼
------------------------------------------------------------------------------------------------
文章來自:http://www.geocities.jp/webcrazyjp/ntstru1.htm (這傢伙竟用日本的空間網域名稱)
作者:WebCrazy(tsu00@263.net)
注:本文最初見於www.nsfocus.com