代码混淆技术是保护软件不被篡改的最有效的方法之一,将计算机程序的代码转换成一种与原始功能相同但更难以阅读和理解的形式,其目的不在于阻止逆向工程,而在于加大逆向的难度。
一、指令混淆方式
指令混淆是一种典型的代码混淆方式,可以进一步分为指令替换、指令加花等,目的是向程序中添加一些无用的信息,这些信息不会改变程序的行为,但是可以将原始的程序隐藏在不相关的代码中来干扰逆向工程师的分析。
1.传统的指令加花:
- 一种是直接在源代码中间插入一些无关的变量,使得代码更加复杂,迫使逆向工程师花费更高的代价来删除无用的指令。
- 一种是在指令的某些位置插入一些不完整的字节,导致反汇编出错。
缺点:容易被反汇编器或代码优化器识别并还原或删除,且往往针对于特定编程语言和机器平台,不具有通用性。
2.基于llvm的指令加花:
- 1.将待混淆的源程序转换为llvm中间表示文件,并提取函数、原始基本块和指令信息.
- 2.对每一个原始基本块内的指令进行依赖分析,按照指令间的依赖关系将原始基本块内的指令拆分为若干指令依赖集合.
- 3.在若干指令集合之间插入叠加跳转指令.
- 4.将混淆后的中间表示文件转换为目标平台的可执行文件.
二、LLVM是什么?
LLVM是一套框架+基于框架的一些编译器,是当下很先进的一套编译系统。特别对于C/C++/Objective-C等语言。LLVM项目是模块化、可重用的编译器以及工具链技术的集合。
1.传统的编译器架构:
- Frontend:前端,词法分析、语法分析、语义分析、生成中间代码
- Optimizer:优化器,中间代码优化
- Backend:后端,生成机器码
2. LLVM架构:
- 不同的前端后端使用统一的中间代码LLVM IR
- 需要支持新的编程语言,只需要实现一个新的前端和后端
- LLVM IR可以dump出来成为可阅读的文本形式,模块化的设计比较好
C/C++/OC语言LLVM架构的主要部件:
- Clang:前端,词法分析、语法分析、语义分析、生成中间代码
- LLVM Proper:将IR 转换为 IR的Pass
- 后端:生成实际的机器码,不需要接触这部分。
3. 什么是Clang:
LLVM项目的一个子项目,基于LLVM架构的C/C++/Objective-C编译器前端。
Clang与LLVM关系:
4. OC源文件的编译过程:
源代码(c/c++)经过clang–> 中间代码(经过一系列的优化,优化用的是Pass) –> 机器码
- 找到main.m文件
- 预处理器,处理include、import、宏定义
- 编译器编译,编译成ir中间代码
- 后端,生成目标代码
- 汇编
- 链接其他动态库静态库
- 编译成适合某个架构的代码
5. LLVM IR:
LLVM IR有3种表示形式(本质是等价的)
- text:便于阅读的文本格式,类似于汇编语言,拓展名.ll $ clang -S -emit-llvm main.m
- memory:内存格式
- bitcode:二进制格式,拓展名.bc, $ clang -c -emit-llvm main.m
IR基本语法:
- 注释以分号 ; 开头
- 全局标识符以@开头,局部标识符以%开头
- alloca,在当前函数栈帧中分配内存
- i32,32bit,4个字节的意思
- align,内存对齐
- store,写入数据
- load,读取数据
6. LLVM构建和安装:
1.选择下载全部的组件,也就是 lvm-project monorepo source code 这个代码。
2.同级目录中创建两个目录, b 用来存放构建需要的文件,i 是最终的安装目录。
3.进入 b 目录,下面开始构建,我选择构建 Release 版本,开启 Assertion。并且同时需要构建 clang:
cd b cmake ../llvm-project-11.0.0/llvm/ -DCMAKE_BUILD_TYPE=Release -DLLVM_ENABLE_ASSERTIONS=ON -DLLVM_ENABLE_PROJECTS=clang cmake --build . -- -j 8
构建的时间会很长,取决于你电脑的配置,一般需要三四十分钟以上(不得不夸下 Apple M1,20 来分钟搞定)。
构建成功之后进行安装,这里建议配置一个安装目录的环境变量,方便后续使用
# 当前目录 b
# 进入上一级目录
cd ..
export LLVM_HOME=`pwd`/i
cd b
cmake -DCMAKE_INSTALL_PREFIX=$LLVM_HOME -P cmake_install.cmake
LLVM安装验证:
cd /tmp
/tmp $LLVM_HOME/bin/clang -v
clang version 11.0.0
Target: arm64-apple-darwin20.3.0
Thread model: posix
InstalledDir: /Users/sines/Code/LLVMs/11.0.0/i/bin
/tmp $LLVM_HOME/bin/clang --sysroot /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX11.1.sdk /tmp/test.c
/tmp ./a.out
Hello World!
三、LLVM代码混淆:
1. LLVM Obfuscator是一款工业级别的代码混淆器
LLVM Obfuscator是一个基于LLVM框架实现的一个开源代码混淆器,整个项目包含了三个相对独立的LLVM pass, 每个pass实现了一种混淆方式,通过这些混淆手段,可以模糊原程序的流程或者某一部分的算法,给逆向分析带来一些困难。
由于上述的三个pass是基于LLVM IR实现的, 因此从理论上来说, 这种混淆器是支持世界上任何一种语言和机器架构的。
四、应用与实践的参考:
1.libclang、libTooling
官方参考:
https://clang.llvm.org/docs/Tooling.html
应用:语法树分析、语言转换等
2.Clang插件开发
官方参考:
https://clang.llvm.org/docs/ClangPlugins.html
https://clang.llvm.org/docs/ExternalClangExamples.html
https://clang.llvm.org/docs/RAVFrontendAction.html
应用:代码检查(命名规范、代码规范)等
3.Pass开发
官方参考:
https://llvm.org/docs/WritingAnLLVMPass.html
应用:代码优化、代码混淆等
4.开发新的编程语言
https://llvm-tutorial-cn.readthedocs.io/en/latest/index.html
https://kaleidoscope-llvm-tutorial-zh-cn.readthedocs.io/zh_CN/latest/
总结:
LLVM 非常庞大,还有一些没有涉及的主题:
- LLVM 提供的大量经典编译器分析。
- 通过拓展后端来生成任何特殊的机器指令,就像架构师经常想做的那样。
- 利用debug info,可以连接回与 IR 对应的源码位置。
- 为 Clang编写前端插件。
- 本文作者: Grx
- 本文链接: https://ruixiaoguo.github.io/Grx.github.io/Grx.github.io/2023/10/03/iOS基于LLVM + Clang混淆/
- 版权声明: 本博客所有文章除特别声明外,均采用 MIT 许可协议。转载请注明出处!