NtOpenProcess

 一般在內核SSDT HOOK的時候就是直接鉤住SSDT表替換NtOpenProcess的地址來達到保護進程的目的。而在InlineHook中,側需要更進一步的瞭解NtOpenProcess函數,才能更好的做inlinehook。

首先說說Windows中用戶層 OpenProces,WIN32函數OpenProces執行後,調用NTDLL.DLL中NtOpenProcess函數,然後此函數INT2E自陷進入內核,開始從SSDT表中查找NtOpenProcess函數地址,繼而在內核中執行NtOpenProcess函數。這是OpenProcess調用的路徑。如圖。

 

那麼就這樣說,在Windows中,OpenProcess函數是對NtOpenProcess調用的一個包裝。NtOpenProcess包含在內核模塊NTOSKRNL.EXE當中。

內核中NtOpenProcess函數結構如下:

NTSTATUS NtOpenProcess (
   OUT PHANDLE ProcessHandle,
   IN ACCESS_MASK DesiredAccess,
   IN POBJECT_ATTRIBUTES ObjectAttributes,
   IN PCLIENT_ID ClientId OPTIONAL);


ClientID參數是OpenProcess傳遞的實際PID。這個參數是可選的,但是根據我們的觀察,OpenProcess在調用NtOpenProcess的時候總是使用這個參數。

在內核中執行的時候,NtOpenProcess主要實現3個功能:

1. 它通過調用PsLookupProcessByProcessId函數來驗證進程是否存在。
2. 它通過調用ObOpenObjectByPointer來打開進程的句柄。
3. 如果打開進程句柄成功,就將句柄返回給調用者。

那麼很自然的,PsLookupProcessByProcessId函數和ObOpenObjectByPointer函數就成爲我們inlinehook的目標

先看PsLookupProcessByProcessId函數:

lkd> u 805caefa
nt!NtOpenProcess+0x1fc:
805caefa 45              inc     ebp
805caefb dc50ff          fcom    qword ptr [eax-1]
805caefe 75d4            jne     nt!NtOpenProcess+0x1d6 (805caed4)
805caf00 e8617a0000      call    nt!PsLookupProcessByProcessId (805d2966)
805caf05 ebde            jmp     nt!NtOpenProcess+0x1e7 (805caee5)
805caf07 8d45e0          lea     eax,[ebp-20h]
805caf0a 50              push    eax
805caf0b ff75cc          push    dword ptr [ebp-34h]

lkd> u nt!PsLookupProcessByProcessId
nt!PsLookupProcessByProcessId:
805d2966 8bff            mov     edi,edi
805d2968 55              push    ebp
805d2969 8bec            mov     ebp,esp
805d296b 53              push    ebx
805d296c 56              push    esi
805d296d 64a124010000    mov     eax,dword ptr fs:[00000124h]
805d2973 ff7508          push    dword ptr [ebp+8]
805d2976 8bf0            mov     esi,eax
805d2978 ff8ed4000000    dec     dword ptr [esi+0D4h]
805d297e ff35c0385680    push    dword ptr [nt!PsThreadType+0x4 (805638c0)]
805d2984 e803ad0300      call    nt!ExEnumHandleTable+0x408 (8060d68c)
805d2989 8bd8            mov     ebx,eax
805d298b 85db            test    ebx,ebx
805d298d c745080d0000c0 mov     dword ptr [ebp+8],0C000000Dh
805d2994 7432            je      nt!PsLookupProcessByProcessId+0x62 (805d29c8)

再看ObOpenObjectByPointer函數:

lkd> u 805caf15
nt!NtOpenProcess+0x217:
805caf15 8d8548ffffff    lea     eax,[ebp-0B8h]
805caf1b 50              push    eax
805caf1c ff75c8          push    dword ptr [ebp-38h]
805caf1f ff75dc          push    dword ptr [ebp-24h]
805caf22 e8bd07ffff      call    nt!ObOpenObjectByPointer (805bb6e4)
805caf27 8bf8            mov     edi,eax
805caf29 8d8548ffffff    lea     eax,[ebp-0B8h]
805caf2f 50              push    eax

lkd> u nt!ObOpenObjectByPointer
nt!ObOpenObjectByPointer:
805bb6e4 8bff            mov     edi,edi
805bb6e6 55              push    ebp
805bb6e7 8bec            mov     ebp,esp
805bb6e9 81ec94000000    sub     esp,94h
805bb6ef 53              push    ebx
805bb6f0 8b5d08          mov     ebx,dword ptr [ebp+8]
805bb6f3 56              push    esi
805bb6f4 57              push    edi

知道NtOpenProcess執行的過程後,爲防止打開某特定進程的句柄,可以直接inlinehook ObOpenObjectByPointer函數,Hook 這個函數有一大好處,那就是進程線程都一起保護了

ObOpenObjectByPointer(
                             IN PVOID Object,
                             IN ULONG HandleAttributes,
                             IN PACCESS_STATE PassedAccessState OPTIONAL,
                             IN ACCESS_MASK DesiredAccess,
                             IN POBJECT_TYPE ObjectType OPTIONAL,
                             IN KPROCESSOR_MODE AccessMode,
                             OUT PHANDLE Handle
                             )

NTSTATUS
T_ObOpenObjectByPointer(
                             IN PVOID Object,
                             IN ULONG HandleAttributes,
                             IN PACCESS_STATE PassedAccessState OPTIONAL,
                             IN ACCESS_MASK DesiredAccess,
                             IN POBJECT_TYPE ObjectType OPTIONAL,
                             IN KPROCESSOR_MODE AccessMode,
                             OUT PHANDLE Handle
                             )
{
    PEPROCESS EPROCESS;
    if((Object!=NULL) && (MmIsAddressValid(Object)))// 地址有效性驗證
    {
        if(OBJECT_TO_OBJECT_HEADER(Object) ->Type== *PsProcessType)// 若爲進程對象
        {
            if((PsGetCurrentProcess() !=ProtectedProcess))// 若操作者不是受保護的進程自己
            {
                if(Object==ProtectedProcess)// 若被操作進程是受保護進程
                {
                    returnSTATUS_ACCESS_DENIED;// 拒絕訪問
                }

             }
         }
        else
             if(OBJECT_TO_OBJECT_HEADER(Object) ->Type== *PsThreadType)// 若爲線程對象
            {
                EPROCESS=IoThreadToProcess(Object);// 獲取線程對應進程的 EPROCESS
                if(EPROCESS==ProtectedProcess)// 若是受保護進程
                {
                    if((PsGetCurrentProcess() !=ProtectedProcess))// 若操作者不是受保護進程自己
                    {
                        returnSTATUS_ACCESS_DENIED;// 拒絕訪問
                    }
                 }
             }

     }
// 正常調用,執行原 API
    returnMy_ObOpenObjectByPointer(
        Object,
        HandleAttributes,
        PassedAccessState,
        DesiredAccess,
        ObjectType,
        AccessMode,
        Handle
        );
}


My_ObOpenObjectByPointer(
        Object,
        HandleAttributes,
        PassedAccessState,
        DesiredAccess,
        ObjectType,
        AccessMode,
        Handle
        );

函數中,執行完ObOpenObjectByPointer函數前幾字節後JMP到ObOpenObjectByPointer函數內部。

 

 

句柄:句柄的最高Bit表明了句柄是屬於內核的還是屬於用戶態的 最高位爲1,爲內核態句柄,反之爲用戶態句柄。

不過有兩個例外: 用來表示當前進程句柄的-1 和用來表示當前線程句柄的-2

 

 

有關NtOpenProcess的問題:

 

系統中有系統句柄表,每個進程還有自己的句柄表,但是如果在驅動裏創建一個句柄,那麼這個句柄到底是屬於進程的還是系統的? 我過去一直以爲這個是由 ETHREAD::PreviousMode決定的,其實不是。這個是由傳遞給NtOpenProcess的參數ObjectAttributes決定的,這個參數中如果帶有  OBJ_KERNEL_HANDLE 參數,並且

ETHREAD:: PreviousMode = KernelMode,那麼這個句柄就會被創建在系統全局句柄表中。

 

如果 ETHREAD:: PreviousMode = UserMode,那麼即使 ObjectAttributes 中含有 OBJ_KERNEL_HANDLE  參數,也會被 ObSanitizeHandleAttributes() 函數幹掉。因此如果要創建系統全局表中的句柄,就必須令 PreviousMode  = KernelMode

 

另外在從驅動程序中調用NtOpenProcess時,因爲傳遞的參數地址都是內核態地址,此時如果PreviousMode = UserMode,那麼在進入 try塊後可能會產生異常(未親自驗證),導致無法正常完成NtOpenProcess調用。因此要在內核中創建屬於進程上下文句柄表的句柄,要設置PreviousMode  = KernelMode,並在 ObjectAttributes 中去除 OBJ_KERNEL_HANDLE 參數即可。

 

 

說起來NtOpenProcess並不複雜,只是個流程函數而已,大部分活都是Ob函數族和Ps函數族在做。最後是在ObpCreateHandle()完成了句柄的創建,但是究竟選擇進程句柄表還是全局表,則是由 EThread::PreviousMode   ObjectAttributes 一起決定的。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章