1.首先需要把assets中的test.dex复制到应用的files空间下。
string copyDexToData(JNIEnv* env, jobject asset, string dexName, string dataPath) { AAssetManager* asMg = AAssetManager_fromJava(env, asset); AAsset* as = AAssetManager_open(asMg, dexName.c_str(), AASSET_MODE_UNKNOWN); if (as == NULL) { LOGE("%s not found in assets!",dexName.c_str()); return ""; } string strDexPath = dataPath + "/files/" + dexName; int len = AAsset_getLength(as); int file = open(strDexPath.c_str(), O_WRONLY | O_CREAT, 0755); if (file < 0) { AAsset_close(as); LOGE("Open %s File Error!",strDexPath.c_str()); return ""; } char* buf = new char[1024]; while (len > 0) { memset(buf, 0, 1024); int n = AAsset_read(as, buf, 1024); if (n < 0) break; write(file, buf, n); len -= n; } delete[] buf; AAsset_close(as); close(file); return strDexPath; }
2.使用JNI方法创建出dexLoaderClass对象赋值给java的dexLoaderClass成员变量
void Java_com_example_dexload_NativeLoad_loadDex(JNIEnv* env, jobject obj, jstring dexName, jstring dataPath, jobject asset) { JavaVM* jvm; env->GetJavaVM(&jvm); JNIUtil::SetJavaVm(jvm); JNIUtil util; string strDexName = util.Jstring2String(dexName); string strdataPath = util.Jstring2String(dataPath); string strDexPath = copyDexToData(env, asset, strDexName, strdataPath); string strDestDexPath = strdataPath + "/cache"; LOGI("%s",strDexPath.c_str()); LOGI("%s",strDestDexPath.c_str()); jstring jDexPath = util.String2Jstring(strDexPath.c_str()); jstring jDestDexPath = util.String2Jstring(strDestDexPath.c_str()); //查找ClassLoader类并调用静态方法获取系统的classloader对象 jclass classloaderClass = env->FindClass("java/lang/ClassLoader"); jmethodID getsysloaderMethod = env->GetStaticMethodID(classloaderClass, "getSystemClassLoader", "()Ljava/lang/ClassLoader;"); jobject loader = env->CallStaticObjectMethod(classloaderClass, getsysloaderMethod); //查找DexClassLoader类并且创建对象生成优化后的dex jclass dexLoaderClass = env->FindClass("dalvik/system/DexClassLoader"); jmethodID initDexLoaderMethod = env->GetMethodID(dexLoaderClass, "
", "(Ljava/lang/String;Ljava/lang/String;" "Ljava/lang/String;Ljava/lang/ClassLoader;)V"); jobject dexLoader = env->NewObject(dexLoaderClass, initDexLoaderMethod, jDexPath, jDestDexPath , NULL, loader); //赋值给java端的DexClassLoader对象 jclass native = env->GetObjectClass(obj); jfieldID loadID = env->GetFieldID(native, "mDex", "Ldalvik/system/DexClassLoader;"); env->SetObjectField(obj, loadID, dexLoader); }
3.调用test.dex中的Test1类的方法test.void Java_com_example_dexload_NativeLoad_test(JNIEnv* env, jobject obj, jobject ac) { //获取java端的ClassLoader对象 jclass native = env->GetObjectClass(obj); jfieldID loadID = env->GetFieldID(native, "mDex", "Ldalvik/system/DexClassLoader;"); jobject load = env->GetObjectField(obj, loadID); //调用ClassLoader的方法loadClass加载dex中的Test1类 jclass classloaderClass = env->GetObjectClass(load); jmethodID loadClassMethod = env->GetMethodID(classloaderClass, "loadClass", "(Ljava/lang/String;)Ljava/lang/Class;"); JNIUtil util; jstring Test1ClassName = util.String2Jstring("com.example.dextest.Test1"); jclass javaClientClass = (jclass) env->CallObjectMethod(load, loadClassMethod, Test1ClassName); //创建Test1类的对象并且调用其中的test方法 jmethodID initFuncTest = env->GetMethodID(javaClientClass, "
", "()V"); //构造函数ID jobject objTest1 = env->NewObject(javaClientClass, initFuncTest); jmethodID Test_method = env->GetMethodID(javaClientClass, "test", "(Landroid/app/Activity;)V"); if (Test_method != NULL) env->CallVoidMethod(objTest1, Test_method, ac); }
4.二种方法的对比和一些加密的说明。用java方法来加载dex,使用更加简单,但是代码容易被查看,会使很多操作明面显示出来。
用JNI/C++加载dex,比使用java加载dex要麻烦很多,但是在安全性上会更高点,而且可以对assets中的dex进行加密和分块处理,
在copy到data空间的时候进行解密和重新制作出来原始的dex,加密代码都为汇编内容,想要明白具体的操作是非常困难的。
当然具体在加载了类和dex后最好直接把data空间生成的2个dex给删除掉,避免代码被别人看到,这样能更好的为apk进行加固,
防止别人对APK的反编译和破解。
二种方式的源代码链接:http://download.csdn.net/detail/csdn49532/9425977