<一种Object hook的思路和实现过程>
 
作者:sudami [sudami@163.com]
时间:2008/08/08 
主题:NtClose相关逆向
关键词:Object/type/hook/OkayToCloseProcedure
 
------------------------------------------------------
    本来不准备写出来污染眼球的,因为最终没有完全实现.但觉得思路可行,之前也没人具分析过,于是匆匆的写点儿文章,给大家提供些资料参考,也许有兴趣的同学能够进一步的深入...
写的很菜,老鸟飘过 . =.=|
 
    今天上午在坛子看到一帖,关于"抹掉所有进程中自己的Handle",来防止炉子的LzOpenProcess,防dump等作用.主要思路就是NtClose;其实那个古老的RK--FUTO_enhanced的code中已经实现的更加完善. 抽点儿时间想了想,于是就有了下面的一些分析:
 
lkd> u NtClose
nt!NtClose:
8056f9e9 8bff mov edi,edi
8056f9eb 55 push ebp
8056f9ec 8bec mov ebp,esp
8056f9ee 64a124010000 mov eax,dword ptr fs:[00000124h]
8056f9f4 0fbe8040010000 movsx eax,byte ptr [eax+140h]
8056f9fb 6a00 push 0 ;比WRK中多一个参数
8056f9fd 50 push eax
8056f9fe ff7508 push dword ptr [ebp+8]
8056fa01 e85bffffff call nt!ObpCloseHandle (8056f961)
;ObpCloseHandle(Handle, KeGetCurrentThread()->PreviousMode, 0);
 
8056fa06 5d pop ebp
8056fa07 c20400 ret 4
8056fa0a 90 nop
8056fa0b 90 nop
8056fa0c 90 nop
8056fa0d 90 nop
8056fa0e 90 nop
-------------------------------------------------------------------
lkd> u ObpCloseHandle l 20
nt!ObpCloseHandle:
8056f96c c645ff00 mov byte ptr [ebp-1],0
8056f970 64a124010000 mov eax,dword ptr fs:[00000124h]
8056f976 8b4d08 mov ecx,dword ptr [ebp+8] ;ecx = Handle
8056f979 8bf0 mov esi,eax
8056f97b 8b5e44 mov ebx,dword ptr [esi+44h] ;ebx = PsGetCurrentProcess();
8056f97e b800000080 mov eax,80000000h
8056f983 23c8 and ecx,eax
8056f985 3bc8 cmp ecx,eax
;/* Check if we're dealing with a kernel handle */
;#define KERNEL_HANDLE_FLAG (1 << ((sizeof(HANDLE) * 8) - 1))
; return (BOOLEAN)((ULONG_PTR)Handle & KERNEL_HANDLE_FLAG);
 
8056f987 0f84ccf00000 je nt!ObpCloseHandle+0x28 (8057ea59) ; -->Is a kernel handle
8056f98d 8bbbc4000000 mov edi,dword ptr [ebx+0C4h] 
;edi = HandleTable = Process->ObjectTable;
;其他的判断会跳到这里
 
8056f993 ff7508 push dword ptr [ebp+8] ;
8056f996 ff8ed4000000 dec dword ptr [esi+0D4h];/* Disable Kernel APCs */
;Thread->KernelApcDisable--; 
8056f99c 57 push edi
8056f99d e88bf6ffff call nt!ExMapHandleToPointer (8056f02d)
;eax = HandleTableEntry = ExMapHandleToPointer(HandleTable, Handle);
8056f9a2 85c0 test eax,eax
8056f9a4 0f844e210200 je nt!ObpCloseHandle+0xbc (80591af8) ;-->failed,很多处理...
8056f9aa ff7510 push dword ptr [ebp+10h] ;比WRK中多一个参数
8056f9ad 6a00 push 0
8056f9af ff750c push dword ptr [ebp+0Ch]
8056f9b2 ff7508 push dword ptr [ebp+8]
8056f9b5 50 push eax
8056f9b6 57 push edi
8056f9b7 e853000000 call nt!ObpCloseHandleTableEntry (8056fa0f)
; /* Now close the entry */
;ObpCloseHandleTableEntry( HandleTable,HandleTableEntry,Handle,
; AccessMode,FALSE,0 );
8056f9bc ff86d4000000 inc dword ptr [esi+0D4h];/* Enable Kernel APCs */ 
;Thread->KernelApcDisable++; 
 
-------------------------------------------------------------------
lkd> u 8057ea59 l 20 ;-->Is a kernel handle
nt!ObpCloseHandle+0x28:
8057ea59 807d0c00 cmp byte ptr [ebp+0Ch],0
8057ea5d 0f852a0fffff jne nt!ObpCloseHandle+0x5c (8056f98d)
8057ea63 837d08fe cmp dword ptr [ebp+8],0FFFFFFFEh
8057ea67 0f84200fffff je nt!ObpCloseHandle+0x5c (8056f98d)
8057ea6d 837d08ff cmp dword ptr [ebp+8],0FFFFFFFFh
8057ea71 0f84160fffff je nt!ObpCloseHandle+0x5c (8056f98d)
8057ea77 314508 xor dword ptr [ebp+8],eax
;#define ObKernelHandleToHandle(Handle) \
; (HANDLE)((ULONG_PTR)(Handle) & ~KERNEL_HANDLE_FLAG)
;Handle = ObKernelHandleToHandle(Handle);
 
8057ea7a a154965680 mov eax,dword ptr [nt!PsInitialSystemProcess (80569654)]
8057ea7f 3bd8 cmp ebx,eax
;/* Check if we're not in the system process */
8057ea81 8b3d388c5680 mov edi,dword ptr [nt!ObpKernelHandleTable (80568c38)]
8057ea87 0f84060fffff je nt!ObpCloseHandle+0x62 (8056f993)
8057ea8d 8d4de4 lea ecx,[ebp-1Ch]
8057ea90 51 push ecx
8057ea91 50 push eax
8057ea92 e8fe8af7ff call nt!KeStackAttachProcess (804f7595)
8057ea97 c645ff01 mov byte ptr [ebp-1],1
8057ea9b e9f30effff jmp nt!ObpCloseHandle+0x62 (8056f993);attach到system进程后跳回去
--------------------------------------------------------------
 
;继续进到ObpCloseHandleTableEntry函数中:
nt!ObpCloseHandleTableEntry:
8056fa17 8b7d0c mov edi,dword ptr [ebp+0Ch]
8056fa1a 8b37 mov esi,dword ptr [edi]
8056fa1c 83e6f8 and esi,0FFFFFFF8h
;esi = ObjectHeader = ObpGetHandleObject(HandleEntry);
;#define ObpGetHandleObject(x) \
; ((POBJECT_HEADER)((ULONG_PTR)x->Object & ~OBJ_HANDLE_ATTRIBUTES))
 
8056fa1f 8b4e08 mov ecx,dword ptr [esi+8]
;nt!_OBJECT_HEADER +0x008 Type : Ptr32 _OBJECT_TYPE
;ecx = ObjectType = ObjectHeader->Type;
8056fa22 83b9a800000000 cmp dword ptr [ecx+0A8h],0
; nt!_OBJECT_TYPE +0x060 TypeInfo : _OBJECT_TYPE_INITIALIZER
; nt!_OBJECT_TYPE_INITIALIZER +0x048 OkayToCloseProcedure
;
8056fa29 8d5618 lea edx,[esi+18h] ; edx=Body=&ObjectHeader->Body;
8056fa2c 894dfc mov dword ptr [ebp-4],ecx
8056fa2f 89550c mov dword ptr [ebp+0Ch],edx
8056fa32 0f85e4ab0100 jne nt!ObpCloseHandleTableEntry+0x25 (8058a61c)
;-->正是跳到我们关心的地方.
 
------------------------------------------------------------------------
lkd> u 8058a61c
nt!ObpCloseHandleTableEntry+0x25:
8058a61c 64a124010000 mov eax,dword ptr fs:[00000124h]
8058a622 ff7514 push dword ptr [ebp+14h] ;AccessMode
8058a625 ff7510 push dword ptr [ebp+10h] ;Handle
8058a628 52 push edx ;Body
8058a629 ff7044 push dword ptr [eax+44h] ;PsGetCurrentProcess()
8058a62c ff91a8000000 call dword ptr [ecx+0A8h]
; if (!ObjectType->TypeInfo.OkayToCloseProcedure(
; PsGetCurrentProcess(),Body,Handle,AccessMode)) {
; /* 让其返回denny,直接拒绝关我们的句柄 */
; return STATUS_HANDLE_NOT_CLOSABLE;
; }
;
; ---- 要做手脚 ----
;典型的Object hook(MJ0011以前写过一篇文章,即是关于这个的[干涉注册表的hook];
;想必MJ跟踪了这些类似函数的流程,发现了这种隐蔽的hook)
;替换掉这个函数,在我们的fake函数中做处理,是我们自己要保护的handle,就拒绝之
; sudami[sudami@163.com] 2008/08/08
;
8058a632 84c0 test al,al
8058a634 0f85fe53feff jne nt!ObpCloseHandleTableEntry+0x52 (8056fa38)
8058a63a e929490700 jmp nt!ObpCloseHandleTableEntry+0x3f (805fef68) ; --->
8058a63f c3 ret
--------------------------------------------------------------------
lkd> u 805fef68 l 10
nt!ObpCloseHandleTableEntry+0x3f:
805fef68 57 push edi
805fef69 ff7508 push dword ptr [ebp+8]
805fef6c e802d7f6ff call nt!ExUnlockHandleTableEntry (8056c673)
805fef71 b8350200c0 mov eax,0C0000235h
; #define STATUS_HANDLE_NOT_CLOSABLE ((NTSTATUS)0xC0000235)
805fef76 e9260bf7ff jmp nt!ObpCloseHandleTableEntry+0x158 (8056faa1)
 
lkd> u 8056faa1 l 10
nt!ObpCloseHandleTableEntry+0x158:
8056faa1 5f pop edi
8056faa2 5e pop esi
8056faa3 c9 leave
8056faa4 c21800 ret 18h
 
---------------------------------------------------------------------
ps:或者你可以进到ExDestroyHandle函数中,从这里思考其他思路
-->ExpLookupHandleTableEntry-->InterlockedExchangePointer(&HandleTableEntry->Object, NULL);
 
/////////////////////////////////////////////////////////////////////
继续原来的话题;关键就是patch掉那个函数,看看它的原型:
 
typedef NTSTATUS
(NTAPI *OB_OKAYTOCLOSE_METHOD)(
IN PEPROCESS Process OPTIONAL,
IN PVOID Object,
IN HANDLE Handle,
IN KPROCESSOR_MODE AccessMode
);
 
在fake函数中:

引用:
 
1. 处理参数2 - Object;若其type是进程/线程,且和我们关心的相匹配,return 0;
2. 处理参数3 - Handle;ObRefenceObjectByHanlde得到EPROCESS,判断之
 
以上思路具体实现后,便可以防止被其他程序关闭自身句柄.作用如下:
    1. 文件保护 -- 若部分ARK尝试关掉你进程的所有句柄后,删除你的"站炕"文件
[yykingking牛的那种站炕],即使发IRP,也会failed.
    2. 进程相关 -- 可能恶意程序想关掉CSRSS中的自身句柄,来防止被dump;你可以用此种方式保护之
    3. [...]
 
部分关键code如下:

//安装HOOK
void InstallHook()
{
    NTSTATUS status;
    PVOID CureentProcess;
    CureentProcess = (PVOID)PsGetCurrentProcess();

    __asm
    {
        push eax
        mov eax,CureentProcess
        mov eax,[eax-0x10]
        mov EprocessObjectType,eax
        pop eax
    }

    DbgPrint("Eprocess Object Type :%08x \n" , EprocessObjectType );
    Old_OkayToCloseProcedure = EprocessObjectType->TypeInfo.OkayToCloseProcedure;
    DbgPrint("Eprocess OkayToCloseProcedure routine :%08x \n ", Old_OkayToCloseProcedure );
    DbgPrint("DeleteProcedure routine :%08x \n "
                EprocessObjectType->TypeInfo.DeleteProcedure);
    if (!MmIsAddressValid(Old_OkayToCloseProcedure)) {
        DbgPrint("!MmIsAddressValid");
        return ;
    }
    EprocessObjectType->TypeInfo.OkayToCloseProcedure = fake_OkayToCloseProcedure;
    g_bObjectHook = TRUE;
    return ;
}

NTSTATUS
fake_OkayToCloseProcedure(    
    PEPROCESS Process OPTIONAL,
    PVOID Object,
    HANDLE Handle,
    KPROCESSOR_MODE AccessCheckMode
    )
{
    NTSTATUS stat ;
    PVOID ProcessObject;
    
    stat = ObReferenceObjectByHandle(Handle,
        GENERIC_READ,
        NULL,
        KernelMode,
        &ProcessObject,
        0);
    if (!NT_SUCCESS( stat )) {
        dbg("ObReferenceObjectByHandle failed!\n");
        goto _orig_;
    }
    // 若操作的对象是我们关心的进程,且是其他进程在操作
    // 拒绝之
    if ( (DWORD)g_target_eprocess == (DWORD)ProcessObject &&
        (DWORD)g_target_eprocess != (DWORD)Process ) {
        DbgPrint("%d :denny it \n", (DWORD)Process);
        return 0 ;
    }
    
    ///////////////////////////////////////////////////////////////////
_orig_:
    __asm
    {
        push eax
        movzx eax, AccessCheckMode
        push eax
        push Handle
        push Object
        push Process
        call Old_OkayToCloseProcedure
        mov stat, eax
        pop eax
    } 
    return stat ;

 













 
附件是完整的object type hook的源码 和 调试笔记
上传的附件 NtClose相关逆向.txt
sudami.ObjectHook.code.rar