1 Windows内核中的对象管理
每个对象都分为对象头和对象体,定义如下:
typedef struct _OBJECT_HEADER
{
LONG PointerCount; //引用计数
union
{
LONG HandleCount; //指向该对象的句柄数
PVOID NextToFree; //对象被延迟删除时加入到一条链中
};
POBJECT_TYPE Type; //指向对象的类型对象
UCHAR NameInfoOffset; //名称信息的内存偏移
UCHAR HandleInfoOffset; //句柄信息的内存偏移
UCHAR QuotaInfoOffset; //配额信息的内存偏移
UCHAR Flags;
union
{
POBJECT_CREATE_INFORMATION ObjectCreateInfo;
PVOID QuotaBlockCharged;
};
PVOID SecurityDescriptor; //安全描述符
QUAD Body; //对象体开始
} OBJECT_HEADER, *POBJECT_HEADER;
每种对象都有一个对应的类型对象(OBJECT_TYPE),定义如下:
#define OBJECT_LOCK_COUNT 4
typedef struct _OBJECT_TYPE {
ERESOURCE Mutex;
LIST_ENTRY TypeList;
UNICODE_STRING Name; // Copy from object header for convenience
PVOID DefaultObject;
ULONG Index; //此类型对象在全局数组中的索引
ULONG TotalNumberOfObjects;
ULONG TotalNumberOfHandles;
ULONG HighWaterNumberOfObjects;
ULONG HighWaterNumberOfHandles;
OBJECT_TYPE_INITIALIZER TypeInfo;
#ifdef POOL_TAGGING
ULONG Key;
#endif //POOL_TAGGING
ERESOURCE ObjectLocks[ OBJECT_LOCK_COUNT ];
} OBJECT_TYPE, *POBJECT_TYPE;
WRK中的全局类型对象变量,如下表:
CmpKeyObjectType | ExWindowStationObjectType | ObpDeviceMapObjectType |
DbgkDebugObjectType | IoAdapterObjectType | ObpDirectoryObjectType |
ExCallbackObjectType | IoCompletionObjectType | ObpSymbolicLinkObjectType |
ExDesktopObjectType | IoControllerObjectType | ObpTypeObjectType |
ExEventObjectType | IoDeviceHandlerObjectType | PsJobType |
ExEventPairObjectType | IoDriviceObjectType | PsProcessType |
ExMutantObjectType | IoDriverObjectType | PsThreadType |
ExpKeyedEventObjectType | IoFileObjectType | SeTokenObjectType |
ExProfileObjectType | LpcPortObjectType | WmipGuidObjectType |
ExSemaphoreObjectType | LpcWaitablePortObjectType |
|
ExTimerObjectType | MmSectionObjectType |
|
通常在初始化过程调用ObCreateObjectType函数构建这种对象类型,完成相应全局变量的初始化。
NTKERNELAPI
NTSTATUS
ObCreateObjectType(
__in PUNICODE_STRING TypeName,
__in POBJECT_TYPE_INITIALIZER ObjectTypeInitializer,
__in_opt PSECURITY_DESCRIPTOR SecurityDescriptor,
__out POBJECT_TYPE *ObjectType
);
ObjectTypeInitializer值其类型为指向OBJECT_TYPE_INITIALIZER结构的指针,结构定义如下:
typedef struct _OBJECT_TYPE_INITIALIZER {
USHORT Length;
BOOLEAN UseDefaultObject;
BOOLEAN CaseInsensitive;
ULONG InvalidAttributes;
GENERIC_MAPPING GenericMapping;
ULONG ValidAccessMask;
BOOLEAN SecurityRequired;
BOOLEAN MaintainHandleCount;
BOOLEAN MaintainTypeList;
POOL_TYPE PoolType;
ULONG DefaultPagedPoolCharge;
ULONG DefaultNonPagedPoolCharge;
OB_DUMP_METHOD DumpProcedure;
OB_OPEN_METHOD OpenProcedure;
OB_CLOSE_METHOD CloseProcedure;
OB_DELETE_METHOD DeleteProcedure;
OB_PARSE_METHOD ParseProcedure;
OB_SECURITY_METHOD SecurityProcedure;
OB_QUERYNAME_METHOD QueryNameProcedure;
OB_OKAYTOCLOSE_METHOD OkayToCloseProcedure;
} OBJECT_TYPE_INITIALIZER, *POBJECT_TYPE_INITIALIZER;
可以看到在调用ObCreateObjectType创建对象类型,除了数据,还指定了基本的增删改等基本操作。之后内核就可以调用ObCreateObject来创建此种对象了。系统有一个全局变量ObpObjectTypes数组记录了所有已创建的类型。
NTSTATUS
ObCreateObject (
__in KPROCESSOR_MODE ProbeMode, // 决定是否要验证参数
__in POBJECT_TYPE ObjectType, // 对象类型指针
__in POBJECT_ATTRIBUTES ObjectAttributes, // 对象的属性, 最终会转化成ObAllocateObject需要的OBJECT_CREATE_INFORMATION结构
__in KPROCESSOR_MODE OwnershipMode, // 内核对象?用户对象? 同上
__inout_opt PVOID ParseContext, // 这参数没用
__in ULONG ObjectBodySize, // 对象体大小
__in ULONG PagedPoolCharge, // ...
__in ULONG NonPagedPoolCharge, // ...
__out PVOID *Object // 接收对象体的指针
)
以进程和线程对象为例:
RtlZeroMemory (&ObjectTypeInitializer, sizeof (ObjectTypeInitializer));
ObjectTypeInitializer.Length = sizeof (ObjectTypeInitializer);
ObjectTypeInitializer.SecurityRequired = TRUE;
ObjectTypeInitializer.PoolType = NonPagedPool;
ObjectTypeInitializer.InvalidAttributes = OBJ_PERMANENT |
OBJ_EXCLUSIVE |
OBJ_OPENIF;
RtlInitUnicodeString (&NameString, L"Process");
ObjectTypeInitializer.DefaultPagedPoolCharge = PSP_PROCESS_PAGED_CHARGE;
ObjectTypeInitializer.DefaultNonPagedPoolCharge = PSP_PROCESS_NONPAGED_CHARGE;
ObjectTypeInitializer.DeleteProcedure = PspProcessDelete;
ObjectTypeInitializer.ValidAccessMask = PROCESS_ALL_ACCESS;
ObjectTypeInitializer.GenericMapping = PspProcessMapping;
if (!NT_SUCCESS (ObCreateObjectType (&NameString,
&ObjectTypeInitializer,
(PSECURITY_DESCRIPTOR) NULL,
&PsProcessType))) {
return FALSE;
}
RtlInitUnicodeString (&NameString, L"Thread");
ObjectTypeInitializer.DefaultPagedPoolCharge = PSP_THREAD_PAGED_CHARGE;
ObjectTypeInitializer.DefaultNonPagedPoolCharge = PSP_THREAD_NONPAGED_CHARGE;
ObjectTypeInitializer.DeleteProcedure = PspThreadDelete;
ObjectTypeInitializer.ValidAccessMask = THREAD_ALL_ACCESS;
ObjectTypeInitializer.GenericMapping = PspThreadMapping;
if (!NT_SUCCESS (ObCreateObjectType (&NameString,
&ObjectTypeInitializer,
(PSECURITY_DESCRIPTOR) NULL,
&PsThreadType))) {
return FALSE;
}
创建进程:
Status = ObCreateObject (PreviousMode,
PsProcessType,
ObjectAttributes,
PreviousMode,
NULL,
sizeof (EPROCESS),
0,
0,
&Process);
创建线程:
Status = ObCreateObject (PreviousMode,
PsThreadType,
ObjectAttributes,
PreviousMode,
NULL,
sizeof(ETHREAD),
0,
0,
&Thread);
对象管理器使用的对象头中还有两个重要信息:
1.指针计数,记录了内核本身(也包括驱动程序)引用该对象的次数。
2.句柄计数,记录了有多少个句柄引用此对象。这些句柄可能出现在不同进程中。
对象构造由两部分完成
1. 调用ObCreateObject,根据指定的类型对象来完成对象头初始化,并且按指定大小分配对象体内存。
2. 完成对象体的初始化。
Windows允许以名称的方式管理对象。Windows维护一套全局的名称查询机制,ObpDirectoryObjectType类型对象就说实现这一机制的关键。
Windows内部维护了一个对象层次目录(系统全局名字空间),其根目录对象是由全局变量ObRootDirectoryObject来定义。
NTCreateDirectoryObject 可以查看Callback、ArcName、Device、Driver、FileSystem
、KernelObjects 等子目录创建的过程。
ObpLookupDirectoryEntry指定目录查找一个名称。
ObpInsertDirectoryEntry 把一个对象插入到一个目录中。
ObpDeleteDirectoryEntry 删除找到的某一项。
ObpLookupObjectName 从指定的目录或根目录,递归地根据名称来找到一个对象。
ObpLookupObjectName定义如下:
NTSTATUS
ObpLookupObjectName (
IN HANDLE RootDirectoryHandle,
IN PUNICODE_STRING ObjectName,
IN ULONG Attributes,
IN POBJECT_TYPE ObjectType,
IN KPROCESSOR_MODE AccessMode,
IN PVOID ParseContext OPTIONAL,
IN PSECURITY_QUALITY_OF_SERVICE SecurityQos OPTIONAL,
IN PVOID InsertObject OPTIONAL,
IN OUT PACCESS_STATE AccessState,
OUT POBP_LOOKUP_CONTEXT LookupContext,
OUT PVOID *FoundObject
);
ObpLookupObjectName执行逻辑:
a. 首先判断名称是否以”\??\”打头,如果是的话,需要拿到当前进程的DeviceMap(设备表),以进一步查询。
b. 如果名称正好是”\??”,则直接返回当前进程的DeviceMap 作为结果。
c. 调用ObpLookupDirectoryEntry 查询。
在ObpLookupObjectName的代码逻辑中,可以看到进程设备表(DeviceMap),而且在目录对象的数据结构OBJECT_DIRECTORY中也有一个名为DeviceMap的成员,指向一个DEVICE_MAP。DEVICE_MAP的含义是,它定义了一个DOS设备名字空间,比如驱动器字母(C:/D:)和一些外设(com1),当对象管理器看到一个以“\??\”这样的名称,它会利用进程DeviceMap来获得相应的对象目录,然后进一步解析剩余名称字符串。
对象管理器中的对象是执行体对象,位于系统地址空间中,所有进程都可以访问这些对象。用户模式代码是调用系统服务时通过句柄来引用执行体对象。在内核中通过ObReferenceObjectByHandle将一个句柄转换成对应的对象。该函数负责从当前进程环境或内核环境句柄表中获得指定的对象引用。
关于对象的内存结构和生命周期,对象分为对象头对象体,头部结构是OBJECT_HEADER,对象体因对象而异,所以看到很多函数在接受对象作为参数时,类型为PVOID,而对象头和体通过ObpAllocateObject函数可知在同一块内存中。
从对象体转换到对象头,可以通过
#define OBJECT_TO_OBJECT_HEADER( o ) \
CONTAINING_RECORD( (o), OBJECT_HEADER, Body )
对象是通过引用计数实现管理生命周期,一旦计数为零,则生命周期结束,对象的引用计数来源于2个方面,第一个来源是内核中的指针引用,一旦内核中新增了一个对象的引用,则对象引用计数需要增1,如果不在引用,则减1。第二个来源是进程打开一个对象获取一个句柄,它以后通过此句柄来引用此对象。对象头信息中准确记录有多少个句柄指向该对象,当1句柄不再被使用时,句柄计数减一。两种作用是在函数ObpIncrementHandleCount/ObpDecrementHandleCount中完成的。