通常使用Windows
系統自帶的工作管理員
可以正常地結束
掉一般程序
,而某些特殊的
程序在應用層很難被結束掉,例如某些系統核心程序
其許可權是在0環
核心態,但有時我們不得不想辦法結束掉這些特殊的程序,當然某些正常程序在特殊狀態下也會無法被正常結束,此時使用驅動前行在核心態將其結束掉就變得很有用了,驅動結束程序有多種方法。
ZwOpenProcess
開啟程序獲得控制程式碼,然後使用ZwTerminateProcess
這個核心API實現結束程序,最後使用ZwClose
關閉控制程式碼。PspTerminateThreadByPointer
這個核心函數地址,然後呼叫該函數結束掉程序中所有的執行緒,當執行緒為空則程序也就消亡了。首先是第一種方法結束程序,封裝實現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
沒有被匯出,所以我們需要動態的這個記憶體地址,然後動態呼叫即可,這個尋找方法可以總結為以下步驟。
PsTerminateSystemThread
函數地址,這個地址可以直接通過MmGetSystemRoutineAddress
函數得到。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
內的所有執行緒資訊,效果如下;