編輯:關於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 Studio已經越來越流行了,但很多人還是習慣於Eclipse或源碼環境下開發JNI應用。筆者是從以前在學校參加谷歌大學學術合作項目的時候接觸JN
廢話不多說了,直接給大家貼代碼了。java類如下: import android.content.Context; import android.content.res.
本文為大家分享Android登陸界面實現清除輸入框內容和震動效果的全部代碼,具體內容如下:效果圖:主要代碼如下自定義的一個EditText,用於實現有文字的時候顯示可以清
1、ctrl + shift + z 我想就連沒編過程序的人都知道 ctrl + z 是回退鍵,但是很少人知道 這個“前進鍵”吧,事實上這個快捷鍵