首页 > 解决方案 > 在 C# 中使用 ObRegisterCallbacks

问题描述

因此,我尝试使用 C# 和ObRegisterCallbacks函数来获取有关对OpenProcess.

这是我到目前为止的代码:

internal static class Win32SelfProtection
{
    [DllImport("NtosKrnl.exe", SetLastError = true, PreserveSig = false)]
    private static extern uint ObRegisterCallbacks(IntPtr callbackRegistration, out IntPtr registrationHandle);

    [DllImport("NtosKrnl.exe", SetLastError = true, PreserveSig = false)]
    private static extern void ObUnRegisterCallbacks(IntPtr registrationHandle);

    [DllImport("kernel32.dll")]
    internal static extern bool VirtualProtect(IntPtr lpAddress, uint dwSize, uint flNewProtect, out uint lpflOldProtect);

    private const uint OB_OPERATION_HANDLE_CREATE = 0x00000001;
    private const uint OB_OPERATION_HANDLE_DUPLICATE = 0x00000002;

    private const uint PAGE_READWRITE = 0x04;

    [StructLayout(LayoutKind.Sequential)]
    internal struct OB_CALLBACK_REGISTRATION
    {
        internal ushort Version;
        internal ushort OperationRegistrationCount; // 1
        internal IntPtr Altitude;
        internal IntPtr RegistrationContext; // NULL, probably
        internal IntPtr OperationRegistration; // OB_OPERATION_REGISTRATION*
    }

    [StructLayout(LayoutKind.Sequential)]
    internal struct OB_OPERATION_REGISTRATION
    {
        internal IntPtr ObjectType; // PsProcessType
        internal uint Operations; // OB_OPERATION_HANDLE_CREATE
        internal IntPtr PreOperation; // POB_PRE_OPERATION_CALLBACK
        internal IntPtr PostOperation; // POB_POST_OPERATION_CALLBACK
    }

    [StructLayout(LayoutKind.Sequential)]
    internal unsafe struct UNICODE_STRING
    {
        internal ushort Length;
        internal ushort MaximumLength;
        internal IntPtr Buffer;
    }

    internal static unsafe void Protect()
    {
        PobPreOperationCallback preOperationCallback = PreOperationCallback;
        IntPtr pPreOperationCallback = Marshal.GetFunctionPointerForDelegate(preOperationCallback);
        PobPostOperationCallback postOperationCallback = PostOperationCallback;
        IntPtr pPostOperationCallback = Marshal.GetFunctionPointerForDelegate(postOperationCallback);
        OB_OPERATION_REGISTRATION operationRegistration = new OB_OPERATION_REGISTRATION
        {
            ObjectType = IntPtr.Zero, // I have no idea ... <-- Need pointer to PsProcessType
            Operations = OB_OPERATION_HANDLE_CREATE,
            PreOperation = pPreOperationCallback,
            PostOperation = pPostOperationCallback
        };
        IntPtr pOperationRegistration = Marshal.AllocHGlobal(sizeof(OB_OPERATION_REGISTRATION));
        Marshal.StructureToPtr(operationRegistration, pOperationRegistration, false);

        const ushort buffersize = sizeof(ushort) * 64;
        IntPtr buffer = Marshal.AllocHGlobal(buffersize);
        // No idea what kind of string I should put in here :C just zero it for now ...
        Marshal.Copy(new byte[buffersize], 0, buffer, buffersize);
        UNICODE_STRING unicodeString = new UNICODE_STRING
        {
            Length = buffersize,
            MaximumLength = buffersize,
            Buffer = buffer
        };
        IntPtr pUnicodeString = Marshal.AllocHGlobal(sizeof(UNICODE_STRING));
        Marshal.StructureToPtr(unicodeString, pUnicodeString, false);

        OB_CALLBACK_REGISTRATION callbackRegistration = new OB_CALLBACK_REGISTRATION
        {
            Version = 0x0100,
            OperationRegistrationCount = 1,
            Altitude = pUnicodeString,
            RegistrationContext = IntPtr.Zero,
            OperationRegistration = pOperationRegistration
        };
        IntPtr pCallbackRegistration = Marshal.AllocHGlobal(sizeof(OB_CALLBACK_REGISTRATION));
        Marshal.StructureToPtr(callbackRegistration, pCallbackRegistration, false);

        uint status = ObRegisterCallbacks(pCallbackRegistration, out IntPtr hRegistration); // FAILS WITH: AccessViolationException

        // yeah, yeah I'll remember to call Marshal.FreeHGlobal() later ... :D
    } 

    public delegate uint PobPreOperationCallback(IntPtr registrationContext, IntPtr operationInformation);

    // dummy method for now
    internal static uint PreOperationCallback(IntPtr registrationContext, IntPtr operationInformation)
    {
        Console.WriteLine("PreOperationCallback!");
        return 0x0;
    }

    
    public delegate void PobPostOperationCallback(IntPtr registrationContext, IntPtr operationInformation);

    // dummy method for now
    internal static void PostOperationCallback(IntPtr registrationContext, IntPtr operationInformation)
    {
        Console.WriteLine("PostOperationCallback!");
    }
}

ObRegisterCallbacks函数将OB_CALLBACK_REGISTRATION结构(此处的文档)作为参数,该结构本身由结构数组OB_OPERATION_REGISTRATION此处的文档)组成。

这就是我卡住的地方: 的OB_OPERATION_REGISTRATION第一个成员“ObjectType”记录如下

ObjectType

指向触发回调例程的对象类型的指针。指定以下值之一:

  • PsProcessType用于进程句柄操作
  • PsThreadType用于线程句柄操作
  • ExDesktopObjectType用于桌面句柄操作。此值在 Windows 10 中受支持,而在早期版本的操作系统中不受支持。

经过几个小时的搜索,我仍然不知道应该如何指定PsProcessType和初始化我的结构。PsProcessType 似乎process.c在第 20 行中定义。然而它实际上只是说

POBJECT_TYPE PsProcessType = NULL;

这并不是特别有用,因为IntPtr.Zero当初始化OB_OPERATION_REGISTRATION结构时将 ObjectType 字段设置为时System.AccessViolationException,调用ObRegisterCallbacks. (在被调用UNICODE_STRINGOB_OPERATION_REGISTRATION结构Altitude中还有一个必须设置为某个值(但目前不是lol :D),但是该字符串已被初始化和分配,因此它不应该对访问冲突负责......对?)

这是我第一次深入研究 Windows 内核的东西,所以如果有人能帮我解决这个问题或者指出一些我没有设法挖掘的隐藏资源,那就太好了:)

一篇文章用于ObRegisterCallbacks类似的事情(尽管在 C++ 中),但是它们并没有真正指定它们的PsProcessType来源或定义方式。因此,如果他们可以成功使用该“ObjectType”字段,则必须在某个地方有文档,对吗?

标签: c#windowswinapidriver

解决方案


PsProcessType在 ntoskrnl.exe 导出,和 一样ObRegisterCallbacks,区别是一个是导出的全局变量,一个是导出的函数。

在 C 中,这些全局变量在 wdm.h 中声明:

extern POBJECT_TYPE *CmKeyObjectType;
extern POBJECT_TYPE *IoFileObjectType;
extern POBJECT_TYPE *ExEventObjectType;
extern POBJECT_TYPE *ExSemaphoreObjectType;
extern POBJECT_TYPE *TmTransactionManagerObjectType;
extern POBJECT_TYPE *TmResourceManagerObjectType;
extern POBJECT_TYPE *TmEnlistmentObjectType;
extern POBJECT_TYPE *TmTransactionObjectType;
extern POBJECT_TYPE *PsProcessType;
extern POBJECT_TYPE *PsThreadType;
extern POBJECT_TYPE *PsJobType;
extern POBJECT_TYPE *SeTokenObjectType;
#if (NTDDI_VERSION >= NTDDI_THRESHOLD)
extern POBJECT_TYPE *ExDesktopObjectType;
#endif

因此,您可以只使用这些变量而无需执行任何其他操作。
但是我不擅长C#,我什至不确定C#模块是否可以加载到内核中。

最推荐的方法是使用 C/C++ 进行内核编程,而我从未听说有人使用 C# 这样做。


推荐阅读