突然看到了一种反调试的手段,检测api函数的首字节是否为0xcc,即int 3类型的断点,来反调试,尝试一下
#include<stdio.h>
#include<stdlib.h>
void fun(int a) {a++;a--;a += 5;a -= 5;return;
}
int main() {void (*ptr)(int) = NULL;ptr = fun;int i =0; char *arr=(char*)(ptr);for (i = 0; i < 99; i++) {printf("0x%2x\t", arr[i]);}return 0;
}
最开始的时候就遇到了一个问题,我用的virtual studio ,但是这是c++编译的,导致最后的函数指针不是指向的fun函数,这里可以很清楚的看到。
额,不太清楚编译的原理,那我用纯c,直接用devc++,里面有自带的gcc编译器
然后就和我预想的一样了,fun就是那个函数指针,在fun里多打一点断点(有个问题,为什么fun函数编译出来会有个nop,不是很懂)
执行结果上面就有预期的0xCC了,可见,ida确实是用的int 3断点
这种方法很简单,也很容易绕过,我们来看一下硬件断点,硬件断点基于DRx寄存器,换句话说,可以通过检测DRx寄存器来检测硬件断点
可以通过SetThreadContext直接设置指定线程的调试寄存器
DRx寄存器可以直接操作,但是需要设置特权级CPL等于0,这里采用比较简单的方法,直接用GetThreadContext获取当前线程的上下文
我一共下了3个硬件断点,执行结果如下
可见,确实用DRx寄存器来设置了硬件断点,而该程序正常执行情况(没有断点)如下
#include<stdio.h>
#include<stdlib.h>
#include<Windows.h>
const char *strr = (const char*)"hello touful";
int main()
{ printf("enter\n");//获取当前线程的句柄HANDLE hThread = GetCurrentThread();//创建一个CONTEXT结构体CONTEXT threadContext;threadContext.ContextFlags = CONTEXT_ALL;//获取当前线程的运行上下文if (!GetThreadContext(hThread, &threadContext)){printf("GetThreadContext failed\n");return 0;}printf("DRO = 0x%x\n", threadContext.Dr0);printf("DR1 = 0x%x\n", threadContext.Dr1);printf("DR2 = 0x%x\n", threadContext.Dr2);printf("DR3 = 0x%x\n", threadContext.Dr3);printf("exit\n");return 0;
}
这里顺便贴出CONTEXT结构体的定义
typedef struct DECLSPEC_NOINITALL _CONTEXT {//// The flags values within this flag control the contents of// a CONTEXT record.//// If the context record is used as an input parameter, then// for each portion of the context record controlled by a flag// whose value is set, it is assumed that that portion of the// context record contains valid context. If the context record// is being used to modify a threads context, then only that// portion of the threads context will be modified.//// If the context record is used as an IN OUT parameter to capture// the context of a thread, then only those portions of the thread's// context corresponding to set flags will be returned.//// The context record is never used as an OUT only parameter.//DWORD ContextFlags;//// This section is specified/returned if CONTEXT_DEBUG_REGISTERS is// set in ContextFlags. Note that CONTEXT_DEBUG_REGISTERS is NOT// included in CONTEXT_FULL.//DWORD Dr0;DWORD Dr1;DWORD Dr2;DWORD Dr3;DWORD Dr6;DWORD Dr7;//// This section is specified/returned if the// ContextFlags word contians the flag CONTEXT_FLOATING_POINT.//FLOATING_SAVE_AREA FloatSave;//// This section is specified/returned if the// ContextFlags word contians the flag CONTEXT_SEGMENTS.//DWORD SegGs;DWORD SegFs;DWORD SegEs;DWORD SegDs;//// This section is specified/returned if the// ContextFlags word contians the flag CONTEXT_INTEGER.//DWORD Edi;DWORD Esi;DWORD Ebx;DWORD Edx;DWORD Ecx;DWORD Eax;//// This section is specified/returned if the// ContextFlags word contians the flag CONTEXT_CONTROL.//DWORD Ebp;DWORD Eip;DWORD SegCs; // MUST BE SANITIZEDDWORD EFlags; // MUST BE SANITIZEDDWORD Esp;DWORD SegSs;//// This section is specified/returned if the ContextFlags word// contains the flag CONTEXT_EXTENDED_REGISTERS.// The format and contexts are processor specific//BYTE ExtendedRegisters[MAXIMUM_SUPPORTED_EXTENSION];} CONTEXT;
对于内存断点,是以页的方式添加的,设置页的权限位就行了,最常用的两种断点实现了最常用的检测方式