编译器/反编译器中间语言转换过程如下:Target Dependent HIR → Target Independent HIR(Option) → Target Independent VMIR(Option) → Target Independent MIR → Target Independent LIR(Option) → Target Dependent LIR IDA Pro SDK 从7.1 版本开始,已经有部分中端MIR相关操作的API。(本文相关图片都来自官方博客Microcode in pictures )
从SDK可知基本块类型BasicBlock有以下定义:
一种情况是流图的Edge是隐式的,比如LLVM的流图是这种情况,IDA Pro的流图也是这种情况;一种是流图的Edge是显式的,GCC的流图就是这种情况。 基本块是用双向链表存储的。
一种情况是流图的Entry和Exit是Real Entry和Real Exit,比如LLVM的流图是这种情况;一种是流图的Entry和Exit是Virtual Entry和Virtual Exit,比如GCC的流图就是这种情况,IDA Pro的流图也是这种情况,控制流图有额外的Virtual Entry和Virtual Exit。
AT&T-like 语法,即第一个操作数是源操作数而不是目的操作数(Intel 语法第一个操作数是目的操作数),当前有72 条指令,指令也是用双向链表存储的。
熟悉了解DFS,Dominator,Strongly Connected Components等理论,可以使用Boost Graph库
经典的方法先计算Dominance Frontiers,然后构造SSA PHI,最新的实现方法是这篇论文:Simple and Efficient Construction of Static Single Assignment Form ,推荐关于静态单赋值SSA的书籍:
英文名:Static Single Assignment Book 作者: Zadeck
Abstract Interpretation ,静态程序分析通常需要它,数据流分析就是在它的基础上实现的。推荐关于数据流分析的书籍:
英文名:Data Flow Analysis– Theory and Practice Principles of Program Analysis 作者:Uday Khedker ,Amitabha Sanyal, Bageshri Sathe
Symbolic Execution ,通常用于动态程序分析,一些符号执行工具基于llvm、qemu、valgrind、intel pin等实现,但符号执行有一些缺陷:路径爆炸,一些复杂的语义比如递归、交互式程序、字符串、系统调用等相关API语义很难实现。
编译器或反编译器的优化、混淆等功能定义成Pass,用Pass Manager进行Pass管理。IDA Pro 框架集成了事件驱动框架,主Pass对应的事件可以认为反编译器的流水线的各个阶段,主Pass有多个副Pass,目前IDA反编译器SDK只有部分副官方Pass关闭,并不是所有的官方Pass都可以关闭。
反编译器可以是可配置Retargetable Decompiler,即对于不同的编译器编译、不同的操作系统、不同的ABI、不同的字节序(大小端)、不同的ISA等二进制程序,反编译器反编译类似于编译器编译,即反编译器/编译器前端转中端不同,反编译器/编译器中端和后端几乎可以是相同的。
代码反混淆最好是基于Target Independent IR。现代反编译器从中端Target Independent MIR 转化成后端Target Dependent LIR时,Region/Structural Analysis是控制流重建的主要方法。反编译器的结构化分析是编译器结构化分析增强版,编译器结构化分析当前主要实现有这些:Sharir Structural Analysis,Zadeck Structural Analysis,Eclipse OMR Structural Analysis等。
反编译器中端结构化分析需要对无环Acyclic Regions进行Condition Combine条件跳转合并归约,通常有两种方法:一种方法是根据规则进行模式匹配进行归约,该方法由于不可能覆盖所有规则,所以不可能完美归约合并;另外一种方法是用类似求解数据流分析问题的方法进行归约合并。对有环Cyclic Regions进行Loop Refinement,生成While、DoWhile、For等高级语言的循环语句特性。
IDA Pro7.2以前的版本的结构化分析好像是无法处理Multi Exits Regions,比如这种情况:前一个区域是Multi Exits Regions,当前区域是Cyclic Regions,则Multi Exits Regions的多个Condition可能会被归约成Loop Header,可能会生成的可读性非常差的后端For循环伪代码,看起来IDA Pro 7.2好像有改进了反编译器的结构化分析。IDA Pro事件hexrays_event_t中的hxe_structural所使用的control_graph_t *ct类型SDK并没有定义,并且hxe_structural在官方Pass Manager相对应的Pass也无法关闭,自定义重新实现官方反编译器的结构化分析比较困难。
虽然基于反编译器后端Target Dependent LIR的控制流分析可以进行反混淆,但需要把它原来的中端Target Independent MIR 结构化分析后生成的的后端带有高级语言特性的Target Dependent LIR转化成没有高级语言特性,只有goto和if的Target Independent LIR,再进行控制流分析/数据流分析优化来实现反混淆,最后再重新进行后端结构化分析生成带有高级语言语句特性的Target Dependent LIR,等同于重新实现了反编译器的中端和后端的控制流分析,太麻烦了,因此,为了使用反编译器官方的中端结构化分析,代码反混淆最好是基于反编译器中端Target Independent MIR 的控制流分析/数据流分析优化实现。
中端优化在进行控制流分析时,需要实现多个优化Pass,主要的反编译器或编译器共有的优化Pass大概有这些: Const Fold Const Propagation Inst Combine Inst Reassociate Peephole Copy Propagation Sparse Conditional Constant Propagation Jump Threading Load/Store Optimize Block Sink Common Subexpression Elimination Dead Code Elimination Register Coalescer
后端优化反编译器有独有的优化Pass。
软件实现的Soft Float浮点数操作识别或硬件用Vector/Simd指令实现的Float浮点数操作识别,除数是常量的除法识别等
OLLVM ,开源混淆编译器,有很多商用版本的混淆编译器基于它改进实现。本文探讨如何反混淆OLLVM编译的程序。
该混淆Pass是Pattern-Based Obfuscation Pass,Inst Combine/Inst Reassociate和Peephole编译器优化Pass可以反混淆该Pass。
首先,可以用符号执行 和SMT Solver 反混淆,当谓词表达式恒为真时,表达式SMT Solver求解有解,反转后SMT Solver求解无解;当谓词表达式恒为假时,表达式SMT Solver求解无解,反转后SMT Solver求解有解。其次,该Pass也是Pattern-Based Obfuscation Pass,可以认为该混淆Pass对应的反混淆优化Pass是Peephole。
控制流平坦化,源程序函数转换成FSM的一种方法,还可以用于反编译器的控制流分析,比如LLVM IR To JavaScript工具Emscripten实现的ReLooper。
它将源程序函数分成一个入口块和多个相邻的基本块(Case代码块),每个基本块都有相应编号,并且这些基本块都有相同的前驱块,前驱块可称之为主分发器,通过状态机的值来完成基本块的分发,每次执行哪个基本块由状态机的值来决定,而每个基本块最后会更新状态机的值,并且会替换原始的后继块。如果这些基本块替换原始的后继块后有相同的新后继块,该后继块共用一个Loop BackEdge跳回到主分发块,此新后继块可称为预处理块;如果这些基本块替换原始的后继块后的新后继块独自用Loop BackEdge跳回到主分发块,不共用一个Loop BackEdge,这种情况可能是原生混淆编译器实现或混淆编译器优化生成的结果。然后分发块根据更新后的这个值,再来决定执行哪个基本块,主分发块用Switch实现,而Switch的最终实现通常用二叉树比较If表示。
x86平台的控制流平坦化OLLVM生成的代码是有预处理块的,Switch Case Block共用一个Loop BackEdge;arm平台的控制流平坦化OLLVM生成的代码好像是没有预处理块的,Switch Case Block独自用一个Loop BackEdge。注意OLLVM的实现可能有BUG,简单的改进方法:由于LLVM的流图的Entry和Exit是Real Entry和Real Exit,可以Split Real Entry BasicBlock生成空的Single Virtual Entry,并调用UnifyFunctionExitNodes Pass生成Single Virtual Exit。
有以下这些反混淆方法:
[注意]传递专业知识、拓宽行业人脉——看雪讲师团队等你加入!
最后于 2019-4-10 04:19
被vasthao编辑
,原因: