C++ 的 try throw catch 反汇编方法 收藏

Unity逆向工程利器:Cpp2IL原理、实战与高级应用全解析 在软件逆向工程领域,理解编译后的程序逻辑是关键挑战。AOT(Ahead-Of-Time)编译技术将托管代码(如C#/.NET IL)转换为原生机器码以提升性能,但这也使得传统的基于中间语言的分析方法失效。其核心原理在于通过静态编译,消除即时编译开销,直接生成平台相关的二进制文件。这一技术为高性能应用和游戏(如Unity的IL2CPP方案)带来了显著优势,但也增加了安全分析和逻辑理解的难度。为此,逆向工程领域发展出了针对性的二进制分析工具,通过解析元数据和机器码模式,重建高级逻辑表示。Cpp2IL正是这一方向 阅读详情

 C++ 的 try throw catch 反汇编方法 收藏

新一篇: 如何使用 Symbol Retriever  | 旧一篇: VBS病毒解密初探

C++代码中如果包括了 try - throw - catch ,分析起来很不方便,不容易搞懂.可惜我非要搞懂
一段这种代码,只好分析一下,简单笔记.

为了方便区分各部分,起它们起名叫TAG_1,TAG_2等等.

反汇编代码中,一个包含try throw catch的函数通常是这样的结构:
                push    ebp
                mov     ebp, esp
                push    0FFFFFFFFh
                push    offset TAG_2
                mov     eax, dword ptr fs:__except_list
                push    eax
                mov     dword ptr fs:__except_list, esp
                sub     esp, 20h
                push    ebx
                push    esi
......
                lea     ecx, [ebp+var_18]
                push    offset TAG_A
                push    ecx
                mov     [ebp+var_18], TAG_ThrowValue
                call    _CxxThrowException(x,x)
......


                mov     ecx, [ebp-0Ch]
                pop     edi
                pop     esi
                mov     dword ptr fs:__except_list, ecx
                pop     ebx
                mov     esp, ebp
                pop     ebp
                retn

上面的TAG_ThrowValue是throw的值,类型要看TAG_A
TAG_2是这么个东东:
TAG_2:
                mov     eax, offset TAG_3
                jmp     ___CxxFrameHandler
TAG_3是这么个东东:
TAG_3           dd 19930520h            ; 这叫 magic
                dd 4                    ; 这是 TAG_4 的数目
                dd offset TAG_4         
                dd 2                    ; 这是 TAG_5 的数目
                dd offset TAG_5
                dd 0
                dd 0
TAG_4是:
TAG_4           dd 0FFFFFFFFh
                dd 0
                dd 0FFFFFFFFh
                dd 0
                dd 0FFFFFFFFh
                dd 0
                dd 0FFFFFFFFh
                dd 0
TAG_5是:
TAG_5           dd 0
                dd 0
                dd 1
                dd 4
                dd offset TAG_6_first
                dd 2
                dd 2
                dd 3
                dd 4
                dd offset TAG_6_second
这里有两个 TAG_6 表示本函数中有两份 try-throw-catch
TAG_6是:
TAG_6_first     dd 0
                dd offset TAG_8_char    ;char `RTTI Type Descriptor'
                dd -13h
                dd offset TAG_7_catch_char
                dd 0
                dd offset TAG_8_int     ;int `RTTI Type Descriptor'
                dd -28h
                dd offset TAG_7_catch_int
                dd 1
                dd offset TAG_8_pchar   ;char * `RTTI Type Descriptor'
                dd -2Ch
                dd offset TAG_7_catch_pchar
                dd 0
                dd 0
                dd 0
                dd offset TAG_7_catch_all

TAG_7就是catch处理程序 label 了.
上面 TAG_7_catch_all 前面是3个 dd 0 表示它是 catch(...)

TAG_8是这样的:
TAG_8_char      dd offset const type_info::`vftable'
                dd 0
                db '.D',0
上面的".D"表示它的类型是 char,还有其它的类型:
        ".D"    char
        ".H"    int
        ".PAD"  char *
        ".PAX"  void *
        ".?AVmyexception@@"     class myexception
        ".PAVCException@@"      class CException *


TAG_A 是:
TAG_A           dd 0
                dd 0
                dd 0
                dd offset TAG_B
TAG_B 是:
TAG_B           dd    1
                dd offset TAG_C
TAG_C 是
TAG_C           dd 1                    
                dd offset TAG_8_char    ;char `RTTI Type Descriptor'
                dd 0
                dd 0FFFFFFFFh
                dd 0
                dd 1
                dd 0
以上是简单的分析.对于一般简单的try-throw-catch知道这些足够了.
下面进一步分析复杂一些的.如果对代码:
#include <iostream>
#include <exception>
using namespace std;

class myexception: public exception
{
public:
  virtual const char* what() const
  {
    return "My exception happened";
  }
} ;

int kkmain () {
  try
  {
      myexception myex;
    throw myex;
  }
  catch (myexception& e)
  {
    cout << e.what() << endl;
  }
  return 0;
}
则它的 TAG_4 会变成
TAG_4           dd 0FFFFFFFFh   
                dd 0
                dd 0
                dd offset TAG_42
                dd 0FFFFFFFFh
                dd 0
上面的 TAG_42 是一个 label:
TAG_42:         lea     ecx, [ebp-20h]
                jmp     myexception::~myexception(void)
因为上面 throw 了一个class,这个class是需要析构的.这就是它的析构.
TAG_A 变成了;
TAG_A           dd 0
                dd offset myexception::~myexception(void)       ; 看来这里是throw对象的析构函数
                dd 0
                dd offset TAG_B
而TAG_B 变成了以下,看来不管有多少层父class都会在这里展开:
TAG_B           dd 2             
                dd offset TAG_C_me      ;__CT??_R0?AVmyexception@@@8??0myexception@@QAE@ABV0@@Z12
                dd offset TAG_C_parent  ;__CT??_R0?AVexception@@@8??0exception@@QAE@ABV0@@Z12
而 TAG_C 也复杂了:
TAG_C_me        dd 0
                dd offset TAG_8_myexcepton      ;myexception `RTTI Type Descriptor'
                dd 0
                dd 0FFFFFFFFh
                dd 0
                dd 0Ch
                dd offset myexception::myexception(myexception const &) ; 看来这里是我的构造函数

以上我尽我所能写了一些复杂的try throw catch,编译为OBJ,用 IDA 打开分析.可以对付一般的情况了.非
常可惜我正在研究的一个软件就不包括在这里,它的 TAG_3 是这样子的:
stru_45C2E0     dd 19930520h            ; Magic
                dd 14                   ; Count
                dd offset stru_45C2E0.Info; InfoPtr
                dd 0                    ; CountDtr
                dd 0                    ; DtrPtr
                dd 3 dup(0)             ; _unk
                dd -1                   ; Info.Id
                dd offset sub_453050    ; Info.Proc
                dd 0                    ; Info.Id
                dd offset sub_45305B    ; Info.Proc
                dd 1                    ; Info.Id
                dd offset sub_453066    ; Info.Proc
                dd 2                    ; Info.Id
                dd offset sub_453071    ; Info.Proc
                dd 3                    ; Info.Id
                dd offset sub_45307C    ; Info.Proc
                dd 4                    ; Info.Id
                dd offset sub_453087    ; Info.Proc
                dd 5                    ; Info.Id
                dd offset sub_453092    ; Info.Proc
                dd 6                    ; Info.Id
                dd offset sub_45309D    ; Info.Proc
                dd 5                    ; Info.Id
                dd offset sub_4530A5    ; Info.Proc
                dd 8                    ; Info.Id
                dd offset sub_4530B0    ; Info.Proc
                dd 5                    ; Info.Id
                dd offset sub_4530B8    ; Info.Proc
                dd 10                   ; Info.Id
                dd offset sub_4530C3    ; Info.Proc
                dd 5                    ; Info.Id
                dd offset sub_4530CB    ; Info.Proc
                dd 12                   ; Info.Id
                dd offset sub_4530D6    ; Info.Proc

不可理解.IDA 认识它,还知道它的结构叫 _msExcept :

00000000 _msExcept       struc ; (sizeof=0x20, variable size)
00000000 Magic           dd ?                    ; base 16
00000004 Count           dd ?                    ; base 10
00000008 InfoPtr         dd ?                    ; offset
0000000C CountDtr        dd ?                    ; base 10
00000010 DtrPtr          dd ?                    ; offset
00000014 _unk            dd 3 dup(?)
00000020 Info            _msExcInfo 0 dup(?)
00000020 _msExcept       ends

但我在网上搜索 _msExcept 没找到.什么样的代码才能产生这样的汇编输出呢?研究中...

我知道了!其实上面的一段应该这么理解
TAG_3:
stru_45C2E0     dd 19930520h            ; Magic
                dd 14                   ; Count
                dd offset stru_45C2E0.Info; InfoPtr 实际上这是指向 TAG_4
                dd 0                    ; CountDtr
                dd 0                    ; DtrPtr 这里是 TAG_5
                dd 3 dup(0)             ; _unk

有两个问题.一是它的 TAG_4 有很多项.这是因为这个函数有很多变量需要析构.TAG_4中
的各项就是它们的析构.一般情况下,TAG_4并不产生 C++ 代码.

第二是它的 TAG_5 怎么为空呢?分析表明,只要有一个 catch 就会有一个 TAG_5 项.没有
TAG_5 项只能有一个解释,就是它没有 catch 项!而try与catch是要配对的,没有 catch 
也就没有 try.就是说,这个函数没有 try 也没有 catch 只有 throw 

发表于 @ 2008年05月08日 17:56:19|评论(0)|编辑

新一篇: 如何使用 Symbol Retriever  | 旧一篇: VBS病毒解密初探

C++异常处理机制与RAII原理详解 异常处理是现代编程语言中错误管理的重要机制,其核心原理是通过栈展开(Stack Unwinding)实现跨函数调用的错误传播。C++中的异常处理采用try-catch-throw结构,配合RAII(资源获取即初始化)模式,能有效解决资源泄漏问题。从技术实现看,异常处理会生成栈帧信息表来指导析构和跳转过程,虽然会增加5-15%的代码体积,但在非错误路径上几乎没有性能开销。在金融交易系统等复杂业务场景中,异常机制相比传统错误码能显著简化多层调用栈的错误处理。合理运用noexcept关键字和冷路径优化技巧,可以进 阅读详情

相关推荐

C++异常处理性能优化:从原理到实践的成本分析与策略

在软件工程中,错误处理机制是保障程序健壮性的核心环节。C++提供了异常处理作为结构化错误处理方案,其底层基于栈展开和运行时类型信息匹配机制实现。从技术价值看,异常能将错误处理与正常逻辑分离,提升代码可读性和维护性。然而,这种机制在编译期会引入元数据导致代码膨胀,在运行时的抛出路径上涉及堆分配、栈回溯和析构链调用,带来显著开销。在游戏引擎、高频交易等对延迟敏感的应用场景中,异常抛出的最坏情况耗时可能达到微秒级,成为性能瓶颈。因此,开发者需要深入理解异常处理在二进制体积、运行时开销和优化屏障等方面的多维成本,通

weixin_33739523的博客 397

MFC 消息逆向分析 (看雪PDF文档) [评价可免费]

首先关于 [评价可免费] 的严重声明: 一、评价=评论加评价(评星星); 二、评价必须是下载完了该资源后的评价,没下载就评论无效; 三、如果正确评价了,返还积分可能需要等等,系统需要反应下。呵呵 评论时记得要评分。然后会返回给你花费的分再加1分.理论上有十分就可以下载完所有的资源了。一般人我不告诉他。 MFC 程序逆向 – 消息篇(上) 作者:szdbg Email:szdbg@sina.com 前言: 记得前一段时间,我刚接触软件破解和逆向这一行时,对于一些软件不知从何处跟踪按钮消息,试了好多方法,就是断 不下来,在系统模块中经常转得晕头转向,而一无所获。 MFC 程序是一种常见类型的程序,我静下心来,潜心研究了一下MFC 消息流程。弄清原委之后,一切豁然开朗,发现跟 踪MFC 程序和消息处理原来是如此。。。,跟踪按钮事件处理也由此变得特别简单。 于是,我将这些研究整理成文,以备后忘。并希望大家有所帮助,失误之处,请高手指正。 的确, .Net之类的程序必定是大势所趋,不过,就目前来说,MFC程序在软件市场还是占有重要的一席之地,所以,了解它,对于逆向和破解此类程序还是很有必要的. MFC之所以显的复杂,就在于它隐藏了它的消息的处理机制,可以说, 程序员基本上不需要懂得它的消息处理过程,就可以写出一套满足应用的软件来.这有好有坏,好的是大大简化了程序员写程序的过程,坏的一方面是,给一般程序员留下了一个迷团: 我只知道这样做,而不知道为什么这样做.心里老是觉得不踏实.

c++反汇编教程!经典!

c++反汇编中文教程,国外大牛非常经典之作,教你如何用IDA逆向C++

用IDA反汇编DLL文件心得

最近在学习如何用IDA 反汇编DLL文件,由于是初步接触,水平有限,错误在所难免,不过如果能对像我同样的初学者有点帮助,那就倍感欣慰。由于是原创,收取点资源分安慰自己吧,哈哈,大家不要砖头之哦

C++try throw catch 反汇编方法

C++代码中如果包括了 try - throw - catch ,分析起来很不方便,不容易搞懂.可惜我非要搞懂一段这种代码,只好分析一下,简单笔记.为了方便区分各部分,起它们起名叫TAG_1,TAG_2等等.反汇编代码中,一个包含try throw catch的函数通常是这样的结构:                push    ebp                mov     ebp, es

2520

C++虚函数调用的反汇编解析

C++虚函数调用的反汇编解析     作者:阮建辉 虚函数的调用如何能实现其“虚”?作为C++多态的表现手段,估计很多人对其实现机制感兴趣。大约一般的教科书就说到这个C++强大机制的时候,就是教大家怎么用,何时用,而不会去探究一下这个虚函数的真正实现细节。(当然,因为不同的编译器厂家,可能对虚函数有自己的实现,呵呵,这就算是虚函数对于编译器的“多态”了:)。 作为编译型语言,C

阮建辉的专栏 1万+

C++反汇编与逆向分析技术揭秘》学习之第13章 异常处理

C++标准中规定了异常处理的语法,各编译器厂商必须遵守这些语法。因为C++标准中并没有规定异常处理的实现过程,所以经不同厂商的编译器编译后产生的异常处理代码各不相同。GCC、Clang编译器的异常处理代码取决于使用的异常库,因此本章主要针对VS编译器进行讲解。VS编译器的异常处理与Windows的SEH机制密切相关,大家在学习本章之前,应熟练掌握SEH机制。

chenyijun的专栏 564

内部资源

sjlj (setjump/longjump)与dwarf-2为mingw32两种异常处理模型的实现。sjlj有着开销,而随linux发行的mingw32开发库包都是用sjlj版编译的,而Qt却采用dwarf-2版,那么两者之间有多少差异,本文就这问题对两版的异常代码的反汇编进行分析比较。 我使用mingw-w65-i686-810的sjlj与dwarf-2两个版本对下面异常代码编译。 __attribute__((dllimport)) int dllfunc(); int main() {

466

C++中 异常处理的 实现方式

1、异常是什么? 程序执行期间出现的错误或意外情况。   2、传统的异常处理方式 条件判断、分支处理 if 优点:实现简单直观 缺点:a、种类及情况太多,在大型系统中很难全部考虑到,导致程序崩溃,终止运行 b、某些异常情况的发生具有随机性,无法在程序中做出明确的处理,但是我们有时候又希望程序能够跳过异常部分错误运行。 c、我们希望可以传递异常信息,也就是发生

做一个快乐的人 956

C++运行时机制二进制剖析:虚函数表、多重继承与异常处理

C++作为一门支持面向对象编程的系统级语言,其核心机制如多态、继承和异常处理在源码层面提供了高级抽象。这些特性的底层实现原理,依赖于编译器生成的特定数据结构和调用约定。虚函数表(vtable)是实现运行时多态的关键数据结构,它本质上是一个存储函数指针的数组,并与对象的隐藏指针(vptr)关联,使得动态绑定成为可能。在多重继承场景下,对象内存布局会包含多个基类子对象,涉及复杂的指针调整(this指针调整)以确保正确的函数调用和成员访问。异常处理机制则依赖于平台特定的数据结构(如Windows的SEH或Itan

weixin_30236595的博客 1472

C++异常的幕后16:在着陆垫里找到正确的捕捉

原文地址:https://monoinfinito.wordpress.com/2013/05/07/c-exceptions-under-the-hood-16-finding-the-right-catch-in-a-landing-pad/ 作者:nicolasbrailo 应我们要求第16章要实现能够处理异常的小ABI;上次我们实现了personality函数,使它能够处理有多个着陆垫...

wuhui_gdnt的专栏 300

llama.cpp Android移植实战:端侧大模型推理工程化指南

端侧大模型推理是移动AI落地的核心能力,其本质是将轻量化LLM(如Qwen2、Phi-3)在资源受限的Android设备上实现低延迟、高稳定性的本地运行。技术原理涉及C/C++推理引擎(llama.cpp)与Android NDK工具链的深度适配,关键在于ARM64指令集支持、JNI线程安全调用、量化模型(GGUF/Q4_K_M)内存布局优化及沙箱权限绕行。该能力显著提升隐私性、离线可用性与响应实时性,广泛应用于本地聊天助手、智能硬件嵌入式AI、教育类App离线问答等场景。本文聚焦llama.cpp在And

weixin_33701617的博客 575

C++异常机制深度剖析:从原理到性能优化与工程实践

异常处理是编程语言中用于分离错误检测与处理的核心机制,其设计旨在保证程序在异常情况下的资源安全与状态一致性。从原理上看,C++异常通过栈展开和RAII(资源获取即初始化)确保局部对象的析构函数被正确调用,从而实现基本异常安全保证。在技术价值层面,异常为深层嵌套的错误传播提供了优雅的解决方案,避免了错误码的逐层检查,提升了代码的清晰度。然而,其性能开销主要集中于异常抛出路径,在正常执行路径上,现代编译器(如GCC/Clang采用的Itanium C++ ABI模型)可实现接近零开销。在应用场景上,异常适用于构

weixin_34026276的博客 596

VC++异常处理全解析:从C++标准到SEH与崩溃转储实战

异常处理是现代软件开发中保障程序健壮性的核心机制,它允许程序在运行时错误发生时进行可控的响应与恢复。其原理基于栈展开和资源自动管理,通过中断正常执行流并跳转到预定义的错误处理代码,有效防止了资源泄漏和程序崩溃。在技术价值上,完善的异常处理能提升软件可靠性、可维护性和用户体验,是构建高可用系统的基石。应用场景广泛覆盖网络通信、文件I/O、第三方库集成等高风险操作。本文聚焦于Visual C++平台,深入剖析C++标准异常与Windows结构化异常处理(SEH)的混合使用策略,并详解如何通过MiniDump生成

weixin_34112181的博客 388

C++ 内存管理

在申请自定义类型的空间时,new会自动调用自定义类型的构造函数,delete会自动调用析构函数,而malloc和free都不会。这就是new和delete与malloc和free最大的区别。int _a2;int main()// 申请3个数据类型为A的空间(调用三次默认构造)

Fuyo_0209的博客 649

Windows C++开发实战:从环境搭建到性能优化的完整指南

C++作为高性能系统编程的核心语言,其内存管理、多线程和底层控制能力是构建高效软件的基础。在Windows平台上进行C++开发,需要深入理解其特有的运行时环境、编译链接机制和系统API。通过合理配置开发环境、管理依赖库和运行时,开发者可以充分发挥C++的性能优势,构建稳定可靠的桌面应用、系统服务和高性能计算程序。本文聚焦Windows C++开发中的**环境配置**和**调试技巧**,涵盖从Visual Studio与CMake工具链集成、vcpkg依赖管理,到多线程调试、内存泄漏检测等实战问题,为开发者提

afhu47033的博客 422

C++函数声明与定义:编译期契约与链接逻辑

函数声明与定义是C++程序构建的基础概念,本质是编译器与链接器之间的协作契约:声明建立类型系统可见性,定义提供符号实现。其原理根植于C++的两阶段编译模型——声明在编译期完成语法与类型检查,定义则在链接期兑现符号引用。这一机制支撑了头文件设计、ODR规则、inline语义及跨语言调用(如extern "C")等关键技术价值。典型应用场景包括大型项目模块解耦、DLL导出函数控制、模板定义位置约束,以及避免多重定义错误。深入理解声明与定义的分离逻辑,是掌握cpp中函数的声明和定义、c++函数模板实例化时机等核心

weixin_34056162的博客 391

C++学习

C++基础学习、函数模板、动态内存、新特性、面向对象编程、类的设计以及STL标准模板库的学习

2601_94965056的博客 210
上一篇: 离线浏览器Teleport Pro完全教程
下一篇: If-Modified-Since
skyremember
博客等级 码龄20年 203粉丝 484原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值