彻底弄懂dalvik字节码【三】
【一】、【二】中從代碼的角度分析了dalvik字節碼解釋執行的過程,這篇文章以一個例子來實際分析一下。
我們以這篇文章中提到的crackme為例,下載鏈接參見那篇文章。我們只分析dalvik字節碼,因此忽略so。
0x01:
使用Jeb打開crackme.apk,找到MainActivity的onCreate方法,其smali內容是:
.method protected onCreate(Bundle)V.registers 5.param p1, "savedInstanceState".prologue 00000000 invoke-super AppCompatActivity->onCreate(Bundle)V, p0, p1 00000006 const v2, 0x7F040019 0000000C invoke-virtual MainActivity->setContentView(I)V, p0, v2 00000012 const v2, 0x7F0C0050 00000018 invoke-virtual MainActivity->findViewById(I)View, p0, v2 0000001E move-result-object v1 00000020 check-cast v1, EditText.local v1, txt:Landroid/widget/EditText; 00000024 const v2, 0x7F0C0051 0000002A invoke-virtual MainActivity->findViewById(I)View, p0, v2 00000030 move-result-object v0 00000032 check-cast v0, Button.local v0, btn:Landroid/widget/Button; 00000036 sget-boolean v2, MainActivity->$assertionsDisabled:Z 0000003A if-nez v2, :4E :3E 0000003E if-nez v0, :4E :42 00000042 new-instance v2, AssertionError 00000046 invoke-direct AssertionError-><init>()V, v2 0000004C throw v2 :4E 0000004E new-instance v2, MainActivity$1 00000052 invoke-direct MainActivity$1-><init>(MainActivity, EditText)V, v2, p0, v1 00000058 invoke-virtual Button->setOnClickListener(View$OnClickListener)V, v0, v2 0000005E return-void .end methodsmali 對于Android,可以理解為匯編對于C。smali中定義了一套完整的dalvik操作碼(類似于匯編的指令集),構成了dalvik虛擬機最核心的部分。
字節碼是二進制的,這些二進制通過一定的方式可以被解釋成為smali指令。我們來看看這個過程。
0x02:
使用010editor打開crackme.apk中的classes.dex,應用dex模板,結果如下:
dex文件格式參見這篇文章
我們分析的目標是MainActivity的onCreate方法,直接找到它:
因為onCreate是重寫的父類方法,所以在virtual_methods中,我們看到這個方法需要5個寄存器,2個參數,3個內部方法調用參數,48條指令。字節碼在insns中。
0x03:
下面進入對字節碼的分析,在【二】中分析方法執行時, dvmInterpretPortable的最后一個語句:
FINISH(0); /* fetch and execute first instruction */即為定位到insns的起始處,并取2個字節的內容放置到inst變量中,之后取出inst的低字節數值作為handlerTable數組的索引號,然后跳轉到對應符號去執行。
代碼上的跟蹤比較繁瑣,好在google有相關的文檔:【Dalvik bytecode】、【Dalvik Executable instruction formats】,我們根據文檔來分析。
在這個例子中,第一個“兩字節”是:6F 20, 其低字節是6F,代表著handlerTable數組的索引號,查詢文檔:
表示操作符是invoke-super。
查看OP_INVOKE_SUPER.cpp,里面有對此指令的執行過程:
HANDLE_OPCODE(OP_INVOKE_SUPER /*vB, {vD, vE, vF, vG, vA}, meth@CCCC*/)GOTO_invoke(invokeSuper, false); OP_END其中:
# define HANDLE_OPCODE(_op) op_##_op: OP_INVOKE_SUPER = 0x6f, #define GOTO_invoke(_target, _methodCallRange) \do { \methodCallRange = _methodCallRange; \goto _target; \} while(false)翻譯一下就是:
op_0x6f:do{methodCallRange = false;goto invokeSuper;}while(false)invokeSuper在gotoTargets.cpp中定義:
GOTO_TARGET(invokeSuper, bool methodCallRange){Method* baseMethod;u2 thisReg;EXPORT_PC();vsrc1 = INST_AA(inst); /* AA (count) or BA (count + arg 5) */ref = FETCH(1); /* method ref */vdst = FETCH(2); /* 4 regs -or- first reg */if (methodCallRange) {ILOGV("|invoke-super-range args=%d @0x%04x {regs=v%d-v%d}",vsrc1, ref, vdst, vdst+vsrc1-1);thisReg = vdst;} else {ILOGV("|invoke-super args=%d @0x%04x {regs=0x%04x %x}",vsrc1 >> 4, ref, vdst, vsrc1 & 0x0f);thisReg = vdst & 0x0f;}... //此處省略后后面的內容其中:
#define GOTO_TARGET(_target, ...) _target:#define GOTO_TARGET_END翻譯一下就是:
invokeSuper:{Method* baseMethod;u2 thisReg;EXPORT_PC();vsrc1 = INST_AA(inst); /* AA (count) or BA (count + arg 5) */ref = FETCH(1); /* method ref */vdst = FETCH(2); /* 4 regs -or- first reg */if (methodCallRange) {ILOGV("|invoke-super-range args=%d @0x%04x {regs=v%d-v%d}",vsrc1, ref, vdst, vdst+vsrc1-1);thisReg = vdst;} else {ILOGV("|invoke-super args=%d @0x%04x {regs=0x%04x %x}",vsrc1 >> 4, ref, vdst, vsrc1 & 0x0f);thisReg = vdst & 0x0f;}... //此處省略后后面的內容這樣就找到了invokeSuper真正執行的地方了,可以看到,依然是解析字節碼和獲取新字節碼解析的過程。代碼跟蹤同樣比較復雜,我們直接看文檔:
從圖中可以看出6f的格式是35c(后面會用到),語法格式為:
invoke-*kind*{vC, vD, vE, vF, vG}, meth@BBBB其中的A、B、C的解釋在后面一列有說明,光看這個還不能看懂,看一下35c:
這個文檔我一開始也看得不是太懂,簡單的還能對應起來,復雜的(現在這個例子)就搞不清楚了,文檔只能理解個大概,最后我選擇看代碼。這里直接說我看代碼看明白的:
首先 6F 20 中的 6F表示操作碼,20又分兩個4位來解釋,2表示寄存器的數量,0代表啥還沒有看明白:(,隨后的兩個字節 47 2A 表示的是method id,47 2A 表示數值是0x2A47,即10823,如圖:
所以我們大概明白了:
invoke-super AppCompatActivity->onCreate(Bundle)V隨后的兩個字節 43 00 也是需要被解析的,是用來確定寄存器標號的,至于怎么映射為 p0 p1,我也沒有看懂(代碼在gotoTargets.cpp的GOTO_TARGET(invokeSuper, bool methodCallRange)中,歡迎有興趣的同學繼續研究并加微信交流)。
0x04:
至此,我們分析完第一條指令的字節碼解釋過程了。在源碼中,你可以看到,它不光是解釋成smali這么簡單,它真正的去尋找父類的onCreate方法,構造函數堆棧并進行調用。
pc指針隨著執行過程不斷往后移動,當方法返回后,繼續去下一個“兩字節”進行解釋執行。這個例子中的下一個“兩字節"是14 02,其中14為操作碼,如圖:
可以看到是const vAA, #+BBBBBBBB,和我們在Jeb中看到的const v2, 0x7F040019能夠對上。后續的過程和前面完全一致,只是處理的是不同的操作碼而已。
0x05:
至此,此系列文章結束。其中有一些細節我也沒有弄得很清楚(回頭弄明白再來更新),但是大致流程已經清晰了。歡迎有興趣的同學在此基礎上繼續研究,可以加我微信進行交流。微信見首頁二維碼。
作者:difcareer
鏈接:http://www.jianshu.com/p/aba1f966d7f2
來源:簡書
著作權歸作者所有。商業轉載請聯系作者獲得授權,非商業轉載請注明出處。
總結
以上是生活随笔為你收集整理的彻底弄懂dalvik字节码【三】的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 彻底弄懂dalvik字节码【二】
- 下一篇: Tramp data In Kernel