首页 > 其他分享 >【反编译】基础

【反编译】基础

时间:2024-08-29 10:15:36浏览次数:3  
标签:反编译 规约 控制流 基础 ret int printf 流图

原创 看雪学苑

什么是控制流还原

所谓控制流还原,通俗的讲就是将CFG还原成由if、while、for等组成的高级抽象结构。如下图有个控制流图,他的边本身是个jump,反编译器需要理解控制流的结构,把条件分支和循环识别出来。

反编译器中的控制流还原

控制流还原部分处于反编译步骤的最后几步,在反编译器中端IR优化完成之后,再通过算法将CFG还原成由if、while、for等组成的高级抽象。
《高级编译器的设计与实现》第7章的结构分析技术

鲸书的第七章描述了一个叫做"结构分析"的控制流恢复的算法。其原理大致是预置好各种流图模式,然后在CFG中寻找能对应上的模式后,把流图按照规则进行"塌缩",直到塌缩成一个点就完成分析了。

这样说十分的抽象,举例说明。

预置模式

流图塌缩

什么是"不可规约"流图

使用“结构化分析”这种技术,比较麻烦的问题是对“不可规约”流图的处理。
所谓“不可规约流图”,通俗的说,就是一个CFG无法套用上面任何一种规则进行“塌缩”。

比较典型的“不可规约流图”有多入口的循环,多出口的循环等等,我这里从书里找了几个典型的“不可规约流图”的例子:

为什么会产生不可规约流图?

虽然源码中,不可规约的情况很少很少。但是,在编译的大型二进制产物中,不可规约流图十分常见。

其主要原因是,编译器对源码的优化会修改CFG,产生不可规约的情况,我以比较好理解的编译器的尾融合(tail-merging或cross-jumping)优化为例;
在这个优化中,编译器寻找最后几条指令相同的两个基本块,若这两个基本块都转移到同一个位置,就将这两个基本块的代码尾部合并,以此来缩小编译产物的代码规模。

示例源码:

int fun(int a, int b)
{
    int ret = 0;
    if (getchar() > 0x10) {
        ret = a+b;
    } else {
        ret = a-b;
        if (getchar()>0x20) {
            printf("hello");
            printf("ret=xx");
            return ret;
        } 
    }
    printf("hi");
    printf("ret=xx");
    return ret;
}

编译器优化中发现, printf("ret=xx"); return ret; 这两句代码完全一致,为了减少代码量就插入一个goto语句将这两个尾部进行融合。

int fun(int a, int b)
{
    int ret = 0;
    if (getchar() > 0x10) {
        ret = a+b;
    } else {
        ret = a-b;
        if (getchar()>0x20) {
            printf("hello");
            goto LABEL
        } 
    }
    printf("hi");
LABEL:
    printf("ret=xx");
    return ret;
}

如下图:

很明显,这就是一个典型的"不可规约"流图,《鲸书》中第七章将也有专门提到这种无法规约的场景。

由于这种流图无法正常规约,那么IDA这种反编译器对付的方法就是插入一个GOTO语句进行缓解。

为什么IDA F5后的代码会有GOTO语句?

当前,通用的反编译器,比如IDA/Ghidra等使用的控制流还原技术都是基于“结构化分析”,为了解决不可规约流图的问题,反编译器选用了一种保守的方案:"删除边"后使用goto替代。并重新进行塌缩。这也是为什么F5后的代码会有GOTO语句的原因。

还是之前那个代码,IDA反编译后会插入goto语句解决这个问题。

简单的说就是下面这样处理:

参考

标签:反编译,规约,控制流,基础,ret,int,printf,流图
From: https://www.cnblogs.com/o-O-oO/p/18386025

相关文章

  • Android Audio分区——车载多区音频基础(一)
            AndroidAudio多区音频功能主要针对的是AndroidAutomotive这样的场景,它允许在同一个Android设备上支持多个独立的音频区域,每个区域可以有不同的音频输出设置。这种功能特别适用于汽车环境,因为车内通常有多个乘客,他们可能希望听不同的音频内容。一、概念......
  • Mysql超详细基础干货——几分钟带你认识mysql
    Mysql数据库事务的特性binlog、redolog和undologMySQL事务实现原理leftjoin、rightjoin和innerjoin区别?说一下mysql的行锁和表锁索引有哪些数据结构Innodb和Myisam存储引擎区别为什么索引底层实现选择B+uuid为什么不适合做主键?1万数据未支付,已支付,支付失败状......
  • 【408DS算法题】028基础-查找二叉树的最大值结点
    Index题目分析实现总结题目给定二叉树的根节点,找到二叉树中结点值最大的结点。分析实现对于查找二叉树中的最大值结点,在二叉树的遍历(DFS、层次遍历)的基础上进行修改可以轻松地达成这一目的。本文中选用的是相对直观的后序遍历,具体实现如下:BTNode*findMax(BTN......
  • 渗透基础知识
    POC:利用/证明代码,发现漏洞   EXP:留下漏洞攻击木马:留下,操作控制电脑        病毒:破坏性,正常文件无法打开 payload:有效负载,真正需要执行的代码,管道PoC通常是无害的,Exp通常是有害的,有了POC,才有EXP。Payload有很多种,它可以是Shellcode,也可以直接是一段系......
  • Vue3 变量响应基础
    在Vue3中有两种分别为选项式和组合式的操作,现在一般用组合式,很少用选项式的操作;1.计算,与变量;直接这样写的话,他不会进行计算,需要添加两个大括号;比如 {{1+1}}变量的话我们可以不用script中的因为里面是Vue2的我们可以改成如果不使用{{}}的话他显示的则会变......
  • C++入门基础(内容太干,噎住了)
    文章目录1.缺省参数2.函数重载2.1重载条件:1.参数类型不同2.参数个数不同3.参数类型顺序不同 2.2不做重载条件情况:1.返回值相同时2.当全缺省遇见无参数3.引用3.1引用特性:3.2引用的使用1.缺省参数1.缺省参数是声明或定义函数时为函数的参数指定⼀个缺省值。......
  • 【C++基础】多种函数重载和运算符重载
    目录一、函数重载1.参数类型不同的重载讲解2.参数个数不同的重载讲解3.参数顺序不同的重载讲解4.默认参数与函数重载讲解二、运算符重载1.运算符重载的基本语法示例讲解函数内部的操作:运算符的使用:2.运算符重载的常见用法2.1重载<<和>>运算符(用于输......
  • python基础操作
    python基础仅仅打印输出helloworld是不够的,对吧?你想要做的远不止这些——你想要得到一些输入,操作它并从中得到一些东西。1.注释注释是#符号右侧的任何文本,主要用作程序读者的注释。print('helloworld')#注意,print是一个函数。或者:#注意,print......
  • Spring(基础篇1)
    本篇博客讲解Spring框架里面的通过xml方式对Bean的基础配置,实例化方式,以及DI的注入,偏于基础。一.Bean的基础配置  Id属性andClass属性<beanid="userService"class="com.itheima.Service.Impl.UserServiceImpl"></bean> Scope属性Scope属性取值含义singleto......
  • 计算机网络基础
       本篇是关于计算机网络基础的复习和总结,仅供阅读。目录TCP/IP网络模型 应用层传输层网络层网络接口层总结数据传输的具体过程,都经历了什么?最初的模样:HTTP真实地址查询:通讯录DNS远行工具集合:协议栈可靠的传输:衣服TCP    TCP报文    关于......