天天看點

安卓逆向學習----Dalvik虛拟機

Dalvik名字來源于其作者祖先居住的小村莊,老外喜歡起這種名字,類似的還有ubuntu、Kali,雖說現在使用ART取代了Dalvik,但是感覺簡單學習一下還是有用的。

一、.與java虛拟機對比

Java虛拟機解析class檔案,Dalvik虛拟機解析dex(dalvik executable)檔案

android SDK 的dx工具可以将java位元組碼轉換為Dalvik位元組碼,對java類檔案進行了壓縮,去除了備援資訊,是以體積更小

架構不同,Java虛拟機基于棧結構,Dalvik基于寄存器架構,對于手機裝置來說後者更适用,并且速度更快

下面以Hello.java 為示例檔案,分析兩種位元組碼的差別

public class Hello {
	public int foo(int a,int b){
		return (a+b)*(a-b);
	}

	public static void main(String[] argc){
		Hello hello=new Hello();
		System.out.println(hello.foo(5,3));
	}
}
           

1. javac Hello.java 将其編譯為Hello.class

2. ./dx --dex --output=Hello.dex Hello.class 将class檔案轉化為dex檔案

3. javap -c -classpath . Hello得到反編譯代碼,這是jvm指令集,這裡隻列出foo函數對應代碼。

java位元組碼一個指令為一個位元組,PC計數器以位元組為機關記錄指令偏移量,圖中PC對應的指令為iadd,前兩行指令取出了函數兩個參數并加載到求值棧

安卓逆向學習----Dalvik虛拟機

4.dexdump -d Hello.dex  得到的是dalvik指令集代碼,這裡隻列出foo函數對應代碼,左邊的上半部分和下半部分分别對應指令的十六進制和助記符格式。Dalvik維護一個pc計數器和一個調用棧,但是這個調用棧維護的是一份寄存器清單。

安卓逆向學習----Dalvik虛拟機

二、安卓系統如何啟動及dalvik虛拟機如何運作

1.系統架構以及運作流程

(1)Loader層:加載和運作引導程式

         boot ROM:開機時,引導晶片從rom中預設的代碼開始執行,然後将引導程式加載到ram。

         boot loader:運作引導程式,主要是檢查ram、初始化參數等。

(2)kernel層:Android核心層,在這裡開機剛剛完成進入系統 

         啟動swapper程序(pid=0的程序),這是系統初始化過程kernel建立的第一個程序,用于初始化程序管理、記憶體管理、加載驅動等工作

         啟動kthreadd程序,這是linux系統的核心程序,會建立核心工作線程kworkder、軟中斷程序ksoftirqd和thermal等核心守護程序,kthreadd是所有核心程序的父程序。

(3)native層(C++ Framework層):包含C++庫、硬體抽象層(HAL)、Dalvik虛拟機或者安卓運作時(ART)

        由init程序孵化出使用者空間的守護程序、開機動畫、hal層等。init是linux的守護程序,是所有使用者空間程序的父程序.

        init程序孵化出MediaServer程序,負責啟動和管理C++Framework層,包含AudioFlinger,Camera Service等服務。

        特别的,init程序會解析init.rc檔案,然後孵化出zygote程序,zygote程序是Android系統的第一個java程序(虛拟機程序),zygote是所有java程序的父程序

(4)java Framework層(application Framework層):這一層由java語言編寫

        zygote程序負責加載ZygoteInit類、加載虛拟機、提前加載類preloadClasses、提前加載資源preloadResource

        zygote程序孵化出System Server程序,它負責啟動和管理整個Java Framework,包含ActivityManager、PackageManager、WindowManager等服務

(5)App層:這一層與使用者直接互動,應用使用的是java語言開發

         Zygote程序孵化出的第一個App程序是Luncher,就是桌面App,然後孵化出browser、phone、Email等基礎的App程序,一個App至少運作在一個程序上。

         所有的App程序都是由Zygote程序fork而來

(6)Syscall和JNI

          Native和kernel之間是系統調用(Syscall)層

          Java層與Native層之間的紐帶是JNI(Java Native Interface)

2.dalvik虛拟機如何運作

上文已經介紹了Zygote程序,Zygote fork出其他程序之後,執行的工作就有Dalvik來進行。

它首先通過LoadClassFromDex()函數完成類的裝載工作,類被解析之後有一個ClassObject類型的資料結構儲存在運作時環境中,虛拟機使用gDvm.loadedClasses全局哈希表來儲存和查詢所有裝載的類;

位元組碼驗證器使用verifyCodeFlow()函數對裝入的代碼進行校驗,随後虛拟機調用FindClass()函數查找和裝載main方法類,随後調用dvmInterpret()函數初始化解釋器并執行位元組碼流

3.Dalvik虛拟機JIT(just in time 即時編譯)機制

JIT又稱為動态編譯,是一種在運作時将位元組碼翻譯為機器碼的技術,這使得程式執行速度更快

JIT有兩種代碼編譯方式:Method方式和trace方式。分别以函數為機關和以trace為機關進行編譯。簡單說一下trace方式,函數的代碼被分為許多條執行路徑,根據執行的頻繁與否被分為熱路徑和冷路徑。trace方式能夠快速的擷取熱路徑,以更短的時間和更少的記憶體來編譯代碼

三、Dalik指令

Dalvik指令語言其實就是smali語言,指令這部分有點多,回頭再研究

smali檔案執行個體

.class public LHelloWorld; 	#定義類名
.super Ljava/lang/Object; 	#定義父類
.method public static main([Ljava/lang/String;)V
	.registers 4		#程式中使用4個寄存器,v0,v1,v2和一個參數寄存器
.parameter		#一個參數,有n個參數則有n行
.prologue		#代碼起始的指令
#空指令
    nop
    nop
    nop
    nop
	#資料定義指令
	const/16 v0, 0x8
	const/4 v1, 0x5
	const/4 v2, 0x3
	#資料操作指令
	move v1, v2
	#數組操作指令
	new-array v0, v0, [I
	array-length v1, v0
	#執行個體操作指令
	new-instance v1, Ljava/lang/StringBuilder;
	#方法調用指令
	invoke-direct {v1}, Ljava/lang/StringBuilder;-><init>()V
	#跳轉指令
	if-nez v0, :cond_0
	goto :goto_0
	:cond_0
	#資料轉換指令
	int-to-float v2, v2
	#資料運算指令
	add-float v2, v2, v2
	#比較指令
	cmpl-float v0, v2, v2
	#字段操作指令
	sget-object v0, Ljava/lang/System;->out:Ljava/io/PrintStream;
	const-string v1, "Hello World" #構造字元串
	#方法調用指令
	invoke-virtual {v0, v1}, Ljava/io/PrintStream;->println(Ljava/lang/String;)V
	#傳回指令
	:goto_0
    return-void
.end method
           

編譯smali檔案,生成dex檔案

java-jar smali.jar -o classes.dex Helloworld.smali
           

在真機或者虛拟機測試運作

先将classes.dex壓縮為Helloworld.zip

adb push Helloworld.zip /data/local/tmp
adb shell dalvikvm -cp /data/local/tmp/Helloworld.zip HelloWorld
           

正常運作會列印helloworld字元串