希望对菜鸟理解NT有帮助

作者:Sysnap 

出处:http://hi.baidu.com/sysnap 
标题:Windows 对象管理


把以前的对象笔记帖上来...后面多加了俩个 函数..来加深理解...
一个是ZwQueryDirectoryObject来检测隐藏驱动
一个是让ZwQueryDirectoryObject检测不了的...代码比较简单..先看前面的理论再去理解后面的函数...应该可以编译出来的..否则那个函数你可能埋怨说代码不完整...
1对象数据结构

在对象头的前面有几个可选结构... 可以是下面的组合

typedef struct _OBJECT_HEADER_QUOTA_INFO { 
ULONG PagedPoolCharge; 
ULONG NonPagedPoolCharge; 
ULONG SecurityDescriptorCharge; 
PEPROCESS ExclusiveProcess; // 拥有此对象的进程

#ifdef _WIN64 
ULONG64 Reserved; // Win64 requires these 
structures to be16 bytealigned. 
#endif 
} OBJECT_HEADER_QUOTA_INFO, 
*POBJECT_HEADER_QUOTA_INFO; 

typedef struct _OBJECT_HEADER_HANDLE_INFO { 
union{ 
POBJECT_HANDLE_COUNT_DATABASE 
HandleCountDataBase; 
OBJECT_HANDLE_COUNT_ENTRY SingleEntry; 

}; 
} OBJECT_HEADER_HANDLE_INFO, 
*POBJECT_HEADER_HANDLE_INFO; 

typedef struct _OBJECT_HEADER_NAME_INFO { 

POBJECT_DIRECTORY Directory; // 对象所属的路径
UNICODE_STRING Name;// 对象名
ULONG QueryReferences; 

#if DBG 
ULONG Reserved2; 
LONG DbgDereferenceCount; 

#ifdef _WIN64 

ULONG64 Reserved3; // Win64 requires these 
structures to be16 bytealigned. 
#endif 
#endif 
} OBJECT_HEADER_NAME_INFO, *POBJECT_HEADER_ 


NAME_INFO; 

typedef struct _OBJECT_HEADER_CREATOR_INFO { 

LIST_ENTRY TypeList; // 系统可以将所有同类型的对象串

起来
HANDLE CreatorUniqueProcess;// 对象父进程的ID 
USHORT CreatorBackTraceIndex; 
USHORT Reserved; 

} OBJECT_HEADER_CREATOR_INFO, 
*POBJECT_HEADER_CREATOR_INFO; 

对这些可选择的数据结构的总结: 

1_OBJECT_HEADER_QUOTA_INFO 中的PEPROCESS ExclusiveProcess 指示了拥有此对象的进程
2_OBJECT_HEADER_HANDLE_INFO 中存储着对象句柄相关的信息
3_OBJECT_HEADER_NAME_INFO 有对象所属的路径和对象的名字
4_OBJECT_HEADER_CREATOR_INFO, 系统用LIST_ENTRY TypeList 将所有同类型的对象串起来

接着就是对象头

typedef struct _OBJECT_HEADER { 
LONG_PTR PointerCount; 
union{ 

LONG_PTR HandleCount; 

PVOID NextToFree;
};
POBJECT_TYPE Type;
UCHAR NameInfoOffset;
UCHAR HandleInfoOffset;
UCHAR QuotaInfoOffset;
UCHAR Flags;


union{ 

POBJECT_CREATE_INFORMATION 
ObjectCreateInfo; 
PVOID QuotaBlockCharged; 
}; 

PSECURITY_DESCRIPTOR SecurityDescriptor; 

QUAD Body; 
} OBJECT_HEADER, *POBJECT_HEADER; 

对象头结构的总结
0 PointerCount, 引用记数,它是计算内核模式组件引用对象地址的次数
1 HandleCount, 打开句柄计数2Type, 对象的类型
3 NameInfoOffset,_OBJECT_HEADER_NAME_INFO 的偏移
4 HandleInfoOffset,_OBJECT_HEADER_HANDLE_INFO 的偏移
5 QuotaInfoOffset,_OBJECT_HEADER_QUOTA_INFO 的偏移
6 SecurityDescriptor, 对象的安全描述符

接着就是对象体.. 不同类型的对象这里不一样... 比如驱动对象_DRIVER_OBJECT 

lkd> DT _DRIVER_OBJECT 

nt!_DRIVER_OBJECT 
+0x000 Type : Int2B 
+0x002 Size : Int2B 
+0x004DeviceObject :Ptr32 _DEVICE_OBJECT 
+0x008 Flags : Uint4B 
+0x00cDriverStart :Ptr32Void 
+0x010DriverSize :Uint4B 
+0x014DriverSection :Ptr32Void 
+0x018 DriverExtension : Ptr32 _DRIVER_EXTENSION 
+0x01c DriverName : _UNICODE_STRING 
+0x024 HardwareDatabase: Ptr32 _UNICODE_STRING 
+0x028FastIoDispatch :Ptr32 _FAST_IO_DISPATCH 
+0x02c DriverInit : Ptr32 long 
+0x030 DriverStartIo : Ptr32 void 
+0x034 DriverUnload : Ptr32 void 
+0x038 MajorFunction : [28] Ptr32 long 

一般对象指针就是指向对象的对象体,如果得到一个对象体,想访问对象头的话. 可以使用宏OBJECT_TO_OBJECT_HEADER() 

2一个例子

eg: 

lkd> !object \Driver\beep 

Object:84fd2448Type: (86243e70) Driver
ObjectHeader:84fd2430(oldversion)
HandleCount:0 PointerCount:3
Directory Object:e1864448Name: Beep


可以看到beep 的ObjectHeader 的地址84fd2430 

lkd> dtnt!_object_header 84fd2430 


nt!_OBJECT_HEADER 

+0x000PointerCount :3 

+0x004HandleCount :0 

+0x004NextToFree :(null) 

+0x008 Type : 0x86243e70 _OBJECT_TYPE 

+0x00c NameInfoOffset : 0x10 '' 

+0x00d HandleInfoOffset :0 '' 

+0x00e QuotaInfoOffset: 0 '' 

+0x00fFlags :0x32 '2' 

+0x010 ObjectCreateInfo : 0x00000001 _OBJECT_CREATE_INFORMATION 

+0x010 QuotaBlockCharged : 0x00000001 

+0x014 SecurityDescriptor: 0xe1871aa3 

+0x018 Body : _QUAD 

1 +0x000 PointerCount : 3 表示内核模式组件引用对象地址的次数为3 

ObfReferenceObject(),ObReferenceObjectByHandle(), ObReferenceObjectByName() 和
ObReferenceObjectByPointer() 增加的就是这个PointerCount, ObfDereferenceObject

() 
和ObDereferenceObject() 用来减少这个值。
删除对象跟这个值有关
如果一个对象是临时的,那当踏的引用记数为0的时候,该对象会被系统删除,一个驱动程序
可以把一个临时对象的引用记数减少到0, 这样就可以达到删除该对象的目的
如果一个对象是在创建时标上了OBJ_PERMANENT flag, 那它是一个永久的对象,如果要
删除一个永久性的对象,需要经过下列步骤: 
1调用ObDereferenceObject 把一个永久性的对象的引用记数减少到0 
2调用ZwOpenXxx orZwCreateXxx 来获得该永久性对象的一个句柄
3或得句柄后调用ZwMakeTemporaryObject 把一个永久性的对象转化成一个临时的对象
4用得到的句柄调用ZwClose 删除该对象
所以确定一个对象是临时的和永久的由InitializeObjectAttributes 宏的Attributes 决定

2 +0x004 HandleCount : 0 表示打开句柄数是0 

3+0x008Type :0x86243e70 _OBJECT_TYPE 表示对象的类型
lkd> dt_OBJECT_TYPE 0x86243e70 
nt!_OBJECT_TYPE 

+0x000 Mutex : _ERESOURCE 

+0x038TypeList :_LIST_ENTRY[0x86243ea8 -0x86243ea8] 

+0x040 Name : _UNICODE_STRING "Driver" 

+0x048DefaultObject :0x80562e20 

+0x04cIndex :0x1a 

+0x050 TotalNumberOfObjects: 0x72 

+0x054 TotalNumberOfHandles : 0 

+0x058 HighWaterNumberOfObjects: 0x72 

+0x05c HighWaterNumberOfHandles :1 

+0x060 TypeInfo : _OBJECT_TYPE_INITIALIZER 

+0x0acKey :0x76697244 

+0x0b0ObjectLocks :[4]_ERESOURCE 


+0x040Name :_UNICODE_STRING"Driver" 表示对象的类型是"Driver" 
lkd> DT _OBJECT_TYPE_INITIALIZER 0x86243e70+0X60 
nt!_OBJECT_TYPE_INITIALIZER 

+0x000 Length : 0x4c 

+0x002UseDefaultObject:0x1 '' 

+0x003 CaseInsensitive : 0x1 '' 

+0x004 InvalidAttributes :0x100 

+0x008 GenericMapping : _GENERIC_MAPPING 

+0x018 ValidAccessMask :0x1f01ff 

+0x01c SecurityRequired :0 '' 

+0x01d MaintainHandleCount :0 '' 

+0x01e MaintainTypeList : 0 '' 

+0x020PoolType :0(NonPagedPool) 

+0x024 DefaultPagedPoolCharge :0 

+0x028 DefaultNonPagedPoolCharge: 0xd8 

+0x02c DumpProcedure : (null) 

+0x030 OpenProcedure : (null) 

+0x034 CloseProcedure : (null) 

+0x038DeleteProcedure:0x805833a2 voidnt!IopDeleteDriver+0 

+0x03c ParseProcedure : (null) 

+0x040SecurityProcedure:0x805f79fe longnt!SeDefaultObjectMethod+0 

+0x044 QueryNameProcedure : (null) 

+0x048 OkayToCloseProcedu 

这个地方的东西比较有趣,分别对应着对象的删除、lookup 、获取名字等的例程,一般对象
不是所有的routine 都有。这里有IopDeleteDriver 和SeDefaultObjectMethod 

4 +0x00c NameInfoOffset : 0x10 '' 表示_OBJECT_HEADER_NAME_INFO 的偏移是
0X10 
lkd> dtnt!_object_header_name_info 84fd2430-0x10 
nt!_OBJECT_HEADER_NAME_INFO 

+0x000 Directory : 0xe1864448 _OBJECT_DIRECTORY 

+0x004 Name : _UNICODE_STRING "Beep" 

+0x00c QueryReferences : 1 
我们看到了对象的名字"Beep" 和对象所属的路径
lkd>dt_OBJECT_DIRECTORY 0xe1864448 
nt!_OBJECT_DIRECTORY 

+0x000HashBuckets :[37]0xe185fb10 _OBJECT_DIRECTORY_ENTRY 

+0x094 Lock : _EX_PUSH_LOCK 

+0x098 DeviceMap : (null) 

+0x09c SessionId : 0xffffffff 

+0x0a0 Reserved : 0 

+0x0a2 SymbolicLinkUsageCount : 0 
关于对象目录慢点再讲


5 +0x00d HandleInfoOffset: 0 ''//0 表示没有该结构

+0x00e QuotaInfoOffset :0 ''//0 表示没有该结构

6 +0x014 SecurityDescriptor: 0xe1871aa3 它决定了谁能对该对象进行什么样的操作

lkd> dt_SECURITY_DESCRIPTOR 0xe1871aa3 

nt!_SECURITY_DESCRIPTOR
+0x000 Revision : 0x80 ''
+0x001 Sbz1 : 0x58 'X'
+0x002 Control : 0
+0x004 Owner : 0x00006800
+0x008 Group : (null)
+0x00cSacl :0x00001400 _ACL
+0x010 Dacl : 0x44000200 _ACL


7 +0x010 ObjectCreateInfo :0x00000001 _OBJECT_CREATE_INFORMATION 对象标志中OB_FLAG_CREATE_INFO 被标识

3对象组织

目录对象

WINDOWS 中的各种类型的对象都独立地存在于系统地址空间中,怎么组织起来? 
系统利用目录对象将所有的这些对象组织起来,目录对象可以看成是存储对象的容器。它是
一种特殊的对象,既然是对象,那就有个数据结构了,数据结构如下:
看一下"beep" 所在的对象目录
lkd> dt _OBJECT_DIRECTORY 0xe1864448 
nt!_OBJECT_DIRECTORY 

+0x000HashBuckets :[37]0xe185fb10 _OBJECT_DIRECTORY_ENTRY
+0x094 Lock : _EX_PUSH_LOCK
+0x098 DeviceMap : (null)
+0x09c SessionId : 0xffffffff
+0x0a0 Reserved : 0
+0x0a2 SymbolicLinkUsageCount : 0


目录对象是一个有37 个数组元素组成的哈希树,数组元素的类型是
lkd> dt _OBJECT_DIRECTORY_ENTRY 0xe185fb10 
nt!_OBJECT_DIRECTORY_ENTRY 

+0x000 ChainLink : 0xe1872718 _OBJECT_DIRECTORY_ENTRY
+0x004Object :0x84fd2448


lkd> dt _OBJECT_DIRECTORY_ENTRY 0xe1872718 

nt!_OBJECT_DIRECTORY_ENTRY 
+0x000 ChainLink : 0xe1869278 _OBJECT_DIRECTORY_ENTRY 
+0x004Object :0x8621c600 

dt nt!_DRIVER_OBJECT 0x8621c600 知道


下一个驱动对象是\Driver\NDIS... 知道怎么连接了吧
系统将对象名称进行一定的算法得出一个HASH 值,系统将所有相同HASH 值的对象链接到
响应的数组项中

类型对象

系统中每种类型对象只有一个类型对象。每种类型的对象都在其对象体中存在一个指向其类
型对象的指针,因为一种类型对象只有一个实体,所以每种类型对象的指针都是固定的,

这样我们就可以通过对象体中的类型对象指针来判断和访问对象的类型了
各个类型对象的对象体内并没有链表结构使得它们相互链接起来。但是假如对象头部前面
有OBJECT_CREATOR_INFO 结构,则相同类型的对象就可以通过它的成员ObjectList 相互链
接起来了。

每个对象头部前面有一个OBJECT_CREATOR_INFO 的可选结构。其结构如下:

lkd> dt _OBJECT_HEADER_CREATOR_INFO 

nt!_OBJECT_HEADER_CREATOR_INFO 
+0x000 TypeList : _LIST_ENTRY 
+0x008 CreatorUniqueProcess :Ptr32 Void 
+0x00c CreatorBackTraceIndex :Uint2B 
+0x00e Reserved : Uint2B 

+0x000 TypeList : _LIST_ENTRY 好东西......... 呵呵


4操作对象

可以用对象句柄来使用对象.怎么得到一个对象的句柄呢.ZwCreateXxx 和ZwOpenXxx 就可
以了,比如

NTSTATUS 

ZwOpenKey( 
OUT PHANDLE KeyHandle, 
IN ACCESS_MASK DesiredAccess, 
IN POBJECT_ATTRIBUTES ObjectAttributes 
); 

DesiredAccess, 指定对对象的权限操作,比如可读,可写

ObjectAttributes, 指向OBJECT_ATTRIBUTES 结构的一个指针, 先看下
OBJECT_ATTRIBUTES 的定义吧

typedef struct _OBJECT_ATTRIBUTES{ 
ULONG Length; 
HANDLE RootDirectory; 
PUNICODE_STRING ObjectName; 
ULONG Attributes; 
PSECURITY_DESCRIPTOR SecurityDescriptor; 
PSECURITY_QUALITY_OF_SERVICE SecurityQualityService; 




我们重要的地方在
HANDLE RootDirectory;// 对象所在的对象目录
PUNICODE_STRING ObjectName;// 将被创建和打开的对象的名字
一般我们初始话OBJECT_ATTRIBUTES 用InitializeObjectAttributes 
VOID 
InitializeObjectAttributes( 

OUT POBJECT_ATTRIBUTES InitializedAttributes,
IN PUNICODE_STRING ObjectName,
IN ULONG Attributes,
IN HANDLE RootDirectory,
IN PSECURITY_DESCRIPTOR SecurityDescriptor
);


一般把RootDirectory 设为NULL 就可以.ObjectName 给出全路劲
或者也可以用ZwCreateDirectoryObject 获得一个目录句柄
比如\DosDevices\C:\Directory\File. 
目录名可以是\DosDevices\C:\Directory\ 
文件名File 

看几个跟对象有关的函数: 

NTSTATUS 

ObReferenceObjectByHandle( 
IN HANDLE Handle, 
IN ACCESS_MASK DesiredAccess, 
IN POBJECT_TYPE ObjectType OPTIONAL, // 驱动程序一般设为NULL 就可以了
IN KPROCESSOR_MODE AccessMode, //KernelMode 
OUT PVOID *Object, 
OUT POBJECT_HANDLE_INFORMATION HandleInformation OPTIONAL //NULL 
); 

由对象的句柄获得对象体,当然,有了对象体,你可以得到其对象头

NTKERNELAPI 

NTSTATUS 

ObReferenceObjectByName( 
__inPUNICODE_STRING ObjectName, 
__inULONG Attributes, 
__in_opt PACCESS_STATEAccessState, 
__in_optACCESS_MASK DesiredAccess, 
__inPOBJECT_TYPE ObjectType, 
__inKPROCESSOR_MODE AccessMode, 
__inout_opt PVOID ParseContext, 
__out PVOID *Object 
); 

对象名字得到对象体

到此为止应该可以完了...... 当然还有很多内容... 但对象的总体应该有个概况
下面是用来检测隐藏驱动的...VOID  Scan_Object_Directory()
{
    UNICODE_STRING ObjectName;
    OBJECT_ATTRIBUTES oa ;
    HANDLE OpenObject;
    NTSTATUS status, ntStatus;
    ULONG count=0;
    
    PDIRECTORY_BASIC_INFORMATION  pBuffer2, pBuffer=NULL;
    ULONG                uLength = 0x800;
    ULONG                uContext = 0;
    ULONG                uResult = 0;
    WCHAR ObjName[256]=L"\\Driver\\";
    
    RtlInitUnicodeString(&ObjectName, L"\\Driver");
    InitializeObjectAttributes(&oa,&ObjectName, OBJ_CASE_INSENSITIVE, 

NULL, NULL);
    status=    ZwOpenDirectoryObject(&OpenObject,DIRECTORY_QUERY,&oa);
    if(!NT_SUCCESS(status))
    {
        DbgPrint("failed\n");
        return;
    }
    
    do
    {
        if (pBuffer)
        ExFreePool(pBuffer);
    
        uLength *= 2;
        pBuffer=(PDIRECTORY_BASIC_INFORMATION)ExAllocatePool(NonPagedPool 

,uLength);
        ntStatus = ZwQueryDirectoryObject(OpenObject, pBuffer, 

uLength, FALSE, TRUE, &uContext, &uResult);
    } while(ntStatus == STATUS_MORE_ENTRIES || ntStatus == 

STATUS_BUFFER_TOO_SMALL);
    
    if (ntStatus==STATUS_SUCCESS)
    {
        pBuffer2=pBuffer;
        while (pBuffer2->ObjectName.Length!=0&&pBuffer2-

>ObjectTypeName.Length!=0)
        {
            wcscat(ObjName,pBuffer2->ObjectName.Buffer);
            
            GetDriverObjByName(ObjName);  //自己实现吧..到这里有ObjName就够了
            
            pBuffer2++;
            count++;
            RtlZeroMemory(ObjName,256);
            wcscpy(ObjName,L"\\Driver\\");
        }
    }

    DbgPrint("-----%d\n",count);
    ExFreePool(pBuffer);

}


下面是断链的..

void ObDeleteObjectFromObjectDirectory(POBJECT_DIRECTORY pObjDir) //父节点 pObjDir
{
    POBJECT_DIRECTORY_ENTRY pDirectoryEntry,pEntryPrev = NULL;
    POBJECT_HEADER pObjectHeader;
    PVOID Object;
    int i;
    PUNICODE_STRING unipObjectTypeName,uniHideDriveName;
    POBJECT_DIRECTORY tmpObjDir,pDirectory;
    
    pDirectory = pObjDir;
    
    for(i = 0; i < 36; i ++)  //DIR对象的对象体(BODY)是37个元素的数组。
    {
        pDirectoryEntry = pDirectory->HashTable;  //子根目录
        // DbgPrint("---0x%x\n", pDirectoryEntry);
        while(pDirectoryEntry)
        {
            __try{
                if(pDirectoryEntry)
                {
                    Object = pDirectoryEntry->pObject ; //根目录对象
                   pObjectHeader = OBJECT_TO_OBJECT_HEADER( Object ); //根目录对象头
                   unipObjectTypeName = (PUNICODE_STRING)((*(ULONG*)((ULONG)pObjectHeader+8)+0x40));
                    //这里可以输出很多类型的对象,但我们关心的是Driver
                   //DbgPrint("--%ws  0x%x\n",unipObjectTypeName->Buffer,Object);

    ///////////////////////////////////////////////////////////////////////////////////////////////////////    
                    // DKOM判断
                    if(wcscmp(unipObjectTypeName->Buffer,L"Driver")==0)
                    {
                        uniHideDriveName = (PUNICODE_STRING)((ULONG)Object+0x1c);
                        if(wcscmp(uniHideDriveName->Buffer,L"\\Driver\\NDIS")==0)  //想隐藏的驱动对象
                        {
                            //也可以用wcschr..因为我实验的结果是有时候会出现 \Driver\Beep?? 之类的名字..
                            if (pEntryPrev)  //前继有了
                                pEntryPrev->NextEntry = pDirectoryEntry->NextEntry; //匹配就开始断练了
                        }
                        pEntryPrev = pDirectoryEntry; 
                        
                        //    DbgPrint(" %ws\n",uniHideDriveName->Buffer);

                    }
    ///////////////////////////////////////////////////////////////////////////////////////////////////////                
                    if(wcscmp(unipObjectTypeName->Buffer,L"Directory")==0)
                    {
                        tmpObjDir = Object;
                       ObDeleteObjectFromObjectDirectory( tmpObjDir);  //如果对象的类型是"Directory" 递归处理之
                    }
                }
            }__except(EXCEPTION_EXECUTE_HANDLER)
            {
             ;
            }
         
         pDirectoryEntry = pDirectoryEntry->NextEntry;
     }
  }

}