android APK補強編-3.JNIを使用してdexと呼び出し方法をロードする


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中のTest 1類の方法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.2つの方法の比較といくつかの暗号化の説明.
Javaメソッドでdexをロードすると、より簡単に使用できますが、コードが表示されやすく、多くの操作面が表示されます.
JNI/C++でdexをロードするのはjavaでdexをロードするよりずっと面倒ですが、セキュリティ上はもっと高く、assetsのdexを暗号化したりブロック化したりすることができます.
copyがdata空間に到達したときに復号と元のdexを再作成し,暗号化コードはすべてアセンブリ内容であり,具体的な操作を理解することは困難である.
もちろん具体的にはクラスとdexをロードした後、data空間で生成された2つのdexを直接削除し、コードが他の人に見られないようにすると、apkをより強化することができます.
APKの逆コンパイルと解読を防ぐ.
ソース・リンクの2つの方法:http://download.csdn.net/detail/csdn49532/9425977