編輯:關於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 17xml 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/
從上面列表中找到“無效用戶名或注冊碼”的字符串名稱unsuccessed。打開public.xml文件,它的內容如下:
? 1 2 3 4 5 6 7 8 9 10 11xml 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 2move-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 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文件。
Android NDK——配置NDK及使用Android studio開發Hello JNI並簡單打包so庫
引言盡管Android Studio已經越來越流行了,但很多人還是習慣於Eclipse或源碼環境下開發JNI應用。筆者是從以前在學校參加谷歌大學學術合作項目的時候接觸JN
Android自定義圓角ImageView
廢話不多說了,直接給大家貼代碼了。java類如下: import android.content.Context; import android.content.res.
Android登陸界面實現清除輸入框內容和震動效果
本文為大家分享Android登陸界面實現清除輸入框內容和震動效果的全部代碼,具體內容如下:效果圖:主要代碼如下自定義的一個EditText,用於實現有文字的時候顯示可以清
那些相見恨晚的android studio快捷鍵
1、ctrl + shift + z 我想就連沒編過程序的人都知道 ctrl + z 是回退鍵,但是很少人知道 這個“前進鍵”吧,事實上這個快捷鍵