欢迎来到 嗅灵易学

零基础也能上手的脚本技术课,一对一答疑带你入门

[原创]反编译原理(5)-控制流分析

[原创]反编译原理(5)-控制流分析

控制流分析

控制流结构恢复、变量和类型恢复是反编译器中端向后端转化最关键的两个步骤,本文讨论控制流结构恢复。

1. 编译器

主要是概述鲸书“高级编译器的设计与实现”第7章控制流分析,并且增加了一些内容,所涉及的相关论文书籍自行查找学习,还可以从维基百科了解学习。

1.1 Graph Algorithms

可以从论文"Notes on Graph Algorithms Used in Optimizing Compilers"了解学习

1.2 CFG

ControlFlowGraph(控制流程图),BasicBlock(基本块)、Predecessor(前驱)、Successor(后继)、Entry(入口)、Exit(出口)等概念,大部分的编译器控制流分析相关书籍论文都有介绍这些内容,还有应该熟悉了解图论中的有向图的相关知识。

1.3 BFS and DFS

BFS(广度优先搜索)和DFS(深度优先搜索)是图论中的两个概念,重点关注深度优先搜索,深度优先搜索有前序遍历、中序遍历、后序遍历三种遍历方法,前序遍历和后序遍历在控制流分析时是非常关键的两种遍历。大部分数据结构书籍中的图论都有介绍这些内容。

1.4 Dominator Tree

如果从流图的入口(Entry)结点到结点d的每一条可能的路径都经过结点n,称n是d的必经结点,或者说d支配n;如果从流图的结点d到出口(Exit)结点的每一条可能的路径都经过结点n,称n是d的后必经结点,或者说d后支配n。一种有效的表示支配结点信息的方法是用支配树(Dominator Tree)表示,构建支配树有静态和动态两种方法。

  • 1.4.1 Static

    主要有以下三种方法:

    • The Iterative Algorithm

      可以从论文"A Simple, Fast Dominance Algorithm"学习了解,大部分编译器用这种方法构建支配树。

    • The Lengauer-Tarjan Algorithm

      可以从论文"A Fast Algorithm for Finding Dominators in A Flowgraph"学习了解,一部分编译器用这种方法构建支配树。

    • The SEMI-NCA Algorithm

      可以从论文"Finding Dominators in Practice"学习了解

  • 1.4.2 Dynamic

    主要有以下三种方法:

    • The Sreedhar-Gao-Lee Algorithm

      可以从论文"Incremental Computation of Dominator Trees"学习了解,编译器GCC实现了该算法

    • The Dynamic SEMI-NCA Algorithm

      可以从论文"An Experimental Study of Dynamic Dominators"学习了解,是Static构建方法The SEMI-NCA Algorithm的改进版本

    • The Depth-Based Search Algorithm

      可以从论文"An Experimental Study of Dynamic Dominators"学习了解,最新LLVM版本实现了该算法

1.5 Loop and SCC

编译器和反编译器在控制流分析时,需要处理循环结构,循环结构最普遍的是流图中的SCC(强连通分量),大部分数据结构书籍中的图论都有介绍SCC(强连通分量)。

1.6 Reducible

可归约性是流图的一个非常重要的性质,假设对流图进行若干变换,该变换将子图蜕化为单个结点,归约成更简单的若干子图,如果一系列的转化最终能够把流图归约成单个结点,则称这个流图是可归约的。某些控制流模式会使得流图不可约,大概有以下几种方法处理不可约流图:

  • 1.6.1 Node Spilt

    因为传统的结点分割很容易造成流图的结点个数指数级增长,不推荐使用这种方法。
    可以从论文"Controlled Node Splitting"和论文Handling Irreducible Loops: Optimized Node Splitting vs. DJ-graphs学习非传统方法的结点分割。

  • 1.6.2 CFG Flattening

    CFG Flattening(控制流平坦化),虽然常用于代码混淆,但实际上也是把不可约流图转化成可归约流图的一种方法。

  • 1.6.3 DataFlow Analysis

    对于不可约流图,还可以进行数据流分析,该内容不在本文讨论范围。

1.7 Region

由于历史原因,区间分析Region Analysis也称为Interval Analysis。区间分析将流图划分成各种类型的区域,使每一个区域蜕化成一个抽象节点,最终得到层次化、嵌套的抽象流图,该流图产生一棵控制树。

  • 1.7.1 T1-T2 Interval

    最简单、最早的区间分析,可从鲸书“高级编译器的设计与实现”第7章控制流分析了解详情。

  • 1.7.2 Maximal Interval

    区间分析使用极大区间,忽略不可归约区域,可从鲸书“高级编译器的设计与实现”第7章控制流分析了解详情。

  • 1.7.3 Minimal Interval

    更为现代的区间分析,有考虑不可归约区域,可从鲸书“高级编译器的设计与实现”第7章控制流分析了解详情。

1.8 Structural

1980年Sharir的论文"Structural Analysis: A New Approach to Flow Analysis in Optimizing Compilers"定义了结构化分析。
结构化分析是区间分析的增强版本,比区间分析能分析更多的区间类型。可从鲸书“高级编译器的设计与实现”第7章控制流分析了解该传统结构化分析。

1.9 PST/RPST

区间分析或结构化分析只能处理SESE(Single-Entry-Single-Exit) 区间类型的流图,PST/RPST就是构建只包含SESE(Single-Entry-Single-Exit) 区域类型的控制树的一种方法

  • 1.9.1 PST

    可以从论文"The Process Structure Tree"了解学习

  • 1.9.2 RPST

    • Graph Decomposition

      区间分析或结构化分析的第一步骤和图论中的画图类似,都是要划分流图。图论的画图把流图划分成每一个最小组件,该最小组件称为Triconnected Components,有效表示Triconnected Components的方法是用SQPR-Tree表示。可以从论文"Application of SPQR-Trees in the Planarization Approach for Drawing Graphs"了解学习

    • RPST

      RPST树就是一棵层次化、每个叶子节点都是一个SESE(Single-Entry-Single-Exit) 区间类型的控制树。可以论文"The Refined Process Structure Tree" 和论文"Simplified Computation and Generalization of the Refined Process Structure Tree"了解学习,开源库codebase能构建RPST树


2. 反编译器

反编译器进行控制流结构恢复时,所使用的算法很多来自于编译器的控制流分析。

2.1 CFG+Dominators

反编译器的控制流程图比编译器控制控制流程图结点数量更多,分析时需要更多的时间和内存。早期LLVM版本构建支配树使用静态方法Lengauer-Tarjan算法,最新LLVM版本构建支配树已使用动态方法Depth-Based Search算法。

2.2 Reducible

反编译的不可归约流图比编译器的不可约流图数量和类型更多。

  • 2.2.1 Node Spilt

    teavm,实现了论文"Handling Irreducible Loops: Optimized Node Splitting vs. DJ-graphs"的结点分割算法。

  • 2.2.2 CFG Flattening

    编译器的不可约流图通常是多入口的循环,但反编译器的不可约流图有更多的类型,LLVM源码目录lib/Target/WebAssembly/WebAssemblyFixIrreducibleControlFlow的实现类似于用控制流平坦化处理不可约循环。

  • 2.2.3 DataFlow Analysis

    数据流分析不在本文讨论范围。


2.3 Analysis

反编译器的控制流分析有迭代控制流分析、区间分析和结构化分析三种方法。

2.4 Iterative Analysis

迭代分析主要有以下三种方法:

  • 2.4.1 Simple

    最简单的控制流分析,根据模式进行归约合并,一些反编译器会使用编译器前端语法分析的一些方法。

  • 2.4.2 CFG + Dominator

    最基础的控制流分析,虽然用Simple的方法可以归约合并控制流图,但是有一些缺陷:比如假设有一个控制流的基本块,它的前驱代码位置不在它的前面,它的后继代码位置不在它的后面,则Simple方法根据模式归约合并将会出错,但如果是使用了控制流图和支配树的方法进行归约合并将不会出错。

    开源反编译Retdec就使用了控制流图和支配树的方法进行控制流图重建恢复,问题在于它处理不可约流图时所使用的结点分割是传统的结点分割,导致反编译后的源码膨胀率严重,随便测试了一个百K左右的二进制文件的反编译,最后竟然生成了3-4M的c源码。

  • 2.4.3 DFS parenthesis

    DFS parenthesis特性,可以从算法导论第22章了解。开源反编译器boomerang就是用DFS parenthesis进行控制流分析。

2.5 Region Analysis

区间分析比迭代分析更有效。

  • 2.5.1 T1-T2 Interval

    axtor,采用论文"Controlled Node Splitting"的结点分割算法和T1-T2结构化分析进行控制流图重建
    fernflower,开源Java反编译器,采用传统的结点分割和T1-T2结构化分析进行控制流图重建

  • 2.5.2 Maximal Interval

    dcc是第一个使用极大化区间分析(Maximal Interval)的开源反编译器,没有考虑不可归约图,只支持16位二进制程序反编译,当然扩展支持32位/62位二进制程序反编译也不会太困难。

  • 2.5.3 Minimal Interval

    通常对不可约非正常区域,极小化区间分析方法采用将一个公共节点作为必经节点,其它节点边就必须要从控制流程图删除,并插入goto语句,另外,一些正常区域由于编译优化或代码混淆的原因,匹配规则不确定,也是无法归约的,同样是插入goto语句。

    eclipse omr的区间分析实现不知道是不是极小化区间分析。

2.6 Structural Analysis

结构化分析是反编译器控制流分析最主要的分析方法。

  • 2.6.1 Sharir Structural

    鲸书“高级编译器的设计与实现”第7章控制流分析有详细说明

  • 2.6.2 Zadeck Structural

    Zadeck StructuralGCC补丁,并没有相关论文,使用gcc后端IR RTL,由于它是用于编译器,而编译器比反编译器拥有更多的信息,因此如果要把该算法用于反编译器需要一些修改

  • 2.6.3 Hex-Rays Structural

    IDA Pro反编译插件Hex-Rays Decompiler是反编译器的事实标准,它的结构化分析算法应该和sharir或zadeck的算法有些类似,只是对不可约非正常区域的处理方法有些不同。第14章会不完全探讨它的实现

2.7 PST/RPST Analysis

LLVM的Region Analysis,也是SESE(Single-Entry-Single-Exit) Region Analysis,源码注释了说明其最基本的思路来自于PST/RPSTLLVM源码目录lib/Transform/Scalar下的StructurizeCFG源文件是算法的具体实现,好像是来自早期LLVM源码目录lib/Target/AMDGPU下的AMDGPUMachineCFGStructurizer源文件。

注意:上传附件及图片大小不得大于30M。

⚠️ 版权声明:
本博客所有内容(含教程、源码、工具)仅供个人技术学习与研究交流使用,严禁商用、倒卖、二次分发及非法用途
未经作者书面授权,任何组织或个人不得转载、复制或用于其他平台,违者将追究相关责任。

0 0 0 举报
复制成功