android native java_在Android Native层中创建Java虚拟机实例
前言
Android應(yīng)用中JNI代碼,是作為本地方法運(yùn)行的。而大部分情況下,這些JNI方法均需要傳遞Dalvik虛擬機(jī)實(shí)例作為第一個(gè)參數(shù)。例如,你需要用虛擬機(jī)實(shí)例來(lái)創(chuàng)建jstring和其他的Java對(duì)象、查找類(lèi)或成員變量等。大部分情況下,在你用JNI接口從Java層調(diào)用Native層中的代碼時(shí),你并不需要在native代碼中自己初始化一個(gè)Dalvik虛擬機(jī)實(shí)例。但是,如果你在搞逆向或者寫(xiě)exp,你總是需要鉆研各種非常規(guī)的情況。
最近,我在逆向時(shí)需要在native代碼中手動(dòng)創(chuàng)建虛擬機(jī)實(shí)例用于在JNI接口函數(shù)中傳遞Java對(duì)象。在本文中,我將分享我是如何實(shí)現(xiàn)這種方法的。
標(biāo)準(zhǔn)方法
在JNI中創(chuàng)建JVM虛擬機(jī)實(shí)例的官方文檔在地址How to Create a JVM Instance in JNI。但是,不幸的是這種方法在Android上面是不能正常運(yùn)行的,因?yàn)閖int JNI_CreateJavaVM(JavaVM**, JNIEnv**, void*)函數(shù)不是導(dǎo)出函數(shù),無(wú)法直接調(diào)用。假如你不熟悉這個(gè)方法的話(huà),可以根據(jù)它的名字在jni.h文件中查找一下,確認(rèn)是否是導(dǎo)出函數(shù)。在我這里,jni.h文件位于android-sdk/ ndk-bundle/platforms/android-9/arch-x86/usr/include/jni.h。相關(guān)代碼如下:
如果你嘗試編譯調(diào)用上述截圖中函數(shù)的代碼,你可能會(huì)得到下面的錯(cuò)誤:
官方文檔中介紹的如何創(chuàng)建JVM的方法在這里可以用來(lái)理解上述的API函數(shù)和它們的選項(xiàng)和參數(shù)的用途。如果你想在Android中使用這些方法,你必須顯示從so庫(kù)中調(diào)用這些方法。
官方文檔中介紹的如何初始化虛擬機(jī)的類(lèi)路徑,在此處是非常有用的。其內(nèi)容如下:
上面的配置,設(shè)置當(dāng)前的類(lèi)路徑為當(dāng)前目錄(.)。如果你想要虛擬機(jī)訪問(wèn)系統(tǒng)或者app的類(lèi),這是必須設(shè)置的。實(shí)驗(yàn)表明,將該值設(shè)置為一個(gè)目錄并不會(huì)起作用。我嘗試將其設(shè)置為/data/local/tmp,同時(shí)在該目錄下放置了一個(gè)dex文件、含有dex文件的jar包和apk文件。只有在設(shè)置jar包、dex文件或apk文件的全路徑時(shí),上述選項(xiàng)才起作用。奇怪的是,當(dāng)類(lèi)路徑中沒(méi)有一個(gè)合法的文件時(shí),系統(tǒng)類(lèi)(例如java.lang.String)都不能訪問(wèn)。換句話(huà)說(shuō),除非類(lèi)路徑中至少有一個(gè)文件,否則語(yǔ)句(*env)->FindClass(env, "java.lang.String")返回0,甚至java.lang.String這樣定義在框架中的類(lèi)都無(wú)法訪問(wèn)。
為了測(cè)試,下面將一個(gè)apk文件push到模擬器或真機(jī)設(shè)備中。
JavaVMOption的使用如下:
你現(xiàn)在可以使用FindClass函數(shù)來(lái)加載系統(tǒng)或者app的類(lèi)。此外,如果你需要加載本地庫(kù)到你的虛擬機(jī)中,例如在靜態(tài)初始化器中加載一個(gè)庫(kù)文件,你可以使用optionString = "-Djava.library.path=/data/local/tmp"這樣的設(shè)置。這有個(gè)樣例代碼。
UniccUnlock方法
從文件UniccUnlock.cpp中,展示了另外一種創(chuàng)建虛擬機(jī)的類(lèi)似技巧。我不敢說(shuō)我完全理解了它在做什么,但是其中吸引我的是get_transaction_code部分。下面是它的做的事:
代碼看起來(lái)像是根據(jù)成員值判斷當(dāng)前設(shè)備是否已經(jīng)解鎖或者是解鎖方法是否成功。反正我是不很確定,不過(guò)我也就想抽取其中創(chuàng)建虛擬機(jī)的代碼而已。
該方法是通過(guò)在庫(kù)文件libnativehelper.so或者libdvm.so中加載創(chuàng)建虛擬機(jī)相關(guān)的方法。但是,下面幾行代碼看起來(lái)很奇怪:
任何地方都無(wú)法找到這幾個(gè)方法的文檔說(shuō)明。不過(guò),發(fā)現(xiàn)這些方法調(diào)用的人相當(dāng)聰明。如果不調(diào)用這些方法,你就會(huì)得到下面奇怪的錯(cuò)誤信息:
除了這幾個(gè)奇怪的方法,這種方式創(chuàng)建虛擬機(jī)對(duì)我很好使。但是,我想知道_ZN13JniInvocationC1Ev方法都做了什么,在不同版本間的Android系統(tǒng)中是否可移植。我的直覺(jué)告訴我,硬編碼的方法名可能會(huì)導(dǎo)致在不同的設(shè)備或者Android版本間的不兼容性。
Surfaceflinger 方法
最終,我在谷歌的Surfaceflinger服務(wù)的源碼中找到了:DdmConnection.cpp。
它默認(rèn)查找了在libdvm.so中的函數(shù)JNI_CreateJavaVM。它沒(méi)有調(diào)用方法_ZN13JniInvocation,而是調(diào)用了庫(kù)libandroid_runtime.so中的Java_com_android_internal_util_WithFramework_registerNatives方法。registerNatives方法的內(nèi)容在此描述了。
同時(shí),感興趣的是創(chuàng)建虛擬機(jī)的選項(xiàng):
這些選項(xiàng)在這篇文檔中詳細(xì)描述了。根據(jù)文檔,它僅僅用于調(diào)試JVM時(shí)使用。
同時(shí),我注意到它JNI的版本是1_4,但是我設(shè)置為1_6了,因?yàn)楣雀璧臉永a中就是這樣設(shè)置的。下面就是jni.h中支持的版本號(hào):
最后,我使用上面的方式來(lái)創(chuàng)建虛擬機(jī),因?yàn)樗鼇?lái)自谷歌,具有很好的健壯性和兼容性。
最終代碼
下面就是最終的創(chuàng)建虛擬機(jī)的代碼:
下面是其使用方法:
總結(jié)
以上是生活随笔為你收集整理的android native java_在Android Native层中创建Java虚拟机实例的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: cyclicbarrier java_J
- 下一篇: android 无法接收广播_别告诉我你