Press "Enter" to skip to content

Reversing Windows Internals (Part 1) – Digging Into Handles, Callbacks & ObjectTypes

Sina Karvandi 6

Introduction

Welcome to the first part of a series of posts about Exploring & Reversing Windows Concepts and Internals. If you reach here then you’re probably a security researcher or a programmer and this post and similar posts can help you understand what’s going on in some parts of Windows when you use objects with different users and credentials and what you can expect from Windows and how it internally works.

If you want to follow other parts of this tutorial or other tutorials, please visit here.

Overview

In this part, I’m gonna describe some internal structures and functions relating to the “Handles” and we’ll see how Windows saves these handles in its internal structures then we go to see how callbacks work. For example, when you create a handle what kind of mechanisms exists in Windows to notify you about the handle creation. After that, we’ll see how Windows internally calls these callbacks by analyzing the process of creating a handle when a user-mode application requests a handle to the kernel. At last, we’ll see how Windows saves these callbacks and check other (somehow unknown) callbacks by studying different Object Types in Windows and we’ll practically use them in our drivers.

Table of Contents

  • Introduction
  • Overview
  • Table of Contents
  • Handles
    • What is a Handle?
    • Handles in Windows Kernel
    • Changing Handles Access
  • Callbacks
    • Callbacks for processes
  • Handle Creation Process
    • PsOpenProcess
    • ObOpenObjectByPointer
    • SECURITY_SUBJECT_CONTEXT
    • ACCESS_STATE
    • ObpCreateHandle
  • Object Types
    • ObjectTypes in Windows
    • Finding Process Object Type
    • TypeIndex in Object Types
    • Finding All Windows _OBJECT_TYPE(s)
    • Finding Types which support callback
  • Analyzing Callbacks in ObjectTypes
    • DumpProcedure
    • OpenProcedure
    • CloseProcedure
    • DeleteProcedure
    • ParseProcedure
    • ParseProcedureEx
    • SecurityProcedure
    • QueryNameProcedure
    • OkayToCloseProcedure
  • Using Callbacks in ObjectTypes
  • Conclusion
  • References
Aniiiiiime :)

What is a Handle?

If you’re familiar with the way Windows shares its resources, then you probably know about the handles. In short, the handle is a value that the Windows kernel returns to the user-mode application (if you have needed privileges or have an account which is not denied by DACL) and this handle can be used for further action on the object.

There is a tool called “handle” from the SysInternals which can be downloaded from here.

The official site describes :

Ever wondered which program has a particular file or directory open? Now you can find out. Handle is a utility that displays information about open handles for any process in the system. You can use it to see the programs that have a file open, or to see the object types and names of all the handles of a program.

Let’s see what are the handles of processes.

For example,

This command shows every handle for each process in which their handle name contains “windows\system“. The name match is case-insensitive and the fragment specified can be anywhere in the paths you are interested in. You can imagine how this way can be used to find what process(es) are opening a specific file when you try to remove them.

Handles

Finding all the handles in User-mode

One interesting thing in Windows is if you are in Integrity Level >= Medium, then you can access handles to all processes, even kernel addresses of objects. Alex mentioned that KASLR is not designed to protect against processes with Medium Integrity or above. By the way, its one of the known methods to bypass KASLR (e.g when you have a write-what-where bug and don’t know where to modify then you can use one of the objects in the kernel as you have the addresses).

The PoC for this way is available on GitHub (https://github.com/SinaKarvandi/Process-Magics/tree/master/EnumAllHandles) and you can see the results from the following images.

Dumping all handles

Now you can see all the handles even from other processes (like system process) with an unprivileged (non-elevated UAC) user.

All kernel handles and addresses

Handles In Windows Kernel

Let’s see how Windows saves and manages the handles in its kernel.

In process structure (nt!_EPROCESS) there is a field called “ObjectTable“.

The nt!_HANDLE_TABLE is like this :

Using HandleTableList, you can traverse through each handle of your target process.

Each handle is defined in a structure called “nt!_HANDLE_TABLE_ENTRY ” and among these fields the most interesting one is GrantedAccessBits.

There is a command (!handle) in windbg which used to show the details about the handle.

The following picture describes the details of each field in !handle.

!handle windbg

Changing Handles GrantedAccess

If you map (Entry) field to the _HANDLE_TABLE_ENTRY then you can see the following results.

Note that, 0x1fffff means FULL CONTROL. You easily change the access bit, e.g using “eb”. For example when you use a command like “eb ffffa30c9aa26010+8 ee” then if you see the handle again (!handle 0x04) you can see that GrantedAccess is changed.

There is a good post here which describes how the above method can be used in order to bypass the restrictions that a driver can put on a special process or each process that tries to access memory of a protected-process, for example, a game with anit-cheat protection or a security software which protects its memory from being accessed by a remote process and APIs like WriteProcessMemory or ReadProcessMemory).

Let’s see some other functions relating to the handles.

There is a function ExEnumHandleTable which enumerates all the handle from a process by passing a pointer to process’s ObjectTable.

Also, there is a function ExpLookupHandleTableEntry which gets the handle table as its first argument (RCX) and the handle value as the second argument (RDX) and returns the _HANDLE_TABLE_ENTRY corresponding to that handle. You can use them in your driver or shellcode.

Callbacks for processes

In order to set callback whenever a handle to a process is requested or whatever relating to the handles of Threads or Processes, you can ObRegisterCallbacks function.

The ObRegisterCallbacks routine registers a list of callback routines for thread, process, and desktop handle operations.

The sample for using ObRegisterCallbacks is available on GitHub :

[https://github.com/SinaKarvandi/misc/tree/master/ObRegisterCallbacks]

If you load the above driver, you’ll get the following NTSTATUS error :

“{Access Denied} A process has requested access to an object, but has not been granted those access rights.”

If we look at the decompiled code from IDA and look for the error code (0xC0000022), we’ll reach to the following pseudo-code.

It’s clear that MmVerifyCallbackFunctionCheckFlags is the guilty function, I don’t find a way to register my unsigned driver but for this let’s just patch it.

The above assembly code is enough to always return true so we need to execute the following windbg command:

Update 1: As Yarden mentioned, linking in with /INTEGRITYCHECK will save you the need to patch the kernel from the debugger so instead of using the above command you can add /INTEGRITYCHECK to your linker when you’re compiling your driver.

Now load the driver again. If you encounter errors like: “An instance already exists at this altitude on the volume specified“, then you have to change the following line :

Make sure to run the following windbg command if you previously didn’t enable the debugging outputs.

After running driver, you have to see each handle request to processes or threads with its desired access masks and the corresponding operation (e.g creating a handle or duplicate handle or whatever).

Finally, we’ll get the following results :

Callback results

Handle Creation Process

Now that we know some of the basic concepts from callbacks and handle tables, let’s have a comprehensive survey from user-mode OpenProcess until we come back to user-mode again so we can see how Windows creates handle and saves it on its handle table.

The following functions have to be called in order to make a handle available to the user-mode in the case of opening a handle to a process.

OpenProcess (user-mode) -> NtOpenProcess (user-mode) -> NtOpenProcess (kernel-mode) -> PsOpenProcess – > ObOpenObjectByPointer -> ObpCreateHandle

If you remeber from the ObRegisterCallbacks, our callbacks called from “ObpCallPreOperationCallbacks” and this function is called by ObpCreateHandle. Also “ObpCallPostOperationCallbacks” is responsible for calling our post operation callbacks (we’ll see them).

Let’s start with OpenProcess, here is an ordered decompiled version of OpenProcess.

Among the above parameters, dwProcessId and dwDesiredAccess is not in our interest as they’re more and less clear but the most interesting field here is ObjectAttributes, Microsoft explains about Object Attributes here. The structure is below, you can read about each field in MSDN.

The above field has some Attributes that is interesting to us, their definition as defined in SDK :

We’ll see how they affect process handle creation, later.

NtOpenProcess

NtOpenProcess, first checks for its previous mode (user-mode or kernel-mode) the it calls PsOpenProcess.

PsOpenProcess

The PsOpenProcess is something like this, it’s not documented so it’s based on IDA’s decompile results:

In PsOpenProcess, it first checks whether the handle pointer resides to valid user-mode address then it limits the user-mode handles to 0x1df2. From the following picture, you can see this limitation on handles and their meanings.

IDA Decompiled Source

In the case of kernel attributes, it limits the handle to the following values.

IDA Decompiled Source

As you can see, you don’t have access to OBJ_KERNEL_HANDLE and OBJ_VALID_ATTRIBUTES in user-mode and also some undocumented values 0x11800 which is not revealed by Microsoft.

In PsOpenProcess, the next check is for SeDebugPrivilege. As you might know, this is one of the powerful privileges in Windows that causes to bypass any Privilege checks and give the needed accesses directly. You might see it in tools like Mimikatz. It then passes it to the SePrivilegedServiceAuditAlarm. SePrivilegedServiceAuditAlarm is to be called whenever a privileged system service is attempted.

IDA Decompiled Source

Finally, PsOpenProcess calls ObOpenObjectByPointer.

ObOpenObjectByPointer

In order to explain about ObOpenObjectByPointer, First, we have to know about two structures “ACCESS_STATE” and “SECURITY_SUBJECT_CONTEXT“.

SECURITY_SUBJECT_CONTEXT

The SECURITY_SUBJECT_CONTEXT is used to capture the subject security context for access validation and auditing. It’s like dumping a special context’s token, then lock it in order to avoid any modification and finally do some privilege checks.

Functions like SeCaptureSubjectContext  or SeCaptureSubjectContextEx return a pointer to this structure.

For example, the following code shows how this structure can be used to do some privilege checks.

This structure is important because as James Forshaw mentioned in 3rd part of his post about “AppLocker internals” :

A Windows access check takes 4 main parameters:

  • SECURITY_SUBJECT_CONTEXT which identifies the caller’s access tokens.
  • A desired access mask.
  • GENERIC_MAPPING structure which allows the access check to convert generic access to object-specific access rights.
  • And most importantly, the Security Descriptor which describes the security of the resource being checked.

SECURITY_SUBJECT_CONTEXT is coming from two above mentioned functions, DesiredAccess is also a parameter to ObOpenObjectByPointer and GenericMapping comes from _OBJECT_TYPE‘s _OBJECT_TYPE_INITIALIZER+0x4c (I’ll describe about Object Types later in this post.) while security descriptor can be derived from the _OBJECT_HEADER’s +0x28 and object header is also the first parameter to ObOpenObjectByPointer.

ACCESS_STATE

As the MSDN describes, The ACCESS_STATE structure describes the state of an access in progress. It contains an object’s subject context, remaining desired access types, granted access types, and, optionally, a privilege set to indicate which privileges were used to permit the access.

Now, let’s return to ObOpenObjectByPointer, This function checks whether the caller passes an access state if not then it creates a new one based on the desired access and the object type’s generic mapping by calling SepCreateAccessStateFromSubjectContext , as the name implies it receives an ACCESS_STATE from the SECURITY_SUBJECT_CONTEX.

Eventually, it checks whether the the function itself (ObOpenObjectByPointer) creates ACCESS_STATE or not. If it creates, then it deletes the ACCESS_STATE and SECURITY_SUBJECT_CONTEXT using SepDeleteAccessState and SeReleaseSubjectContext.

Finally, this function calls the popular ObpCreateHandle which creates the handle.

IDA Decompiled Source

ObpCreateHandle

For creating a handle, an undocumented function “ObpCreateHandle” is responsible for creating a new handle to an existing object. Generally, ObpCreateHandle creates an entry in the process’ handle table that becomes associated with the object.

ObpCreateHandle defines like this: (there are some differences between current definition and WRK’s definition).

The first argument to this function is _OB_OPEN_REASON which defines like this :

From the above structure, you can see the cases where ObpCreateHandle might be used for.

If the handle is requested from kernel-mode then “ObpKernelHandleTable” is used as the handle table and if it’s a user-mode application then it calls ObReferenceProcessHandleTable.

IDA Decompiled Source

The above function (ObReferenceProcessHandleTable), first checks whether it can acquire RundownProtect or not. When run-down protection is in effect, the driver can safely access the object without the risk that the object will be deleted before the access completes. You can imagine that if you use ExAcquireRundownProtection on this field (RundownProtect) then each attempt to create a handle by the special process will cause a 0xC000010A error (An attempt was made to access an exiting process.).

This function finally returns Process->ObjectTable.

In the end, ObpCreateHandle calls ExReleaseRundownProtection which releases the RundownProtect of our process.

After that, ObpCreateHandle calls some undocumented Callbacks (SecurityProcedure). I’ll give a detailed explanation about these kinds of callbacks later on this topic but for now, it first checks whether SecurityProcedure is SeDefaultObjectMethod or not. SeDefaultObjectMethod is the default security method for objects. It is responsible for either retrieving, setting, and deleting the security descriptor of an object. It is not used to assign the original security descriptor to an object and as you can see if our callback fails with {buffer too small} error then it tries to call it once more so by now, you know that this callback is responsible for changing SecurityDescriptor of any object type (each object type separately using ObjectType’s SecurityProcedure).

Don’t worry if things are not clear, after reading the last part (about ObjectTypes) you can return here and read it once again and sure you’ll understand it.

IDA Decompiled Source

As you can see, these object type callbacks called for each object separately and it’s not specific to a special object (e.g Process, Thread, or Desktop objects).

After making SecurityDescriptor ready, it’s time to perform security checks, ObpCreateHandle calls SeAccessCheck. The SeAccessCheck routine determines whether the requested access rights can be granted to an object protected by a security descriptor and an object owner so ObpCreateHandle passes the SecurityDescriptor of object and AccessState that we have from the previous function to see if the access is granted or not.

As I told you above, ObpCreateHandle calls ObpCallPreOperationCallbacks and this function is responsible for calling callbacks that are registered by ObRegisterCallbacks but in contrast with above callbacks, these callbacks are limited to some object types (e.g Process, Thread, or Desktop).

This limitation is done by the following check, which checks whether object type supports callbacks and if there is any callback registered.

Later, we have a section called “Finding Types which support callback“, it describes how to find these object types but keep in mind, if you set Support Callback bit of an object manually, then PatchGuard comes in and leads to a BSOD.

IDA Decompiled Source

In order to assign a handle, it first acquires a lock to HANDLE_TABLE.HandleTableLock and then search through HANDLE_TABLE.FreeLists. If there isn’t any empty place in our handle table (based on NextHandleNeedingPool index) then it tries to allocate a new handle table entry for the specified handle table using ExpAllocateHandleTableEntrySlow.

Now, it’s time to compute the handle’s value.

IDA Decompiled Source

When the handle is computed, it calls ExpSetHandleExtraInfo which gets the HandleTable and Handle and sets the _HANDLE_TABLE_ENTRY_INFO to the handle entry.

ExpSetHandleExtraInfo is used to set AuditMask and AccessMask to the handle and you can see that it removes the handle if the above function failed (using ExpFreeHandleTableEntry).

And finally, it sets the handle value :

As you can see, if the handle is user-mode handle then it strips the kernel address bit otherwise it’s a kernel handle and address itself is the handle.

This behavior will be changed in the future if Windows starts supporting Supervisor Mode Access Prevention (SMAP) because as long as they use this protection, they won’t be able to directly write on user-mode addresses and they have to execute extra instruction for this purpose.

The last step is calling ObpPostInterceptHandleCreate this function calls ObpCallPostOperationCallbacks and its responsible for calling Post Operation Callbacks.

Animmmeeeee :)

ObjectTypes in Windows

If you’d ever used functions like ObReferenceObjectByHandle, ObReferenceObjectByPointer, ObOpenObjectByPointer or other functions then you’ve probably heard of POBJECT_TYPE.

Also, creation routines for the various objects, like IoCreateDriver or PspCreateProcess, call the generic ObCreateObject and pass it a pointer to an appropriate _OBJECT_TYPE structure.

_OBJECT_TYPE is one of the important structures in Windows that stores the definition of different object like Process, Thread, Mutex, etc. If you want to know the difference between NT objects and non-objects you can read the article “What is POBJECT_TYPE?“.

Let’s see the definition:

(I dumped the structures using pdbex written by one of my best friends Petr Benes)

Finding Process Object Type

I’ll talk about some important fields later in this post but for now, let’s see how we can find the _OBJECT_TYPE of a specific object, let say process, first find all the processes EPROCESS.

I choose lsass.exe (ffffd68a07b5d080) as the target. As you might know, Windows saves each object (e.g _EPROCESS) like this :

  1. _POOL_HEADER
  2. _OBJECT_QUOTA_CHARGES (optional)
  3. _OBJECT_HANDLE_DB (optional)
  4. _OBJECT_NAME (optional)
  5. _OBJECT_CREATOR_INFO (optional)
  6. _OBJECT_HEADER
  7. object body (e.g _EPROCESS)

So if we subtract sizeof(_OBJECT_HEADER) from the EPROCESS we’ll reach to the _OBJECT_HEADER of this object. (you can perform the same thing for _POOL_HEADER too.)

The object header is like this :

And the sizeof is :

but wait, we’re in top of the object’s Body (means that EPROCESS or whatever starts at _OBJECT_HEADER+0x30) so we have to subtract -0x30 from the EPROCESS (ffffd68a`07b5d080-0x30 = ffffd68a07b5d050) .

You can also confirm this by looking at the “ObjectHeader” field of “!object” command.

In Windows, each object is derived from a special type and even each type (like process, thread, token) derived from another type called “type“.

Let’s see.

In the above code you can see there is a “Type: (ffffd689fd09a4e0) Process” if we map this to nt!_OBJECT_TYPE, we can it’s “Name” which is “Process“.

This _OBJECT_TYPE (process) is also mapped to a global variable called “nt!PsProcessType” (Do you remember we filled OB_OPERATION_REGISTRATION.ObjectType with PsProcessType and PsThreadType ?) and These global variables are OBJECT_TYPE**, not just single indirection pointers.

TypeIndex in Object Types

Another important field from _OBJECT_HEADER is TypeIndex.

Let’s review this line again :

This field didn’t exist in until Windows Seven, means that before Windows Seven each _OBJECT_HEADER has a field called “Type” which was a pointer to its “_OBJECT_TYPE“, that’s the reason for (old version) and (new version) in !object‘s results. You can read more about it in the article in CodeMachine.

But in the newer versions of Windows (> Windows 7) you can see TypeIndex. Instead of pointing directly to the OBJECT_TYPE data structure, the object header now contains an index into a new global data structure nt!ObTypeIndexTable, which is an array of pointers to the different OBJECT_TYPE structures.

The following command is used to get the _OBJECT_TYPE of target index, note that in this example 0x7 is index and @$ptrsize is defined by Windbg which shows either your pointer are 8 Bytes (x64) or 4 Bytes (x86).

You can see the result here :

But wait, in or example we see that (TypeIndex : 0x7a) and its index is not 0x7a! It turns out that in Windows 10 they decided to not directly point to the index (why?) of nt!ObTypeIndexTable instead you have to do some XORs in order to find the right index.

Update 2 :

Take a look at the following slides from NTarakanov :

[http://www.powerofcommunity.net/poc2018/nikita.pdf]

The reason why they XORed TypeIndex with nt!ObHeaderCookie is the fact that it’s possible to modify the nt!_OBJECT_HEADER.TypeIndex of each object (for example in the case of a pool overflow), And then it would be possible to point to other _OBJECT_TYPEs like ALPC_OBJECT and trigger this vulnerability (pool overflow) as it was possible to control the behavior of callbacks in these objects.

Pool over flow (DKOHM)

The following picture is copied from this post which describes how he understands it by reversing nt!ObGetObjectType , you can do the same thing and it works.

Find TypeIndex in Win 10

Keep in mind that the second member of nt!ObTypeIndexTable is “Type“. If you get the _OBJECT_HEADER of Process’s _OBJECT_TYPE itself, then you’ll reach to the following _OBJECT_TYPE .

Now that we know what the object type is, it’s time to dig deeper into Windows _OBJECT_TYPEs and find all of them.

Finding All Windows _OBJECT_TYPE(s)

The first and easiest way is using Windbg’s “!object \ObjectTypes” or using tools like SysInternals’ WinObj.

WinObj

Using windbg , you’ll get the following results but the result of my tests shows that this command’s results is not complete, that’s why we need a third way to explore the kernel types. WinObj also won’t show a complete result.

The third method for getting objects is interpreting Windows structures manually using Windbg. Before further investigation we have to find the “Type“‘s _OBJECT_TYPE (e.g a process type itself is a “Type“), for this purpose first we have to find Process’s _OBJECT_TYPE :

Using !object we can find its type :

you can also find the “Type“‘s location using “!object \ObjectTypes“.

Now that we have a pointer to “Type“‘s _OBJECT_TYPE ( = ffffd689fd09ad20), it’s time to find other types, _OBJECT_TYPE itself has a “TypeList” but doesn’t seem to be a list to Object Types, you can traverse it, it won’t give a valid result. If you remember from the first part of this post, I told you the order when Windows allocates the objects (search for ” Windows saves each object” in this post and see it again.). It turns out that above the Type’s _OBJECT_HEADER, there is another optional structure, called “_OBJECT_HEADER_CREATOR_INFO“, let’s see what’s the definition of this structure.

As I told you, _OBJECT_HEADER_CREATOR_INFO is above the _OBJECT_HEADER so we need a little calculation here, sizeof( _OBJECT_HEADER_CREATOR_INFO) = 0x20 and for reaching to the _OBJECT_HEADER we have to subtract the pointer (ffffd689fd09ad20) by 0x30.

ffffd689fd09ad20 – 0x30 – 0x20 = FFFFD689FD09ACD0‬ => Pointer to _OBJECT_HEADER_CREATOR_INFO

The TypeList in _OBJECT_HEADER_CREATOR_INFO is _LIST_ENTRY which shows us all the Types. We’ll use Windbg to traverse through the list.

You can see a pointer to all of the Types (of course from _OBJECT_HEADER_CREATOR_INFO). Let’s verify we found them correctly by looking at their names (Don’t forget we have to add 0x20 and ox30 to reach the start of the _OBJECT_TYPE).

The above result contains more types than using “!object \ObjectTypes” (why?) in my case, it finds 70 types while “!object \ObjectTypes” gives only 67 types !!!

Update 1: As Alex mentioned, I got more objects because I copy-pasted FilterCommunicationPort and NdisCmState twice.

Update 1: Also, “!object 0 Type” is a built-in way of enumerating the creator info list.

Now that we have the pointers to all of the types, it’s time to investigate through each _OBJECT_TYPE(s).

Finding Types which support callback

In _OBJECT_TYPE there is a structure called “_OBJECT_TYPE_INITIALIZER“. It defines like this :

It contains lots of important fields, for example, “SupportsObjectCallbacks” shows whether the object supports callbacks or not. _OBJECT_TYPE_INITIALIZER starts after 0x40 from the _OBJECT_TYPE so let’s find the objects that support callbacks from the _OBJECT_TYPES that we previously gathered. (Do you remember ObpCallPreOperationCallbacks when we reversed ObpCreateHandle? 🙂 )

You can see that only the following three _OBJECT_TYPEs support callbacks,

The result is not really surprising, previously Alex and other friends told me that Windows 10 TH2 starts supporting “ExDesktopOObjectType“.

OperationRegisteration

There are also another important fields in _OBJECT_TYPE_INITIALIZER for example you can find the ValidAccessMask for that object or find the related functions from DumpProcedure, OpenProcedure, CloseProcedure, DeleteProcedure, ParseProcedure, ParseProcedureEx, SecurityProcedure, QueryNameProcedure, and OkayToCloseProcedure. For example in process type we have the following functions (These are the callbacks that internally used by Microsoft and it’s not revealed to drivers) :

Analyzing Callbacks in ObjectTypes

By now, you’re familiar with these callbacks: DumpProcedure, OpenProcedure, CloseProcedure, DeleteProcedure, ParseProcedure, ParseProcedureEx, SecurityProcedure, QueryNameProcedure, and OkayToCloseProcedure. Now let’s see which functions attempt to call them and what is the purpose of calling them.

The following definitions are the undocumented part of these callbacks.

  • DumpProcedure: Calls from nt!ObpRemoveObjectRoutine.

[Type: void (__cdecl*)(void *,_OBJECT_DUMP_CONTROL *)]

  • OpenProcedure: Calls from nt!ObpIncrementHandleCountEx and the callback target function for process is nt!PspProcessOpen.

[Type: long (__cdecl*)(_OB_OPEN_REASON,char,_EPROCESS *,void *,unsigned long *,unsigned long)]

  • CloseProcedure: Calls from nt!ObCloseHandleTableEntry and the callback target function for process is nt!PspProcessClose and for file is nt!IopCloseFile.

[Type: void (__cdecl*)(_EPROCESS *,void *,unsigned __int64,unsigned __int64)]

  • DeleteProcedure: Calls from nt!ObpRemoveObjectRoutine and the callback target function for process is nt!PspProcessDelete and for file is nt!IopDeleteFile.

[Type: void (__cdecl*)(void *)]

  • ParseProcedure & ParseProcedureEx: Calls from nt!ObpLookupObjectName and the callback target function for file is nt!IopParseFile.

[Type: long (__cdecl*)(void *,void *,_ACCESS_STATE *,char,unsigned long,_UNICODE_STRING *,_UNICODE_STRING *,void *,_SECURITY_QUALITY_OF_SERVICE *,void * )]

[Type: long (__cdecl)(void *,void *,_ACCESS_STATE *,char,unsigned long,_UNICODE_STRING *,_UNICODE_STRING *,void *,_SECURITY_QUALITY_OF_SERVICE *,_OB_EXTENDED_PARSE_PARAMETERS *,void * *)]

  • SecurityProcedure: Calls from nt!NtQuerySecurityObject and nt!ObpCreateHandle (Do you remeber? we see it on ObpCreateHandle and explained about its purpose) and the callback target function for file is nt!IopGetSetSecurityObject and for process is nt!SeDefaultObjectMethod.

[Type: long (__cdecl*)(void *,_SECURITY_OPERATION_CODE,unsigned long *,void *,unsigned long *,void * *,_POOL_TYPE,_GENERIC_MAPPING *,char)]

  • QueryNameProcedure: Calls from nt!ObQueryNameStringMode and target function for file is nt!IopQueryName.

[Type: long (__cdecl*)(void *,unsigned char,_OBJECT_NAME_INFORMATION *,unsigned long,unsigned long *,char)]

  • OkayToCloseProcedure: Calls from nt!ObCloseHandleTableEntry.

[Type: unsigned char (__cdecl*)(_EPROCESS *,void *,void *,char)]

Using Callbacks in ObjectTypes

It’s time to use the above information to build a driver that hooks these callbacks, there is an article here from Souhail Hammou that describes the behavior of OkayToCloseProcedure.

As he describes,

The function (ObpCloseHandleTableEntry) will access the OkayToCloseProcedure field and check if it’s NULL, if that’s true the function will proceed to other checks (check if the handle is protected from being closed).

If the OkayToCloseProcedre field isn’t NULL, the function will proceed to call the callback function. If the callback function returns 0 the handle cannot be closed and ObpCloseHandleTableEntry will return STATUS_HANDLE_NOT_CLOSABLE. If it returns a value other than 0 we will proceed to the other checks as it happens when the OkayToCloseProcedure is NULL.

Now we have to create a driver which hooks all of the non-null callback procedure and also hook OkayToCloseProcedure.

Full source code of object callbacks hook is available on GitHub :

[ https://github.com/SinaKarvandi/misc/tree/master/TypeInfoCallbacksHooker ]

For this, first, you have to find a pointer the _OBJECT_TYPE structure of the object that you need to hook (e.g Process, File and etc.). For example, as I described above PsProcessType contains a pointer to the start of Process OBJECT_TYPE so in the case of hooking the processes callbacks you can use the following code.

Also, there is another option which can be used to do the same task, You can use ObGetObjectType and pass you object as its argument. For example, you can use PsGetCurrentProcess() which is a Process object or any other object. This way is more general.

For each of the callbacks, we use the following code. First, we check whether the callback is NULL or not, if it’s not null then we save the callback pointer (For future calls) then we change the address of the callback so that Windows calls our callback method first and we call the original function.

Whenever Windows calls our callback method, we have to first show the details about the object and process that causes this callback to be invoked. Then we return the result of previous callback (which Windows sets).

Keep in mind that these modifications on callbacks are prohibited due to the presence of PatchGuard but PatchGuard won’t start in a debugged environment.

Finally, the results are:

TypeInfoCallbacksHooker Driver

Conclusion

In this post, we saw some important parts of Windows about handles, callbacks, object types and lots of other cool examples about how to use them.

The details provided in this post might be changed in the future versions of Windows, these details checked on the latest Windows 10 1903 so please don’t hesitate to correct me if you are sure something is wrong or you want to add some additional details for readers or something that changes in the future.

I’m not actively working on these series but I’ll try to post new parts as soon as possible.

That’s it guys, hope you enjoy reading this post.

Aniiiime :D

References

[1] Detecting Sysmon on the Victim Host – (https://ired.team/offensive-security/enumeration-and-discovery/detecting-sysmon-on-the-victim-host)
[2] Sysmon – (https://docs.microsoft.com/en-us/sysinternals/downloads/sysmon)
[3] Sysmon Event ID 16 – (https://www.ultimatewindowssecurity.com/securitylog/encyclopedia/event.aspx?eventid=90016)
[4] Handle – (https://docs.microsoft.com/en-us/sysinternals/downloads/handle)
[5] libelevate – Bypass ObRegisterCallbacks via elevation- (https://github.com/notscimmy/libelevate)
[6] Microsoft Windows Security – (https://www.microsoftpressstore.com/articles/article.aspx?p=2228450&seqNum=3)
[7] OBJECT_ATTRIBUTES structure – (https://docs.microsoft.com/en-us/windows/win32/api/ntdef/ns-ntdef-_object_attributes)
[8] Windows 7 Object Headers – (https://codemachine.com/article_objectheader.html)
[9] A Light on Windows 10’s “OBJECT_HEADER->TypeIndex” – (https://medium.com/@ashabdalhalim/a-light-on-windows-10s-object-header-typeindex-value-e8f907e7073a)
[10] OB_OPERATION_REGISTRATION structure – (https://docs.microsoft.com/en-us/windows-hardware/drivers/ddi/wdm/ns-wdm-_ob_operation_registration)
[11] Kernel Objects – (https://computer.forensikblog.de/en/2009/04/kernel-objects.html)
[12] Operating Offensively Against Sysmon – (https://www.darkoperator.com/blog/2018/10/5/operating-offensively-against-sysmon)
[13] DACLs and ACEs – (https://docs.microsoft.com/en-us/windows/win32/secauthz/dacls-and-aces)
[14] OkayToCloseProcedure callback kernel hook – (http://rce4fun.blogspot.com/2014/07/okaytocloseprocedure-callback-kernel_9.html)
[15] Part 1: Digging deep into LoadLibrary – (https://n4r1b.netlify.com/en/posts/2019/03/part-1-digging-deep-into-loadlibrary/)
[16] The Internals of AppLocker – Part 3 – Access Tokens and Access Checking – (https://tyranidslair.blogspot.com/2019/11/the-internals-of-applocker-part-3.html)

    • Sina Karvandi Sina Karvandi

      Hey Nikita !

      Thanks, seems to be a good presentation, I’ll read it.

  1. x0r19x91 x0r19x91

    Nice Article!

  2. John Doe John Doe

    Awesome work! Please don’t drop this thing, very informative and interesting stuff! waiting for 2 part

    • Sina Karvandi Sina Karvandi

      Sure,
      I have to publish part 7 & 8 of “Hypervisor From Scratch” and then I’ll start the second part.

  3. Clarifying the internal process of windows is quite appealing. I got interest into the cyber security so it is very useful information for me.

Leave a Reply

Your email address will not be published. Required fields are marked *