Using ObRegisterCallbacks With C#

0

So I am trying to use C# and the ObRegisterCallbacks function to get notified about any calls to OpenProcess.

This is the code I have so far:

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!");
    }
}

The ObRegisterCallbacks function takes an OB_CALLBACK_REGISTRATION struct (docs here) as parameter that itself consists of an array of OB_OPERATION_REGISTRATION structs (docs here).

This is where I'm stuck: The OB_OPERATION_REGISTRATION's first member, "ObjectType", is documented as follows

ObjectType

A pointer to the object type that triggers the callback routine. Specify one of the following values:

  • PsProcessType for process handle operations
  • PsThreadType for thread handle operations
  • ExDesktopObjectType for desktop handle operations. This value is supported in Windows 10 and not in the earlier versions of the operating system.

After a few hour of searching I still have no clue how I'm supposed to specify PsProcessType and initialize my struct with it. PsProcessType seems to be defined in process.c in line 20. However it literally just says

POBJECT_TYPE PsProcessType = NULL;

which isn't especially helpful, since when setting the ObjectType field to IntPtr.Zero when initializing the OB_OPERATION_REGISTRATION struct a System.AccessViolationException is triggered when calling ObRegisterCallbacks. (There's also a UNICODE_STRING in the OB_OPERATION_REGISTRATION struct called Altitude that has to be set to some value (but currently isn't lol :D), but that string is initialized and allocated, so it shouldn't be responsible for the access violation... right?)

This is my first time diving this deep into Windows kernel stuff, so it would be nice if someone could help me out with this or point me to some hidden resources I didn't manage to dig up :)

There's an article that uses ObRegisterCallbacks for similar things (in C++ though), however they don't really specify where they're getting PsProcessType from, or how it's defined. So there has to be documentation somewhere out there, if they can successfully use that "ObjectType" field, right?

c#
windows
winapi
driver

1 Answer

1

PsProcessType is exported at ntoskrnl.exe and is the same as ObRegisterCallbacks, the difference between them is that one is an exported global variable and the other is an exported function.

In C, these global variables are declared in 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

So you can just use these variables without having to do anything else.
But I'm not good at C#, and I'm not even sure if C# modules can be loaded into the kernel.

The most recommended approach is to use C/C++ for kernel programming, and I've never heard of anyone doing this with C#.

answered on Stack Overflow Sep 10, 2020 by Sprite

User contributions licensed under CC BY-SA 3.0