前言
因為某個機緣,我拿到一個賽車app,玩了一會想買個裝備,居然要我掏錢包,作為一名cracker,我覺得我的尊嚴受到了嚴重的蔑視(無奈錢包空空),我覺得要捍衛我那脆弱的玻璃心(錢包),所以,開干吧。我搜索了網上相關的帖子,發現這個apk的破解都是講了關鍵點都在哪里,沒有具體的關鍵點查找思路,所以我重新自己破解了一次,中間多次誤入歧途,我把自己的詳細思路發在這里,與所有的菜鳥共勉。
工具
Android killer v1.3.1.0 -- 用於搜索字符串和重打包,下文使用縮寫AK
Apktool Box v1.6.4 – 用於獲取調試啟動命令,下文使用縮寫AB
小米5 MIUI9.5.1.0 android7.0 – 用於運行apk
Android Studio 2.3.3(安裝 smalidea-0.0.5插件) – 用於調試smali源碼,下文使用縮寫AS
Jadx-gui 0.7.1 – 用於查看smali反編譯源碼,其實AK也有反編譯功能,但是AK的反編譯有的地方沒有jadx好看,我發現switchcase結構AK總是翻譯成ifelse結構。
搭建調試環境
重打包生成可調式APK
使用AS調試smali需要apk設置調試標識(不然AS會顯示錯誤:Unable to open debugger port (localhost:7800): java.net.SocketException "connection reset"),用AK打開apk,從工程管理器欄打開文件androidmanifest.xml,在application項添加如下代碼:
1
|
android:debuggable="true"
|
如下圖:
然后選擇菜單android->編譯,重打包APK。並安裝到米5里面。
調試啟動apk
使用AB打開apk,點擊啟動命令按鈕生成調試啟動命令,
將上圖中下面的命令拷貝到cmd中,啟動apk,手機中apk會停在調試啟動界面。然后查看apk的進程PID,
然后使用adb命令轉發調試到tcp端口8700,如下:
接下來,使用AS建立smali源碼的工程,並配置調試端口,這部分內容網上有現成的教程,這里就不多說了,建立完了之后,點擊調試按鈕,AS下面顯示日志如下表示啟動調試成功:
到此,調試環境搭建成功,下面開始破解。
破解過程
思路一:錯誤信息提示
老思路,先是試用一下。點擊游戲的購買,會彈出一個購買的框,需要輸入手機號和驗證碼,這里隨便輸入一個手機號和驗證碼,點擊確認支付,不出意外會失敗,並彈出一個提示框:
好了,這是我的第一個第一個失敗點。我的思路是,既然它給了這個錯誤提示,那么顯示這個錯誤的地方肯定有支付成功和失敗的邏輯,那么我修改了這個邏輯,就可以了。OK,下面就是如何找到這個判斷的邏輯。沒錯,字符串查找,最傳統的方法,在android里面搜字符串,不能直接搜漢字,需要將它轉為unicode碼,然后將“短信驗證碼驗證失敗”轉換為unicode碼,則為“\u77ed\u4fe1\u9a8c\u8bc1\u7801\u9a8c\u8bc1\u5931\u8d25 ”,結果沒有找到,好吧,是不是字符串拼接出來的呢,短一點,搜“短信驗證碼”,然后有了搜索結果
不好意思,我是小菜看不懂smali,將其轉換為java源代碼,
原函數太大了,這里只貼了一部分,看着邏輯真不像,那么到底是不是這個邏輯呢?驗證的辦法很簡單,在這里下個斷點,然后,沒有斷下來。那么在函數入口下個斷點,還是沒有來。好吧,這個思路以失敗告終。后來跟蹤代碼我才知道這個支付框是從so里面彈出來的。嗚嗚。。
思路二:網絡帖子借鑒
之前搜過類似的帖子,帖子說是搜字符串“支付成功”,所以我將字符串轉化為unicode碼,到AK里面去搜索,然后搜索到三個結果:
同樣,驗證這里是不是判斷是否支付成功的邏輯代碼,在每個字符串所在的函數頭部設置斷點,這里沒有在字符串所在的位置設置斷點是因為我是不會掏錢包的,所有的操作都是支付失敗的操作,支付成功的代碼邏輯肯定是不會來的,所以在函數頭部設置斷點,然后,點擊確認支付沒有斷下來,但是當我點擊關閉按鈕時,卻成功的斷在了第一個函數中。這是意外的驚喜。然后使用jadx查看這個函數的邏輯:
代碼的邏輯很清晰,檢查參數resultCode的值,然后判斷是否支付成功,所以這里很有可能就是我要的關鍵代碼邏輯。驗證一下,把所有的switch結構都改成到case1分支。Smali代碼的語法是把所有的case放到一塊,如下
上面代碼的意思是將switch里面的值與1比較,結果為0則跳轉到pswitch_0標號,結果為1則跳轉到pswitch_1執行,所以這里把所有的標號都改為pwtich_0,並在下面添加一行,這樣默認情況也會跳轉到pswtich_0,即三種不管resultCode為何值,都會跳轉到成功的分支,如下:
最后,編譯重打包,安裝測試,成功,OK。至此,成功破解了這個app的內功功能。
其實,當初我並沒有按下支付框的關閉按鈕,所以我走的是另一條思路,如果看到這里你還有耐心,可以看下我的彎路,哈哈。
思路三:我的彎路
當在三個“支付成功”字符串所在的函數設置斷點,點擊手機上確認支付按鈕時,三個斷點都沒有來。然后我換了搜所的字符串為“支付失敗”,然后搜到6個結果
我在6個結果的字符串所在的函數頭部設置斷點,點擊確認支付按鈕,結果所有的斷點都沒有來。后來我就去網上搜帖子,見到有帖子說“支付失敗”所在的一個函數checkPayResultTest的調用者在用戶點擊購買時被調用。所以我轉變了思路,放棄了直接查找判斷是否支付成功的代碼,而是查找能在支付過程中會被執行到的函數,然后我可以從這個函數跟蹤到判斷支付的代碼。使用jadx打開apk,定位到函數checkPayResultTest,右鍵find usage,
然后查看其調用函數,
在此函數的頭部設置斷點,當我點擊購買時,程序斷在了這里。這是個喜訊。此時,我發現手機上的支付框還沒有彈出來,所以我想,我可以從這里跟蹤下去知道支付框彈出,然后找到確認支付按鈕的回調。於是我一路F7單步步入跟進去,當調試跟蹤到下圖中的紅線標識部分時我跟不進去了:
通過查資料我才知道,這是jni調用,我這才知道了,為什么我之前設置的失敗斷點都沒有來,原因最后掉用到so庫里了。那怎么辦,難道我要算調試so文件么?然后我仔細觀察了這個函數調用,發現這里有個回調,於是我找到回調定義的地方,
哈哈,是不是很眼熟,在回調里面設置斷點,然后這次我鬼使神差的點擊了支付框的關閉按鈕,然后斷下來了。哈哈。再然后的操作就如同上個思路一樣了。這就是我的彎路了。
思路四:程序的“漏洞”
這里怎么說呢,其實也不算是程序的漏洞,而是程序員的一種編程習慣。我稱之為“漏洞”,通過這個“漏洞”可以定位到支付功能必須執行的某些代碼。思路是這樣的,我發現在程序的代碼中有很多調試日志輸出,我如果能修改程序輸出日志,那么當我使用支付功能的時候,相關代碼會輸出相關調試信息,然后我搜索輸出的字符串,不就可以定位到支付功能一定會調用的代碼嗎!
首先,隨便找一段代碼,看看它是如何輸出的,
從上圖可以看出,調試信息的輸出是通過一個叫LogUil.iT函數實現的,找到這個函數:
這里會看到,代碼中有個DEBUG標志,如果此標志為真則輸出調試信息,然后找到這個標志賦值的位置:
本來以為這個標志是會在構造里面賦值的,沒想到居然是判斷sdcard路徑下有沒有alg,哈哈,那就更簡單了,我在手機的sdcard下面放了一個alg文件,
然后打開DDMS,啟動程序,點擊購買,DDMS日志視圖里面發現如下信息:
到AK里面去搜索字符串“支付接口被游戲調用”,哈哈,有一條結果:
轉到java源代碼:
在AS中對應位置下斷點,程序成功斷下來啦。哈哈。
總結
雖然走了很多的彎路,但是也發現了很多有趣的東西。例如通過日志來定位代碼,so中彈出對話框是無法在java中找到關鍵點的,要多試幾種別的操作。
其它
附件太大,這里給個鏈接吧:https://pan.baidu.com/s/1P40TZLvJXV727-mlJLNIdA
本帖子如果引起任何其它負面的影響,請版主直接刪帖即可。
如果轉載,請聲明來自看雪。