驅動開發:內核強制結束進程運行

通常使用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內的所有線程信息,效果如下;

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章