<一种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 ; }