突然看到了一种反调试的手段,检测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 SANITIZED
DWORD EFlags; // MUST BE SANITIZED
DWORD 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;
对于内存断点,是以页的方式添加的,设置页的权限位就行了,最常用的两种断点实现了最常用的检测方式