iOS 逆向 day 18 GCC LLVM Clang 代码混淆

一、LLVM

1. 什么是 LLVM
  • 官网:https://llvm.org/
  • The LLVM Project is a collection of modular and reusable compiler and toolchain technologies.
  • LLVM 项目是模块化、可重用的编译器以及工具链技术集合
2.创始人
  • Chris Lattner,亦是 Swift 之父
3. 有些文章把 LLVM 当做 Low Level Virtual Machine (低级虚拟机)的缩写简称,官方开头描述如下
  • The name "LLVM" itself is no an acronym; it is the full name of the project.
  • “LLVM” 这个名称不是首字母缩写词;它是项目的全名

二、编译器架构

1. 传统编译器架构
传统编译器架构
  • Frontend:前端;词法分析、语法分析、语义分析、生成中间代码

  • Optimizer:优化器;中间代码优化

  • Backend:后端;生成机器码

2. LLVM 架构
LLVM 架构
  • 不同的前端后端使用统一的中间代码 LLVM Intermediate Representation (LLIR)
  • 如果需要支持一种新的编程语言,那么只需要实现一个新的前端
  • 如果需要支持一种新的硬甲设备,那么只需要实现一个新的后端
  • 优化阶段是一个通用的阶段,它针对的统一的 LLVM IR,不论是支持新的编程语言,还是支持新的硬件设备,都不需要对优化阶段做修改
  • 相比之下,GCC 的前端和后端没分得太开,前后端耦合在了一起。所以 GCC 为了支持一门新的语言,或者为了支持一个新的目标平台,就变得特别困难
  • LLVM 现在被作为实现各种静态和运行时编译语言的通用基础结构(GCC 家族、Java、.NET、Python、Ruby、Scheme、Haskell 等)

三、Clang

1. 什么是 Clang?
  • LLVM 项目的一个子项目
  • 基于 LLVM 架构的 C/C++/Objective-C 编译器前端
  • 官网:http://clang.llvm.org/
2. 相比于 GCC,Clang 有如下优点
  • 编译速度快
  • 占用内存小
  • 模块化设计
  • 诊断信息可读性强
  • 设计清晰简单,容易理解,易于扩展增强
图解 Clang

四、OC 源文件编译的过程

编写如下 test.m 文件

int test(int a, int b) {
    int c = a + b + 3;
    return c;
}

1. 命令行查看编译过程:clang -ccc-print-phases test.m
carrot__lsp$ clang -ccc-print-phases test.m 
0: input, "test.m", objective-c
1: preprocessor, {0}, objective-c-cpp-output
2: compiler, {1}, ir
3: backend, {2}, assembler
4: assembler, {3}, object
5: linker, {4}, image
6: bind-arch, "x86_64", {5}, image
2. 词法分析,生成 Token:clang -fmodules -E -Xclang -dump-tokens test.m
carrot__lsp$ clang -fmodules -E -Xclang -dump-tokens test.m 
int 'int'    [StartOfLine]  Loc=<test.m:1:1>
identifier 'test'    [LeadingSpace] Loc=<test.m:1:5>
l_paren '('     Loc=<test.m:1:9>
int 'int'       Loc=<test.m:1:10>
identifier 'a'   [LeadingSpace] Loc=<test.m:1:14>
comma ','       Loc=<test.m:1:15>
int 'int'    [LeadingSpace] Loc=<test.m:1:17>
identifier 'b'   [LeadingSpace] Loc=<test.m:1:21>
r_paren ')'     Loc=<test.m:1:22>
l_brace '{'  [LeadingSpace] Loc=<test.m:1:24>
int 'int'    [StartOfLine] [LeadingSpace]   Loc=<test.m:2:5>
identifier 'c'   [LeadingSpace] Loc=<test.m:2:9>
equal '='    [LeadingSpace] Loc=<test.m:2:11>
identifier 'a'   [LeadingSpace] Loc=<test.m:2:13>
plus '+'     [LeadingSpace] Loc=<test.m:2:15>
identifier 'b'   [LeadingSpace] Loc=<test.m:2:17>
plus '+'     [LeadingSpace] Loc=<test.m:2:19>
numeric_constant '3'     [LeadingSpace] Loc=<test.m:2:21>
semi ';'        Loc=<test.m:2:22>
return 'return'  [StartOfLine] [LeadingSpace]   Loc=<test.m:3:5>
identifier 'c'   [LeadingSpace] Loc=<test.m:3:12>
semi ';'        Loc=<test.m:3:13>
r_brace '}'  [StartOfLine]  Loc=<test.m:4:1>
eof ''      Loc=<test.m:4:2>
3. 语法分析,生成语法树(AST,Abstract syntax Tree):carrot__lsp$ clang -fmodules -fsyntax-only -Xclang -ast-dump test.m
carrot__lsp$ clang -fmodules -fsyntax-only -Xclang -ast-dump test.m 
`-FunctionDecl 0x7fc0a8832ff8 <test.m:1:1, line:4:1> line:1:5 test 'int (int, int)'
  |-ParmVarDecl 0x7fc0a8832e70 <col:10, col:14> col:14 used a 'int'
  |-ParmVarDecl 0x7fc0a8832ee8 <col:17, col:21> col:21 used b 'int'
  `-CompoundStmt 0x7fc0a88332e8 <col:24, line:4:1>
    |-DeclStmt 0x7fc0a8833260 <line:2:5, col:22>
    | `-VarDecl 0x7fc0a8833110 <col:5, col:21> col:9 used c 'int' cinit
    |   `-BinaryOperator 0x7fc0a8833238 <col:13, col:21> 'int' '-'
    |     |-BinaryOperator 0x7fc0a88331f0 <col:13, col:17> 'int' '+'
    |     | |-ImplicitCastExpr 0x7fc0a88331c0 <col:13> 'int' <LValueToRValue>
    |     | | `-DeclRefExpr 0x7fc0a8833170 <col:13> 'int' lvalue ParmVar 0x7fc0a8832e70 'a' 'int'
    |     | `-ImplicitCastExpr 0x7fc0a88331d8 <col:17> 'int' <LValueToRValue>
    |     |   `-DeclRefExpr 0x7fc0a8833198 <col:17> 'int' lvalue ParmVar 0x7fc0a8832ee8 'b' 'int'
    |     `-IntegerLiteral 0x7fc0a8833218 <col:21> 'int' 3
    `-ReturnStmt 0x7fc0a88332d0 <line:3:5, col:12>
      `-ImplicitCastExpr 0x7fc0a88332b8 <col:12> 'int' <LValueToRValue>
        `-DeclRefExpr 0x7fc0a8833278 <col:12> 'int' lvalue Var 0x7fc0a8833110 'c' 'int'

五、代码混淆

iOS 程序可以通过 class-dump、Hopper、IDA 等获取类名、方法名、以及分析程序的执行逻辑,如果进行代码混淆,可以加大别人的分析难度。

1. 源码混淆,通过宏定义的方式。
  • 类名
  • 方法名
  • 协议名
2. 字符串加密
  • 很多时候,可执行文件中的字符串信息,对破解者来说,非常关键,是破解的捷径之一

  • 为了加大破解、逆向难度,可以考虑对字符串进行加密

  • 字符串的加密技术有很多种,可以根据自己的需要进行自行定制算法

  • 这里举一个简单的例子:对每个字符串进行异或(^)处理

    3. 混淆注意点
  • 不能混淆系统方法

  • 不能混淆 init 开头的等初始化方法

  • 混淆属性时需要额外注意 set 方法

  • 如果 xib、storyboard 中用到了混淆的内容,需要手动修正

  • 可以考虑吧需要混淆的符号都加上前缀,跟系统自带的符号进行区分

  • 混淆过多可能会被 App Store 拒绝上架,需要说明用途

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

友情链接更多精彩内容