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

一、LLVM

1. 什么是 LLVM
  • 官網(wǎng):https://llvm.org/
  • The LLVM Project is a collection of modular and reusable compiler and toolchain technologies.
  • 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 拒絕上架,需要說明用途

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

友情鏈接更多精彩內(nèi)容