第二章:ObpLookupObjectName 函数
ObjectHeader = OBJECT_TO_OBJECT_HEADER( RootDirectory );
#define OBJECT_TO_OBJECT_HEADER( o ) CONTAINING_RECORD( (o), OBJECT_HEADER, Body )
#define CONTAINING_RECORD(address, type, field) ((type *)( \
(PCHAR)(address) - \
(ULONG_PTR) \
(&((type *)0)->field))) // 即 _object_header = object - ( _object_body - 0 )
首先,&((type *)0) 转化为 type 类型的 NULL 指针,再取其 field 域的地址(即 Body,对象体的地址),这样即可取得其相对地址,但这种写法仅仅能使用在宏中,编译器会预编译并将值计算好。因此这个并非是所有结构体共用一个宏而是,一个结构体使用一个宏。因此,不同的类型头文件不会互相引用。
此处的 IoFileObjectType 是一个全局文件对象类型指针,当然也表示 IoFileObject 是一个全局文件对象类型,可理解为一个记录全局数据的对象类型,在 Win7 中可以增加一些特殊操作。
注意 TotalNumberOfObjects 和 HighWaterNumberOfObjects ,HighWaterxxxxx 代表文件对象最多时的数量,而 TotalNumberxxxxx 则表示当前的数量。
同时注意:此处的 ObjectName 是 PUNICODE_STRING ,其结构体为:
#define OBJ_NAME_PATH_SEPARATOR ((WCHAR)L'\\')
(*(ObjectName->Buffer) == OBJ_NAME_PATH_SEPARATOR)
// 注意此时比较是以 PWSTR,即 unsigned short 为单位,故只比较一个字符
[size_is(MaximumLength / 2), length_is((Length) / 2)] USHORT * Buffer;
//这种语法格式是 MIDL(Microsoft Interface Definition Language),
//是微软用于服务器和客户端程序之间通信所使用的协议(例如,RPC、COM/DCOM)的接口定义语言.
//二者同时使用时 size_is() 代表要为数据分配的内存大小,length_is() 代表要传输的数据大小
// 当 MaximumLength 和 Length 总是相同时应省略 length_is()
其中 size_is() 指明 Buffer 指针指向的地址空间的大小,而 length_is() 则指明元素的数量。
[size_is( , m)] short ** ppshort); // Specifies a pointer to a pointer
// to an m-sized block of shorts
[in, size_is(m)] short b[][20]); // If m = 10, b[10][20]
[size_is(m,n)] short ** ppshort); // Specifies a pointer to an m-sized block
// of pointers, each of which points to an n-sized block of shorts.
// m associates with the pointer closeest to the identifer it decorates.
//n associates with rest. A 指针指向一个以 m 为单位的内存块,每块都存着一个指针 B,B 指向以 n 为单位的内存块
[size_is(size), length_is(length)] char string[*]; // counted string holding at most "size" characters.
const ALIGNEDNAME ObpDosDevicesShortNamePrefix = { L'\\',L'?',L'?',L'\\' }; // L"\??\"
typedef union {
WCHAR Name[sizeof(ULARGE_INTEGER)/sizeof(WCHAR)];
ULARGE_INTEGER Alignment;
} ALIGNEDNAME; // Union 结构体,会选取较大的域分配空间,两个域共用一块内存。
#if defined(MIDL_PASS)
typedef struct _ULARGE_INTEGER {
#else // MIDL_PASS
typedef union _ULARGE_INTEGER {
struct {
ULONG LowPart;
ULONG HighPart;
};
struct {
ULONG LowPart;
ULONG HighPart;
} u;
#endif //MIDL_PASS
ULONGLONG QuadPart;
} ULARGE_INTEGER; // 如果如果没有定义 MIDL_PASS,则有三个域共用一块内存(8字节)
// 此处 MIDL_PASS 的意义是,如果 CPU 不支持一次读取 8 字节,则一次读 4 字节
InsertObject: 为 \ ,且没给出 RootDirectoryHandle 且 根目录对象未被创建时,是期望找到的对象名字。其它情况将为这个参数创建一个目录对象。
FoundObject: Receives a pointer to the object body if found
主要的逻辑结构:
- IF RootDirectoryHandle 参数给出
- 调用 ObReferenceObjectByHandle 获得 RootDirectory 地址
- 若传入的参数 ObjectName 由 \ 开头,且 ObjectName.Type 不为 IoFileObjectType ,则函数返回
- IF RootDirectory 的 ObjectHeader.Type 不是 ObpDirectoryObjectType:
- if ParseProcedure 为 NULL ,则函数返回
- 在 While 循环中,Reparse 次数限定为 32 次
- 调用 ParseProcedure
- if 函数返回值不为 Reparse,判若返回值大于0,则函数返回找到对象,否则函数返回没找到对象
- elif caller 没给 ObjectName 参数,将重新从根目录开始解析,goto ParseFromRoot
- elif MaxReparse == 0 ,而 Object == NULL ,则返回 STATUS_OBJECT_NAME_NOT_FOUND,否则返回找到对象
- ELIF ObjectName 为 NULL,调用 ObReferenceObjectByPointer ,若得到 RootDirectory 的 Object (是的)则返回找到,否则返回失败
- ELSE(从全局根目录 ObpRootDirectoryObject 开始解析)
- RootDirectory = ObpRootDirectoryObject,IF ObjectName 为 NULL 或没有以 \ 开头,则函数返回
- IF ObjectName -> Length == sizeof( (WCHAR)L'\\')
- IF RootDirectory 为 NULL
- IF InsertObject 存在,则调用 ObReferenceObjectByPointer 为 InsertObject 增加一个引用,成功则返回 InsertObject,否则返回 STATUS_INVALID_PARAMETER
- ELSE 调用 ObReferenceObjectByPointer 为 RootDirectory 增加一个引用,成功则返回 RootDirectory,否则返回函数返回的 Status
- IF RootDirectory 为 NULL
- ELSE
ParseFromRoot::
-
- IF DeviceMap == NULL ,则调用 ObDereferenceDeviceMap 解引用它,并将其置为 NULL
- IF ObjectName-> Buffer 指针按1字节对齐后不为 NULL,且 ObjectName-> Buffer 为 ‘\??\'
- IF ObpReferenceDeviceMap() 返回不为 NULL
- IF DeviceMap -> DosDevicesDirectory != NULL ,则 goto quickStart 去 dos device directory 下搜索
- IF ObpReferenceDeviceMap() 返回不为 NULL
- ELSE IF ObjectName-> Buffer 为 ‘\??’,那么返回 DeviceMap->DosDevicesDirectory
- While Reparse:
- While True:
quickStart::
- Reparse = False
- 继续解析剩余的字符串,每次移动 sizeof(WCHAR) 字节,每次解析两个 \xxx\之间的字符串
- IF 取得的字符串不存在,则退出 While 循环,否则通过参数 AccessState 检查和 Attributes 检查 Caller 是否有权限访问这个目录,没有则 Break
- IF 取得的字符串是最后一段,会调用 ObpLockLookupContext 锁住这个目录,接着调用 ObpLookupDirectoryEntry 函数取得对象体
- IF 对象体不存在,则会做一系列的安全检查,然后调用 ExAllocatePoolWithTag ,并创建一个对象体,创建成功则用 ObpInsertDirectoryEntry ,然后 break
ReparseObject::
- IF ParseProcedure && ( ! InsertObject || (ParseProcedure == ObpParseSymbolicLink)),调用 ParseProcedure
- IF 返回参数是 STATUS_REPARSE_OBJECT,则 goto ReparseObject,若为 STATUS_REPARSE 则 goto ParseFromRoot,若解析到的对象不为 NULL ,且 Status 不为前两种情况,则表明没找到对象。
- Break
- ELIF InsertObject 为空且安全检查通过,则为 Object 调用 ObReferenceObjectByPointer ,break
解引用、释放 Contex ,return(Status)