设为首页收藏本站
查看: 3143|回复: 0

[原创] [翻译]反调试:汇编指令

[复制链接]
  • TA的每日心情
    开心
    2022-1-9 18:41
  • 签到天数: 5 天

    [LV.2]偶尔看看I

    发表于 2021-9-12 15:33:29 | 显示全部楼层 |阅读模式
    备注
    原文地址:https://anti-debug.checkpoint.com/techniques/assembly.html
    原文标题:Anti-Debug: Assembly instructions
    更新日期:2021年6月23日
    此文后期:根据自身所学进行内容扩充
    因自身技术有限,只能尽自身所能翻译国外技术文章,供大家学习,若有不当或可完善的地方,希望可以指出,用于共同完善这篇文章。




    目录
    • 汇编指令
    • 1. INT 3
    • 2. INT 2D
    • 3. ICE
    • 4.堆栈段寄存器
    • 5.指令计数
    • 6.POPF和Trap标志寄存器
    • 7.指令前缀
    • 反制措施


    汇编指令
    以下技术旨在根据调试器在CPU执行特定指令时的行为来检测调试器的存在。

    1. INT 3
    指令INT3是一个中断,被用作软件断点。在没有调试器存在的情况下,在到达INT3指令后,会产生异常EXCEPTION_BREAKPOINT(0x80000003),并且会调用一个异常处理程序。如果调试器存在,控制权就不会被交给异常处理程序。
    C/C++ 代码:
    1. bool IsDebugged()
    2. {
    3.     __try
    4.     {
    5.         __asm int 3;
    6.         return true;
    7.     }
    8.     __except(EXCEPTION_EXECUTE_HANDLER)
    9.     {
    10.         return false;
    11.     }
    12. }
    复制代码

    除了INT3指令的短形式(0xCC操作码),还有一个长形式的指令。CD 03操作码。

    当异常EXCEPTION_BREAKPOINT发生时,Windows将EIP寄存器递减到0xCC操作码的假定位置,并将控制传递给异常处理程序。在INT3指令的长形式的情况下,EIP将指向指令的中间(即指向0x03字节)。因此,如果我们想在INT3指令之后继续执行,应该在异常处理程序中编辑EIP(否则我们很可能得到一个EXCEPTION_ACCESS_VIOLATION异常)。如果没有,我们可以忽略指令指针的修改。
    C/C++ 代码:
    1. bool g_bDebugged = false;

    2. int filter(unsigned int code, struct _EXCEPTION_POINTERS *ep)
    3. {
    4.     g_bDebugged = code != EXCEPTION_BREAKPOINT;
    5.     return EXCEPTION_EXECUTE_HANDLER;
    6. }

    7. bool IsDebugged()
    8. {
    9.     __try
    10.     {
    11.         __asm __emit(0xCD);
    12.         __asm __emit(0x03);
    13.     }
    14.     __except (filter(GetExceptionCode(), GetExceptionInformation()))
    15.     {
    16.         return g_bDebugged;
    17.     }
    18. }
    复制代码

    2. INT 2D
    就像在INT3指令的情况下,当指令INT2D被执行时,异常EXCEPTION_BREAKPOINT也被提出。但是对于INT2D,Windows使用EIP寄存器作为异常地址,然后增加EIP寄存器的值。在执行INT2D的时候,Windows还检查了EAX寄存器的值。如果它在所有版本的Windows中是1、3或4,或者在Vista+中是5,那么异常地址将被增加1。

    这条指令可能会给一些调试器带来问题,因为在EIP入选后,INT2D指令后面的字节将被跳过,执行可能会从损坏的指令继续进行。

    在这个例子中,我们在INT2D后面放了一个字节的NOP指令,以便在任何情况下跳过它。如果程序在没有调试器的情况下被执行,控制将被传递给异常处理程序。
    C/C++ 代码:
    1. bool IsDebugged()
    2. {
    3.     __try
    4.     {
    5.         __asm xor eax, eax;
    6.         __asm int 0x2d;
    7.         __asm nop;
    8.         return true;
    9.     }
    10.     __except(EXCEPTION_EXECUTE_HANDLER)
    11.     {
    12.         return false;
    13.     }
    14. }
    复制代码

    3. ICE
    "ICE "是英特尔的一个未记录的指令。它的操作码是0xF1。它可以用来检测程序是否被跟踪。

    如果ICE指令被执行,EXCEPTION_SINGLE_STEP(0x80000004)异常将被引发。

    但是,如果程序已经被跟踪,调试器会认为这个异常是执行指令时在Flags寄存器中设置了SingleStep位产生的正常异常。因此,在调试器下,异常处理程序不会被调用,在ICE指令后继续执行。
    C/C++ 代码:
    1. bool IsDebugged()
    2. {
    3.     __try
    4.     {
    5.         __asm __emit 0xF1;
    6.         return true;
    7.     }
    8.     __except(EXCEPTION_EXECUTE_HANDLER)
    9.     {
    10.         return false;
    11.     }
    12. }
    复制代码


    4.堆栈段寄存器
    这是一个可以用来检测程序是否被追踪的技巧。这个技巧包括追踪以下汇编指令的排序:
    1. push ss
    2. pop ss
    3. pushf
    复制代码

    在调试器中单步通过这段代码后,Trap 标志寄存器将被设置。通常情况下,它是不可见的,因为调试器在每个调试器事件传递后都会清除Trap 标志寄存器。然而,如果我们先前将EFLAGS保存到堆栈中,我们就能检查Trap Flag是否被设置。
    C/C++ 代码:
    1. bool IsDebugged()
    2. {
    3.     bool bTraced = false;

    4.     __asm
    5.     {
    6.         push ss
    7.         pop ss
    8.         pushf
    9.         test byte ptr [esp+1], 1
    10.         jz movss_not_being_debugged
    11.     }

    12.     bTraced = true;

    13. movss_not_being_debugged:
    14.     // restore stack
    15.     __asm popf;

    16.     return bTraced;
    17. }
    复制代码

    5.指令计数
    这种技术滥用了一些调试器处理EXCEPTION_SINGLE_STEP异常的方式。

    这个技巧的想法是在一些预定义的序列(例如NOP序列)中为每条指令设置硬件断点。执行带有硬件断点的指令会引发EXCEPTION_SINGLE_STEP异常,这个异常可以被一个定向的异常处理程序捕获。在异常处理程序中,我们增加一个寄存器,这个寄存器起到指令计数器(在我们的例子中是EAX)和指令指针EIP的作用,将控制权传递给序列中的下一条指令。因此,每当控制被传递到我们序列中的下一条指令时,异常就会被引发,计数器也会被递增。序列结束后,我们检查计数器,如果它不等于我们序列的长度,我们就把它看作是程序正在被调试的情况。
    C/C++ 代码:
    1. #include "hwbrk.h"

    2. static LONG WINAPI InstructionCountingExeptionHandler(PEXCEPTION_POINTERS pExceptionInfo)
    3. {
    4.     if (pExceptionInfo->ExceptionRecord->ExceptionCode == EXCEPTION_SINGLE_STEP)
    5.     {
    6.         pExceptionInfo->ContextRecord->Eax += 1;
    7.         pExceptionInfo->ContextRecord->Eip += 1;
    8.         return EXCEPTION_CONTINUE_EXECUTION;
    9.     }
    10.     return EXCEPTION_CONTINUE_SEARCH;
    11. }

    12. __declspec(naked) DWORD WINAPI InstructionCountingFunc(LPVOID lpThreadParameter)
    13. {
    14.     __asm
    15.     {
    16.         xor eax, eax
    17.         nop
    18.         nop
    19.         nop
    20.         nop
    21.         cmp al, 4
    22.         jne being_debugged
    23.     }

    24.     ExitThread(FALSE);

    25. being_debugged:
    26.     ExitThread(TRUE);
    27. }

    28. bool IsDebugged()
    29. {
    30.     PVOID hVeh = nullptr;
    31.     HANDLE hThread = nullptr;
    32.     bool bDebugged = false;

    33.     __try
    34.     {
    35.         hVeh = AddVectoredExceptionHandler(TRUE, InstructionCountingExeptionHandler);
    36.         if (!hVeh)
    37.             __leave;

    38.         hThread = CreateThread(0, 0, InstructionCountingFunc, NULL, CREATE_SUSPENDED, 0);
    39.         if (!hThread)
    40.             __leave;

    41.         PVOID pThreadAddr = &InstructionCountingFunc;
    42.         // Fix thread entry address if it is a JMP stub (E9 XX XX XX XX)
    43.         if (*(PBYTE)pThreadAddr == 0xE9)
    44.             pThreadAddr = (PVOID)((DWORD)pThreadAddr + 5 + *(PDWORD)((PBYTE)pThreadAddr + 1));

    45.         for (auto i = 0; i < m_nInstructionCount; i++)
    46.             m_hHwBps[i] = SetHardwareBreakpoint(
    47.                 hThread, HWBRK_TYPE_CODE, HWBRK_SIZE_1, (PVOID)((DWORD)pThreadAddr + 2 + i));

    48.         ResumeThread(hThread);
    49.         WaitForSingleObject(hThread, INFINITE);

    50.         DWORD dwThreadExitCode;
    51.         if (TRUE == GetExitCodeThread(hThread, &dwThreadExitCode))
    52.             bDebugged = (TRUE == dwThreadExitCode);
    53.     }
    54.     __finally
    55.     {
    56.         if (hThread)
    57.             CloseHandle(hThread);

    58.         for (int i = 0; i < 4; i++)
    59.         {
    60.             if (m_hHwBps[i])
    61.                 RemoveHardwareBreakpoint(m_hHwBps[i]);
    62.         }

    63.         if (hVeh)
    64.             RemoveVectoredExceptionHandler(hVeh);
    65.     }

    66.     return bDebugged;
    67. }
    复制代码

    6.POPF和Trap标志寄存器
    这是另一个可以表明程序是否被追踪的技巧。

    在Flags寄存器中有一个Trap标志。当Trap 标志寄存器被设置时,异常SINGLE_STEP被引发。然而,如果我们追踪了代码,Trap 标志寄存器将被调试器清除,所以我们不会看到这个异常。
    C/C++ 代码:
    1. bool IsDebugged()
    2. {
    3.     __try
    4.     {
    5.         __asm
    6.         {
    7.             pushfd
    8.             mov dword ptr [esp], 0x100
    9.             popfd
    10.             nop
    11.         }
    12.         return true;
    13.     }
    14.     __except(GetExceptionCode() == EXCEPTION_SINGLE_STEP
    15.         ? EXCEPTION_EXECUTE_HANDLER
    16.         : EXCEPTION_CONTINUE_EXECUTION)
    17.     {
    18.         return false;
    19.     }
    20. }
    复制代码


    7.指令前缀
    这个技巧只在一些调试器中起作用。它滥用了这些调试器处理指令前缀的方式。

    如果我们在OllyDbg中执行下面的代码,在步进到第一个字节F3后,我们会立即到达尝试块的末端。调试器只是跳过前缀,将控制权交给INT1指令。

    如果我们在没有调试器的情况下运行同样的代码,就会产生一个异常,我们就会进入 except block。
    C/C++ 代码:
    1. bool IsDebugged()
    2. {
    3.     __try
    4.     {
    5.         // 0xF3 0x64 disassembles as PREFIX REP:
    6.         __asm __emit 0xF3
    7.         __asm __emit 0x64
    8.         // One byte INT 1
    9.         __asm __emit 0xF1
    10.         return true;
    11.     }
    12.     __except(EXCEPTION_EXECUTE_HANDLER)
    13.     {
    14.         return false;
    15.     }
    16. }
    复制代码

    反制措施
    调试期间:
    • 反制以下所有检查的最好方法是用NOP指令来修补它们。
    • 关于反跟踪技术:我们可以不修补代码,而是简单地在检查后的代码中设置一个断点,然后运行程序直到这个断点。

    对于反调试绕过工具的开发:没有反制措施。
    您需要登录后才可以回帖 登录 | 注册

    本版积分规则

    红盟社区--红客联盟 

    Processed in 0.056881 second(s), 21 queries.

    站点统计| 举报| Archiver| 手机版| 黑屋 |   

    备案号:冀ICP备20006029号-1 Powered by HUC © 2001-2021 Comsenz Inc.

    手机扫我进入移动触屏客户端

    关注我们可获取更多热点资讯

    Honor accompaniments. theme macfee

    快速回复 返回顶部 返回列表