參考:《Windows內核情景分析》數組
0x01 ObReferenceObjectByHandle函數
這個函數從句柄獲得對應的內核對象,並遞增其引用計數。spa
NTSTATUS ObReferenceObjectByHandle( IN HANDLE Handle, IN ACCESS_MASK DesiredAccess, IN POBJECT_TYPE ObjectType,IN KPROCESSOR_MODE AccessMode, OUT PVOID* Object, OUT POBJECT_HANDLE_INFORMATION HandleInformation) { *Object = NULL; //若句柄是一個內核句柄或當前進程、線程的句柄 if (HandleToLong(Handle) < 0) { if (Handle == NtCurrentProcess())//若句柄值是當前進程的句柄(-1),特殊處理 { if ((ObjectType == PsProcessType) || !(ObjectType)) { CurrentProcess = PsGetCurrentProcess(); GrantedAccess = CurrentProcess->GrantedAccess; //if內核模式/要求的權限<=進程對象支持的權限(權限檢查) if ((AccessMode == KernelMode) ||!(~GrantedAccess & DesiredAccess)) { if (HandleInformation) { HandleInformation->HandleAttributes = 0; HandleInformation->GrantedAccess = GrantedAccess; } ObjectHeader = OBJECT_TO_OBJECT_HEADER(CurrentProcess); InterlockedExchangeAdd(&ObjectHeader->PointerCount, 1);//遞增引用計數 *Object = CurrentProcess;//返回獲得的對象指針 Status = STATUS_SUCCESS; } Else //權限檢查不經過 Status = STATUS_ACCESS_DENIED; } else Status = STATUS_OBJECT_TYPE_MISMATCH; return Status; } else if (Handle == NtCurrentThread())//若句柄值是當前線程的句柄(-2),特殊處理 { if ((ObjectType == PsThreadType) || !(ObjectType)) { CurrentThread = PsGetCurrentThread(); GrantedAccess = CurrentThread->GrantedAccess; if ((AccessMode == KernelMode) ||!(~GrantedAccess & DesiredAccess)) { if (HandleInformation) { HandleInformation->HandleAttributes = 0; HandleInformation->GrantedAccess = GrantedAccess; } ObjectHeader = OBJECT_TO_OBJECT_HEADER(CurrentThread); InterlockedExchangeAdd(&ObjectHeader->PointerCount, 1); *Object = CurrentThread; Status = STATUS_SUCCESS; } else Status = STATUS_ACCESS_DENIED; } else Status = STATUS_OBJECT_TYPE_MISMATCH; return Status; } else if (AccessMode == KernelMode)//若句柄是一個內核句柄 { Handle = ObKernelHandleToHandle(Handle);//去掉最高位的1,轉爲普通句柄 HandleTable = ObpKernelHandleTable;//採用內核句柄表 } } Else //最典型的狀況,普通句柄,就使用當前進程的句柄表 HandleTable = PsGetCurrentProcess()->ObjectTable; //以該句柄的值爲「索引」,找到句柄表中對應的句柄表項 HandleEntry = ExMapHandleToPointer(HandleTable, Handle) if (HandleEntry)//若是找到了,這就是一個有效句柄 { ObjectHeader = ObpGetHandleObject(HandleEntry);//關鍵。得到該句柄指向的對應對象 if (!(ObjectType) || (ObjectType == ObjectHeader->Type)) { GrantedAccess = HandleEntry->GrantedAccess; if ((AccessMode == KernelMode) ||!(~GrantedAccess & DesiredAccess))//經過權限檢查 { InterlockedIncrement(&ObjectHeader->PointerCount); Attributes = HandleEntry->ObAttributes & OBJ_HANDLE_ATTRIBUTES; if (HandleInformation) { HandleInformation->HandleAttributes = Attributes; HandleInformation->GrantedAccess = GrantedAccess; } *Object = &ObjectHeader->Body;//返回的是對象體的地址 return STATUS_SUCCESS; } Else //權限檢查沒經過 Status = STATUS_ACCESS_DENIED; } else Status = STATUS_OBJECT_TYPE_MISMATCH; } Else //有可能用戶給定的句柄值是一個無效句柄,在句柄表中找不到 Status = STATUS_INVALID_HANDLE; *Object = NULL; return Status; }
兩個特殊狀況:線程
#define NtCurrentProcess() (HANDLE)-1指針
#define NtCurrentThread() (HANDLE)-2orm
這是兩個僞句柄值,永遠得到的是當前進程、線程的內核對象。對象
另外:若句柄值的最高位是1,則是一個內核句柄,各進程通用。內核型句柄是「System」進程的句柄表中的句柄。所以,要得到內核句柄對應的對象,系統會掛靠到「System」進程的地址空間中,去查詢句柄表。blog
根據句柄值在句柄表中找到對應的表項是靠ExMamHandleToPointer這個函數實現的,這個函數又在內部調用ExpLookupHandleTableEntry來真正查找。句柄表組織爲一個稀疏數組(目的用來節省內存),但能夠索引
簡單的看作一個一維數組,不影響理解,句柄值自己也可簡單理解爲一個索引。進程
調用方傳遞給驅動程序的句柄不會通過 I/O 管理器,所以 I/O 管理器不對這類句柄執行任何驗證檢查。決不要假設一個句柄有效;始終確保句柄擁有正確的對象類型、對於所需任務的合適的訪問權、正確的訪問模式,而且訪問模式與請求的訪問兼容。
驅動程序應該謹慎使用句柄,特別是那些從用戶模式應用程序接收到的句柄。
第一,這種句柄特定於進程上下文,所以它們僅在打開句柄的進程中有效。當從不一樣的進程上下文或工做線程使用時,句柄能夠引用不一樣的對象或者只是變得無效。
第二,在驅動程序使用句柄期間,攻擊者能夠關閉和從新打開句柄來改變其引用的內容。
第三,攻擊者能夠傳入這樣一個句柄來引誘驅動程序執行對於應用程序非法的操做,例如調用 ZwXxx 函數。對於這些函數的內核模式調用方,訪問檢查被跳過,所以攻擊者能夠使用這種機制繞過驗證。
驅動程序還應該確保用戶模式應用程序不能誤用驅動程序建立的句柄。爲一個句柄設置 OBJ_KERNEL_HANDLE 屬性使其成爲內核句柄,內核句柄能夠在任何進程上下文中使用,可是隻能從內核模式進行訪問(對於傳遞給ZwXxx 例程的句柄,這特別重要)。用戶模式的進程不能訪問、關閉或替換內核句柄。