第二章: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 引數,將重新從根目錄開始解析,gotoParseFromRoot
- elif MaxReparse == 0 ,而 Object == NULL ,則返回 STATUS_OBJECT_NAME_NOT_FOUND,否則返回找到物件
- ELIF ObjectName 為 NULL,呼叫 ObReferenceObjectByPointer ,若得到RootDirectory 的 Object (是的)則返回找到,否則返回失敗
- ELSE
- RootDirectory = ObpRootDirectoryObject,IF ObjectName 為 NULL 或沒有以 \ 開頭,則函式返回
- IFObjectName -> Length ==sizeof( (WCHAR)L'\\')
- IF RootDirectory 為 NULL
- IFInsertObject 存在,則呼叫ObReferenceObjectByPointer 為 InsertObject 增加一個引用,成功則返回InsertObject,否則返回STATUS_INVALID_PARAMETER
- ELSE呼叫ObReferenceObjectByPointer 為RootDirectory 增加一個引用,成功則返回RootDirectory,否則返回函式返回的 Status
- IF RootDirectory 為 NULL
- ELSE
ParseFromRoot::
-
- IFDeviceMap == NULL ,則呼叫 ObfDereferenceDeviceMap 解引用它,並將其置為 NULL
- IFObjectName-> Buffer 指標按1位元組對齊後不為 NULL,且ObjectName-> Buffer 為 ‘\??\'
- IFObpReferenceDeviceMap() 返回不為 NULL
- IF DeviceMap -> DosDevicesDirectory != NULL ,則 goto quickStart 去 dosdevice directory 下搜尋
- IFObpReferenceDeviceMap() 返回不為 NULL
- ELSE IFObjectName-> Buffer 為 ‘\??’,那麼返回DeviceMap->DosDevicesDirectory
- While Reparse:
- While True:
quickStart::
- 繼續解析剩餘的字串,在這個迴圈中,每次移動 sizeof(WCHAR) 位元組,每次解析兩個 \xxx\之間的字串
- IF 不存在,則退出 While 迴圈,否則通過引數 AccessState 檢查和 Attributes 檢查 Caller 是否有許可權訪問這個目錄,沒有則 Break
- IF 取得的字串是最後一段,會呼叫ObpLockLookupContext 鎖住這個目錄,接著呼叫ObpLookupDirectoryEntry 函式取得物件體
- IF 物件體不存在,則會做一系列的安全檢查,然後呼叫 ExAllocatePoolWithTag ,並建立一個物件體
ReparseObject::
- IF 物件體不存在,則會做一系列的安全檢查,然後呼叫ExAllocatePoolWithTag ,並建立一個物件體,Break。
- IFParseProcedure ==ObpParseSymbolicLink 且InsertObject 為 NULL,呼叫ParseProcedure
- IF 返回引數是STATUS_REPARSE_OBJECT,則 gotoReparseObject,若為 STATUS_REPARSE 則 goto ParseFromRoot,若解析到的物件不為 NULL ,且 Status 不為前兩種情況,則 Break。
解引用、釋放 Contex ,return(Status)