代码混淆技术系列
代码混淆技术之控制流平坦化
00 分钟
2022-12-21
2024-11-11
type
status
date
slug
summary
tags
category
icon
password

控制流平坦化是什么

控制流平坦化指的是将正常控制流中基本块之间的跳转关系删除,用一个集中 的分发块来调度基本块的执行顺序
如图所示,右边的图就是通过控制流平坦化实现的(因为看起来很平坦,所以被称为控制流平坦化)
 
notion image

控制流平坦化流程结构

大致分为四个结构
  1. 入口块(进入函数第一个执行的基本块)
  1. 主分发块与子分发块(:负责跳转到下一个要执 行的原基本块)
  1. 原基本块(混淆之前的基本块,真正完成程序工 作的基本块)
  1. 返回块(返回到主分发块)
notion image

程序流程混淆效果

notion image
  • 当我们分析正常的控制流时,我们能很容易的分析出程序的执行顺序,以及每一段代码完成的工作如何执行(一段代码可能由多个互相关联的基本块组成),进而掌握整个程序的逻辑
  • 当我们分析平坦化后的控制流时,在不知道基本块执行顺序的情况下,分别对每一个基本块进行分析是很难的,而如果要得知每一个基本块的执行顺序,必须分析分发块的调度逻辑
  • 在实际分析中当函数比较复杂的时候,通过手动分析分发块还原原基本块的执行顺序是非常复杂的(比如上面这个图)

程序代码混淆效果

控制流平坦化混淆后的伪代码,while + switch 结构对应平坦化后的控制流代码结构
notion image

代码实现

实现步骤

大致分为 5 步
  1. 保存原基本块
  1. 创建分发块和返回块
  1. 实现分发块调度
  1. 实现调度变量自动调整
  1. 修复 PHI 指令和逃逸变量

保存原基本块

  • 将除入口块以外的以外的基本块保存到 vector 容器中,方便后续处理(原因可以看上一篇的 LLVM 基本块分割里面说的有)
  • 如果入口块的终结指令是条件分支指令,则将该指令单独分离出来作为一个基本块,加入到 vector 容器的最前面
notion image
代码实现:

创建分发块和返回块

  • 除了原基本块之外,我们还要续创建一个分发块来调度基本块的执行顺序。并建立入口块到分发块的绝对跳转。
  • 再创建一个返回块,原基本块执行完后都需要跳转到这个返回块,返回块会直接跳转到分发块。
notion image
代码实现:

实现分发块调度

  • 在入口块中创建并初始化 switch 变量,在调度块中插入 switch 指令实现分发功能
  • 将原基本块移动到返回块之前,并分配随机的 case 值,并将其添加到 switch 指令的分支中
notion image
代码实现:

实现调度变量自动调整

  • 在每个原基本块最后添加修改 switch 变量值的指令,以便返回分发块之后,能够正确执行到下一个基本块
  • 删除原基本块末尾的跳转,使其结束执行后跳转到返回块
    • notion image
代码实现:

修复 PHI 指令和逃逸变量

  • PHI 指令的值由前驱块决定,平坦化后所有原基本块的前驱块都变成了分发块,因此 PHI 指令发生了损坏
  • 逃逸变量指在一个基本块中定义,并且在另一个基本块被引用的变量。在原程序中某些基本块可能引用之前某个基本块中的变量,平坦化后原基本块之间不存在确定的前后关系了(由分发块决定),因此某些变量的引用可能会损坏
  • 修复的方法是,将 PHI 指令和逃逸变量都转化为内存存取指令
代码实现:

最终效果

没有加基本块分割实现的控制流平坦化
notion image
加了基本块分割后
notion image
 
 

参考于

上一篇
代码混淆技术之虚假控制流
下一篇
恶意代码实战分析(二)