安卓逆向菜鳥的初體驗

前言

因為某個機緣,我拿到一個賽車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 項添加如下代碼:

android:debuggable="true"

如下圖:

然後選擇菜單android->編譯,重打包APK。並安裝到米5裡面。

調試啟動apk

使用AB打開apk,點擊啟動命令按鈕生成調試啟動命令,

將上圖中下面的命令拷貝到cmd中,啟動apk,手機中apk會停在調試啟動界面。然後查看apk的進程PID,

然後使用adb命令轉發調試到tcp埠8700,如下:

接下來,使用AS建立smali源碼的工程,並配置調試埠,這部分內容網上有現成的教程,這裡就不多說了,建立完了之後,點擊調試按鈕,AS下面顯示日誌如下表示啟動調試成功:

到此,調試環境搭建成功,下面開始破解。

破解過程

思路一:錯誤信息提示

老思路,先是試用一下。點擊遊戲的購買,會彈出一個購買的框,需要輸入手機號和驗證碼,這裡隨便輸入一個手機號和驗證碼,點擊確認支付,不出意外會失敗,並彈出一個提示框,

好了,這是我的第一個第一個失敗點。我的思路是,既然它給了這個錯誤提示,那麼顯示這個錯誤的地方肯定有支付成功和失敗的邏輯,那麼我修改了這個邏輯,就可以了。

OK,下面就是如何找到這個判斷的邏輯。沒錯,字元串查找,最傳統的方法,在android裡面搜字元串,不能直接搜漢字,需要將它轉為unicode碼,然後將「簡訊驗證碼驗證失敗」轉換為unicode碼,則為「u77edu4fe1u9a8cu8bc1u7801u9a8cu8bc1u5931u8d25 」,結果沒有找到,好吧,是不是字元串拼接出來的呢,短一點,搜「簡訊驗證碼」,然後有了搜索結果,

不好意思,我是小菜看不懂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中找到關鍵點的,要多試幾種別的操作。

其它

附件太大,這裡給個鏈接吧:pan.baidu.com/s/1P40TZL

原文鏈接:[原創]記某app內購破解 - 安卓逆向菜鳥的初體驗

本文由看雪論壇 Yearthmain 原創

轉載請註明來自看雪社區

推薦閱讀:

攻擊TrustZone系列(Pt.2) -- 逆向高通TrustZone
堆溢出研究二
安卓U3D逆向從Assembly-CSharp到il2cpp
常見函數調用約定(x86、x64、arm、arm64)
No Power No Shell --- 非PE攻擊中的套路

TAG:Android | 遊戲 | 軟體逆向工程 |