C++ SEH结构化异常捕获处理(双平台支持 Linux、Windows)。

测试:

    try_ctor();
    try_call(
        []()
        {
            printf("1111111111111111111111\r\n");
            int* p = NULL;
            *p = 100;
            throw 1;

            // try_eeh();
        }, 
        []()
        {
            printf("2222222222222222222222\r\n");
        });

设置NULL指针P的值引发程式崩溃,可以被正确捕获(catch)处理,不支持 finally,可以自己改改,这是一个相对来说较为简洁的实现,该实现的代码可以在WIN、LINUX平台上编译通过且正常就绪运行。

##

try_call 保护代码快调用类似 lua 的 xpcall,try_ctor,注册所需的信号,try_eeh 是回到当前压入栈顶的结构化的异常处理器,vc++ 的seh 结构化处理也是这个处理,可以参考易语言seh结构化异常处理,写这个小哥是用汇编来写的,或者自己反调试看看seh展开的机器代码,思想上这个东西都差不多,但 try_catch 也有缺点,如果栈坏的很彻底就没法恢复了,比如C#.NET,单一时间内异常抛多了,catch 是捕获不了的,毕竟不是java 这类型的语言。

实现:

class __try_context__ final {
public:
    jmp_buf                                                         __try_jmp_buf;
    bool                                                            __try_jmp_flg;

public:
    std::function<void()>                                           __try_block;
    std::function<void()>                                           __try_catch;

public:
    inline __try_context__()
        : __try_jmp_flg(false) {

    }
};

static thread_local std::list<std::shared_ptr<__try_context__>>     __try_contexts__;
static thread_local std::shared_ptr<__try_context__>                __try_eeh__;

static std::shared_ptr<__try_context__> try_seh_pop_context() noexcept {
    auto tail = __try_contexts__.begin();
    auto endl = __try_contexts__.end();
    if (tail == endl) {
        return NULL;
    }

    std::shared_ptr<__try_context__> context = std::move(*tail);
    __try_contexts__.erase(tail);
    return context;
}

static void try_seh_pop() noexcept {
    std::shared_ptr<__try_context__> context = try_seh_pop_context();
    context.reset();
}

static void try_seh_eeh_clear() noexcept {
    __try_eeh__ = NULL;
}

static void try_seh_linux(__try_context__* context) noexcept {
    int32_t signo = setjmp(context->__try_jmp_buf);
    if (signo == 0) {
        context->__try_jmp_flg = true;
        context->__try_block();
        try_seh_pop();
    }
    else {
        context = __try_eeh__.get();
        context->__try_catch();
    }

    try_seh_eeh_clear();
}

#ifdef _MSC_VER
static void try_seh_windows(__try_context__* context) {
    __try {
        try_seh_linux(context);
    }
    __except (sehCrashFilter(GetExceptionCode(), GetExceptionInformation())) { /* GetExceptionCode() == EXCEPTION_ACCESS_VIOLATION ? EXCEPTION_EXECUTE_HANDLER : EXCEPTION_CONTINUE_SEARCH */
        context->__try_catch();

        try_seh_pop();
        try_seh_eeh_clear();
    }
}
#endif

void try_call(std::function<void()>&& __try_block, std::function<void()>&& __try_catch) {
    if (NULL == __try_block) {
        throw std::runtime_error("__try_block argument not allow is NULL.");
    }

    __try_context__* context = NULL;
    if (NULL == __try_catch) {
        throw std::runtime_error("__try_catch argument not allow is NULL.");
    }
    else
    {
        std::shared_ptr<__try_context__> try_context = std::make_shared<__try_context__>();
        if (NULL == try_context) {
            throw std::runtime_error("unable to make try context.");
        }
        else {
            context = try_context.get();
        }

        try_context->__try_block = std::move(__try_block);
        try_context->__try_catch = std::move(__try_catch);
        __try_contexts__.emplace_back(try_context);
    }

#ifdef _MSC_VER
    try_seh_windows(context);
#else
    try_seh_linux(context);
#endif
}

bool try_eeh() {
    jmp_buf __try_jmp_buf;
    for (;;) {
        {
            std::shared_ptr<__try_context__> context = try_seh_pop_context();
            if (NULL == context) {
                break;
            }

            if (!context->__try_jmp_flg) {
                continue;
            }
            else {
                __try_eeh__ = context;
            }

            memcpy(__try_jmp_buf, context->__try_jmp_buf, sizeof(__try_jmp_buf));
        }

        longjmp(__try_jmp_buf, 1);
        return true;
    }
    return false;
}

void try_ctor() {
    auto __try_eeh__ = [](int signo) noexcept -> void {
        bool ok = try_eeh();
        if (!ok) {
            signal(signo, SIG_DFL);
            raise(signo);
        }
    };

#ifdef __GNUC__
    signal(SIGTRAP, __try_eeh__);   // 调试陷阱
    signal(SIGBUS, __try_eeh__);    // 总线错误(常见于结构对齐问题)
    signal(SIGQUIT, __try_eeh__);   // CTRL+\退出终端
    signal(SIGSTKFLT, __try_eeh__); // 进程堆栈崩坏
#endif

    signal(SIGSEGV, __try_eeh__);   // 段错误(试图访问无效地址)
    signal(SIGFPE, __try_eeh__);    // 致命的算术运算问题(常见于试图除以零或者FPU/IEEE-754浮点数问题)
    signal(SIGABRT, __try_eeh__);   // 程式被中止执行(常见于三方库或固有程式遭遇一般性错误执行abort()强行关闭主板程式)
    signal(SIGILL, __try_eeh__);    // 非法硬件指令(CPU/RING 0 ABORT)
}

  • 5
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
前言 本程序基于东灿的异常调试模块5.2部分版权信息如下: 1. 该模块功能是补足易错误管理器无法捕捉的异常。 2. 本模块可以截获许多异常,比如内存读写错误,非法汇编指令,算术异常,其他异常等等。 3. 本模块拥有try/catch的异常处理模型,支持多层嵌套,支持向上处理。允许用户自己设定跳过致命异常。 4. 并且支持易原错误管理器接管 5. 建议配合配套工具使用更加好用 正文: 程序原使用模块: 模块名 作者或工作室 用处 高级ApiHookEx 作者未知 HookApi,在模块中用于Hook:CreateRemoteThreadEx和CreateRemoteThread AppThreadVar 作者未知 进程内变量,线程内变量。可以在任意ec模块,任意DLL内调用,全部共享 callstack 作者未知 函数调用堆栈跟踪。 disasm 作者未知 支持MMX/SSE/SSE2/SSE3/3DNow取CPU指令长度 GetThisModuleHandle 作者未知 取出当前ModuleHandle e-try/catch(即new_SEH) 星锋工作室-东灿 SEH结构体等 精简内容 简化模块(即多模块合一,但互相不影响,并且删去了重复的API,数据类型等) 增加注释:对于代码,追加了更多的注释。 部分原理理解(很多都是本人自己的理解,可能不对,若有错误,欢迎指出):AppThreadVar:工作核心:tls线程局部存储(缩写为TLS。进程中的全局变量与函数内定义的静态(static)变量,是各个线程都可以访问的共享变量。在一个线程修改的内存内容,对所有线程都生效。这是一个优点也是一个缺点。说它是优点,线程的数据交换变得非常快捷。说它是缺点,一个线程死掉了,其它线程也性命不保; 多个线程访问共享数据,需要昂贵的同步开销,也容易造成同步相关的BUG。) 部分功能:它主要是为了避免多个线程同时访存同一全局变量或者静态变量时所导致的冲突,尤其是多个线程同时需要修改这一变量时。为了解决这个问题,我们可以通过TLS机制,为每一个使用该全局变量的线程都提供一个变量值的副本,每一个线程均可以独立地改变自己的副本,而不会和其它线程的副本冲突。从线程的角度看,就好像每一个线程都完全拥有该变量。而从全局变量的角度上来看,就好像一个全局变量被克隆成了多份副本,而每一份副本都可以被一个线程独立地改变。 用途:动态TLS和静态TLS这两项技术在创建DLL的时候更加有用,这是因为DLL通常并不知道它们被链接到的应用程序的结构是什么样的。1. 如果应用程序高度依赖全局变量或静态变量,那么TLS可以成为我们的救生符。因而最好在开发中最大限度地减少对此类变量的使用,更多的依赖于自动变量(栈上的变量)和通过函数参数传入的数据,因为栈上的变量始终都是与某个特定的线程相关联的。如果不使用此类变量,那么就可以避免使用TLS。2. 但是在编写应用程序时,我们一般都知道自己要创建多少线程,自己会如何使用这些线程,然后我们就可以设计一些替代方案来为每个线程关联数据,或者设计得好一点的话,可以使用基于栈的方法(局部变量)来为每个线程关联数据 TLS分动态和静态,AppThreadVar我认为是动态TLS(调用了TlsAlloc函数) 相当于静态变量(我也只能理解到这里了) ApiHookEx和大部分的APIHook工作原理类似 e-try/catch说实话,这个模块我几乎不能理解原理(能理解的基本上是一些Api,比如MiniDumpWriteDump) 这个可能也是用于捕获异常,而且比较神奇的是,这个捕获异常以后不会导致程序的退出,而是程序能够正常运行! 可以抛出自定义数值异常,由catch_int/catch_eq_int接收 可以抛出最后一次异常,让上层错误处理处理 部分实例: try/catch测试:try/catch结果: 部分崩溃代码测试: 结果部分: 程序可以自己写出DMP文件: 更多功能请自己测试!!

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值