跟踪随笔一则
首先预祝张老师5月份即将出版的新书《软件调试》(Debugging Principle) 能获得巨大的成功!^_^ 其实就张老师十余年的技术积累、沉淀来看该书的成功也是必然。怎奈鄙人才疏学浅,许多方面只是一知半解(Machine Checked Architect / JTAG Debugging / KD Engine etc...),并不敢妄加评论... 所以我还是力所能及,先写点小文章道个喜吧。
也确实好久没写随笔了,一直忙于各种杂事和内核跟踪。今天整理一些平时的跟踪涂鸦贴过来,请各位前辈指正!
C的代码就不完整列了,毕竟微$不让... 感兴趣的话需要自己对照着查阅 WRK1.2 或是 ReactOS0.44,所以下面的信息都是汇编的,而且我每一行都写了注释。
不过也不是什么函数都值得往下面列,大部分情况下编译器生成的代码都中规中矩,看C足矣。细节看多了除了能让你更加清楚过程调用和强身健体以外没什么特别的好处;但也有时编译器会给你些惊喜(当然,好的或抓狂的都有),这些惊喜会让你看到C语言不易被察觉的地方。正好这两天在研究 \ob 文件夹,我选了nt!ObpAllocateObject 这个 ASM 较为特别的函数入手。
; 平台:Win-XP SP2
; 下面的注释缩小显示, 需要研究细节的朋友请拷贝到适当的编辑环境下查看
nt!ObpAllocateObject:
805b6008 8bff mov edi,edi ; ┓
805b600a 55 push ebp ; ┣ Prologue
805b600b 8bec mov ebp,esp ; ┛
805b600d 51 push ecx ;
805b600e 8b4d08 mov ecx,dword ptr [ebp+8] ; ECX <-- IN POBJECT_CREATE_INFORMATION ObjectCreateInfo
805b6011 53 push ebx ;
805b6012 56 push esi ;
805b6013 8b7514 mov esi,dword ptr [ebp+14h] ; ESI <-- IN PUNICODE_STRING ObjectName
805b6016 33d2 xor edx,edx ; ┓ if(ObjectCreateInfo == NULL)
805b6018 3bca cmp ecx,edx ; ┛
805b601a 57 push edi ;
805b601b 8b7d10 mov edi,dword ptr [ebp+10h] ; EDI <-- IN POBJECT_TYPE ObjectType OPTIONAL
805b601e 750e jne nt!ObpAllocateObject+0x26 (805b602e) ; else 的执行流 (我们目前不走这个执行流)
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~; 下面这段代码编译器生成的很绝!
805b6020 6a10 push 10h ; ┓ sizeof(OBJECT_HEADER_NAME_INFO) == sizeof(OBJECT_HEADER_CREATOR_INFO)
805b6022 5b pop ebx ; ┛
805b6023 8955fc mov dword ptr [ebp-4],edx ; QuotaInfoSize = 0;
805b6026 895510 mov dword ptr [ebp+10h],edx ; HandleInfoSize = 0;
805b6029 895d14 mov dword ptr [ebp+14h],ebx ; CreatorInfoSize = sizeof(OBJECT_HEADER_CREATOR_INFO);
805b602c eb63 jmp nt!ObpAllocateObject+0x89 (805b6091) ; NameInfoSize = sizeof(OBJECT_HEADER_NAME_INFO); 这句最绝,用 EBX 寄存器保存 NameInfoSize,连局部变量都不要
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
805b602e 8b4110 mov eax,dword ptr [ecx+10h] ; ┓
805b6031 3b8784000000 cmp eax,dword ptr [edi+84h] ; ┣ (ObjectCreateInfo->PagedPoolCharge != ObjectType->TypeInfo.DefaultPagedPoolCharge)
805b6037 7514 jne nt!ObpAllocateObject+0x45 (805b604d) ; ┛
805b6039 8b4114 mov eax,dword ptr [ecx+14h] ; ┓
805b603c 3b8788000000 cmp eax,dword ptr [edi+88h] ; ┣ (ObjectCreateInfo->NonPagedPoolCharge != ObjectType->TypeInfo.DefaultNonPagedPoolCharge)
805b6042 7509 jne nt!ObpAllocateObject+0x45 (805b604d) ; ┛
805b6044 81791800080000 cmp dword ptr [ecx+18h],800h ; (ObjectCreateInfo->SecurityDescriptorCharge > SE_DEFAULT_SECURITY_QUOTA)
805b604b 7611 jbe nt!ObpAllocateObject+0x56 (805b605e) ; ┓
805b604d 64a124010000 mov eax,dword ptr fs:[00000124h] ; ┃
805b6053 8b4044 mov eax,dword ptr [eax+44h] ; ┣ (PsGetCurrentProcess() != PsInitialSystemProcess)
805b6056 3b0554a35580 cmp eax,dword ptr [nt!PsInitialSystemProcess (8055a354)] ; ┃
805b605c 7508 jne nt!ObpAllocateObject+0x5e (805b6066) ; ┛
805b605e f60120 test byte ptr [ecx],20h ; (ObjectCreateInfo->Attributes & OBJ_EXCLUSIVE)
805b6061 8955fc mov dword ptr [ebp-4],edx ; ┓ QuotaInfoSize = 0;
805b6064 7407 je nt!ObpAllocateObject+0x65 (805b606d) ; ┛
805b6066 c745fc10000000 mov dword ptr [ebp-4],10h ; QuotaInfoSize = sizeof(OBJECT_HEADER_QUOTA_INFO);
805b606d 8a477d mov al,byte ptr [edi+7Dh] ; --[2 if(ObjectType->TypeInfo.MaintainHandleCount)
805b6070 8b5e04 mov ebx,dword ptr [esi+4] ; --[3 if(ObjectName->Buffer != NULL)
805b6073 f6d8 neg al ; --[2 如果执行NEG指令时操作数为0,则CF将被置0;如果操作数为非零,则CF将被置1
805b6075 1bc0 sbb eax,eax ; --[2 (ObjectType->TypeInfo.MaintainHandleCount)为零时,EAX为0;非零时EAX为-1
805b6077 83e008 and eax,8 ; --[2 得出结果
805b607a f7db neg ebx ; --[3 如果执行NEG指令时操作数为0,则CF将被置0;如果操作数为非零,则CF将被置1
805b607c 894510 mov dword ptr [ebp+10h],eax ; --[2 保存结果
805b607f 8a477e mov al,byte ptr [edi+7Eh] ; --[4 if(ObjectType->TypeInfo.MaintainTypeList)
805b6082 1bdb sbb ebx,ebx ; --[3 if(ObjectName->Buffer != NULL)为零时,EBX为0;非零时EBX为-1
805b6084 83e310 and ebx,10h ; --[3 得出结果 (就保存在EBX)
805b6087 f6d8 neg al ; --[4 如果执行NEG指令时操作数为0,则CF将被置0;如果操作数为非零,则CF将被置1
805b6089 1bc0 sbb eax,eax ; --[4 if(ObjectType->TypeInfo.MaintainTypeList)为零时,EAX为0;非零时EAX为-1
805b608b 83e010 and eax,10h ; --[4 得出结果
805b608e 894514 mov dword ptr [ebp+14h],eax ; --[4 保存结果
--------------------------------------------------------------------------------------------
805b6091 8b4514 mov eax,dword ptr [ebp+14h] ; CreatorInfoSize == 10h
805b6094 8b4dfc mov ecx,dword ptr [ebp-4] ; QuotaInfoSize == 00h
805b6097 03c3 add eax,ebx ; NameInfoSize == 10h
805b6099 034510 add eax,dword ptr [ebp+10h] ; HandleInfoSize == 00h
805b609c 3bfa cmp edi,edx ; (ObjectType == NULL)
805b609e 8d4c0818 lea ecx,[eax+ecx+18h] ; 这句对象头累加操作也非常绝!
805b60a2 740b je nt!ObpAllocateObject+0xa7 (805b60af) ; 我们的执行流 o-----------------------------+
805b60a4 399780000000 cmp dword ptr [edi+80h],edx ; || (ObjectType->TypeInfo.PoolType == NonPagedPool) |
805b60aa 7403 je nt!ObpAllocateObject+0xa7 (805b60af) ; 我们的执行流 o-----------------------------+
805b60ac 33d2 xor edx,edx ; ┓ PoolType = PagedPool; |
805b60ae 42 inc edx ; ┛ |
805b60af 85ff test edi,edi ; ObjectType == NULL ? <-----------------------------/
805b60b1 b84f626a54 mov eax,546A624Fh ; 1. 'TjbO' 我们的执行流 o------------------+
805b60b6 7406 je nt!ObpAllocateObject+0xb6 (805b60be) ; |
805b60b8 8b87ac000000 mov eax,dword ptr [edi+0ACh] ; 2. ObjectType->Key o------------------+
805b60be 0d00000080 or eax,80000000h ; | PROTECTED_POOL <------------------/
805b60c3 50 push eax ; (ARG3) : (ObjectType == NULL ? 'TjbO' : ObjectType->Key) | PROTECTED_POOL
805b60c4 8b4518 mov eax,dword ptr [ebp+18h] ; ┓
805b60c7 03c8 add ecx,eax ; ┣ (ARG2) : HeaderSize + ObjectBodySize
805b60c9 51 push ecx ; ┛
805b60ca 52 push edx ; (ARG1) : PoolType
805b60cb e8b0eff8ff call nt!ExAllocatePoolWithTag (80545080) ; {FUN} : ExAllocatePoolWithTag()
805b60d0 8bd0 mov edx,eax ; ┓
805b60d2 85d2 test edx,edx ; ┣ if(ObjectHeader == NULL)
805b60d4 750a jne nt!ObpAllocateObject+0xd8 (805b60e0) ; ┛
805b60d6 b89a0000c0 mov eax,0C000009Ah ; return STATUS_INSUFFICIENT_RESOURCES;
805b60db e925010000 jmp nt!ObpAllocateObject+0x1fd (805b6205) ; -=[ 程序错误退出 ]=-
--------------------------------------------------------------------------------------------
805b60e0 8b4dfc mov ecx,dword ptr [ebp-4] ; ┓
805b60e3 85c9 test ecx,ecx ; ┣ 1. 我们没有 _OBJECT_HEADER_QUOTA_INFO 结构
805b60e5 7421 je nt!ObpAllocateObject+0x100 (805b6108) ; ┛
805b60e7 8b4508 mov eax,dword ptr [ebp+8] ; ┓
805b60ea 8b4010 mov eax,dword ptr [eax+10h] ; ┣ QuotaInfo->PagedPoolCharge = ObjectCreateInfo->PagedPoolCharge;
805b60ed 8902 mov dword ptr [edx],eax ; ┛
805b60ef 8b4508 mov eax,dword ptr [ebp+8] ; ┓
805b60f2 8b4014 mov eax,dword ptr [eax+14h] ; ┣ QuotaInfo->NonPagedPoolCharge = ObjectCreateInfo->NonPagedPoolCharge;
805b60f5 894204 mov dword ptr [edx+4],eax ; ┛
805b60f8 8b4508 mov eax,dword ptr [ebp+8] ; ┓
805b60fb 8b4018 mov eax,dword ptr [eax+18h] ; ┣ QuotaInfo->SecurityDescriptorCharge = ObjectCreateInfo->SecurityDescriptorCharge;
805b60fe 83620c00 and dword ptr [edx+0Ch],0 ; QuotaInfo->ExclusiveProcess = NULL;
805b6102 894208 mov dword ptr [edx+8],eax ; ┛
805b6105 83c210 add edx,10h ; 对象头指针跳过可选头 _OBJECT_HEADER_QUOTA_INFO
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
805b6108 837d1000 cmp dword ptr [ebp+10h],0 ; ┓ 2. 我们没有 _OBJECT_HEADER_HANDLE_INFO 结构
805b610c 7407 je nt!ObpAllocateObject+0x10d (805b6115) ; ┛
805b610e 83620400 and dword ptr [edx+4],0 ; HandleInfo->SingleEntry.HandleCount = 0;
805b6112 83c208 add edx,8 ; 对象头指针跳过可选头 _OBJECT_HEADER_HANDLE_INFO
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
805b6115 85db test ebx,ebx ; ┓ 3. 我们存在 _OBJECT_HEADER_NAME_INFO 结构
805b6117 7418 je nt!ObpAllocateObject+0x129 (805b6131) ; ┛
805b6119 8b06 mov eax,dword ptr [esi] ; EAX <-- IN PUNICODE_STRING ObjectName
805b611b 894204 mov dword ptr [edx+4],eax ; ┓
805b611e 8b4604 mov eax,dword ptr [esi+4] ; ┣ NameInfo->Name = *ObjectName;
805b6121 832200 and dword ptr [edx],0 ; NameInfo->Directory = NULL;
805b6124 894208 mov dword ptr [edx+8],eax ; ┛
805b6127 c7420c01000000 mov dword ptr [edx+0Ch],1 ; NameInfo->QueryReferences = 1;
805b612e 83c210 add edx,10h ; 对象头指针跳过可选头 _OBJECT_HEADER_NAME_INFO
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
805b6131 837d1400 cmp dword ptr [ebp+14h],0 ; ┓ 4. 我们存在 _OBJECT_HEADER_CREATOR_INFO 结构
805b6135 741f je nt!ObpAllocateObject+0x14e (805b6156) ; ┛
805b6137 6683620c00 and word ptr [edx+0Ch],0 ; CreatorInfo->CreatorBackTraceIndex = 0;
805b613c 64a124010000 mov eax,dword ptr fs:[00000124h] ; ┓
805b6142 8b4044 mov eax,dword ptr [eax+44h] ; ┣ PsGetCurrentProcess()->UniqueProcessId;
805b6145 8b8084000000 mov eax,dword ptr [eax+84h] ; ┛
805b614b 894208 mov dword ptr [edx+8],eax ; CreatorInfo->CreatorUniqueProcess = PsGetCurrentProcess()->UniqueProcessId;
805b614e 895204 mov dword ptr [edx+4],edx ; ┓ InitializeListHead(&CreatorInfo->TypeList);
805b6151 8912 mov dword ptr [edx],edx ; ┛
805b6153 83c210 add edx,10h ; 对象头指针跳过可选头 _OBJECT_HEADER_CREATOR_INFO
--------------------------------------------------------------------------------------------
805b6156 85c9 test ecx,ecx ; ┓ 1. 我们没有 _OBJECT_HEADER_QUOTA_INFO 结构
805b6158 740d je nt!ObpAllocateObject+0x15f (805b6167) ; ┛ if(QuotaInfoSize != 0)
805b615a 024d10 add cl,byte ptr [ebp+10h] ; ┓
805b615d 02cb add cl,bl ; ┃ ObjectHeader->QuotaInfoOffset = (UCHAR)(QuotaInfoSize + \
805b615f 024d14 add cl,byte ptr [ebp+14h] ; ┣ HandleInfoSize + \
805b6162 884a0e mov byte ptr [edx+0Eh],cl ; ┃ NameInfoSize + \
805b6165 eb04 jmp nt!ObpAllocateObject+0x163 (805b616b) ; ┛ CreatorInfoSize);
805b6167 c6420e00 mov byte ptr [edx+0Eh],0 ; ObjectHeader->QuotaInfoOffset = 0;
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
805b616b 837d1000 cmp dword ptr [ebp+10h],0 ; ┓ 2. 我们没有 _OBJECT_HEADER_HANDLE_INFO 结构
805b616f 740d je nt!ObpAllocateObject+0x176 (805b617e) ; ┛ if(HandleInfoSize != 0)
805b6171 8a4510 mov al,byte ptr [ebp+10h] ; ┓
805b6174 02c3 add al,bl ; ┃ ObjectHeader->HandleInfoOffset = (UCHAR)(HandleInfoSize + \
805b6176 024514 add al,byte ptr [ebp+14h] ; ┣ NameInfoSize + \
805b6179 88420d mov byte ptr [edx+0Dh],al ; ┃ CreatorInfoSize);
805b617c eb04 jmp nt!ObpAllocateObject+0x17a (805b6182) ; ┛
805b617e c6420d00 mov byte ptr [edx+0Dh],0 ; ObjectHeader->HandleInfoOffset = 0;
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
805b6182 33c9 xor ecx,ecx ; ┓ 3. 我们存在 _OBJECT_HEADER_NAME_INFO 结构
805b6184 3bd9 cmp ebx,ecx ; ┛ if(NameInfoSize != 0)
805b6186 7408 je nt!ObpAllocateObject+0x188 (805b6190) ; ┓
805b6188 025d14 add bl,byte ptr [ebp+14h] ; ┣ ObjectHeader->NameInfoOffset = (UCHAR)(NameInfoSize + \
805b618b 885a0c mov byte ptr [edx+0Ch],bl ; ┃ CreatorInfoSize);
805b618e eb04 jmp nt!ObpAllocateObject+0x18c (805b6194) ; ┛
805b6190 c6420c00 mov byte ptr [edx+0Ch],0 ; ObjectHeader->NameInfoOffset = 0;
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
805b6194 394d14 cmp dword ptr [ebp+14h],ecx ; ┓ 4. 我们存在 _OBJECT_HEADER_CREATOR_INFO 结构,但没有单独的域表示 CreatorInfoOffset
805b6197 c6420f01 mov byte ptr [edx+0Fh],1 ; ObjectHeader->Flags = OB_FLAG_NEW_OBJECT;
805b619b 7404 je nt!ObpAllocateObject+0x199 (805b61a1) ; ┛
805b619d c6420f05 mov byte ptr [edx+0Fh],5 ; OB_FLAG_NEW_OBJECT | OB_FLAG_CREATOR_INFO
805b61a1 394d10 cmp dword ptr [ebp+10h],ecx ; ┓ if(HandleInfoSize != 0) {
805b61a4 7404 je nt!ObpAllocateObject+0x1a2 (805b61aa) ; ┣ ObjectHeader->Flags |= OB_FLAG_SINGLE_HANDLE_ENTRY;
805b61a6 804a0f40 or byte ptr [edx+0Fh],40h ; ┛ }
805b61aa 807d0c00 cmp byte ptr [ebp+0Ch],0 ; ┓ if(OwnershipMode == KernelMode)
805b61ae c70201000000 mov dword ptr [edx],1 ; ObjectHeader->PointerCount = 1; <<<<
805b61b4 894a04 mov dword ptr [edx+4],ecx ; ObjectHeader->HandleCount = 0; <<<<
805b61b7 897a08 mov dword ptr [edx+8],edi ; ObjectHeader->Type = ObjectType; <<<<
805b61ba 7504 jne nt!ObpAllocateObject+0x1b8 (805b61c0) ; ┣ ObjectHeader->Flags |= OB_FLAG_KERNEL_OBJECT;
805b61bc 804a0f02 or byte ptr [edx+0Fh],2 ; ┛
805b61c0 8b4508 mov eax,dword ptr [ebp+8] ; ┓
805b61c3 3bc1 cmp eax,ecx ; ┣ (ObjectCreateInfo != NULL)
805b61c5 7412 je nt!ObpAllocateObject+0x1d1 (805b61d9) ; ┛
805b61c7 f60010 test byte ptr [eax],10h ; && (ObjectCreateInfo->Attributes & OBJ_PERMANENT)
805b61ca 7404 je nt!ObpAllocateObject+0x1c8 (805b61d0) ; ┓ ObjectHeader->Flags |= OB_FLAG_PERMANENT_OBJECT;
805b61cc 804a0f10 or byte ptr [edx+0Fh],10h ; ┛
805b61d0 f60020 test byte ptr [eax],20h ; && (ObjectCreateInfo->Attributes & OBJ_EXCLUSIVE)
805b61d3 7404 je nt!ObpAllocateObject+0x1d1 (805b61d9) ; ┓ ObjectHeader->Flags |= OB_FLAG_EXCLUSIVE_OBJECT;
805b61d5 804a0f08 or byte ptr [edx+0Fh],8 ; ┛
805b61d9 3bf9 cmp edi,ecx ; ┓ if(ObjectType != NULL)
805b61db 894210 mov dword ptr [edx+10h],eax ; ObjectHeader->ObjectCreateInfo = ObjectCreateInfo; <<<<
805b61de 894a14 mov dword ptr [edx+14h],ecx ; ObjectHeader->SecurityDescriptor = NULL; <<<<
805b61e1 741b je nt!ObpAllocateObject+0x1f6 (805b61fe) ; ┛
805b61e3 8d7750 lea esi,[edi+50h] ; ┓
805b61e6 89750c mov dword ptr [ebp+0Ch],esi ; ┃
805b61e9 b801000000 mov eax,1 ; ┣ InterlockedIncrement((PLONG)&ObjectType->TotalNumberOfObjects);
805b61ee 8b4d0c mov ecx,dword ptr [ebp+0Ch] ; ┃
805b61f1 0fc101 xadd dword ptr [ecx],eax ; ┃
805b61f4 8b36 mov esi,dword ptr [esi] ; ┛
805b61f6 3b7758 cmp esi,dword ptr [edi+58h] ; if(ObjectType->TotalNumberOfObjects > ObjectType->HighWaterNumberOfObjects) {
805b61f9 7603 jbe nt!ObpAllocateObject+0x1f6 (805b61fe) ; ObjectType->HighWaterNumberOfObjects = ObjectType->TotalNumberOfObjects;
805b61fb 897758 mov dword ptr [edi+58h],esi ; }
--------------------------------------------------------------------------------------------
805b61fe 8b451c mov eax,dword ptr [ebp+1Ch] ; ┓ *ReturnedObjectHeader = ObjectHeader;
805b6201 8910 mov dword ptr [eax],edx ; ┛
805b6203 33c0 xor eax,eax ; return STATUS_SUCCESS;
805b6205 5f pop edi ; ┓
805b6206 5e pop esi ; ┃
805b6207 5b pop ebx ; ┣ Epilogue
805b6208 c9 leave ; ┃
805b6209 c21800 ret 18h ; ┛
对于Win内核程序员来说“对象管理器”应该是一门必修课。众所周知,在对象体的负向偏移处,还有一个大小为18字节的 _OBJECT_HEADER 以及 4个(至多)大小不等的可选头部:
+-----------------------------------------------------------------+
+------->| ( _OBJECT_HEADER_QUOTA_INFO ) |
| +---->| ( _OBJECT_HEADER_HANDLE_INFO ) |
| | +->| ( _OBJECT_HEADER_NAME_INFO ) |
| | | | ( _OBJECT_HEADER_CREATOR_INFO ) |
| | | +------------------------[ Object Header ]------------------------+
| | | | nt!_OBJECT_HEADER |
| | | | +0x000 PointerCount : Int4B |
| | | | +0x004 HandleCount : Int4B |
| | | | +0x004 NextToFree : Ptr32 Void |
| | | | +0x008 Type : Ptr32 _OBJECT_TYPE |
| | +-o| +0x00c NameInfoOffset : UChar |
| +----o| +0x00d HandleInfoOffset : UChar |
+-------o| +0x00e QuotaInfoOffset : UChar |
| +0x00f Flags : UChar |
| +0x010 ObjectCreateInfo : Ptr32 _OBJECT_CREATE_INFORMATION |
| +0x010 QuotaBlockCharged : Ptr32 Void |
| +0x014 SecurityDescriptor : Ptr32 Void |
| +0x018 Body : _QUAD |
+-------------------------[ Object Body ]-------------------------+
| OBJECT_DIRECTORY, DRIVER_OBJECT, DEVICE_OBJECT, FILE_OBJECT... |
+-----------------------------------------------------------------+
那么你是否好奇过,这些结构是在哪里诞生的? 相互关系怎样? 顺序怎样? 等等等等,Well,这些答案都在函数 nt!ObpAllocateObject 里。它的大致内容是这样的:
1. 根据函数的参数一等 (IN POBJECT_CREATE_INFORMATION ObjectCreateInfo) 确定4个可选头部是否存在。
2. 计算首部大小,并根据函数的参数五 (IN ULONG ObjectBodySize) 分配整个对象的空间(包括可选头部、头部和对象体)
3. 如空间分配成功,依次给可选头部初始化
4. 初始化 _OBJECT_HEADER
5. 返回 _OBJECT_HEADER 的指针,程序结束,外层的封装函数继续初始化对象体
可以想见,这个函数的调用会非常的频繁,因为每一个对象(包括头部)都是由它来分配空间并初始化(对象体是由外部的封装函数初始化)的,所以,在这里 Inline Hook 一下我们可以干坏事... 先打住~
以 Type Object Type 为例,它的头结构是这样的(_OBJECT_BODY 由外层封装 nt!ObCreateObjectType 初始化):
kd> dt nt!_OBJECT_HEADER_NAME_INFO 82ded5d0-20
+0x000 Directory : (null)
+0x004 Name : _UNICODE_STRING "Type"
+0x00c QueryReferences : 1
kd> dt nt!_OBJECT_HEADER_CREATOR_INFO 82ded5d0-10
+0x000 TypeList : _LIST_ENTRY [ 0x82ded5c0 - 0x82ded5c0 ] ; 这个域是可以遍历的 ObpTypeObjectType
+0x008 CreatorUniqueProcess : (null)
+0x00c CreatorBackTraceIndex : 0
+0x00e Reserved : 0
kd> dt nt!_OBJECT_HEADER 82ded5d0
+0x000 PointerCount : 1 ; 指针引用计数
+0x004 HandleCount : 0 ; 句柄引用计数
+0x004 NextToFree : (null)
+0x008 Type : (null) ; 注意这里,目前仍是 NULL
+0x00c NameInfoOffset : 0x20 ' '
+0x00d HandleInfoOffset : 0 ''
+0x00e QuotaInfoOffset : 0 ''
+0x00f Flags : 0x7 '' ; OB_FLAG_NEW_OBJECT | OB_FLAG_CREATOR_INFO | OB_FLAG_KERNEL_OBJECT
+0x010 ObjectCreateInfo : (null)
+0x010 QuotaBlockCharged : (null)
+0x014 SecurityDescriptor : (null)
+0x018 Body : _QUAD
那么,这个函数的汇编代码有什么特别的地方吗? 其实也什么... 只是看惯了中规中矩的代码后这里有几个地方值得讲一讲:
1. 在确定4个可选头部是否存在的时候,汇编代码混合使用了 NEG / SBB / AND 指令(1个用普通方法,3个用 NEG / SBB),而我个人比较偏爱后者
C的代码是这样的:
..............
1) /* Check if we have quota */
if (((ObjectCreateInfo->PagedPoolCharge != ObjectType->TypeInfo.DefaultPagedPoolCharge ||
ObjectCreateInfo->NonPagedPoolCharge != ObjectType->TypeInfo.DefaultNonPagedPoolCharge ||
ObjectCreateInfo->SecurityDescriptorCharge > SE_DEFAULT_SECURITY_QUOTA) &&
PsGetCurrentProcess() != PsInitialSystemProcess) ||
(ObjectCreateInfo->Attributes & OBJ_EXCLUSIVE)) {
/* Set quota size */
QuotaInfoSize = sizeof( OBJECT_HEADER_QUOTA_INFO );
} else {
/* No Quota */
QuotaInfoSize = 0;
}
2) /* Check if we have a handle database */
if (ObjectType->TypeInfo.MaintainHandleCount) {
/* Set handle database size */
HandleInfoSize = sizeof( OBJECT_HEADER_HANDLE_INFO );
} else {
/* None */
HandleInfoSize = 0;
}
..............
1) 和 2) 的语义基本一致,可生成的汇编代码却大相径庭:1) 的中规中矩;2) 的简洁高效(连选择分支的跳转语句都没有):
1) 805b602e 8b4110 mov eax,dword ptr [ecx+10h]
805b6031 3b8784000000 cmp eax,dword ptr [edi+84h]
805b6037 7514 jne nt!ObpAllocateObject+0x45 (805b604d)
805b6039 8b4114 mov eax,dword ptr [ecx+14h]
805b603c 3b8788000000 cmp eax,dword ptr [edi+88h]
805b6042 7509 jne nt!ObpAllocateObject+0x45 (805b604d)
805b6044 81791800080000 cmp dword ptr [ecx+18h],800h
805b604b 7611 jbe nt!ObpAllocateObject+0x56 (805b605e)
805b604d 64a124010000 mov eax,dword ptr fs:[00000124h]
805b6053 8b4044 mov eax,dword ptr [eax+44h]
805b6056 3b0554a35580 cmp eax,dword ptr [nt!PsInitialSystemProcess (8055a354)]
805b605c 7508 jne nt!ObpAllocateObject+0x5e (805b6066)
805b605e f60120 test byte ptr [ecx],20h
805b6061 8955fc mov dword ptr [ebp-4],edx
805b6064 7407 je nt!ObpAllocateObject+0x65 (805b606d)
805b6066 c745fc10000000 mov dword ptr [ebp-4],10h
2) 805b606d 8a477d mov al,byte ptr [edi+7Dh]
805b6073 f6d8 neg al
805b6075 1bc0 sbb eax,eax
805b6077 83e008 and eax,8
805b607c 894510 mov dword ptr [ebp+10h],eax
熟悉反汇编的人应该经常能看到 neg / sbb 对,这种代码在处理某些条件语句上往往有很好的效率体现。此时您一定还会说我偷梁换柱,因为 1) 的入口条件过于复杂,是不可以用 neg 指令影响 CF 做简单判定的 —— 嘿嘿,我这里只是示意一下编译上的差别,而这也正是 Win 编译器在编译上述代码时生成了3个简单句1个复杂句的原因。neg / sbb 指令对的原理请参见代码注释,这里不再累述。
2. nt!ObpAllocateObject 函数局部变量的使用经过了优化
C的代码:
ULONG QuotaInfoSize;
ULONG HandleInfoSize;
ULONG NameInfoSize;
ULONG CreatorInfoSize;
.........
//
// Now compute the total header size
//
HeaderSize = QuotaInfoSize +
HandleInfoSize +
NameInfoSize +
CreatorInfoSize +
FIELD_OFFSET( OBJECT_HEADER, Body );
.........
四个本应该在局部变量空间上的变量被优化成:1个在局部(栈)上、2个在参数空间上、一个在寄存器里,非常和谐...
805b6091 8b4514 mov eax,dword ptr [ebp+14h] ; CreatorInfoSize == 10h - 占用了参数空间!
805b6094 8b4dfc mov ecx,dword ptr [ebp-4] ; QuotaInfoSize == 00h
805b6097 03c3 add eax,ebx ; NameInfoSize == 10h - 占用了 EBX 寄存器!
805b6099 034510 add eax,dword ptr [ebp+10h] ; HandleInfoSize == 00h - 占用了参数空间!
805b609e 8d4c0818 lea ecx,[eax+ecx+18h] ; 这句对象头累加操作也非常绝!
既然聊到了对象管理器,那下面显然还应该聊聊句柄表。句柄表是由函数 nt!ExpAllocateHandleTable 创建的,它的第一次调用位于 nt!ObInitSystem,目标是 SYSTEM 进程。
nt!ExpAllocateHandleTable:
8060325c 8bff mov edi,edi ; ┓
8060325e 55 push ebp ; ┣ Prologue
8060325f 8bec mov ebp,esp ; ┛
80603261 56 push esi ; : HandleTable
80603262 684f627462 push 6274624Fh ; (ARG) : 'btbO'
80603267 6a44 push 44h ; (ARG) : sizeof(HANDLE_TABLE)
80603269 6a01 push 1 ; (ARG) : PagedPool
8060326b e8101ef4ff call nt!ExAllocatePoolWithTag (80545080) ; {FUN} : ExAllocatePoolWithTag()
80603270 8bf0 mov esi,eax ; ┓
80603272 85f6 test esi,esi ; ┣ ExAllocatePoolWithTag() 返回值判断
80603274 0f84b1000000 je nt!ExpAllocateHandleTable+0xcf (8060332b) ; ┛
8060327a 53 push ebx ; : Process
8060327b 8b5d08 mov ebx,dword ptr [ebp+8] ; ┓ EBX <-- Process
8060327e 85db test ebx,ebx ; ┣ if(ARGUMENT_PRESENT(Process))
80603280 741b je nt!ExpAllocateHandleTable+0x41 (8060329d) ; ┛
80603282 6a44 push 44h ; (ARG) : sizeof(HANDLE_TABLE)
80603284 53 push ebx ; (ARG) : Process
80603285 e8c641f2ff call nt!PsChargeProcessPagedPoolQuota (80527450) ; {FUN} : PsChargeProcessPagedPoolQuota()
8060328a 85c0 test eax,eax ; ┓ 函数 ExpAllocateHandleTable() 的返回值判断
8060328c 7d0f jge nt!ExpAllocateHandleTable+0x41 (8060329d) ; ┛ 正确的程序执行流
8060328e 6a00 push 0 ; (ARG) : 0
80603290 56 push esi ; (ARG) : HandleTable
80603291 e85017f4ff call nt!ExFreePoolWithTag (805449e6) ; {FUN} : ExFreePoolWithTag()
80603296 33c0 xor eax,eax ; return NULL;
80603298 e98d000000 jmp nt!ExpAllocateHandleTable+0xce (8060332a) ; -=[ 程序错误退出 ]=-
--------------------------------------------------------------------------------------------
8060329d 57 push edi