綁定帳號登入

Android 台灣中文網

打印 上一主題 下一主題

[教程] 分析Android程式之解密Crackme

[複製連結] 查看: 3699|回覆: 6|好評: 0
跳轉到指定樓層
樓主
fam1001 | 收聽TA | 只看該作者 回帖獎勵 |倒序瀏覽 |閱讀模式
發表於 2016-3-24 11:09

馬上加入Android 台灣中文網,立即免費下載應用遊戲。

您需要 登錄 才可以下載或查看,沒有帳號?註冊

x
破解Android程式通常的方法是將apk檔案利用ApkTool反編譯,生成Smali格式的反彙編代碼,然後閱讀Smali檔案的代碼來理解程式的執行機制,找到程式的突破口進行修改,最後使用ApkTool重新編譯生成apk檔案並簽名,最後執行測試,如此循環,直至程式被成功破解。

1. 反編譯APK檔案

ApkTool是跨平台的工具,可以在windows平台與linux平台下直接使用。目前最新版本為1.4.3,Windows平台需要下載apktool1.4.3.tar.bz2與apktool-install-windows-r04-brut1.tar.bz2兩個壓縮包,如果是linux系統則需要下載apktool1.4.3.tar.bz2與apktool-install-linux-r04-brut1.tar.bz2,將下載後的檔案解壓到同一目錄下。進入到命令行的解壓目錄下,執行apktool命令會列出程式的用法:

反編譯apk檔案的命令為: apktool  d[ecode]  [OPTS]  <file.apk>  [<dir>]

編譯apk檔案的命令為: apktool  b[uild]  [OPTS]  [<app_path>]  [<out_file>]

那麼在命令行下進入到apktool工具目錄,輸入命令:
  1. $ ./apktool  d  /home/fuhd/apk/gnapk/nice/com.nice.main.apk  outdir
複製代碼


2. 分析APK檔案

如上例,反編譯apk檔案成功後,會在目前的outdir目錄下生成一系列目錄與檔案。其中smali目錄下存放了程式所有的反彙編代碼,res目錄則是程式中所有的資源檔案,這些目錄的子目錄和檔案與開發時的源碼目錄組織結構是一致的。

如何尋找突破口是分析一個程式的關鍵。對於一般Android來說,錯誤提示訊息通常是指引關鍵代碼的風向標。以書中的註冊示例為例,在錯誤提示附近一般是程式的核心驗證代碼,分析人員需要閱讀這些代碼來理解軟體的註冊流程。

錯誤提示是Android程式中的字符串資源,開發Android程式時,這些字符串可能硬編碼到源碼中,也可能引用 自「res/values」目錄下的strings.xml檔案,apk檔案在打包時,strings.xml中的字符串被加密存儲為「resources.arsc」檔案保存到apk程式包中,apk被成功反編譯後這個檔案也被解密出來了。

以書中2.1.2節執行程式時的錯誤提示,在軟體註冊失敗時會Toast彈出「無效用戶名稱或註冊碼」,我們以此為線索來尋找關鍵代碼。開啟「res/values/strings.xml」檔案,內容如下:
  1. <?xml version="1.0" encoding="utf-8" ?>
  2. <resources>
  3.     <string name="app_name">Crackme0201</string>
  4.     <string name="hello_world">Hello world!<string>
  5.     <string name="menu_settings">Settings</string>
  6.     <string name="title_activity_main">crackme02</string>
  7.     <string name="info">Android程式破解演示實例</string>
  8.     <string name="username">用戶名稱:</string>
  9.     <string name="sn">註冊碼:</string>
  10.     <string name="register">註冊</string>
  11.     <string name="hint_username">請輸入用戶名稱</string>
  12.     <string name="hint_sn">請輸入16位的註冊碼</string>
  13.     <string name="unregister">程式未註冊</string>
  14.     <string name="registered">程式已註冊</string>
  15.     <string name="unsuccessed">無效用戶名稱或註冊碼</string>    <!-- 就是這一行 -->
  16.     <string name="successed">恭喜您!註冊成功</string>
  17. </resources>
複製代碼

開發Android程式時,strings.xml檔案中的所有字符串資源都在「gen/<packagename>/R.java」檔案的String類中被標識,每個字符串都有唯一的int類型索引值,使用Apktool反編譯apk檔案後,所有的索引值保存在strings.xml檔案同目錄下的public.xml檔案中。

從上面列表中找到「無效用戶名稱或註冊碼」的字符串名稱unsuccessed。開啟public.xml檔案,它的內容如下:
  1. <?xml version="1.0" encoding="utf-8"?>
  2. <resources>
  3.     <public type="drawable" name="ic_launcher" id="0x7f020001" />
  4.     <public type="drawable" name="ic_action_search" id="0x7f020000" />
  5.     .......
  6.     <public type="string" name="unsuccessed" id="0x7f05000c"/>     <!-- 這是這一行 -->
  7.     .......
  8.     <public type="id" name="edit_sn" id="0x7f080002" />
  9.     <public type="id" name="button_register" id="0x7f080003" />
  10.     <public type="id" name="menu_settings" id="0x7f080004" />
  11. </resources>
複製代碼

unsuccessed的id值為0x7f05000c,在smali目錄中搜尋含有內容為0x7f05000c的檔案,最後發現只有MainActivity$1.smali檔案一處調用,代碼如下:
  1. # virtual methods
  2. .method public onClick(Landroid/view/View;)V
  3.     .locals 4
  4.     .parameter "v"
  5.     .prologue
  6.     const/4 v3, 0x0
  7.     ......
  8.     .line 32
  9.     #calls:
  10.     Locm/droider/crackme0201/MainActivity;->checkSN(Ljava/lang/String;Ljava/lang/String;)Z
  11.     invoke-static {v0,v1,v2}, Lcom/droider/crackme0201/MainActivity;->
  12.         #檢查註冊碼是否合法
  13.         access$2(Lcom/droider/crackme0201/MainActivity;Ljava/lang/string;Ljava/lang/String;)Z   
  14.     move-result v0
  15.     if-nez v0, :cond_0  #如果結果不為0,就跳轉到cond_0標號處
  16.     .line 34
  17.     iget-object v0, p0, Lcom/droider/crackme0201/MainActivity$1;->
  18.         this$0:Lcom/droider/crackme0201/MainActivity;
  19.     .line 35
  20.     const v1, 0x7f05000c  #unsuccessed字符串,就是這一句
  21.     .line 34
  22.     invoke-static {v0, v1, v3}, Landroid/widget/Toast;->
  23.         makeText(Landroid/content/Context;II) Landroid/widget/Toast;
  24.     move-result-object v0
  25.     .............
  26.     .............(略)
  27.     .............
  28. .end method
複製代碼

Smali代碼中新增的註釋使用「#」號開頭,".line 32"行調用了checkSN()函數進行註冊碼的合法檢查,接著下面有如下兩行代碼:
  1. move-result v0
  2. if-nez v0,  :cond_0
複製代碼

checkSN()函數返回Boolean類型的值。這裡的第一行代碼將返回的結果保存到v0寄存器中,第二行代碼對v0進行判斷,如果v0的值不為零,即條件為真的情況下,跳轉到cond_0標號處,反之,程式順序向下執行。

如果代碼不跳轉,會執行如下幾行代碼:
  1. .line 34
  2. iget-object v0, p0, Lcom/droider/crackme0201/MainActivity$1;->
  3.     this$0:Lcom/droider/crackme0201/MainActivity;
  4. .line 35
  5. const v1, 0x7f05000c        #unsuccessed字符串
  6. .line 34
  7. invoke-static {v0, v1, v3}, Landroid/widget/Toast;->
  8.     makeText(Landroid/content/Context;II)Landroid/widget/Toast;
  9. move-result-object v0
  10. .line 35
  11. invoke-virtual {v0}, Landroid/widget/Toast;->show()V
  12. .line 42
  13. :goto_0
  14. return-void
複製代碼

「.line 34」行使用iget-object指令獲取MainActivity實例的引用。代碼中的->this$0是內部類MainActivity$1中的一個synthetic字段,存儲 的是父類MainActivity的引用,這是Java語言的一個特性,類似的還有->access$0,這一類代碼會在後面進行詳細介紹。「.line 35」行將v1寄存器傳入unsuccessed字符串的id值,接著調用Toast;->makeText()建立字符串,然後調用Toast;->show()V方法彈出提示,最後.line 40行調用return-void函數返回。

如果代碼跳轉,會執行如下代碼:
  1. :cond_0
  2. iget-object v0, p0, Lcom/droider/crackme0201/MainActivity$1;->
  3.     this$0:Lcom/droider/crackme0201/MainActivity;
  4. .line 38
  5. const v1, 0x7f05000d         #successed字符串
  6. .line 37
  7. invoke-static {v0, v1, v3}, Landroid/widget/Toast;->
  8.     makeText(Landroid/content/Context;II)Landroid/widget/Toast;
  9. move-result-object v0
  10. .line 38
  11. invoke-virtual {v0}, Landroid/widget/Toast;->show()V
  12. .line 39
  13. iget-object v0, p0, Lcom/droider/crackme0201/MainActivity$1;->
  14.     this$0:Lcom/droider/crackme0201/MainActivity;
  15. #getter for:Lcom/droider/crackme0201/MainActivity;->btn_register:Landroid/widger/Button;
  16. invoke-static {v0}; Lcom/droider/crackme0201/MainActivity;->
  17.     access$3(Lcom/droider/crackme0201/MainActivity;)Landroid/widget/Button;
  18. move-result-object v0
  19. invoke-virtual {v0, v3}, Landroid/widget/Button;->setEnabled(Z)V    #設定註冊按鈕不可用
  20. .line 40
  21. iget-object v0, p0, Lcom/droider/crackme0201/MainActivity$1;->
  22.     this$0:Lcom/droider/crackme0201/MainActivity;
  23. const v1, 0x7f05000b   # registered字符串,模擬註冊成功
  24. invoke-virtual {v0, v1}, Lcom/droider/crackme0201/MainActivity;->setTitle(I)V
  25. goto :goto_0
複製代碼

這段代碼的功能是彈出註冊成功提示,也就是說,上面的跳轉如果成功意味著程式會成功註冊。

3. 修改Smali檔案代碼

經過上一小節的分析可以發現,「.line 32」行的代碼「if-nez v0,:cond_0」是程式的破解點。if-nez是Dalvik指令集中的一個條件跳轉指令。類似的還有if-eqz,if-gez,if-lez等。這些指令會在後面blog中進行介紹,在這裡只需要知道,與if-nez指令功能相反的指令為if-eqz,表示比較結果為0或相等時進行跳轉。

用任意一款文本編輯器開啟MainActivity$1.smali檔案,將「.line 32」行的代碼「if-nez v0,:cond_0」修改為「if-eqz v0,:cond_0」,保存後退出,代碼就算修改完成了。

4. 重新編譯APK檔案並簽名

修改完Smali檔案代碼後,需要將修改後的檔案重新進行編譯打包成apk檔案。編譯apk檔案的命令格式為:

apktool  b[uild]  [OPTS]  [<app_path>]  [<out_file>],開啟命令行進入到apktool工具的目錄,執行以下命令:
  1. $ ./apktool  b  /home/fuhd/apk/gw/outdir/
複製代碼

不出意外的話,程式就會編譯成功。編譯成功 後會在outdir目錄下生成dist目錄,裡面存放著編譯成功的apk檔案。編譯生成的crackme02.apk沒有簽名,還不能安裝測試,接下來需要使用signapk.jar工具對apk檔案進行簽名。signapk.jar是Android源碼包中的一個簽名工具。代碼位於Android源碼目錄下的/build/tools/signapk/SignApk.java檔案中,源碼編譯後可以在/out/host/linux-X86/framework目錄中找到它。使用signapk.jar簽名時需要提供簽名檔案,我們在此可以使用Android源碼中提供的簽名檔案 testkey.pk8與testkey.x509.pem,它們位於Android源碼的build/target/product/security目錄。將signapk.jar,testkey.x509.pem,testkey.pk8,3個檔案放到同一目錄,然後在命令提示符下輸入如下命令對APK檔案進行簽名:
  1. $ java -jar signapk.jar testkey.x509.pem testkey.pk8
  2.         /home/fuhd/apk/gw/outdir/crackme02.apk  crackme02Sign.apk
複製代碼

簽名成功後會在同目錄下生成crackme02sign.apk檔案。

4. 安裝測試

現在是時候測試修改後的成果了。啟動一個Android AVD,或者使用資料線連接手機與電腦,然後在命令提示符下執行以下命令安裝破解後的程式:
  1. $ adb install crackme02sign.apk
複製代碼
「用Android 就來APK.TW」,快來加入粉絲吧!
Android 台灣中文網(APK.TW)

評分

參與人數 8碎鑽 +7 幫助 +8 收起 理由
web921 + 1 + 1 非常讃
uybni + 1 + 1 非常讃
y6509ou + 1 + 1 非常讃
franklin2002 + 1 + 1 非常讃
caltima + 1 + 1 非常讃
skyblue + 1 + 1 很給力!
caryvincent + 1 + 1 非常讃
球-球 + 1 好內容,老衲來為這篇文章開開光.

查看全部評分

收藏收藏15 分享分享 分享專題
用Android 就來Android 台灣中文網(https://apk.tw)
回覆

使用道具 舉報

沙發
police4 | 收聽TA | 只看該作者
發表於 2016-4-19 21:05
受益良多,繼續加油!!
用Android 就來Android 台灣中文網(https://apk.tw)
回覆 支持 反對

使用道具 舉報

板凳
caryvincent | 收聽TA | 只看該作者
發表於 2016-4-20 12:01
好厲害,解說得很詳細,
不過小弟我還是霧煞煞!
用Android 就來Android 台灣中文網(https://apk.tw)
回覆 支持 反對

使用道具 舉報

地板
xinbenxin | 收聽TA | 只看該作者
發表於 2016-4-21 01:29

好厲害,解說得很詳細
用Android 就來Android 台灣中文網(https://apk.tw)
回覆 支持 反對

使用道具 舉報

5
chunnannn | 收聽TA | 只看該作者
發表於 2016-8-2 09:39
由 手機網頁 發佈
版大,不好意思,不知道發在這裡適不適合?!
請問一下遊戲的修改數據,通常都是尋找一串dword,修改為另一個dword,分析完smail後,怎麼轉換成這串dword呢?
感謝您!
用Android 就來Android 台灣中文網(https://apk.tw)
回覆 支持 反對

使用道具 舉報

6
680mb | 收聽TA | 只看該作者
發表於 2016-8-20 18:36
很不錯的入門指導,感謝樓主對初學者的用心。
用Android 就來Android 台灣中文網(https://apk.tw)
回覆 支持 反對

使用道具 舉報

7
oabearoa | 收聽TA | 只看該作者
發表於 2016-12-15 10:28
樓主~~感謝你的指導,小弟全不會但很努力的看完這篇,有事請教一下,

最後.line 40行調用return-void函數返回。這裏的40是否有誤,是不是42
用Android 就來Android 台灣中文網(https://apk.tw)
回覆 支持 反對

使用道具 舉報

您需要登錄後才可以回帖 登錄 | 註冊

本版積分規則