Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> 關於Android編程 >> 分析Android程序之破解第一個程序

分析Android程序之破解第一個程序

編輯:關於Android編程

 

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

1. 反編譯APK文件

ApkTool是跨平台的工具,可以在windows平台與linux平台下直接使用。使用前到:http://code.google.com/p/android-apktool/ 下載ApkTool,目前最新版本為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] [

]

 

編譯apk文件的命令為: apktool b[uild] [OPTS] [] []

那麼在命令行下進入到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 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 xml version=1.0 encoding=utf-8 ?> <resources> <string name=app_name>Crackme0201string> <string name=hello_world>Hello world!<string> <string name=menu_settings>Settingsstring> <string name=title_activity_main>crackme02string> <string name=info>Android程序破解演示實例string> <string name=username>用戶名:string> <string name=sn>注冊碼:string> <string name=register>注冊string> <string name=hint_username>請輸入用戶名string> <string name=hint_sn>請輸入16位的注冊碼string> <string name=unregister>程序未注冊string> <string name=registered>程序已注冊string> <string name=unsuccessed>無效用戶名或注冊碼string> <string name=successed>恭喜您!注冊成功string> resources>

 

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

從上面列表中找到“無效用戶名或注冊碼”的字符串名稱unsuccessed。打開public.xml文件,它的內容如下:

? 1 2 3 4 5 6 7 8 9 10 11 xml version=1.0 encoding=utf-8?> <resources> <public type=drawable name=ic_launcher id=0x7f020001 /> <public type=drawable name=ic_action_search id=0x7f020000 /> ....... <public type=string name=unsuccessed id=0x7f05000c/> ....... <public type=id name=edit_sn id=0x7f080002 /> <public type=id name=button_register id=0x7f080003 /> <public type=id name=menu_settings id=0x7f080004 /> resources>

 

unsuccessed的id值為0x7f05000c,在smali目錄中搜索含有內容為0x7f05000c的文件,最後發現只有MainActivity$1.smali文件一處調用,代碼如下:

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

 

Smali代碼中添加的注釋使用“#”號開頭,.line 32行調用了checkSN()函數進行注冊碼的合法檢查,接著下面有如下兩行代碼:

? 1 2 move-result v0 if-nez v0, :cond_0

 

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

如果代碼不跳轉,會執行如下幾行代碼:

? 1 2 3 4 5 6 7 8 9 10 11 12 13 14 .line 34 iget-object v0, p0, Lcom/droider/crackme0201/MainActivity$1;-> this$0:Lcom/droider/crackme0201/MainActivity; .line 35 const v1, 0x7f05000c #unsuccessed字符串 .line 34 invoke-static {v0, v1, v3}, Landroid/widget/Toast;-> makeText(Landroid/content/Context;II)Landroid/widget/Toast; move-result-object v0 .line 35 invoke-virtual {v0}, Landroid/widget/Toast;->show()V .line 42 :goto_0 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 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 :cond_0 iget-object v0, p0, Lcom/droider/crackme0201/MainActivity$1;-> this$0:Lcom/droider/crackme0201/MainActivity; .line 38 const v1, 0x7f05000d #successed字符串 .line 37 invoke-static {v0, v1, v3}, Landroid/widget/Toast;-> makeText(Landroid/content/Context;II)Landroid/widget/Toast; move-result-object v0 .line 38 invoke-virtual {v0}, Landroid/widget/Toast;->show()V .line 39 iget-object v0, p0, Lcom/droider/crackme0201/MainActivity$1;-> this$0:Lcom/droider/crackme0201/MainActivity; #getter for:Lcom/droider/crackme0201/MainActivity;->btn_register:Landroid/widger/Button; invoke-static {v0}; Lcom/droider/crackme0201/MainActivity;-> access$3(Lcom/droider/crackme0201/MainActivity;)Landroid/widget/Button; move-result-object v0 invoke-virtual {v0, v3}, Landroid/widget/Button;->setEnabled(Z)V #設置注冊按鈕不可用 .line 40 iget-object v0, p0, Lcom/droider/crackme0201/MainActivity$1;-> this$0:Lcom/droider/crackme0201/MainActivity; const v1, 0x7f05000b # registered字符串,模擬注冊成功 invoke-virtual {v0, v1}, Lcom/droider/crackme0201/MainActivity;->setTitle(I)V 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] [] [],打開命令行進入到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 2 $ java -jar signapk.jar testkey.x509.pem testkey.pk8 /home/fuhd/apk/gw/outdir/crackme02.apk crackme02Sign.apk

 

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

 


  1. 上一頁:
  2. 下一頁:
熱門文章
閱讀排行版
Copyright © Android教程網 All Rights Reserved