Ollvm 初探

在现在很多保护手段中,有许多程序使用O-LLVM来保护代码,虽然效率低下,但是能增加逆向的难度。接下来通过分析0ctf中的choices来简单了解下O-LLVM。


0x1.LLVM

O-LLVM是基于LLVM上开发的

什么是LLVM?

LLVM是一个好用、好玩,而且超前的系统语言(比如C和C++语言)编译器。

当然,因为LLVM实在太强大,你会听到许多其他特性(它可以是个JIT;支持了一大批非类C语言;还是App Store上的一种新的发布方式等等)。这些都是真的,不过就这篇文章而言,还是上面的定义更重要。

LLVM组成

前端,流程(Pass),后端

  • 前端获取你的源代码然后将它转变为某种中间表示。这种翻译简化了编译器其他部分的工作,这样它们就不需要面对比如C++源码的所有复杂性了。作为一个豪迈人,你很可能不想再做这部分工作;可以不加改动地使用Clang来完成。
  • “流程”将程序在中间表示之间互相变换。一般情况下,流程也用来优化代码:流程输出的(中间表示)程序和它输入的(中间表示)程序相比在功能上完全相同,只是在性能上得到改进。这部分通常是给你发挥的地方。你的研究工具可以通过观察和修改编译过程流中的IR来完成任务。
  • 后端部分可以生成实际运行的机器码。你几乎肯定不想动这部分了。

所以O-LLVM是在LLVM 的Pass中进行开发的


0x2.O-LLVM混淆

  • 通过重写runOnFunction方法,LLVM会在编译每个函数的时候先唤起这个方法。在O-LLVM中,这里调用flatten方法进行了混淆。
  • 在flatten方法中,获取到一个SCRAMBLERKEY,这个SCRAMBLERKEY既可以通过参数给定,也可以随机取值。接下来会用这个SCRAMBLER_KEY,变换后调用scramble32去给case id:中的id赋值。

unsigned CryptoUtils::scramble32(const unsigned in, const char key[16])
参数一:不同BB的ID值
参数二:Key值

  • 将函数分为代码块(BB),处理分支语句并增加switch,处理原始代码分支。

0x3.实例分析

在这里我们通过0ctf中的choices对处理的分支进行分析。

出题人思路(猜测分析)
  • 程序对一段56-byte变换后取SHA256值校验。
  • scanf获取用户输入,通过输入来处理分支,对56-byte进行不同的变换。
  • 变换部分是通过O-LLVM处理的。
异同点

编译对Pass端处理使用O-LLVM,但在此基础上有以下修改点。 1. 判断是否是main函数,并不对main函数进行混淆
2. 给First BB插入scanf函数。

  1. 获取每一个BB的ID值,并传递给scramble32计算case ID值。

由以上得知,我们输入的值就是BB ID计算出来的值,程序正确执行BB的顺序就是从0-n计算出来的值。

解题

直接修改暴力计算scramble32后面每一个值.

Code

0xN.总结

并不会llvm中间层自定义流程编写,所以理解起源代码比较吃力。但分析下来对O-LLVM有一定的了解

参考

为什么人人都该懂点LLVM 0CTF 2017 Quals: Choices Flatten算法实现

comments powered by Disqus