天天看點

驅動開發:核心監控程序與線程回調

在前面的文章中

LyShark

一直在重複的實作對系統底層子產品的枚舉,今天我們将展開一個新的話題,核心監控,我們以

監控程序線程

建立為例,在

Win10

系統中監控程序與線程可以使用微軟提供給我們的兩個新函數來實作,此類函數的原理是建立一個回調事件,當有程序或線程被建立或者登出時,系統會通過回調機制将該程序相關資訊優先傳回給我們自己的函數待處理結束後再轉向系統層。

程序回調預設會設定

CreateProcess

通知,而線程回調則會設定

CreateThread

通知,我們來看ARK工具中的枚舉效果。

驅動開發:核心監控程式與線程回調
  • 通常情況下:
    • PsSetCreateProcessNotifyRoutineEx 用于監控程序
    • PsSetCreateThreadNotifyRoutine 用于監控線程

監控程序的啟動與退出可以使用

PsSetCreateProcessNotifyRoutineEx

來建立回調,當新程序建立時會優先執行回調,我們看下微軟是如何定義的結構。

// 參數1: 新程序回調函數
// 參數2: 是否登出
NTSTATUS PsSetCreateProcessNotifyRoutineEx(
  [in] PCREATE_PROCESS_NOTIFY_ROUTINE_EX NotifyRoutine,
  [in] BOOLEAN                           Remove
);
           

如上,該函數隻有兩個參數,第一個參數是回調函數,第二個參數是是否登出,通常在驅動退出時可以傳入

TRUE

對該回調進行登出,通常情況下如果驅動關閉,則必須要登出回調,而對于

MyLySharkCreateProcessNotifyEx

自定義回調來說,則需要指定三個必須要有的參數傳遞。

// 參數1: 新程序的EProcess
// 參數2: 新程序PID
// 參數3: 新程序詳細資訊 (僅在建立程序時有效)

VOID MyLySharkCreateProcessNotifyEx(PEPROCESS Process, HANDLE ProcessId, PPS_CREATE_NOTIFY_INFO CreateInfo)
           

根據如上函數定義,就可以實作監控功能了,例如我們監控如果程序名是

lyshark.exe

則直接

CreateInfo->CreationStatus = STATUS_UNSUCCESSFUL

禁止該程序打開。

// 署名權
// right to sign one's name on a piece of work
// PowerBy: LyShark
// Email: [email protected]
#include <ntifs.h>

// 兩個未公開函數導出
NTKERNELAPI PCHAR PsGetProcessImageFileName(PEPROCESS Process);
NTKERNELAPI NTSTATUS PsLookupProcessByProcessId(HANDLE ProcessId, PEPROCESS *Process);

// 通過PID獲得程序名
PCHAR GetProcessNameByProcessId(HANDLE ProcessId)
{
	NTSTATUS st = STATUS_UNSUCCESSFUL;
	PEPROCESS ProcessObj = NULL;
	PCHAR string = NULL;
	st = PsLookupProcessByProcessId(ProcessId, &ProcessObj);
	if (NT_SUCCESS(st))
	{
		string = PsGetProcessImageFileName(ProcessObj);
		ObfDereferenceObject(ProcessObj);
	}
	return string;
}

// 繞過簽名檢查
BOOLEAN BypassCheckSign(PDRIVER_OBJECT pDriverObject)
{
#ifdef _WIN64
	typedef struct _KLDR_DATA_TABLE_ENTRY
	{
		LIST_ENTRY listEntry;
		ULONG64 __Undefined1;
		ULONG64 __Undefined2;
		ULONG64 __Undefined3;
		ULONG64 NonPagedDebugInfo;
		ULONG64 DllBase;
		ULONG64 EntryPoint;
		ULONG SizeOfImage;
		UNICODE_STRING path;
		UNICODE_STRING name;
		ULONG   Flags;
		USHORT  LoadCount;
		USHORT  __Undefined5;
		ULONG64 __Undefined6;
		ULONG   CheckSum;
		ULONG   __padding1;
		ULONG   TimeDateStamp;
		ULONG   __padding2;
	} KLDR_DATA_TABLE_ENTRY, *PKLDR_DATA_TABLE_ENTRY;
#else
	typedef struct _KLDR_DATA_TABLE_ENTRY
	{
		LIST_ENTRY listEntry;
		ULONG unknown1;
		ULONG unknown2;
		ULONG unknown3;
		ULONG unknown4;
		ULONG unknown5;
		ULONG unknown6;
		ULONG unknown7;
		UNICODE_STRING path;
		UNICODE_STRING name;
		ULONG   Flags;
	} KLDR_DATA_TABLE_ENTRY, *PKLDR_DATA_TABLE_ENTRY;
#endif

	PKLDR_DATA_TABLE_ENTRY pLdrData = (PKLDR_DATA_TABLE_ENTRY)pDriverObject->DriverSection;
	pLdrData->Flags = pLdrData->Flags | 0x20;

	return TRUE;
}

// 程序回調函數
VOID My_LyShark_Com_CreateProcessNotifyEx(PEPROCESS Process, HANDLE ProcessId, PPS_CREATE_NOTIFY_INFO CreateInfo)
{
	char ProcName[16] = { 0 };
	if (CreateInfo != NULL)
	{
		strcpy_s(ProcName, 16, PsGetProcessImageFileName(Process));
		DbgPrint("[LyShark] 父程序ID: %ld | 父程序名: %s | 程序名: %s | 程序路徑:%wZ \n", CreateInfo->ParentProcessId, GetProcessNameByProcessId(CreateInfo->ParentProcessId), PsGetProcessImageFileName(Process), CreateInfo->ImageFileName);

		// 判斷是否為指定程序
		if (0 == _stricmp(ProcName, "lyshark.exe"))
		{
			// 禁止打開
			CreateInfo->CreationStatus = STATUS_UNSUCCESSFUL;
		}
	}
	else
	{
		strcpy_s(ProcName, 16, PsGetProcessImageFileName(Process));
		DbgPrint("[LyShark] 程序[ %s ] 退出了, 程式被關閉", ProcName);
	}
}

VOID UnDriver(PDRIVER_OBJECT driver)
{
	DWORD32 ref = 0;

	// 登出程序回調
	ref = PsSetCreateProcessNotifyRoutineEx((PCREATE_PROCESS_NOTIFY_ROUTINE_EX)My_LyShark_Com_CreateProcessNotifyEx, TRUE);
	DbgPrint("[lyshark.com] 登出程序回調: %d \n", ref);
}

NTSTATUS DriverEntry(IN PDRIVER_OBJECT Driver, PUNICODE_STRING RegistryPath)
{
	NTSTATUS status;

	// 繞過簽名檢查
	// LINKER_FLAGS=/INTEGRITYCHECK
	BypassCheckSign(Driver);

	DbgPrint("hello lyshark.com \n");

	// 建立程序回調
	// 參數1: 新程序的EProcess
	// 參數2: 新程序PID
	// 參數3: 新程序詳細資訊 (僅在建立程序時有效)
	status = PsSetCreateProcessNotifyRoutineEx((PCREATE_PROCESS_NOTIFY_ROUTINE_EX)My_LyShark_Com_CreateProcessNotifyEx, FALSE);
	if (!NT_SUCCESS(status))
	{
		DbgPrint("[lyshark.com] 建立程序回調錯誤");
	}
	Driver->DriverUnload = UnDriver;
	return STATUS_SUCCESS;
}
           

編譯并運作這個驅動程式,我們可以在

ARK

工具中看到這個驅動所加載的

CreateProcess

的回調事件。

驅動開發:核心監控程式與線程回調

當驅動加載後,如果你嘗試打開

lyshark.exe

那麼會提示連接配接的裝置沒有發揮作用,我們則成功攔截了這次打開,當然如果在打開程序之前掃描其特征并根據特征拒絕程序打開,那麼就可以實作一個簡單的防惡意程式,程序監控在防惡意程式中也是用的最多的。

驅動開發:核心監控程式與線程回調
// 署名權
// right to sign one's name on a piece of work
// PowerBy: LyShark
// Email: [email protected]
#include <ntifs.h>

// 兩個未公開函數導出
NTKERNELAPI PCHAR PsGetProcessImageFileName(PEPROCESS Process);
NTKERNELAPI NTSTATUS PsLookupProcessByProcessId(HANDLE ProcessId, PEPROCESS *Process);
NTKERNELAPI NTSTATUS PsLookupThreadByThreadId(HANDLE ThreadId, PETHREAD *Thread);

// 繞過簽名檢查
BOOLEAN BypassCheckSign(PDRIVER_OBJECT pDriverObject)
{
#ifdef _WIN64
	typedef struct _KLDR_DATA_TABLE_ENTRY
	{
		LIST_ENTRY listEntry;
		ULONG64 __Undefined1;
		ULONG64 __Undefined2;
		ULONG64 __Undefined3;
		ULONG64 NonPagedDebugInfo;
		ULONG64 DllBase;
		ULONG64 EntryPoint;
		ULONG SizeOfImage;
		UNICODE_STRING path;
		UNICODE_STRING name;
		ULONG   Flags;
		USHORT  LoadCount;
		USHORT  __Undefined5;
		ULONG64 __Undefined6;
		ULONG   CheckSum;
		ULONG   __padding1;
		ULONG   TimeDateStamp;
		ULONG   __padding2;
	} KLDR_DATA_TABLE_ENTRY, *PKLDR_DATA_TABLE_ENTRY;
#else
	typedef struct _KLDR_DATA_TABLE_ENTRY
	{
		LIST_ENTRY listEntry;
		ULONG unknown1;
		ULONG unknown2;
		ULONG unknown3;
		ULONG unknown4;
		ULONG unknown5;
		ULONG unknown6;
		ULONG unknown7;
		UNICODE_STRING path;
		UNICODE_STRING name;
		ULONG   Flags;
	} KLDR_DATA_TABLE_ENTRY, *PKLDR_DATA_TABLE_ENTRY;
#endif

	PKLDR_DATA_TABLE_ENTRY pLdrData = (PKLDR_DATA_TABLE_ENTRY)pDriverObject->DriverSection;
	pLdrData->Flags = pLdrData->Flags | 0x20;

	return TRUE;
}

// 線程回調函數
VOID MyCreateThreadNotify(HANDLE ProcessId, HANDLE ThreadId, BOOLEAN CreateInfo)
{
	PEPROCESS eprocess = NULL;
	PETHREAD ethread = NULL;
	UCHAR *pWin32Address = NULL;

	// 通過此函數拿到程式的EPROCESS結構
	PsLookupProcessByProcessId(ProcessId, &eprocess);
	PsLookupThreadByThreadId(ThreadId, &ethread);

	if (CreateInfo)
	{
		DbgPrint("[lyshark.com] 線程TID: %1d | 所屬程序名: %s | 程序PID: %1d \n", ThreadId, PsGetProcessImageFileName(eprocess), PsGetProcessId(eprocess));
		/*
		if (0 == _stricmp(PsGetProcessImageFileName(eprocess), "lyshark.exe"))
		{
		DbgPrint("線程TID: %1d | 所屬程序名: %s | 程序PID: %1d \n", ThreadId, PsGetProcessImageFileName(eprocess), PsGetProcessId(eprocess));

		// dt _kthread
		// 尋找裡面的 Win32StartAddress 并寫入ret
		pWin32Address = *(UCHAR**)((UCHAR*)ethread + 0x1c8);
		if (MmIsAddressValid(pWin32Address))
		{
		*pWin32Address = 0xC3;
		}
		}
		*/
	}
	else
	{
		DbgPrint("[LyShark] %s 線程已退出...", ThreadId);
	}

	if (eprocess)
		ObDereferenceObject(eprocess);
	if (ethread)
		ObDereferenceObject(ethread);
}

VOID UnDriver(PDRIVER_OBJECT driver)
{
	NTSTATUS status;

	// 登出程序回調
	status = PsRemoveCreateThreadNotifyRoutine(MyCreateThreadNotify);
}

NTSTATUS DriverEntry(IN PDRIVER_OBJECT Driver, PUNICODE_STRING RegistryPath)
{
	NTSTATUS status;

	DbgPrint("hello lyshark.com \n");

	// 繞過簽名檢查
	// LINKER_FLAGS=/INTEGRITYCHECK
	BypassCheckSign(Driver);

	// 建立線程回調
	// 參數1: 新線程ProcessID
	// 參數2: 新線程ThreadID
	// 參數3: 線程建立/退出标志
	status = PsSetCreateThreadNotifyRoutine(MyCreateThreadNotify);
	if (!NT_SUCCESS(status))
	{
		DbgPrint("建立線程回調錯誤");
	}

	Driver->DriverUnload = UnDriver;
	return STATUS_SUCCESS;
}