7.2 Windows驅動開發:內核註冊並監控對象回調

在筆者上一篇文章《內核枚舉進程與線程ObCall回調》簡單介紹瞭如何枚舉系統中已經存在的進程與線程回調,本章LyShark將通過對象回調實現對進程線程的句柄監控,在內核中提供了ObRegisterCallbacks回調,使用這個內核回調函數,可註冊一個對象回調,不過目前該函數只能監控進程與線程句柄操作,通過監控進程或線程句柄,可實現保護指定進程線程不被終止的目的。

ObRegisterCallbacks是Windows操作系統提供的一個內核API函數,它允許開發者註冊一個回調函數,用於監控對象的創建、打開、關閉和刪除等事件。對象可以是文件、目錄、進程、線程、註冊表鍵等等。

當操作系統創建、打開、關閉或刪除一個對象時,它會觸發註冊的回調函數,然後在回調函數中調用開發者定義的代碼。開發者可以在回調函數中執行自定義的邏輯,例如記錄日誌、過濾敏感數據、或者阻止某些操作。

ObRegisterCallbacks函數提供了多個回調函數的註冊,這些回調函數包括:

  • PreOperation: 在操作執行之前被調用,可以阻止或修改操作。
  • PostOperation: 在操作執行之後被調用,可以記錄操作結果或者清理資源。

需要注意的是,註冊回調函數需要開發者有一定的內核開發經驗,並且需要遵守一些約束條件,例如不能阻塞或掛起對象的操作,不能調用一些內核API函數等。

內核註冊並監控對象回調ObRegisterCallbacks在安全軟件、系統監控和調試工具等領域有着廣泛的應用。開發者可以利用這個機制來監控系統對象的使用情況,以保護系統安全。

由於目前對象回調只能監控進程與線程,而這個監控是通過ObjectType這麼一個成員控制的,如果成員是PsProcessType則代表監控進程,反之PsThreadType則是監控線程,無論監控進程還是線程都調用ObRegisterCallbacks這個函數來完成註冊。

函數ObRegisterCallbacks其微軟對他的定義是這樣的,用戶傳入OB_OPERATION_REGISTRATION結構,以及OB_CALLBACK_REGISTRATION回調結構,其中PreOperation則是傳入的回調函數,也是最重要的,其次是ObjectType指定成進程回調。

NTSTATUS ObRegisterCallbacks(
  [in]  POB_CALLBACK_REGISTRATION CallbackRegistration,
  [out] PVOID                     *RegistrationHandle
);

首先來實現一個檢測的案例,註冊一個進程回調對象MyLySharkComObjectCallBack,通過ObRegisterCallbacks註冊的回調只需要傳入一個填充好的OB_CALLBACK_REGISTRATION回調結構體,以及一個全局句柄即可,這個全局句柄的作用僅僅只是在程序結束時,調用ObUnRegisterCallbacks卸載監控而已,實現代碼如下所示。

#include <ntddk.h>
#include <ntstrsafe.h>

PVOID Globle_Object_Handle;

// 繞過簽名檢測
void BypassCheckSign(PDRIVER_OBJECT pDriverObj)
{
    typedef struct _LDR_DATA
    {
        struct _LIST_ENTRY InLoadOrderLinks;
        struct _LIST_ENTRY InMemoryOrderLinks;
        struct _LIST_ENTRY InInitializationOrderLinks;
        VOID*        DllBase;
        VOID*        EntryPoint;
        ULONG32      SizeOfImage;
        UINT8        _PADDING0_[0x4];
        struct _UNICODE_STRING FullDllName;
        struct _UNICODE_STRING BaseDllName;
        ULONG32      Flags;
    }LDR_DATA, *PLDR_DATA;

    PLDR_DATA ldr;
    ldr = (PLDR_DATA)(pDriverObj->DriverSection);
    ldr->Flags |= 0x20;
}

// 自定義回調
OB_PREOP_CALLBACK_STATUS MyLySharkComObjectCallBack(PVOID RegistrationContext, POB_PRE_OPERATION_INFORMATION OperationInformation)
{
    DbgPrint("[lyshark] 執行回調函數... \n");
    return STATUS_SUCCESS;
}

VOID UnDriver(PDRIVER_OBJECT driver)
{
    ObUnRegisterCallbacks(Globle_Object_Handle);
    DbgPrint("回調卸載完成... \n");
}

NTSTATUS DriverEntry(IN PDRIVER_OBJECT Driver, PUNICODE_STRING RegistryPath)
{
    DbgPrint("hello lyshark \n");

    BypassCheckSign(Driver);

    OB_OPERATION_REGISTRATION Base;                          // 回調函數結構體
    OB_CALLBACK_REGISTRATION CallbackReg;                    // 回調函數

    CallbackReg.RegistrationContext = NULL;                  // 註冊上下文(你回調函數返回參數)
    CallbackReg.Version = OB_FLT_REGISTRATION_VERSION;       // 註冊回調版本
    CallbackReg.OperationRegistration = &Base;               // 回調結構體
    CallbackReg.OperationRegistrationCount = 1;              // 操作計數(下鉤數量)

    RtlUnicodeStringInit(&CallbackReg.Altitude, L"600000");   // 長度
    Base.ObjectType = PsProcessType;                          // 進程操作類型.此處爲進程操作
    Base.Operations = OB_OPERATION_HANDLE_CREATE;             // 操作句柄創建
    Base.PreOperation = MyLySharkComObjectCallBack;           // 你自己的回調函數
    Base.PostOperation = NULL;

    // 註冊回調
    if (ObRegisterCallbacks(&CallbackReg, &Globle_Object_Handle))
    {
        DbgPrint("[lyshark message] 回調註冊成功...");
    }
    Driver->DriverUnload = UnDriver;
    return STATUS_SUCCESS;
}

當驅動程序被加載以後,一旦有進程運行則會執行我們自己的MyLySharkComObjectCallBack回調,而在回調函數內則可以執行任意功能,運行如下所示。

如上所示只是演示基本的回調申請流程,回調函數通常需要包含兩個值,其一RegistrationContext用於標註上下文,其二POB_PRE_OPERATION_INFORMATION則用於標註進程或者線程創建的信息結構體。

OB_PREOP_CALLBACK_STATUS MyLySharkComObjectCallBack(PVOID RegistrationContext, POB_PRE_OPERATION_INFORMATION OperationInformation)

那麼如何實現攔截進程啓動這個功能呢,我們可以在回調函數中寫入以下代碼進行攔截。

  • CreateHandleInformation.DesiredAccess 將打開句柄的權限清零
  • CreateHandleInformation.OriginalDesiredAccess 判斷是否終止
if (pOperationInformation->Operation == OB_OPERATION_HANDLE_CREATE)
{
    DbgPrint("lyshark.exe 進程打開 \n");
    pOperationInformation->Parameters->CreateHandleInformation.DesiredAccess=0;
    if ((pOperationInformation->Parameters->CreateHandleInformation.OriginalDesiredAccess & PROCESS_TERMINATE) == PROCESS_TERMINATE)
    {
        pOperationInformation->Parameters->CreateHandleInformation.DesiredAccess &= ~PROCESS_TERMINATE;
    }
}

攔截進程創建核心代碼如下所示。

#include <ntddk.h>
#include <ntstrsafe.h>

#define PROCESS_TERMINATE 0x1

// 導出兩個API
NTKERNELAPI PEPROCESS IoThreadToProcess(PETHREAD Thread);
NTKERNELAPI char* PsGetProcessImageFileName(PEPROCESS Process);

// 全局句柄
PVOID Globle_Object_Handle = NULL;

// 繞過簽名檢測
void BypassCheckSign(PDRIVER_OBJECT pDriverObj)
{
    typedef struct _LDR_DATA
    {
        struct _LIST_ENTRY InLoadOrderLinks;
        struct _LIST_ENTRY InMemoryOrderLinks;
        struct _LIST_ENTRY InInitializationOrderLinks;
        VOID*        DllBase;
        VOID*        EntryPoint;
        ULONG32      SizeOfImage;
        UINT8        _PADDING0_[0x4];
        struct _UNICODE_STRING FullDllName;
        struct _UNICODE_STRING BaseDllName;
        ULONG32      Flags;
    }LDR_DATA, *PLDR_DATA;

    PLDR_DATA ldr;
    ldr = (PLDR_DATA)(pDriverObj->DriverSection);
    ldr->Flags |= 0x20;
}

// 判斷是否是需要保護的進程
BOOLEAN CheckProcess(PEPROCESS eprocess)
{
    char *Name = PsGetProcessImageFileName(eprocess);
    if (!_stricmp("lyshark.exe", Name))
        return TRUE;
    else
        return FALSE;
}

// 進程回調
OB_PREOP_CALLBACK_STATUS MyLySharkProcessObjectCallBack(PVOID RegistrationContext, POB_PRE_OPERATION_INFORMATION pOperationInformation)
{
    HANDLE pid;

    // 只取出進程回調
    if (pOperationInformation->ObjectType != *PsProcessType)
    {
        return OB_PREOP_SUCCESS;
    }

    // 得到所有進程的ID
    pid = PsGetProcessId((PEPROCESS)pOperationInformation->Object);
    // DbgPrint("進程PID= %ld \n", pid);

    UNREFERENCED_PARAMETER(RegistrationContext);

    // 驗證是否是需要的進程
    if (CheckProcess((PEPROCESS)pOperationInformation->Object))
    {
        // 創建句柄
        if (pOperationInformation->Operation == OB_OPERATION_HANDLE_CREATE)
        {
            DbgPrint("lyshark.exe 進程打開事件 \n");
            pOperationInformation->Parameters->CreateHandleInformation.DesiredAccess=0;
            if ((pOperationInformation->Parameters->CreateHandleInformation.OriginalDesiredAccess & PROCESS_TERMINATE) == PROCESS_TERMINATE)
            {
                DbgPrint("[LyShark Message] 攔截進程打開 \n");
                pOperationInformation->Parameters->CreateHandleInformation.DesiredAccess &= ~PROCESS_TERMINATE;
            }
        }
        // 複製句柄
        if (pOperationInformation->Operation == OB_OPERATION_HANDLE_DUPLICATE)
        {
            DbgPrint("lyshark.exe 進程被關閉 \n");
            pOperationInformation->Parameters->DuplicateHandleInformation.DesiredAccess=0;
            if ((pOperationInformation->Parameters->DuplicateHandleInformation.OriginalDesiredAccess & PROCESS_TERMINATE) == PROCESS_TERMINATE)
            {
                pOperationInformation->Parameters->DuplicateHandleInformation.DesiredAccess &= ~PROCESS_TERMINATE;
            }
        }
    }
    return OB_PREOP_SUCCESS;
}

VOID UnDriver(PDRIVER_OBJECT driver)
{
    ObUnRegisterCallbacks(Globle_Object_Handle);
    DbgPrint("回調卸載完成... \n");
}

NTSTATUS DriverEntry(IN PDRIVER_OBJECT Driver, PUNICODE_STRING RegistryPath)
{
    DbgPrint("hello lyshark \n");

    BypassCheckSign(Driver);

    OB_OPERATION_REGISTRATION ob_process_callback;
    OB_CALLBACK_REGISTRATION op_process_operation;

    memset(&ob_process_callback, 0, sizeof(ob_process_callback));
    ob_process_callback.ObjectType = PsProcessType;
    ob_process_callback.Operations = OB_OPERATION_HANDLE_CREATE | OB_OPERATION_HANDLE_DUPLICATE;
    ob_process_callback.PreOperation = MyLySharkProcessObjectCallBack;
    ob_process_callback.PostOperation = NULL;

    RtlUnicodeStringInit(&op_process_operation.Altitude, L"600000");
    op_process_operation.RegistrationContext = NULL;
    op_process_operation.Version = OB_FLT_REGISTRATION_VERSION;
    op_process_operation.OperationRegistration = &ob_process_callback;
    op_process_operation.OperationRegistrationCount = 1;

    // 註冊進程回調
    if (ObRegisterCallbacks(&op_process_operation, &Globle_Object_Handle))
    {
        DbgPrint("進程回調註冊成功...");
    }

    Driver->DriverUnload = UnDriver;
    return STATUS_SUCCESS;
}

加載這個驅動,當有進程被創建時,則首先判斷是否是lyshark.exe如果是則直接禁止打開,也就是終止掉。

同理進程可以被攔截,那麼如果增加更多的過濾條件,則線程同樣可以被攔截,攔截線程代碼如下所示。

#include <ntddk.h>
#include <ntstrsafe.h>

#define THREAD_TERMINATE2 0x1

// 導出兩個API
NTKERNELAPI PEPROCESS IoThreadToProcess(PETHREAD Thread);
NTKERNELAPI char* PsGetProcessImageFileName(PEPROCESS Process);

// 全局句柄
PVOID Globle_Object_Handle = NULL;

// 繞過簽名檢測
void BypassCheckSign(PDRIVER_OBJECT pDriverObj)
{
    typedef struct _LDR_DATA
    {
        struct _LIST_ENTRY InLoadOrderLinks;
        struct _LIST_ENTRY InMemoryOrderLinks;
        struct _LIST_ENTRY InInitializationOrderLinks;
        VOID*        DllBase;
        VOID*        EntryPoint;
        ULONG32      SizeOfImage;
        UINT8        _PADDING0_[0x4];
        struct _UNICODE_STRING FullDllName;
        struct _UNICODE_STRING BaseDllName;
        ULONG32      Flags;
    }LDR_DATA, *PLDR_DATA;

    PLDR_DATA ldr;
    ldr = (PLDR_DATA)(pDriverObj->DriverSection);
    ldr->Flags |= 0x20;
}

// 判斷是否是需要保護的進程
BOOLEAN CheckProcess(PEPROCESS eprocess)
{
    char *Name = PsGetProcessImageFileName(eprocess);
    if (!_stricmp("lyshark.exe", Name))
        return TRUE;
    else
        return FALSE;
}

// 線程回調
OB_PREOP_CALLBACK_STATUS MyThreadObjectCallBack(PVOID RegistrationContext, POB_PRE_OPERATION_INFORMATION pOperationInformation)
{
    PEPROCESS ep;
    PETHREAD et;
    HANDLE pid;

    // 線程過濾
    if (pOperationInformation->ObjectType != *PsThreadType)
    {
        return OB_PREOP_SUCCESS;
    }

    et = (PETHREAD)pOperationInformation->Object;
    ep = IoThreadToProcess(et);
    pid = PsGetProcessId(ep);

    // DbgPrint("線程PID= %ld | TID= %ld \n", pid, PsGetThreadId(et));
    UNREFERENCED_PARAMETER(RegistrationContext);

    if (CheckProcess(ep))
    {
        if (pOperationInformation->Operation == OB_OPERATION_HANDLE_CREATE)
        {
            pOperationInformation->Parameters->CreateHandleInformation.DesiredAccess=0;
            if ((pOperationInformation->Parameters->CreateHandleInformation.OriginalDesiredAccess & THREAD_TERMINATE2) == THREAD_TERMINATE2)
            {
                DbgPrint("[LyShark] 攔截lyshark.exe進程內 %d 線程創建 \n", PsGetThreadId(et));
                pOperationInformation->Parameters->CreateHandleInformation.DesiredAccess &= ~THREAD_TERMINATE2;
            }
        }
        if (pOperationInformation->Operation == OB_OPERATION_HANDLE_DUPLICATE)
        {
            pOperationInformation->Parameters->DuplicateHandleInformation.DesiredAccess=0;
            if ((pOperationInformation->Parameters->DuplicateHandleInformation.OriginalDesiredAccess & THREAD_TERMINATE2) == THREAD_TERMINATE2)
            {
                pOperationInformation->Parameters->DuplicateHandleInformation.DesiredAccess &= ~THREAD_TERMINATE2;
            }
        }
    }
    return OB_PREOP_SUCCESS;
}

VOID UnDriver(PDRIVER_OBJECT driver)
{
    ObUnRegisterCallbacks(Globle_Object_Handle);
    DbgPrint("回調卸載完成... \n");
}

NTSTATUS DriverEntry(IN PDRIVER_OBJECT Driver, PUNICODE_STRING RegistryPath)
{
    DbgPrint("hello lyshark \n");

    BypassCheckSign(Driver);

    OB_OPERATION_REGISTRATION ob_thread_callback;
    OB_CALLBACK_REGISTRATION op_thread_operation;

    memset(&ob_thread_callback, 0, sizeof(ob_thread_callback));
    ob_thread_callback.ObjectType = PsThreadType;
    ob_thread_callback.Operations = OB_OPERATION_HANDLE_CREATE | OB_OPERATION_HANDLE_DUPLICATE;
    ob_thread_callback.PreOperation = MyThreadObjectCallBack;
    ob_thread_callback.PostOperation = NULL;

    RtlUnicodeStringInit(&op_thread_operation.Altitude, L"600001");
    op_thread_operation.RegistrationContext = NULL;
    op_thread_operation.Version = OB_FLT_REGISTRATION_VERSION;
    op_thread_operation.OperationRegistration = &ob_thread_callback;
    op_thread_operation.OperationRegistrationCount = 1;

    // 註冊進程回調
    if (ObRegisterCallbacks(&op_thread_operation, &Globle_Object_Handle))
    {
        DbgPrint("進程回調註冊成功...");
    }

    Driver->DriverUnload = UnDriver;
    return STATUS_SUCCESS;
}

這段驅動加載後,如果有新線程被創建,則會被攔截並打印輸出,效果圖如下。

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