使用IDA除錯Android原生程式
今天已是國慶的第五天,白天去武館訓練過後,晚上回來品一杯西湖龍井,更一篇部落格,一來幫助需要之人,二來加深自己的理解。
下面就說關於在IDA中Android so的動態除錯的問題以及在so的三個層次下斷點的操作。
問題篇:
1.動態除錯的作用以及與我們常說的脫殼區別之處?
2.IDA的下斷點除錯的原理?
3.有無反除錯的步驟區別?以及原理?
4.反除錯與反附加的區別?
5.IDA動態除錯so時有哪三個層次?以及如何下斷點?
注意:so的動態除錯與脫殼在步驟上有很多的相似之處,關於脫殼在後面會詳細介紹加殼以及脫殼的發展歷程。
解答原理篇:
第一個問題:
曰:動態除錯作用有二:
其一:dump記憶體,即:找準時機dump出解密後的正確檔案;
其二:檢視每一步狀態,進一步分析出正確的邏輯;
脫殼只是我們在除錯系統級別的.so檔案後,找準時機dump出正確而真實的.so檔案,而動態除錯只不過是手動脫殼的一種表現方式。
第二個問題:
曰:(由於師哥說面試時喜歡問,此處列出來)
下斷點原理:
由於下斷點有硬體斷點和軟體斷點,我們在這裡只說IDA中的軟體斷點原理:
X86系列處理器提供了一條專門用來支援除錯的指令,即INT 3,這條指令的目的就是使CPU中斷(break)到偵錯程式,以供除錯者對執行現場進行各種分析。
當我們在IDA中對程式碼的某一行設定斷點時,即:F2,偵錯程式會先把這裡的本來指令的第一個位元組儲存起來,然後寫入一條INT 3指令,因為INT 3指令的機器碼為11001100b(0xCC)當執行到這的時候CPU會捕獲一條異常,轉去處理異常,CPU會保留上上下文環境,然後中斷到偵錯程式,大多數偵錯程式的做法是在被除錯程式中斷到偵錯程式時,會先將所有斷點位置被替換為INT 3的指令恢復成原來的指令,然後再把控制權交給使用者。這樣我們就可以愉快的開始除錯了。如下圖所示也是寫偵錯程式的原理圖:
第三個問題:
曰:先說無反除錯:
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(執行許可權要給)
6.再開一個cmd
adb forward tcp:23946 tcp:23946(埠轉發,除錯手機上的某個程序要有協議支援通訊)
7.開啟待除錯的應用程式,就可以愉快的除錯了
再來說有反除錯:
曰:在很多情況下我們遇到的是有反除錯並且用上面的步驟,附加進去以後直接就退出了,這樣的例子數不勝數,那就是反除錯惹的貨。
這時候我們就要改變除錯戰略了
在上文的基礎上:
1.啟動android_server;
2.埠轉發adb forward tcp:23946 tcp:23946;
3.adb shell am start -D -n 包名/類名;
(說明:以啟動模式啟動,是停在載入so檔案之前,報名在AndroidMainfest檔案中可以找到)
4.開啟IDA,附加上對應的程序之後,設定IDA中的load so的時機,在debug options中設定一下,後面會有實戰部分;
5.adb forward tcp:8700 jdwp:程序號;(jdwp是後面jdb偵錯程式的協議,轉換到待除錯的指定的應用程式);
6.jdb -connect com.sun.jdi.SocketAttach:hostname=localhost,port=8700(jdb進行附加);
7.可以愉快的下斷點,開始除錯了;
第四個問題:
曰:反除錯就是阻止你進行動態除錯所採用的一種手段,在下一篇中會進行具體的講解反除錯的手段,以及解決反除錯的辦法。
反附加,在這塊重要的是說jdb的反附加,很多情況下jdb會附加不上,就是會出現“無法附加到目標的VM”這樣的問題那是因為在每個應用程式下,有這個android:debuggable="true"才能除錯,因為篇幅問題,照樣會在下一篇中會針對反附加尋找目前所有解決辦法。
第五個問題:
曰:我們知道在so的載入時候有個這個過程:
.init->->.init array->->JNI_Onload->->java_com_XXX;
還有我們在脫殼的過程中會在一些系統級的.so中下斷點比如:fopen,fget,dvmdexfileopen,等等
而.init以及.init_array一般會作為殼的入口地方,那我們索性叫它外殼級的.so檔案
這裡歸納為三類:
應用級別的:java_com_XXX;
外殼級別的:JNI_Onload,.init,.init_array;
系統級別的:fopen,fget,dvmdexfileopen;
對於在應用級別的和系統級別的就不說了比較簡單容易理解,這裡也是在實現篇中會重點說的,看到上面的.so的載入執行過程我們知道如果說反除錯放在外殼級別的.so檔案的話我們就會遇程式在應用級核心函式一下斷點就退出的尷尬,事實上多數的反除錯會放在這,那麼過反除錯就必須要在這些地方下斷點,那麼我們就重點的說如何在.init_array和JNI_Onload處理下斷點。
實現篇:
這裡我們會拿阿里有一年的比賽樣本,會放在附件中。
在JNI_Onload處下斷點方法一:(雙開定位)
1.啟動android_server;
2.埠轉發以及除錯模式啟動:如圖所示:
3.開啟IDA,設定
4.附加上對應的程序進去之後如圖:
5.這一步很重要在Debugger option下面選擇這三個選項(讓在load so的每個介面處停下來)
6.jdwp協議埠轉發
7.jdb附加
8.F9執行,忽略提示框;這時候執行到linker處,如圖:
9.這時候找JNI_Onload的絕對地址:
基地址+相對地址;
基地址為:ctrl+s顯示為:
相對地址,用IDA靜態分析libcrack.so可得到相對地止:
絕對地址為:4151E000+1B9C=4151FB9C
按下“G”鍵輸入4151FB9C
如圖所示:按下F2下好斷點,再按F9執行到斷點處就可以愉快的除錯了
在JNI_Onload處下斷點方法二:(簡單好用)
1.首先把要分析的libcrackme.so檔案拉進IDA裡面在要下斷點的JNI_Onload處下好斷點如圖所示:
2.啟動android_server與上面一樣;
3.埠轉發以及除錯模式啟動:如圖所示
4.先設定一下Debugger 如圖所示
5.IDA進行附加程序回到之前靜態分析libcrackme.so的IDA介面單擊Debugger -> Process options 配置除錯資訊,這裡只需配置hostname為localhost,其餘的保持預設設定即可
6.單擊Debugger -> Attach to process進行附加程序
7.jdwp轉發(當然開啟DDMS就不需要這一步了)jdb附加
8.F9執行一路取消就OK,得到如圖所示:
是不是很簡單??
在.iniy_array處下斷點(與上面方法二雷同)
得到的結果是:
OK,搞定
在JNI_Onload處下斷點方法三:(適合於脫殼的時候)
1.可以根據看原始碼,對應不同版本的系統原始碼就會發現一點,如下在vm/Native.cpp路徑下:
2.我們逆向去看,首先把系統中的libdvm.so a db pull出來,拉到IDA中去分析;
找到JNI_Onload處進行分析:F5可以看到,首先V20進行“JNI_Onload”符號查詢,同時在V23有對V20的呼叫,
回到ARM指令處可以看到如下:
0x50008就是偏移處,這個時候我們就開始下斷:
載入上要除錯的APK以後,找到libdvm.so的基址,然後加上50008,下斷點。如果看不到彙編,那就P一下,下斷點,這個比較適合於脫殼的時候。
----------------------
1.動態除錯一般的Android原生程式
例項程式:debugnativeapp
①在Android裝置中新增、配置android_server程式
先將IDA目錄下的./dbgsrv/android_server程式拷貝到Android裝置下:
adb push[IDA]/dbgsrv/android_server /data/local/tmp/
配置檔案許可權屬性,使其為可執行檔案:
adb shell chmod 755/data/local/tmp/android_server
②將例項程式新增到Android裝置
adb push debugnativeapp/data/local/tmp/
adb shell chmod 755/data/local/tmp/debugnativeapp
③啟動除錯服務
adb shell/data/local/tmp/android_server
④進行埠轉發,使PC埠與Android埠可進行互動 adb forward tcp:23946tcp:23946 ⑤啟動IDA,進行程式除錯 啟動IDA的32bit程式,點選:Debugger-Run-RemoteArmLinux/Android debugger,開啟除錯程式的設定對話方塊: Application:對應除錯程式所在的路徑 Directory:對應除錯程式所在的目錄路徑 HostName:輸入localhost,Port:PC埠 點選OK,IDA便可進入除錯介面: IDA動態除錯的小技巧 ①除錯快捷鍵: F2設定斷點,F4執行到滑鼠位置,F7單步步入,F8單步步過,F9執行程式 F5/Tab 將相應的簡單彙編代表粗略轉換為C/C++程式碼 ②將IDA誤識別的程式碼手動轉為ARM彙編程式碼 如下圖,程式在Thumb狀態執行BX PC,PC最後一位為0,應是跳轉到ARM狀態,但應IDA仍誤識別為16位的Thumb,所以不是正常的程式。 如果按快捷鍵C來直接強制轉換,將不成功。應先通過Edit-Segment-Change segment register value(Alt+G),將Value改為0x00,這時程式會變為下邊CODE32的編碼方式,但仍非正常轉換 再按下U,轉為未定義 最後通過快捷鍵C,來轉換便可成功。如是ARM轉為Thumb狀態,則相似(將Value改為0x01) ③修改二進位制碼並儲存修改程式 修改:Edit-PatchProgram-Change byte 儲存:Edit-PatchProgram-Apply patches to input file 2.除錯Android原生動態連結庫 例項:debugjniso.apk ①將apk安裝在裝置中,執行後介面如下,點選“設定標題”按鈕,便會呼叫動態連結庫libdebugjniso.so中的jniString()方法返回一個修改標題欄的字串。在分析加殼程式的殼時,應以除錯方式來啟動除錯程式(adb shell am start-D -n packagename/activityname) ②啟動除錯服務,進行埠轉發 adb shell/data/local/tmp/android_server adb forward tcp:23946tcp:23946 ③啟動IDA進行程式除錯 點選Debugger-Attach-RemoteArmLinux/Android debugger,開啟設定框 HostName:輸入localhost,Port:對應PC埠 點選OK,便會彈出附加Android程序的對話方塊,選中程序com.droider.debugjniso ④使用jdb來連線上apk的java層 jdb -connectcom.sun.jdi.SocketAttach:hostname=127.0.0.1,port=8601 程式所對應的埠可通過eclipse中的devices視窗檢視,執行上邊jdb的命令後,便會在連線上的程式前顯示綠色昆蟲 ⑤確定jniString()方法的偏移地址 從apk中取出動態連結庫檔案,並在另啟的IDA例項上載入,在反彙編程式碼中定位方法的程式位置,由下圖可知,jniString()方法在相應段中的偏移地址為:0xC38(一般固定不變) ⑥確定jniString()方法當前的基地址 回到除錯的IDA,使用Ctrl+S快捷鍵開啟段選擇對話方塊,可查詢到libdebugjniso.so當前的基地址為:4B1F2000(隨載入改變) ⑦定位到jniString()方法的程式位置 由記憶體地址=基地址+偏移地址(4b1f2000:00000c38),可得當前jniString()方法的記憶體地址為:0x4b1f2c38,使用快捷鍵G開啟地址跳轉框,輸入記憶體地址 ⑧設定斷點,進行除錯 跳轉在jniString()方法的程式位置,在0x4b1f2c38行設定斷點(F2) 點選工作欄上的綠色箭頭(F9)讓程式執行起來,然後按下Android上的“設定標題”按鈕,程式會中斷在0x4b1f2c38行,便可進行相應除錯。如按F9執行程式,標題修改結果如下: 3.另一種除錯動態連結庫的方法(無需用到jdb) ①先使用IDA來載入libdebugjniso.so,定位到jniString()方法的程式位置,並在程式開始處設定下斷點 ②點選Debugger-Processoptions,將HostName設定為localhost,然後點選Debugger-Attach to Process,彈出附加Android程序的對話方塊,選中程序com.droider.debugjniso ③載入程序後,便會彈出警告框,詢問兩個檔案是否相同,選擇same ④點選Debugger-Breakpoints-Breakpointlist檢視斷點列表,可發現原先設定的斷點在載入動態庫後仍然存在,且定位到相應的記憶體地址 ⑤進入斷點位置,點選工作欄上的綠色箭頭(F9)讓程式執行起來,然後按下Android上的“設定標題”按鈕,程式會中斷在0x4b1f2c38行,便可進行相應除錯。 |