花指令与anti-debug

花指令

BR

  1. 介绍:在ARM架构下,通过控制对寄存器的赋值,BR到寄存器的值对应地址,来调控程序执行
  2. 主要:
    CMP             W0, #0
    /*                  */
    CSEL            W16, W9, W10, EQ
    /*
        W16与X8进行运算
    */
    BR              X8
    

anti-Debug

windows

  1. NtGlobalFlag

    • 32位;通过获取fs寄存器找到peb表(gs:[0x60])的位置;readgsdword(0x68)读取NtGlobal,默认值为0,如果被调试,则为0x70.
    • 32位;通过获取fs寄存器找到peb表(fs:[0x30])的位置;readfsdword(0x68)读取NtGlobal,默认值为0,如果被调试,则为0x70.
    • 识别:如果在32位上看到fs,在64位上看到gs就需要留意此反调试.
  2. BingDebugged

    • 64位:调试标志位(BeingDebugged flag)在PEB表(Process Environment Block)偏移0x2的位置;通过获取gs寄存器找到peb表(gs:[0x60])的位置;readgsqword(0x62)得到调试标志位,读取该值如果该值为1, 表示当前进程正在被调试;如果为0, 则表示当前进程没有被调试。
    • 32位:获取peb表(fs:[0x30])
    • 识别: fs与gs
    • 反反调试方法,因为他读取的1有固定位置,可以将那个位置改为1,一劳永逸.
  3. CheckRemoteDebuggerPresent

    • debugflag藏在了参数中

        CheckRemoteDebuggerPresent(GetCurrentProcess(),&ret)   
      
    • 被调试时位Ture,否则为False

  4. NtQueryInformationProcess

    • 第二个参数 ProcessInformationClass 给定了需要查询的进程信息类型。当给定值为 0
      (ProcessBasicInformation)或 7(ProcessDebugPort)时,就能得到相关调试信息,返回信息会写到第三个参数 ProcessInformation 指向的缓冲区中,也会return这个调试信息.
      img

    • 识别:

      img

  5. ZwSetInformationThread
    ZwSetInformationThread 等同于 NtSetInformationThread,通过为线程设置 ThreadHideFromDebugger,可以禁止线程产生调试事件,代码如下

     typedef DWORD(WINAPI* ZW_SET_INFORMATION_THREAD) (HANDLE, DWORD, PVOID, ULONG);
     VOID DisableDebugEvent(VOID)
     {
         HINSTANCE hModule;
         ZW_SET_INFORMATION_THREAD ZwSetInformationThread;
         hModule = GetModuleHandleA("Ntdll");
         ZwSetInformationThread = (ZW_SET_INFORMATION_THREAD)GetProcAddress(hModule, "ZwSetInformationThread");
         ZwSetInformationThread(GetCurrentThread(), 0x11, 0, 0);
     }
    
    • 当这个函数运行后,会直接让ida与程序失去连接,导致ida一直认为程序在运行,也无法关闭,并且下的断点会变成橙色
    • 解决方法: 直接将函数patch掉,但是要注意维持栈平衡(将push的内容也一起patch)
      img
    • patch位置:
      img
  6. 断点检测

    • 通过修改代码指令为 INT3(机器码为0xCC)触发软件异常
    • 如果程序检测在特定位置是否有0xcc的机械码,以此进行检测
  7. TLS回调函数

    • 在进程启动时执行,优先级高于EP
    • 需要注意的里面的反调试可能不是让程序退出,而是修改我们的key或者cipher
    • 类似的还有一个在windows中的先于main函数执行的位置,如图
      img
      在这两个地址中间可能会有函数的地址,那这个函数将会被执行.

linux

  1. ptrace反调试
    (1) ptrace系统调从名字上看是用于进程跟踪的,它提供了父进程可以观察和控制其子进程执行的能力,并允许父进程检查和替换子进程的内核镜像(包括寄存器)的值。其基本原理是: 当使用了ptrace跟踪后,所有发送给被跟踪的子进程的信号(除了SIGKILL),都会被转发给父进程,而子进程则会被阻塞,这时子进程的状态就会被系统标注为TASK_TRACED。而父进程收到信号后,就可以对停止下来的子进程进行检查和修改,然后让子进程继续运行。
    函数原型:

     \#include <sys/ptrace.h>
         long ptrace(enum __ptrace_request request, pid_t pid, void *addr, void *data);
    

    ptrace有四个参数:
    - enum __ptrace_request request:指示了ptrace要执行的命令。
    - pid_t pid: 指示ptrace要跟踪的进程。
    - void *addr: 指示要监控的内存地址。
    - void *data: 存放读取出的或者要写入的数据。
    返回值:
    - 成功时,ptrace()返回0,否则返回-1并设置errno。

    部分Ptrace request:

    • PTRACE_TRACEME
      表示本进程将被其父进程跟踪,交付给这个进程的所有信号(除SIGKILL之外),都将使其停止,父进程将通过wait()获知这一情况。
      它通常总是与 fork/exec 一起使用。对于每一个进程,PTRACE_TRACEME 只能被调用一次。

    • PTRACE_ATTACH
      根据pid将调试进程附加到被调试进程上,PTRACE_ATTACH向被调试进程发送SIGSTOP信号使之停下.
      但是在ptrace(PTRACE_ATTACH,pid,0,0)执行完毕时被调试进程可能还没有暂停,可以使用waitpid()等待其状态变化.

    • PTRACE_DETACH
      将被调试进程与调试进程分离,使被调试进程正常运行.

    • PTRACE_SYSCALL
      使被调试进程继续运行,但是在下一个系统调用的入口处或出口处停下,或者是执行完一条指令后停下.

    • PTRACE_GETREGS
      表示请求类型为“读取寄存器值”(Read General-Purpose Registers)。
      例如,调试进程可以监视被调试进程系统调用入口处的参数,接着再使用SYSCALL,监视系统调用的返回值.

    • PTRACE_PEEKTEXT
      系统调用的一个 请求类型(Request Type),用于 从被跟踪进程的内存中读取数据。

    • 部分request会涉及user_regs_struct结构体,可在下面找到该结构体 https://codebrowser.dev/glibc/glibc/sysdeps/unix/sysv/linux/x86/sys/user.h.html#user_regs_struct

      PTRACE_GETREGS

      PTRACE_SETREGS

      PTRACE_PEEKUSER

      PTRACE_POKEUSER

    (2) ptrace系统调用号为101,则syscall(101,0,0,0,0)表示ptrace(0,0,0,0)而当request参数为0时,表示PTRACE_TRACEME.相当于Ptrace自己的进程.然而一个程序只能被ptrace一次,所以当调试的时候,ptrace(0,0,0,0)会失败返回-1.
    依此便可以生成一个反调试的脚本.

    #include <sys/ptrace.h>
    #include <stdio.h>
    int main() {
        if(-1 == ptrace(PTRACE_TRACEME, 0, 0, 0))
        {   
            printf("debugger!\n");
            return -1;
        }
        printf("not debugger!\n");
        return 0;
    }
    

    example:[sctf SGAME]

    img

    识别:strace ptrace

  2. 检测 /proc/self/status

    • 检测是否含有TracerPid: xxxxxx\n字段,如果有,则被调试
    • 如果未被调试则该段为TracerPid: 0\n,有可能以此进行加密作为程序加密的key
    • 演示:gdb angr_find再start后,进行检测,发现TracePid是gdb的pid
      img
    • 所以就可以用strace来检测是否cat了/proc/self/status,进行判断存在debug.
  3. 时间反调试

     int main(int argc, char const *argv[]) {
         time_t before = time(0);
         // 插入一堆算法
         time_t after = time(0);
         if(after - before > 1) {
         exit(-1);
         }
         return 0;
     }
    
posted @ 2024-10-11 20:29  Un1corn  阅读(91)  评论(0)    收藏  举报