驅動開發:核心強制結束程序執行

2022-10-29 12:00:44

通常使用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內的所有執行緒資訊,效果如下;