一、LLVM
1. 什么是 LLVM
- 官網(wǎng):https://llvm.org/
- The LLVM Project is a collection of modular and reusable
compilerandtoolchaintechnologies. - LLVM 項目是模塊化、可重用的
編譯器以及工具鏈技術(shù)集合
2.創(chuàng)始人
-
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” 這個名稱不是首字母縮寫詞;它是項目的全名
二、編譯器架構(gòu)
1. 傳統(tǒng)編譯器架構(gòu)

傳統(tǒng)編譯器架構(gòu)
Frontend:前端;詞法分析、語法分析、語義分析、生成中間代碼
Optimizer:優(yōu)化器;中間代碼優(yōu)化
Backend:后端;生成機器碼
2. LLVM 架構(gòu)

LLVM 架構(gòu)
- 不同的前端后端使用統(tǒng)一的中間代碼 LLVM Intermediate Representation (LLIR)
- 如果需要支持一種新的編程語言,那么只需要實現(xiàn)一個新的前端
- 如果需要支持一種新的硬甲設(shè)備,那么只需要實現(xiàn)一個新的后端
- 優(yōu)化階段是一個通用的階段,它針對的統(tǒng)一的 LLVM IR,不論是支持新的編程語言,還是支持新的硬件設(shè)備,都不需要對優(yōu)化階段做修改
- 相比之下,GCC 的前端和后端沒分得太開,前后端耦合在了一起。所以 GCC 為了支持一門新的語言,或者為了支持一個新的目標平臺,就變得特別困難
- LLVM 現(xiàn)在被作為實現(xiàn)各種靜態(tài)和運行時編譯語言的通用基礎(chǔ)結(jié)構(gòu)(GCC 家族、Java、.NET、Python、Ruby、Scheme、Haskell 等)
三、Clang
1. 什么是 Clang?
- LLVM 項目的一個子項目
- 基于 LLVM 架構(gòu)的 C/C++/Objective-C 編譯器
前端 - 官網(wǎng):http://clang.llvm.org/
2. 相比于 GCC,Clang 有如下優(yōu)點
- 編譯速度快
- 占用內(nèi)存小
- 模塊化設(shè)計
- 診斷信息可讀性強
- 設(shè)計清晰簡單,容易理解,易于擴展增強

圖解 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 等獲取類名、方法名、以及分析程序的執(zhí)行邏輯,如果進行代碼混淆,可以加大別人的分析難度。
1. 源碼混淆,通過宏定義的方式。
- 類名
- 方法名
- 協(xié)議名
2. 字符串加密
很多時候,可執(zhí)行文件中的字符串信息,對破解者來說,非常關(guān)鍵,是破解的捷徑之一
為了加大破解、逆向難度,可以考慮對字符串進行加密
字符串的加密技術(shù)有很多種,可以根據(jù)自己的需要進行自行定制算法
-
這里舉一個簡單的例子:對每個字符串進行異或(^)處理
3. 混淆注意點
不能混淆系統(tǒng)方法
不能混淆 init 開頭的等初始化方法
混淆屬性時需要額外注意 set 方法
如果 xib、storyboard 中用到了混淆的內(nèi)容,需要手動修正
可以考慮吧需要混淆的符號都加上前綴,跟系統(tǒng)自帶的符號進行區(qū)分
混淆過多可能會被 App Store 拒絕上架,需要說明用途