系統調用篇——總結與提升
- 2022 年 1 月 21 日
- 筆記
- Win系統內核, 羽夏看Win系統內核
寫在前面
此系列是本人一個字一個字碼出來的,包括示例和實驗截圖。由於系統內核的複雜性,故可能有錯誤或者不全面的地方,如有錯誤,歡迎批評指正,本教程將會長期更新。 如有好的建議,歡迎回饋。碼字不易,如果本篇文章有幫助你的,如有閑錢,可以打賞支援我的創作。如想轉載,請把我的轉載資訊附在文章後面,並聲明我的個人資訊和本人部落格地址即可,但必須事先通知我。
你如果是從中間插過來看的,請仔細閱讀 羽夏看Win系統內核——簡述 ,方便學習本教程。
看此教程之前,問幾個問題,基礎知識儲備好了嗎?保護模式篇學會了嗎?系統調用篇學會了嗎?練習做完了嗎?沒有的話就不要繼續了。
🔒 華麗的分割線 🔒
篇章總結
由於本篇章十分簡單,沒學APC,完整的系統調用流程無法講解。我們就用簡單總結一下我們所學:我們首先直接調用我們的函數比如OpenProcess,它會經過層層參數校驗,然後通過將服務號賦給eax,將堆棧賦給esp,通過sysenter或者中斷門進入內核,經過層層處理,調用真正的內核函數,通過APC返回。既然學習了本篇,我們就做一個項目,實現進程只能通過自己關閉,不能通過他人調用關閉,也就是實現所謂的SSDT Hook。比如你打開一個記事本,只能通過點擊關閉按鈕或者菜單退出可以,而不能通過其他程式調用TerminateProcess關閉它。
項目程式碼分析
在分析項目源碼之前,我們來看看效果:
如果沒有寫程式碼的話,就不要繼續了。程式碼可以不和我一樣,只要實現它的功能即可,本項目程式碼僅供參考。
我們的核心功能肯定需要在驅動上,單純的3環是做不到的,實現SSDT Hook,還要修改它,為了程式碼的可讀性和方便性,我們首先定義一下它的結構體和導入變數:
struct SSDT_ITEM
{
PULONG funcTable;
ULONG count;
ULONG limit;
PUCHAR paramTable;
};
extern struct SSDT_ITEM* KeServiceDescriptorTable;
我們阻止他人關閉要保護的程式,首先得知道它是怎樣關閉程式的,如下是任務管理器關閉程式的關鍵函數的偽程式碼:
BOOL __thiscall CProcPage::KillProcess(CProcPage *this, DWORD a2, int a3)
{
// [COLLAPSED LOCAL DECLARATIONS. PRESS KEYPAD CTRL-"+" TO EXPAND]
dwMessageId = 0;
v4 = FindProcInArrayByPID(*(this + 3), a2);
v5 = v4;
if ( !v4 || CProcPage::IsSystemProcess(this, a2, v4) )
return 0;
v7 = *(v5 + 35);
if ( v7 )
v8 = *(v7 + 8);
else
v8 = *(v5 + 2);
v9 = *(v5 + 72);
dwProcessId = v8;
if ( !a3 && CProcPage::QuickConfirm(this, 0x2717u, 0x2719u) != 6 )
return 0;
if ( v7 )
return VDMTerminateTaskWOW(dwProcessId, v9);
CPrivilegeEnable::CPrivilegeEnable(v13, L"SeDebugPrivilege");
v10 = OpenProcess(1u, 0, a2);
v11 = v10;
if ( v10 )
{
if ( TerminateProcess(v10, 1u) )
(*(*this + 24))(this);
else
dwMessageId = GetLastError();
CloseHandle(v11);
}
else
{
dwMessageId = GetLastError();
}
if ( dwMessageId )
{
DisplayFailureMsg(*(this + 1), 0x2721u, dwMessageId);
v12 = 0;
}
else
{
v12 = 1;
}
CPrivilegeEnable::~CPrivilegeEnable(v13);
return v12;
}
可以知道,任務管理器是通過TerminateProcess來關閉這個進程,如果失敗會彈窗顯示錯誤原因。要想Hook,就必須知道在SSDT的服務號,如下是跟蹤流程,見如下分析,我們先定位到該函數:
; BOOL __stdcall TerminateProcess(HANDLE hProcess, UINT uExitCode)
public _TerminateProcess@8
_TerminateProcess@8 proc near ; CODE XREF: .text:7C839AB2↓j
; ConsoleIMERoutine(x)+102↓p
; DATA XREF: ...
hProcess = dword ptr 8
uExitCode = dword ptr 0Ch
mov edi, edi
push ebp
mov ebp, esp
cmp [ebp+hProcess], 0
jnz short loc_7C801E2E
push 6 ; dwErrCode
call _SetLastError@4 ; SetLastError(x)
jmp short loc_7C801E49
; ---------------------------------------------------------------------------
loc_7C801E2E: ; CODE XREF: TerminateProcess(x,x)+9↑j
push [ebp+uExitCode] ; ExitStatus
push [ebp+hProcess] ; ProcessHandle
call ds:__imp__NtTerminateProcess@8 ; NtTerminateProcess(x,x)
test eax, eax
jl short loc_7C801E43
xor eax, eax
inc eax
jmp short loc_7C801E4B
; ---------------------------------------------------------------------------
loc_7C801E43: ; CODE XREF: TerminateProcess(x,x)+22↑j
push eax ; Status
call _BaseSetLastNTError@4 ; BaseSetLastNTError(x)
loc_7C801E49: ; CODE XREF: TerminateProcess(x,x)+12↑j
xor eax, eax
loc_7C801E4B: ; CODE XREF: TerminateProcess(x,x)+27↑j
pop ebp
retn 8
_TerminateProcess@8 endp
上面的函數在kernel32.dll裡面,可以看到它又調用了NtTerminateProcess,這個函數又在ntdll.dll中,如下所示:
; Exported entry 348. NtTerminateProcess
; Exported entry 1157. ZwTerminateProcess
; =============== S U B R O U T I N E =======================================
; __stdcall ZwTerminateProcess(x, x)
public _ZwTerminateProcess@8
_ZwTerminateProcess@8 proc near ; CODE XREF: LdrpGenericExceptionFilter(x,x)+9107↓p
; ___report_gsfailure+E1↓p ...
mov eax, 101h ; NtTerminateProcess
mov edx, 7FFE0300h
call dword ptr [edx]
retn 8
_ZwTerminateProcess@8 endp
最終,我們找到了它的服務號是0x101,剩下的我們就開始寫程式碼了。
既然修改SSDT,我們就必須具有寫許可權,修改自身模組我們可以想改就改沒啥問題,但是要修改其他模組的,就得小心行事了,因為我們不能確保那塊記憶體我們具有寫的許可權。一種方式我們可以修改物理頁屬性,這個我就不贅述了。本項目通過修改CR0的WP位實現繞過只讀,如果忘卻,請自行複習 保護模式篇——中斷與異常和控制暫存器 中的控制暫存器的CR0,具體程式碼如下:
//恢復記憶體保護
void PageProtectOn()
{
__asm
{
mov eax, cr0;
or eax, 10000h;
mov cr0, eax;
sti; //恢復中斷
}
}
//去掉記憶體保護
void PageProtectOff()
{
__asm
{
cli; //屏蔽中斷
mov eax, cr0;
and eax, not 10000h;
mov cr0, eax;
}
}
下面我只需要寫個實現Hook的和接管Hook的函數就行了,如下所示:
typedef NTSTATUS(__stdcall *NtTerminateProcess)(HANDLE ProcessHandle, NTSTATUS ExitStatus);
NtTerminateProcess oldNtTerminateProcess = NULL;
NTSTATUS __stdcall HookNtTerminateProcess(HANDLE ProcessHandle, NTSTATUS ExitStatus)
{
if (protectedProcess != NULL)
{
PEPROCESS pro = IoGetCurrentProcess();
PEPROCESS p;
if (ObReferenceObjectByHandle(ProcessHandle, NULL, *PsProcessType, KernelMode, &p, NULL) == STATUS_SUCCESS)
{
if (p == protectedProcess && pro != protectedProcess)
{
return STATUS_ACCESS_DENIED;
}
}
}
return oldNtTerminateProcess(ProcessHandle, ExitStatus);
}
void HookTerminateProcess()
{
PageProtectOff();
oldNtTerminateProcess = KeServiceDescriptorTable->funcTable[TerminateProcessIndex];
KeServiceDescriptorTable->funcTable[TerminateProcessIndex] = HookNtTerminateProcess;
PageProtectOn();
}
看HookNtTerminateProcess函數,這個函數是用來實現Hook函數接管的,為什麼我還要調用原來的NtTerminateProcess函數呢?是因為所有的3環程式都需要用到它,我必須調用它才行。
剩下的程式碼我就不再贅述了,如下是該項目的完整程式碼:
🔒 點擊查看驅動程式碼 🔒
#include <ntifs.h>
#include <wdm.h>
#include <ntddk.h>
#define TerminateProcessIndex 0x101
UNICODE_STRING Devicename;
UNICODE_STRING SymbolLink;
#define DEVICE_NAME L"\\Device\\ProcessHook"
#define SYMBOL_LINK L"\\??\\ProcessHook"
//操作碼:0x0-0x7FF 被保留,0x800-0xFFF 可用
#define Hook CTL_CODE(FILE_DEVICE_UNKNOWN,0x800,METHOD_BUFFERED,FILE_ANY_ACCESS)
#define UnHook CTL_CODE(FILE_DEVICE_UNKNOWN,0x900,METHOD_BUFFERED,FILE_ANY_ACCESS)
NTSTATUS DEVICE_CONTROL_Dispatch(PDEVICE_OBJECT pDevObj, PIRP pIrp);
NTSTATUS DEVICE_CREATE_Dispatch(PDEVICE_OBJECT pDevObj, PIRP pIrp);
NTSTATUS DEVICE_CLOSE_Dispatch(PDEVICE_OBJECT pDevObj, PIRP pIrp);
PEPROCESS protectedProcess = NULL;
struct SSDT_ITEM
{
PULONG funcTable;
ULONG count;
ULONG limit;
PUCHAR paramTable;
};
extern struct SSDT_ITEM* KeServiceDescriptorTable;
typedef NTSTATUS(__stdcall *NtTerminateProcess)(HANDLE ProcessHandle, NTSTATUS ExitStatus);
NtTerminateProcess oldNtTerminateProcess = NULL;
//恢復記憶體保護
void PageProtectOn()
{
__asm
{
mov eax, cr0;
or eax, 10000h;
mov cr0, eax;
sti;
}
}
//去掉記憶體保護
void PageProtectOff()
{
__asm
{
cli;
mov eax, cr0;
and eax, not 10000h;
mov cr0, eax;
}
}
NTSTATUS __stdcall HookNtTerminateProcess(HANDLE ProcessHandle, NTSTATUS ExitStatus)
{
if (protectedProcess != NULL)
{
PEPROCESS pro = IoGetCurrentProcess();
PEPROCESS p;
if (ObReferenceObjectByHandle(ProcessHandle, NULL, *PsProcessType, KernelMode, &p, NULL) == STATUS_SUCCESS)
{
if (p == protectedProcess && pro != protectedProcess)
{
return STATUS_ACCESS_DENIED;
}
}
}
return oldNtTerminateProcess(ProcessHandle, ExitStatus);
}
void HookTerminateProcess()
{
PageProtectOff();
oldNtTerminateProcess = KeServiceDescriptorTable->funcTable[TerminateProcessIndex];
KeServiceDescriptorTable->funcTable[TerminateProcessIndex] = HookNtTerminateProcess;
PageProtectOn();
}
PDEVICE_OBJECT dobj;
void UnloadDriver(PDRIVER_OBJECT DriverObject)
{
IoDeleteSymbolicLink(&SymbolLink);
IoDeleteDevice(dobj);
if (oldNtTerminateProcess!=NULL)
{
KeServiceDescriptorTable->funcTable[TerminateProcessIndex] = oldNtTerminateProcess;
}
DbgPrint("卸載成功!!!\n");
}
NTSTATUS DriverEntry(PDRIVER_OBJECT DriverObject, PUNICODE_STRING RegistryPath)
{
DriverObject->DriverUnload = UnloadDriver;
RtlInitUnicodeString(&Devicename, DEVICE_NAME);
IoCreateDevice(DriverObject, 0, &Devicename, FILE_DEVICE_UNKNOWN, FILE_DEVICE_SECURE_OPEN, FALSE, &dobj);
RtlInitUnicodeString(&SymbolLink, SYMBOL_LINK);
IoCreateSymbolicLink(&SymbolLink, &Devicename);
DriverObject->Flags |= DO_BUFFERED_IO;
DriverObject->MajorFunction[IRP_MJ_CREATE] = DEVICE_CREATE_Dispatch;
DriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL] = DEVICE_CONTROL_Dispatch;
DriverObject->MajorFunction[IRP_MJ_CLOSE] = DEVICE_CLOSE_Dispatch;
HookTerminateProcess();
DbgPrint("驅動載入完畢!!!\n");
return STATUS_SUCCESS;
}
NTSTATUS DEVICE_CONTROL_Dispatch(PDEVICE_OBJECT pDevObj, PIRP pIrp)
{
NTSTATUS status = STATUS_SUCCESS;
PIO_STACK_LOCATION pIrqlStack;
ULONG uIoControlCode;
PVOID pIoBuffer;
ULONG uInLength;
ULONG uOutLength;
ULONG uRead;
//獲取IRP教據
pIrqlStack = IoGetCurrentIrpStackLocation(pIrp);
//獲取控制碼
uIoControlCode = pIrqlStack->Parameters.DeviceIoControl.IoControlCode;
pIoBuffer = pIrp->AssociatedIrp.SystemBuffer;
uInLength = pIrqlStack->Parameters.DeviceIoControl.InputBufferLength;
uOutLength = pIrqlStack->Parameters.DeviceIoControl.OutputBufferLength;
switch (uIoControlCode)
{
case Hook:
RtlMoveMemory(&uRead, pIoBuffer, 4);
if (PsLookupProcessByProcessId((HANDLE)uRead, &protectedProcess) == STATUS_SUCCESS)
{
DbgPrint("得到消息,目前 EPROCESS 地址為:0x%p", protectedProcess);
}
else
{
status = STATUS_INVALID_HANDLE;
}
break;
case UnHook:
protectedProcess = NULL;
break;
default:
status = STATUS_INVALID_DEVICE_REQUEST;
break;
}
//設置返回狀態,否則默認是失敗
pIrp->IoStatus.Status = status;
pIrp->IoStatus.Information = 0; //返回給3環多少位元組數據,沒有填0
IoCompleteRequest(pIrp, IO_NO_INCREMENT);
return STATUS_SUCCESS;
}
NTSTATUS DEVICE_CREATE_Dispatch(PDEVICE_OBJECT pDevObj, PIRP pIrp)
{
DbgPrint("Create Success!\n");
//設置返回狀態,否則默認是失敗
pIrp->IoStatus.Status = STATUS_SUCCESS;
pIrp->IoStatus.Information = 0; //返回給3環多少位元組數據,沒有填0
IoCompleteRequest(pIrp, IO_NO_INCREMENT);
return STATUS_SUCCESS;
}
NTSTATUS DEVICE_CLOSE_Dispatch(PDEVICE_OBJECT pDevObj, PIRP pIrp)
{
DbgPrint("Close Success!\n");
//設置返回狀態,否則默認是失敗
pIrp->IoStatus.Status = STATUS_SUCCESS;
pIrp->IoStatus.Information = 0; //返回給3環多少位元組數據,沒有填0
IoCompleteRequest(pIrp, IO_NO_INCREMENT);
return STATUS_SUCCESS;
}
🔒 點擊查看應用程式碼 🔒
#include "stdafx.h"
#include <windows.h>
#include <winioctl.h>
#include <stdlib.h>
#define Hook CTL_CODE(FILE_DEVICE_UNKNOWN,0x800,METHOD_BUFFERED,FILE_ANY_ACCESS)
#define UnHook CTL_CODE(FILE_DEVICE_UNKNOWN,0x900,METHOD_BUFFERED,FILE_ANY_ACCESS)
#define SYMBOL_LINK_NAME L"\\\\.\\ProcessHook"
HANDLE g_Device;
int main(int argc, char* argv[])
{
//獲取驅動鏈接對象句柄
g_Device=CreateFileW(SYMBOL_LINK_NAME,GENERIC_READ|GENERIC_WRITE,0,0,OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL,0);
if (g_Device==INVALID_HANDLE_VALUE)
{
puts("訪問驅動符號鏈接失敗!");
goto endproc;
}
DWORD pid;
DWORD outBuffer;
DWORD lbret;
puts("請輸入需要保護的程式的 PID :");
scanf("%d",&pid);
if (DeviceIoControl(g_Device,Hook,&pid,sizeof(DWORD),&outBuffer,sizeof(DWORD),&lbret,NULL))
{
puts("保護命令正在發送,請測試……");
}
CloseHandle(g_Device);
endproc:
system("pause");
return 0;
}
SSDT Hook 隱藏嘗試
SSDT Hook在現在是比較爛大街的技術,很多殺軟是基於這個技術進行自保等技術,比如我們上面做的項目就是一個自保的實現。我們上面的項目很容易被PCHunter檢測到:
既然有檢測,肯定有對抗。根據系統調用的流程有一些對抗ARK的SSDT Hook隱藏思路,這裡就提供一些思路,其他具體程式碼的實現需要自行完成。
據他人分析,PCHunter是通過重載內核文件使用SSDT表一個一個比對完成的,什麼是內核重載將會在進程執行緒篇進行講解,但其實不是,沒那麼簡單,下面介紹的方法可以繞過內核重載檢查SSDT。簡單點說,內核重載就是把內核文件重新在記憶體展開,因為內核文件也遵守PE格式。我們從IDA看看SSDT表長啥樣子,由於篇幅,只摘錄一部分:
.data:0047BFA0 _KeServiceDescriptorTable dd 0 ; DATA XREF: KeAddSystemServiceTable(x,x,x,x,x)+11↓r
.data:0047BFA0 ; KeAddSystemServiceTable(x,x,x,x,x)+4D↓w ...
.data:0047BFA4 dword_47BFA4 dd 0 ; DATA XREF: KeAddSystemServiceTable(x,x,x,x,x)+53↓w
.data:0047BFA4 ; KeRemoveSystemServiceTable(x)+47↓w ...
.data:0047BFA8 dword_47BFA8 dd 0 ; DATA XREF: KeAddSystemServiceTable(x,x,x,x,x)+59↓w
.data:0047BFA8 ; KeRemoveSystemServiceTable(x)+4D↓w ...
.data:0047BFAC dword_47BFAC dd 0 ; DATA XREF: KeAddSystemServiceTable(x,x,x,x,x)+5F↓w
.data:0047BFAC ; KeRemoveSystemServiceTable(x)+53↓w ...
咱們逆向分析0環調用流程時,我們也做過思考題,如下所示:
APIService: ; CODE XREF: _KiBBTUnexpectedRange+18↑j
; _KiSystemService+6F↑j
mov edi, eax ; eax = 3環傳來的服務號
shr edi, 8
and edi, 30h
mov ecx, edi ; 正好一個表的大小就是0x10,如果是GUI相關,就加;反之不加。
add edi, [esi+_KTHREAD.ServiceTable] ; edi = ServiceTable
mov ebx, eax ; eax = 3環傳來的服務號
and eax, 0FFFh ; 去掉索引為12的位
cmp eax, [edi+8] ; 得到的結果與ServiceLimit比較
jnb _KiBBTUnexpectedRange ; 如果超出,說明越界,跳走
cmp ecx, 10h
jnz short loc_46660C ; 判斷是否是調用 win32k.sys 的,不是的話跳走
mov ecx, ds:0FFDFF018h ; _DWORD
xor ebx, ebx
我們可以看到,咱們調用內核真實的函數地址是從當前執行緒取出的,也就是說,我們替換當前執行緒的ServiceTable,PCHunter就不會檢測出來。
注意涉及GUI的就不是KeServiceDescriptorTable,而是KeServiceDescriptorTableShadow了。為了替換ServiceTable,我們首先申請一塊記憶體,把SSDTShadow拷貝一份到裡面,然後把該Hook的函數寫入進去即可。我們測試一下(有些知識可能有點超前,可以自行學習玩進程執行緒篇後再來看不懂的程式碼):
🔒 點擊查看驅動程式碼 🔒
#include <ntifs.h>
#include <wdm.h>
#include <ntddk.h>
#define TerminateProcessIndex 0x101
const int ThreadListAndSerivceTableSpace = 0x1b0 - 0xe0;
UNICODE_STRING Devicename;
UNICODE_STRING SymbolLink;
#define DEVICE_NAME L"\\Device\\ProcessHook"
#define SYMBOL_LINK L"\\??\\ProcessHook"
//操作碼:0x0-0x7FF 被保留,0x800-0xFFF 可用
#define Hook CTL_CODE(FILE_DEVICE_UNKNOWN,0x800,METHOD_BUFFERED,FILE_ANY_ACCESS)
#define UnHook CTL_CODE(FILE_DEVICE_UNKNOWN,0x900,METHOD_BUFFERED,FILE_ANY_ACCESS)
NTSTATUS DEVICE_CONTROL_Dispatch(PDEVICE_OBJECT pDevObj, PIRP pIrp);
NTSTATUS DEVICE_CREATE_Dispatch(PDEVICE_OBJECT pDevObj, PIRP pIrp);
NTSTATUS DEVICE_CLOSE_Dispatch(PDEVICE_OBJECT pDevObj, PIRP pIrp);
PEPROCESS protectedProcess = NULL;
struct SSDT_ITEM
{
PULONG funcTable;
ULONG count;
ULONG limit;
PUCHAR paramTable;
};
extern struct SSDT_ITEM* KeServiceDescriptorTable;
typedef NTSTATUS(__stdcall* NtTerminateProcess)(HANDLE ProcessHandle, NTSTATUS ExitStatus);
NtTerminateProcess oldNtTerminateProcess = NULL;
struct SSDT_ITEM* mySSDT;
NTSTATUS __stdcall HookNtTerminateProcess(HANDLE ProcessHandle, NTSTATUS ExitStatus)
{
if (protectedProcess != NULL)
{
PEPROCESS pro = IoGetCurrentProcess();
PEPROCESS p;
if (ObReferenceObjectByHandle(ProcessHandle, NULL, *PsProcessType, KernelMode, &p, NULL) == STATUS_SUCCESS)
{
if (p == protectedProcess && pro != protectedProcess)
{
return STATUS_ACCESS_DENIED;
}
}
}
return oldNtTerminateProcess(ProcessHandle, ExitStatus);
}
void HookTerminateProcess()
{
mySSDT = ExAllocatePool(NonPagedPool, 0x40);
if (mySSDT == NULL)
{
DbgPrint("構建SSDT失敗!");
return;
}
memset(mySSDT, 0, 0x40);
oldNtTerminateProcess = KeServiceDescriptorTable->funcTable[TerminateProcessIndex];
RtlCopyMemory(mySSDT, (INT)KeServiceDescriptorTable - 0x40, 0x40);
mySSDT->funcTable[TerminateProcessIndex] = HookNtTerminateProcess;
}
PDEVICE_OBJECT dobj;
void UnloadDriver(PDRIVER_OBJECT DriverObject)
{
IoDeleteSymbolicLink(&SymbolLink);
IoDeleteDevice(dobj);
if (mySSDT != NULL)
{
ExFreePool(mySSDT);
}
DbgPrint("卸載成功!!!\n");
}
NTSTATUS DriverEntry(PDRIVER_OBJECT DriverObject, PUNICODE_STRING RegistryPath)
{
DriverObject->DriverUnload = UnloadDriver;
RtlInitUnicodeString(&Devicename, DEVICE_NAME);
IoCreateDevice(DriverObject, 0, &Devicename, FILE_DEVICE_UNKNOWN, FILE_DEVICE_SECURE_OPEN, FALSE, &dobj);
RtlInitUnicodeString(&SymbolLink, SYMBOL_LINK);
IoCreateSymbolicLink(&SymbolLink, &Devicename);
DriverObject->Flags |= DO_BUFFERED_IO;
DriverObject->MajorFunction[IRP_MJ_CREATE] = DEVICE_CREATE_Dispatch;
DriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL] = DEVICE_CONTROL_Dispatch;
DriverObject->MajorFunction[IRP_MJ_CLOSE] = DEVICE_CLOSE_Dispatch;
HookTerminateProcess();
DbgPrint("驅動載入完畢!!!\n");
return STATUS_SUCCESS;
}
NTSTATUS DEVICE_CONTROL_Dispatch(PDEVICE_OBJECT pDevObj, PIRP pIrp)
{
NTSTATUS status = STATUS_SUCCESS;
PIO_STACK_LOCATION pIrqlStack;
ULONG uIoControlCode;
PVOID pIoBuffer;
ULONG uInLength;
ULONG uOutLength;
ULONG uRead;
//獲取IRP教據
pIrqlStack = IoGetCurrentIrpStackLocation(pIrp);
//獲取控制碼
uIoControlCode = pIrqlStack->Parameters.DeviceIoControl.IoControlCode;
pIoBuffer = pIrp->AssociatedIrp.SystemBuffer;
uInLength = pIrqlStack->Parameters.DeviceIoControl.InputBufferLength;
uOutLength = pIrqlStack->Parameters.DeviceIoControl.OutputBufferLength;
switch (uIoControlCode)
{
case Hook:
RtlMoveMemory(&uRead, pIoBuffer, 4);
if (PsLookupProcessByProcessId((HANDLE)uRead, &protectedProcess) == STATUS_SUCCESS)
{
DbgPrint("得到消息,目前 EPROCESS 地址為:0x%p", protectedProcess);
LIST_ENTRY* le = (INT)protectedProcess + 0x50;
LIST_ENTRY* pe = le;
while (1)
{
pe = le->Blink;
if (pe == le)
{
break;
}
*(UINT32*)((UINT32)pe - ThreadListAndSerivceTableSpace) = mySSDT;
}
DbgPrint("處理完畢!");
}
else
{
status = STATUS_INVALID_HANDLE;
}
break;
case UnHook:
protectedProcess = NULL;
LIST_ENTRY* le = (INT)protectedProcess + 0x50;
LIST_ENTRY* pe = le;
while (1)
{
pe = le->Blink;
if (pe == le)
{
break;
}
*(UINT32*)((UINT32)pe - ThreadListAndSerivceTableSpace) = KeServiceDescriptorTable;
}
break;
default:
status = STATUS_INVALID_DEVICE_REQUEST;
break;
}
//設置返回狀態,否則默認是失敗
pIrp->IoStatus.Status = status;
pIrp->IoStatus.Information = 0; //返回給3環多少位元組數據,沒有填0
IoCompleteRequest(pIrp, IO_NO_INCREMENT);
return STATUS_SUCCESS;
}
NTSTATUS DEVICE_CREATE_Dispatch(PDEVICE_OBJECT pDevObj, PIRP pIrp)
{
DbgPrint("Create Success!\n");
//設置返回狀態,否則默認是失敗
pIrp->IoStatus.Status = STATUS_SUCCESS;
pIrp->IoStatus.Information = 0; //返回給3環多少位元組數據,沒有填0
IoCompleteRequest(pIrp, IO_NO_INCREMENT);
return STATUS_SUCCESS;
}
NTSTATUS DEVICE_CLOSE_Dispatch(PDEVICE_OBJECT pDevObj, PIRP pIrp)
{
DbgPrint("Close Success!\n");
//設置返回狀態,否則默認是失敗
pIrp->IoStatus.Status = STATUS_SUCCESS;
pIrp->IoStatus.Information = 0; //返回給3環多少位元組數據,沒有填0
IoCompleteRequest(pIrp, IO_NO_INCREMENT);
return STATUS_SUCCESS;
}
應用層程式碼同本篇小結項目。PCHunter還是能夠檢測到該種SSDT Hook。卸載該驅動模組後,PCHunter檢測此方法會有後遺症,如下圖所示:
其實我早已把我構造的假SSDT撤掉了。
GUI 執行緒轉化
普通非GUI執行緒轉化為GUI執行緒的時候,會調用PsConvertToGuiThread函數實現,為了方便理解,直接看其偽程式碼:
int __stdcall PsConvertToGuiThread()
{
// [COLLAPSED LOCAL DECLARATIONS. PRESS KEYPAD CTRL-"+" TO EXPAND]
v0 = KeGetCurrentThread();
v1 = v0;
if ( !v0->PreviousMode )
return 0xC000000D;
if ( !PspW32ProcessCallout )
return 0xC0000022;
if ( v0->ServiceTable != &KeServiceDescriptorTable )
return 0x4000001B;
v7 = v0->ApcState.Process;
if ( !v0->LargeStack )
{
v3 = MmCreateKernelStack(1, v0->InitialNode);
if ( !v3 )
{
ms_exc.registration.TryLevel = 0;
KeGetPcr()->NtTib.Self[1].Self = 8;
ms_exc.registration.TryLevel = -1;
return 0xC0000017;
}
NewIrql = KfRaiseIrql(1u);
v4 = KeSwitchKernelStack(v3, v3 - 12288);
KfLowerIrql(NewIrql);
MmDeleteKernelStack(v4, 0);
}
if ( PPerfGlobalGroupMask && (*(PPerfGlobalGroupMask + 4) & 1) != 0 )
{
v5[6] = *&v1[1].DebugActive;
v5[7] = *&v1[1].Iopl;
v5[0] = v1->StackBase;
v5[1] = v1->StackLimit;
v5[2] = 0;
v5[3] = 0;
v5[4] = 0;
v5[5] = 0;
v6 = -1;
PerfInfoLogBytes(1315, v5, 36);
}
result = PspW32ProcessCallout(v7, 1);
if ( result >= 0 )
{
v1->ServiceTable = &KeServiceDescriptorTableShadow;
result = PspW32ThreadCallout(v1, 0);
if ( result < 0 )
v1->ServiceTable = &KeServiceDescriptorTable;
}
return result;
}
大概流程就是創建更大的執行緒堆棧,換掉並刪除原來的棧,經歷過一定流程的時候,再換ServiceTable,而這個特徵就是判斷是否為GUI執行緒的最明顯的特徵。


