1.句柄表
什么是句柄(內核對象)
當一個進程創建或者打開一個內核對象時,將獲得一個句柄,通過這個句柄可以訪問內核對象。
如:
HANDLE g_hMutex =::CreateMutex(NULL,FALSE, "XYZ"); HANDLE g_hMutex =::OpenMutex(MUTEX ALL ACCESS, FALSE, "XYZ"); HANDLE g_hEvent =::CreateEvent(NULL, TRUE, FALSE, NULL); HANDLE g_hThread =::CreateTpread(NULL, 0, Proc, NULL, 0, NULL); ...我們調用一些窗口相關函數的句柄,雖然也是句柄但是和這個是不一樣的,這里面的句柄它一定對應一個內核對象。
這些內核對象其實也是一個結構體,要在0環才能進行讀寫。
為什么要有句柄?
句柄存在的目的是為了避免在應用層直接修改內核對象。
HANDLE g hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
如果 g_hEvent 存儲的就是EVENT內核對象的地址,那么就意味著我們可以在應用層修改這個地址,一旦指向了無效的內核內存地址就會藍屏
每一個進程都會一張句柄表。
創建的和打開的句柄它在0環對應的內核對象的地址就會存儲到這張表中。(創建和打開是不一樣的)
如果再次通過OpenEvent來打開,這時系統并不會為這個事件創建一個新的結構體,它有一個計數器打開一次它就加一次。
調用的函數返回值為HANDLE的返回的就是句柄表中對應成員的索引。
句柄表的位置
dt _EPROCESS ...+0x0c4 ObjectTable : Ptr32 _HANDLE_TABLE ...kd> dt _HANDLE_TABLE nt!_HANDLE_TABLE+0x000 TableCode : //指向句柄表的存儲結構+0x004 QuotaProcess : _EPROCESS//句柄表記錄的內存資源記錄在此進程中+0x008 UniqueProcessId : Void//創建進程的ID,用于回調函數+0x00c HandleTableLock : [4] _EX_PUSH_LOCK//句柄表鎖 (僅在句柄表拓展時使用)+0x01c HandleTableList : _LIST_ENTRY //所有句柄表形成一個鏈表,鏈表頭為全局變量HandleTableListHand+0x024 HandleContentionEvent : _EX_PUSH_LOCK//若在訪問句柄表時發生競爭,則在此推鎖上等待+0x028 DebugInfo : //調試信息,當調試句柄時才有意義+0x02c ExtraInfoPages : //審計信息所占用的頁面數量+0x030 FirstFree : //空閑鏈表表頭的句柄索引+0x034 LastFree : //最近被釋放的句柄索引,用于FIFO類型空閑鏈表+0x038 NextHandleNeedingPool : //下一次句柄表拓展的起始句柄索引+0x03c HandleCount : //正在使用的句柄表項的數量+0x040 Flags : //標志域+0x040 StrictFIFO : //是否使用FIFO風格的重用,既先釋放先重用測試代碼:
int main() {DWORD pid;HANDLE hProcess;HWND hwnd = ::FindWindow(NULL,"計算器");::GetWindowThreadProcessId(hwnd, &pid);for (int i = 0;i<100;i++){hProcess = ::OpenProcess(PROCESS_ALL_ACCESS,TRUE, pid);//hProcess = ::OpenProcess(PROCESS_CREATE_THREAD, TRUE, pid);printf("句柄:%x\n", hProcess);}//HANDLE_FLAG_PROTECT_FROM_CLOSE 句柄不可用CloseHandle關閉//SetHandleInformation(hProcess, HANDLE_FLAG_PROTECT_FROM_CLOSE, HANDLE_FLAG_PROTECT_FROM_CLOSE);getchar();return 0; } !!! 這段代碼,可以看到有100個句柄,但不要認為創建了100個,這里一個都沒創建 進程句柄在你的程序運行起來就創建好了。(都是同一個內核對象)句柄表的成員每個是8字節
我們來看看最后一個句柄,我這是0x640。0x640/4 =0x190。(x86的 HANDLE 為4字節)
應用層函數對句柄表中表項的影響
typedef struct _HANDLE_TABLE_ENTRY {union{PVOID Object;//指向句柄所代表的對象 (bit31- bit0)ULONG_PTR ObAttributes;//最低3位有特別含義,參見OBJ_HANDLE_ATTRIBUTES結構PHANDLE_TABLE_ENTRY_INFO InfoTable;//各個句柄表頁面的第一個句柄表項,使用此成員指向一張表ULONG_PTR Value;};union{ULONG GrantedAccess;//訪問掩碼 (②)//當NtGlobalFlag中包含 FLE_KERNEL_STACK_TRACE_DB 標記時使用struct{USHORT GrantedAccessIndex;USHORT CreatorBackTraceIndex;};LONG NextFreeTableEntry;//空閑時表示下一個句柄表索引}; } HANDLE_TABLE_ENTRY, *PHANDLE_TABLE_ENTRY;
8字節分成4部分每個部分2字節。
①這一塊共計兩個字節,低字節保留恒為0,高位字節是給SetHandlelnformation,這個函數用的,
比如寫成SetHandleInformation(Handle, HANDLE_FLAG_PROTECT_FROM_CLOSE, HANDLE_FLAG PROTECT_FROM_CLOSE),那么這個位置將被寫入0x02;
HANDLE_FLAG PROTECT_FROM_CLOSE宏的值為0x00000002,取最低字節,最終①這塊是 0x02 00
②這塊是訪問掩碼,是給OpenProcess這個函數用的,
OpenProcess(dmDesirdAccess,blnheriHande, dwProcessld)具體的存的就是這個函數的第一個參數的值
③和④這兩個塊共計四個字節,其中bito-bit2存的是這個句柄的屬性,其中bit2 bit0 默認為0,1;
bit1表示的函數是該句柄是否可繼承; OpenProcess的第二個參數與bit1有關;
bit31- bit3 則是存放的該內核對象在內核中的具體的地址
演示代碼
int main() {DWORD pid;HANDLE hProcess;HWND hwnd = ::FindWindow(NULL,L"計算器");::GetWindowThreadProcessId(hwnd, &pid);for (int i = 0;i<100;i++){//hProcess = ::OpenProcess(PROCESS_ALL_ACCESS,TRUE, pid);hProcess = ::OpenProcess(PROCESS_CREATE_THREAD, TRUE, pid);printf("句柄:%x\n", hProcess);}//HANDLE_FLAG_PROTECT_FROM_CLOSE 句柄不可用CloseHandle關閉SetHandleInformation(hProcess, HANDLE_FLAG_PROTECT_FROM_CLOSE, HANDLE_FLAG_PROTECT_FROM_CLOSE);getchar();return 0; }
只有它一個不一樣,這就是OpenProcess和SetHandleInformation對它的影響。
后4字節就是對應的0環結構體的地址,后3bit要清0。
所有的內核對象看到的并不是完整的,它上面還有0x18個字節的_OBJECT_HEADER結構
kd> dt _OBJECT_HEADER nt!_OBJECT_HEADER+0x000 PointerCount : Int4B+0x004 HandleCount : Int4B+0x004 NextToFree : Ptr32 Void+0x008 Type : Ptr32 _OBJECT_TYPE+0x00c NameInfoOffset : UChar+0x00d HandleInfoOffset : UChar+0x00e QuotaInfoOffset : UChar+0x00f Flags : UChar+0x010 ObjectCreateInfo : Ptr32 _OBJECT_CREATE_INFORMATION+0x010 QuotaBlockCharged : Ptr32 Void+0x014 SecurityDescriptor : Ptr32 Void+0x018 Body : _QUAD句柄表中后4字節的地址后3bit清0后,得到的是一個完整的結構,我這里OpenProcess對應的是_EPROCESS。
_OBJECT_HEADER后面緊跟著就是_EPROCESS。
892e81b3后3bit清0 -> 892e81b0 dt _EPROCESS 892e81b0 + 0x18 ......略 ;就是當前進程的結構對象總結