Windows内核回调
Arcueid 发表于 浙江 二进制安全 176浏览 · 2024-12-21 07:17

内核回调

MSDN中内核回调的介绍如下

内核的回调机制为驱动程序提供了一种在满足某些条件时请求和提供通知的常规方法

windows提供了如下函数来进行注册

PsSetCreateProcessNotifyRoutine
PsSetCreateProcessNotifyRoutineEx
PsSetCreateThreadNotifyRoutine
PsRemoveCreateThreadNotifyRoutine
PsRemoveLoadImageNotifyRoutine
PsSetLoadImageNotifyRoutine

下面简单介绍一下

进程 线程回调

PsSetCreateProcessNotifyRoutine

NTSTATUS
PsSetCreateProcessNotifyRoutine(
    _In_ PCREATE_PROCESS_NOTIFY_ROUTINE NotifyRoutine,
    _In_ BOOLEAN Remove
    );

接收两个参数 第一个是回调函数 第二个表名是注册还是移除

demo如下

VOID TestRoutine(
    _In_ HANDLE ParentId,
    _In_ HANDLE ProcessId,
    _In_ BOOLEAN Create
) {

    if (Create) {
        DbgPrint("PID: %d, ID: %d, Create", ParentId, ProcessId);
    }
    else
    {
        DbgPrint("PID: %d, ID: %d, Remove", ParentId, ProcessId);
    }

}

NTSTATUS UnloadDriver(PDRIVER_OBJECT DriverObject)
{
    PsSetCreateProcessNotifyRoutine((PCREATE_PROCESS_NOTIFY_ROUTINE)TestRoutine, TRUE);

    DbgPrint("Unload!");
}

NTSTATUS DriverEntry(PDRIVER_OBJECT DriverObject, PUNICODE_STRING RegistryPath)
{

    PsSetCreateProcessNotifyRoutine((PCREATE_PROCESS_NOTIFY_ROUTINE)TestRoutine,FALSE);

    DriverObject->DriverUnload = UnloadDriver;

    return STATUS_SUCCESS;
}

PsSetCreateProcessNotifyRoutineEx

类似的 我们先给出demo

VOID TestRoutine(
    _Inout_ PEPROCESS Process,
    _In_ HANDLE ProcessId,
    _Inout_opt_ PPS_CREATE_NOTIFY_INFO CreateInfo
    ) {

    if (CreateInfo) {
        DbgPrint("ID: %d, Create",ProcessId);
    }
    else
    {
        DbgPrint("ID: %d, Remove",ProcessId);
    }

}

NTSTATUS UnloadDriver(PDRIVER_OBJECT DriverObject)
{
    // PsSetCreateProcessNotifyRoutine((PCREATE_PROCESS_NOTIFY_ROUTINE)TestRoutine, TRUE);
    PsSetCreateProcessNotifyRoutineEx((PCREATE_PROCESS_NOTIFY_ROUTINE_EX)TestRoutine, TRUE);
    DbgPrint("Unload!");
}

NTSTATUS DriverEntry(PDRIVER_OBJECT DriverObject, PUNICODE_STRING RegistryPath)
{

    PsSetCreateProcessNotifyRoutineEx((PCREATE_PROCESS_NOTIFY_ROUTINE_EX)TestRoutine,FALSE);

    DriverObject->DriverUnload = UnloadDriver;

    return STATUS_SUCCESS;
}

发现完全没输出 区别在哪儿呢?

可以看见这两个函数 内部都调用了PspSetCreateProcessNotifyRoutine 其中第三个参数不一样

卸载部分简要看看

注册部分通过MmVerifyCallbackFunction判断回调函数合法性

如果想要成功注册回调 要么context为0 要么flags的值必须得有0x20 也就是LDRP_IMAGE_INTEGRITY_FORCED

那么修改flags就行

PLDR_DATA_TABLE_ENTRY ldr = (PLDR_DATA_TABLE_ENTRY)((ULONG_PTR)DriverObject->DriverSection);
    ldr->Flags |= 0x20;

线程的类似 在此不过多赘述

模块回调

PsSetLoadImageNotifyRoutine

demo如下

VOID TestRoutine(
    _In_opt_ PUNICODE_STRING FullImageName,
    _In_ HANDLE ProcessId,                // pid into which image is being mapped
    _In_ PIMAGE_INFO ImageInfo
) {
    DbgPrint("%wZ", FullImageName);

}

NTSTATUS UnloadDriver(PDRIVER_OBJECT DriverObject)
{
    PsRemoveLoadImageNotifyRoutine((PLOAD_IMAGE_NOTIFY_ROUTINE)TestRoutine);
    DbgPrint("Unload!");
}




NTSTATUS DriverEntry(PDRIVER_OBJECT DriverObject, PUNICODE_STRING RegistryPath)
{



    PsSetLoadImageNotifyRoutine((PLOAD_IMAGE_NOTIFY_ROUTINE)TestRoutine);


    DriverObject->DriverUnload = UnloadDriver;

    return STATUS_SUCCESS;
}

可以通过模块回调实现阻止驱动加载

通过ImageInfo获得imageBase

然后解析pe文件 修改入口点执行的代码 直接ret

注意 直接修改会蓝屏 要关闭写保护

void EnableCR0WriteProtection(KIRQL irql)
{
    UINT64 cr0 = __readcr0();
    cr0 |= 0x10000;
    _enable();
    __writecr0(cr0);
    KeLowerIrql(irql);
}



KIRQL DisableCR0WriteProtection()
{
    KIRQL irql = KeRaiseIrqlToDpcLevel();
    UINT64 cr0 = __readcr0();
    cr0 &= 0xfffffffffffeffff;
    __writecr0(cr0);
    _disable();
    return irql;

}

NTSTATUS DieEntry(IN PDRIVER_OBJECT MyDriver, IN PUNICODE_STRING RegistryPath)
{
    return STATUS_UNSUCCESSFUL;
}

VOID TestRoutine(
    _In_opt_ PUNICODE_STRING FullImageName,
    _In_ HANDLE ProcessId,                // pid into which image is being mapped
    _In_ PIMAGE_INFO ImageInfo
) {

    if (ImageInfo->SystemModeImage) {
        PIMAGE_NT_HEADERS pNtHeader = NULL;
        PIMAGE_DOS_HEADER pDosHeader = NULL;
        PULONG Entry = NULL;

        KIRQL  kirql = DisableCR0WriteProtection();


        pDosHeader = (PIMAGE_DOS_HEADER)(ULONG_PTR)ImageInfo->ImageBase;
        pNtHeader = (PIMAGE_NT_HEADERS)((ULONG_PTR)ImageInfo->ImageBase + pDosHeader->e_lfanew);
        Entry = (ULONG_PTR)ImageInfo->ImageBase + pNtHeader->OptionalHeader.AddressOfEntryPoint;

        DbgPrint("%x\n", Entry);

        memcpy(Entry, DieEntry, 32);
        EnableCR0WriteProtection(kirql);

    }

}

NTSTATUS UnloadDriver(PDRIVER_OBJECT DriverObject)
{
    PsRemoveLoadImageNotifyRoutine((PLOAD_IMAGE_NOTIFY_ROUTINE)TestRoutine);
    DbgPrint("Unload!");
}




NTSTATUS DriverEntry(PDRIVER_OBJECT DriverObject, PUNICODE_STRING RegistryPath)
{



    PsSetLoadImageNotifyRoutine((PLOAD_IMAGE_NOTIFY_ROUTINE)TestRoutine);


    DriverObject->DriverUnload = UnloadDriver;

    return STATUS_SUCCESS;
}

对象回调

先看OBJECT_TYPE结构

struct _OBJECT_TYPE
{
    struct _LIST_ENTRY TypeList;                                            //0x0
    struct _UNICODE_STRING Name;                                            //0x8
    VOID* DefaultObject;                                                    //0x10
    UCHAR Index;                                                            //0x14
    ULONG TotalNumberOfObjects;                                             //0x18
    ULONG TotalNumberOfHandles;                                             //0x1c
    ULONG HighWaterNumberOfObjects;                                         //0x20
    ULONG HighWaterNumberOfHandles;                                         //0x24
    struct _OBJECT_TYPE_INITIALIZER TypeInfo;                               //0x28
    struct _EX_PUSH_LOCK TypeLock;                                          //0x78
    ULONG Key;                                                              //0x7c
    struct _LIST_ENTRY CallbackList;                                        //0x80
};

我们关注TypeInfoCallbackList这两个成员

CallbackList是回调函数列表

TypeInfo的结构如下

//0x50 bytes (sizeof)
struct _OBJECT_TYPE_INITIALIZER
{
    USHORT Length;                                                          //0x0
    union
    {
        UCHAR ObjectTypeFlags;                                              //0x2
        struct
        {
            UCHAR CaseInsensitive:1;                                        //0x2
            UCHAR UnnamedObjectsOnly:1;                                     //0x2
            UCHAR UseDefaultObject:1;                                       //0x2
            UCHAR SecurityRequired:1;                                       //0x2
            UCHAR MaintainHandleCount:1;                                    //0x2
            UCHAR MaintainTypeList:1;                                       //0x2
            UCHAR SupportsObjectCallbacks:1;                                //0x2
        };
    };
    ULONG ObjectTypeCode;                                                   //0x4
    ULONG InvalidAttributes;                                                //0x8
    struct _GENERIC_MAPPING GenericMapping;                                 //0xc
    ULONG ValidAccessMask;                                                  //0x1c
    ULONG RetainAccess;                                                     //0x20
    enum _POOL_TYPE PoolType;                                               //0x24
    ULONG DefaultPagedPoolCharge;                                           //0x28
    ULONG DefaultNonPagedPoolCharge;                                        //0x2c
    VOID (*DumpProcedure)(VOID* arg1, struct _OBJECT_DUMP_CONTROL* arg2);   //0x30
    LONG (*OpenProcedure)(enum _OB_OPEN_REASON arg1, CHAR arg2, struct _EPROCESS* arg3, VOID* arg4, ULONG* arg5, ULONG arg6); //0x34
    VOID (*CloseProcedure)(struct _EPROCESS* arg1, VOID* arg2, ULONG arg3, ULONG arg4); //0x38
    VOID (*DeleteProcedure)(VOID* arg1);                                    //0x3c
    LONG (*ParseProcedure)(VOID* arg1, VOID* arg2, struct _ACCESS_STATE* arg3, CHAR arg4, ULONG arg5, struct _UNICODE_STRING* arg6, struct _UNICODE_STRING* arg7, VOID* arg8, struct _SECURITY_QUALITY_OF_SERVICE* arg9, VOID** arg10); //0x40
    LONG (*SecurityProcedure)(VOID* arg1, enum _SECURITY_OPERATION_CODE arg2, ULONG* arg3, VOID* arg4, ULONG* arg5, VOID** arg6, enum _POOL_TYPE arg7, struct _GENERIC_MAPPING* arg8, CHAR arg9); //0x44
    LONG (*QueryNameProcedure)(VOID* arg1, UCHAR arg2, struct _OBJECT_NAME_INFORMATION* arg3, ULONG arg4, ULONG* arg5, CHAR arg6); //0x48
    UCHAR (*OkayToCloseProcedure)(struct _EPROCESS* arg1, VOID* arg2, VOID* arg3, CHAR arg4); //0x4c
};

SupportsObjectCallbacks成员决定了CallbackList是否启用 如果为0 则不启用

OBCALL.sys实现了calc.exe的保护 在修改SupportsObjectCallbacks位后失效

ValidAccessMask 表示权限 句柄的权限与上这个权限就是实际的权限

下面这些**Procedure则是另一组回调函数 优先级高于CallbackList

可以通过ObRegisterCallbacks创建对象回调

ObRegisterCallbacks 接收两个参数 指向_OB_CALLBACK_REGISTRATION结构的指针和注册后的回调句柄

第二个参数在卸载回调的时候需要用到

NTKERNELAPI
NTSTATUS
ObRegisterCallbacks (
    _In_ POB_CALLBACK_REGISTRATION CallbackRegistration,
    _Outptr_ PVOID *RegistrationHandle
    );

typedef struct _OB_OPERATION_REGISTRATION {
    _In_ POBJECT_TYPE                *ObjectType;
    _In_ OB_OPERATION                Operations;
    _In_ POB_PRE_OPERATION_CALLBACK  PreOperation;
    _In_ POB_POST_OPERATION_CALLBACK PostOperation;
} OB_OPERATION_REGISTRATION, *POB_OPERATION_REGISTRATION;

typedef struct _OB_CALLBACK_REGISTRATION {
    _In_ USHORT                     Version;
    _In_ USHORT                     OperationRegistrationCount;
    _In_ UNICODE_STRING             Altitude;
    _In_ PVOID                      RegistrationContext;
    _In_ OB_OPERATION_REGISTRATION  *OperationRegistration;
} OB_CALLBACK_REGISTRATION, *POB_CALLBACK_REGISTRATION;

_OB_OPERATION_REGISTRATION 结构有4个成员

ObjectType 对象类型 可以指定进程线程互斥体等

Operations create还是dump

PreOperation PostOperation 回调函数 pre 和 post代表时机

_OB_CALLBACK_REGISTRATION的5个成员

Version 驱动指定为OB_FLT_REGISTRATION_VERSION 或者使用ObGetFilterVersion函数来获取

OperationRegistrationCount 需要注册几个回调函数

Altitude 海拔 一个字符串 表示加载的优先级 数字越大优先级越低

OperationRegistration 储存回调函数的数组

RegistrationContext 回调的参数

demo如下

PVOID RegistrationHandle = NULL;


NTSTATUS UnloadDriver(PDRIVER_OBJECT DriverObject)
{
    if (RegistrationHandle) {
        ObUnRegisterCallbacks(RegistrationHandle);
    }
    DbgPrint("Unload!");
}



OB_PREOP_CALLBACK_STATUS TestRoutine(
    _In_ PVOID RegistrationContext,
    _Inout_ POB_PRE_OPERATION_INFORMATION OperationInformation
) {

    DbgPrint("111\n");
    return OB_PREOP_SUCCESS;
}


NTSTATUS DriverEntry(PDRIVER_OBJECT DriverObject, PUNICODE_STRING RegistryPath)
{
    OB_OPERATION_REGISTRATION obOpreationRegistration = { 0 };
    OB_CALLBACK_REGISTRATION obCbRegistration = { 0 };

    obOpreationRegistration.ObjectType = *PsProcessType;
    obOpreationRegistration.Operations = OB_OPERATION_HANDLE_CREATE;
    obOpreationRegistration.PreOperation = TestRoutine;


    obCbRegistration.Version = ObGetFilterVersion();
    UNICODE_STRING altitude = {0};
    RtlInitUnicodeString(&altitude, "114514");
    obCbRegistration.Altitude = altitude;
    obCbRegistration.OperationRegistrationCount = 1;
    obCbRegistration.RegistrationContext = NULL;
    obCbRegistration.OperationRegistration = &obOpreationRegistration;

    ObRegisterCallbacks(&obCbRegistration, &RegistrationHandle);



    DriverObject->DriverUnload = UnloadDriver;

    return STATUS_SUCCESS;
}

驱动加载后发现没用 什么原因呢?

调试发现ObRegisterCallbacks返回0xc0000022

可以看到是MmVerifyCallbackFunction 没过 那么我们和PsSetCreateProcessNotifyRoutineEx的处理方式一样

PLDR_DATA_TABLE_ENTRY ldr = (PLDR_DATA_TABLE_ENTRY)DriverObject->DriverSection;
    ldr->Flags |= 0x20;

在回调函数的参数中POB_PRE_OPERATION_INFORMATION的几个成员含义如下

typedef struct _OB_PRE_OPERATION_INFORMATION {
    _In_ OB_OPERATION           Operation;
    union {
        _In_ ULONG Flags;
        struct {
            _In_ ULONG KernelHandle:1;
            _In_ ULONG Reserved:31;
        };
    };
    _In_ PVOID                         Object;
    _In_ POBJECT_TYPE                  ObjectType;
    _Out_ PVOID                        CallContext;
    _In_ POB_PRE_OPERATION_PARAMETERS  Parameters;
} OB_PRE_OPERATION_INFORMATION, *POB_PRE_OPERATION_INFORMATION;

Operation 是创建还是dump触发的回调

KernelHandle 是否是内核调用的

Object 被操作的对象

ObjectType 对象类型

CallContext 回调参数

Parameters 描述当前操作的具体参数 可以以此设置权限

OB_PREOP_CALLBACK_STATUS TestRoutine(
    _In_ PVOID RegistrationContext,
    _Inout_ POB_PRE_OPERATION_INFORMATION OperationInformation
) {
    if (OperationInformation->Object) {
        pEPROCESS pEprocess = (pEPROCESS)(OperationInformation->Object);
        if (_stricmp("calc.exe", pEprocess->ImageFileName) == 0) {
            DbgPrint("Done!\n");
            OperationInformation->Parameters->CreateHandleInformation.DesiredAccess = 0;
        }
    }
    return OB_PREOP_SUCCESS;
}

效果如下

0 条评论
某人
表情
可输入 255