1. 加固的緣由?
我們都知道,在越獄機(jī)型上,如果程序的可執(zhí)行文件被獲取到,就可以通過(guò)一些逆向工具來(lái)反編譯我們的程序,從而可以實(shí)現(xiàn):
任意讀寫(xiě)文件系統(tǒng)數(shù)據(jù)
HTTP(S)實(shí)時(shí)被監(jiān)測(cè)
重新打包ipa
暴露的函數(shù)符號(hào)
未加密的靜態(tài)字符
篡改程序邏輯控制流
攔截系統(tǒng)框架API
逆向加密邏輯
跟蹤函數(shù)調(diào)用過(guò)程(objc_msgSend)
可見(jiàn)視圖的具體實(shí)現(xiàn)
偽造設(shè)備標(biāo)識(shí)
可用的URL schemes
runtime任意方法調(diào)用
2. 編譯過(guò)程
其實(shí)使用 Xcode 構(gòu)建一個(gè)程序,就是把源文件 ( .h 和 .m .swift ) 文件轉(zhuǎn)換為一個(gè)可執(zhí)行文件。這個(gè)Mach-O文件中包含的字節(jié)碼會(huì)將被 CPU (包括iOS 設(shè)備中的 ARM 處理器或 Mac 上的 Intel 處理器) 執(zhí)行。大致流程如下:
預(yù)處理
- 符號(hào)化 (Tokenization)
- 宏定義的展開(kāi)
-
#include的展開(kāi)
語(yǔ)法和語(yǔ)義分析
- 將符號(hào)化后的內(nèi)容轉(zhuǎn)化為一棵解析樹(shù) (parse tree)
- 解析樹(shù)做語(yǔ)義分析
- 輸出一棵抽象語(yǔ)法樹(shù)(Abstract Syntax Tree* (AST))
生成代碼和優(yōu)化
- 將 AST 轉(zhuǎn)換為更低級(jí)的中間碼 (LLVM IR)
- 對(duì)生成的中間碼做優(yōu)化
- 生成特定目標(biāo)代碼
- 輸出匯編代碼
匯編器
- 將匯編代碼轉(zhuǎn)換為目標(biāo)對(duì)象文件。
鏈接器
- 將多個(gè)目標(biāo)對(duì)象文件合并為一個(gè)可執(zhí)行文件 (或者一個(gè)動(dòng)態(tài)庫(kù))
Objective-C 早期采用GCC,從Xcode5之后采用 Clang 作為前端,而 Swift 則采用 swift() 作為前端(Swift的語(yǔ)法分析器是一個(gè)簡(jiǎn)單的,對(duì)整體通過(guò)遞歸向下的方式進(jìn)行語(yǔ)法分析的手工編碼詞法分析器,他是在lib/Parse內(nèi)實(shí)現(xiàn)的。該分析器負(fù)責(zé)生成不包含語(yǔ)義和類(lèi)型信息的抽象語(yǔ)法樹(shù)AST(Abstract Syntax Tree)。這個(gè)階段生成的AST也不包含警告和錯(cuò)誤的注入,因?yàn)?swift 在編譯時(shí)就完成了方法綁定直接通過(guò)地址調(diào)用屬于強(qiáng)類(lèi)型語(yǔ)言,方法調(diào)用不再是像OC那樣的消息發(fā)送,這樣編譯就可以獲得更多的信息用在后面的后端優(yōu)化上),二者都是用 LLVM(Low level vritual machine) 作為編譯器后端。
對(duì)應(yīng)前端編譯器地址如下:
# Objective-C
/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/clang
# Swift
/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/swift
GCC
(GNU Compiler Collection)縮寫(xiě),一個(gè)編程語(yǔ)言編譯器,是GNU(自由軟件理事會(huì))的關(guān)鍵部分。也是GNU工具鏈的一部分。GCC常被認(rèn)為是跨平臺(tái)編譯器的事實(shí)標(biāo)準(zhǔn),特別是它的C語(yǔ)言編譯器。GCC原本只能處理C語(yǔ)言。但是面對(duì)Clang的競(jìng)爭(zhēng),很快作出了擴(kuò)展,現(xiàn)在已經(jīng)可以處理C++,F(xiàn)ortran、Pascal、Object-C、Java、Ada,以及Go語(yǔ)言。許多操作系統(tǒng),包括許多Unix系統(tǒng),如Linux及BSD家族都采用GCC作為標(biāo)準(zhǔn)編譯器。MacOSX也是采用這個(gè)編譯器。
LLVM
是Low Level Virtual Machine的簡(jiǎn)稱。這個(gè)庫(kù)提供了與編譯器相關(guān)的支持,能夠進(jìn)行程序語(yǔ)言的編譯期優(yōu)化、鏈接優(yōu)化、在線編譯優(yōu)化、代碼生成??梢宰鳛槎喾N語(yǔ)言編譯器的后臺(tái)來(lái)使用。
LLVM是一個(gè)優(yōu)秀的編譯器框架,如圖:

它采用經(jīng)典的三段式設(shè)計(jì)。前端可以使用不同的編譯工具對(duì)代碼文件做詞法分析以形成抽象語(yǔ)法樹(shù)AST,然后將分析好的代碼轉(zhuǎn)換成LLVM的中間表示IR(intermediate representation);它既不是源代碼,也不是機(jī)器碼。從代碼組織結(jié)構(gòu)上看它比較接近機(jī)器碼,但是在函數(shù)和指令層面使用了很多高級(jí)語(yǔ)言的特性。
IR代碼是由一個(gè)個(gè)Module組成的,每個(gè)Module之間互相聯(lián)系,而Module又是由一個(gè)個(gè)Function組成,F(xiàn)unction又是由一個(gè)個(gè)BasicBlock組成,在BasicBlock中又包含了一條條Instruction。
中間部分的優(yōu)化器只對(duì)中間表示IR操作,通過(guò)一系列的Pass對(duì)IR做優(yōu)化;后端負(fù)責(zé)將優(yōu)化好的IR解釋成對(duì)應(yīng)平臺(tái)的機(jī)器碼。LLVM的優(yōu)點(diǎn)在于,中間表示IR代碼編寫(xiě)良好,而且不同的前端語(yǔ)言最終都轉(zhuǎn)換成同一種的IR。
Clang
Clang 是 LLVM 的子項(xiàng)目,是 C,C++ 和 Objective-C 編譯器,目的是提供驚人的快速編譯,比 GCC 快3倍,其中的 clang static analyzer 主要是進(jìn)行語(yǔ)法分析,語(yǔ)義分析和生成中間代碼,當(dāng)然這個(gè)過(guò)程會(huì)對(duì)代碼進(jìn)行檢查,出錯(cuò)的和需要警告的會(huì)標(biāo)注出來(lái)。
前端編譯器
編譯器前端的任務(wù)是進(jìn)行:語(yǔ)法分析,語(yǔ)義分析,生成中間代碼(intermediate representation )。在這個(gè)過(guò)程中,會(huì)進(jìn)行類(lèi)型檢查,如果發(fā)現(xiàn)錯(cuò)誤或者警告會(huì)標(biāo)注出來(lái)在哪一行。
后端編譯器
編譯器后端會(huì)進(jìn)行機(jī)器無(wú)關(guān)的代碼優(yōu)化,生成機(jī)器語(yǔ)言,并且進(jìn)行機(jī)器相關(guān)的代碼優(yōu)化。iOS的編譯過(guò)程,后端的處理如下
LVVM 優(yōu)化器會(huì)進(jìn)行 BitCode 的生成,鏈接期優(yōu)化等等。
3. 加固類(lèi)型
1.字符串混淆
對(duì)應(yīng)用程序中使用到的字符串進(jìn)行加密,保證源碼被逆向后不能看出字符串的直觀含義。
2.類(lèi)名、方法名混淆
對(duì)應(yīng)用程序的方法名和方法體進(jìn)行混淆,保證源碼被逆向后很難明白它的真正功能。
3.程序結(jié)構(gòu)混淆加密
對(duì)應(yīng)用程序邏輯結(jié)構(gòu)進(jìn)行打亂混排,保證源碼可讀性降到最低。
4.反調(diào)試、反注入等一些主動(dòng)保護(hù)策略
這是一些主動(dòng)保護(hù)策略,增大破解者調(diào)試、分析App的門(mén)檻。
4. 逆向工具??
1. class-dump
class-dump 利用 Objective-C runtime 特性,將存儲(chǔ)在 Mach-O 文件結(jié)構(gòu)里 data 部分的類(lèi)屬性和方法等信息提取出來(lái),并生成對(duì)應(yīng)的 .h 文件的工具。
在 class-dump 官網(wǎng)下載 dmg,將 dmg 里面的 class-dump 拷貝到 /usr/local/bin 文件夾下,然后就可以在終端中使用 class-dump 命令了。
簡(jiǎn)單使用:
class-dump -H [需要被導(dǎo)出的 Mach-O 文件路徑] -o [頭文件輸出目錄地址]
2. hopper
Hopper 是一種適用于 OS X 和 Linux 的逆向工程工具,可以用于反匯編、反編譯和調(diào)試 32位/64位英特爾處理器的 Mac、Linux、Windows 和 iOS 可執(zhí)行程序。
3. IDA
是目前最棒的一個(gè)靜態(tài)反編譯軟件,為眾多0day世界的成員和ShellCode安全分析人士不可缺少的利器。
就其本質(zhì)而言,IDA是一種遞歸下降反匯編器。但是,為了提高遞歸下降過(guò)程的效率,IDA的開(kāi)發(fā)者付出了巨大的努力,來(lái)為這個(gè)過(guò)程開(kāi)發(fā)邏輯。為了克服遞歸下降的一個(gè)最大的缺點(diǎn),IDA在區(qū)分?jǐn)?shù)據(jù)與代碼的同時(shí),還設(shè)法確定這些數(shù)據(jù)的類(lèi)型。雖然你在IDA中看到的是匯編語(yǔ)言形式的代碼,但I(xiàn)DA的主要目標(biāo)之一,在于呈現(xiàn)盡可能接近源代碼的代碼。此外,IDA不僅使用數(shù)據(jù)類(lèi)型信息,而且通過(guò)派生的變量和函數(shù)名稱來(lái)盡其所能地注釋生成的反匯編代碼。這些注釋將原始十六進(jìn)制代碼的數(shù)量減到最少,并顯著增加了向用戶提供的符號(hào)化信息的數(shù)量。
5. OLLVM
O-llvm是基于llvm進(jìn)行編寫(xiě)的一個(gè)開(kāi)源項(xiàng)目(https://github.com/obfuscator-llvm/obfuscator),它的作用是對(duì)前端語(yǔ)言生成的中間代碼進(jìn)行混淆。
O-llvm總體構(gòu)架和llvm是一致的:

其中IR(intermediate representation)中間代碼表示,也是Pass(總的來(lái)說(shuō),所有的pass大致可以分為兩類(lèi):分析和轉(zhuǎn)換;分析類(lèi)的pass以提供信息為主,轉(zhuǎn)換類(lèi)的會(huì)修改中間代碼)操作的對(duì)象,它主要包含四個(gè)部分:
(1)Module:比如一個(gè).c或者.cpp文件。
(2)Function:代表文件中的一個(gè)函數(shù)。
(3)BasicBlock:每個(gè)函數(shù)會(huì)被劃分為一些block,它的劃分標(biāo)準(zhǔn)是:一個(gè)block只有一個(gè)入口和一個(gè)出口。
(4)Instruction:具體的指令。
對(duì)于OLLVM的每個(gè)pass,其主要的工作繼承對(duì)應(yīng)的pass類(lèi),就是對(duì)相應(yīng)的方法進(jìn)行重寫(xiě),例如SplitBasicBlock的實(shí)現(xiàn),它繼承自FunctionPass,并重寫(xiě)了runOnFunction方法。
O-llvm包含有三個(gè)pass,分別是BogusControlFlow、Flattening 和 Instruction Substitution。它們是O-llvm實(shí)現(xiàn)混淆功能的核心,具體實(shí)現(xiàn)位于llvm/lib/Transforms/Obfuscation/目錄下。
控制流扁平化
這個(gè)模式主要是把一些if-else語(yǔ)句,嵌套成do-while語(yǔ)句
-mllvm -fla:激活控制流扁平化
-mllvm -split:激活基本塊分割。在一起使用時(shí)改善展平。
-mllvm -split_num=3:如果激活了傳遞,則在每個(gè)基本塊上應(yīng)用3次。默認(rèn)值:1
指令替換
這個(gè)模式主要用功能上等效但更復(fù)雜的指令序列替換標(biāo)準(zhǔn)二元運(yùn)算符(+ , – , & , | 和 ^)
-mllvm -sub:激活指令替換
-mllvm -sub_loop=3:如果激活了傳遞,則在函數(shù)上應(yīng)用3次。默認(rèn)值:1
虛假控制流程
這個(gè)模式主要嵌套幾層判斷邏輯,一個(gè)簡(jiǎn)單的運(yùn)算都會(huì)在外面包幾層if-else,所以這個(gè)模式加上編譯速度會(huì)慢很多因?yàn)橐鰩讓蛹俚倪壿嫲嬲杏玫拇a。
另外說(shuō)一下這個(gè)模式編譯的時(shí)候要浪費(fèi)相當(dāng)長(zhǎng)時(shí)間包哪幾層不是鬧得!
-mllvm -bcf:激活虛假控制流程
-mllvm -bcf_loop=3:如果激活了傳遞,則在函數(shù)上應(yīng)用3次。默認(rèn)值:1
-mllvm -bcf_prob=40:如果激活了傳遞,基本塊將以40%的概率進(jìn)行模糊處理。默認(rèn)值:30
6. Hikari
HikariObfuscator/Hikari 是一個(gè)基于 Obfuscator-LLVM 對(duì) Xcode9的適配。
如果需要自行編譯,需要安裝 cmake and ninja 以及 SWIG 。
cmake : CMake 是一個(gè)跨平臺(tái)的自動(dòng)化建構(gòu)系統(tǒng),它使用一個(gè)名為 CMakeLists.txt 的文件來(lái)描述構(gòu)建過(guò)程,可以產(chǎn)生標(biāo)準(zhǔn)的構(gòu)建文件,如 Unix 的 Makefile 或 Windows Visual C++ 的 projects/workspaces 。文件 CMakeLists.txt 需要手工編寫(xiě),也可以通過(guò)編寫(xiě)腳本進(jìn)行半自動(dòng)的生成。CMake 提供了比 autoconfig 更簡(jiǎn)潔的語(yǔ)法。一些使用 CMake 作為項(xiàng)目架構(gòu)系統(tǒng)的知名開(kāi)源項(xiàng)目有 VTK、ITK、KDE、OpenCV、OSG 等。
在 linux 平臺(tái)下使用 CMake 生成 Makefile 并編譯的流程如下:
- 編寫(xiě) CMake 配置文件 CMakeLists.txt 。
- 執(zhí)行命令
cmake PATH或者ccmake PATH生成 Makefile。(ccmake和cmake的區(qū)別在于前者提供了一個(gè)交互式的界面)其中,PATH是 CMakeLists.txt 所在的目錄。 - 使用
make命令進(jìn)行編譯。
下載地址: https://cmake.org/download/
在終端安裝:
sudo "/Applications/CMake.app/Contents/bin/cmake-gui" --install
Ninja : Ninja是一種類(lèi)似GNU make的編譯系統(tǒng)。 就像make有Makefile,它也有自己的編譯配置文件。 相對(duì)來(lái)說(shuō),Ninja文件沒(méi)有分支、循環(huán)的流程控制,本質(zhì)上就是純粹的配置文件,所以要比Makefile簡(jiǎn)單得多。
Ninja目前主要應(yīng)用在Google Chrome,部分Android系統(tǒng),LLVM以及CMake的Ninja后端中。
可通過(guò)官網(wǎng)下載安裝release版本,也可通過(guò)homebrew或者npm包管理工具安裝 brew install Ninja
SWIG :Simplified Wrapper and Interface Generator,SWIG完整支持ANSI C,支持除嵌套類(lèi)外的所有C++特性。SWIG是一個(gè)接口編譯器,旨在為C/C++方便地提供腳本語(yǔ)言接口。SWIG不僅可以為C/C++程序生成 Python接口,目前可以生成CLISP,Java,Lua,PHP,Ruby,Tcl等19種語(yǔ)言的接口。SWIG被Subversion, wxPython, Xapian等項(xiàng)目使用。值得一提的是,Google也使用SWIG。
SWIG本質(zhì)上是個(gè)代碼生成器,為C/C++程序生成到其他語(yǔ)言的包裝代碼(wrapper code),這些包裝代碼里會(huì)利用各語(yǔ)言提供的C API,將C/C++程序中的內(nèi)容暴露給相應(yīng)語(yǔ)言。為了生成這些包裝代碼,SWIG需要一個(gè)接口描述文件,描述將什么樣的接口暴露給其他語(yǔ)言。
SWIG的 接口描述文件可以包含以下內(nèi)容:
- ANSI C函數(shù)原型聲明
- ANSI C變量聲明
- SWIG指示器(directive)相關(guān)內(nèi)容
利用SWIG,可以現(xiàn)實(shí)以下功能:
- 用Python調(diào)用C/C++庫(kù)
- 用Python繼承C++類(lèi),并在Python中使用該繼承類(lèi)
- C++使用Python擴(kuò)展(通過(guò)文檔描述應(yīng)該可以支持,未驗(yàn)證)
自行安裝需要在/Hikari/tools/xcode-toolchain/CMakeLists.txt中加上set(CMAKE_CXX_FLAGS "-std=c++11 ${CMAKE_CXX_FLAGS}")才能編譯通過(guò)。另如果執(zhí)行完成報(bào)錯(cuò) lldb_codesign: no identity found,則是需要添加一個(gè)lldb_codesign證書(shū),然后信任,具體可參照 這里 或 編譯mac下的lldb
安裝完成之后執(zhí)行腳本,到生成Toolchains我這邊大約花了2小時(shí):
git clone --recursive -b release_80 https://github.com/HikariObfuscator/Hikari.git Hikari && cd Hikari && git submodule update --remote --recursive && cd ../ && mkdir Build && cd Build && cmake -G "Ninja" -DLLDB_CODESIGN_IDENTITY='' -DCMAKE_BUILD_TYPE=MinSizeRel -DLLVM_APPEND_VC_REV=on -DLLVM_CREATE_XCODE_TOOLCHAIN=on -DCMAKE_INSTALL_PREFIX=~/Library/Developer/ ../Hikari && ninja &&ninja install-xcode-toolchain && git clone https://github.com/HikariObfuscator/Resources.git ~/Hikari && rsync -a --ignore-existing /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/ ~/Library/Developer/Toolchains/Hikari.xctoolchain/ && rm ~/Library/Developer/Toolchains/Hikari.xctoolchain/ToolchainInfo.plist
另外也可以直接下載安裝作者提供的release版本 pkg 文件:
1 https://github.com/HikariObfuscator/Hikari/releases
重啟Xcode就能在Toolchains里面看到除了系統(tǒng)默認(rèn)的編譯工具還多了一個(gè)Hikari編譯工具。
-
Xcode->Toolchains->Hikari將混淆工具和項(xiàng)目關(guān)聯(lián) - 將所有與要運(yùn)行的 target 相關(guān)的 target(包括pod進(jìn)來(lái)的庫(kù))
Enable Index-While-Building的值改為 NO。 -
Optimization Level的值設(shè)置為None[-O0] - 在
Build Settings->Other C Flags中加入混淆標(biāo)記
在 Wiki 中可以看到用法:
The following flags are supported
-enable-bcfobf 啟用偽控制流
-enable-cffobf 啟用控制流平坦化
-enable-splitobf 啟用基本塊分割
-enable-subobf 啟用指令替換
-enable-acdobf 啟用反class-dump
-enable-indibran 啟用基于寄存器的相對(duì)跳轉(zhuǎn),配合其他加固可以徹底破壞IDA/Hopper的偽代碼(俗稱F5)
-enable-strcry 啟用字符串加密
-enable-funcwra 啟用函數(shù)封裝
7. 代碼虛擬化
使用一套自定義的字節(jié)碼來(lái)替換掉程序中原有的native指令,而字節(jié)碼在執(zhí)行的時(shí)候又由程序中的解釋器來(lái)解釋執(zhí)行。自定義的字節(jié)碼是只有解釋器才能識(shí)別的,所以一般的工具是無(wú)法識(shí)別我們自定義的字節(jié)碼,也是因?yàn)檫@一點(diǎn),基于虛擬機(jī)的保護(hù)相對(duì)其他保護(hù)而言要更加難破解。
目前很多地方都會(huì)用到虛擬化技術(shù),比如sandbox、程序保護(hù)殼等。很多時(shí)候?yàn)榱朔乐箰阂獯a對(duì)我們的系統(tǒng)造成破壞,我們需要一個(gè)sandbox,使程序運(yùn)行在sandbox中,即使惡意代碼破壞系統(tǒng)也只是破壞了sandbox而不會(huì)對(duì)我們的系統(tǒng)造成影響。
基于虛擬機(jī)的代碼保護(hù)也可以算是代碼混淆技術(shù)的一種。代碼混淆的目的就是防止代碼被逆向分析,但是所有的混淆技術(shù)都不是完全不能被分析出來(lái),只是增加了分析的難度或者加長(zhǎng)了分析的時(shí)間,雖然這些技術(shù)對(duì)保護(hù)代碼很有效果,但是也存在著副作用,比如會(huì)或多或少的降低程序效率,這一點(diǎn)在基于虛擬機(jī)的保護(hù)中格外突出,所以大多基于虛擬機(jī)的保護(hù)都只是保護(hù)了其中比較重要的部分。
LLVM
The Compiler
CMake入門(mén)實(shí)戰(zhàn)
Mach-O 可執(zhí)行文件
在linux下使用CMake構(gòu)建應(yīng)用程序
利用 SWIG 對(duì) C++ 庫(kù)進(jìn)行 Python 包裝