Apk文件结构, Dex反编译
android 系統(tǒng)最常見(jiàn)的莫過(guò)就似乎apk文件了,這就是android的安裝文件
下面簡(jiǎn)單說(shuō)說(shuō)這個(gè)apk的文件格式吧,轉(zhuǎn)載了下別人的說(shuō)明:
Apk文件:
Android application package文件。每個(gè)要安裝到OPhone平臺(tái)的應(yīng)用都要被編譯打包為一個(gè)單獨(dú)的文件,后綴名為.apk,其中包含了應(yīng)用的二進(jìn)制代碼、資源、配置文件等。
apk文件實(shí)際是一個(gè)zip壓縮包,可以通過(guò)解壓縮工具解開。可以用zip解開*.apk文件,下面是一個(gè)helloword的apk示例文件
|– AndroidManifest.xml????
|– META-INF????
|?? |– CERT.RSA????
|?? |– CERT.SF????
|?? `– MANIFEST.MF????
|– classes.dex????
|– res????
|?? |– drawable????
|?? |?? `– icon.png????
|?? `– layout????
|?????? `– main.xml????
`– resources.arsc???
Manifest文件
AndroidManifest.xml是每個(gè)應(yīng)用都必須定義和包含的,它描述了應(yīng)用的名字、版本、權(quán)限、引用的庫(kù)文件等等信息[ , ],如要把a(bǔ)pk上傳到 Google Market上,也要對(duì)這個(gè)xml做一些配置。網(wǎng)上已有很多資料,在此就不多做介紹了。
在apk中的AndroidManifest.xml是經(jīng)過(guò)壓縮的,可以通過(guò)AXMLPrinter2工具[ , ]解開,具體命令為:
java -jar AXMLPrinter2.jar AndroidManifest.xml???
3.2 META-INF目錄
META-INF目錄下存放的是簽名信息,用來(lái)保證apk包的完整性和系統(tǒng)的安全。在eclipse編譯生成一個(gè) api包時(shí),會(huì)對(duì)所有要打包的文件做一個(gè)校驗(yàn)計(jì)算,并把計(jì)算結(jié)果放在META-INF目錄下。而在OPhone平臺(tái)上安裝apk包時(shí),應(yīng)用管理器會(huì)按照同樣的算法對(duì)包里的文件做校驗(yàn),如果校驗(yàn)結(jié)果與META-INF下的內(nèi)容不一致,系統(tǒng)就不會(huì)安裝這個(gè)apk。這就保證了apk包里的文件不能被隨意替換。比如拿到一個(gè)apk包后,如果想要替換里面的一幅圖片,一段代碼, 或一段版權(quán)信息,想直接解壓縮、替換再重新打包,基本是不可能的。如此一來(lái)就給病毒感染和惡意修改增加了難度,有助于保護(hù)系 統(tǒng)的安全。
?
3.3 classes.dex文件
?????? classes.dex是java源碼編譯后生成的java字節(jié)碼文件。但由于Android使用的dalvik虛擬機(jī)與標(biāo)準(zhǔn)的java虛擬機(jī)是不兼容的,dex文件與class文件相比,不論是文件結(jié)構(gòu)還是opcode都不一樣。目前常見(jiàn)的java反編譯工具都不能處理dex文件。
?????? Android模擬器中提供了一個(gè)dex文件的反編譯工具,dexdump。用法為首先啟動(dòng)Android模擬器,把要查看的dex文件用adb push上傳的模擬器中,然后通過(guò)adb shell登錄,找到要查看的dex文件,執(zhí)行dexdump xxx.dex。
?????? 仍然以hello world程序作為演示。
?
view plaincopy to clipboardprint?
# dexdump classes.dex????
Processing ‘classes.dex’…????
Opened ‘classes.dex’, DEX version ‘035′???
Class #0??????????? -????
? Class descriptor? : ‘Lhello/world/R$attr;’???
? …????
Class #5??????????? -????
? Class descriptor? : ‘Lhello/world/hello;’???
? Access flags????? : 0×0001 (PUBLIC)????
? Superclass??????? : ‘Landroid/app/Activity;’???
? Interfaces??????? -????
? Static fields???? -????
? Instance fields?? -????
? Direct methods??? -????
??? #0????????????? : (in Lhello/world/hello;)????
????? name????????? : ‘<init>’???
????? type????????? : ‘()V’???
????? access??????? : 0×10001 (PUBLIC CONSTRUCTOR)????
????? code????????? -????
????? registers???? : 1???
????? ins?????????? : 1???
????? outs????????? : 1???
????? insns size??? : 4 16-bit code units????
????? catches?????? : (none)????
????? positions???? :?????
??????? 0×0000 line=7???
????? locals??????? :?????
??????? 0×0000 - 0×0004 reg=0 this Lhello/world/hello;?????
? Virtual methods?? -????
??? #0????????????? : (in Lhello/world/hello;)????
????? name????????? : ‘onCreate’???
????? type????????? : ‘(Landroid/os/Bundle;)V’???
????? access??????? : 0×0001 (PUBLIC)????
????? code????????? -????
????? registers???? : 4???
????? ins?????????? : 2???
????? outs????????? : 2???
????? insns size??? : 17 16-bit code units????
????? catches?????? : (none)????
????? positions???? :?????
??????? 0×0000 line=11???
??????? 0×0003 line=13???
??????? 0×0008 line=14???
??????? 0×000d line=15???
??????? 0×0010 line=16???
????? locals??????? :?????
??????? 0×0008 - 0×0011 reg=0 test Landroid/widget/TextView;?????
??????? 0×0000 - 0×0011 reg=2 this Lhello/world/hello;?????
??????? 0×0000 - 0×0011 reg=3 savedInstanceState Landroid/os/Bundle;?????
? source_file_idx?? : 27 (hello.java)???
# dexdump classes.dex Processing 'classes.dex'... Opened 'classes.dex', DEX version '035' Class #0 - Class descriptor : 'Lhello/world/R$attr;' … Class #5 - Class descriptor : 'Lhello/world/hello;' Access flags : 0x0001 (PUBLIC) Superclass : 'Landroid/app/Activity;' Interfaces - Static fields - Instance fields - Direct methods - #0 : (in Lhello/world/hello;) name : '<init>' type : '()V' access : 0x10001 (PUBLIC CONSTRUCTOR) code - registers : 1 ins : 1 outs : 1 insns size : 4 16-bit code units catches : (none) positions : 0x0000 line=7 locals : 0x0000 - 0x0004 reg=0 this Lhello/world/hello; Virtual methods - #0 : (in Lhello/world/hello;) name : 'onCreate' type : '(Landroid/os/Bundle;)V' access : 0x0001 (PUBLIC) code - registers : 4 ins : 2 outs : 2 insns size : 17 16-bit code units catches : (none) positions : 0x0000 line=11 0x0003 line=13 0x0008 line=14 0x000d line=15 0x0010 line=16 locals : 0x0008 - 0x0011 reg=0 test Landroid/widget/TextView; 0x0000 - 0x0011 reg=2 this Lhello/world/hello; 0x0000 - 0x0011 reg=3 savedInstanceState Landroid/os/Bundle; source_file_idx : 27 (hello.java)
? Dexdump的結(jié)果可以看到有class0到class5六個(gè)class,跟工程目錄下bin目錄中的class數(shù)目相對(duì)應(yīng),可以想象 dex文件包含了所有的class文件。但對(duì)hello.java的反編譯結(jié)果(Class #5)中很難發(fā)現(xiàn)我們做的修改,即如何輸出“hello, OPhone”。分支跳轉(zhuǎn)表的反編譯不完整,嚴(yán)格來(lái)說(shuō)就沒(méi)有完整的dump出來(lái)。fill- array-data表也存在同樣的問(wèn)題。還有其他很多限制。總的來(lái)說(shuō)dexdump反編的結(jié)果可讀性很差。
?????? 目前在網(wǎng)上能找到的另一個(gè)dex文件的反編譯工具是Dedexer。Dedexer可以讀取dex格式的文件,生成一種類似于匯編語(yǔ)言的輸出。這種輸出與jasmin[ ]的輸出相似,但包含的是Dalvik的字節(jié)碼。我們會(huì)在下一節(jié)詳細(xì)介紹一下Dedexer。
?
3.4 res目錄
????? res目錄存放資源文件。關(guān)于apk文件中的資源管理,OPhone SDN網(wǎng)站上已經(jīng)有文章做過(guò)詳細(xì)介紹[ ],就不在此敷述。
?
3.5 resources.arsc
????? 編譯后的二進(jìn)制資源文件。
?
四.反編譯工具Dedexer
??????? Dedexer是目前在網(wǎng)上能找到的唯一一個(gè)反編譯dex文件的開源工具[ ]。Dedexer下載后需要編譯才能使用。如果你用過(guò)ant編譯java程序,那么編譯Dedexer是一件非常簡(jiǎn)單的工作。注意目前Dedexer的最新版本是1.5,只能使用junit4.5編譯。下面以linux環(huán)境為例,講一下Dedexer的編譯使用過(guò)程。
?????? 下載ddx1.5.zip后,解壓縮會(huì)產(chǎn)生一個(gè)dedexer目錄,其中包含build.xml文件。我們需要根據(jù)本機(jī)的環(huán)境配置build.xml的內(nèi)容,注意下面的粗體部分是我本機(jī)的配置。
?
view plaincopy to clipboardprint?
<!– Directories of the project –>????
<property name=“home” value=“/home/danny/myproject/dedex/dedexer”/>????
<property name=“junit-home” value=“/home/danny/myproject/dedex”/>????
<!– Directories derived from the source tree root –>????
<property name=“classdir” value=“${home}/classes”/>????
<property name=“src” value=“${home}/sources”/>????
<property name=“testbase” value=“${home}/testfiles”/>????
<!– Directories derived from the JUnit base –>????
<property name=“junit_jar” value=“${junit-home}/junit-4.5.jar”/>??
<!-- Directories of the project --> <property name="home" value="/home/danny/myproject/dedex/dedexer"/> <property name="junit-home" value="/home/danny/myproject/dedex"/> <!-- Directories derived from the source tree root --> <property name="classdir" value="${home}/classes"/> <property name="src" value="${home}/sources"/> <property name="testbase" value="${home}/testfiles"/> <!-- Directories derived from the JUnit base --> <property name="junit_jar" value="${junit-home}/junit-4.5.jar"/>
??? 環(huán)境配置好之后可以開始編譯了。當(dāng)然要保證你已經(jīng)安裝好了ant編譯工具。執(zhí)行ant。
?
view plaincopy to clipboardprint?
danny@danny-desktop:~/myproject/dedex$?ant????
Buildfile: build.xml????
init:????
??? [mkdir] Created dir: /home/danny/myproject/dedex/dedexer/classes????
compile:????
??? [javac] Compiling 48 source files to /home/danny/myproject/dedex/dedexer/classes????
??? [javac] Note: /home/danny/myproject/dedex/dedexer/sources/hu/uw/pallergabor/dedexer/Annotation.java uses unchecked or unsafe operations.????
??? [javac] Note: Recompile with -Xlint:unchecked for details.????
?package:????
????? [jar] Building jar: /home/danny/myproject/dedex/dedexer/ddx.jar????
???
BUILD SUCCESSFUL????
Total time: 3 seconds??
danny@danny-desktop:~/myproject/dedex$?ant Buildfile: build.xml init: [mkdir] Created dir: /home/danny/myproject/dedex/dedexer/classes compile: [javac] Compiling 48 source files to /home/danny/myproject/dedex/dedexer/classes [javac] Note: /home/danny/myproject/dedex/dedexer/sources/hu/uw/pallergabor/dedexer/Annotation.java uses unchecked or unsafe operations. [javac] Note: Recompile with -Xlint:unchecked for details. package: [jar] Building jar: /home/danny/myproject/dedex/dedexer/ddx.jar BUILD SUCCESSFUL Total time: 3 seconds
好了,編譯dedexer成功,只用了3秒種,生成了ddx.jar文件。我習(xí)慣修改一下它的文件名,加上版本號(hào)。用來(lái)反編譯的命令如下:
?
view plaincopy to clipboardprint?
danny@danny-desktop:~/myproject/dedex$?java -jar ddx1.5.jar -d [target folder] classes.dex????
Processing hello/world/R$string????
Processing hello/world/R$layout????
Processing hello/world/hello????
Processing hello/world/R$attr????
Processing hello/world/R????
Processing hello/world/R$drawable??
danny@danny-desktop:~/myproject/dedex$?java -jar ddx1.5.jar -d [target folder] classes.dex Processing hello/world/R$string Processing hello/world/R$layout Processing hello/world/hello Processing hello/world/R$attr Processing hello/world/R Processing hello/world/R$drawable
dedexer為每個(gè)class文件生成了一個(gè)后綴為ddx的文件。不出所料,有6個(gè)ddx文件。
?
view plaincopy to clipboardprint?
danny@danny-desktop:~/myproject/dedex$?ls hello/world/????
R$attr.ddx R.ddx R$drawable.ddx R$layout.ddx R$string.ddx hello.ddx??
danny@danny-desktop:~/myproject/dedex$?ls hello/world/ R$attr.ddx R.ddx R$drawable.ddx R$layout.ddx R$string.ddx hello.ddx
????????? 看一下我們所關(guān)心的hello.ddx的內(nèi)容。
?
view plaincopy to clipboardprint?
class public hello/world/hello????
.super android/app/Activity????
.source hello.java????
???
.method public <init>()V????
.line 7???
??????? invoke-direct?? {v0},android/app/Activity/<init>??????? ; <init>()V????
??????? return-void???
.end method????
???
.method public onCreate(Landroid/os/Bundle;)V????
.line 11???
??????? invoke-super??? {v2,v3},android/app/Activity/onCreate?? ; onCreate(Landroid/os/Bundle;)V????
.line 13???
??????? new-instance??? v0,android/widget/TextView????
??????? invoke-direct?? {v0,v2},android/widget/TextView/<init>? ; <init>(Landroid/content/Context;)V????
.line 14???
??????? const-string??? v1,“hello, OPhone”???
??????? invoke-virtual? {v0,v1},android/widget/TextView/setText ; setText(Ljava/lang/CharSequence;)V????
.line 15???
??????? invoke-virtual? {v2,v0},hello/world/hello/setContentView??????? ; setContentView(Landroid/view/View;)V????
.line 16???
??????? return-void???
.end method??
class public hello/world/hello .super android/app/Activity .source hello.java .method public <init>()V .line 7 invoke-direct {v0},android/app/Activity/<init> ; <init>()V return-void .end method .method public onCreate(Landroid/os/Bundle;)V .line 11 invoke-super {v2,v3},android/app/Activity/onCreate ; onCreate(Landroid/os/Bundle;)V .line 13 new-instance v0,android/widget/TextView invoke-direct {v0,v2},android/widget/TextView/<init> ; <init>(Landroid/content/Context;)V .line 14 const-string v1,"hello, OPhone" invoke-virtual {v0,v1},android/widget/TextView/setText ; setText(Ljava/lang/CharSequence;)V .line 15 invoke-virtual {v2,v0},hello/world/hello/setContentView ; setContentView(Landroid/view/View;)V .line 16 return-void .end method
從反編譯的結(jié)果來(lái)看,代碼的可讀性仍然比較差,但比dexdump相比要好一些。我們能夠看到“hello, OPhone”字符串是通過(guò)invoke-virtual {v0, v1}, android/widget/TextView/setText調(diào)用的。
dedexer與dexdump相比至少有3個(gè)優(yōu)點(diǎn)。一,不需要在android模擬器中運(yùn)行。二,把dex文件按照java源代碼package的目錄結(jié)構(gòu)建好了目錄,每個(gè)class文件對(duì)應(yīng)一個(gè)ddx文件。不像dexdump那樣把所有的結(jié)果都放在一起。三,按照Dedexer作者的說(shuō)法,可以把 Dedexer作為一個(gè)像jasmin那樣的反編譯引擎,目前好多強(qiáng)大的java反編譯工具都是以jasmin作為反編譯引擎的。
?
參考文獻(xiàn)
http://www.ophonesdn.com/article/show/20
??
http://developer.android.com/guide/appendix/glossary.html
??
http://forum.xda-developers.com/showthread.php?t=514412
??
http://code.google.com/p/android4me/downloads/list
??
http://jasmin.sourceforge.net/
??
http://www.ophonesdn.com/article/show/18
??
http://dedexer.sourceforge.net/
總結(jié)
以上是生活随笔為你收集整理的Apk文件结构, Dex反编译的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: ADT17中引入外部JAR包时出现Cla
- 下一篇: Eclipse Outline图标