反调试与反反调试

反调试与反反调试

什么是反调试? 什么是反反调试?

静态反调试

  • 特点:一般在调试开始时阻拦调试者,调试只需要找到原因后就可以一次性突破

  • PEB

    • BeginDebug : 调试标志位
    // 通过检查 PEB.BeingDebuged 字段判断是否被调试
    // 可以在目标程序运行之前修改对应的字段为 0 进行反反调试
    bool CheckBeingDebugged()
    {
      __asm 
      {
          ; 获取到 PEB 的内容
          mov eax, fs:[0x30]
          ; 获取 PEB 内偏移为 2 大小为 1 字节的字段
          movzx eax, byte ptr[eax + 2]
      }
    }
    
    
    int main()
    {
      if (CheckBeingDebugged())
          printf("当前处于[被]调试状态\n");
      else
          printf("当前处于[非]调试状态\n");
    
      system("pause");
      return 0;
    }
    // 使用 IsDebuggerPresent 原理同样是判断 PEB.BeingDebuged
    // 通过 HOOK 函数和修改对应字段的方式可以进行反调试
    
    int main()
    {
      if (IsDebuggerPresent())
          printf("当前处于[被]调试状态\n");
      else
          printf("当前处于[非]调试状态\n");
    
      system("pause");
      return 0;
    }
    • Ldr 内存状态
    • Heap (Flags, Force Flags): 堆状态
    • NtGlobalFlag : 内核全局标记
      • 通过PEB.NtGlobalFlag 判断是否被调试,当处于被调试状态时PEB.NtGlobalFlag保存的是 0x70,可以通过修改标志反反调试
    bool CheckNtGlobalFlag()
    {
      __asm
      {
          ; 获取到 PEB, 保存在 FS :[0x30]
          mov eax, fs : [0x30]
          ;获取到 NtGlobalFlag 字段的内容
          mov eax, [eax + 0x68]
      }
    }
    int main()
    {
      if (CheckNtGlobalFlag())
          printf("当前处于[被]调试状态\n");
      else
          printf("当前处于[非]调试状态\n");
    
      system("pause");
      return 0;
    }
  • TEB

    • StaticUnicodeString :静态缓冲区
  • 使用原始 API

    • NtQuerySystemInformationProcess()
      • ProcessDebugPort(0x07): 获取调试端口
      • ProcessDebugObjectHandle(0x1E): 获取调试句柄
      • ProcessDebugFlag(0x1F): 获取调试标记
    • NtQuerySystemInformation():
      • SystemKernelDeBuggerInformation(0x23) :获取系统调试状态 (双机
    • NtQueryObject() : 遍历系统内核
  • 攻击调试器

    • NtSetInformationThread()
      • ThreadHideFormDebugger(0x11)
  • 打开进程检查

    • SetDebugPrivilege ➡️ 检查进程是否具有调试权限
  • 利用TLS回调函数

  • 使用普通API

    • 父进程的检查
      • 如果一个进程被正常打开,那么它的父进程应该是 Explorer.exe,也就是资源管理器,如果判断不是,就可能被调试,提供参考。
    bool CheckParentProcess()
    {
    
      struct PROCESS_BASIC_INFORMATION {
          ULONG ExitStatus;           // 进程返回码
          PPEB  PebBaseAddress;       // PEB 地址
          ULONG AffinityMask;         // CPU 亲和性掩码
          LONG  BasePriority;         // 基本优先级
          ULONG UniqueProcessId;      // 本进程PID
          ULONG InheritedFromUniqueProcessId; // 父进程PID
      }stcProcInfo;
    
      // 查询到目标进程的对应信息,主要是 父进程 ID
      NtQueryInformationProcess(
          GetCurrentProcess(), 
          ProcessBasicInformation, 
          &stcProcInfo,
          sizeof(stcProcInfo), 
          NULL);
    
      // 查询资源管理器对应的 PID
      DWORD ExplorerPID = 0;
      DWORD CurrentPID = stcProcInfo.InheritedFromUniqueProcessId;
      GetWindowThreadProcessId(FindWindow(L"Progman", NULL), &ExplorerPID);
    
      // 比对两个 PID 的值,相同就OK,不同就可能被调试
      return ExplorerPID == CurrentPID ? false : true;
    }
    
    
    int main()
    {
      if (CheckParentProcess())
          printf("当前处于[被]调试状态\n");
      else
          printf("当前处于[非]调试状态\n");
    
      system("pause");
      return 0;
    }
    • 窗口名检查
      • 原理是通过查找窗口类或窗口名称对应的窗口是否存在来进行调试器或其它分析工具的检查。[缺点是窗口的的名字通常不是固定的,检查起来非常的不方便]
      • 还可以通过遍历进程的方式来检查调试器或其它工具是否存在。[缺点是可以通过修改 exe 的名字修改进程名]
    int main()
    {
      if (FindWindow(NULL, L"OllyDbg"))
          printf("存在调试器\n");
      else
          printf("没检测到调试器\n");
    
      return 0;
    }
    • 进程名检查
      • 原理就是查询 EPROCESS 结构体中相关的字段,因为不能在 R3 修改 R0 的数据,所以只能 HOOK
    bool CheckProcessDebugPort() 
    {
      int nDebugPort = 0;
      NtQueryInformationProcess(
          GetCurrentProcess(),    // 目标进程句柄
          ProcessDebugPort,       // 查询信息类型(7)
          &nDebugPort,            // 输出查询信息
          sizeof(nDebugPort),     // 查询类型大小
          NULL);                  // 实际返回数据大小
    
      // 如果返回的是 -1 ,那么就被调试了
      return nDebugPort == 0xFFFFFFFF ? true : false;
    }
    
    
    int main()
    {
      if (CheckProcessDebugPort())
          printf("当前处于[被]调试状态\n");
      else
          printf("当前处于[非]调试状态\n");
    
      system("pause");
      return 0;
    }
    • 文件名及文件路径检查
    • 注册表检查
    • 。。。

动态反调试

  • 特点:一般在调试的过程中阻拦调试者,可在调试的过程中被频繁触发因此需要调试者随时关注
  • 使用SEH
    • 异常
    • 断点
    • SetUnhandleedExceptionFilter()
  • 时间检查
  • 单步检查
  • 补丁检查
  • 反反汇编
  • 偷取代码
  • 分页保护
  • 虚拟机

OllyDbg插件编写

  1. 插件通常是以什么样的方式存在的?

通常以DLL的方式存在的,但是后缀名可能会有变化

  1. 应用程序如何找到所有的插件?

所有的插件都应该被保护到具体的路径中

  1. 应用程序怎样知道是否符合自己的要求?
  • 插件必须提供指定的名称的函数说明自己的信息,插件还需要通过对应名称的函数提供具体的功能

猜你喜欢

转载自www.cnblogs.com/TJTO/p/11374003.html