http://hi.baidu.com/csh0w/blog/item/f44412e8cbf12936b90e2d2b.html
標(biāo) 題:
【原創(chuàng)】掃盲貼,HOOK SSDT 短文一篇。
作 者:
梧桐
時(shí) 間: 2008-12-06,23:01
鏈 接: http://bbs.pediy.com/showthread.php?t=78218
//author:梧桐
//轉(zhuǎn)載請(qǐng)注明出處
------------------------很不華麗的分割線------------------------------------------------------
本文章僅供那些在驅(qū)動(dòng)開發(fā)門外徘徊的程序愛好者參考和學(xué)習(xí),大牛就繞過吧,
如有錯(cuò)誤的地方,還請(qǐng)多多指出,不勝感激。
------------------------很不華麗的分割線------------------------------------------------------
?????? 對(duì)于Driver 一詞兒,我想大家都不陌生,它是工作在ring0下的,正是因?yàn)槿绱?,它不不能夠快速上手的,更多的時(shí)候你要熟悉它的技術(shù)資料和接口,還要熟悉底層工作的原理,一不小心搞個(gè)BSOD,那是會(huì)非常郁悶的。
???? 好了,現(xiàn)在讓我們步入正題,首先確認(rèn)你已經(jīng)安裝好了DDK(學(xué)習(xí)驅(qū)動(dòng)開發(fā),推薦WINXP DDK 2600、 Windows XP,?? VS2003),配置好了你的開發(fā)環(huán)境(DDK Wizard),在VS20003里添加WINDDK路徑。
???? 打開VS2003,Tools選項(xiàng),選取 Options(如下圖):

然后,我們找到 Projects(工程),選取VC++ Directories,添加WINDDK路徑:

配置好這些環(huán)境以后,我們開始與驅(qū)動(dòng)的親密接觸吧。
???? 先來建立一個(gè)驅(qū)動(dòng)的工程:

那些默認(rèn)的選項(xiàng),全部取消掉:

接著點(diǎn) Finish ,刪除 Header Files、Resource Files,此時(shí),DDK Wizard 已經(jīng)為我們建立了一套Driver模板了,但細(xì)看,是不是感覺非常非常的亂?
???? OK,我們把它K掉,現(xiàn)在我們自己來打造一個(gè)簡(jiǎn)單的入口點(diǎn)。
????
代碼:
#include "ntddk.h"
//Unload
VOID UnLoad(IN PDRIVER_OBJECT DriverObject)
{
??? DbgPrint("UnLoad Driver.\n");
}
//EntryPoint.
NTSTATUS DriverEntry(IN PDRIVER_OBJECT DriverObject,
??????????? IN PUNICODE_STRING RegistryPath)
{
?? DriverObject->DriverUnload = UnLoad;
?? //TODO
?? return STATUS_SUCCESS;
}
???? 在 DriverName.WXP上點(diǎn)Build,看看objchk\i386目錄下產(chǎn)生的Sys文件,OK,Cool吧,當(dāng)然,我們這個(gè)驅(qū)動(dòng)連任何功能都沒有的,Cool是很Cool,就是Cool得沒內(nèi)涵了,那么我們來實(shí)現(xiàn)點(diǎn)什么功能呢,好吧,讓我們來對(duì)SSDT掛鉤一下吧。
???? 要掛鉤SSDT,就必須先要由內(nèi)核到處一個(gè)KeServiceDescriptorTable,那么我們還要先定義一個(gè)KeServiceDescriptorTable類型的的結(jié)構(gòu)體:
代碼:
typedef struct ServiceDescriptorEntry
{
???????? unsigned int *ServiceTableBase;
???????? unsigned int *ServiceCounterTableBase; //Used only in checked build
???????? unsigned int NumberOfServices;
???????? unsigned char *ParamTableBase;
} SSDTEntry;
???? 定義了KSDT的結(jié)構(gòu),那么我們想想要HOOK那個(gè)函數(shù)呢,好吧,就以ZwTerminateProcess為例,我們開動(dòng),首先定義一個(gè)ZwTerminateProcess函數(shù)結(jié)構(gòu),函數(shù)原型:
代碼:
ZwTerminateProcess(
???????? IN HANDLE ProcessHandle OPTIONAL,
???????? IN NTSTATUS ExitStatus
???????? );
結(jié)構(gòu)定義:
代碼:
typedef NTSTATUS(*_ZwTerminateProcess)(
???????????????? IN HANDLE ProcessHandle OPTIONAL,
???????????????? IN NTSTATUS ExitStatus
???????????????? );
???? 我們要HOOK ZwTerminateProcess,那么我們是不是要先找出它在KSDT中的位置呢,沒錯(cuò),那么我們來定義一個(gè)通過SSDT服務(wù)號(hào)得到函數(shù)地址的宏以達(dá)到我們的目的:
代碼:
#define?? GetSystemFunc(FuncName) KeServiceDescriptorTable.ServiceTableBase[*(PULONG)((PUCHAR)FuncName+1)];
???? 想要達(dá)到改寫SSDT的目的,那么首先要解決的是內(nèi)存保護(hù)機(jī)制的問題,眾所周知,Windows的某些版本對(duì)內(nèi)存區(qū)域啟用了寫保護(hù)的功能,在XP和2003中更為常見,SSDT是只讀的,那怎么辦呢?
???? 有的網(wǎng)友此刻估計(jì)已經(jīng)明白了。
???? 沒錯(cuò),就是 Memory Descriptor List,簡(jiǎn)稱 MDL。有的同學(xué)可能會(huì)問了,MDL究竟是個(gè)什么東西呢?從字面意思看,不難理解,內(nèi)存描述符列表。MDL包含了內(nèi)存區(qū)域的起始、擁有者proc、字節(jié)數(shù)、標(biāo)記等。OK,我們需要先定義一個(gè)MDL的指針。
???? 定義了MDL的指針以后,我們要通過MAPPED系列的參數(shù)來使內(nèi)存擁有可寫性,然后鎖定內(nèi)存中的MDL,那么我們就要定義一個(gè)PVOID的指針,來供MmMap操作。
????
???? 代碼片段:
代碼:
MDLSystemCall = MmCreateMdl(NULL, KeServiceDescriptorTable.ServiceTableBase, KeServiceDescriptorTable.NumberOfServices*4);
if(!MDLSystemCall)
?? return STATUS_UNSUCCESSFUL;
???? 那么,建立了MDL,是不是該填充一下頁數(shù)組啊?
???? 對(duì)的,沒錯(cuò)。
????
代碼:
MmBuildMdlForNonPagedPool(MDLSystemCall);
MDLSystemCall->MdlFlags = MDLSystemCall->MdlFlags | MDL_MAPPED_TO_SYSTEM_VA; //可寫
MappedSCT = MmMapLockedPages(MDLSystemCall, KernelMode);
???? 我們轉(zhuǎn)入HOOK話題,繼續(xù)。
???? 剛才我們已經(jīng)定義了 ZwTerminateProcess 的結(jié)構(gòu)。
????
代碼:
Old_ZwTerminateProcess = (_ZwTerminateProcess)(GetSystemFunc(ZwTerminateProcess));
???? 獲取沒被HOOK之前的ZwTerminateProcess在KSDT中的索引,保存。
???? 那么下一步呢,就是干掉他了,替換為我們的函數(shù),那么我們是不是要構(gòu)造一個(gè)自己的函數(shù)過程呢,恩,沒錯(cuò)。
????
代碼:
NTSTATUS NewZwTerminateProcess(
???????????????? IN HANDLE ProcessHandle OPTIONAL,
???????????????? IN NTSTATUS ExitStatus
???????????????? )
{
?? //TODO
?? return STATUS_SUCCESS;
}
???? 在過程里要怎么玩,全看你自己了。
???? 下面就是替換函數(shù)了,修改SSDT中函數(shù)地址指向的位置,下面是宏定義:
代碼:
#define GetIndex(_foo) *(PULONG)((PUCHAR)_foo+1)
#define HookOn(_Old,_New) InterlockedExchange((PLONG)&MappedSCT[GetIndex(_Old)] ,(LONG)_New)
????
代碼:
MDLSystemCall = MmCreateMdl(NULL, KeServiceDescriptorTable.ServiceTableBase, KeServiceDescriptorTable.NumberOfServices*4);
if(!MDLSystemCall)
?? return STATUS_UNSUCCESSFUL;
MmBuildMdlForNonPagedPool(MDLSystemCall);
MDLSystemCall->MdlFlags = MDLSystemCall->MdlFlags | MDL_MAPPED_TO_SYSTEM_VA; //可寫
MappedSCT = MmMapLockedPages(MDLSystemCall, KernelMode);
HookOn(ZwTerminateProcess, NewZwTerminateProcess);
return STATUS_SUCCESS;
完整代碼:
代碼:
///////////////////////////////////////////////////////////////////////////////
///
/// Copyright (c) 2008 - <company name here>
///
/// Original filename: NtHook.c
/// Project?????????? : NtHook
/// Date of creation : 2008-11-20
/// Author(s)???????? : 梧桐
///
/// Purpose?????????? : <description>
///
/// Revisions:
///?? 0000 [2008-11-20] Initial revision.
///
///////////////////////////////////////////////////////////////////////////////
#include "ntddk.h"
#pragma pack(1)
typedef struct ServiceDescriptorEntry
{
???????? unsigned int *ServiceTableBase;
???????? unsigned int *ServiceCounterTableBase; //Used only in checked build
???????? unsigned int NumberOfServices;
???????? unsigned char *ParamTableBase;
} SSDTEntry;
__declspec(dllimport)?? SSDTEntry KeServiceDescriptorTable;
#pragma pack()
NTKERNELAPI NTSTATUS ZwTerminateProcess(
???????????????? IN HANDLE ProcessHandle OPTIONAL,
???????????????? IN NTSTATUS ExitStatus
???????????????? );
typedef NTSTATUS(*_ZwTerminateProcess)(
???????????????? IN HANDLE ProcessHandle OPTIONAL,
???????????????? IN NTSTATUS ExitStatus
???????????????? );
_ZwTerminateProcess Old_ZwTerminateProcess;
#define GetSystemFunc(FuncName) KeServiceDescriptorTable.ServiceTableBase[*(PULONG)((PUCHAR)FuncName+1)]
PMDL?? MDSystemCall;
PVOID *MappedSCT;
#define GetIndex(_Function) *(PULONG)((PUCHAR)_Function+1)
#define HookOn(_Old, _New)?? \
??????? (PVOID) InterlockedExchange( (PLONG) &MappedSCT[GetIndex(_Old)], (LONG) _New)
#define UnHook(_Old, _New)?? \
??????? InterlockedExchange( (PLONG) &MappedSCT[GetIndex(_Old)], (LONG) _New)
NTSTATUS NewZwTerminateProcess(
???????????????? IN HANDLE ProcessHandle OPTIONAL,
???????????????? IN NTSTATUS ExitStatus
???????????????? )
{
?? return STATUS_SUCCESS;
}
//Unload
VOID UnLoad(IN PDRIVER_OBJECT DriverObject)
{
??? DbgPrint("UnLoad Driver.\n");
??? //卸載Hook
??? UnHook( ZwTerminateProcess, Old_ZwTerminateProcess);
??? //解鎖、釋放MDL
??? if(MDSystemCall)
??? {
?????? MmUnmapLockedPages(MappedSCT, MDSystemCall);
?????? IoFreeMdl(MDSystemCall);
??? }
}
//EntryPoint.
NTSTATUS DriverEntry(IN PDRIVER_OBJECT DriverObject,
??????????? IN PUNICODE_STRING RegistryPath)
{
?? DriverObject->DriverUnload = UnLoad;
??
?? //找出舊函數(shù)地址并保存
?? Old_ZwTerminateProcess =(_ZwTerminateProcess)(GetSystemFunc(ZwTerminateProcess));
?? MDSystemCall = MmCreateMdl(NULL, KeServiceDescriptorTable.ServiceTableBase, KeServiceDescriptorTable.NumberOfServices*4);
?? if(!MDSystemCall)
???? return STATUS_UNSUCCESSFUL;
?? MmBuildMdlForNonPagedPool(MDSystemCall);
?? MDSystemCall->MdlFlags = MDSystemCall->MdlFlags | MDL_MAPPED_TO_SYSTEM_VA;
?? MappedSCT = MmMapLockedPages(MDSystemCall, KernelMode);
?? //安裝HOOK
?? HookOn( ZwTerminateProcess, NewZwTerminateProcess);
?? return STATUS_SUCCESS;
}
工程附件:http://xlick.3video.cn/1.zip
一段類似的代碼
#include "ntddk.h"
ULONG pid=1548;
#pragma pack(1) //SSDT表的結(jié)構(gòu)
typedef struct ServiceDescriptorEntry {
unsigned int *ServiceTableBase;
unsigned int *ServiceCounterTableBase; //Used only in checked build
unsigned int NumberOfServices;
unsigned char *ParamTableBase;
} ServiceDescriptorTableEntry_t, *PServiceDescriptorTableEntry_t;
#pragma pack()
__declspec(dllimport) ServiceDescriptorTableEntry_t KeServiceDescriptorTable; //變量名是不能變的,因?yàn)槭菑耐獠繉?dǎo)入
//取函數(shù)在SSDT中的位置
#define SYSTEMSERVICE(_function) KeServiceDescriptorTable.ServiceTableBase[ *(PULONG)((PUCHAR)_function+1)]
//取函數(shù)的索引
#define SYSCALL_INDEX(_Function) *(PULONG)((PUCHAR)_Function+1)
//修改函數(shù)的地址
#define HOOK_SYSCALL(_Function, _Hook, _Orig ) _Orig = (PVOID) InterlockedExchange( (PLONG) &m_Mapped[SYSCALL_INDEX(_Function)], (LONG) _Hook)
PMDL m_MDL;
PVOID *m_Mapped;
NTSYSAPI NTSTATUS NTAPI ZwOpenProcess(OUT PHANDLE ProcessHandle,IN ACCESS_MASK DesiredAccess,IN POBJECT_ATTRIBUTES ObjectAttributes,IN PCLIENT_ID ClientId OPTIONAL);
NTSYSAPI NTSTATUS NTAPI ZwTerminateProcess(IN HANDLE ProcessHandle OPTIONAL,IN NTSTATUS ExitStatus);
typedef NTSTATUS (*ZWOPENPROCESS)(OUT PHANDLE ProcessHandle,IN ACCESS_MASK DesiredAccess,IN POBJECT_ATTRIBUTES ObjectAttributes,IN PCLIENT_ID ClientId OPTIONAL);
typedef NTSTATUS (*ZWTERMINATEPROCESS)(IN HANDLE ProcessHandle OPTIONAL,IN NTSTATUS ExitStatus);
NTSTATUS NewZwOpenProcess(OUT PHANDLE ProcessHandle,IN ACCESS_MASK DesiredAccess,IN POBJECT_ATTRIBUTES ObjectAttributes,IN PCLIENT_ID ClientId OPTIONAL);
NTSTATUS NewZwTerminateProcess(IN HANDLE ProcessHandle OPTIONAL,IN NTSTATUS ExitStatus);
NTSTATUS PsLookupProcessByProcessId(IN ULONG ulProcId, OUT PEPROCESS *pEProcess);
ZWOPENPROCESS OldZwOpenProcess = NULL;
ZWTERMINATEPROCESS OldZwTerminateProcess = NULL;
VOID OnUnload(IN PDRIVER_OBJECT DriverObject)
{
//卸載時(shí)會(huì)調(diào)用
PVOID Oldfun = NULL;
HOOK_SYSCALL(ZwOpenProcess,OldZwOpenProcess,Oldfun);
HOOK_SYSCALL(ZwTerminateProcess,OldZwTerminateProcess,Oldfun);
if(m_MDL){
?? MmUnmapLockedPages(m_Mapped,m_MDL);
?? IoFreeMdl(m_MDL);
}
KdPrint(("驅(qū)動(dòng)卸載完畢.\n"));
}
NTSTATUS NewZwOpenProcess(OUT PHANDLE ProcessHandle,IN ACCESS_MASK DesiredAccess,IN POBJECT_ATTRIBUTES ObjectAttributes,IN PCLIENT_ID ClientId OPTIONAL)
{
if((long)ClientId->UniqueProcess == pid)
{
?? KdPrint(("保護(hù)進(jìn)程,打開操作 PID:%ld\n",pid));
?? return STATUS_ACCESS_DENIED;
}
//剩下的交給我們的原函數(shù)
return OldZwOpenProcess(ProcessHandle,DesiredAccess,ObjectAttributes,ClientId);
}
NTSTATUS NewZwTerminateProcess(IN HANDLE ProcessHandle OPTIONAL,IN NTSTATUS ExitStatus)
{
NTSTATUS nStatus = STATUS_SUCCESS;
PEPROCESS EPROCESSPROTECT = NULL;
PEPROCESS EPROCESSKILL = NULL;
//我們要保護(hù)的進(jìn)程的PID存在變量pid里,使用PsLookupProcessByProcessId可以通過PID獲得EPROCESS
PsLookupProcessByProcessId((ULONG)pid,&EPROCESSPROTECT);
//通過ProcessHandle來獲得當(dāng)前要結(jié)束的進(jìn)程的EPROCESS
if (ObReferenceObjectByHandle(ProcessHandle,GENERIC_READ,NULL,KernelMode,&EPROCESSKILL,0) == STATUS_SUCCESS)
{
?? //如果要結(jié)束的是我們需要保護(hù)的進(jìn)程,這里分兩種情況
?? if (EPROCESSPROTECT== EPROCESSKILL)
?? {
??? if (EPROCESSPROTECT != PsGetCurrentProcess())
??? {//情況一:當(dāng)前進(jìn)程不是我們所保護(hù)的進(jìn)程
???? //換句話說也就是其他進(jìn)程試圖結(jié)束我們所保護(hù)的進(jìn)程,當(dāng)然不能讓他結(jié)束
???? KdPrint(("[-]進(jìn)程保護(hù),外部程序試圖關(guān)閉保護(hù)進(jìn)程\n"));
???? nStatus = STATUS_ACCESS_DENIED;
??? }else{
???? //當(dāng)我們程序點(diǎn)擊關(guān)閉也是使用的TermianteProcess,
???? //所以這種情況下當(dāng)前進(jìn)程是我們所保護(hù)的進(jìn)程,則正常退出
???? KdPrint(("[-]進(jìn)程保護(hù),程序自身退出請(qǐng)求!\n"));
??? }
???
?? }
}
//剩下的交給我們的原函數(shù)
if (nStatus != STATUS_SUCCESS)
?? return nStatus;
else
?? return OldZwTerminateProcess(ProcessHandle,ExitStatus);
}
NTSTATUS DriverEntry(IN PDRIVER_OBJECT DriverObject,IN PUNICODE_STRING theRegistryPath)
{
NTSTATUS??????? ntStatus = STATUS_SUCCESS;
PDEVICE_OBJECT deviceObject = NULL;
DriverObject->DriverUnload = OnUnload;
m_MDL = MmCreateMdl(NULL,KeServiceDescriptorTable.ServiceTableBase,KeServiceDescriptorTable.NumberOfServices*4);
if(!m_MDL)
{
?? return STATUS_UNSUCCESSFUL;
}
//非分頁內(nèi)存
MmBuildMdlForNonPagedPool(m_MDL);
m_MDL->MdlFlags = m_MDL->MdlFlags | MDL_MAPPED_TO_SYSTEM_VA;
//鎖定
m_Mapped = MmMapLockedPages(m_MDL, KernelMode);
HOOK_SYSCALL(ZwOpenProcess,NewZwOpenProcess,OldZwOpenProcess);
HOOK_SYSCALL(ZwTerminateProcess,NewZwTerminateProcess,OldZwTerminateProcess);
return STATUS_SUCCESS;
}