Apk文件结构, Dex反编译
android 系統最常見的莫過就似乎apk文件了,這就是android的安裝文件
下面簡單說說這個apk的文件格式吧,轉載了下別人的說明:
Apk文件:
Android application package文件。每個要安裝到OPhone平臺的應用都要被編譯打包為一個單獨的文件,后綴名為.apk,其中包含了應用的二進制代碼、資源、配置文件等。
apk文件實際是一個zip壓縮包,可以通過解壓縮工具解開。可以用zip解開*.apk文件,下面是一個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是每個應用都必須定義和包含的,它描述了應用的名字、版本、權限、引用的庫文件等等信息[ , ],如要把apk上傳到 Google Market上,也要對這個xml做一些配置。網上已有很多資料,在此就不多做介紹了。
在apk中的AndroidManifest.xml是經過壓縮的,可以通過AXMLPrinter2工具[ , ]解開,具體命令為:
java -jar AXMLPrinter2.jar AndroidManifest.xml???
3.2 META-INF目錄
META-INF目錄下存放的是簽名信息,用來保證apk包的完整性和系統的安全。在eclipse編譯生成一個 api包時,會對所有要打包的文件做一個校驗計算,并把計算結果放在META-INF目錄下。而在OPhone平臺上安裝apk包時,應用管理器會按照同樣的算法對包里的文件做校驗,如果校驗結果與META-INF下的內容不一致,系統就不會安裝這個apk。這就保證了apk包里的文件不能被隨意替換。比如拿到一個apk包后,如果想要替換里面的一幅圖片,一段代碼, 或一段版權信息,想直接解壓縮、替換再重新打包,基本是不可能的。如此一來就給病毒感染和惡意修改增加了難度,有助于保護系 統的安全。
?
3.3 classes.dex文件
?????? classes.dex是java源碼編譯后生成的java字節碼文件。但由于Android使用的dalvik虛擬機與標準的java虛擬機是不兼容的,dex文件與class文件相比,不論是文件結構還是opcode都不一樣。目前常見的java反編譯工具都不能處理dex文件。
?????? Android模擬器中提供了一個dex文件的反編譯工具,dexdump。用法為首先啟動Android模擬器,把要查看的dex文件用adb push上傳的模擬器中,然后通過adb shell登錄,找到要查看的dex文件,執行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的結果可以看到有class0到class5六個class,跟工程目錄下bin目錄中的class數目相對應,可以想象 dex文件包含了所有的class文件。但對hello.java的反編譯結果(Class #5)中很難發現我們做的修改,即如何輸出“hello, OPhone”。分支跳轉表的反編譯不完整,嚴格來說就沒有完整的dump出來。fill- array-data表也存在同樣的問題。還有其他很多限制。總的來說dexdump反編的結果可讀性很差。
?????? 目前在網上能找到的另一個dex文件的反編譯工具是Dedexer。Dedexer可以讀取dex格式的文件,生成一種類似于匯編語言的輸出。這種輸出與jasmin[ ]的輸出相似,但包含的是Dalvik的字節碼。我們會在下一節詳細介紹一下Dedexer。
?
3.4 res目錄
????? res目錄存放資源文件。關于apk文件中的資源管理,OPhone SDN網站上已經有文章做過詳細介紹[ ],就不在此敷述。
?
3.5 resources.arsc
????? 編譯后的二進制資源文件。
?
四.反編譯工具Dedexer
??????? Dedexer是目前在網上能找到的唯一一個反編譯dex文件的開源工具[ ]。Dedexer下載后需要編譯才能使用。如果你用過ant編譯java程序,那么編譯Dedexer是一件非常簡單的工作。注意目前Dedexer的最新版本是1.5,只能使用junit4.5編譯。下面以linux環境為例,講一下Dedexer的編譯使用過程。
?????? 下載ddx1.5.zip后,解壓縮會產生一個dedexer目錄,其中包含build.xml文件。我們需要根據本機的環境配置build.xml的內容,注意下面的粗體部分是我本機的配置。
?
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"/>
??? 環境配置好之后可以開始編譯了。當然要保證你已經安裝好了ant編譯工具。執行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文件。我習慣修改一下它的文件名,加上版本號。用來反編譯的命令如下:
?
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為每個class文件生成了一個后綴為ddx的文件。不出所料,有6個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
????????? 看一下我們所關心的hello.ddx的內容。
?
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
從反編譯的結果來看,代碼的可讀性仍然比較差,但比dexdump相比要好一些。我們能夠看到“hello, OPhone”字符串是通過invoke-virtual {v0, v1}, android/widget/TextView/setText調用的。
dedexer與dexdump相比至少有3個優點。一,不需要在android模擬器中運行。二,把dex文件按照java源代碼package的目錄結構建好了目錄,每個class文件對應一個ddx文件。不像dexdump那樣把所有的結果都放在一起。三,按照Dedexer作者的說法,可以把 Dedexer作為一個像jasmin那樣的反編譯引擎,目前好多強大的java反編譯工具都是以jasmin作為反編譯引擎的。
?
參考文獻
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/
總結
以上是生活随笔為你收集整理的Apk文件结构, Dex反编译的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: ADT17中引入外部JAR包时出现Cla
- 下一篇: Eclipse Outline图标