簡介html
隨着 Xcode 7 的發佈,蘋果爲 Xcode 增長了一個新的特性 Bitcode [1]:前端
新的特性每每意味着新的攻擊面。本文首先介紹什麼是 Bitcode 及 Bitcode 相關的工做流程,在熟悉了 Bitcode 的工做流程後,接下來是評估 Bitcode 相關的攻擊面,最後介紹針對各個攻擊面的測試方法及目前的測試結果。ios
什麼是 Bitcode後端
簡單來講,Bitcode 是 LLVM-IR 在磁盤上的一種二進制表示形式。關於 Bitcode 詳細描述,請參考[2],這裏會用例子來讓你們對 Bitcode 有個感性認識。安全
先寫一個簡單的 C 程序,功能是計算兩個數的和,代碼以下:服務器
1
2
3
4
5
|
int add(int a, int b)
{
int c = a + b;
return
c;
}
|
將如上程序保存爲 add.c,而後咱們將源程序編譯成 Bitcode:app
1
|
clang -emit-llvm -c add.c -o add.bc
|
執行如上命令會生成 add.bc,咱們使用二進制編輯器打開生成的文件,查看文件內容:編輯器
因爲 Bitcode 是 LLVM-IR 的二進制表示形式,如上圖,在不瞭解編碼方式的前提下基本不可讀。下面咱們把 Bitcode 轉換成文本形式:ide
1
|
llvm-dis add.bc -o add.ll
|
用文本編輯器打開 add.ll,能夠看到 add 函數的 LLVM-IR 內容以下:函數
對比源碼與已經註釋過的 add() 函數的 LLVM-IR 表示,你們應該對 LLVM-IR 有個感性認識了,下面咱們一塊兒看下 Bitcode 的工做流程。
工做流程
蘋果關於工做流程的描述:
「 When you archive for submission to the App Store, Xcode compiles your app into an intermediate representation. The App Store then compiles the bitcode down into the 64- or 32-bit executables as necessary.」
如上的工做流程能夠分爲兩個階段:
在將應用上傳到 AppStore 時,Xcode 會將程序對應的 Bitcode 一塊兒上傳。
AppStore 會將 Bitcode 從新編譯爲可執行程序,供用戶下載。
下面會將 Bitcode 相關的完整的工做流程分解爲以下幾個問題或子過程並分別作說明:
Where is the Bitcode?
嵌入 Bitcode 的方法
從 Bitcode 生成可執行程序的方法
Where is the Bitcode?
參考蘋果的描述,只有在 Archive 時纔會生成 Bitcode,因而創建了一個測試工程:
執行 Archive,而後查看生成的包結構:
通過分析在如上的目錄中並無直接找到 Bitcode,接下來檢查生成的 MachO。使用 MachOView 加載生成的 MachO,結果以下圖:
從上圖能夠看到最終的可執行程序中多了 LLVM 相關的 Segment 與 Section。繼續查看對應的 Section 的信息:
如上圖,Section __bundle 中保存的是一個 xar 文檔,提取出 xar 文檔,而後使用以下命令解開文檔:
1
|
解開:xar -x -f XXX.xar
|
解開後,能夠看到 Bitcode 文件。
總結:程序對應的 Bitcode 被 Xcode 打包成 xar 文檔,嵌入的 MachO 中。
下面咱們看下在 MachO 中嵌入 Bitcode 的方法。
嵌入 Bitcode 的方法
方法一
經過對比 Archive 與非 Archive 時的編譯參數,發現只要在以下圖所示的位置添加編譯參數:-fembed-bitcode,便可讓 Xcode 普通編譯時也在 MachO 中嵌入 Bitcode:
方法二
方法一雖然很方便,可是 IDE 作了太多工做,不便於理解具體過程,接下來咱們本身編譯可執行文件。從源代碼生成可執行程序主要分爲:編譯、連接兩個過程,爲了控制這兩個過程,下面會講解 Makefile 的配置,及這兩個過程用到的參數。
在使用 Makefile 編譯 iOS 程序時,有些通用的配置,以下的通用配置,供你們參考:
以 main.m 爲例說明編譯須要的參數:
將 main.o,AppDelegate.o,ViewController.o 連接成可執行程序的參數:
你們把如上的 Makefile 片斷稍加修改,整理到一個 Makefile 文件中,就能夠經過 make 命令嵌入 Bitcode 到可執行程序。
方法三
在這個方法中咱們會將上面的步驟進一步分解,具體過程爲:
源碼→Bitcode→xar→可執行程序
源碼→Bitcode
在這個過程當中咱們將 iOS 應用的源碼編譯成 Bitcode,下面會 main.m 爲例來講明使用的參數:
完成這個過程後,咱們能夠獲得三個Bitcode 文件:
main.bc
AppDelegate.bc
ViewController.bc
Bitcode→xar
在這一步咱們會將如上獲得的三個 Bitcode 文件打包到一個 xar 文檔中。打包沒什麼特別,須要注意的是須要與 Xcode 生成的 xar 保持兼容,具體參數以下:
生成:xar –toc-cksum none -c -f BC.xar main.bc AppDelegate.bc ViewController.bc
xar→可執行程序
爲了簡化過程,這裏咱們會跳出 Makefile,使用 Xcode,首先清除以下的編譯參數:
將剛剛生成的 BC.xar 拷貝到測試工程的根目錄:
編輯工程設置的 Other Linker Flags,添加:-Wl,-sectcreate,__LLVM,__bundle,$(SRCROOT)/BC.xar,以下圖:
編譯程序,查看生成的 MachO 文件,能夠看到 Bitcode 已經被添加到了 MachO 中。
如上咱們介紹了在 MachO 中嵌入 Bitcode 的方法,對應的是第一個過程:「在將應用上傳到 AppStore 時,Xcode 會將程序對應的 Bitcode 一塊兒上傳」,下面咱們說明第二個過程。
從 Bitcode 生成可執行程序的方法
第二個過程爲:「AppStore 會將 Bitcode 從新編譯爲可執行程序,供用戶下載」。第二個過程是在蘋果的 Server 上進行的,咱們無法直接得到細節,可是應該都是基於相同的工具鏈,咱們能夠模擬這個過程。
從 MachO 中提取 Bitcode
AppStore 拿到咱們上傳的 IPA 後,首先須要從 IPA 內的 MachO 文件中提取出包含 Bitcode 的 xar 文檔。在 Xcode 的工具鏈中有個工具 segedit 能夠用來從 MachO 提取 Section,提取 xar 的具體參數以下:
提取到 xar 後,解開 xar:
獲得以下幾個 Bitcode 文件:
還可使用 llvm-dis 工具將如上文件處理成可讀形式,從而瞭解每一個文件的內容。
生成可執行程序
在有了 Bitcode 後,接下來須要將 Bitcode 編譯成可執行程序,分爲兩個過程:將 Bitcode 編譯成 Object 文件;連接 Object 文件到可執行程序。
將 Bitcode 編譯成 Object 文件
Makefile 片斷以下:
連接 Object 文件到可執行程序
如上咱們已經從 Bitcode 從新生成了可執行程序 XBCTest。
攻擊面
咱們先回顧下 Bitcode 在本地的工做流程:Xcode 將嵌入了 Bitcode 的 MachO 上傳到 AppStore。經過分析能夠發現這裏存在兩個問題:
1.MachO 與其中嵌入的 Bitcode 的一致性問題。即:可否把 程序B 的 Bitcode 嵌入到 程序A中。
2.AppStore 是否信任了 Xcode,而沒有檢查一致性問題,從而容許將 Malformed MachO 上傳到 AppStore。
在分析了可能存在的問題後,咱們認爲若是 Bitcode 流程與功能存在缺陷,即可以對兩個目標造成威脅:普通用戶、蘋果。
普通用戶
因爲 Bitcode 對普通用戶是透明的,所以沒法經過其弱點直接攻擊用戶。可是一致性問題是可能對普通用戶形成威脅的,試想:若是提交 AppStore 審覈的 程序A 中嵌入了含有惡意代碼的Bitcode,普通用戶就有可能從AppStore 下載到含有惡意代碼的程序。
對於這種攻擊方式咱們將其叫作 Bitcode Injection,下文會詳細介紹這種攻擊的實施方法,及咱們的測試結果。
蘋果
若是 Malformed MachO 能夠被上傳到蘋果的服務器,蘋果的服務器相較於以前,主要須要進行兩個額外的操做:解開 xar;編譯 Bitcode。若是這兩個過程出現問題,輕則能夠在蘋果的服務器上形成 DoS,重則能夠在蘋果的服務器上形成任意代碼執行。
另外,Bitcode 本來是 LLVM-IR 的一種序列化形式,而 LLVM-IR 是一種中間形式,以前沒有被直接暴露出來,如今徹底開放了,並且又是二進制格式,這是很容易出問題的。從 Bitcode 生成可執行文件的過程主要由以下幾個子過程組成:
1.基於平臺無關的 IR的代碼優化。
2.IR的平臺相關化、合法化。
3.平臺相關的優化、代碼生成。
這些本來是編譯器的內部過程,因爲各類緣由,傳統的對編譯器的測試主要集中在前端的 Parser 與 Lexer,如今藉由 Bitcode 如上的一些中間或者後端過程也暴露了出來,若是如上的過程出現問題最糟糕的結果是能夠控制編譯器的指令生成。
以上是關於攻擊面的分析,後文會介紹測試 xar 及 Bitcode 的思路,以及發現的問題。
Bitcode Injection
上文在介紹 Bitcode 工做流程時已經介紹了實施 Bitcode Injection 的方法,可是上面提到的方法不夠簡練,這裏咱們再介紹一種更簡單的方法,主要的思路就是最大限度的利用 Xcode,這個方法的具體實施步驟爲:
用 Xcode 創建工程XBCTest:
2. 複製工程XBCTest,獲得工程XBCTest2:
3. 修改工程XBCTest2的源碼,嵌入惡意代碼:
4. Archive 工程XBCTest2:
5. 得到 MachO,利用 segedit 從 MachO 中提取出含有 Bitcode 的 xar:
提取xar: segedit ./XBCTest -extract "__LLVM" "__bundle" BC.xar
6. 修改工程XBCTest的連接標記,將提取的 xar: BC.xar 嵌入到工程XBCTest的 MachO文件中。
7. 禁用工程XBCTest的 Bitcode 特性,Archive 並上傳 AppStore:
咱們在測試的過程當中並無嵌入惡意代碼,而是從網上找個兩個徹底不一樣的應用,將其中一個的 Bitcode 嵌入到另外一個的 MachO 中,並提交到AppStore。
在將應用提交到 AppStore 的過程當中主要會進行兩個方面的檢查:Xcode 在本地進行靜態分析;提交後,蘋果的服務器還會進行檢查。可是使用 Bitcode Injection 構造的應用能夠經過這兩項檢查:
通過漫長的審覈後,咱們的應用被拒了,理由是:咱們的應用與描述不符。在描述中咱們的應用應該長成以下樣子:
可是蘋果的審覈人員安裝後,程序卻長成這個樣子:
這至少能夠說明三個問題:
1.咱們使用的 Bitcode Injection 方法沒有問題。
2.蘋果的審覈人員審覈的是從 Bitcode 編譯出來的程序。
3.一致性是靠人肉區分的。若是嵌入對 UI 沒有影響的惡意代碼,仍是有可能繞過審覈的。
測試xar
思路
對 xar 進行模糊測試,生成數據的方法是基於標準的 xar 文檔進行變異。
測試結果
目前主要 Fuzz 出一些空指針解引用問題。
測試 clang
思路
對 clang 中的Bitcode 到 Object 的功能進行模糊測試,也是採用變異的方法生成測試數據。
測試結果
經過對 clang 的 Fuzz 咱們發現了一些堆損壞相關的問題。
總結
1. The Xcode 7 bitcode feature has opened a huge attacking surface, Apple should do something to narrow it, for example: checking the bitcode is identical to the related MachO file.
2. 在上文中咱們詳細介紹了所考慮到的攻擊面,及針對每一個攻擊面的測試思路,但願這些對你們研究 Bitcode 相關的攻擊面及安全性有幫助。
參考資料
[1] What’s New in Xcode
[2] LLVM Bitcode File Format
做者:360NirvanTeam(企業帳號)