调试器检测
-
DebugPort清零 在没有被调试之前
+0x0bc DebugPort : (null) +0x248 Flags : 0xd0800 +0x248 CreateReported : 0y0 +0x248 NoDebugInherit : 0y0
被调试之后:
+0x0bc DebugPort : 0x89367b38 Void +0x248 Flags : 0xd0801 +0x248 CreateReported : 0y1 +0x248 NoDebugInherit : 0y0
-
调试信息查询
NtQuerySystemInformation(ProcessBasicInformation 和 ProcessDebugPort) 检测当前系统是否处于调试状态, 比如双机调试 SystemKernelDebuggerInformation->DebuggerEnabled = FALSE; (双机调试 ) SystemKernelDebuggerInformation->DebuggerNotPresent = FALSE; (双机调试 ) 检测kdcom.dll内核模块是否加载 (双机调试 ) dt _KUSER_SHARED_DATA结构成员s 检测 KdDebuggerEnabled 值是否为1, 这个值还会控制桌面是否显示测试模式 (双机调试 ) 检测 KdEnteredDebugger 值是否为1 (双机调试 ) 检测 KdDebuggerNotPresent 值是否为0,正常应该为1 KiDebugRoutine = KdpStub 则不可调试, 可调试的是kdpTrap (双机调试 ) 检测内核调试的其他变: 已开启内核调试:0 未开启内核调试:1dd KdpTimeSlipPending l1 已开启内核调试:1 未开启内核调试:0db KdpBootedNodebug l1 已开启内核调试:0 未开启内核调试:1dd KdDebuggerLockMaxWaitTime l1 已开启内核调试:有值 未开启内核调试:0dd KdDebuggerEnteredCount l1 已开启内核调试:有值 未开启内核调试:0db KdpContextSent l1 已开启内核调试:1 未开启内核调试:0db KdpBreakpointTable l4 下内核断点:有值 未下内核断点:0dq KdTimerStop l1 已开启内核调试:有值 未开启内核调试:0db KdpPathBuffer l4 已开启内核调试:有值 未开启内核调试:0dq KdTimerDifference l1 已开启内核调试:有值 未开启内核调试:0db KdpControlCPressed l1 挂起:1 恢复:0dd KdUmBreakMarker l1 已开启内核调试:有值 未开启内核调试:0dd KdEnteredDebugger l1 挂起:1 恢复:0dq KdDebugDevice l1 已开启内核调试:有值 未开启内核调试:0db KdPageDebuggerSection l1 已开启内核调试:0 未开启内核调试:1db KdpDebuggerStructuresInitialized l1 已开启内核调试:1 未开启内核调试:0dq KdTimerStart l1 已开启内核调试:有值 未开启内核调试:0db KdLogBuffer l4 已开启内核调试:有值 未开启内核调试:0db KdDebuggerEnabled l1 已开启内核调试:1 未开启内核调试:0dq KdDebuggerNotPresent l1 已开启内核调试:0 未开启内核调试:1db KdpContext l4 已开启内核调试:有值 未开启内核调试:0db KdBreakAfterSymbolLoad l1 已开启内核调试:1 未开启内核调试:0db KdpDataBlockEncoded l1 已开启内核调试:0 未开启内核调试:1dd KdpDebugRoutineSelect l1 已开启内核调试:1 未开启内核调试:0dq KdpTimeSlipTimer l1 已开启内核调试:有值 未开启内核调试:0db KdPortLocked l1 已开启内核调试:1 未开启内核调试:0db KdpMessageBuffer l4 已开启内核调试:有值 未开启内核调试:0dq KdDebuggerLock l1 NtQueryInformationProcess(ProcessBasicInformation 和 ProcessDebugPort) ProcessDebugPort ProcessDebugObjectHandle ProcessDebugFlags 检测父进程,进程被调试时父进程为调试进程
-
OnNtQueryObject()查询调试对象名
typedef enum _OBJECT_INFORMATION_CLASS{ ObjectBasicInformation, ObjectNameInformation, ObjectTypeInformation, ObjectAllInformation, ObjectDataInformation }OBJECT_INFORMATION_CLASS, *POBJECT_INFORMATION_CLASS;
这是一个对象的结构,我们只关心调试对象: ObjectAllInformation
第四个成员ObjectAllInformation,查询的是调试对象名:DebugObject,每当一个应用程序被调试的时候,将会为调试对话在内核中创建一个DebugObject类型的对象。程序可以检查DebugObject类型内核对象的数量来确定是否有调试器的存在。
-
NtSetInformationThread()修改线程标志,让其不可被调试, 某些加壳也会使用这个方式。 CrossThreadFlags标志位不能被修改,hook后让其可以被修改
-
hook NtDebugActiveProces附加到游戏进程
-
DebugPort偏移修改,hook DbgkpSetProcessDebugObject这个函数,将读取DebugPort偏移的值改掉
NTSTATUS DbgkpSetProcessDebugObject( IN PROCESS Process, // 这里其实就指进入的游戏进程对象 IN PDEBUG_OBJECT DebugObject, IN NTSTATUS MsgStatus, IN PETHREAD LastThread )
思路1: Hook这个函数将第一个参数设置为explorer.exe, 函数就会把游戏进程的DebugObject对象转移到Explorer->Process->DebugPort, 就算游戏保护对游戏进程的DebugPort清零,也不会影响我们调试 思路2: 处理NTCreateDebugObject, 采用Hook ObCreateObject函数判断上层调用者是否是NTCreateDebugObject, 如果是,则将参数里的DebugObject备份保存 思路3: 需要修改偏移的函数
KiDispatchException PspExitThread PspTerminateAllThreads PspProcessDelete PsIsProcessBeingDebugged PsGetProcessDebugPort NtQueryInformationProcess DbgkpSetProcessDebugObject DbgkClearProcessDebugObject DbgkpQueueMessage DbgkOpenProcessDebugPort DbgkCopyProcessDebugPort DbgkpCloseObjec DbgkpMarkProcessPeb DbgkForwardException DbgkCreateThread DbgkMapViewOfSection DbgkUnMapViewOfSection DbgkExitProcess(); DbgkExitThread();
-
ValidAccessMask清零 ValidAccessMask是在DebugObjectType的OBJECT_TYPE_INITIALIZER成员里
0: kd> dt _object_type 89a32298 nt!_OBJECT_TYPE +0x000 Mutex : _ERESOURCE +0x038 TypeList : _LIST_ENTRY [ 0x89a322d0 - 0x89a322d0 ] +0x040 Name : _UNICODE_STRING "DebugObject" +0x048 DefaultObject : (null) +0x04c Index : 8 +0x050 TotalNumberOfObjects : 0 +0x054 TotalNumberOfHandles : 0 +0x058 HighWaterNumberOfObjects : 0 +0x05c HighWaterNumberOfHandles : 0 +0x060 TypeInfo : _OBJECT_TYPE_INITIALIZER +0x0ac Key : 0x75626544 +0x0b0 ObjectLocks : [4] _ERESOURCE 0: kd> dt _OBJECT_TYPE_INITIALIZER 89a32298+0x60 nt!_OBJECT_TYPE_INITIALIZER +0x000 Length : 0x4c +0x002 UseDefaultObject : 0 '' +0x003 CaseInsensitive : 0 '' +0x004 InvalidAttributes : 0 +0x008 GenericMapping : _GENERIC_MAPPING +0x018 ValidAccessMask : 0x1f000f +0x01c SecurityRequired : 0x1 '' +0x01d MaintainHandleCount : 0 '' +0x01e MaintainTypeList : 0 '' +0x020 PoolType : 0 ( NonPagedPool ) +0x024 DefaultPagedPoolCharge : 0 +0x028 DefaultNonPagedPoolCharge : 0x30 +0x02c DumpProcedure : (null) +0x030 OpenProcedure : (null) +0x034 CloseProcedure : 0x80643b68 void nt!DbgkpCloseObject+0 +0x038 DeleteProcedure : 0x8060d98e void nt!FsRtlInitializeOplock+0 +0x03c ParseProcedure : (null) +0x040 SecurityProcedure : 0x805f9144 long nt!SeDefaultObjectMethod+0 +0x044 QueryNameProcedure : (null) +0x048 OkayToCloseProcedure : (null)
可以通过Hook ObReferenceObjectByHandle代替DebugObjectType, 让我们的调试器使用
-
检测 这里手动在windbg修改KUSER_SHARED_DATA 全局变量 的KdDebuggerEnabled dt _KUSER_SHARED_DATA 0xFFFFF780
00000000 修改的命令是eb 0xFFFFF780
00000000+0x2d4 0 -
hook kdcom!KdSendPacket 和 kdcom!KdReceivePacket
-
反硬件断点和自定义异常处理,在自定义函数中获取硬件断点信息来检测测试器: https://www.cnblogs.com/Sna1lGo/p/15358626.html
SetUnhandledExceptionFilter(MyUnhandledExceptionFilter); // SEH, VCH, UEH AddVectoredExceptionHandler(); // VEH AddVectoredContinueHandler(); // VCH GetThreadContext(); // 检测硬件断点 SetThreadContext(); // 硬件断点清零
-
ObRegisterCallbacks监控进程和线程的创建和退出、暂停、恢复等操作