Android中Native ELF的反彙編與破解的一些經驗
工具選擇與使用
一般Android執行的HW有:
- 32Bit的ARM
- 64Bit的ARM64
- X86
- X64
對於Intel/AMD的X86/X64, 可選的工具比較多, 因為ELF執行的Host是Android, 使用靜態反彙編工具Hopper Disassemble與IDA是比較好的選擇.
對於32位ARM的ARM, IDA與Hopper Disassemble都可以完成, 但是對於ARM64就只有Hopper Disassemble了, 當然最新的IDA Pro也可以,但是一般難以獲取得到.
剩下的方式就是用readelf + Android中的Toolchain中的objdump來完成.
一般情況下, 多種工具需要一起結合使用.
如何快速定位
得到了我們需要反彙編與patch的ELF檔案後, 將其拖入到Disassemble工具中, 就可以看到其Disassemble後的不同Section了, 例如Text, Code段. 那麼要完成patch, 我們需要找到patch的位置. 對此, 個人總結有3種方式來幫助我們快速定位.
根據字串來快速定位
一般程式執行的時候會有log輸出, 提示是何種錯誤, 例如對於在servicemanager中的add_service, 那麼如果我們的service不在service list中,那麼會提示許可權不足"Permission Denied", 那麼這個字串就是很好的切入點.
在IDA中我們直接使用Search中的Text即可找到.
編譯帶有符號的ELF來輔助定位
對於Android程式碼, 不同板子或者Android裝置的ROM一般都是基於AOSP做的修改, 因此絕大地方的程式碼和AOSP的程式碼是一樣的. 因此我們可以藉助AOSP程式碼來幫助我們定位.
對此第一步是需要獲取Android裝置上面Android的版本, 這個在關於介面, 或者使用getprop, 或者在/system/build.prop中可以獲取tag以及version.
然後我們就可以根據這些資訊去下載對應版本的AOSP, 然後選擇對應的Arch編譯, 編譯後, 在out目錄中會有一個帶有symbol的可供除錯用的ELF檔案. 這個一般位於:
out/target/product/XXX/symbols/
然後我們可以將這裡面的對應的ELF檔案拖入到IDA Pro或者其他Disassemble工具中進行檢視, 此時因為有Debug 資訊, 反彙編的程式碼與變數等變得很容易被識別, 很容易讓我們定位. 例如下面是對bluedroid stack的反彙編:
圖片1
裡面的變數我們可以都可以找到. 然後使用IDA的pseudo外掛, 可以得到非常好的虛擬碼:
圖片2
而且因為有symbols, 所以我們也就可以知道其位於哪個函式中. 這對我們對應者source code來看instructions, 然後對後面的寫patching tool都很有幫助.
根據立即數來定位
有些程式碼中對某些變數或者引數有臨界值判斷, 這個臨界值一般都是常量const, 或者是立即數. 這個時候, 使用這個立即數來查詢也可以很快幫主我們定位. 例如前面圖片2中的Line49中有一個立即數: 0xC7A.
這個0xC7A的來源是0xC80-6, 和下面的BTM_BLE_CONN_INT_MAX是匹配的.然後根據ARM中的立即數的表示規則,會發現只能是0xC7A, 即不管任何的Toolchain編譯下得到的arm-v7 的指令集中的這個立即數都是這個值.
-
stack/include/btm_ble_api.h
-
131:#define BTM_BLE_CONN_INT_MAX 0x0C80
-
170:#ifndef BTM_BLE_CONN_INT_MAX_DEF
-
171:#define BTM_BLE_CONN_INT_MAX_DEF 40/* recommended max: 50 ms = 56 * 1.25 */
-
bta/include/bta_api.h
-
689:#define BTA_DM_BLE_CONN_INT_MAX BTM_BLE_CONN_INT_MAX
因此我們也還可以使用立即數來查詢:
如何結合原始碼對ELF檔案進行快速破解
對於servicemanager, mediaserver, surfaceflinger等等這些AOSP含有的service, AOSP中含有原始碼, 而一般相同版本的Android,那麼程式碼基本是絕大部分相同的.
因此, 我們完全可以直接在程式碼中查詢對應的立即數, 字串等有用的資訊, 找到對應的函式, 然後根據相關的程式碼塊來得到對應的instructions. 然後用此在strip過的ELF中查詢.
另外我們還可以根據程式碼的結構來查詢, 例如如果含有switch case, 那麼在IDA Pro中一般也會反彙編有jumptable. 而且使用IDA Pro的虛擬碼生成外掛也可以得到類似的程式碼:
可以看到裡面的case 12下面的Line97 98和source code中對應:
其中裡面的case值為0x12:
如何patching變更
如何替換patched後的檔案
普通檔案直接remount 分割槽,然後cp即可, 但是某些檔案在正常模式無法替換. 可以參考:
製作patching tools
如果是特徵值查詢替換, 那麼patching tools可以直接用C/C++等編寫, 也可以使用現有的框架, 例如:AT4RE Patcher.
直接寫的話, 也可以簡單粗暴的直接fread, 然後memcpy, 然後替換, 然後fwrite即可.
另外patching tools如果使用AT4RE製作, 那麼生成的exe可以在Linux下面藉助Wine執行.
如果使用C/C++編寫, 那麼在Windows下面可以使用msys2來靜態(-static)編譯, 然後只需要拷貝一個msys2的dll即可到處執行, 也相當於只需要寫一次即可在Linux與Windows下面執行.
如何讓自己的Patching tools/破解機適應多個版本的Android
要完成這項任務,我們需要先檢視和跟蹤程式碼在不同版本Android中的變化,這個可以使用git來檢視, 例如servicemanger:
我們可以看到從Android4.4.2到Android 4.4.4_r1幾乎沒有變化, 那麼我們的patching tool就可以適配這些版本. 如果有變化的話, 我們需要更改patching tool中的特徵匹配pattern. 由此來選擇如何search & replace.