-
-
[原创]# 内核异常拦截
-
发表于:
2025-8-14 09:28
1792
-
新人新帖 菜鸟一个
前言
某个游戏的反调试检测问题
在测试某款游戏时即使将异常直接交给游戏自身的异常处理逻辑,也不会导致崩溃;
然而,最近再帮好友另一款游戏在遇到类似异常时,如果未进行拦截、而直接崩溃。


来简单的实现针对性异常过滤与处理机制,以避免游戏因反调试检测而异常退出
处理拦截 KiDispatchException 是用户态异常处理链的核心分发函数,
对特定异常进行有条件拦截
只作用于被标记为调试对象的目标进程。
在反作弊场景(如 EAC、BE)中,当然还有魔兽 游戏往往会主动制造异常,经过Xdbg测试
基本都会抛出 :
EXCEPTION_ILLEGAL_INSTRUCTION (0xC000001D) CPU 遇到无法识别的指令(非法指令)。
EXCEPTION_POSSIBLE_DEADLOCK(0x10000002) 系列中的“伪异常码”,实际上是 MS 的调试特例码
EXCEPTION_ACCESS_VIOLATION 0xC0000005
和 DEP
OK 让AI 写一个 指令长度解析 的函数
static ULONG FastGetInstructionLength64(PVOID Address)
{
UCHAR* code = (UCHAR*)Address;
ULONG len = 0;
// 跳过常用前缀
BOOLEAN prefix = TRUE;
while (prefix)
{
switch (code[len])
{
case 0xF0: case 0xF2: case 0xF3:
case 0x2E: case 0x36: case 0x3E: case 0x26:
case 0x64: case 0x65: case 0x66: case 0x67:
len++;
break;
default:
prefix = FALSE;
break;
}
}
UCHAR opcode = code[len++];
if (opcode == 0x0F) opcode = (UCHAR)(opcode << 8 | code[len++]); // 两字节 opcode
// 常用简单指令直接返回长度
if (opcode == 0xCC || opcode == 0xF4 || opcode == 0x0F0B) return len;
if (opcode == 0xCD) return len + 1; // int imm8
// 快速跳过 ModR/M + SIB + disp + imm(粗略估计)
if ((opcode & 0xC0) == 0x00) len += 1;
return len;
}
#define MAX_DEBUG_EXC 64
static PVOID g_LastExcAddr[MAX_DEBUG_EXC] = { 0 };
static ULONG g_LastExcIndex = 0;
__forceinline BOOLEAN IsRepeatedException(PVOID addr)
{
for (ULONG i = 0; i < MAX_DEBUG_EXC; i++)
{
if (g_LastExcAddr[i] == addr)
return TRUE;
}
g_LastExcAddr[g_LastExcIndex++ % MAX_DEBUG_EXC] = addr;
return FALSE;
}去 hook 接管 KiDispatchException 大致函数实现流程就是:
__forceinline BOOLEAN IsDebugTarget()
{
return (PsGetCurrentProcessId() == g_DebugProcess.TargetPID);
}
LONG KiDispatchException(PEXCEPTION_RECORD ExceptionRecord, void* ExceptionFrame, PKTRAP_FRAME TrapFrame, KPROCESSOR_MODE PreviousMode,
BOOLEAN FirstChance)
{
if (PreviousMode != KernelMode && IsDebugTarget())
{
// 吃掉游戏异常
if (FirstChance &&
(ExceptionRecord->ExceptionCode == EXCEPTION_ILLEGAL_INSTRUCTION ||
ExceptionRecord->ExceptionCode == EXCEPTION_ACCESS_VIOLATION ||
ExceptionRecord->ExceptionCode == 0x10000002)) // 自定义异常
{
if (!IsRepeatedException((PVOID)TrapFrame->Rip))
{
ULONG instLen = FastGetInstructionLength64((PVOID)TrapFrame->Rip);
if (instLen == 0) instLen = 1;
TrapFrame->Rip += instLen;
}
return EXCEPTION_CONTINUE_EXECUTION;
}
if (FirstChance &&
ExceptionRecord->ExceptionCode == 0x10000004 &&
ExceptionRecord->NumberParameters == 2 &&
ExceptionRecord->ExceptionInformation[0] == 8)
{
return EXCEPTION_CONTINUE_SEARCH;
}
// 转换用户态 SegCs 异常码
USHORT segCs = 0;
__try { segCs = TrapFrame->SegCs; }
__except (EXCEPTION_EXECUTE_HANDLER) { segCs = 0; }
if ((segCs & 0xfff8) == KGDT64_R3_CMCODE)
{
switch (ExceptionRecord->ExceptionCode)
{
case STATUS_BREAKPOINT:
ExceptionRecord->ExceptionCode = STATUS_WX86_BREAKPOINT;
break;
case STATUS_SINGLE_STEP:
ExceptionRecord->ExceptionCode = STATUS_WX86_SINGLE_STEP;
break;
}
}
if (DbgkForwardException(ExceptionRecord, TRUE, FALSE))
{
BOOLEAN isInt2d = FALSE;
__try
{
if (*(PUSHORT)((ULONG64)(TrapFrame->Rip) - 3) == 0x2DCD)
isInt2d = TRUE;
}
__except (EXCEPTION_EXECUTE_HANDLER) { isInt2d = FALSE; }
if (!isInt2d)
return FALSE;
}
__try { segCs = TrapFrame->SegCs; }
__except (EXCEPTION_EXECUTE_HANDLER) { segCs = 0; }
if ((segCs & 0xfff8) == KGDT64_R3_CMCODE)
{
switch (ExceptionRecord->ExceptionCode)
{
case STATUS_WX86_BREAKPOINT:
ExceptionRecord->ExceptionCode = STATUS_BREAKPOINT;
break;
case STATUS_WX86_SINGLE_STEP:
ExceptionRecord->ExceptionCode = STATUS_SINGLE_STEP;
break;
}
}
}
// 调用原始 KiDispatchException
return fnKernel::old_KiDispatchException(
ExceptionRecord, ExceptionFrame, TrapFrame, PreviousMode, FirstChance);
}
经过测试 对ms 或者jd 正常下段 不再出现游戏崩溃问题 如代码出现问题 请大佬指教

传播安全知识、拓宽行业人脉——看雪讲师团队等你加入!