控制程式碼表篇——程式控制程式碼表

寂靜的羽夏發表於2022-01-19

寫在前面

  此係列是本人一個字一個字碼出來的,包括示例和實驗截圖。由於系統核心的複雜性,故可能有錯誤或者不全面的地方,如有錯誤,歡迎批評指正,本教程將會長期更新。 如有好的建議,歡迎反饋。碼字不易,如果本篇文章有幫助你的,如有閒錢,可以打賞支援我的創作。如想轉載,請把我的轉載資訊附在文章後面,並宣告我的個人資訊和本人部落格地址即可,但必須事先通知我

你如果是從中間插過來看的,請仔細閱讀 羽夏看Win系統核心——簡述 ,方便學習本教程。

  看此教程之前,問幾個問題,基礎知識儲備好了嗎?保護模式篇學會了嗎?系統呼叫篇學會了嗎?練習做完了嗎?沒有的話就不要繼續了。


? 華麗的分割線 ?


控制程式碼

  控制程式碼是一種核心物件,當一個程式建立或者開啟一個核心物件時,將獲得一個控制程式碼,通過這個控制程式碼可以訪問核心物件。

HANDLE g_hMutex = ::CreateMutex( NULL , FALSE, "XYZ");
HANDLE g_hMutex = ::OpenMutex( MUTEX_ALL_ACCESSFALSE, "XYZ");
HANDLE g_hEvent = ::CreateEvent( NULL, TRUE, FALSE, NULL);
HANDLE g_hThread = ::CreateThread( NULL, 0, Proc,NULL, 0, NULL);

  那麼,控制程式碼到底是何方神聖?為什麼作業系統要使用這個控制程式碼?控制程式碼存在的目的是為了避免在應用層直接修改核心物件,而控制程式碼就是一個索引,通過這個索引,我就可以在核心輕鬆找到對應的核心物件結構體的地址。
  注意,我在這裡強調一下 控制程式碼是給3環用的,而不是給核心用的 。所以在寫驅動的時候,不要搞控制程式碼花裡胡哨的東西。Windows所有涉及控制程式碼的API,一旦到了真正函式實現的部分,就立刻使用ObReferenceObjectByHandle把它轉化為真正的指向核心物件的指標,舉個例子,由於篇幅,只列出開頭部分:

; int __stdcall PspCreateProcess(int, ACCESS_MASK DesiredAccess, int, HANDLE Handle, int, HANDLE, HANDLE, HANDLE, int)
_PspCreateProcess@36 proc near          ; CODE XREF: NtCreateProcessEx(x,x,x,x,x,x,x,x,x)+72↓p
                                        ; PsCreateSystemProcess(x,x,x)+1B↓p ...

; __unwind { // __SEH_prolog
                push    11Ch
                push    offset stru_402EB0
                call    __SEH_prolog
                mov     eax, large fs:124h
                mov     [ebp+var_84], eax
                mov     cl, [eax+140h]
                mov     [ebp+AccessMode], cl
                mov     eax, [eax+44h]
                mov     [ebp+RunRef], eax
                xor     esi, esi
                mov     [ebp+var_1D], 0
                mov     [ebp+var_48], esi
                mov     [ebp+var_44], esi
                test    [ebp+arg_10], 0FFFFFFF0h
                jnz     short loc_4EFB0B
                cmp     [ebp+Handle], esi
                jz      short loc_4EFB1A
                push    esi             ; HandleInformation
                lea     eax, [ebp+Object]
                push    eax             ; Object
                push    dword ptr [ebp+AccessMode] ; AccessMode
                push    _PsProcessType  ; ObjectType
                push    80h ; '€'       ; DesiredAccess
                push    [ebp+Handle]    ; Handle
                call    _ObReferenceObjectByHandle@24 ; ObReferenceObjectByHandle(x,x,x,x,x,x)
                mov     ecx, [ebp+Object] ; Object
                mov     [ebp+var_1C], ecx
                cmp     eax, esi
                jl      loc_4F01FB
                cmp     [ebp+arg_20], esi
                jz      short loc_4EFB15
                cmp     [ecx+134h], esi
                jnz     short loc_4EFB15
                call    @ObfDereferenceObject@4 ; ObfDereferenceObject(x)

loc_4EFB0B:                             ; CODE XREF: PspCreateProcess(x,x,x,x,x,x,x,x,x)+3D↑j
                mov     eax, 0C000000Dh
                jmp     loc_4F01FB
; ---------------------------------------------------------------------------

  下面是微軟官方對ObReferenceObjectByHandle的解釋:

The ObReferenceObjectByHandle routine provides access validation on the object handle, and, if access can be granted, returns the corresponding pointer to the object's body.

控制程式碼表

  上面介紹了控制程式碼是什麼,我們來介紹控制程式碼表這個東西。不加特殊說明,我們所說的控制程式碼表是程式控制程式碼表。那麼控制程式碼表在哪裡呢?看下面的結構體:

kd> dt _EPROCESS
ntdll!_EPROCESS
   +0x000 Pcb              : _KPROCESS
   +0x06c ProcessLock      : _EX_PUSH_LOCK
   +0x070 CreateTime       : _LARGE_INTEGER
   +0x078 ExitTime         : _LARGE_INTEGER
   +0x080 RundownProtect   : _EX_RUNDOWN_REF
   +0x084 UniqueProcessId  : Ptr32 Void
   +0x088 ActiveProcessLinks : _LIST_ENTRY
   +0x090 QuotaUsage       : [3] Uint4B
   +0x09c QuotaPeak        : [3] Uint4B
   +0x0a8 CommitCharge     : Uint4B
   +0x0ac PeakVirtualSize  : Uint4B
   +0x0b0 VirtualSize      : Uint4B
   +0x0b4 SessionProcessLinks : _LIST_ENTRY
   +0x0bc DebugPort        : Ptr32 Void
   +0x0c0 ExceptionPort    : Ptr32 Void
   +0x0c4 ObjectTable      : Ptr32 _HANDLE_TABLE

  這個結構體我們之前接觸過,由於這個結構體十分龐大,只顯示了一部分。在0xc4這個偏移就是存放控制程式碼表的。我們來看看它的結構是什麼:

kd> dt _HANDLE_TABLE 
ntdll!_HANDLE_TABLE
   +0x000 TableCode        : Uint4B
   +0x004 QuotaProcess     : Ptr32 _EPROCESS
   +0x008 UniqueProcessId  : Ptr32 Void
   +0x00c HandleTableLock  : [4] _EX_PUSH_LOCK
   +0x01c HandleTableList  : _LIST_ENTRY
   +0x024 HandleContentionEvent : _EX_PUSH_LOCK
   +0x028 DebugInfo        : Ptr32 _HANDLE_TRACE_DEBUG_INFO
   +0x02c ExtraInfoPages   : Int4B
   +0x030 FirstFree        : Uint4B
   +0x034 LastFree         : Uint4B
   +0x038 NextHandleNeedingPool : Uint4B
   +0x03c HandleCount      : Int4B
   +0x040 Flags            : Uint4B
   +0x040 StrictFIFO       : Pos 0, 1 Bit

  TableCode就是所謂的控制程式碼表,但控制程式碼表是有結構的,下面我們來看看它的結構:

控制程式碼表篇——程式控制程式碼表

  ①:這一塊共計兩個位元組,高位位元組是給SetHandleInformation這個函式用的,比如寫成如下形式,那麼這個位置將被寫入0x02

`SetHandleInformation(Handle,HANDLE_FLAG_PROTECT_FROM_CLOSE,HANDLE_FLAG_PROTECT_FROM_CLOSE);`

  HANDLE_FLAG_PROTECT_FROM_CLOSE巨集的值為0x00000002,取最低位元組,最終 ① 這塊是0x0200
  ②:這塊是訪問掩碼,是給OpenProcess這個函式用的,具體的存的值就是這個函式的第一個引數的值。
  ③ 和 ④ 這兩個塊共計四個位元組,其中bit0-bit2存的是這個控制程式碼的屬性,其中bit2bit0預設為01; bit1表示的函式是該控制程式碼是否可繼承,OpenProcess的第二個引數與bit1有關,
bit31-bit3則是存放的該核心物件在核心中的具體的地址。

控制程式碼表結構

  控制程式碼表的結構還是比較複雜的,具體結構如下:

控制程式碼表篇——程式控制程式碼表

  TableCode從上圖可知,指明控制程式碼表的級數,如果低兩位是0,那麼指向的控制程式碼表裡面存放的就是真正的控制程式碼;如果低兩位是1,那麼指向的第一張控制程式碼表的每一個成員都是一個地址,每一個地址指向每一個真正的控制程式碼表,以此類推低兩位是2的情況。

本節練習

本節的答案將會在下一節進行講解,務必把本節練習做完後看下一個講解內容。不要偷懶,實驗是學習本教程的捷徑。

  俗話說得好,光說不練假把式,如下是本節相關的練習。如果練習沒做好,就不要看下一節教程了,越到後面,不做練習的話容易夾生了,開始還明白,後來就真的一點都不明白了。本節練習不多,請保質保量的完成。

1️⃣ 思考當我用函式開啟一個核心物件時,如果用CloseHandle,這個核心物件一定會被銷燬嗎?假設我用OpenProcess開啟了一個現有的程式,但開啟後,這個程式被關閉了,這個核心物件還存在嗎?
2️⃣ 使用迴圈開啟100次某個核心物件,分析控制程式碼表的結構;然後開啟1000次某個核心物件,繼續分析上述操作。

下一篇

  控制程式碼表篇——全域性控制程式碼表

相關文章