首页
社区
课程
招聘
[原创][翻译]反调试:汇编指令
发表于: 2021-6-23 17:54 7267

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

2021-6-23 17:54
7267

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




目录

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

1. INT 3
指令INT3是一个中断,被用作软件断点。在没有调试器存在的情况下,在到达INT3指令后,会产生异常EXCEPTION_BREAKPOINT(0x80000003),并且会调用一个异常处理程序。如果调试器存在,控制权就不会被交给异常处理程序。
C/C++ 代码:


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

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


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++ 代码:


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

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

但是,如果程序已经被跟踪,调试器会认为这个异常是执行指令时在Flags寄存器中设置了SingleStep位产生的正常异常。因此,在调试器下,异常处理程序不会被调用,在ICE指令后继续执行。
C/C++ 代码:


4.堆栈段寄存器
这是一个可以用来检测程序是否被追踪的技巧。这个技巧包括追踪以下汇编指令的排序:

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


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

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


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

在Flags寄存器中有一个Trap标志。当Trap 标志寄存器被设置时,异常SINGLE_STEP被引发。然而,如果我们追踪了代码,Trap 标志寄存器将被调试器清除,所以我们不会看到这个异常。
C/C++ 代码:


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

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

如果我们在没有调试器的情况下运行同样的代码,就会产生一个异常,我们就会进入 except block。
C/C++ 代码:


反制措施
调试期间:


对于反调试绕过工具的开发:没有反制措施。


bool IsDebugged()
{
    __try
    {
        __asm int 3;
        return true;
    }
    __except(EXCEPTION_EXECUTE_HANDLER)
    {
        return false;
    }
}
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;
    }
}
bool IsDebugged()
{
    __try
    {
        __asm xor eax, eax;
        __asm int 0x2d;
        __asm nop;
        return true;
    }
    __except(EXCEPTION_EXECUTE_HANDLER)
    {
        return false;
    }
}
bool IsDebugged()
{
    __try
    {
        __asm __emit 0xF1;
        return true;
    }
    __except(EXCEPTION_EXECUTE_HANDLER)
    {
        return false;
    }
}
push ss 
pop ss 
pushf

[注意]传递专业知识、拓宽行业人脉——看雪讲师团队等你加入!

收藏
免费 2
支持
分享
最新回复 (1)
雪    币: 4569
活跃值: (2610)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
2
感谢分享
2021-6-28 18:44
0
游客
登录 | 注册 方可回帖
返回
//