实现用句柄表反调试
句柄
句柄可以说是Windows编程的核心,当一个进程创建或者打开一个内核对象时,就会获得一个句柄,通过这个句柄可以访问内核对象
为什么要有句柄
句柄存在的目的是为了避免在应用层直接修改内核对象,句柄也就是内核对象中的一个索引,而内核对象存放在句柄表中
句柄表的位置
在_EPROCESS中,有一个成员叫ObjectTable,offset为c4
其指向了一个_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
在offset 为0的地方 他指向的就是句柄表
句柄表结构
而这个指针的值,最后两位代表的是多少等级的句柄表,也就是如下
句柄表中,句柄大小是4字节,但是却需要占8个字节(Windows就是这么设计的),如果是0级句柄表,那就是512个条目,如果是1级句柄表,那就是512*1024,因为一个句柄表大小为4k,然候0级句柄表的每个条目指向的也是一个句柄表,此时的0级句柄表的条目只需要4个字节,因为当成了索引
句柄表项
由于句柄大小为8字节,在内存中我们通过Handle/4得到句柄在句柄表中的序号,然后在通过TableCode+(Handle/4)*8得到其句柄表项,其中句柄表项的属性如下
也就是说,当我们有一个进程打开了一个进程时,他的句柄表中,会有另一个进程的句柄,此时我们遍历所有进程的句柄表,查看是否有自身,即可验证自己是否被调试
遍历句柄表实现反调试
我们已经知道句柄表了,那么如果调试器要调试程序,就肯定要打开那个程序,通过OpenProcess来获取程序的句柄,那么其对应的句柄表,肯定有这个值,所以我们可以根据这个特性来反调试
所以思路是:遍历所有其他进程句柄表,看哪个进程的句柄表中保护自己的进程,如果有,说明正在被调试。
这里我监视的是notepad.exe是否有调试程序
#include <ntddk.h> #include <ntstatus.h> ULONG GetProcessEprocess(char* ProcessName) { PEPROCESS pEprocess,pCurEProcess; //获取进程的EProcess __asm { mov eax,fs:[0x124] mov eax,[eax+0x220] mov pEprocess,eax } pCurEProcess=pEprocess; do { PCHAR ImageFileName=(PCHAR)pCurEProcess+0x174; if(strcmp(ImageFileName,ProcessName)==0) { return (ULONG)pCurEProcess; } pCurEProcess=(PEPROCESS)(*(PULONG)((ULONG)pCurEProcess+0x88)-0x88); }while(pCurEProcess!=pEprocess); return 0; } BOOLEAN CheckProcessDebug(ULONG CheckedProcess) { PEPROCESS pEprocess,pCurEProcess; PULONG table; PEPROCESS eps; ULONG ObTable; int i,j,k; //获取进程的EProcess __asm { mov eax,fs:[0x124] mov eax,[eax+0x220] mov pEprocess,eax } DbgPrint("开始检查\n"); pCurEProcess=pEprocess; do { PCHAR ImageFileName=(PCHAR)pCurEProcess+0x174; ULONG ObjectTable=*(PULONG)((ULONG)pCurEProcess+0xc4); if(ObjectTable!=0) { DbgPrint("[%s]\t[%s]\t[%x]\t[%x]\n",ImageFileName,((PCHAR)CheckedProcess+0x174),ObjectTable,CheckedProcess); ObTable=(ULONG)ObjectTable; switch(ObTable&0x3) { case 0: table=(PULONG)((*(PULONG)ObTable)&0xfffffffc); for(i=0;i<512;i+=2) { eps=(PEPROCESS)((table[i]&0xfffffff8)+0x18); //DbgPrint("程序正在被 [%x] 检查!\n", (ULONG)eps); if(strcmp(ImageFileName,"csrss.exe")!=0&&eps==(PEPROCESS)CheckedProcess) { DbgPrint("程序正在被 [%s] 调试!\n", ImageFileName); return TRUE; } } break; case 1: for(i=0;i<1024;i++) { table=(PULONG)(*(PULONG)ObTable)+i; if(MmIsAddressValid((PVOID)table)) for(j=0;j<512;j++) { eps=(PEPROCESS)((ULONG)(table+j*2)&0xfffffff8+0x18); DbgPrint("程序正在被 [%s] 检查!\n", (PCHAR)eps+0x174); if(eps==(PEPROCESS)CheckedProcess) { DbgPrint("程序正在被 [%s] 调试!\n", ImageFileName); return TRUE; } } } break; case 2: for(i=0;i<1024;i++) { table=(PULONG)(*(PULONG)ObTable)+i; if(MmIsAddressValid((PVOID)table)) for(j=0;j<1024;j++) { table=(PULONG)(*(PULONG)ObTable)+j; if(MmIsAddressValid((PVOID)table)) for(k=0;k<512;k++) { eps=(PEPROCESS)((ULONG)(table+k*2)&0xfffffff8+0x18); DbgPrint("程序正在被 [%s] 检查!\n", (PCHAR)eps+0x174); if(eps==(PEPROCESS)CheckedProcess) { DbgPrint("程序正在被 [%s] 调试!\n", ImageFileName); return TRUE; } } } } break; default: DbgPrint("No Pass"); } } pCurEProcess=(PEPROCESS)(*(PULONG)((ULONG)pCurEProcess+0x88)-0x88); }while(pCurEProcess!=pEprocess); DbgPrint("Processing is not debug"); return FALSE; } VOID DriverUnload(PDRIVER_OBJECT pDriver) { DbgPrint("Driver unloaded.\n"); } NTSTATUS DriverEntry(PDRIVER_OBJECT pDriver, PUNICODE_STRING reg_path) { ULONG addr; pDriver->DriverUnload = DriverUnload; //获取需要保护的进程的EProcess addr=GetProcessEprocess("notepad.exe"); DbgPrint("程序正在被 [%d] 调试!\n", addr); CheckProcessDebug(addr); return STATUS_SUCCESS; }