自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

Android虛擬機(jī)調(diào)試器原理與實(shí)現(xiàn)

云計(jì)算 虛擬化
本文主要講解Android虛擬機(jī)動(dòng)態(tài)調(diào)試背后涉及到的技術(shù)原理,除了JDWP協(xié)議細(xì)節(jié),還包括任意位置斷點(diǎn)、堆棧輸出、變量值獲取等基礎(chǔ)調(diào)試功能的具體實(shí)現(xiàn)。另外本文提供了一款新的android動(dòng)態(tài)調(diào)試工具——AVMDBG,提供調(diào)試API接口,支持python腳本擴(kuò)展。作為android調(diào)試技術(shù)研究過(guò)程中的實(shí)驗(yàn)項(xiàng)目,AVMDBG功能尚不完善,開(kāi)源出來(lái)僅供參考,如過(guò)有bug或其他疑問(wèn)反饋歡迎提交issue。

本文主要講解Android虛擬機(jī)動(dòng)態(tài)調(diào)試背后涉及到的技術(shù)原理,除了JDWP協(xié)議細(xì)節(jié),還包括任意位置斷點(diǎn)、堆棧輸出、變量值獲取等基礎(chǔ)調(diào)試功能的具體實(shí)現(xiàn)。另外本文提供了一款新的android動(dòng)態(tài)調(diào)試工具——AVMDBG,提供調(diào)試API接口,支持python腳本擴(kuò)展。作為android調(diào)試技術(shù)研究過(guò)程中的實(shí)驗(yàn)項(xiàng)目,AVMDBG功能尚不完善,開(kāi)源出來(lái)僅供參考,如過(guò)有bug或其他疑問(wèn)反饋歡迎提交issue。

一、Android動(dòng)態(tài)調(diào)試方案

在講解android動(dòng)態(tài)調(diào)試實(shí)現(xiàn)之前,先回顧下針對(duì)apk進(jìn)行動(dòng)態(tài)調(diào)試常用的幾種方法,比較下不同解決方案的特點(diǎn)和存在的問(wèn)題:

  1. smali插樁大法,反編譯apk后在關(guān)鍵位置插入smali代碼,通過(guò)打印日志的形式進(jìn)行調(diào)試跟蹤。但是這個(gè)方法非常繁瑣,除了需要編寫(xiě)smali代碼外,每次新增日志輸出點(diǎn)都需要重新解包重打包,而且經(jīng)常會(huì)遇到apk保護(hù)導(dǎo)致解包失敗的問(wèn)題需要解決。
  2. IDA在6.6版本以后支持dex文件的動(dòng)態(tài)調(diào)試,但是部分功能并不完善,比如監(jiān)視寄存器變量的值,需要通過(guò)watch窗口手動(dòng)添加并指定類型,而且單步過(guò)程中需要留意類型變化,可能導(dǎo)致虛擬機(jī)意外崩潰,導(dǎo)致這個(gè)問(wèn)題的原因在后續(xù)文章中會(huì)分析到。
  3. JEB,堪稱apk靜態(tài)分析的神器,在2.0版本以后開(kāi)始加入動(dòng)態(tài)調(diào)試的功能,最新版本同時(shí)支持smali代碼和native代碼的調(diào)試,功能可以說(shuō)非常強(qiáng)大,如果說(shuō)缺點(diǎn)可能只有一個(gè),和IDA一樣都是商業(yè)軟件,收費(fèi)不菲。
  4. andbug開(kāi)源項(xiàng)目,5年前的項(xiàng)目原作者已經(jīng)不再維護(hù),原項(xiàng)目只支持linux環(huán)境,動(dòng)態(tài)調(diào)試關(guān)鍵功能也有缺失,例如不支持任意代碼位置斷點(diǎn)等。使用過(guò)程中也碰到過(guò)一些bug,類似shell的交互方式使用起來(lái)并不是很順手。后來(lái)有國(guó)內(nèi)開(kāi)發(fā)者anbc對(duì)該項(xiàng)目進(jìn)行了完善改造,新增函數(shù)調(diào)用監(jiān)控功能支持配置文件。
  5. android studio+smalidea插件,也是一個(gè)非常不錯(cuò)的調(diào)試解決方案,作者通過(guò)IDE調(diào)試器插件的形式支持smali代碼的動(dòng)態(tài)調(diào)試,使用過(guò)程中偶爾會(huì)遇到跳行的bug。smalidea插件同樣是開(kāi)源項(xiàng)目,作者的作品還有非常知名的dex反匯編工具baksmali等。
  6. xposed類的安卓代碼Hook框架,可以通過(guò)在函數(shù)入口和出口處hook進(jìn)行調(diào)試輸出,但同樣無(wú)法對(duì)函數(shù)內(nèi)部代碼進(jìn)行斷點(diǎn)輸出。類似的項(xiàng)目還有cydia_substrate、frida、ddi等,也都是非常優(yōu)秀的開(kāi)源項(xiàng)目。

二、JDWP協(xié)議簡(jiǎn)述

JDWP是JavaDebug Wire Protocol (Java調(diào)試線協(xié)議)的縮寫(xiě),簡(jiǎn)單來(lái)講,就是調(diào)試器和目標(biāo)虛擬機(jī)進(jìn)行調(diào)試交互的通信協(xié)議,調(diào)試器通過(guò)JDWP協(xié)議來(lái)獲取JAVA虛擬機(jī)中程序的信息或控制目標(biāo)程序運(yùn)行狀態(tài),比如函數(shù)斷點(diǎn)設(shè)置,線程狀態(tài)、變量值獲取等,而目標(biāo)虛擬機(jī)也在指定事件觸發(fā)時(shí)通過(guò)JDWP協(xié)議通知調(diào)試器進(jìn)行處理,比如斷點(diǎn)事件消息、線程創(chuàng)建消息等。


[1] JDWP協(xié)議交互簡(jiǎn)圖

Android虛擬機(jī)雖然和普通java虛擬機(jī)存在不少差異,但是它的調(diào)試接口同樣是基于JDWP協(xié)議的,Dalvik虛擬機(jī)JDWP服務(wù)端的實(shí)現(xiàn)源碼位于:./android/dalvik/vm/jdwp。androidstudio、eclipse 等IDE的調(diào)試功能和DDMS的監(jiān)控功能也都是基于JDWP協(xié)議實(shí)現(xiàn)的。

JDWP協(xié)議通信要求首先進(jìn)行握手會(huì)話來(lái)表明互相的身份,調(diào)試器端發(fā)送的與虛擬機(jī)返回的數(shù)據(jù)包內(nèi)容一樣,內(nèi)容為“JDWP-Handshake”,通過(guò)驗(yàn)證以后才可以繼續(xù)后續(xù)的會(huì)話。


[2] JDWP協(xié)議通信前需要進(jìn)行握手會(huì)話

Android虛擬機(jī)的JDWP實(shí)現(xiàn)支持adb和socket兩種通信方式,可以使用adb的jdwp命令進(jìn)行端口轉(zhuǎn)發(fā)綁定,這樣就可以通過(guò)socket和調(diào)試目標(biāo)進(jìn)程進(jìn)行通訊,命令格式為”adb forwardtcp:[port] jdwp:[pid]”。

JDWP協(xié)議的通信會(huì)話主要包含2類數(shù)據(jù)包,分別為Command packet(命令包)與Reply packet(回復(fù)包):

  1. Command packet:調(diào)試器發(fā)送給虛擬機(jī)用于獲取程序狀態(tài)信息或控制程序運(yùn)行;虛擬機(jī)發(fā)送給調(diào)試器用于通知事件觸發(fā)消息。
  2. Reply packet:虛擬機(jī)發(fā)送給調(diào)試器用于回復(fù)命令包的請(qǐng)求或者執(zhí)行結(jié)果。

JDWP數(shù)據(jù)包主要包含包頭和和數(shù)據(jù)兩部分,包頭部分格式長(zhǎng)度固定,為11字節(jié),數(shù)據(jù)部分為可變長(zhǎng)度,字段結(jié)構(gòu)由其包頭指定的類型決定。下面詳解包頭各字段的含義:


[3] JDWP數(shù)據(jù)包頭結(jié)構(gòu)簡(jiǎn)圖

從上圖可以看出,命令包和回復(fù)包的包頭結(jié)構(gòu)基本一致,區(qū)別在于最后2個(gè)字節(jié),命令包拆分為2個(gè)單字節(jié)分別表示命令分組和命令序號(hào),在回復(fù)包中則用于表示錯(cuò)誤碼,非0表示命令執(zhí)行存在錯(cuò)誤。

以java7為例說(shuō)明,JDWP協(xié)議按功能劃分為18組命令,總計(jì)91個(gè)不同命令請(qǐng)求,包含了虛擬機(jī)、引用類型、對(duì)象、線程、方法、堆棧、事件等不同類型的操作命令。JDWP協(xié)議支持的命令細(xì)節(jié)可以參考官方文檔,這里不再贅述。

Android虛擬機(jī)對(duì)JDWP協(xié)議的支持實(shí)現(xiàn)并不完整,當(dāng)然調(diào)試需要的絕大部分關(guān)鍵命令都是支持的,具體信息可以參考安卓dalvik虛擬機(jī)源碼:./android/dalvik/vm/jdwp/JdwpHandler.cpp ,下圖是dalvik虛擬機(jī)(以android4.2版本為準(zhǔn))支持的命令請(qǐng)求類型表:


[4] JDWP協(xié)議命令表簡(jiǎn)圖(以android4.2版本支持為準(zhǔn))

雖然上面圖表中的命令類型比較多,但JDWP協(xié)議本身并不算復(fù)雜,按協(xié)議標(biāo)準(zhǔn)進(jìn)行組包請(qǐng)求即可,下面以獲取目標(biāo)虛擬機(jī)版本信息的命令“VirtualMachine:version”為例,演示一次JDWP協(xié)議的交互通信過(guò)程:


[5] “VirtualMachine:version”命令返回?cái)?shù)據(jù)的注解

從上圖的命令注解中可以看出,“VirtualMachine:version”請(qǐng)求命令沒(méi)有附加數(shù)據(jù),回復(fù)包數(shù)據(jù)中包含5個(gè)字段,通信數(shù)據(jù)包解析過(guò)程如下圖:


[6]“VirtualMachine:version”命令通信數(shù)據(jù)包解析

從上圖的解析示意中,我們最終獲取到目標(biāo)虛擬機(jī)的版本信息,這中間有2點(diǎn)需要注意:

  1. 數(shù)據(jù)包中數(shù)據(jù)使用大端模式;
  2. 基本數(shù)據(jù)類型的內(nèi)存結(jié)構(gòu),例如string,使用[長(zhǎng)度]+[字符數(shù)據(jù)]的形式;

下面我們整理下JDWP協(xié)議中使用到的其他基本數(shù)據(jù)類型,在后續(xù)的命令請(qǐng)求與數(shù)據(jù)包解析中都會(huì)頻繁使用到。其中有些數(shù)據(jù)類型的長(zhǎng)度是由虛擬機(jī)實(shí)現(xiàn)定義的,比如ObjectID等,可以通過(guò)“VirtualMachine:IDSizes”命令進(jìn)行獲取。下圖整理的數(shù)據(jù)類型說(shuō)明以Android虛擬機(jī)的實(shí)現(xiàn)為標(biāo)準(zhǔn):


[7] JDWP協(xié)議使用到基本數(shù)據(jù)類型

三、動(dòng)態(tài)調(diào)試的核心 —— 任意代碼位置斷點(diǎn)

針對(duì)簡(jiǎn)單apk進(jìn)行逆向時(shí)靜態(tài)分析就足夠勝任,但是碰到下面幾種情況的時(shí)候,“任意代碼位置斷點(diǎn)”的動(dòng)態(tài)調(diào)試分析則更加合適:

  1. 需要對(duì)代碼中的關(guān)鍵參數(shù)、變量值進(jìn)行觀察,例如加密后的字符串。
  2. 對(duì)偏底層的公共函數(shù)庫(kù)進(jìn)行hook輸出,快速篩選定位可疑調(diào)用,例如API調(diào)用監(jiān)控等。
  3. 在代碼規(guī)模比較龐大、調(diào)用邏輯比較復(fù)雜的情況下,需要使用堆棧跟蹤對(duì)可疑關(guān)鍵點(diǎn)的調(diào)用路徑梳理確認(rèn)。

“任意代碼位置斷點(diǎn)”功能不單指代碼斷點(diǎn)的調(diào)試事件通知,還會(huì)涉及到虛擬機(jī)棧結(jié)構(gòu)、寄存器使用、參數(shù)變量值獲取以及堆棧跟蹤等方面的功能,但是其中很多知識(shí)點(diǎn)限于篇幅無(wú)法在本文中全部講解透徹,這里推薦兩篇關(guān)于Dalvik虛擬機(jī)的文章可以作為擴(kuò)展閱讀:《深入理解Android之Java虛擬機(jī)Dalvik》、《Dalvik虛擬機(jī)進(jìn)程和線程的創(chuàng)建過(guò)程分析》。

下面重點(diǎn)講解斷點(diǎn)功能的實(shí)現(xiàn),其中的關(guān)鍵點(diǎn)可以歸結(jié)為以下幾個(gè)問(wèn)題:

1) 如何設(shè)置和處理斷點(diǎn)事件?

斷點(diǎn)設(shè)置需要用到的命令是“EventRequest:Set”,該命令支持多種事件請(qǐng)求,包括斷點(diǎn)、單步、類加載、方法進(jìn)出、字段訪問(wèn)、線程、異常等多種事件,設(shè)置成功以后目標(biāo)虛擬機(jī)會(huì)返回RequestID,并且在事件觸發(fā)時(shí)會(huì)發(fā)送相應(yīng)的事件信息給調(diào)試器請(qǐng)求處理(Event:Composite),各類事件命令的具體格式可以參考官方文檔。下面以斷點(diǎn)為例進(jìn)行講解事件設(shè)置,首先看下事件命令請(qǐng)求的結(jié)構(gòu)字段:


[8] 斷點(diǎn)命令請(qǐng)求的數(shù)據(jù)包字段結(jié)構(gòu)

上面的斷點(diǎn)事件我們只設(shè)置了一個(gè)過(guò)濾器,就是斷點(diǎn)位置Location,這個(gè)結(jié)構(gòu)體用于指明觸發(fā)事件的代碼位置,包含類型標(biāo)記,ClassID,MethodID以及代碼偏移DexPc。

1.其中的ClassID和MethodID兩個(gè)字段的值可以通過(guò)“VirtualMachine: ClassesBySignature”命令與“ReferenceType: MethodsWithGeneric”命令獲取。


[9] 獲取ClassID與MethodId的演示代碼

2.代碼偏移位置DexPc值可以使用反匯編工具baksmali的“-l”參數(shù)獲取。下圖紅圈中的數(shù)字就是代碼偏移位置。


[10] backsmali反匯編結(jié)果中的代碼偏移標(biāo)記

當(dāng)虛擬機(jī)運(yùn)行到我們?cè)O(shè)置的斷點(diǎn)位置以后,會(huì)發(fā)送“Event: Composite”命令給調(diào)試器,目標(biāo)虛擬機(jī)發(fā)送的命令中會(huì)包含線事件類型、請(qǐng)求序號(hào)、線程序號(hào)以及代碼位置等信息,其中請(qǐng)求序號(hào)和斷點(diǎn)命令返回的請(qǐng)求序號(hào)是對(duì)應(yīng)的,調(diào)試器可以根據(jù)請(qǐng)求序號(hào)進(jìn)行相應(yīng)后續(xù)處理,例如獲取線程堆棧、變量值等,最后需要恢復(fù)虛擬機(jī)的運(yùn)行狀態(tài)。斷點(diǎn)事件報(bào)告的字段結(jié)構(gòu)如下圖:


[11] 斷點(diǎn)事件報(bào)告的數(shù)據(jù)包字段結(jié)構(gòu)

2) 如果獲取當(dāng)前函數(shù)調(diào)用棧信息

通過(guò)獲取當(dāng)前線程的調(diào)用棧信息可以定位目標(biāo)函數(shù)的調(diào)用路徑和源頭,對(duì)于邏輯層次復(fù)雜的逆向分析非常有用。對(duì)應(yīng)的JDWP命令為“ThreadReference:Frames”,該命令的請(qǐng)求包與返回包字段比較簡(jiǎn)單,結(jié)構(gòu)如下:


[12] “ThreadReference:Frames”命令相關(guān)包的字段結(jié)構(gòu)

返回?cái)?shù)據(jù)為棧幀數(shù)組,每一層棧幀信息包含棧幀ID和棧幀位置2個(gè)字段,第一層棧幀一般為當(dāng)前函數(shù),其棧幀ID在后面堆棧獲取變量值命令中需要使用。棧幀信息解析后輸出的例子如下:


[13] “ThreadReference:Frames”返回?cái)?shù)據(jù)的解析結(jié)果

3) 如何獲取函數(shù)參數(shù)值與變量值

dalvik虛擬機(jī)和普通java虛擬機(jī)最大的區(qū)別之一應(yīng)該是dalvik是基于寄存器架構(gòu)的,可以觀察dex反匯編后的smali代碼內(nèi)容,參數(shù)傳遞、變量賦值全部都是對(duì)寄存器的操作。關(guān)于smali語(yǔ)法與寄存器變量等方面的基礎(chǔ)知識(shí),推薦幾篇文章作為前置閱讀:《smali-Registers》、《smali-TypesMethodsAndFields》、《Smali學(xué)習(xí)筆記》,本文就不再贅述。

從當(dāng)前堆棧中獲取寄存器值需要使用“StackFrame:GetValues”命令,下面我們解析下該命令的請(qǐng)求包、返回包的字段結(jié)構(gòu):


[14] “StackFrame: GetValues”命令的注解

該命令請(qǐng)求中的有幾個(gè)關(guān)鍵字段需要詳細(xì)解釋:

1)ThreadId,返回包中觸發(fā)斷點(diǎn)事件被掛起的線程ID。

2)FrameId,可以通過(guò)“ThreadReference: Frames”命令獲取,從返回的棧列表中取第一層棧幀,即可獲得的我們需要的當(dāng)前棧幀ID。

3)Slot,變量偏移位置,這個(gè)字段是“StackFrame: GetValues”命中最為關(guān)鍵的字段,對(duì)于Debug版的apk可以通過(guò)“Method: VariableTable”命令獲取,但是逆向分析遇到的幾乎都是Release版本,是不包含這些調(diào)試輔助信息的,關(guān)于參數(shù)和變量對(duì)應(yīng)偏移位置slot的計(jì)算方法,我們可以嘗試從dalvik虛擬機(jī)源碼中尋找答案。

dalvik虛擬機(jī)處理“StackFrame: GetValues”命令的函數(shù)為handleSF_GetValues,handleSF_GetValues函數(shù)主要負(fù)責(zé)解析請(qǐng)求包的字段信息,擴(kuò)展返回?cái)?shù)據(jù)的內(nèi)存空間,最后調(diào)用dvmDbgGetLocalValue獲取變量值。下面我們跟蹤slot參數(shù)的傳遞使用過(guò)程來(lái)分析它的含義。


[15] dvmDbgGetLocalValue函數(shù)的代碼解析

從上圖的dvmDbgGetLocalValue函數(shù)代碼解析過(guò)程中,我們有以下幾點(diǎn)發(fā)現(xiàn):

1) frameId值就是當(dāng)前棧指針的內(nèi)存地址,函數(shù)調(diào)用過(guò)程中使用的寄存器(參數(shù)與局部變量)相當(dāng)于此處的內(nèi)存映射,而slot值就相當(dāng)于映射偏移索引號(hào)。參數(shù)使用最后的N個(gè)寄存器(內(nèi)存段高地址),局部變量使用從v0開(kāi)始的前(M-N)個(gè)寄存器(注意參數(shù)占用2個(gè)寄存器的情況)。

2) Slot偏移值會(huì)經(jīng)過(guò)untweakSlot函數(shù)處理,這可能是針對(duì)Eclipse的變通方案。1000偏移被重定向到0,而0偏移被重定向到參數(shù)偏移起始處,在計(jì)算slot索引值時(shí)需要注意到這一點(diǎn)。

3) 獲取變量值支持的幾種類型細(xì)節(jié)如下:

boolean、byte、short、char、int、float類型的變量直接根據(jù)slot偏移取值,大小為4字節(jié),占用1個(gè)寄存器;

array、object類型根據(jù)slot偏移取值為Object指針,查表獲得ObjectId返回,大小為4字節(jié),占用1個(gè)寄存器;

double、long類型根據(jù)slot偏移取值,大小為8字節(jié),占用2個(gè)寄存器。

4) IDA的watch窗口指定變量類型取值后可能導(dǎo)致崩潰的原因在這里也可以找到,在進(jìn)入其他函數(shù)調(diào)用過(guò)程時(shí),沒(méi)有及時(shí)手動(dòng)修正watch窗口指定的寄存器類型,取值過(guò)程由于讀取異常拋出導(dǎo)致虛擬機(jī)退出。

以下為2個(gè)函數(shù)的參數(shù)與局部變量slot偏移結(jié)果的對(duì)比,其中一個(gè)是普通成員函數(shù),另外一個(gè)為靜態(tài)成員函數(shù),可以通過(guò)這兩個(gè)例子加深對(duì)slot計(jì)算的理解:


[16] 普通成員函數(shù)參數(shù)與變量slot解析


[17] 靜態(tài)成員函數(shù)參數(shù)與變量slot解析

四、全新的android動(dòng)態(tài)調(diào)試工具——AVMDBG

AVMDBG是android虛擬機(jī)調(diào)試技術(shù)研究過(guò)程中一個(gè)實(shí)驗(yàn)項(xiàng)目,目前只支持dalvik虛擬機(jī),已實(shí)現(xiàn)代碼斷點(diǎn)、堆棧輸出、參數(shù)變量值獲取等基礎(chǔ)功能。

項(xiàng)目地址:https://github.com/cheetahsec/avmdbg

項(xiàng)目說(shuō)明:AVMDBG的目標(biāo)是打造一款輕量級(jí)的的android虛擬機(jī)調(diào)試器,底層使用C++編寫(xiě),通過(guò)Python擴(kuò)展的方式提供調(diào)試接口,可以通過(guò)編寫(xiě)python腳本實(shí)現(xiàn)對(duì)安卓app的快速動(dòng)態(tài)調(diào)試,當(dāng)前版本主要提供以下API:

1.bool attach(string&processName);

功能: 附加到目標(biāo)進(jìn)程

2.void waitLoop();

功能: 循環(huán)等待調(diào)試事件

3.bool setBreakPoint(py::dict&breakPoint);

功能: 設(shè)置斷點(diǎn)事件

4.py::list getStackFrames(ObjectIdthreadId);

功能: 獲取當(dāng)前線程堆棧,可在斷點(diǎn)回調(diào)函數(shù)中使用

5.py::dictgetRegisterValue(py::dict& Context, string& regName, u1varType);

功能: 獲取寄存器變量的值,需要指定寄存器名稱(V命名或P命名法)以及變量類型

6.py::listgetObjectFieldValues(ObjectId objectId);

功能: 獲取對(duì)象的成員變量信息,參數(shù)為對(duì)象ID

7.py::dict getArrayObjectValue(ObjectIdobjectId);

功能: 獲取數(shù)組類型變量的信息

8.py::str getStringValue(ObjectIdobjectId);

功能:獲取字符串String類型變量的值

詳細(xì)的使用說(shuō)明可以參考項(xiàng)目文檔和測(cè)試demo,以下為部分測(cè)試代碼展示:


[18] AVMDBG功能測(cè)試代碼


[19] AVMDBG測(cè)試輸出結(jié)果

五、參考文檔

  1. 《深入Java調(diào)試體系》
  2. 《Java(tm) Debug WireProtocol》
  3. 《深入理解Android之Java虛擬機(jī)Dalvik》
  4. 《Dalvik虛擬機(jī)進(jìn)程和線程的創(chuàng)建過(guò)程分析》
  5. 《smali-Registers》、《smali-TypesMethodsAndFields》
  6. 《Android dalvik虛擬機(jī)源代碼》

 

責(zé)任編輯:未麗燕 來(lái)源: FreeBuf
相關(guān)推薦

2011-08-31 16:51:12

Lua調(diào)試器

2009-09-07 21:51:59

2011-08-24 16:41:38

lua調(diào)試器

2011-08-31 16:39:06

Lua調(diào)試器

2011-08-31 16:47:07

Lua調(diào)試器

2017-04-19 21:35:38

Linux調(diào)試器工作原理

2024-03-26 07:30:07

Java虛擬機(jī)源文件

2020-03-16 10:05:13

EmacsGUDLinux

2010-03-01 11:06:52

Python 調(diào)試器

2019-08-27 16:23:41

Docker虛擬化虛擬機(jī)

2012-05-18 10:22:23

2018-05-08 14:47:38

虛擬機(jī)方法代碼

2010-02-06 15:13:46

ibmdwJava

2009-12-14 10:57:34

Ruby調(diào)試器

2010-03-04 09:35:21

Android虛擬機(jī)

2013-07-17 09:32:58

2011-11-15 09:16:12

虛擬化虛擬機(jī)Archipel

2023-02-28 11:39:55

CMake腳本項(xiàng)目

2010-07-26 09:02:38

2011-02-16 14:49:17

虛擬機(jī)
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)