KeyObject结构中的Name信息

2010-11-26 Nie.Meining Debug

今天遇到个问题,需要从KeyObject结构中找出Name信息。
首先查看一下Key这种类型的Object对应的QueryNameProcedure:

0: kd> !object E1B90D70h

Object: e1b90d70 Type: (821ac858) Key

    ObjectHeader: e1b90d58 (old version)

    HandleCount: 1 PointerCount: 1

    Directory Object: 00000000 Name: \REGISTRY\USER\S-1-5-21-789336058-2146961391-682003330-1003

0: kd> dt _OBJECT_HEADER e1b90d58

nt!_OBJECT_HEADER

   +0x000 PointerCount     : 0n1

   +0x004 HandleCount      : 0n1

   +0x004 NextToFree       : 0x00000001 Void

   +0x008 Type             : 0x821ac858 _OBJECT_TYPE

   +0x00c NameInfoOffset   : 0 ''

   +0x00d HandleInfoOffset : 0 ''

   +0x00e QuotaInfoOffset : 0 ''

   +0x00f Flags            : 0 ''

   +0x010 ObjectCreateInfo : 0x81c23820 _OBJECT_CREATE_INFORMATION

   +0x010 QuotaBlockCharged : 0x81c23820 Void

   +0x014 SecurityDescriptor : (null)

   +0x018 Body             : _QUAD

 

0: kd> dt 0x821ac858 _OBJECT_TYPE

nt!_OBJECT_TYPE

   +0x000 Mutex            : _ERESOURCE

   +0x038 TypeList         : _LIST_ENTRY [ 0x821ac890 - 0x821ac890 ]

   +0x040 Name             : _UNICODE_STRING "Key"

   +0x048 DefaultObject    : 0x80562e20 Void

   +0x04c Index            : 0x14

   +0x050 TotalNumberOfObjects : 0x2e1

   +0x054 TotalNumberOfHandles : 0x2df

   +0x058 HighWaterNumberOfObjects : 0x307

   +0x05c HighWaterNumberOfHandles : 0x305

   +0x060 TypeInfo         : _OBJECT_TYPE_INITIALIZER

   +0x0ac Key              : 0x2079654b

   +0x0b0 ObjectLocks      : [4] _ERESOURCE

 

0: kd> dt _OBJECT_TYPE_INITIALIZER 0x821ac858+60

nt!_OBJECT_TYPE_INITIALIZER

   +0x000 Length                    : 0x4c

   +0x002 UseDefaultObject          : 0x1 ''

   +0x003 CaseInsensitive           : 0 ''

   +0x004 InvalidAttributes         : 0x30

   +0x008 GenericMapping            : _GENERIC_MAPPING

   +0x018 ValidAccessMask           : 0x1f003f

   +0x01c SecurityRequired          : 0x1 ''

   +0x01d MaintainHandleCount       : 0 ''

   +0x01e MaintainTypeList          : 0 ''

   +0x020 PoolType                  : 1 ( PagedPool )

   +0x024 DefaultPagedPoolCharge    : 0x74

   +0x028 DefaultNonPagedPoolCharge : 0

   +0x02c DumpProcedure             : (null)

   +0x030 OpenProcedure             : (null)

   +0x034 CloseProcedure            : 0x80636462   void nt!CmpCloseKeyObject+0

   +0x038 DeleteProcedure           : 0x80636348   void nt!CmpDeleteKeyObject+0

   +0x03c ParseProcedure            : 0x8062e586   long nt!CmpParseKey+0

   +0x040 SecurityProcedure         : 0x806361aa   long nt!CmpSecurityMethod+0

   +0x044 QueryNameProcedure        : 0x80635404   long nt!CmpQueryKeyName+0

   +0x048 OkayToCloseProcedure      : (null)

  

可以猜测,KeyObject应该是通过CmpQueryKeyName来获取其结构中的Name信息。
但是KeyObject的结构是未公开的,所以这里写个很简单的程序调用一下ObQueryNameString去获取任意一个KeyObject的Name信息,然后我们跟踪这个过程。

NTSTATUS DriverEntry(PDRIVER_OBJECT DriverObject, PUNICODE_STRING RegistryPath) {

    // by Fypher

    CHAR buff[512];

    POBJECT_NAME_INFORMATION pObjNameInfo = (POBJECT_NAME_INFORMATION)buff;

    ULONG ulRetLen;

    // 0xe1b90d70是某个KeyObject的地址。

    ObQueryNameString((PVOID)0xe1b90d70, pObjNameInfo, sizeof(buff), &ulRetLen);

    return STATUS_UNSUCCESSFUL;

}

 

单步跟几下可以看到:

1: kd> p

nt!ObQueryNameString+0xbe:

805c36e0 ff7514          push    dword ptr [ebp+14h]

1: kd> p

nt!ObQueryNameString+0xc1:

805c36e3 ff7510          push    dword ptr [ebp+10h]

1: kd> p

nt!ObQueryNameString+0xc4:

805c36e6 ff750c          push    dword ptr [ebp+0Ch]

1: kd> p

nt!ObQueryNameString+0xc7:

805c36e9 50              push    eax

1: kd> p

nt!ObQueryNameString+0xc8:

805c36ea ff7508          push    dword ptr [ebp+8]

1: kd> p

nt!ObQueryNameString+0xcb:

805c36ed ffd1            call    ecx

 

1: kd> u ecx

nt!CmpQueryKeyName:

80635404 6a14            push    14h

80635406 68401d4e80      push    offset nt!`string'+0x1b0 (804e1d40)

8063540b e8006bf0ff      call    nt!_SEH_prolog (8053bf10)

80635410 e8d95bffff      call    nt!CmpLockRegistry (8062afee)

80635415 8b4508          mov     eax,dword ptr [ebp+8]

80635418 8b4004          mov     eax,dword ptr [eax+4]

8063541b f6400502        test    byte ptr [eax+5],2

8063541f 7407            je      nt!CmpQueryKeyName+0x24 (80635428)

0: kd> u

nt!CmpQueryKeyName+0x1d:

80635421 be7c0100c0      mov     esi,0C000017Ch

80635426 eb70            jmp     nt!CmpQueryKeyName+0x94 (80635498)

80635428 50              push    eax

80635429 e874f3ffff      call    nt!CmpConstructName (806347a2)

8063542e 8945dc          mov     dword ptr [ebp-24h],eax

80635431 85c0            test    eax,eax

80635433 7507            jne     nt!CmpQueryKeyName+0x38 (8063543c)

80635435 be9a0000c0      mov     esi,0C000009Ah

 

果然进入了CmpQueryKeyName了。
注意这里可以看到,进入CmpQueryKeyName后,紧接着调用了CmpConstructName函数。简单调试一下可以发现,CmpConstructName函数的参数为[KeyObject+4]:

0: kd> u

nt!CmpQueryKeyName+0x11:

80635415 8b4508          mov     eax,dword ptr [ebp+8]

80635418 8b4004          mov     eax,dword ptr [eax+4]

8063541b f6400502        test    byte ptr [eax+5],2

8063541f 7407            je      nt!CmpQueryKeyName+0x24 (80635428)

80635421 be7c0100c0      mov     esi,0C000017Ch

80635426 eb70            jmp     nt!CmpQueryKeyName+0x94 (80635498)

80635428 50              push    eax

80635429 e874f3ffff      call    nt!CmpConstructName (806347a2)

 

1: kd> dd ebp+8

f8af5994 e1b90d70 821ac800 f8af5a7c 00000200

f8af59a4 f8af5c80 81c42b10 e1c5b31c 00000000

f8af59b4 805c362c e1c5b31c f892a000 02000000

f8af59c4 f8af59e4 80545e0d 806e5f63 00000000

f8af59d4 f8af5c84 806e435f 806e4300 000000d1

f8af59e4 f8af5c84 805c362c badb0d00 f8af5c80

f8af59f4 f8af5c80 f8af5c80 00000000 00000000

f8af5a04 00000000 00000000 00000000 00000000

 

1: kd> !object e1b90d70

Object: e1b90d70 Type: (821ac858) Key

    ObjectHeader: e1b90d58 (old version)

    HandleCount: 1 PointerCount: 1

    Directory Object: 00000000 Name: \REGISTRY\USER\S-1-5-21-789336058-2146961391-682003330-1003

 


并且CmpConstructName的返回值就是这个KeyObject的Name:

1: kd> dt _UNICODE_STRING eax

nt!_UNICODE_STRING

"\REGISTRY\USER\S-1-5-21-789336058-2146961391-682003330-1003"

   +0x000 Length           : 0x76

   +0x002 MaximumLength    : 0x76

   +0x004 Buffer           : 0xe1537bb8 "\REGISTRY\USER\S-1-5-21-789336058-2146961391-682003330-1003"

 

可见,KeyObject+4的地方应该指向了一个关键结构,把这个指针丢到CmpConstructName里XX一阵就可以得到Name信息。
接下来就好办了,可以把CmpConstructName逆向分析一下就清楚了。CmpConstructName是个纯内存操作的函数,逆向不难。这里我们采用别的方法:
转到WRK去看看CmpConstructName的声明(可惜WRK里没有改函数的实现代码),可以发现其参数,也就是KeyObjcet+4处,其实是个指向CM_KEY_CONTROL_BLOCK结构的指针。
有了结构就好办了:

0: kd> dt _CM_KEY_CONTROL_BLOCK e1ac8e68

nt!_CM_KEY_CONTROL_BLOCK

   +0x000 RefCount         : 0xf

   +0x004 ExtFlags         : 0y00000100 (0x4)

   +0x004 PrivateAlloc     : 0y1

   +0x004 Delete           : 0y0

   +0x004 DelayedCloseIndex : 0y100000000000 (0x800)

   +0x004 TotalLevels      : 0y0000000011 (0x3)

   +0x008 KeyHash          : _CM_KEY_HASH

   +0x008 ConvKey          : 0x3d9f0f77

   +0x00c NextHash         : (null)

   +0x010 KeyHive          : 0xe1c89878 _HHIVE

   +0x014 KeyCell          : 0x20

   +0x018 ParentKcb        : 0xe10343c8 _CM_KEY_CONTROL_BLOCK

   +0x01c NameBlock        : 0xe1577820 _CM_NAME_CONTROL_BLOCK

   +0x020 CachedSecurity   : 0xe1b02308 _CM_KEY_SECURITY_CACHE

   +0x024 ValueCache       : _CACHED_CHILD_LIST

   +0x02c IndexHint        : 0xe1c06898 _CM_INDEX_HINT_BLOCK

   +0x02c HashKey          : 0xe1c06898

   +0x02c SubKeyCount      : 0xe1c06898

   +0x030 KeyBodyListHead : _LIST_ENTRY [ 0xe1adf874 - 0xe1b68f8c ]

   +0x030 FreeListEntry    : _LIST_ENTRY [ 0xe1adf874 - 0xe1b68f8c ]

   +0x038 KcbLastWriteTime : _LARGE_INTEGER 0x1cb8ce6`1b91d560

   +0x040 KcbMaxNameLen    : 0x38

   +0x042 KcbMaxValueNameLen : 0

   +0x044 KcbMaxValueDataLen : 0

   +0x048 KcbUserFlags     : 0y0000

   +0x048 KcbVirtControlFlags : 0y0000

   +0x048 KcbDebug         : 0y00000000 (0)

   +0x048 Flags            : 0y0000000000101100 (0x2c)

 

留意上面我粗体标示出的域,ParentKcb和NameBlock。有理由相信,Name信息就在NameBlock中:

0: kd> dt 0xe1577820 _CM_NAME_CONTROL_BLOCK

nt!_CM_NAME_CONTROL_BLOCK

   +0x000 Compressed       : 0x1 ''

   +0x002 RefCount         : 2

   +0x004 NameHash         : _CM_NAME_HASH

   +0x004 ConvKey          : 0xda862e4f

   +0x008 NextHash         : (null)

   +0x00c NameLength       : 0x2c

   +0x00e Name             : [1] 0x2d53

 

0: kd> db 0xe1577820+e

e157782e 53 2d 31 2d 35 2d 32 31-2d 37 38 39 33 33 36 30 S-1-5-21-7893360

e157783e 35 38 2d 32 31 34 36 39-36 31 33 39 31 2d 36 38 58-2146961391-68

e157784e 32 30 30 33 33 33 30 2d-31 30 30 33 00 00 00 00 2003330-1003....

e157785e 00 00 09 06 01 00 4f 62-53 71 01 06 02 0c 4f 62 ......ObSq....Ob

e157786e 44 69 00 00 00 00 00 6a-d4 81 02 06 01 00 49 6f Di.....j......Io

e157787e 4e 6d 01 06 07 0c 4d 6d-53 74 c0 38 d0 00 00 00 Nm....MmSt.8....

e157788e 00 00 c0 48 d8 00 00 00-00 00 c0 58 c0 00 00 00 ...H.......X....

e157789e 00 00 c0 68 dc 00 00 00-00 00 c0 78 e8 1f 00 00 ...h.......x....

 

终于找到Name信息了。不过这个Name信息并不完整,因为还需要拼接,NameBlock前面不是还有个ParentKcb吗?层层往上找ParentKcb,拼接一下就搞定了,呵呵。

发表评论:

Powered by emlog