Skip to content

Files

Latest commit

 Cannot retrieve latest commit at this time.

History

History
150 lines (113 loc) · 4.74 KB

algo.md

File metadata and controls

150 lines (113 loc) · 4.74 KB

算法介绍

总体思路

reccon 用一系列图变换操作,把控制流图(CFG)转为等价的抽象语法树(AST)。在转化过程中,reccon 通过引入新变量和复制节点,消除 goto 语句。

算法大体分为“循环结构化”和“AST转化”两步。

图中可能出现三种非结构化的形式:循环有多个入口、循环有多个出口、异常选择路径。前两种情况在“循环结构化”中处理,最后一种情况在“AST转化”时处理。

识别循环

利用 DFS 识别每个循环的入口和循环包含的节点。

DFS 时,记录每个点的 DFS 序数(先序和后序),把边分三类(前向边、后向边、横插边)。所有前向边构成图的生成树。

循环头:后向边的目标节点。

循环包含的节点:与循环头 h 双向可达的节点(不经过 h 在生成树上的任何父节点)。

循环之间的包含关系应是树形结构。按 DFS 后序遍历,可以由内到外访问所有循环。

注意! 循环识别结果与 DFS 遍历顺序相关,reccon 总是从图的入口开始,先访问 false 分支再访问 true 分支。

循环结构化

根据 Conversion of Unstructured Flow Diagrams to Structured Form 论文中提出的方法,把图中非结构化的循环转为结构化的循环,即每个循环只有一个入口和至多一个出口。

入口:从一个循环外的点连向一个循环内的点。连向循环头节点的是主入口,其它入口为异常入口。

出口:从一个循环内的点连向一个循环外的点。

多出口转单出口

对于出口边 (x_i, X_i),原图

     |
-->--|
|    |
|   x_0 --> X_0
|    |
|   x_1 --> X_1
|    |
|   x_n --> X_n
|    |
---<--

添加新变量 b,记录从循环退出的位置,转为

     |
    b=-1
-->--|
|   b!=-1 -->----------|
|    |                 |
|   x_0 -> b=0 -->|   b==0 --> X_0
|    |            |    |
|   x_1 -> b=1 -->|   b==1 --> X_1
|    |            |    |
|   x_n -> b=n -->|  (b==n) -> X_n
|    |            |
---<------------<--

多入口转单入口

只保留主入口。

对于异常入口 (E_i, e_i),原图

         |
         |-<--
         |   |
        pre  |
         |   |
E_i --> e_i  |
         |   |
       succ  |
         |   |
         ->--|

递归复制 e_i 即其后继节点(到主入口为止),转为

            |
       -->--|-<--
       |    |   |
E_i    |   pre  |
 |     |    |   |
e_i    |   e_i  |
 |     |    |   |
succ-->|  succ  |
            |   |
            ->--|

AST 转化

按从内到外的顺序遍历每个循环,分别进行 AST 转化。这样每个循环内部包含的小循环已经被处理完了,只需要单个循环。

对于单个循环,把所有连向循环头的边改为 continue,把出口边改为 break。这样单循环图就转化成了有向无环图(DAG)。

对于 DAG,采用 Decompilation of Conirol Structures by Means of Graph Transformations 论文中的方法,逐步把特定结构的子图替换成新节点,直到整个图变成一个点。

上述论文没有处理异常选择路径,因此 reccon 在其基础上,加入短路条件优化,来缓解这一问题。

即如下形式的子图

cond1 ------------>|
  |                |
(mid1)           (mid2)
  |                |
  |<- (mid3) <-- cond2
  |                |
 br1              br2

添加临时变量 p,转为

if (cond1) {
    mid1;
    p = true;
}
else {
    mid2;
    p = cond2;
    if (p)
        mid3;
}
if (p)
    br1;
else
    br2;

(当 mid1, mid2, mid3 都不存在时,应转为 cond1 or cond2

对于短路条件优化仍没法处理的异常选择路径,使用 A Comb for Decompiled C Code 论文中的方法,复制某些节点,来消除异常选择路径:若存在点 N,位于某条件节点 C 到 “C 的最近反向支配点”的路径上,且存在不被 C 支配的点 X 和边 (X,N),则复制点 NN',把 (X,N) 替换为 (X,N')

自动化测试

给定一个 CFG 和控制流恢复的结果,reccon 可以自动化验证结果是否正确。这样就可以随机生成一些图来做测试了。

验证方法是,同步在 CFG 和 AST 上模拟执行,遍历所有路径,检查所有状态下 AST 节点是否恰好与 CFG 的节点对应。

模拟执行过程中,维护当前在 CFG 和 AST 上所处的位置,和各个自定义变量的值。

遇到条件节点(对应条件语句),就拆分成两个状态,分别模拟条件为 false 和 true 的情形。

用哈希表记录所有模拟中出现的状态。如果有多个状态位于同一个 CFG 位置,且它们的所有变量的值都相同,则被认为是相同的状态,不再重复模拟。