最近好幾天來一直在看動態調試。首先是這一篇(http://www.52pojie.cn/forum.php?mod=viewthread&tid=293648)里面介紹了多種IDA動態調試的情形,比如調試JNICALL,調試JNI_Onload等等。步驟大概都是這樣:
執行android_server
端口轉發 adb forward tcp:23946 tcp:23946
調試模式啟動程序 adb shell am start -D -n 包名/類名
IDA附加
靜態找到目標函數對應所在模塊的偏移地址
Ctrl+S找到對應模塊的基地址,兩個地址相加得到最終地址
G跳轉至地址,然后下斷
F9運行
執行jdb -connect com.sun.jdi.SocketAttach:hostname=127.0.0.1,port=8700
斷下,進行調試。
但我一直不太懂,接下來說的「進行調試」是干什么。
后來又看了http://1.xbalien.sinaapp.com/?p=342這篇,assest目錄下存在兩個jar包,分析說可能是dex加密保存在了這里。執行真正代碼的時候會進行解密那就可以直接dump出明文dex了。(修復結構、反調試在這題並不考慮)
文中介紹了兩種初級dump的方法,第一種是gcoredump。第二種是通過在dvmDexFileOpenPartial斷點dump。
關於dvmDexFileOpenPartial:
int dvmDexFileOpenPartial(const void* addr, int len, DvmDex** ppDvmDex);
第一個參數就是dex內存起始地址,第二個參數就是dex大小。所以在這個函數下斷點可以直接dump出明文dex。
我腦補一下原理,大概就是說它解密完了之后在內存里打開這個明文dex,然后我們要拿到它的這兩個參數,就可以保存成解密后的dex了!(還是不太明白它的適用性)
用這種方法調試的步驟跟前面列出的步驟基本一致,過程中遇到很多問題,首當其沖是輸入jdb -connect com.sun.jdi.SocketAttach:hostname=127.0.0.1,port=8700的時候,提示「致命錯誤,無法附加到指定VM」。我看到很多人都沒有用到這個jdb指令,也不知道這個指令到底什么時候用。后來看到一些文章他們沒用這個命令,而且不用算地址。比如:http://blog.sina.com.cn/s/blog_92b6d74d0102uyds.html#cmt_3043762
我提問:
請問你是怎么在G中填寫dvmDexFileOpenPartial就跳轉到dvmDexFileOpenPartial函數的?是不是應該先在libdvm.so中找到dvmDexFileOpenPartial函數的偏移地址,再用偏移地址加上調試程序的libdvm.so的基址,再用G跳轉,下斷嗎?
人家說他用的是正版IDA6.6。我用的是IDA6.5。
操作jni_onload是成功了,但我仍不知道下面該怎么調試。因為jni_onload里面保存了一些信息所以也去dump出來?可那個不是dex吧。
為什么無法附加指定VM,我還是覺得自己的地址算錯了,因為在調試Jni_Onload的時候,jdb就成功attach了,這里卻不行。但是,我分別用了2.3(Ray),4.0.4(Ray),4.2.2(Nexus 4)和4.4.4(L36h)的不同的libdvm.so測試,皆不行。回想調試Jni_Onload的時候,跟這個的區別是1.開了Eclipse,2.先F9+JDB 再下的斷點。等會兒再試試。
剛才試了一下,這次我像調試jni_onload一樣,步驟是F9 ——JDB命令(成功attach后程序中斷)——G到目標地址下斷點——再按一次F9觸發斷點,斷在了下的那個斷點的地方。這時候之前下斷點的地方呈現出libdvm.so中那個方法調用的地方一樣的匯編代碼,為什么之前同樣的地址什么都沒有呢。但是,這時候對應R0的地址是0,顯然是錯誤的。
而帖子中的順序是:下斷點,F9,JDB(附加后程序會中斷)。
還有個疑點,出現了這個:
destination可以指定就算了,source竟然也可以指定。。醉了。暫時不弄了。感覺好蠢,糾結於各個IDA各個版本和android各個版本的細微差別之間,試圖將所有的耦合全部嘗試一遍,卻仍然拿不到想要的東西。百度上沒有任何相關的資料。
好的,我假裝自己已經定位到那個dex的起始位置了,下面開始dump:
{ auto fp, dex_addr, end_addr; fp = fopen("F:\\dump.dex", "wb"); end_addr = r0 + r1; for ( dex_addr = r0; dex_addr < end_addr; dex_addr ++ ) fputc(Byte(dex_addr), fp); }
下面用smailview看dex。對於:http://1.xbalien.sinaapp.com/?p=342這道題,后面要學一下webview。
---------------------------------------
adb forward tcp:<本地機器的端口號> tcp:<模擬器或是真機的端口號>
例:adb [-d|-e|-s <serialNumber>] forward tcp:6100 tcp:7100 表示把本機的6100端口號與模擬器的7100端口建立起相關,
當模擬器或真機向自己的7100端口發送了數據,那們我們可以在本機的6100端口讀取其發送的內容,這是一個很關鍵的命令,
以后我們使用jdb調式apk之前,就要用它先把目標進程和本地端口建立起關聯。
jdb是一個支持java代碼級調試的工具,它是由java jdk提供的,存在於xxx\Java\jdk1.6.0_21\bin之下
通過attach方式進行調試
adb jdwp顯示所有可供調試的用戶進程
adb forward tcp:xxx jdwp:<pid>
jdb -connect com.sun.jdi.SocketAttach:hostname=localhost,port=xxx
調試模式啟動intent,手機上顯示waiting for the debugger to attach,這時候attach的時候ida顯示:
protocol version is 14 expected 17。
我是這樣做的,把android_server重新push到手機里,權限改成777,運行。
這時候用64bit的IDA打開提示類似address 4 epected 4
我是這樣做的,用32bitIDA打開。可以attach了。
----------一些摘自書上的內容----------
DDMS
DDMS(Dalvik Debug Monitor Server)就是動態調試的一個工具(不知Android L之后會不會改名--!)。DDMS提供文件瀏覽、Logcat、Method Profiling等功能。
定位關鍵代碼
1.代碼注入法
用Apktool反編譯得到smali,查找onClick(),比如要找程序注冊碼,仔細閱讀之后發現比對注冊碼與用戶輸入的函數
invoke-virtual {v1, v0},Ljava/lang/String;->equalsIgnoreCase(Ljava/lang/String;)Z
move=result v3
if-eqz v3, :cond_2
那么加入Log.v()來輸出v0寄存器:
const-string v3, "SN"
invoke-static {v3, v0},Landroid/util/Log;->v(Ljava/lang/String;Ljava/lang/String;)I
然后用Apktool打包、簽名,
用"adb logcat -s SN:v"輸出SN。
2.棧追蹤法
代碼注入配合Logcat好用但是需要閱讀大量反匯編代碼來找「輸出點」。
棧追蹤法也屬於注入的范疇。
比如要找一個Toast是啥時候被調用的,不用閱讀太多反匯編代碼,而是定位到Toast,然后在這一段之后加入
new Exception("print trace").printStackTrace();
對應smali在書上就不寫了。
然后打包簽名運行,
在CMD輸入"adb logcat -s System.err:V *:W"
會以堆棧的方式,先輸出java.lang.Exception print trace
然后輸出從程序啟動到printStackTrace()執行期間所有被調用過的方法。
3.Method Profiling 4.AndBug 5.IDA Pro..
另外參考:http://www.blogbus.com/riusksk-logs/271566148.html