背景
公司某同事,想通過python腳本自動調用某應用接口,以達到自己的目的(你懂得)。但是通過抓包發現,幾乎所有接口里面都兩個公共字段"timestamp"和"sign"。timestamp是時間戳很好得到,但是"sign"卻不停在變化,猜測后端會根據"sign"字段值,對數據做校驗。于是開啟了對該App逆向路程。
內容
- 逆向工具
- 原生層逆向
- so逆向
- so動態調試
逆向工具
-
apktools
- apktool d [apk]
將apk反編譯smali文件形式,通過這種方式,得到的資源文件不會出現亂碼的,并且可以重新編譯成apk,簽名安裝。 - apktool b [smali目錄]
重新生成apk文件,記得要重新簽名,不然安裝不了。
- apktool d [apk]
-
dex2jar
將apk解壓得到的dex文件反編譯成jar文件,方便查看- ./dex2jar/d2j-dex2jar.sh [dex文件]
生成jar文件
- ./dex2jar/d2j-dex2jar.sh [dex文件]
jd-gui
查看jar文件,配合搜索可以方便快速定位代碼位置jadx
除了可以像jd-gui查看jar文件,還可以對混淆字段重新取名字,更加方便查看,更多功能請自行查看。ida
查看so文件,so動態調試等功能
原生層逆向
dex生成jar文件
更具前面介紹,接口請求參數里面都會添加"sign"字段,那么我們可以全局搜索"sign",查看跳轉路徑,找到"sign"字段值生成的地方,最終發現native層把接口傳遞參數轉成json字符串后,調用JNI層方法返回一個字符串,這個字符串就是"sign"的值。這下無可奈何了。-
apk生成smali文件
為了確定"sign"值就是調用JNI方法生成的,于是我找到了登錄頁面,找到登錄按鈕,通過這里查看調用邏輯。-
如何快速定位到“登錄按鈕”點擊事件處理的地方
由于點擊事件監聽處,都是10進字的常量值,所有并不知道那個是“登錄按鈕”,而且該apk的資源文件混淆,無法直接找到登錄頁面的布局文件。這時,可以借助Android Studio查看apk功能,找到resources.arsc,把10進字轉換成16進字,然后在resources.arsc中找到對應的文件,屬性名。
截屏2020-12-17 下午3.42.54.png
-
so逆向
用ida打開so文件,入下圖:
該apk調用的是getApiSign這個jni方法,返回的"sign"值。
-
arm轉c
F5快捷鍵可以將arm指令轉化成可讀的C語言
截屏2020-12-17 下午4.35.08.png
轉成C語音后,就是這樣,如果能夠看懂,當然可以直接找出計算"sign"值算法,如果看不懂,那么可以使用so動態調試,查看相應的變量值。
注意:這里要用32的ida,64位不支持轉換且動態調試會報錯。
so動態調試
so動態調試,需要root手機一臺。
-
復制android_server(IDA目錄>dbgsrv>android_server)到設備中
1. adb push android_server /sdcard 2. adb shell 3. mount -o rw,remount /system (出于安全考慮,操作完之后,把文件改回只讀屬性:mount -o ro,remount /system) 4. mv /sdcard/android_server /system/bin 5. chmod 777 /system/bin/android_server
-
啟動android_server
- su do 切換到root權限(如果不是root權限,后面ida調試找不到應用進程)
- ./android_server (root權限下啟動服務 )
- 新啟命令行執行:adb forward tcp:23946 tcp:23946建立端口轉發。
- adb forward --list
查看端口映射列表 - adb forward --remove tcp:23946
移除建立的端口轉發
-
在手機上運行該apk
由于其它原因,我把計算"sign"字段的so庫導入我自己新建的android工程,然后寫了一個簡單的點擊事件去調用該jni方法。
截屏2020-12-17 下午5.01.21.png -
ida動態調試so
打開32位ida,點擊"go"打開一個空白頁,選擇要調試的進程。
截屏2020-12-17 下午5.03.36.png
-
使用Ctrl+S找到需要調試so的基地址:EF606000
截屏2020-12-17 下午5.07.25.png -
然后通過另外一個IDA打開so文件(可以用64位的打開so),查看函數的相對地址:1794
截屏2020-12-17 下午5.10.50.png
那么得到了函數的絕對地址就是:EF607794,使用G鍵快速跳轉到這個絕對地址:
點擊左邊藍點,下斷點,點擊運行,然后在應用點擊,調用getApiSign JNI方法。
觸發getApiSign JNI方法調用后名,程序執行到圖中紅框地方,按F8進行單步調試,F9運行,通過R0寄存器找到算法使用的密鑰。
拿得密鑰后,就可以用其它語言實現。整個破解過程就到這里。
-
其它配置
這里是so在調試之前就已經加載,如果要停留在so加載的地方,那么可以打開Debugger setup,配置如下:
截屏2020-12-17 下午5.26.32.png 使用am命令
adb am start -D -n com.example.testarm/com.example.testarm.MainActivity
啟動應用,讓進程處于等待調試中,然后用ida attach進程,進行調試,適用在apk啟動時就加載so并且需要斷點so加載過程。-
這里為什么會斷在libc.so中呢
android系統中libc是c層中最基本的函數庫,libc中封裝了io、文件、socket等基本系統調用。所有上層的調用都需要經過libc封裝層。所以libc.so是最基本的,所以會斷在這里,而且我們還需要知道一些常用的系統so,比如linker我們知道,這個linker是用于加載so文件的模塊,所以后面我們在分析如何在.init_array處下斷點
還有一個就是libdvm.so文件,他包含了DVM中所有的底層加載dex的一些方法,我們在后面動態調試需要dump出加密之后的dex文件,就需要調試這個so文件了。