系統調用篇——總結與提升
- 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
線程的最明顯的特徵。