背景
最近在研究插件技術時遇到一個問題劳较,用插件技術調起應用驹止,應用里面的攝像頭無法打開浩聋,我就查看了攝像頭相關的源碼,發(fā)現(xiàn)問題出在了android.hardware.Camera類的初始化里面臊恋。具體來說衣洁,當我們初始化一個Camera類對象時,在初始化過程中調用了一個native的方法:
private void final int native_setup(Object camera_this,int cameraId, int halVersion, String packageName);
這個方法在JNI層通過Binder通信請求Server端的ICameraService去初始化攝像頭抖仅。在Server端ICameraService會檢查Client端傳過來的包名坊夫,然后去PackageManagerService那邊請求該包名對應的應用是否聲明了Camera相關的權限,如果有岸售,則打開攝像頭践樱,如果沒有在界面上就會提示去設置里面打開相應的權限。我們的問題就出在這里凸丸,當我們的插件調起初始化Camera類時拷邢,Camera類獲取的包名是插件的包名,而插件沒有安裝屎慢,所以在Server端進行權限校驗時就會失敗瞭稼,最終導致攝像頭無法打開
解決問題的思路
其實這個問題我們也好解,只要我們在宿主應用里面申請Camera相關的權限腻惠,然后Hook住Camera類里面的native_setup方法在調用該方法時傳入宿主的包名环肘,這樣權限校驗就能通過,攝像頭就能打開集灌。我們解決問題的具體的思路如下:
對于Java里面的native方法悔雹,在JNI層肯定有一個對應的函數(shù)
我們在JNI層找到保存這個函數(shù)地址的指針,先把指針保存起來
我們自己寫一個代理函數(shù)欣喧,然后替換上面的那個指針
-
在我們的代理函數(shù)里面對傳進來的參數(shù)做修改(針對我們的這個Case只需要替換包名腌零,把“com.baidu.browser.apps”替換成我們的宿主包名),然后再調用我們在上面保存的函數(shù)指針唆阿,傳進我們修改后的參數(shù)完成相應的操作
針對上面解決問題的思路益涧,我們的目標就很清晰了,首先并且最重要的是:我們得在JNI層找到android.hardware.Camera.native_setup的對應的函數(shù)驯鳖。
JNI中如何去調用Java方法
// 這是調用Java中的static方法
jmethodID method = env->GetStaticMethodID(g_jclass, JAVA_CALLBACK__ON_KILL_PROCESS, JAVA_CALLBACK__ON_KILL_PROCESS_SIGNATURE);
env->CallStaticVoidMethod(g_jclass, method, pid, sig);
// 這是調用Java中的非static方法
jmethodID method = env->GetMethodID(g_jclass, JAVA_CALLBACK__ON_KILL_PROCESS, JAVA_CALLBACK__ON_KILL_PROCESS_SIGNATURE);
env->CallVoidMethod(g_jclass, method, pid, sig);
從上面的代碼我們看到闲询,不管是調用靜態(tài)方法還是非靜態(tài)方法,我們都得找到代表這個方法的jmethodID浅辙,然后通過這個jmethodID去調用JNIEnv的相應的CallXXXXMethod;
jmethodID到底是什么呢扭弧?從jni.h文件里面可以看到下面這段定義:
struct _jmethodID; /* opaque structure */
typedef struct _jmethodID* jmethodID; /* method IDs */
其實可以看到jmethodID其實就是一個結構體指針,這個結構體叫__jmethodID是如何定義的呢摔握?其定義如下:
struct Method {
ClassObject* clazz; // 代表這個方法所屬的類
u4 accessFlags; // 訪問標識符寄狼, 定義是public? native氨淌?synchronized泊愧?
u2 methodIndex; // index,在ClassObject中一個保存Method對象的數(shù)組的位置盛正。
u2 registersSize; // 僅對native方法有用删咱,保存方法的參數(shù)的個數(shù)。
u2 outsSize; // unknown豪筝, 不重要
u2 insSize; // unknown痰滋, 不重要
const char* name; // 方法名
DexProto prototype; // 我們可以理解這是一個代表該方法簽名的字符串(包含返回值以及參數(shù)類型)
const char* shorty; // 跟上面的意義,方法的簽名续崖,like:(ILjava/lang/String)V
const u2* insns; // 方法的指令代碼
int jniArgInfo; // jni參數(shù)信息敲街,好像沒啥用
DalvikBridgeFunc nativeFunc; // native函數(shù)指針,可能是真正的native函數(shù)严望,也可能是JNI橋接函數(shù)
bool fastJni; // unknown多艇, 不重要
bool noRef; // unknown, 不重要
bool shouldTrace; // unknown像吻, 不重要
const RegisterMap* registerMap; // unknown峻黍, 不重要
bool inProfile; // unknown, 不重要
};
針對上面的這些參數(shù)拨匆,我們只挑幾個重要的下面可能會用到的說一下:
-
accessFlags: 在Dalvik虛擬機中定義了一系列的值用來指示這個方法是public的還是private的姆涩,是static的還是非static的,是native 的還是非native的惭每,有沒有final骨饿、synchronized、absttact等等關鍵字台腥。 我們可以修改這個值來修改一個方法的屬性宏赘,比如說下面這段代碼,可以講一個非native的方法修改成native的方法:
public static final int ACC_NATIVE = 0x0100; (method->accessFlags & ACC_NATIVE) != 0
registersSize:這個保存方法的參數(shù)個數(shù)
-
nativeFuc:這個最重要览爵,它是一個函數(shù)指針置鼻,這個指針可能指向該方法對應的native函數(shù),也可能指向一個橋接函數(shù)蜓竹。簡單的說就是箕母,在Dalvik虛擬機下面,這個指向的是橋接函數(shù)俱济;在Art虛擬機下嘶是,指向的是native函數(shù)。至于這個橋接函數(shù)蛛碌,它的聲明如下:
typedef void (* DalvikBridgeFunc)(const u4* args, JValue* pResult, const Method* method, struct Thread* self);
這個橋接函數(shù)的參數(shù)我解釋一下:
args: 存儲的是調研native方法傳過來的參數(shù)聂喇,對于非static方法,args[0]存儲的是this對象,args[1]存儲的是第一個參數(shù)值希太,args[2]存儲的是第二個參數(shù)值克饶,以此類推。對于static對象誊辉,args[0]存儲的是第一個參數(shù)值矾湃,args[1]存儲的是第二個參數(shù)值, 舉個例子,比如說對于我自己定一個一個方法getSubString(String aString, int aStart, int aLength), 這是一個非static方法堕澄。 我現(xiàn)在調用該方法邀跃,getString("This is a sentence", 5, 6); 當方法走到我們的橋接函數(shù)中時,我們可以通過args[1]獲取到字符串"This is a sentence", 通過args[2]獲取到5蛙紫,通過args[3]獲取到6拍屑。
pResult: 存儲函數(shù)的返回值
method: 存儲與該橋接函數(shù)對應的method對象。
self: 抱歉坑傅,這個我也沒搞明白僵驰。-_-
對于native函數(shù),這個就簡單了裁蚁,看下面的代碼就一目了然了:
static JNINativeMethod gMethods[] = {
{"hookNativeMethod", "(Ljava/lang/Object;Ljava/lang/String;Z)Z", (void *) native_hook}
};
void native_hook(JNIEnv *env, jclass clazz, jobject method, jstring pkg,
jboolean isArt)
env->RegisterNatives(javaClass, gMethods, 1)
上面的"hookNativeMethod"對應的是java里面的聲明為native的方法矢渊, native_hook代表的是native函數(shù),我們通過JNIEnv 的RegisterNatives將這他們關聯(lián)起來枉证。 java層調用hookNativeMethod最終回轉化成native層調用native_hook函數(shù)矮男。在Art虛擬機下,Method結構體中存儲的nativeFuc指針指向的就是這個native_hook函數(shù)室谚。
上面已經(jīng)解釋清楚jmethodID代表啥之后毡鉴,下面我們就來著手Hook住Camara的native_setup方法:我們分步來實現(xiàn)Hook:
第一步: 獲取native_setup方法的jmethodID值:
我們直接看代碼:
private static native boolean hookNativeMethod(Object method, String packageName);
public static void fixCamera() {
if (!sNativeFixed) {
try {
Method native_setup;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
native_setup = Reflect.on(Camera.class).exactMethod("native_setup",
new Class[]{Object.class, int.class, int.class, String.class});
} else {
native_setup = Reflect.on(Camera.class).exactMethod("native_setup",
new Class[]{Object.class, int.class, String.class});
}
hookNativeMethod(native_setup, VirtualCore.getCore().getHostPkg());
} catch (NoSuchMethodException e) {
e.printStackTrace();
}
sNativeFixed = true;
}
}
這是Java層代碼,我們通過反射獲取到Camera類里面native_setup方法對應的Method對象(注意秒赤,這是Java里面的Method類猪瞬,不是我們上面說的Method結構體);然后我們通過一個native方法hookNativeMethod將這個Method傳到JNI層入篮。
下面是JNI層的代碼:
jmethodID mtd_openDexNative = env->FromReflectedMethod(javaMethod);
在JNI層陈瘦,我們調用JNIEnv的FromReflectedMethod方法,傳入java層傳過來的代表Method對象的jobject對象潮售,得到了native_setup在native層的jmethodID對象痊项。
第二步:我們找到jmethodID之后,接下來要找到這個結構體里面的nativeFuc指針
正如我們上面所說酥诽,這個jmethodID其實就是上面的我們解釋的Method結構體了鞍泉,我們現(xiàn)在就是要找到這個結構體里面的nativeFunc指針,然后把這個指針改成我們自己定義的一個函數(shù)肮帐,這樣就完成了Hook咖驮。那么我們怎么去找到這個nativeFuc指針呢,一種比較簡單的方法就是將jmethodID強制轉換成結構題Method的指針,但是我們目前沒這么做托修,原因很簡單忘巧,上面的Method結構體里面還包含了另外一些結構題,如DexProto诀黍、RegisterMap等等袋坑,這兩個結構題里面可能還包含了其他的結構體仗处,我要么得把這些結構體都拷過來眯勾,要么我就得引用定義這些結構體的頭文件,這個比較麻煩婆誓。我們換用另一種方法:
我們在java類里面定義一個mark()的native方法吃环,在JNI里面也定義一個nativeMark()函數(shù),然后通過JNIEnv的RegisterNatives函數(shù)將他們兩個關聯(lián)起來洋幻。然后我們通過JNIEnv的GetMethodId函數(shù)獲取到mark方法的jmethodID郁轻,jmethodID是Method結構體的指針(其實就是這個結構體的內存起始地址),我們知道這個指針之后文留,下面要分兩種情況來分析了:
Art虛擬機下
我們上面說了Art虛擬機下面結構體Method里面的nativeFunc其實指向的就是native函數(shù)好唯,在我們這個案例中指向的就是nativeMark函數(shù)。那下面就好辦了:我們用一個while循環(huán)燥翅,從jmethodID所代表的內存地址開始骑篙,4個字節(jié)4個字節(jié)的往后尋址,然后通過“*”取出所指地址中存儲的值森书,用這個值跟nativeHook指針比較靶端,當相等時,我們就找到了結構體Method里面的nativeFuc的偏移量凛膏。Dalvik虛擬機下
還是上面說的杨名,在Dalvik虛擬機下結構體Method里面的nativeFunc指向的是一個橋接函數(shù),那么我們如何去找到保存這個橋接函數(shù)的指針呢猖毫?可以通過下面這段代碼找到:
char vmSoName[15] = {0};
__system_property_get("persist.sys.dalvik.vm.lib", vmSoName);
LOGD("Find the so name : %s.", strlen(vmSoName) == 0 ? "<EMPTY>" : vmSoName);
void *vmHandle = dlopen(vmSoName, 0);
if (!vmHandle) {
LOGE("Unable to open the %s.", vmSoName);
vmHandle = RTLD_DEFAULT;
}
gOffset.art_work_around_app_jni_bugs = dlsym(vmHandle, "art_work_around_app_jni_bugs");
上面這段代碼可以找到橋接函數(shù)的指針台谍,同樣,我們從jmethodID代表的內存地址開始吁断,4個字節(jié)4個字節(jié)的往后尋址趁蕊,然后通過“*”取出所指地址中存儲的值,用這個值跟我們剛剛找到的橋接函數(shù)的指針比較胯府,當相等時介衔,我們就找到了結構體Method里面的nativeFuc的偏移量。
下面這段代碼就是尋址和比較的代碼:
void mark() {
// Do nothing
};
void searchJniOffset(JNIEnv *env, bool isArt) {
jclass g_class = env->FindClass(JAVA_CLASS);
jmethodID mtd_nativeHook = env->GetStaticMethodID(g_class, "mark", "()V");
size_t startAddress = (size_t) mtd_nativeHook;
size_t targetAddress = (size_t) nativeMark;
if (isArt && gOffset.art_work_around_app_jni_bugs) {
targetAddress = (size_t) gOffset.art_work_around_app_jni_bugs;
}
int offset = 0;
bool found = false;
while (true) {
if (*((size_t *) (startAddress + offset)) == targetAddress) {
found = true;
break;
}
offset += 4;
if (offset >= 100) {
LOGE("Ops: Unable to find the jni function.");
break;
}
}
if (found) {
gOffset.nativeOffset = offset;
if (!isArt) {
gOffset.nativeOffset += (sizeof(int) + sizeof(void *));
}
LOGD("Hoho, Get the offset : %d.", gOffset.nativeOffset);
}
}
上面我們找到nativeFunc偏移量之后,我們前面又找到了Camera類的native_setup對應的jmethodID骂因,jmethodID加上這個偏移量就是native_setup對應的Method結構體里的nativeFuc地址炎咖,這個地址里面存儲了一個C函數(shù)指針,在Dalvik虛擬機下,這個是橋接函數(shù)的指針乘盼,在Art虛擬機下這個指針就是native函數(shù)的指針升熊。
第三步:找到C函數(shù)指針之后,把指針替換掉绸栅,換成我們定義的Hook函數(shù)的函數(shù)指針
這一步就很簡單了级野,直接看代碼:
inline void replaceImplementation(JNIEnv *env, jobject javaMethod, jboolean isArt) {
size_t mtd_openDexNative = (size_t) env->FromReflectedMethod(javaMethod);
int nativeFuncOffset = gOffset.nativeOffset;
void **jniFuncPtr = (void **) (mtd_openDexNative + nativeFuncOffset);
if (!isArt) {
LOGD("The vm is dalvik");
gOffset.orig_DalvikBridgeFunc = (Bridge_DalvikBridgeFunc) (*jniFuncPtr);
*jniFuncPtr = (void *) new_bridge_nativeSetUpFunc;
} else {
char vmSoName[4] = {0};
__system_property_get("ro.build.version.sdk", vmSoName);
int sdk;
sscanf(vmSoName, "%d", &sdk);
LOGD("The vm is art and the sdk int is %d", sdk);
if (sdk < 21) {
gOffset.orig_native_openDexNativeDalvikFunc = (Native_nativeSetUpDalvikFunc) (*jniFuncPtr);
*jniFuncPtr = (void *) new_nativeSetUpDalvikFunc;
} else {
gOffset.orig_native_openDexNativeFunc = (Native_nativeSetUpFunc) (*jniFuncPtr);
*jniFuncPtr = (void *) new_nativeSetUpFunc;
}
}
}
這里我們解釋一下,我們區(qū)分了Dalvik虛擬機和Art虛擬機兩種情形粹胯,Art虛擬機下直接替換的就是native函數(shù)蓖柔,而Dalvik虛擬機下我們替換的是橋接函數(shù)。其實更通俗的講就是风纠,Art虛擬機下面我們Hook的是真正的native函數(shù)况鸣,在Dalvik虛擬機下面我們要Hook的是橋接函數(shù)。
第四步: 定義Hook函數(shù)竹观,在第三步中會用我們定義的Hook函數(shù)替換掉原來的函數(shù)
直接看代碼镐捧,分Dalvik虛擬機和Art虛擬機:
// 這個函數(shù)是Art虛擬機下的hook函數(shù)
static jobject new_nativeSetUpArtFunc(JNIEnv *env, jclass jclazz,
jobject weak_this, jint halVersion,
jstring clientPackageName) {
jstring newPkg = env->NewStringUTF(newPkgName);
return gOffset.orig_native_openArtNativeDalvikFunc(env, jclazz, weak_this, halVersion, newPkg);
}
// 這個是Dalvik虛擬機下面的hook函數(shù),用該函數(shù)去Hook橋接函數(shù)
static void new_bridge_nativeSetUpFunc(const void **args, void *pResult, const void *method, void *self) {
JNIEnv *env = NULL;
g_vm->GetEnv((void **) &env, JNI_VERSION_1_6);
g_vm->AttachCurrentThread(&env, NULL);
typedef char* (*GetCstrFromString)(void *);
typedef void* (*GetStringFromCstr)(const char*);
const char* origPkg3 = args[3] == NULL ? NULL : gOffset.GetCstrFromString((void*) args[3]);
LOGD("The original package3 is: %s", origPkg3);
args[3] = gOffset.GetStringFromCstr(newPkgName);
gOffset.orig_DalvikBridgeFunc(args, pResult, method, self);
}
至此臭增,我們分四步就已經(jīng)完成了Camera類的native_setup的Hook懂酱。總結起來就是:
- 找到Camera.native_setup方法在jni層的jmethodID
- 在jmethodID中找到nativeFuc誊抛,將這個值改成我們自己定義的Hook函數(shù)的地址
- 自己寫Hook函數(shù)列牺,要分Dalvik虛擬機和Art虛擬機
進階(如果你想,你就能)
其實上面的代碼可以實現(xiàn)所有Java方法的Hook芍锚;
對于native的方法上面的代碼稍微把方法名昔园,方法簽名修改一下,實現(xiàn)一下對應的Hook函數(shù)就可以了并炮。
對于非native的方法默刚,我們可以把Method結構體中的accessFlags改一下,就可以把它變成native方法逃魄,然后將nativeFuc賦值成我們寫的Hook函數(shù)荤西,然后再改一下registersSize和insSize的值,這就是保存參數(shù)個數(shù)的地方伍俘,最后再該一下jniArgsInfo值邪锌,具體的改法可以參照git上的代碼AllHookInOne
下面是我寫的完整版的代碼
這是Java層的代碼
<!-- lang:java -->
public class CameraFixer {
private static native boolean hookNativeMethod(
Object method, String packageName, boolean isArt);
private static native void hook();
}
這是C代碼:
camera_hook.h
#ifndef VIRTUALAPP_CAMERA_HOOK_H
#define VIRTUALAPP_CAMERA_HOOK_H
#include <jni.h>
#include <stdlib.h>
#include <android/log.h>
#include <dlfcn.h>
#include <stddef.h>
#include <fcntl.h>
#include <sys/system_properties.h>
#include <stdio.h>
#define TAG "HOOKCAMERA"
#define JAVA_CLASS "com/baidu/freeinstall/client/ContextFixer"
#define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG, TAG, __VA_ARGS__)
#define LOGI(...) __android_log_print(ANDROID_LOG_INFO, TAG, __VA_ARGS__)
#define LOGW(...) __android_log_print(ANDROID_LOG_WARN, TAG, __VA_ARGS__)
#define LOGE(...) __android_log_print(ANDROID_LOG_ERROR, TAG, __VA_ARGS__)
__BEGIN_DECLS
JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM *vm, void *reserved);
JNIEXPORT void JNICALL JNI_OnUnload(JavaVM* vm, void* reserved);
__END_DECLS
#endif //VIRTUALAPP_CAMERA_HOOK_H
camera_hook.c
#include "camera_hook.h"
JavaVM *g_vm;
typedef void (*Bridge_DalvikBridgeFunc)(const void **, void *, const void *, void *);
typedef jobject (*Native_nativeSetUpFunc)(JNIEnv *, jclass, jobject weak_this, jint cameraId, jint halVersion, jstring clientPackageName);
typedef jobject (*Native_nativeSetUpDalvikFunc)(JNIEnv *, jclass,
jobject weak_this, jint halVersion, jstring clientPackageName);
void mark() {
// Do nothing
};
static struct {
bool isArt;
int nativeOffset;
void *art_work_around_app_jni_bugs;
char *(*GetCstrFromString)(void *);
void *(*GetStringFromCstr)(const char *);
Bridge_DalvikBridgeFunc orig_DalvikBridgeFunc;
Native_nativeSetUpFunc orig_native_openDexNativeFunc;
Native_nativeSetUpDalvikFunc orig_native_openDexNativeDalvikFunc;
} gOffset;
char *newPkgName;
static jobject new_nativeSetUpFunc(JNIEnv *env, jclass jclazz,
jobject weak_this, jint cameraId, jint halVersion, jstring clientPackageName) {
jstring newPkg = env->NewStringUTF(newPkgName);
return gOffset.orig_native_openDexNativeFunc(env, jclazz, weak_this, cameraId, halVersion, newPkg);
}
static jobject new_nativeSetUpDalvikFunc(JNIEnv *env, jclass jclazz,
jobject weak_this, jint halVersion, jstring clientPackageName) {
jstring newPkg = env->NewStringUTF(newPkgName);
return gOffset.orig_native_openDexNativeDalvikFunc(env, jclazz, weak_this, halVersion, newPkg);
}
static void new_bridge_nativeSetUpFunc(const void **args, void *pResult, const void *method, void *self) {
JNIEnv *env = NULL;
g_vm->GetEnv((void **) &env, JNI_VERSION_1_6);
g_vm->AttachCurrentThread(&env, NULL);
typedef char* (*GetCstrFromString)(void *);
typedef void* (*GetStringFromCstr)(const char*);
const char* origPkg0 = args[0] == NULL ? NULL : gOffset.GetCstrFromString((void*) args[0]);
LOGD("The original package0 is: %s", origPkg0);
const char* origPkg = args[2] == NULL ? NULL : gOffset.GetCstrFromString((void*) args[2]);
LOGD("The original package2 is: %s", origPkg);
const char* origPkg3 = args[3] == NULL ? NULL : gOffset.GetCstrFromString((void*) args[3]);
LOGD("The original package3 is: %s", origPkg3);
args[3] = gOffset.GetStringFromCstr(newPkgName);
gOffset.orig_DalvikBridgeFunc(args, pResult, method, self);
}
void searchJniOffset(JNIEnv *env, bool isArt) {
jclass g_class = env->FindClass(JAVA_CLASS);
jmethodID mtd_nativeHook = env->GetStaticMethodID(g_class, "nativeMark", "()V");
size_t startAddress = (size_t) mtd_nativeHook;
size_t targetAddress = (size_t) mark;
if (isArt && gOffset.art_work_around_app_jni_bugs) {
targetAddress = (size_t) gOffset.art_work_around_app_jni_bugs;
}
int offset = 0;
bool found = false;
while (true) {
if (*((size_t *) (startAddress + offset)) == targetAddress) {
found = true;
break;
}
offset += 4;
if (offset >= 100) {
LOGE("Ops: Unable to find the jni function.");
break;
}
}
if (found) {
gOffset.nativeOffset = offset;
if (!isArt) {
gOffset.nativeOffset += (sizeof(int) + sizeof(void *));
}
LOGD("Hoho, Get the offset : %d.", gOffset.nativeOffset);
}
}
inline void replaceImplementation(JNIEnv *env, jobject javaMethod, jboolean isArt) {
size_t mtd_openDexNative = (size_t) env->FromReflectedMethod(javaMethod);
int nativeFuncOffset = gOffset.nativeOffset;
void **jniFuncPtr = (void **) (mtd_openDexNative + nativeFuncOffset);
if (!isArt) {
LOGD("The vm is dalvik");
gOffset.orig_DalvikBridgeFunc = (Bridge_DalvikBridgeFunc) (*jniFuncPtr);
*jniFuncPtr = (void *) new_bridge_nativeSetUpFunc;
} else {
char vmSoName[4] = {0};
__system_property_get("ro.build.version.sdk", vmSoName);
int sdk;
sscanf(vmSoName, "%d", &sdk);
LOGD("The vm is art and the sdk int is %d", sdk);
if (sdk < 21) {
gOffset.orig_native_openDexNativeDalvikFunc = (Native_nativeSetUpDalvikFunc) (*jniFuncPtr);
*jniFuncPtr = (void *) new_nativeSetUpDalvikFunc;
} else {
gOffset.orig_native_openDexNativeFunc = (Native_nativeSetUpFunc) (*jniFuncPtr);
*jniFuncPtr = (void *) new_nativeSetUpFunc;
}
}
}
static JNINativeMethod gMarkMethods[] = {
{"nativeMark", "()V", (void *) mark}
};
void native_hook(JNIEnv *env, jclass clazz, jobject method, jstring pkg, jboolean isArt) {
newPkgName = (char *) env->GetStringUTFChars(pkg, NULL);
g_vm->GetEnv((void **) &env, JNI_VERSION_1_6);
g_vm->AttachCurrentThread(&env, NULL);
jclass g_class = env->FindClass(JAVA_CLASS);
if (env->RegisterNatives(g_class, gMarkMethods, 1) < 0) {
return;
}
gOffset.isArt = isArt;
char vmSoName[15] = {0};
__system_property_get("persist.sys.dalvik.vm.lib", vmSoName);
LOGD("Find the so name : %s.", strlen(vmSoName) == 0 ? "<EMPTY>" : vmSoName);
void *vmHandle = dlopen(vmSoName, 0);
if (!vmHandle) {
LOGE("Unable to open the %s.", vmSoName);
vmHandle = RTLD_DEFAULT;
}
if (isArt) {
gOffset.art_work_around_app_jni_bugs = dlsym(vmHandle, "art_work_around_app_jni_bugs");
} else {
gOffset.GetCstrFromString = (char *(*)(void *)) dlsym(vmHandle, "_Z23dvmCreateCstrFromStringPK12StringObject");
if (!gOffset.GetCstrFromString) {
gOffset.GetCstrFromString = (char *(*)(void *)) dlsym(vmHandle,
"dvmCreateCstrFromString");
}
gOffset.GetStringFromCstr = (void *(*)(const char *)) dlsym(vmHandle,
"_Z23dvmCreateStringFromCstrPKc");
if (!gOffset.GetStringFromCstr) {
gOffset.GetStringFromCstr = (void *(*)(const char *)) dlsym(vmHandle, "dvmCreateStringFromCstr");
}
}
searchJniOffset(env, isArt);
replaceImplementation(env, method, isArt);
}
static JNINativeMethod gMethods[] = {
{ "hookNativeMethod", "(Ljava/lang/Object;Ljava/lang/String;Z)Z",
(void *) native_hook }
};
JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM *vm, void *reserved) {
g_vm = vm;
JNIEnv *env;
LOGE("JNI_Onload start");
if (vm->GetEnv((void **) &env, JNI_VERSION_1_6) != JNI_OK) {
LOGE("GetEnv() FAILED!!!");
return JNI_ERR;
}
jclass javaClass = env->FindClass(JAVA_CLASS);
LOGE("we have found the class: %s", JAVA_CLASS);
if (javaClass == NULL) {
LOGE("unable to find class: %s", JAVA_CLASS);
return JNI_ERR;
}
env->UnregisterNatives(javaClass);
if (env->RegisterNatives(javaClass, gMethods, 1) < 0) {
LOGE("register methods FAILED!!!");
return JNI_ERR;
}
env->DeleteLocalRef(javaClass);
LOGI("JavaVM::GetEnv() SUCCESS!");
return JNI_VERSION_1_6;
}