IOS逆向-LLVM代码混淆
Posted GY-93
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了IOS逆向-LLVM代码混淆相关的知识,希望对你有一定的参考价值。
1 LLVM
- 什么是LLVM
- 官网https://llvm.org
- LLVM项目是模块化,可重用的编译器以及工具链技术的集合,LLVM不是首字母的缩写,它是项目名称
1.1 传统编译架构
- Frontend:词法分析、语法分析、语义分析、生成中间代码
- Optimizer:优化器(中间代码)
- Backend:后端,生成机器码
1.2 LLVM的编译架构
- 不同的前端后端使用统一的中间代码
LLVM Intermediate Representation(LLVM IR)
- 如果需要支持一种新的编程语言,那么只需要实现一个新的前端
- 如果需要支持一种新的硬件设备,那么只需要实现一个新的 后端
- 优化阶段是一个通用的阶段,它针对的是统一的LLVM IR,不论是支持新的变成语言,还是支持新的硬件设备,都不需要对优化阶段做修改
- 相比之下,GCC的全段和后端没分的太开,前端和后端耦合在一起,所以GCC为了支持一门新的语言,或则为了支持一个新的目标平台,就变的特别困难
- LLVM现在被作为实现各种金泰和运行编译语言的通用基础(GCC家族、Java、.NET、Python、Ruby、Scheme、Haskell、D等)
1.3 Clang
1.3.1 什么是Clang
- 什么是Clang?
- LLVM项目的一个子项目
- 基于LLVM架构的C/C++/Objective-C编译器
前端
- 官网:http://clang.llvm.org/
- 相比于GCC,Clang具有如下优点:
- 编译速度快:在某些平台上,Clang的编译速度显著的快过GCC(Debug模式下编译OC速度比GGC快3倍)
- 占用内存小:Clang生成的AST所占用的内存是GCC的五分之一左右
- 模块化设计:Clang采用基于库的模块化设计,易于 IDE 集成及其他用途的重用
- 诊断信息可读性强:在编译过程中,Clang 创建并保留了大量详细的元数据 (metadata),有利于调试和错误报告
- 设计清晰简单,容易理解,易于扩展增强
1.3.2 Clang与LLVM
- 广义上的LLVM: 整个LLVM架构
- 狭义的LLVM:LLVM后端(代码优化、目标代码生成等)
2. OC源文件的编译过程
2.1 编译过程和预编译
#include <stdio.h>
#define AGE 40
int main(int argc, const char * argv[]) {
int a = 10;
int b = 20;
int c = a + b + AGE;
return 0;
}
-
命令行查看编译的过程:
clang -ccc-print-phases main.m
-
查看preprocessor(预处理)的结果:
clang -E main.m
我们可以看到预编译做了很多事情,然后宏定义在预编译期就被替换了
2.2 词法分析
- 词法分析,生成Token:
clang -fmodules -E -Xclang -dump-tokens main.m
void test(int a, int b) {
int c = a + b - 3;
}
2.3 语法树-AST
- 语法分析,生成语法树(AST,Abstract Syntax Tree):
clang -fmodules -fsyntax-only -Xclang -ast-dump main.m
2.4中间代码(LLVM IR)
- LLVM IR有3种表示形式(但本质是等价的,就好比水可以有气体、液体、固体3种形态)
- text:便于阅读的文本格式,类似于汇编语言,拓展名.ll,
clang -S -emit-llvm main.m
- memory:内存格式
- bitcode:二进制格式,拓展名.bc,
clang -c -emit-llvm main.m
main.ll的代码:
- IR基本语法:
- 注释以分号 ; 开头
- 全局标识符以@开头,局部标识符以%开头
alloca
,在当前函数栈帧中分配内存i32
,32bit,4个字节的意思align
,内存对齐store
,写入数据load
,读取数据
- 官方语法参考:https://llvm.org/docs/LangRef.html
3. LLVM源码
3.1 LLVM的源码下载
- 下载LLVM:
git clone https://git.llvm.org/git/llvm.git/
- 下载clang:
cd llvm/tools
(需要进入LLVM的源码目录下的Tools文件夹,然后下载clang源码),然后执行:git clone https://git.llvm.org/git/clang.git/
- clang编译器其实Xcode是带有的,我们可以查看下:
3.2 源码编译
3.2.1 ninja编译方式
- 安装cmake和ninja(先安装brew,https://brew.sh/)
brew install cmake
brew install ninja
- ninja如果安装失败,可以直接从github获取release版放入
【/usr/local/bin】
中 - 在LLVM源码同级目录下新建一个
【llvm_build】
目录(最终会在【llvm_build】
目录下生成【build.ninja
】,表示成功)cd llvm_build
cmake -G Ninja ../llvm -DCMAKE_INSTALL_PREFIX=LLVM的安装路径
:编译Ninja模板- 更多cmake相关选项,可以参考:https://llvm.org/docs/CMake.html
- 依次执行编译、安装指令
ninja
- 编译完毕后, 【llvm_build】目录大概 21.05 G(仅供参考)
ninja install
- 安装完毕后,安装目录大概 11.92 G(仅供参考)
3.2.2
- 也可以生成Xcode项目再进行编译,但是速度很慢(可能需要1个多小时)
- 在llvm同级目录下新建一个【llvm_xcode】目录
cd llvm_xcode
cmake -G Xcode ../llvm
生成xcode项目
4 应用和实践
- libclang、libTooling
- 官方参考:https://clang.llvm.org/docs/Tooling.html
- 应用:
语法树分析、语言转换等
- Clang插件开发
- Pass开发
- 官方参考: https://llvm.org/docs/WritingAnLLVMPass.html
- 应用:
代码优化、代码混淆
等
- 应用:
- 官方参考: https://llvm.org/docs/WritingAnLLVMPass.html
开发新的编程语言
5 clang插件开发
5.1 clang插件开发1 – 插件目录
- 在
【clang/tools】
源码目录下新建一个插件目录,假设叫做【mj-plugin】
- 在【clang/tools/CMakeLists.txt】最后加入内容:
add_clang_subdirectory(mj-plugin),小括号里是插件目录名
5.2 clang插件开发2 – 插件必要文件
- 在【mj-plugin】目录下新建一个【CMakeLists.txt】,文件内容是:
add_llvm_loadable_module(MJPlugin MJPlugin.cpp)
MJPlugin
是插件名,MJPlugin.cpp
是源代码文件
5.3 clang插件开发3 – 编写插件源码
- 【MJPlugin.cpp】参考
5.4 clang插件开发4 – 编译插件
- 利用cmake生成的Xcode项目来编译插件(第一次编写完插件,需要利用cmake重新生成一下Xcode项目)
- 插件源代码在【Sources/Loadable modules】目录下可以找到,这样就可以直接在Xcode里编写插件代码
- 选择MJPlugin这个target进行编译,编译完会生成一个动态库文件
5.5 clang插件开发5 – 加载插件
- 在Xcode项目中指定加载插件动态库:Build Settings > OTHER_CFLAGS
-Xclang -load -Xclang 动态库路径 -Xclang -add-plugin -Xclang 插件名称
5.6 clang插件开发6 – Hack Xcode
- 首先要对Xcode进行Hack,才能修改默认的编译器
- 下载【XcodeHacking.zip】,解压,修改【HackedClang.xcplugin/Contents/Resources/HackedClang.xcspec】的内容,设
置一下自己编译好的clang的路径
- 下载【XcodeHacking.zip】,解压,修改【HackedClang.xcplugin/Contents/Resources/HackedClang.xcspec】的内容,设
- 然后在XcodeHacking目录下进行命令行,将XcodeHacking的内容剪切到Xcode内部
sudo mv HackedClang.xcplugin
xcode-select-printpath/../PlugIns/Xcode3Core.ideplugin/Contents/SharedSupport/Developer/Library/Xcode/Plug-ins
sudo mv HackedBuildSystem.xcspec
xcode-select-printpath/Platforms/iPhoneSimulator.platform/Developer/Library/Xcode/Specifications
5.7 clang插件开发7 – 修改Xcode的编译器
5.8 clang插件开发8 – 编译项目
- 编译项目后,会在编译日志看到MJPlugin插件的打印信息(如果插件更新了,最好先Clean一下项目)
5.9 clang插件开发9 – 更多
- 想要实现更复杂的插件功能,就需要利用clang的API针对语法树(AST)进行相应的分析和处理
- 关于AST的资料
5.10 提示警告、错误信息
5.11 推荐书籍
6 代码混淆
6.1 基本概念
- 什么是加固: 加固是为了增加应用的安全性,防止应用被破解、盗版、二次打包、注入、反编译等
- 常见的加固方式有
- 数据加密(字符串、网络数据、敏感数据等)
- 应用加壳(二进制加密)
- 代码混淆(类名、方法名、代码逻辑等)
- …(不同平台还有不同的做法)
6.2 ios中代码混淆
6.2.1 基本介绍
- iOS程序可以通过class-dump、Hopper、IDA等获取类名、方法名、以及分析程序的执行逻辑
- 如果进行代码混淆,可以加大别人的分析难度
- iOS的代码混淆方案
- 源码的混淆
- 类名
- 方法名
- 协议名
- …
- LLVM中间代码IR的混淆(容易产生BUG)
- 自己编写Pass
- ollvm:https://github.com/obfuscator-llvm/obfuscator
- 源码的混淆
6.2.2 源码的混淆 - 通过宏定义混淆方法名、类名
-
使用pch文件中定义的宏定义来混淆代码,但是pch文件中是不会替换xib、storyboard中的 名称的,需要手动修改
-
注意点
- 不能混淆系统方法
- 不能混淆init开头的等初始化方法
- 混淆属性时需要额外注意set方法
- 如果xib、storyboard中用到了混淆的内容,需要手动修正
- 可以考虑把需要混淆的符号都加上前缀,跟系统自带的符号进行区分
- 混淆过多可能会被AppStore拒绝上架,需要说明用途
-
建议
- 给需要混淆的符号加上了一个特定的前缀
6.2.3 iOS-class-guard
- 第三方工具
- 下载链接: https://github.com/Polidea/ios-class-guard
- 它是基于class-dump的扩展
- 用class-dump扫描出可执行文件中的类名、方法名、属性名等并做替换,会更新xib和storyboard的名字等等
- 用法
- brew install ios-class-guard
- ios-class-guard [options]
- 常用参数
--sdk-root <path>
:用于指定SDK路径,如果是模拟器SDK,一般路径就是/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator.sdk
--sdk-ios
:相当于指定SDK路径为真机设备SDK-X <path>
:用于指定xib、storyboard所在目录,它会递归搜索-O <path>
:生成的混淆头文件路径-m <path>
: 符号映射表(默认是symbols.json)
6.2.4
- 很多时候,可执行文件中的字符串信息,对破解者来说,非常关键,是破解的捷径之一
- 为了加大破解、逆向难度,可以考虑对字符串进行加密
- 字符串的加密技术有很多种,可以根据自己的需要自行制定算法
- 这里举一个简单的例子
- 对每个字符进行异或(^)处理
- 需要使用字符串时,对异或()过的字符再进行一次异或(),就可以获得原字符
以上是关于IOS逆向-LLVM代码混淆的主要内容,如果未能解决你的问题,请参考以下文章