Open gremlinbeet opened 1 month ago
It's a good idea, but the question is how? ObOpenObjectByName could be used to open the type object, but not quite, as the type object pointer of the type object is needed, and is not exported by the kernel. This makes it harder to do.
That's easy! You just need to use ObOpenObjectByName to open type object type first! Oh, wait a second...
Haha, but seriously, I see 2 ways:
_OBJECT_HEADER_CREATOR_INFO
:
_OBJECT_HEADER
preceeds the object, examine its InfoMask to know what structs preceed the headerOBP_CREATOR_INFO_BIT
(1) | OBP_NAME_INFO_BIT
(2), so in fact there would be _OBJECT_HEADER_CREATOR_INFO
right before the object header._OBJECT_HEADER_CREATOR_INFO
is 4 (System)._OBJECT_TYPE.TypeList
of the Type type, not _OBJECT_HEADER_CREATOR_INFO.TypeList
._OBJECT_HEADER_CREATOR_INFO
location reliably using CreatorUniqueProcess == 4
and assumption that the object type after Event is Mutant (ExpEventInitialization, ExpMutantInitialization) - you can check double-linked pointers in TypeList of these types w/o dereferencing anything, so it's safe.nt!_OBJECT_HEADER_CREATOR_INFO
+0x000 TypeList : _LIST_ENTRY << all types linked
+0x010 CreatorUniqueProcess : Ptr64 Void
+0x018 CreatorBackTraceIndex : Uint2B
+0x01a Reserved1 : Uint2B
+0x01c Reserved2 : Uint4B
nt!_OBJECT_HEADER
+0x000 PointerCount : Int8B
+0x008 HandleCount : Int8B
+0x008 NextToFree : Ptr64 Void
+0x010 Lock : _EX_PUSH_LOCK
+0x018 TypeIndex : UChar
+0x019 TraceFlags : UChar
+0x019 DbgRefTrace : Pos 0, 1 Bit
+0x019 DbgTracePermanent : Pos 1, 1 Bit
+0x01a InfoMask : UChar <<
+0x01b Flags : UChar
+0x01b NewObject : Pos 0, 1 Bit
+0x01b KernelObject : Pos 1, 1 Bit
+0x01b KernelOnlyAccess : Pos 2, 1 Bit
+0x01b ExclusiveObject : Pos 3, 1 Bit
+0x01b PermanentObject : Pos 4, 1 Bit
+0x01b DefaultSecurityQuota : Pos 5, 1 Bit
+0x01b SingleHandleEntry : Pos 6, 1 Bit
+0x01b DeletedInline : Pos 7, 1 Bit
+0x01c Reserved : Uint4B
+0x020 ObjectCreateInfo : Ptr64 _OBJECT_CREATE_INFORMATION
+0x020 QuotaBlockCharged : Ptr64 Void
+0x028 SecurityDescriptor : Ptr64 Void
+0x030 Body : _QUAD
0: kd> dt _object_type ffffc48d630f9e80
nt!_OBJECT_TYPE
+0x000 TypeList : _LIST_ENTRY [ 0xffffc48d`630f9e80 - 0xffffc48d`630f9e80 ] << wrong list
+0x010 Name : _UNICODE_STRING "Event" << wrong type, need "Type"
+0x020 DefaultObject : (null)
+0x028 Index : 0x13 '' << should be 2 for "Type" in all current OS
0: kd> dt _object_type ffffc48d`630b87a0
nt!_OBJECT_TYPE
+0x000 TypeList : _LIST_ENTRY [ 0xffffc48d`630b8750 - 0xffffc48d`654b31d0 ] << LIST HEAD here!
+0x010 Name : _UNICODE_STRING "Type" << correct Type
+0x020 DefaultObject : 0xfffff806`4343ff60 Void
+0x028 Index : 0x2 ''
+0x02c TotalNumberOfObjects : 0x48 << should be same
+0x030 TotalNumberOfHandles : 0
+0x034 HighWaterNumberOfObjects : 0x48 << should be same
+0x038 HighWaterNumberOfHandles : 0
Certainly possible if using undocumented structures... way beyond the sample I wish to convey :)
Indeed, I see how that'll shift focus from the sample core and make it more complicated. Anyway, good alternative could be to bail with failure rather than returning success (or even create object type with alternate name in debug builds, to facilitate rapid testing).
What initially triggered me is STATUS_SUCCESS while g_DataStackType was still null; but of course you check g_DataStackType against null later anyway, so may be it's okay as is. Feel free to close then, regardless of decision.
(Very nice sample and blog entries btw, kudos!)
I agree, I will change that to return a failure because the rest will not work anyway. Thanks :)
Might want to either bail with failure status there, or open existing type and store its pointer in g_DataStackType: https://github.com/zodiacon/KObjects/blob/797b3b6b97c60cf6763de7d6ee12ab881fe8d635/KObjects/DataStack.cpp#L178