class文件與dex文件解析

目錄

前言

一、class文件解析

1-1、class文件基本概念

1-2、生成class文件

1-3、class文件的作用

1-4、class文件的整體結構

1-5、class文件的微觀結構

1-6、具體查看並分析

1-7、class文件的弊端

二、dex文件解析

2-1、dex文件的基本概念

2-2、生成dex文件

2-3、執行dex文件

2-4、dex文件作用

2-5、dex文件的整體結構

2-6、dex文件的微觀結構

2-7、class文件與dex文件對比


前言

正式寫之前先說兩句廢話,這篇筆記是我去年的時候創建的,當時是寫了一部分,後來因爲亂七八糟的事情太忙了,結果放到草稿箱裏給忘記了,昨天回過頭去複習這部分的內容偶然間發現了它,還是個沒完成的它,大寫的尷尬啊,所以急忙給補上了,此處鄙視一下自己!

下面進入今天的正題——解析class文件和dex文件,做個筆記,方便總結和回顧。

一、class文件解析

1-1、class文件基本概念

能夠被JVM識別,加載並執行的文件格式,說白了就是一種文件格式,像mp4、doc、txt這種文件格式一樣,只不過class文件中存儲的是應用程序,並且有很多語言都可以生成class文件,並不是只有Java語言,比如:Scala、Python、Small等等都可以生成class字節碼來被JVM識別並且執行。

1-2、生成class文件

兩種方式,一種是通過IDE開發工具自動build,另一種是通過javac,即java compiler編譯命令手動執行生成class文件。

生成了class之後,執行方式同樣也是兩種,一種是通過點擊IDE的Run執行,另一種是通過java命令手動執行。

這裏來看一下如何手動生成class文件?

首先電腦上基本的Java開發環境要配置完成,包括環境變量的配置,有了這個基礎才能具體實戰。

首先我在自己電腦E盤中hotfix文件夾下準備了一個.java的源文件,然後打開電腦控制檯,查找一下這個文件是否存在,如下圖:

然後我們進入到hotfix目錄下面查看裏面的文件,並且打開這個文件,這時候系統會調起電腦中的其他程序加載對應的文件:

可以看到就一個Hello.java的源文件,然後裏面就打印一句話:Hello Jarchie。下面我們來手動執行javac -Hello.java命令生成class文件,然後繼續執行java Hello命令,執行這個應用程序,觀察結果,確實打印出了代碼中的內容,如下圖所示:

1-3、class文件的作用

記錄一個類文件的所有信息,記住是所有!包括一個類的名稱、類中所有的方法、類中所有的變量等等,class文件中所包含的信息,遠遠多於java源代碼中所能看到的信息。舉個例子,爲什麼在一個類中並沒有定義this,super這樣的關鍵字,但是我們卻可以使用這些關鍵字來調用我們父類的方法或者調用當前類的變量,那是因爲在生成class字節碼文件的時候,java虛擬機幫我們記錄了它的當前類this關鍵字和父類super關鍵字,所以可以這樣使用。

1-4、class文件的整體結構

  • 一種8位字節的二進制流文件
  • 各個數據按順序緊密的排列,無間隙
  • 每個類或接口都單獨佔據一個class文件

從整體上看,首先它是一種8位字節的二進制流文件,這一點與大部分文件都一樣,比如音視頻文件都是二進制流。其次它的各個數據按順序緊密的排列,沒有絲毫的間隙,不像有些文件,爲了讀取上的方便,會做一些填充,比如每80個字節定爲一行,那麼緊密排列的好處就是可以減少class文件的體積,讓JVM在加載class文件的時候更加快速。最後,每個類或接口都單獨佔據一個class文件,這樣做的好處是每個類或者接口都可以獨自管理自己內部的內容,而無需相互交叉,這是class文件在宏觀上的三個特點。

1-5、class文件的微觀結構

上圖中呢就是class文件中的所有字段,下面來具體說說每個字段的主要作用。

magic:無符號四字節類型,主要作用加密段,類似於文件的md5加密,主要是給虛擬機用來判斷當前class文件是否被篡改過。

minor_version:當前class文件最小可以被哪個版本的jdk加載,就是它最小適配的jdk版本。

major_version:當前class文件是由哪個版本的jdk生成的。

constant_pool_count:當前class文件中常量池的數量,通常來說都是一個常量池。

constant_pool:真正的常量池,類型cp_info即結構體類型。先來看三個比較好理解的,即CONSTANT_Integer_info、CONSTANT_Long_info、CONSTANT_String_info,分別存儲class文件中所有的Integer類型、Long類型、String類型,當然還有short、byte等類型也有對應的字段去存儲,這裏就不再一一列舉了。再來看三個比較複雜的,即CONSTANT_Class_info、CONSTANT_Fieldref_info、CONSTANT_Methodref_info,這三個字段分別記錄了類相關的信息、類中成員變量和類中方法相關的信息,其中CONSTANT_Class_info不僅記錄了當前類的一些信息,還記錄了當前類所引用到的類的信息,另外CONSTANT_Fieldref_info、CONSTANT_Methodref_info中記錄的並不是對應類型的真正信息,而是存儲的索引地址,這些索引最終指向的又是對應的CONSTANT_Integer_info這些,所以所有內容實際上都是存儲在常量池中的Integer_info、String_info這些字段裏面的。

access_flags:作用域標誌,比如public類型或者private類型,它的取值範圍如下表所示:

this_class&super_class:JVM默認填充當前類以及當前類的父類。

interfaces_count&interfaces:class文件繼承的接口數量和接口,只記錄當前類直接繼承的接口,不會記錄間接繼承的接口。

fields_count&fields:class文件包含的所有成員變量及數量,其中fields是結構體類型,表明它內部還包含了其他內容,主要是每個成員變量的name、所屬的類以及類型。

methods_count&methods:class文件中所有的方法相關的內容,其中methods是結構體類型,內部包含每個方法的name、access_flag作用域、所屬類以及類型。

attribute_count&attributes:記錄類的一些屬性相關的內容,上面這些字段沒有包含到的內容都會放到attribute中,比如類上面的註解。

總體上看,一個class文件中定義了這麼多字段,並且這些字段裏面可能又包含字段,就像我們的JSON文件一樣,是一層套一層的,通過這些字段的詳細定義,我們的Java虛擬機就可以找到每個class文件中的任何內容。

講了這麼多class字節的字段以及他們的作用,是不是還是沒什麼感覺,很虛無縹緲枯燥乏味的知識點是不是都不太想看,那麼下面我們就具體的來查看一下class文件中的內容,看一下它的內部結構,眼見爲實嘛。我們就以上面生成的Hello.class爲例,來具體看一下,來驗證一下我們的這些理論知識。

1-6、具體查看並分析

首先介紹一款軟件——010 Editor,它是主要用來查看二進制文件的,不僅可以查看class文件,後面還能查看dex文件。

先來整體上看一下,如下圖所示,把這個結構體縮起來以後,選中的內容表明整個class文件由這些二進制的數據組成。

展開struct將結構體打開後,具體來看一下,如下圖所示:

先以第一個字段爲例說明一下怎麼看,首先來看下方struct部分展開後會出現一個列表,如果選中第一個字段magic(紅線圈出的位置),上方十六進制區域會顯示出這個字段所在的位置,然後下方開始位置爲0h,也就是說它是從第0行第0列開始,大小爲4h,意思是跨度爲4列,這裏的4是十六進制。按照這個說明,我給大家選中了一個CONSTANT_Methodref,它是記錄了class字節中的一個方法,然後展開可以看到這裏面有幾個字段,首先是有一個標誌,然後一個class_index,表明這個方法是屬於哪個類的,第三個字段表明這個方法的名字和方法的類型,注意這裏都是index,所以這兩個字段都是一個指針,根據指針可以查找到具體指向的內容。這裏面的內容有很多,最好就是下載一下這個軟件,自己對應着去查看一下,下面都是constant_pool,這裏就不再一一的看了,翻到最下面,可以看到我們的access_flags,這裏顯示是public類型,再往下看還能看到this、super、方法、成員變量這些內容,從這裏也能看出,都是論證了我們之前的理論知識的。如下圖所示:

1-7、class文件的弊端

1、內存佔用大,不合適移動端:每一個class文件包括很多的常量池,以及所有的field、method,而我們一個應用中有成百上千個類,是非常常見的,如果單純的使用class字節碼這種文件去存儲類的信息的話,那麼在移動端是不現實的,因爲移動端的內存是非常少的。

2、堆棧的加載模式,加載速度慢;

3、文件IO操作多,類查找和加載慢:每個class文件只存儲了一個java源文件中的所有的信息,每次去加載一個新的class的時候,都要去執行一遍加載查詢,所以相對來說查找較慢。

class文件還有一些其他的缺點,正是因爲這些缺點,所以導致移動開發不適合使用class字節碼文件,所以接下來來看一下dex文件是如何規避這些缺點的,並且還做了哪些優化。

二、dex文件解析

2-1、dex文件的基本概念

能夠被DVM(Dalvik Virtual Machine,是Google專門爲Android平臺開發的虛擬機,運行在Android運行時庫中)識別,加載並執行的文件格式。同樣的它不僅能由Java源文件生成,還能由C/C++生成。這也說明了Android編程不僅可以使用Java語言,也可以使用C/C++來編寫Android應用程序。

2-2、生成dex文件

兩種方式:第一種是通過IDE自動幫我們build生成,第二種是手動通過dx命令去生成dex文件。做過Android逆向的人肯定都知道,我們去反編譯一個apk時,首先會先把apk解壓縮,然後會發現裏面會有一個classes.dex文件,然後可以通過dex2jar將.dex文件轉化爲.jar文件,然後再通過jd-gui這個工具就可以查看源代碼了。這裏就是簡單的說一下,主要是想說如何快速看到dex文件。那麼我們該如何手動通過dx命令生成dex文件呢?

第一步:需要配置一個環境變量,找到android sdk目錄,然後找到build-tools下面具體的某個版本的文件夾打開,裏面可以看到dx工具,然後把這個目錄複製下來,這就是要配置的環境變量的目錄,具體配置環境變量的方式這裏就不詳細說了,不會的自行谷歌或者百度。

第二步:創建一個Hello.java文件,我在裏面隨便打印了一句話"Welcome to [email protected]",代碼很簡單:

然後通過命令行 javac -target 1.6 -source 1.6 Hello.java 指定了jdk版本編譯,這裏爲了防止某些高版本的手機上跑不起來,那通過這一步就生成了Hello.class文件。

第三步:通過 dx --dex --output [輸出的dex文件名 ] [要執行的class文件名] 這個命令就可以生成dex文件了,給大家舉個栗子如下圖:

2-3、執行dex文件

第一步:要執行dex文件首先需要一臺手機,我這裏將我的華爲手機連上了我的電腦,打開開發者模式,並開啓USB調試。

第二步:將我們生成的Hello.dex文件push到手機中,打開控制檯,通過adb push [需要push的文件名] [push到的位置] 命令將文件push到了我手機的SD卡上。

第三步:通過adb shell 命令進入手機的控制檯,這裏我的是進入到了我的這部華爲手機的控制檯。

第四步:通過 dalvikvm -cp /sdcard/Hello.dex Hello命令執行dex文件,這裏-cp是指定dex文件的路徑,Hello是要執行的class name,這樣就可以看到執行結果了,跟我們代碼裏寫的一樣是不是,這裏也給大家截了個圖:

2-4、dex文件作用

 記錄整個工程中所有類文件的信息,記住是整個工程!

2-5、dex文件的整體結構

  • 一種8位字節的二進制流文件
  • 各個數據按順序緊密的排列,無間隙 
  • 整個應用中所有Java源文件都放在一個dex中(不考慮Android multidex技術的情況)
dex文件結構
文件頭 header 文件頭
索引區 string_ids 字符串的索引
type_ids 類型的索引
proto_ids 方法原型的索引
field_ids 域的索引
method_ids 方法的索引
數據區 class_defs 類的定義區
data 數據區
link_data 鏈接數據區

上面的表格中表明瞭dex文件格式有哪些區域,從表中可以看到主要分爲三個部分,第一部分是文件頭header,主要記錄了dex文件的信息以及所有字段的大致分佈。第二部分是索引區,主要包含了中間的一些字段,這部分完全定義了整個dex文件所有的類、方法、存儲的位置等。第三部分是數據區,分爲普通數據區和鏈接數據區,鏈接數據區主要是對一些動態鏈接庫so的指向。

2-6、dex文件的微觀結構

dex文件頭的各個字段的具體名稱及說明信息見下圖,它主要是對dex文件的整體做了介紹,介紹了它的大小、各個區段及對應的起始位置和偏移量等:

下面我們打開010 Editor軟件來具體查看一下dex文件的結構:

我們看到結果中顯示的結構體名稱和我們表格中的區域是一致的,最上面是文件頭,將文件頭展開就是圖片中展示的那些內容,文件頭下面就是全部的索引區,每個結構體代表了不同的索引區,比如第一個就是字符串索引,它記錄了整個應用中所有的字符串,可以看到它是從70h開始的,它的大小是38h,在上圖中我也用藍色選中了這部分內容,具體的查看方法和hello.class文件是類似的,大家可以自己對照着將結構體展開詳細的看一下,這裏就不再一一展開詳述了,因爲內容確實比較多,總的來說微觀結構和整體結構中說的是一一對應的。對於數據區的內容沒有單獨列出來,其實就是value對應的這部分內容,即通過索引就可以到數據區找到它對應的值。

2-7、class文件與dex文件對比

  • 本質上它們都是一樣的,dex文件是從class文件演變而來的 
  • class文件存在許多冗餘信息,dex會去除冗餘並整合

從下面這張圖就能夠很清楚的看出它們的異同了:

寫到這裏算是粗略的寫完了,中間可能有的地方說的並不是很清楚,說實話我理解的也並不是太深入,有什麼不懂的大家留言討論吧,天也不早了,人也很少了,大家洗洗睡吧!

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章