一、前言
Android 的 App 實際上并不是運行在 Java 虛擬機中唱较,而是運行在 Dalvik 虛擬機中扎唾。Dalvik 虛擬機對 Java 虛擬機做了一些額外的優(yōu)化,讓它更適用于移動設備南缓。而 Dalvik 也有自己獨特的匯編語言胸遇,Dalvik 就是通過這些匯編的指令集,來運行我們編譯好的 Apk 程序汉形。
一般這些內容纸镊,我們正常開發(fā) App 是接觸不到的,但是如果你有反編譯的需求概疆,那你就需要花點時間研究一下它逗威。本文不會介紹 Dalvik 的匯編指令集,它本身已經(jīng)有完備的文檔岔冀,沒什么好說的凯旭。
本文就從逆向思維的路子,教你如何寫一個可在 Dalvik 上獨立運行的 Hello World 程序使套。
在這個過程中罐呼,我們需要了解 smali 語法,smali 是一種寬松的 Jasmin/dedexer 語法侦高,它可以通過 baksmali 將我們已經(jīng)編譯好的 dex 格式的匯編語言弄贿,反匯編成 smali 文件,供我們閱讀矫膨。
那么,我們的第一個 Dalvik 版本的 Hello World 期奔,就從一個編寫一個 smali 文件開始吧侧馅。
二、開始編寫 Smali
既然是 smali 文件呐萌,當然是以 .smali
為文件后綴馁痴,這里先創(chuàng)建一個 SmaliHello.smali 文件,直接上代碼肺孤,再來看每行的含義罗晕。
第 1~3 行,實際上是聲明了 smali 文件的頭赠堵,每個 smali 文件都會有它們小渊。.class
表示類名,這里定義了一個 public 的類茫叭,全類名是 com.cxmyDev.smalidemo.SmaliHelo
酬屉。.super
表示它的父類,這里是 Object。.source
表示它對應的 Java 文件的文件名呐萨,這只是個標記杀饵,實際上在真實反編譯的場景下,如果代碼被混淆了谬擦,.source
可能會沒有值切距。
第 6 行,定義了一個 # direct methos
惨远,它是 baksmali 為我們添加的一行注釋谜悟,表示之后緊跟著這個類相對應的方法,需要注意的是锨络,只會包含構造方法和靜態(tài)方法赌躺,這里不展開討論了。
第 7~14 行羡儿,以一個 .method
開始礼患,.end
結尾,表示它是一個方法,而 publi constructor
表示它是一個公有的構造方法光羞,這里其實就是 Java 類默認的構造方法锻离,如果我們不聲明構造方法,編譯器會為我們創(chuàng)建一個?無參的構造方法肤粱,這里就是它了。沒啥好說的厨相,直接寫就好了领曼。
第 16~28 行,它也是一個方法蛮穿,public static
表示它是一個公有的靜態(tài)方法庶骄,方法名是 main。而之后緊跟的 ([LJava/lang/String;])V
表示它需要傳遞一個 String 數(shù)組践磅,并且返回值是 void单刁。再來看看方法內部的代碼,第 17 行府适,.registers
表示寄存器的聲明羔飞,這里聲明了 3 個寄存器,供后面使用檐春,.param
表示了方法傳遞的參數(shù)逻淌,參數(shù)名叫 args
,并且是一個 String 數(shù)組類型疟暖。.prologue
表示一個開場恍风,之后跟隨的才是我們業(yè)務邏輯的代碼。
第 21 行,sget-object
表示創(chuàng)建了一個 PrintStream 對象朋贬,并存入 v0 寄存器中凯楔。
第 23 行,const-string
表示什么了一個字符串 "Hello CxmyDev!"锦募,并存入 v1 寄存器中摆屯。
第 25行,invoke-virtual
表示調用了 PrintStream 中的 printIn()
方法糠亩,參數(shù)傳遞的是 v1 寄存器中的值虐骑,就是之前存儲的 "Hello CxmyDev!"。
到這里赎线,smali 中的代碼廷没,我們已經(jīng)逐行認清楚它是干嘛的了,有些細節(jié)就不展開講了垂寥,不了解的可以看看 Dalvik 的語法和 smali 的語法颠黎,有興趣可以先看看這兩個鏈接。
Dalvik-bytecode:
https://source.android.com/devices/tech/dalvik/dalvik-bytecode
Dex 格式:
三滞项、編譯 smali
編寫完 smali 代碼之后狭归,接下來就要將它編譯成 dex 文件了,這就需要用到 smali.jar 這個工具文判。你可以在 Bitbucket 上直接下載到 jar 包过椎。
smali.jar 最新的版本版本是 2.2.1,所以這里下載這個版本就可以了戏仓。(不方便下載的話疚宇,文末有下載方式)
先來看看 smali.jar 的幫助文檔,直接使用 java -jar
命令即可赏殃。
我們這里主要會用到它的 assemble
命令敷待,再來看看 assemble 的幫助文檔,使用 java -jar snali.jar a
命令即可查看嗓奢,a
是 assemble
的縮寫。
可以看到浑厚,使用 -o
就可以指定輸出的 dex 文件股耽,然后再指定編譯的 smali 文件即可。
java -jar smali-2.2.1.jar a -o hello.dex SmaliHello.smali
執(zhí)行完成钳幅,如果沒有報錯的話物蝙,可以在當前目錄下,生成一個 hello.dex 文件敢艰。如果有其它輸出诬乞,應該就是報錯了,查看一下報錯信息解決它就好了。
得到 hello.dex 文件之后震嫉,我們還需要將它放到我們的 Android 設備上森瘪,才可以運行,這個非常簡單票堵,使用 adb push
命令即可扼睬。
最終運行這個 dex 文件,還需要使用到 dalvikvm 悴势,使用 adb shell dalvikvm -h
命令窗宇,查看幫助文檔,文檔比較長特纤,這里截取關鍵部分军俊。
這里我們主要是使用 -cp
指定 classpath 即可執(zhí)行,它后續(xù)接收的是類的完整簽名捧存,包含包名粪躬。
然后我們就需要使用 dalvikvm -cp
命令即可執(zhí)行,主要指定要執(zhí)行的類矗蕊,需要包含包名的全類名短蜕。
這里,就可以輸出我們之前編寫的 Hello CxmyDev! 了傻咖。
下面?zhèn)浞菀幌螺斎氲拿睢?/p>
adb shell push hello.dex /sdcard/
adb shell dalvikvm -cp /sdcard/hello.dex com.cxmydev.smalidemo.SmaliHello
四朋魔、Dex 的 Java 代碼
到這里就算是將清楚,從零編寫一個 smali 代碼卿操,到編譯成 dex 并成功執(zhí)行的所有過程了警检。
我們再來看看,我們編輯的 smali 代碼害淤,到底用 Java 代碼編寫扇雕,是什么內容,可以幫助我們更好的理解它窥摄。
其實很簡單镶奉,再使用 jadx 工具,對 dex 進行反編譯崭放。因為我們這里也不涉及混淆哨苛,所以代碼結構非常的清晰。
這里就是初學 Java 的時候币砂,一個標準的 Java 程序建峭,有個 main 函數(shù)為程序的入口函數(shù)。
五决摧、小結
本文到這里就算是完成了整個逆向的反逆向流程亿蒸,相信能讓你加深對反編譯和 smali 的理解凑兰。
有些工具如果不方便下載(原因你懂的),可以在承香墨影公眾號回復 smali工具 進行下載边锁,可以下載到本文所有涉及到的資源文件姑食。
更多反編譯的細節(jié),可以在承香墨影公眾號回復 Android反編譯砚蓬,你將獲得我整理好的一些關于反編譯的資料矢门。
今天在承香墨影公眾號的后臺,回復 成長灰蛙。我會送你一些我整理的學習資料祟剔,包含:Android反編譯、算法摩梧、設計模式物延、Web項目源碼。
推薦閱讀:
- Glide 這樣用仅父,更省內存E咽怼!笙纤!
- 跳槽前耗溜,不思考這些等于白跳!J∪荨抖拴!
- 哪些年熬過的夜,終將會"回報"你的
- 當你只有一個設備的時候腥椒,如何做好屏幕適配
- Android 源碼阿宅,在線閱讀更方便!A搿洒放!