首页
社区
课程
招聘
[原创][翻译]反调试:异常情况
2021-6-10 16:20 8579

[原创][翻译]反调试:异常情况

2021-6-10 16:20
8579

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

目录

  • 异常

  • 1. UnhandledExceptionFilter()

  • 2. RaiseException()

  • 3. 用异常处理程序来隐藏控制流

  • 反制措施


异常
下面的方法故意导致异常,以验证进一步的行为对于没有调试器运行的进程是否不是典型的。

1. UnhandledExceptionFilter()
如果一个异常发生而没有注册异常处理程序(或者已经注册但没有处理这样的异常),kernel32!UnhandledExceptionFilter()函数将被调用。可以使用kernel32!SetUnhandledExceptionFilter()来注册一个自定义的未处理异常过滤器。但是如果程序在调试器下运行,自定义的过滤器将不会被调用,异常将被传递给调试器。因此,如果未处理的异常过滤器被注册,并且控制被传递给它,那么这个进程就不是在调试器下运行。
x86 汇编 (FASM):

include 'win32ax.inc'

.code

start:
        jmp begin

not_debugged:
        invoke  MessageBox,HWND_DESKTOP,"Not Debugged","",MB_OK
        invoke  ExitProcess,0

begin:
        invoke SetUnhandledExceptionFilter, not_debugged
        int  3
        jmp  being_debugged

being_debugged:
        invoke  MessageBox,HWND_DESKTOP,"Debugged","",MB_OK
        invoke  ExitProcess,0

.end start

C/C++ 代码:

LONG UnhandledExceptionFilter(PEXCEPTION_POINTERS pExceptionInfo)
{
    PCONTEXT ctx = pExceptionInfo->ContextRecord;
    ctx->Eip += 3; // Skip \xCC\xEB\x??
    return EXCEPTION_CONTINUE_EXECUTION;
}

bool Check()
{
    bool bDebugged = true;
    SetUnhandledExceptionFilter((LPTOP_LEVEL_EXCEPTION_FILTER)UnhandledExceptionFilter);
    __asm
    {
        int 3                      // CC
        jmp near being_debugged    // EB ??
    }
    bDebugged = false;

being_debugged:
    return bDebugged;
}

2. RaiseException()
诸如DBC_CONTROL_C或DBG_RIPEVENT之类的异常不会传递给当前进程的异常处理程序,而是由调试器使用。这让我们注册一个异常处理程序,使用kernel32!RaiseException()函数,并检查控件是否传递给了我们的处理程序。如果未调用异常处理程序,则该进程可能正在调试中。
C/C++ 代码:

bool Check()
{
    __try
    {
        RaiseException(DBG_CONTROL_C, 0, 0, NULL);
        return true;
    }
    __except(DBG_CONTROL_C == GetExceptionCode()
        ? EXCEPTION_EXECUTE_HANDLER 
        : EXCEPTION_CONTINUE_SEARCH)
    {
        return false;
    }
}


3. 用异常处理程序来隐藏控制流
这种方法不能检查调试器是否存在,但它有助于在异常处理程序的序列中隐藏程序的控制流。

我们可以注册一个异常处理程序(结构化的或矢量的),引发另一个异常,这个异常被传递给下一个处理程序,引发下一个异常,以此类推。最后,处理程序的序列应该通向我们想要隐藏的程序
使用结构化异常处理程序:
C/C++ 代码:

#include <Windows.h>

void MaliciousEntry()
{
    // ...
}

void Trampoline2()
{
    __try
    {
        __asm int 3;
    }
    __except (EXCEPTION_EXECUTE_HANDLER)
    {
        MaliciousEntry();
    }
}

void Trampoline1()
{
    __try 
    {
        __asm int 3;
    }
    __except (EXCEPTION_EXECUTE_HANDLER)
    {
        Trampoline2();
    }
}

int main(void)
{
    __try
    {
        __asm int 3;
    }
    __except (EXCEPTION_EXECUTE_HANDLER) {}
    {
        Trampoline1();
    }

    return 0;
}


使用矢量异常处理程序:
C/C++ 代码:

#include <Windows.h>

PVOID g_pLastVeh = nullptr;

void MaliciousEntry()
{
    // ...
}

LONG WINAPI ExeptionHandler2(PEXCEPTION_POINTERS pExceptionInfo)
{
    MaliciousEntry();
    ExitProcess(0);
}

LONG WINAPI ExeptionHandler1(PEXCEPTION_POINTERS pExceptionInfo)
{
    if (g_pLastVeh)
    {
        RemoveVectoredExceptionHandler(g_pLastVeh);
        g_pLastVeh = AddVectoredExceptionHandler(TRUE, ExeptionHandler2);
        if (g_pLastVeh)
            __asm int 3;
    }
    ExitProcess(0);
}


int main(void)
{
    g_pLastVeh = AddVectoredExceptionHandler(TRUE, ExeptionHandler1);
    if (g_pLastVeh)
        __asm int 3;

    return 0;
}


反制措施
调试期间:

  • 对于调试器的检测检查。只要在相应的检查中填入NOP即可。

  • 对于控制流隐藏。你必须手动追踪程序直到payload。

对于反调试绕过工具的开发。这类技术的问题是,不同的调试器会产生不同的异常,并且不把它们返回给调试器。这意味着你必须为特定的调试器实现一个插件,并改变相应的异常后触发的事件处理程序的行为。



[培训]内核驱动高级班,冲击BAT一流互联网大厂工作,每周日13:00-18:00直播授课

最后于 2021-6-18 16:38 被梦幻的彼岸编辑 ,原因:
收藏
点赞4
打赏
分享
最新回复 (3)
雪    币: 1451
活跃值: (14614)
能力值: ( LV12,RANK:380 )
在线值:
发帖
回帖
粉丝
SSH山水画 3 2021-6-10 16:25
2
0
梦幻大佬带带弟弟
雪    币: 8044
活跃值: (4335)
能力值: ( LV4,RANK:50 )
在线值:
发帖
回帖
粉丝
sunsjw 1 2021-6-17 17:53
3
0
不错,学习了。
雪    币: 25
活跃值: (570)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
夺命黑裤衩 2022-1-21 05:10
4
0
大佬大佬带带我
游客
登录 | 注册 方可回帖
返回