以前的安卓逆向都是在Java層上面的,可是當前大多數App,爲了安全或者效率問題,會把一些重要功能放到native層,因此這裏經過例子記錄一下使用IDA對so文件進行調試的過程並對要點進行總結。android
1、IDA經常使用快捷鍵總結shell
Shift+F12:快速查看so文件中的字符串信息,分析過程當中經過一些關鍵字符串可以迅速定位到關鍵函數;安全
F5: 能夠將ARM指令轉化爲可讀的C代碼,同時可使用Y鍵,對JNIEnv指
針作一個類型轉換,從而對JNI裏常用的JNIEnv方法可以識別;tcp
Ctrl + S: 有兩個用途,在IDA View頁面中能夠查看文件so文件的全部段信息,在調試頁面能夠查看程序中全部so文件映射到內存的基地址。tips:在進行so調試過程當中,頗有用的一個小技巧就是IDA雙開,一個用於進行靜態分析;一個用於動態調試。好比說調試過程當中要找到一個函數的加載到內存中的位置,編輯器
G:能夠在調試界面快速跳轉到指定的絕對地址,進行下斷點調試。若是跳轉到目的地址以後,發現是DCB數據的話,可使用P鍵進行轉化;函數
F7:單步進入調試; F8: 單步調試。學習
2、IDA動態調試so步驟ui
對於沒有反調試的步驟:
1)adb push d:\android_server(IDA的dbgsrv目錄下) /data/local/tmp/android_server(這個目錄是能夠隨便放的)。
2) adb shell
3) su(必定要有root權限)
4) cd /data/local/tmp
5) chmod 777 android_server (給android_server可執行權限)
./android_server對本地設備端口進行監聽
6)再開一個cmd:
adb forward tcp:23946 tcp:23946(端口轉發,調試手機上的某個進程要有協議支持通訊)讓遠程調試端IDA能夠鏈接到被調試端
7)使用IDA鏈接上轉發的端口,查看設備的全部進程,找到須要調試的進程。具體步驟方法爲:在Debugger選項卡中選擇Attach,選擇android debugger,點擊Ok。
8)動靜結合方式(基地址+相對地址)肯定函數地址進行調試。操作系統
對於有反調試的步驟:
1)啓動android_server
2)端口轉發adb forward tcp:23946 tcp:23946
3)adb shell am start -D -n 包名/類名;出現Debugger的等待狀態
(說明:以啓動模式啓動,是停在加載so文件以前,包名能夠在androidmanifest文件中找到)
4)打開IDA,附加上對應的進程以後,設置IDA中的load so時機,即在debug options中設置;
5)運行命令:jdb -connect com.sun.jdi.SocketAttach:hostname=localhost, port=8700
6)點擊IDA運行按鈕,或者F9快捷鍵。.net
3、IDA動態調試實例
這裏咱們結合一個實例,來使用IDA對so文件進行調試。首先看一下apk在手機上的運行效果:
這裏須要輸入密碼,也就是拿到flag。
下面仍是常規操做,將apk放到android killer中對其進行反編譯。反編譯完成,查看AndroidManifest.xml文件:
程序的包名爲com.yaotong.crackme,主活動爲MainActivity. apk而且apk沒有通過加固處理。
使用jd-gui來查看對應的反編譯後對應的Java代碼:
這裏的程序邏輯仍是很簡單的,在btn的onClick方法中獲取用戶輸入的密碼,經過crackme庫中的原生函數securityCheck對輸入密碼進行校驗,若是正確則啓動一個新的活動顯示輸入正確;不然提示驗證碼校驗錯誤。
嗯,因此這裏的關鍵仍是對於原生函數securityCheck的分析。
接下來,從apk中提取出libcrackme.so文件(使用解壓縮軟件打開),並用IDA首先進行靜態分析。
這裏以Java_com_yaotong_crackme開頭的就是原生函數代碼了。
點進函數便可查看其ARM指令,這裏對於ARM指令就不作分析了,直接F5查看C代碼:
這裏有個while循環,能看出來是比較兩個字符串是否相等,而v6就是內嵌的比較字符串,點進去查看內容,發現字符串「awojiushidaan」,猜測這多是flag,在apk程序中輸入發現校驗錯誤,說明程序在運行時確定對其進行了處理。那麼接下來,咱們就對so進行動態調試。
首先獲取android_server文件,位於IDA的安裝目錄\dbgsrv\android_server,並將其放在設備的/data目錄下,修改運行權限,最後在root環境下運行:
接下來用adb forward tcp:23946 tcp:23946使得Pc端的IDA連上這個端口,在IDA中Debugger選項中選擇Remote ARMLinux/Android debugger進行Attach操做,
因爲android_server在設備中具備root權限,所以可以獲取到設備中的進程以及其調試信息:
選擇對應的進程便可進入調試窗口。接下來就是找到函數下斷點了,這裏打開兩個IDA,經過對libcrackme.so靜態分析獲取到原生函數的相對地址,在動態分析過程當中(此時so文件已經動態加載),crtl+s可以得到libcrackme.so在內存中的基址,基址+相對地址就定位到咱們要動態調試分析的函數了。在函數的開始指令處下斷點,F9運行,可是動態調試並無斷在咱們指望的地方,而是直接退出了。
所以這裏確定是作了反調試檢測。基本原理是這樣的:IDA使用android_server在root環境下注入到被調試的進程中,用到的技術是Linux中的ptrace,當Android中的一個進程被另一個進程ptrace以後,在其status文件中有一個字段TracerPid能夠標識是被哪個進程trace了。(Linux中的/proc/pid/status文件,操做系統課程設計中有涉及到)。這裏有兩個地方是so動態加載完畢前執行的,.init_array是一個so最早加載的一個段信息,時機最先,如今通常so解密操做都是在這裏作的;JNI_OnLoad是so被System.loadLibrary調用的時候執行的,它的時機早於native方法的執行。
接下來咱們就嘗試斷在JNI_OnLoad函數指令處,首先在IDA調試選項中作以下設置:
可是因爲被調試程序一運行就會執行static中的語句,所以須要讓程序停在加載so文件以前,這裏能夠添加watiForDebugger,或者使用更加簡單的方法,使用debug方式來啓動:
adb shell am start -D -n com.yaotong.crackme/.MainActivity
接下來在IDA中進行attach,可是此時發現沒有RX權限的so文件,說明so並無被加載到內存中去,那麼就須要讓程序跑起來,使用以下命令:
jdb -connect com.sun.jdi.SocketAttach:hostname=127.0.0.1, port=8700
可是此時出現以下錯誤:
出現這種問題大多數是被調試的程序不可調試,能夠查看apk的android:debuggable屬性,因此這裏須要添加這個屬性爲true再進行回編譯:
重編譯以後按照以下步驟操做斷在so加載過程當中:
1)運行命令:
adb shell am start -D -n com.yaotong.crackme/.MainAcitivity
出現Debugger等待狀態
2)啓動IDA進行目標進程的Attach操做
3)運行命令:
jdb -connect com.sun.jdi.SocketAttach:hostname=127.0.0.1,port=8700
4)設置Debugger Option選項
5)點擊IDA運行按鈕
這樣咱們就能夠看到可執行的libcrackme.so了。
其在動態加載過程當中內存的起始地址爲75545000,在靜態分析的IDA中查看JNI_Onload函數的相對地址爲1B9C,從而在動態分析對JNI_Onload函數下斷點。
F8單步調試,結合反彙編出來的C代碼對arm進行分析,調試到以下所示地方時發現調試結束:
觀察此時R7寄存器的值,註釋提示爲pthread_create函數的地址,在操做系統課程中學過,這是Linux中建立一個線程的操做,進而能夠猜錯,程序就是在這裏進行了反調試的檢測,相關的函數即爲pthread_create函數第三個參數所對應的回調函數。
在靜態分析中咱們查看這個回調函數sub_16A4對應的反彙編代碼:
這裏應該就是在新的線程中不斷重複檢測是否被調試,繼續跟到sub_130C()函數中看下:
在動態調試過程當中,經過查看函數的參數對應的內存地址和函數地址對應的註釋能夠發現其中調用了getpid,fopen,sprintf等函數,而且fopen傳入的參數是/proc/31305/status。所以這裏就是反調試檢測的代碼了。接下來的代碼應該就是用fgets循環讀取一行,當讀到TrackerPid字段值時比較後面的值是否爲0.在這裏調試的過程當中咱們是直接單步查看寄存器的值以及內存發現fopen函數的,還能夠用一個小技巧:直接在libc.so中的fopen函數處下一個斷點,而後再hex view窗口中設置數據與R0同步,也就是fopen函數的第一個參數,這樣也能夠跟到相應的關鍵位置;一樣,接下來能夠繼續在strstr函數處下斷點,並判斷什麼時候讀到TrackerPid字段值。
肯定了apk中的反調試代碼,咱們只須要將BLX R7這段指令幹掉便可,這樣apk就不會新建線程去執行檢測代碼了。在靜態分析的IDA中查看:
指令的機器碼爲37 FF 2F E1, 這裏咱們在編輯器中查找這段機器碼並將其替換爲Nop指令,在arm中對應00 00 00 00.
在IDA中查看修改後的so文件對應指令,
能夠看到指令成功修改了。
接下來將修改以後的so文件替換到apk中並從新編譯安裝到手機上。
咱們從新開始動態調試,這裏不須要再給JNI_Onload下斷點了,由於已經修改了反調試功能,因此只須要進行如下步驟:
1)啓動程序
2)Attach進程
3)關鍵函數下斷點
首先定位到Libcrackme.so在內存中的基地址,
在靜態分析IDA中查看原生函數的相對地址爲11A8,所以在動態調試中基地址加上相對地址處下個斷點。
能夠看到,反調試功能失效了,此時成功斷在了原生函數的arm指令代碼處。咱們接下來進行調試分析,經過查看靜態arm彙編指令,咱們知道這裏就是最後比較字符串是否一致的關鍵代碼:
這裏重點關注兩個寄存器中存放的字符串的起始地址,咱們在這裏下個斷點,直接運行到此處:
咱們在hex-view中直接跟隨寄存器R0,能夠看到這裏正好是咱們輸入的密碼,這裏運氣比較好,並無對輸入的密碼進行處理,那麼此時R2寄存器對應的地址確定就是真正的flag了,觀察內存中的字符串以下:
到這裏咱們應該就成功得到了flag。
4、小結
這裏咱們學習瞭如何經過IDA調試so文件,並學習瞭如何移除程序中的反調試功能,收穫良多!
參考:安卓動態調試七種武器之孔雀翎 – Ida Pro
from:https://blog.csdn.net/hbhgyu/article/details/81321923