zodiacon / KObjects

Sample for Creating a new kernel object type and supporting API
MIT License
22 stars 5 forks source link

type name collision handling #1

Open gremlinbeet opened 1 month ago

gremlinbeet commented 1 month ago

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

zodiacon commented 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.

gremlinbeet commented 1 month ago

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:

  1. Parse "ObjectTypes" Directory manually (all the buckets). Structures seems quite stable, but I guess it's something we all would rather avoid for anything but may be debug tools.
  2. Utilize _OBJECT_HEADER_CREATOR_INFO:
    • get pointer to any type object, e.g. use exported ExEventObjectType
    • _OBJECT_HEADER preceeds the object, examine its InfoMask to know what structs preceed the header
    • you'll likely see 3 == OBP_CREATOR_INFO_BIT (1) | OBP_NAME_INFO_BIT (2), so in fact there would be _OBJECT_HEADER_CREATOR_INFO right before the object header.
    • you can cross-validate by checking if CreatorUniqueProcess of _OBJECT_HEADER_CREATOR_INFO is 4 (System).
    • the 'TypeList' links all type objects, so by traversing it you'll get to Type object type eventually.
    • beware that list head is _OBJECT_TYPE.TypeList of the Type type, not _OBJECT_HEADER_CREATOR_INFO.TypeList.
    • you can scan stuff w/o knowledge about InfoMask by finding _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
zodiacon commented 1 month ago

Certainly possible if using undocumented structures... way beyond the sample I wish to convey :)

gremlinbeet commented 1 month ago

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!)

zodiacon commented 1 month ago

I agree, I will change that to return a failure because the rest will not work anyway. Thanks :)