要注意的是,如果设置了/ INCREMENTAL:NO链接器选项,那么在获取函数地址来计算校验和的情况下,将会得到相对跳转地址:
- DebuggeeFunction:
- 013C16DB jmp DebuggeeFunction (013C4950h)
g_origCrc全局变量包含已由CalcFuncCrc函数计算的crc。为了终止检测函数,我们使用了存根函数的技巧。按着函数代码顺序排列,DebuggeeFunction函数的末尾是DebuggeeFunctionEnd函数的开头。我们还使用#pragma auto_inline(off)指令来防止编译器的嵌入函数。
如何避开软件断点检查
由于目前还没有一个通用的方法,所以为了避开此保护,应该找到代码计算校验和并用常量替换返回的值,以及存储函数校验和的所有变量的值。
硬件断点
在Windows x86架构中,开发人员在检查和调试代码时使用了一组调试寄存器。这些寄存器允许在访问内存读取或写入时中断程序执行并将控制传输到调试器。调试寄存器是一种特权资源,只能在具有特权级别CPL = 0的实模式或安全模式下由程序使用。8字节的调试寄存器DR0-DR7有:
1.DR0-DR3 -断点寄存器
2.DR4,DR5 -储藏
3.DR6 -调试状态
4.DR7 – 调试控制
DR0-DR3包含断点的线性地址,这些地址的比较是在物理地址转换之前进行的。这些断点中的每一个都会在DR7寄存器中被单独描述。 DR6寄存器会指示哪个断点被激活。 DR7通过访问模式定义断点激活模式:读,写,执行。以下是硬件断点检查的示例:
- CONTEXT ctx = {};
- ctx.ContextFlags = CONTEXT_DEBUG_REGISTERS;
- if (GetThreadContext(GetCurrentThread(), &ctx))
- {
- if (ctx.Dr0 != 0 || ctx.Dr1 != 0 || ctx.Dr2 != 0 || ctx.Dr3 != 0)
- {
- std::cout << "Stop debugging program!" << std::endl;
- exit(-1);
- }
- }
也可以通过SetThreadContext函数重置硬件断点,以下是硬件断点重置示例:
- CONTEXT ctx = {};
- ctx.ContextFlags = CONTEXT_DEBUG_REGISTERS;
- SetThreadContext(GetCurrentThread(), &ctx);
我们可以看到,所有DRx寄存器都设置为了0。
如何避开硬件断点检查和重置
如果我们看看GetThreadContext函数,我们会明白它调用了NtGetContextThread函数:
- 0:000> u KERNELBASE!GetThreadContext L6
- KERNELBASE!GetThreadContext:
- 7538d580 8bff mov edi,edi
- 7538d582 55 push ebp
- 7538d583 8bec mov ebp,esp
- 7538d585 ff750c push dword ptr [ebp+0Ch]
- 7538d588 ff7508 push dword ptr [ebp+8]
- 7538d58b ff1504683975 call dword ptr [KERNELBASE!_imp__NtGetContextThread (75396804)]
如果反调试保护在Dr0-Dr7中接收到零值,应该在CONTEXT结构的ContextFlags字段中重置CONTEXT_DEBUG_REGISTERS标识,然后在原始的NtGetContextThread函数调用后恢复其值。对于GetThreadContext函数,它在内部调用NtSetContextThread。以下就是避开硬件断点的检查和重置:
- typedef NTSTATUS(NTAPI *pfnNtGetContextThread)(
- _In_ HANDLE ThreadHandle,
- _Out_ PCONTEXT pContext
- );
- typedef NTSTATUS(NTAPI *pfnNtSetContextThread)(
- _In_ HANDLE ThreadHandle,
- _In_ PCONTEXT pContext
- );
- pfnNtGetContextThread g_origNtGetContextThread = NULL;
- pfnNtSetContextThread g_origNtSetContextThread = NULL;
- NTSTATUS NTAPI HookNtGetContextThread(
- _In_ HANDLE ThreadHandle,
- _Out_ PCONTEXT pContext)
- {
- DWORD backupContextFlags = pContext->ContextFlags;
- pContext->ContextFlags &= ~CONTEXT_DEBUG_REGISTERS;
- NTSTATUS status = g_origNtGetContextThread(ThreadHandle, pContext);
- pContext->ContextFlags = backupContextFlags;
- return status;
- }
- NTSTATUS NTAPI HookNtSetContextThread(
- _In_ HANDLE ThreadHandle,
- _In_ PCONTEXT pContext)
- {
- DWORD backupContextFlags = pContext->ContextFlags;
- pContext->ContextFlags &= ~CONTEXT_DEBUG_REGISTERS;
- NTSTATUS status = g_origNtSetContextThread(ThreadHandle, pContext);
- pContext->ContextFlags = backupContextFlags;
- return status;
- }
- void HookThreadContext()
- {
- HMODULE hNtDll = LoadLibrary(TEXT("ntdll.dll"));
- g_origNtGetContextThread = (pfnNtGetContextThread)GetProcAddress(hNtDll, "NtGetContextThread");
- g_origNtSetContextThread = (pfnNtSetContextThread)GetProcAddress(hNtDll, "NtSetContextThread");
- Mhook_SetHook((PVOID*)&g_origNtGetContextThread, HookNtGetContextThread);
- Mhook_SetHook((PVOID*)&g_origNtSetContextThread, HookNtSetContextThread);
- }
SEH(结构化异常处理) (编辑:晋中站长网)
【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!
|