459121520 发表于 2022-11-20 23:03

汇编级别 反调试(一)

本帖最后由 459121520 于 2022-11-20 23:59 编辑

一、INT 3指令INT3是用作软件断点的中断。在没有调试器的情况下,在到达INT3指令后,将生成异常EXCEPTION_BREAKPOINT (0x80000003),并将调用异常处理程序。如果存在调试器,则不会将控制权赋予异常处理程序。bool IsDebugged(){    __try    {      __asm int 3;      return true;    }    __except(EXCEPTION_EXECUTE_HANDLER)    {      return false;    }}
除了 INT 3指令的简短形式0xCC之外,该指令还有一种长形式:CD 03。当异常EXCEPTION_BREAKPOINT 发生时,Windows将EIP寄存器递减到0xCC操作码的假定位置,并将控制权传递给异常处理程序。对于INT3指令的长形式,EIP将指向指令的中间(0x03偏移字节处)。因此,如果我们想在INT3指令之后继续执行,就应该在异常处理程序中编辑EIP(否则我们很可能会得到EXCEPTION_ACCESS_VIOLATION 异常)。如果没有,我们可以忽略指令指针的修改。bool g_bDebugged = false;int filter(unsigned int code, struct _EXCEPTION_POINTERS *ep){    g_bDebugged = code != EXCEPTION_BREAKPOINT;    return EXCEPTION_EXECUTE_HANDLER;}bool IsDebugged(){    __try    {      __asm __emit(0xCD);      __asm __emit(0x03);    }    __except (filter(GetExceptionCode(), GetExceptionInformation()))    {      return g_bDebugged;    }}

二、 INT 2D跟INT 3一样,该指令也会触发EXCEPTION_BREAKPOINT ,但对于INT2D,Windows使用EIP寄存器作为异常地址,然后递增EIP寄存器值。在执行INT2D时,Windows还检查EAX寄存器的值。如果为1、3或4,或在Vista+上为5,则异常地址将增加1。 对于一些调试器可能会崩溃,当EIP用作异常处理地址之后,INT 2D指令后的代码将会被跳过,可能跳入到一些非法指令处。 在本例中,我们将一个字节的NOP指令放在INT2D之后,以便在任何情况下跳过它都不影响。如果程序在没有调试器的情况下执行,则控制权将传递给异常处理程序。bool IsDebugged(){    __try    {      __asm xor eax, eax;      __asm int 0x2d;      __asm nop;      return true;    }    __except(EXCEPTION_EXECUTE_HANDLER)    {      return false;    }}

三、 DebugBreak如DebugBreak文档中所述,“DebugBreak导致当前进程中发生断点异常。这允许调用线程向调试器发出信号以处理异常”。 如果程序在没有调试器的情况下执行,则控制权将传递给异常处理程序。否则,调试器将拦截执行。bool IsDebugged(){    __try    {      DebugBreak();    }    __except(EXCEPTION_BREAKPOINT)    {      return false;    }      return true;}

四、ICE“ICE”是英特尔的一条未经证明的指令。它的操作码是0xF1????。它可以用来检测程序是否被跟踪。如果执行ICE指令,将引发EXCEPTION_SINGLE_STEP(0x8000004)异常。 但是,如果程序已经被跟踪调试,调试器会将此异常视为通过在Flags寄存器中设置SingleStep位来执行指令而生成的正常异常。因此,在调试器下,不会调用异常处理程序,ICE指令后将继续执行。bool IsDebugged(){    __try    {      __asm __emit 0xF1;      return true;    }    __except(EXCEPTION_EXECUTE_HANDLER)    {      return false;    }}

五、堆栈寄存器跟踪以下汇编指令序列:push ss pop ss pushf
如果在调试器中单步经过此代码,????Trap标志位 将会被设置,正常情况下,调试器在每一个调试事件传送完后清理 TRAP 标志,并且不可见。但是只要事前保存EFLAGS到堆栈,就可以检查 Tarp 是否更改。bool IsDebugged(){    bool bTraced = false;    __asm    {      push ss      pop ss      pushf      test byte ptr , 1      jz movss_not_being_debugged    }    bTraced = true;movss_not_being_debugged:    __asm popf;    return bTraced;}


六、指令计数滥用调试器EXCEPTION_SINGLE_STEP异常处理方式这个技巧的思想是按照预定义的顺序为每条指令设置硬件断点。执行带有硬件断点的指令会引发EXCEPTION_SINGLE_STEP异常,并被异常处理链捕获。在异常处理程序中,我们设置一个寄存器,该寄存器充当指令计数器(在本例中为EAX)和指令指针EIP的角色,以将控制权传递给序列中的下一条指令。因此,每次将控制权传递到序列中的下一条指令时,????都会引发异常并递增计数器。当指定的序列执行完后,比较指令数和计数器,不一样就代表有调试器存在:代码片段
七、POPFD 和 Trap 标志设置 Trap 标志时,将引发异常 SINGLE_STEP。但是,如果我们跟踪调试代码,Trap 标志将被调试器清除,因此我们不会看到异常。bool IsDebugged(){    __try    {      __asm      {            pushfd            mov dword ptr , 0x100            popfd            nop      }      return true;    }    __except(GetExceptionCode() == EXCEPTION_SINGLE_STEP      ? EXCEPTION_EXECUTE_HANDLER      : EXCEPTION_CONTINUE_EXECUTION)    {      return false;    }}

八、 指令前缀(OllyDbg v1.10)Instruction Prefixes在"PUSHFD",前面加上REP指令前缀,od识别不了。 在下面的例子中,如果在od调试器,当步入到 0xF3 时,我们会直接跳到 try 的末尾。调试器直接跳过前缀,并把控制权交给 INT1 指令。bool IsDebugged(){    __try    {      // 0xF3 0x64PREFIX REP:      __asm __emit 0xF3      __asm __emit 0x64      // One byte INT 1      __asm __emit 0xF1      return true;    }    __except(EXCEPTION_EXECUTE_HANDLER)    {      return false;    }}




WolfKing 发表于 2022-11-21 09:30

来抢个沙发坐坐没问题吧

459121520 发表于 2022-11-21 11:10

就叫东子吧 发表于 2022-11-21 09:30
来抢个沙发坐坐没问题吧

非常谢谢你的支持!{:5_117:}

Cerolluo 发表于 2022-11-21 20:06

谢谢楼主分享,学习学习!

459121520 发表于 2022-11-21 20:21

Cerolluo 发表于 2022-11-21 20:06
谢谢楼主分享,学习学习!

感谢支持

Wayne 发表于 2022-11-22 08:17

五十七 发表于 2022-11-22 11:28

别管我了行 发表于 2022-11-23 17:28

bnjzzheng 发表于 2022-11-28 16:21

感谢分享,感谢分享

创客者V2.0 发表于 2022-12-15 22:31

谢谢分享碰到好多都是int凌空
页: [1] 2
查看完整版本: 汇编级别 反调试(一)