常见反调试技术

反调试技术

一. 使用Windows API函数

1. IsDebuggerPresent函数

2.CheckRemoteDebuggerPresent函数

3.OutputDebuggerPresent函数

二. 手动检测数据结构

1. 检测BeingDebugged属性

BeingDebugged属性位于PEB结构的第二个字节位置处

mov eax, dword ptr fs:[30h];//fs:[30h]指向的是PEB的基地址
mov ebx, byte ptr [eax+2];// 指向对应的BeingDebugged属性
test ebx, ebx;
jz ... ;

2. 检测系统痕迹

遍历当前运行进程,查看是否存在调试器进程,或者使用FinWindow函数查看。

三. 识别调试器行为

1. 扫描是否存在INT3断点

INT3断点的机器码是0xcc

call $+5;
pop edi;
sub edi, 5;
mov ecx, 400h;
mov eax, 0cch;
repne scasb;
jz DebuggerPresent;

2. 时钟检测

通过对比两次运行的时间戳来判断是否存在调试器,因为如果存在调试器,运行速度会大幅度的降低。
使用rdtsc指令实现

rdtsc;
xor ecx, ecx;
add ecx, eax;//第一次运行时间
rdtsc;
sub eax, ecx;//计算两次运行时间的差值
cmp eax, 0xfff;//比较是否运行时间过长
jb NoDebuggerDetected;

硬件断点检测

硬件断点的核心是使用8个调试寄存器实现,所以关注的重点也就落在了调试寄存器上

1.1 DR0-DR3

DR0到DR3被称为“调试地址寄存器”或“地址断点寄存器”,它们非常简单,其中仅包含断点的线性地址。当该地址与指令或数据引用匹配时,将发生中断。调试寄存器DR7可用于对每个断点的条件进行更细粒度的控制。因为寄存器需要填充线性地址,所以即使关闭分页,它们也可以正常工作。在这种情况下,线性地址将与物理地址相同。

由于这些寄存器中只有4个是可用的,因此每个线程最多只能同时具有4个断点。

1.2 DR4-DR5

DR4和DR5被称为“保留的调试寄存器”。尽管它们的名称中有“保留”字样,但实际上却不总是保留的,仍然可以使用。它们的功能取决于控制寄存器CR4中DE字段的值。在启用此位后,将启用I/O断点,如果尝试访问其中一个寄存器将会导致#UD异常。但是,如果未启用DE位时,调试寄存器DR4和DR5分别映射到DR6和DR7.这样做的目的是为了与旧版本处理器的软件相兼容。

1.3 DR6

在触发硬件断点时,调试状态存储在调试寄存器DR6中。也正因如此,该寄存器被称为“调试状态寄存器”。其中包含用于快速检查某些事件是否被触发的位。
第0-3位是根据触发的硬件断点而进行设置,可以用于快速检查触发了哪个断点。

第13位称为BD,如果由于访问调试寄存器而触发当前异常,则会将其置为1。必须在DR7中启用GD位,才能触发此类异常。

第14位称为BS,如果由于单个步骤而触发当前异常,则会设置这一位。必须在EFLAGS寄存器中启用TF标志,才能触发此类异常。

第15位称为TS,如果由于当前任务切换到了启用调试陷阱标志的任务而触发了当前异常,则会设置这一位。

1.4 DR7

DR7被称为“调试控制寄存器”,允许对每个硬件断点进行精细控制。其中,前8位控制是否启用了特定的硬件断点。偶数位(0、2、4、6)称为L0-L3,在本地启用了断点,这意味着仅在当前任务中检测到断点异常时才会触发。奇数位(1、3、5、7)称为G0-G3,在全局启用了断点,这意味着在任何任务中检测到断点异常时都会触发。如果在本地启用了断点,则在发生硬件任务切换时会删除相应的位,以避免新任务中出现不必要的断点。在全局启用时不会清除这些位。
第8位和第9位分别称为LEGE,是沿用的传统功能,在现代处理器上无法执行任何操作。这些位用于指示处理器检测断点发生的确切指令。在现代处理器上,所有断点条件都是精确的。为了与旧硬件兼容,建议始终将这两个位都设置为1。

第13位被称为GD,这一位非常值得关注。如果这一位被启用,则当每一条指令尝试访问调试寄存器时,都会生成调试异常。为了将这种类型的异常与普通的硬件断点异常区分开来,在调试寄存器DR6中设置了BD标志。这一位通常用于阻止程序干扰调试寄存器。关键点在于,异常发生在指令执行之前,并且当进入调试异常处理程序时,该标志会被处理器自动删除。但是,这样的解决方案并不完美,因为它只能使用MOV指令来访问调试寄存器。这些在用户模式下是不可访问的,并且根据我的测试,GetThreadContextSetThreadContext函数不会触发该事件。这样一来,这种检测就无法在用户模式下使用。

第16-31位用于控制每个硬件断点的条件和大小。每个寄存器有4位,分为4个2位字段。前2位用于确定硬件断点的类型。仅能在指令执行、数据写入、I/O读写、数据读写时才能生成调试异常。仅有在启用了控制寄存器CR4的DE字段时,才启用I/O读写功能,否则这种情况是不确定的。大小可以使用后2位来控制,并用于指定特定地址处内存位置的大小。可用的大小有1字节、2字节、4字节和8字节。

Windows使用SetThreadContextGetThreadContextAPI以支持对这些寄存器的更改

BOOL SetThreadContext(
  HANDLE hThread,   // 指向线程句柄的指针
  CONTEXT *pContext// 指向线程上下文的指针
);
//设置线程的上下文信息,例如寄存器值、堆栈指针、标志位等
BOOL GetThreadContext(
  HANDLE hThread,   // 指向线程句柄的指针
  CONTEXT *pContext// 指向一个CONTEXT结构体的指针,该结构体用于存储获取到的上下文信息
);
//获取线程的上下文信息
//具体例子
int main()
{
  // 获取当前线程的句柄
  HANDLE hThread = GetCurrentThread();

  // 定义一个CONTEXT结构体,用于存储要设置的上下文信息
  CONTEXT context;
  memset(&context, 0, sizeof(CONTEXT));

  // 备份当前线程的上下文信息
  CONTEXT backupContext;
  if (GetThreadContext(hThread, &backupContext))
  {
// 设置新的上下文信息
context.ContextFlags = backupContext.ContextFlags;
context.Eip = backupContext.Eip + 5;
context.Esp = backupContext.Esp + 10;
context.Ebp = backupContext.Ebp + 20;
context.SegCs = backupContext.SegCs + 30;
}
  // 设置线程的上下文信息
  if (SetThreadContext(hThread, &context))
  {
std::cout << "线程上下文信息已修改。" << std::endl;
  }
  else
  {
std::cout << "线程上下文信息修改失败。" << std::endl;
  }

处理方式:

  1. GetThreadContext函数:
int sub_4015E0()
{
  HANDLE CurrentThread; // esi
  CONTEXT Context; // [esp+4h] [ebp-2D0h] BYREF

  memset(&Context.Dr0, 0, 0x2C8u);
  Context.ContextFlags = 65599;
  CurrentThread = GetCurrentThread();
  if ( !GetThreadContext(CurrentThread, &Context) || !Context.Dr7 )
return 0;
  Context.Dr7 = 0;
  SetThreadContext(CurrentThread, &Context);
  Context.ContextFlags = 65599;
  if ( GetThreadContext(CurrentThread, &Context) )
  {
if ( Context.Dr7 )
  ExitProcess(0xFFFFFF9D);
  }
  return 1;
}
//这里是通过判断DR7寄存器来进行分析判断的,倘若存在硬件断点则修改,如果无法修改则退出程序,可以使用patch解决该问题。
  1. 检查DR6寄存器:

在触发硬件断点时,DR6会填充有关事件的信息,这可以用来对当前情况进行更明确的决定。而重点就在于,在处理完硬件断点后,不会自动清除DR6。在Intel SDM中对这一部分进行了更详细的描述:

某些调试异常可能会清除第0-3位。处理器永远不会清除DR6寄存器的其余内容。为避免在识别调试异常时产生混淆,调试处理程序应该在返回中断的任务之前清除寄存器(除了应该设置的第16位之外)。

如果我们确定某个程序没有使用硬件断点,那么就可以使用前面提到的任何一种技术来检查DR6值,因为攻击者可能没有清除寄存器。

方法很好,但是没有实操,不太清楚具体情况

posted @ 2023-08-20 22:03  ONE_ZJ  阅读(75)  评论(0编辑  收藏  举报