驅動開發:內核強制結束進程運行
- 2022 年 10 月 29 日
- 筆記
- Windows 內核安全開發, Windows 內核驅動開發
通常使用Windows
系統自帶的任務管理器
可以正常地結束
掉一般進程
,而某些特殊的
進程在應用層很難被結束掉,例如某些系統核心進程
其許可權是在0環
內核態,但有時我們不得不想辦法結束掉這些特殊的進程,當然某些正常進程在特殊狀態下也會無法被正常結束,此時使用驅動前行在內核態將其結束掉就變得很有用了,驅動結束進程有多種方法。
- 1.標準方法就是使用
ZwOpenProcess
打開進程獲得句柄,然後使用ZwTerminateProcess
這個內核API實現結束進程,最後使用ZwClose
關閉句柄。 - 2.第二種方法,通過動態定位的方式找到
PspTerminateThreadByPointer
這個內核函數地址,然後調用該函數結束掉進程中所有的執行緒,當執行緒為空則進程也就消亡了。 - 3.第三種方法,我將其稱作是記憶體清零法,其核心原理是通過打開進程,得到進程的基址,通過記憶體填充的方式將對端記憶體全部置0實現類似於結束的效果。
首先是第一種方法結束進程,封裝實現KillProcess
函數,用戶傳入lyshark.exe
進程名,進程內執行PsGetProcessImageFileName
判斷是否是我們要結束的如果是則,調用ZwOpenProcess
打開進程,並發送ZwTerminateProcess
終止訊號從而正常結束,其核心程式碼如下所示。
// 署名權
// right to sign one's name on a piece of work
// PowerBy: LyShark
// Email: [email protected]
#include <ntifs.h>
NTKERNELAPI UCHAR* PsGetProcessImageFileName(IN PEPROCESS Process);
// 根據進程ID返回進程EPROCESS結構體,失敗返回NULL
PEPROCESS GetProcessNameByProcessId(HANDLE pid)
{
PEPROCESS ProcessObj = NULL;
NTSTATUS Status = STATUS_UNSUCCESSFUL;
Status = PsLookupProcessByProcessId(pid, &ProcessObj);
if (NT_SUCCESS(Status))
return ProcessObj;
return NULL;
}
// 根據ProcessName獲取到進程的PID號
HANDLE GetPidByProcessName(char *ProcessName)
{
PEPROCESS pCurrentEprocess = NULL;
HANDLE pid = 0;
for (int i = 0; i < 1000000000; i += 4)
{
pCurrentEprocess = GetProcessNameByProcessId((HANDLE)i);
if (pCurrentEprocess != NULL)
{
pid = PsGetProcessId(pCurrentEprocess);
if (strstr(PsGetProcessImageFileName(pCurrentEprocess), ProcessName) != NULL)
{
ObDereferenceObject(pCurrentEprocess);
return pid;
}
ObDereferenceObject(pCurrentEprocess);
}
}
return (HANDLE)-1;
}
// 傳入進程名稱,終止掉該進程
BOOLEAN KillProcess(PCHAR ProcessName)
{
PEPROCESS pCurrentEprocess = NULL;
HANDLE pid = 0;
HANDLE Handle = NULL;
OBJECT_ATTRIBUTES obj;
CLIENT_ID cid = { 0 };
NTSTATUS Status = STATUS_UNSUCCESSFUL;
for (int i = 0; i < 10000000; i += 4)
{
pCurrentEprocess = GetProcessNameByProcessId((HANDLE)i);
if (pCurrentEprocess != NULL)
{
pid = PsGetProcessId(pCurrentEprocess);
// 判斷當前鏡像名稱是否是需要結束的進程
if (strstr(PsGetProcessImageFileName(pCurrentEprocess), ProcessName) != NULL)
{
ObDereferenceObject(pCurrentEprocess);
// 找到後開始結束
InitializeObjectAttributes(&obj, NULL, OBJ_KERNEL_HANDLE | OBJ_CASE_INSENSITIVE, NULL, NULL);
cid.UniqueProcess = (HANDLE)pid;
cid.UniqueThread = 0;
// 打開進程
Status = ZwOpenProcess(&Handle, GENERIC_ALL, &obj, &cid);
if (NT_SUCCESS(Status))
{
// 發送終止訊號
ZwTerminateProcess(Handle, 0);
ZwClose(Handle);
}
ZwClose(Handle);
return TRUE;
}
ObDereferenceObject(pCurrentEprocess);
}
}
return FALSE;
}
VOID UnDriver(PDRIVER_OBJECT driver)
{
DbgPrint("驅動已卸載 \n");
}
NTSTATUS DriverEntry(IN PDRIVER_OBJECT Driver, PUNICODE_STRING RegistryPath)
{
DbgPrint("hello lyshark.com \n");
BOOLEAN Retn;
Retn = KillProcess("lyshark.exe");
DbgPrint("結束狀態: %d \n", Retn);
Driver->DriverUnload = UnDriver;
return STATUS_SUCCESS;
}
我們運行這個驅動,當進程lyshark.exe
存在時則可以看到結束效果,當然這種方式只是在內核層面調用了結束進程函數,其本質上還是正常結束,只是這種方式許可權要大一些僅此而已。
第二種方法,其原理就是將進程內的執行緒全部結束掉從而讓進程自動結束,由於PspTerminateThreadByPointer
沒有被導出,所以我們需要動態的這個記憶體地址,然後動態調用即可,這個尋找方法可以總結為以下步驟。
- 1.尋找
PsTerminateSystemThread
函數地址,這個地址可以直接通過MmGetSystemRoutineAddress
函數得到。 - 2.在
PsTerminateSystemThread
函數地址內向下掃描特徵e80cb6f6ff
得到call nt!PspTerminateThreadByPointer
地址。
根據《驅動開發:內核枚舉LoadImage映像回調》
中使用的SearchMemory
函數實現搜索PspTerminateThreadByPointer
記憶體地址。
// 署名權
// right to sign one's name on a piece of work
// PowerBy: LyShark
// Email: [email protected]
#include <ntifs.h>
// 得到PspTerminateThreadByPointer記憶體地址
PVOID PspTerminateThreadByPointer()
{
UNICODE_STRING ustrFuncName;
PVOID pAddress = NULL;
LONG lOffset = 0;
PVOID pPsTerminateSystemThread = NULL;
PVOID pPspTerminateThreadByPointer = NULL;
// 獲取 PsTerminateSystemThread 函數地址
RtlInitUnicodeString(&ustrFuncName, L"PsTerminateSystemThread");
pPsTerminateSystemThread = MmGetSystemRoutineAddress(&ustrFuncName);
DbgPrint("pPsTerminateSystemThread = 0x%p \n", pPsTerminateSystemThread);
if (NULL == pPsTerminateSystemThread)
{
return 0;
}
// 查找 PspTerminateThreadByPointer 函數地址
/*
1: kd> uf PsTerminateSystemThread
nt!PsTerminateSystemThread:
fffff802`254e6a90 4883ec28 sub rsp,28h
fffff802`254e6a94 8bd1 mov edx,ecx
fffff802`254e6a96 65488b0c2588010000 mov rcx,qword ptr gs:[188h]
fffff802`254e6a9f f7417400040000 test dword ptr [rcx+74h],400h
fffff802`254e6aa6 0f8444081100 je nt!PsTerminateSystemThread+0x110860 (fffff802`255f72f0) Branch
nt!PsTerminateSystemThread+0x1c:
fffff802`254e6aac 41b001 mov r8b,1
fffff802`254e6aaf e80cb6f6ff call nt!PspTerminateThreadByPointer (fffff802`254520c0)
nt!PsTerminateSystemThread+0x24:
fffff802`254e6ab4 4883c428 add rsp,28h
fffff802`254e6ab8 c3 ret
nt!PsTerminateSystemThread+0x110860:
fffff802`255f72f0 b80d0000c0 mov eax,0C000000Dh
fffff802`255f72f5 e9baf7eeff jmp nt!PsTerminateSystemThread+0x24 (fffff802`254e6ab4) Branch
*/
UCHAR pSpecialData[50] = { 0 };
ULONG ulSpecialDataSize = 0;
// fffff802`254e6aaf e80cb6f6ff call nt!PspTerminateThreadByPointer (fffff802`254520c0)
pSpecialData[0] = 0xE8;
ulSpecialDataSize = 1;
// 搜索地址 PsTerminateSystemThread --> PsTerminateSystemThread + 0xff 查找 e80cb6f6ff
pAddress = SearchMemory(pPsTerminateSystemThread, (PVOID)((PUCHAR)pPsTerminateSystemThread + 0xFF), pSpecialData, ulSpecialDataSize);
if (NULL == pAddress)
{
return 0;
}
// 先獲取偏移,再計算地址
lOffset = *(PLONG)pAddress;
pPspTerminateThreadByPointer = (PVOID)((PUCHAR)pAddress + sizeof(LONG) + lOffset);
if (NULL == pPspTerminateThreadByPointer)
{
return 0;
}
return pPspTerminateThreadByPointer;
}
VOID UnDriver(PDRIVER_OBJECT driver)
{
DbgPrint("驅動已卸載 \n");
}
NTSTATUS DriverEntry(IN PDRIVER_OBJECT Driver, PUNICODE_STRING RegistryPath)
{
DbgPrint("hello lyshark.com \n");
PVOID address = PspTerminateThreadByPointer();
DbgPrint("PspTerminateThreadByPointer = 0x%p \n", address);
Driver->DriverUnload = UnDriver;
return STATUS_SUCCESS;
}
運行驅動程式,首先得到PspTerminateThreadByPointer
的記憶體地址,效果如下。
得到記憶體地址以後直接將地址typedef
轉為指針函數,調用並批量結束進程內的執行緒即可。
// 署名權
// right to sign one's name on a piece of work
// PowerBy: LyShark
// Email: [email protected]
#include <ntifs.h>
typedef NTSTATUS(__fastcall *PSPTERMINATETHREADBYPOINTER) (PETHREAD pEThread, NTSTATUS ntExitCode, BOOLEAN bDirectTerminate);
VOID UnDriver(PDRIVER_OBJECT driver)
{
DbgPrint("驅動已卸載 \n");
}
NTSTATUS DriverEntry(IN PDRIVER_OBJECT Driver, PUNICODE_STRING RegistryPath)
{
DbgPrint("hello lyshark.com \n");
PVOID pPspTerminateThreadByPointerAddress = 0xFFFFF802254520C0;
HANDLE hProcessId = 6956;
PEPROCESS pEProcess = NULL;
PETHREAD pEThread = NULL;
PEPROCESS pThreadEProcess = NULL;
NTSTATUS status = STATUS_SUCCESS;
ULONG i = 0;
// 獲取結束進程的進程結構對象EPROCESS
status = PsLookupProcessByProcessId(hProcessId, &pEProcess);
if (!NT_SUCCESS(status))
{
return status;
}
// 遍歷所有執行緒, 並結束所有指定進程的執行緒
for (i = 4; i < 0x80000; i = i + 4)
{
status = PsLookupThreadByThreadId((HANDLE)i, &pEThread);
if (NT_SUCCESS(status))
{
// 獲取執行緒對應的進程結構對象
pThreadEProcess = PsGetThreadProcess(pEThread);
// 結束進程中的執行緒
if (pEProcess == pThreadEProcess)
{
((PSPTERMINATETHREADBYPOINTER)pPspTerminateThreadByPointerAddress)(pEThread, 0, 1);
DbgPrint("結束執行緒: %d \n", i);
}
ObDereferenceObject(pEThread);
}
}
ObDereferenceObject(pEProcess);
Driver->DriverUnload = UnDriver;
return STATUS_SUCCESS;
}
循環結束進程6956
內的所有執行緒資訊,效果如下;