#include <jni.h>
#include <android/log.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdlib.h>
#include "mycommom.h"
#include <sys/stat.h>
#include <sys/types.h>
#include <android/asset_manager.h>
#include <android/asset_manager_jni.h>
#include <dlfcn.h>
#include <sys/mman.h>
void native_attachContextBaseContext(JNIEnv *env, jclass clazz,jobject ctx);
void native_onCreate(JNIEnv *env, jclass clazz);
JNIEXPORT jint JNI_OnLoad(JavaVM* vm, void* reserved);
#define TAG "info"
#define LOGI(...) __android_log_print(ANDROID_LOG_INFO,TAG,__VA_ARGS__)
#define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG, TAG, __VA_ARGS__)
#define LOGE(...) __android_log_print(ANDROID_LOG_ERROR,TAG,__VA_ARGS__)
# define NELEM(x) ((int) (sizeof(x) / sizeof((x)[0])))
#define JNIREG_CLASS "com/example/jnishell3/StubApplication"
//#define JNIREG_CLASS "com/ali/mobisecenhance/StubApplication"
jclass g_jclass_Build_version;
jint g_jint_sdk_int;
jclass g_jclass_ActivityThread;
jfieldID g_jfieldID_ActivityThread_mPackages;
jclass g_jclass_ArrayMap;
jmethodID g_jmethodID_ArrayMap_get;
jfieldID g_jfieldID_ActivityThread_mBoundApplication;
jfieldID g_jfieldID_ActivityThread_mInitialApplication;
jfieldID g_jfieldID_ActivityThread_mAllApplications;
jmethodID g_jmethodID_ActivityThread_currentActivityThread;
jclass g_jclass_AppBindData;
jfieldID g_jfieldID_AppBindData_info;
jclass g_jclass_ArrayList;
jmethodID g_jmethodID_ArrayList_size;
jmethodID g_jmethodID_ArrayList_get;
jmethodID g_jmethodID_ArrayList_set;
jclass g_jclass_Context;
jmethodID g_jmethodID_Context_getPackageName ;
jmethodID g_jmethodID_Context_getApplicationInfo;
jmethodID g_jmethodID_Context_getClassLoader;
jmethodID g_jmethodID_Context_getAssets;
jmethodID g_jmethodID_Context_getPackageResourePath;
jclass g_jclass_WeakReference;
jmethodID g_jmethodID_WeakReference_get;
jclass g_jclass_LoadedApk;
jfieldID g_jfieldID_LoadedApk_mClassLoader;
jfieldID g_jfieldID_LoadedApk_mApplication ;
jclass g_jclass_ApplicationInfo;
jfieldID g_jfieldID_ApplicationInfo_dataDir;
jfieldID g_jfieldID_ApplicationInfo_nativeLibraryDir;
jfieldID g_jfieldID_ApplicationInfo_sourceDir ;
jclass g_jclass_Application;
jmethodID g_jmethodID_Application_onCreate;
jmethodID g_jmethodID_Application_attach;
jclass g_jclass_ContextWrapper;
jmethodID g_jmethodID_ContextWrapper_attachBaseContext ;
jclass g_jclass_PathClassLoader;
jclass g_jclass_BaseDexClassLoader;
jfieldID g_jfieldID_BaseDexClassLoader_pathList ;
jclass g_jclass_DexPathList;
jfieldID g_jfieldID_DexPathList_dexElements;
jclass g_jclass_DexPathList_Element;
jfieldID g_jfieldID_DexPathList_Element_dexFile;
jfieldID g_jfieldID_DexPathList_Element_file;
jclass g_jclass_File;
jmethodID g_jmethodID_File_getAbsolutePath;
jclass g_jclass_DexFile;
jfieldID g_jfieldID_DexFile_mCookie;
jmethodID g_jmethodID_DexFile_OpenDexFile;
jclass g_jclass_ClassLoader;
jmethodID g_jmethodID_ClassLoader_loadClass;
jclass g_jclass_System;
jmethodID g_jmethodID_System_getProperty;
jclass g_jclass_status;
jmethodID g_jmethodID_SystemProperties_get;
jclass g_jclass_SystemProperties;
int g_nIsDalvik;
int g_nIsArt;
jobject g_jobject_newApplication;
const char* g_lpszPackageName;
const char* g_lpszabsolutePath;
JNINativeMethod *g_dvm_dalvik_system_DexFile;
void (*openDexFile)(const u4* args,union JValue* pResult);
typedef int (*pArtFun)(int,int );
int g_n_dvm_Cookie=0;//4.4
jlong g_jlong_art_Cookie=0;//5.0-5.1
jobject g_jobject_art_MarCookie=0;//6.0
// 获取指定名称类的全局引用
jclass myFindClass(JNIEnv *env,jclass* out_jclazz,char* lpszClassName)
{
jobject globalRef_jclazz;
jclass clazz = (*env)->FindClass(env,lpszClassName);
if(clazz)
{
获取类的全局引用(class)
globalRef_jclazz = (*env)->NewGlobalRef(env,clazz);
//将全局引用(class)存到ptr中
*out_jclazz=globalRef_jclazz;
return globalRef_jclazz;
}
else
{
return 0;
}
}
void init_class(JNIEnv *env, jobject obj,jobject ctx)
{
// 获取类android.os.Build.VERSION的全局引用(class)
if( !myFindClass(env,&g_jclass_Build_version,"android/os/Build$VERSION"))
{
// LOGI("ERROR:Build$VERSION");
return;
}
//通过Build_VERSION获取当前SDK版本的ID,并通过ID调用获取当前的SDK版本
jfieldID jfieldID_VERSION_SDK_INT= ((*env)->GetStaticFieldID)(env, g_jclass_Build_version, "SDK_INT", "I");
g_jint_sdk_int=(*env)->GetStaticIntField(env,g_jclass_Build_version,jfieldID_VERSION_SDK_INT);
//LOGI("sdk_int %d\n",sdk_int);
// 获取类android.app.ActivityThread的全局引用
if( !myFindClass(env, &g_jclass_ActivityThread, "android/app/ActivityThread"))
{
//LOGI("ERROR:ActivityThread");
return;
}
//判断sdk版本是否>18,似乎必须大于18才行
if(g_jint_sdk_int>18)
{
//获取类android.app.ActivityThread的非静态成员变量mPackages的ID
g_jfieldID_ActivityThread_mPackages=(*env)->GetFieldID(env, g_jclass_ActivityThread, "mPackages", "Landroid/util/ArrayMap;");
// 获取类android.util.ArrayMap的全局引用(class)
if ( !myFindClass(env, &g_jclass_ArrayMap, "android/util/ArrayMap") )
{
//LOGI("ERROR:myArrayMap");
return;
}
//通过myArrayMap获取其get方法的ID
g_jmethodID_ArrayMap_get=(*env)->GetMethodID(env,g_jclass_ArrayMap,"get","(Ljava/lang/Object;)Ljava/lang/Object;");
//通过ActivityThread获取mBoundApplication的ID
g_jfieldID_ActivityThread_mBoundApplication = (*env)->GetFieldID(env,
g_jclass_ActivityThread,
"mBoundApplication",
"Landroid/app/ActivityThread$AppBindData;");
//通过ActivityThread获取mInitialApplication的ID
g_jfieldID_ActivityThread_mInitialApplication = (*env)->GetFieldID(
env,
g_jclass_ActivityThread,
"mInitialApplication",
"Landroid/app/Application;");
//通过ActivityThread获取mAllApplications的ID
g_jfieldID_ActivityThread_mAllApplications = (*env)->GetFieldID(env,
g_jclass_ActivityThread,
"mAllApplications",
"Ljava/util/ArrayList;");
//通过ActivityThread获取currentActivityThread的ID
g_jmethodID_ActivityThread_currentActivityThread = (*env)->GetStaticMethodID(
env,
g_jclass_ActivityThread,
"currentActivityThread",
"()Landroid/app/ActivityThread;");
//LOGI("ActivityThread:%p,%p,%p,%p",mBoundApplication,mInitialApplication,mAllApplications,currentActivityThread);
// 获取类android.app.ActivityThread$AppBindData的全局引用(内部class)
if ( !myFindClass(env, &g_jclass_AppBindData, "android/app/ActivityThread$AppBindData") )
{
//LOGI("ERROR:AppBindData");
return;
}
//通过AppBindData获取info的ID
g_jfieldID_AppBindData_info=(*env)->GetFieldID(env, g_jclass_AppBindData, "info", "Landroid/app/LoadedApk;");
// 获取类java.util.ArrayList的全局引用(class)
if ( !myFindClass(env, &g_jclass_ArrayList, "java/util/ArrayList") )
{
//LOGI("ERROR:myArrayList");
return;
}
//通过ArrayList获取ArrayList的size、get、set方法的ID
g_jmethodID_ArrayList_size = (*env)->GetMethodID(env, g_jclass_ArrayList, "size", "()I");
g_jmethodID_ArrayList_get = (*env)->GetMethodID(env, g_jclass_ArrayList, "get", "(I)Ljava/lang/Object;");
g_jmethodID_ArrayList_set = (*env)->GetMethodID(env, g_jclass_ArrayList, "set", "(ILjava/lang/Object;)Ljava/lang/Object;");
// 获取类android.content.Context的全局引用(class)
if ( !myFindClass(env, &g_jclass_Context, "android/content/Context") )
{
//LOGI("ERROR:myContext");
return;
}
//通过Context获取Context的getPackName、getApplicationInfo、getClassLoader、getAssets、getPackageResourePath方法的ID
g_jmethodID_Context_getPackageName = (*env)->GetMethodID(env, g_jclass_Context, "getPackageName", "()Ljava/lang/String;");
g_jmethodID_Context_getApplicationInfo = (*env)->GetMethodID(
env,
g_jclass_Context,
"getApplicationInfo",
"()Landroid/content/pm/ApplicationInfo;");
g_jmethodID_Context_getClassLoader = (*env)->GetMethodID(
env,
g_jclass_Context,
"getClassLoader",
"()Ljava/lang/ClassLoader;");
g_jmethodID_Context_getAssets = (*env)->GetMethodID(
env,
g_jclass_Context,
"getAssets",
"()Landroid/content/res/AssetManager;");
g_jmethodID_Context_getPackageResourePath = (*env)->GetMethodID(
env,
g_jclass_Context,
"getPackageResourcePath",
"()Ljava/lang/String;");
// 获取类java.lang.rel.WeakReference的全局引用(class)
if ( !myFindClass(env, &g_jclass_WeakReference, "java/lang/ref/WeakReference") )
{
//LOGI("ERROR:myWeakReference");
return;
}
//通过WeakReference获取其的get方法的ID
g_jmethodID_WeakReference_get = (*env)->GetMethodID(env, g_jclass_WeakReference, "get", "()Ljava/lang/Object;");
// 获取类android.app.LoadedApk的全局引用(class)
if ( !myFindClass(env, &g_jclass_LoadedApk, "android/app/LoadedApk") )
{
//LOGI("ERROR:myLoadedApk");
return;
}
//通过LoadedApk获取其的mClassLoader、mApplication的ID
g_jfieldID_LoadedApk_mClassLoader = (*env)->GetFieldID(
env,
g_jclass_LoadedApk,
"mClassLoader",
"Ljava/lang/ClassLoader;");
g_jfieldID_LoadedApk_mApplication = (*env)->GetFieldID(
env,
g_jclass_LoadedApk,
"mApplication",
"Landroid/app/Application;");
// 获取类android.content.pm.ApplicationInfo的全局引用(class)
if ( !myFindClass(env, &g_jclass_ApplicationInfo, "android/content/pm/ApplicationInfo") )
{
//LOGI("ERROR:myApplicationInfo");
return;
}
//通过ApplicationInfo获取其dataDir、nativeLibraryDir、sourceDir的ID
g_jfieldID_ApplicationInfo_dataDir = (*env)->GetFieldID(
env,
g_jclass_ApplicationInfo,
"dataDir",
"Ljava/lang/String;");
g_jfieldID_ApplicationInfo_nativeLibraryDir =(*env)->GetFieldID(
env,
g_jclass_ApplicationInfo,
"nativeLibraryDir",
"Ljava/lang/String;");
g_jfieldID_ApplicationInfo_sourceDir = (*env)->GetFieldID(
env,
g_jclass_ApplicationInfo,
"sourceDir",
"Ljava/lang/String;");
// 获取类android.app.Application的全局引用(class)
if ( !myFindClass(env, &g_jclass_Application, "android/app/Application") )
{
//LOGI("ERROR:myApplication");
return;
}
//通过Application类获取其onCreate、attach方法的ID
g_jmethodID_Application_onCreate = (*env)->GetMethodID(env, g_jclass_Application, "onCreate", "()V");
g_jmethodID_Application_attach = (*env)->GetMethodID(
env,
g_jclass_Application,
"attach",
"(Landroid/content/Context;)V");
/*
StubApplication-->atachContextBaseContext()-->super.attachBaseContext
*/
// 获取类android.content.ContextWrapper的全局引用(class)
if ( !myFindClass(env, &g_jclass_ContextWrapper, "android/content/ContextWrapper") )
{
//LOGI("ERROR:myContextWrapper");
return;
}
//通过android.content.ContextWrapper类获取其attachBaseContext方法的ID
g_jmethodID_ContextWrapper_attachBaseContext = (*env)->GetMethodID(
env,
g_jclass_ContextWrapper,
"attachBaseContext",
"(Landroid/content/Context;)V");
//LOGI("PathClassLoader start");
// 获取类dalvik.system.PathClassLoader的全局引用(class)
if ( !myFindClass(env, &g_jclass_PathClassLoader, "dalvik/system/PathClassLoader") )
{
//LOGI("ERROR:myPathClassLoader");
return;
}
//判断sdk版本
if(g_jint_sdk_int>13)
{
// 获取类dalvik.system.BaseDexClassLoader的全局引用(class)
if ( !myFindClass(env, &g_jclass_BaseDexClassLoader, "dalvik/system/BaseDexClassLoader") )
{
//LOGI("ERROR:myBaseDexClassLoader");
return;
}
//通过BaseDexClassLoader类获取其pathlist的ID
g_jfieldID_BaseDexClassLoader_pathList = (*env)->GetFieldID(
env,
g_jclass_BaseDexClassLoader,
"pathList",
"Ldalvik/system/DexPathList;");
// 获取类dalvik.system.DexPathList的全局引用(class)
if ( !myFindClass(env, &g_jclass_DexPathList, "dalvik/system/DexPathList") )
{
//LOGI("ERROR:myDexPathList");
return;
}
//通过DexPathList类获取其pathlist的ID
g_jfieldID_DexPathList_dexElements = (*env)->GetFieldID(
env,
g_jclass_DexPathList,
"dexElements",
"[Ldalvik/system/DexPathList$Element;");
// 获取类dalvik.system.DexPathList.Element的全局引用(内部class)
if ( !myFindClass(env, &g_jclass_DexPathList_Element, "dalvik/system/DexPathList$Element") )
{
//LOGI("ERROR:myElement");
return;
}
//通过Element类获取其dexFile的ID
g_jfieldID_DexPathList_Element_dexFile = (*env)->GetFieldID(
env,
g_jclass_DexPathList_Element,
"dexFile",
"Ldalvik/system/DexFile;");
//判断sdk版本,这里是基于安卓6.0的初始化
/*
由于6.0和其之前的版本存在差异,需要反射其他代码
*/
if(g_jint_sdk_int>22){//6.0
//通过Element类获取其dir的ID
g_jfieldID_DexPathList_Element_file = (*env)->GetFieldID(env, g_jclass_DexPathList_Element, "dir", "Ljava/io/File;");
}else{
//通过Element类获取其dir的ID
g_jfieldID_DexPathList_Element_file = (*env)->GetFieldID(env, g_jclass_DexPathList_Element, "file", "Ljava/io/File;");
}
//获取类java.io.File的全局引用(class)
if ( !myFindClass(env, &g_jclass_File, "java/io/File") )
{
//LOGI("ERROR:myFile");
return;
}
//通过myFile类获取其getAbsolutePath的ID
g_jmethodID_File_getAbsolutePath = (*env)->GetMethodID(
env,
g_jclass_File,
"getAbsolutePath",
"()Ljava/lang/String;");
//LOGI("PathClassLoader end");
//获取类dalvik.system.DexFile的全局引用(class)
if ( !myFindClass(env, &g_jclass_DexFile, "dalvik/system/DexFile") )
{
//LOGI("ERROR:myDexFile");
return;
}
//判断sdk版本(其实这里有点累赘了可以把代码放在前面的判断)
if(g_jint_sdk_int>22)
{//通过DexFile类获取其mCookie的ID和静态方法openDexFile的ID
g_jfieldID_DexFile_mCookie = (*env)->GetFieldID(env, g_jclass_DexFile, "mCookie", "Ljava/lang/Object;");
g_jmethodID_DexFile_OpenDexFile=(*env)->GetStaticMethodID(env, g_jclass_DexFile, "openDexFile", "(Ljava/lang/String;Ljava/lang/String;I)Ljava/lang/Object;");
}
else if ( g_jint_sdk_int > 19 )//5.0版本的mCookie和myOpenDexFile的签名和6.0存在差异
{//通过DexFile类获取其mCookie的ID和静态方法openDexFile的ID
g_jfieldID_DexFile_mCookie = (*env)->GetFieldID(env, g_jclass_DexFile, "mCookie", "J");
g_jmethodID_DexFile_OpenDexFile=(*env)->GetStaticMethodID(env, g_jclass_DexFile, "openDexFile", "(Ljava/lang/String;Ljava/lang/String;I)J");
}
else
{
//5.0以下版本的mCookie和myOpenDexFile的签名和6.0存在差异
g_jfieldID_DexFile_mCookie = (*env)->GetFieldID(env,g_jclass_DexFile, "mCookie", "I");
g_jmethodID_DexFile_OpenDexFile=(*env)->GetStaticMethodID(env, g_jclass_DexFile, "openDexFile", "(Ljava/lang/String;Ljava/lang/String;I)I");
}
if ( !myFindClass(env, &g_jclass_ClassLoader, "java/lang/ClassLoader") )
{
//LOGI("ERROR:myClassLoader");
return;
}
//android 5+以上无法用findClass找到android.app.Application类
g_jmethodID_ClassLoader_loadClass = (*env)->GetMethodID( env,
g_jclass_ClassLoader,
"loadClass",
"(Ljava/lang/String;)Ljava/lang/Class;");
//LOGI("System start");
//获取类java.lang.System的全局引用(class)
if ( !myFindClass(env, &g_jclass_System, "java/lang/System") )
{
//LOGI("ERROR:myClassLoader");
return;
}
//通过System类获取getProperty静态方法的ID
g_jmethodID_System_getProperty = (*env)->GetStaticMethodID(
env,
g_jclass_System,
"getProperty",
"(Ljava/lang/String;)Ljava/lang/String;");
//LOGI("SystemProperties start");
g_jclass_status= myFindClass(env,
&g_jclass_SystemProperties,
"android/os/SystemProperties");
if(g_jclass_status)
{
/* SystemProperties_get = (*env)->GetStaticMethodID(
env,
mySystemProperties,
"get",
"(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;");*/
g_jmethodID_SystemProperties_get = (*env)->GetStaticMethodID(
env,
g_jclass_SystemProperties,
"get",
"(Ljava/lang/String;)Ljava/lang/String;");
//获取jstring类型的变量vmname,存放"java.vm.name"字符串
jstring jstring_vmname_key = (*env)->NewStringUTF(env, "java.vm.name");
//通过获取的getProperty方法的ID,调用有参方法getProperty("java.vm.name"),获取当前安卓虚拟机的名字
jobject jstring_vmname_value= (*env)->CallStaticObjectMethod(env, g_jclass_System, g_jmethodID_System_getProperty, jstring_vmname_key);
// 将tmp转换成C语言字符串
const char* lpszVmnameValue = (*env)->GetStringUTFChars(env, jstring_vmname_value, 0);
//LOGI("------- vmNameStr:%s", v22);//将转换成c语言字符的虚拟机名字打印
(*env)->ReleaseStringUTFChars(env, jstring_vmname_value, lpszVmnameValue);
// persist.sys.dalvik.vm.lib
// persist.sys.dalvik.vm.lib.2
jstring jstring_vmlib_key = (*env)->NewStringUTF(env, "persist.sys.dalvik.vm.lib.2");
//这里反编译出错
jobject jstring_vmlib_value = (*env)->CallStaticObjectMethod(env, g_jclass_SystemProperties, g_jmethodID_SystemProperties_get, jstring_vmlib_key );
const char* lpszVmlibValue = (*env)->GetStringUTFChars(env, jstring_vmlib_value, 0);
//释放内存空间
(*env)->ReleaseStringUTFChars(env, jstring_vmlib_value, lpszVmlibValue);
//获取jstring类型的变量vm_version,存放"java.vm.version"字符串
jstring jstring_vm_version_key = (*env)->NewStringUTF(env, "java.vm.version");
//通过获取的getProperty方法的ID,调用有参方法getProperty("java.vm.name"),获取当前安卓虚拟机的版本
jobject jstring_vm_version_value = (*env)->CallStaticObjectMethod(env, g_jclass_System, g_jmethodID_System_getProperty, jstring_vm_version_key);
// 将v32转换成C语言字符串
const char* lpszVmVersionValue = (*env)->GetStringUTFChars(env, jstring_vm_version_value, 0);
//LOGI("---- vmVersionStr:%s", runtime_version);
//将虚拟机版本字符串转化成双精度浮点型(double)
double dVmVersion=atof(lpszVmVersionValue);
//根据逻辑,如果d>2则不是Dalvik虚拟机
if(dVmVersion>2){
g_nIsDalvik=0;
}
else{
g_nIsDalvik=1;
}
//释放内存
(*env)->ReleaseStringUTFChars(env, jstring_vm_version_value, lpszVmVersionValue);
return ;
}
}
}
}
//在JNINativeMethod结构体中根据函数名称和函数签名字符串匹配查找到该函数对应的函数调用指针
int lookup(JNINativeMethod *table, const char *name, const char *sig,void (**fnPtrout)(u4 const *, union JValue *))
{
int i = 0;
while (table[i].name != NULL)
{
//LOGI("lookup %d %s" ,i,table[i].name);
// 根据函数名称和函数签名进行匹配查找
if ((strcmp(name, table[i].name) == 0)
&& (strcmp(sig, table[i].signature) == 0))
{ //获取该函数的名称和函数签名对应的指针
*fnPtrout = table[i].fnPtr;
return 1;
}
i++;
}
return 0;
}
/**
*三个参数,env,application,szPath
*env指针,application为apk当前的Application对象,szPath="/data/data/%s/files/dump.dex"字符串
*将/data/data/<packagename>/asserts/dump.dex文件写入/data/data/<packagename>/files/dump.dex文件中
*
*
*/
void myExtractFile(JNIEnv* env,jobject jobject_application,const char* lpszPath)
{
//szPath=/data/data/%s/files/dump.dex
AAssetManager* lpstAAssetManager;//声明资源管理器对象mgr
//判断szPath路径下是否已经存在dump.dex文件
if(access(lpszPath,R_OK))
{
//通过application对象获取application类
jclass jclass_Application = (*env)->GetObjectClass(env, jobject_application);
//通过application类获取getAssets()方法的ID
jmethodID jmethodID_Application_getAssets = (*env)->GetMethodID(env, jclass_Application, "getAssets", "()Landroid/content/res/AssetManager;");
//调用getAssets()方法
jobject jobject_assetManager = (*env)->CallObjectMethod(env, jobject_application, jmethodID_Application_getAssets);
//给定一个Dalvik AssetManager对象,获取相应的本地AAssetManager对象。
lpstAAssetManager= AAssetManager_fromJava(env, jobject_assetManager);
if(lpstAAssetManager==NULL)
{ //获取失败
//LOGI(" %s","AAssetManager==NULL");
return ;
}
//打开apk资源文件夹下的dump.dex
//即data/data/<packagename>/assets/dump.dex
AAsset* lpszAAssetDumpDex = AAssetManager_open(lpstAAssetManager, "dump.dex",AASSET_MODE_STREAMING);
FILE* lpstFile;
void* lpszBuffer;
int numBytesRead;
if(lpszAAssetDumpDex)
{
//以szPath的字符串路径创建新的file(dump.dex)位于/data/data/<packagename>/files/目录下
lpstFile=fopen(lpszPath,"w");
// int bufferSize = AAsset_getLength(asset);
// LOGI("buffersize is %d",bufferSize);
//申请1024临时空间
lpszBuffer=malloc(1024);
while(1)
{ //将资源文件目录下的dump.dex文件读取出来放到申请的临时空间buffer中
numBytesRead=AAsset_read(lpszAAssetDumpDex, lpszBuffer, 1024);
//读取完成,结束while循环
if(numBytesRead<=0)
break;
//将存放到/data/data/<packagename>/files/file 文件中
fwrite(lpszBuffer,numBytesRead,1,lpstFile);
}
free(lpszBuffer);
fclose(lpstFile);
AAsset_close(lpszAAssetDumpDex);
}
else
{
//LOGI("Error AAssetManager_open");
return;
}
}
else
{
//LOGI("dump.dex existed");
}
}
/**
*Dalvik模式下加载dex
*将dump.dex映射到内存中,获取其返回值
*优点:通过bytearry进行加载返回mCookie不会暴露解密完成后dex文件在内存中的位置
*/
void myLoadDex_dvm(JNIEnv* env,char* lpszDexFilePath)
{
//加载动态库libdvm.so
void *lpstdvmSo = (void*) dlopen("libdvm.so", 1);
//获取动态库"libdvm.so"的导出结构体dvm_dalcik_system_DexFile,该结构体如下,里面存着各种方法:
/**
// jni函数注册的结构体DalvikNativeMethod
const DalvikNativeMethod dvm_dalvik_system_DexFile[] = {
{ "openDexFileNative", "(Ljava/lang/String;Ljava/lang/String;I)I",
Dalvik_dalvik_system_DexFile_openDexFileNative },
{ "openDexFile", "([B)I",
Dalvik_dalvik_system_DexFile_openDexFile_bytearray },
{ "closeDexFile", "(I)V",
Dalvik_dalvik_system_DexFile_closeDexFile },
{ "defineClassNative", "(Ljava/lang/String;Ljava/lang/ClassLoader;I)Ljava/lang/Class;",
Dalvik_dalvik_system_DexFile_defineClassNative },
{ "getClassNameList", "(I)[Ljava/lang/String;",
Dalvik_dalvik_system_DexFile_getClassNameList },
{ "isDexOptNeeded", "(Ljava/lang/String;)Z",
Dalvik_dalvik_system_DexFile_isDexOptNeeded },
{ NULL, NULL, NULL },
};
**/
JNINativeMethod* lpst_dvm_dalvik_system_DexFile = (JNINativeMethod*) dlsym(lpstdvmSo,"dvm_dalvik_system_DexFile");
//找到dvm_dalvik_system_DexFile结构体的openDexFile方法
if(0 == lookup(lpst_dvm_dalvik_system_DexFile, "openDexFile", "([B)I", &openDexFile))
{
openDexFile = NULL;
//LOGI("method does not found ");//如果没有找到,就返回
return ;
}
else
{
//LOGI("openDexFile method found ! HAVE_BIG_ENDIAN");
}
int handle;
struct stat stStat={0};
//打开szPath文件下的dex文件/data/data/<pacakgename>/files/dump.dex
handle=open(lpszDexFilePath,0);
//LOGI("handle:%X\n",handle);
if(!handle)
{//打开失败
//LOGI("open dump.dex failed");
return;
}
//获取dex文件的大小
int status=fstat(handle,&stStat);
if(status)
{
//LOGI("fstat failed");
return;
}
//获取dex文件长度
int dexLen=stStat.st_size;
//LOGI("dexLen:%d,st_blksize:%d",dexLen,(int)buf.st_blksize);
//#define PROT_READ 0x1 /* Page can be read. */
//#define PROT_WRITE 0x2 /* Page can be written. */
//#define PROT_EXEC 0x4 /* Page can be executed. */
//#define PROT_NONE 0x0 /* Page can not be accessed. */
//#define MAP_SHARED 0x01 /* Share changes. */
//#define MAP_PRIVATE 0x02 /* Changes are private. */
//将dex映射进入内存mmap函数
//映射起始地址0,映射内存区域大小dexLen,期望的内存保护标志3,映射对象类型1,文件描述符handle(open函数返
//回值),被映射对象从哪里开始对应0
char* lpszDexMapBase = (char*)mmap(0, dexLen, 3, 2, handle, 0);
/*LOGI("dex magic %c %c %c %c %c %c %c",
*lpszDexMapBase,
*(lpszDexMapBase + 1),
*(lpszDexMapBase + 2),
*(lpszDexMapBase + 3),
*(lpszDexMapBase + 4),
*(lpszDexMapBase + 5),
*(lpszDexMapBase + 6));*/
char* lpszBuffer;
//申请dexLen+16大小的空间Dalvik构建Dalvik_dalvik_system_DexFile_openDexFile_bytearray函数第一个传入参数args
lpszBuffer=(char*)malloc(16+dexLen);
ArrayObject *lpstArrayObject=(ArrayObject*)lpszBuffer;
//LOGI("sizeof ArrayObject:%d",sizeof(ArrayObject));
//ao获取dexLen
lpstArrayObject->length=dexLen;
//为什么获取的空间大小必须为dexLen+16呢?
memcpy(lpszBuffer+16,lpszDexMapBase,dexLen);
munmap(lpszDexMapBase, dexLen);
u4 args[] = { (u4) lpstArrayObject };
union JValue jvalue_result;
jint jint_result;
if(openDexFile != NULL)
{
//调用Dalvik_dalvik_system_DexFile_openDexFile_bytearray加载dex文件,返回mCookie
openDexFile(args,&jvalue_result);
jint_result = (jint) jvalue_result.l;
g_n_dvm_Cookie=jint_result;
//LOGI("Dalvik Cookie :%X" , result);
}
}
/**
*art模式下加载dex
*暂时没有考虑到暴露内存中dex文件的问题
*
*/
void myLoadDex_art(JNIEnv* env,char* lpszDexFilePath)
{
jstring jstring_dexFilePath=(*env)->NewStringUTF(env,lpszDexFilePath);
if(g_jint_sdk_int>22)//6.0以上
{
g_jobject_art_MarCookie=(*env)->CallStaticObjectMethod(env, g_jclass_DexFile, g_jmethodID_DexFile_OpenDexFile, jstring_dexFilePath,0,0);
//LOGI("----MarCookie:%p",art_MarCookie);
}
else
{
g_jlong_art_Cookie=(*env)->CallStaticLongMethod(env, g_jclass_DexFile, g_jmethodID_DexFile_OpenDexFile, jstring_dexFilePath,0,0);
//LOGI("----artCookie:%llx",art_Cookie);
}
void* lpstArtSo=dlopen("libart.so",1);
pArtFun pArtDexFileFindClassDef=(pArtFun)dlsym(lpstArtSo,"_ZNK3art7DexFile12FindClassDefEt");
//LOGI("pArtDexFileFindClassDef:%p",pArtDexFileFindClassDef);
}
void replace_classloader_cookie(JNIEnv *env,jobject classLoader)
{
if(g_jint_sdk_int>13)
{
// "java/lang/ClassLoader"-->pathList对象
jobject jobject_pathList = (*env)->GetObjectField(env, classLoader, g_jfieldID_BaseDexClassLoader_pathList);
//获取Elements数组对象
//pathList-->Element数组对象
jobject jobject_dexElements = (*env)->GetObjectField(env, jobject_pathList, g_jfieldID_DexPathList_dexElements);
int count = (*env)->GetArrayLength(env, jobject_dexElements);//获取Element数组长度
//由于只有一个dex文件,count一直是1
//LOGI("element count: %d", count);
int i=0;
while(i<count)
{
//获取Element数组的第i个元素
jobject jobject_element = (*env)->GetObjectArrayElement(env, jobject_dexElements, i);
//获取dDexPathList&Element示例对象的非静态成员变量dir/file,这些好像有点多余的感觉
jobject jobject_file= (*env)->GetObjectField(env, jobject_element, g_jfieldID_DexPathList_Element_file);// 获取file类
jobject jstringAbsolutePath = (*env)->CallObjectMethod(env, jobject_file, g_jmethodID_File_getAbsolutePath);// file.getAbsolutePath()
const char* lpszAbsolutePath = (*env)->GetStringUTFChars(env, jstringAbsolutePath, 0);
//android 6.0下str为:/
//LOGI("element is %s", lpszAbsolutePath);
//
/*int length = ((*env)->GetStringUTFLength)(env, v20);
int cmpstatus = strncasecmp("apk", (length - 3 + str), 3);
((*env)->ReleaseStringUTFChars)(env, v20, str);*/
//通过Element获取DexFile对象
jobject jobject_dexFile = (*env)->GetObjectField(env, jobject_element, g_jfieldID_DexPathList_Element_dexFile);// 获取DexFile类
//判断sdk版本,并替换对应的mCookie
if(g_jint_sdk_int<=19)
{
//LOGI("SetIntField");
//LOGI("---dvm_Cookie:%X",dvm_Cookie);
(*env)->SetIntField(env, jobject_dexFile, g_jfieldID_DexFile_mCookie, g_n_dvm_Cookie);
}
else if(g_jint_sdk_int>22)
{
//LOGI("SetObjectField");
//LOGI("---art_MarCookie:%X",art_MarCookie);
(*env)->SetObjectField(env, jobject_dexFile, g_jfieldID_DexFile_mCookie, g_jobject_art_MarCookie);
}
else
{
//LOGI("SetLongField");
//LOGI("----artCookie:%llx",art_Cookie);
(*env)->SetLongField(env, jobject_dexFile, g_jfieldID_DexFile_mCookie, g_jlong_art_Cookie);
}
/*if(!cmpstatus)
{
break;
}*/
i++;
}
//LOGI("exit replace_classloader_cookie");
}
}
/*
1.调用父类的attachBaseContext
2./data/data/<packagename>/assets/dump.dex复制到/data/data/<pacakgename>/files/dump.dex
3.加载/data/data/<pacakgename>/files/dump.dex,得到cookie
4.得到的cookie替换掉原壳的cookie.即 context.getClassLoader().pathList.dexElements[i].dexFile.mCookie
5.利用反射生成原Application对象,调用原Application的attach方法
*/
void native_attachContextBaseContext(JNIEnv *env, jobject jobject_param_application,jobject jobject_context)
{
jobject jobject_application=jobject_param_application;
// 通过JNI调用,获取必要的jmethodID和jfieldID以备用以及当前Android虚拟机所处的运行模式
init_class(env,jobject_param_application,jobject_context);
//LOGI("arg:application_obj:%p, myContextWrapper:%p, ContextWrapper_attachBaseContext:%p",application_obj, jclass_ContextWrapper, jmethodID_ContextWrapper_attachBaseContext);
/**
*通过init_class生成的ContextWrapper和获取的attachBaseContext
*调用父类的方法android.content.ContextWrapper.attachBaseContext(ctx)
*/
(*env)->CallNonvirtualVoidMethod(env, jobject_application, g_jclass_ContextWrapper, g_jmethodID_ContextWrapper_attachBaseContext,jobject_context);
/**
*首先通过jobject_param_application和env获取StubApplication类,再通过其获取到它的getFilesDir()方法
*然后调用getFileDir()方法获取能得到文件路径的File实例对象
*再调用File对象获取File类的getAbsolutePath()方法,获取到文件路径的对象,在将其转化成C语言字符串
*/
// 获取StubApplication类
jclass jclass_Application = (*env)->GetObjectClass(env, jobject_application);
//获取getFilesDir()方法
jmethodID jmethodID_Application_getFilesDir =(*env)->GetMethodID(env, jclass_Application, "getFilesDir", "()Ljava/io/File;");
// 调用File.getFilesDir()方法,该方法返回/data/data/<packagename>/files的File实例对象
jobject jobject_file = (*env)->CallObjectMethod(env,jobject_param_application , jmethodID_Application_getFilesDir);
//file_obj(obj)-->file_classz(class),通过File对象获取File类
jclass jclass_File=(*env)->GetObjectClass(env,jobject_file);
//获取file类的getAbsolutePath方法ID
jmethodID jmethodId_File_getAbsolutePath = (*env)->GetMethodID(env, jclass_File, "getAbsolutePath", "()Ljava/lang/String;");
//调用File.getAbsolutePath()获取文件路径/data/data/<packagename>/files
jobject jstring_absolutePath = (*env)->CallObjectMethod(env, jobject_file, jmethodId_File_getAbsolutePath);
//6.0下为/data/user/0/packagename/files/目录
//将文件路径对象转化成字符串
g_lpszabsolutePath=(*env)->GetStringUTFChars(env,jstring_absolutePath,0);
//LOGI("global files path is %s",lpszabsolutePath);
//return ApplicationInfo
/**
*调用一些初始化的方法获取外壳apk的so文件路径,
*首先根据当前jobject_application调用context的getApplicationInfo方法获取ApplicationInfo实例对象
*然后ApplicationInfo对象的属性nativeLibraryDir获取so文件路径的File对象
*最后将so文件路径对象转化成C语言字符串,存放到mNativeLibraryDir对象中
*/
//调用getApplicationInfo方法获取当前ApplicationInfo
jobject jobject_applicationInfo = (*env)->CallObjectMethod(env, jobject_application, g_jmethodID_Context_getApplicationInfo);
//获取so文件路径对象
jobject jobject_nativeLibraryDir = (*env)->GetObjectField(env, jobject_applicationInfo, g_jfieldID_ApplicationInfo_nativeLibraryDir);
//获取外壳apk的so文件路径
const char* lpsznativeLibraryDir=(*env)->GetStringUTFChars(env,jobject_nativeLibraryDir,0);
//LOGI("mNativeLibraryDir is %s",mNativeLibraryDir);
/**
*getPackageResourcePath()获取当前应用的安装包路径
*调用一些初始化的方法ID获取apk的资源文件路径,然后指定外壳apk的进程APKAPATH作为资源存放路径
*调用StubApplication的getPackageResourcePath()获取资源文件路径对象,然后将其转化成char,在把它设置为APKPATH
*的值设置为apk的资源文件存放路径
*/
//调用StuApplication的getPackageResourcePath()获取资源文件路径对象
jobject jobject_packageResourcePath = (*env)->CallObjectMethod(env, jobject_application, g_jmethodID_Context_getPackageResourePath);
//将资源文件路径对象转化成char*
const char* lpszPackageResourePath=(*env)->GetStringUTFChars(env,jobject_packageResourcePath,0);
//将环境变量"APKPATH"的值设置为apk的资源文件存放路径
setenv("APKPATH", lpszPackageResourePath, 1);
//LOGI("APK Path is %s",mPackageResourePath);
/**
*调用Context的getPackageName方法获取APK包名
*mPackageName接受包名字符串
*
*/
//调用Context的getPackageName()方法,获取APK包名对象
jobject jstring_pacakgeName = (*env)->CallObjectMethod(env, jobject_context, g_jmethodID_Context_getPackageName);
//获取APK包名字符串(jstring)
g_lpszPackageName=(*env)->GetStringUTFChars(env,jstring_pacakgeName,0);
//LOGI("mPackageName:%s",mPackageName);
/**
*调用Context的getClassLoader()方法获取APK当前加载的ClassLoader
*以便替换mCookie
*
*/
// public ClassLoader getClassLoader()
//调用context的getClassLoader()方法,获取当前apk加载使用的ClassLoader实例
jobject jobject_classLoader =(*env)->CallObjectMethod(env, jobject_context, g_jmethodID_Context_getClassLoader);
//LOGI("classLoader:%p",classLoader);
/**
*调用获取到的mPackgeName,从/data/data/<packagename>/files目录下释放出dump.dex
*/
char szPath[260]={0};
//使用获取/data/data/<packagename>/files/dump.dex的完整路径
sprintf(szPath,"/data/data/%s/files/dump.dex",g_lpszPackageName);
//LOGI("szPath:%s",szPath);
/**
*将/data/data/<packagename>/assets/dump.dex文件释放到内存中,在写入到
* data/data/<pacakgename>/files/dump.dex文件中
*/
myExtractFile(env,jobject_application,szPath);
//important
//判断虚拟机类型,根据虚拟机的不同进行不同方式的dex文件加载
if(g_nIsDalvik)
{
myLoadDex_dvm(env,szPath);
}
else
{
myLoadDex_art(env,szPath);
}
//important
//用待加载的dex的ClassLoader的mCookie替换掉当前加载的ClassLoader的mCookie值
replace_classloader_cookie(env,jobject_classLoader);
//LOGI("enter new application");
/**
* *(1)假设当前加载的dex文件继承了类android.app.Application,那么这里改为继承后的类的字符串,详细的查看androidmanifest.xml
* *(2)假设当前加载的dex文件(真实的dex)没有继承实现android.app.Application,会直接获取android.app.Application
*/
jstring jstring_applicationClassName= (*env)->NewStringUTF(env, "android.app.Application");
//加载被保护dex文件的Application类
jobject jobject_application = (*env)->CallObjectMethod(env, jobject_classLoader,g_jmethodID_ClassLoader_loadClass, jstring_applicationClassName);
if(!jobject_application)
{
//LOGI("can't findClass realAppClass");
return;
}
//通过获取的findclass获取Application类的构造函数<inti>的ID
jmethodID jmethodID_Application_constructor = (*env)->GetMethodID(env, jobject_application, "<init>", "()V");
//通过findclass调用application类的构造函数创建application的实例对象
g_jobject_newApplication = (*env)->NewObject(env, jobject_application, jmethodID_Application_constructor);
//调用attach方法
(*env)->CallVoidMethod(env, g_jobject_newApplication, g_jmethodID_Application_attach, jobject_context);
//创建待加载dex文件的application类对象的实例引用
if ( g_jobject_newApplication )
{
g_jobject_newApplication = (*env)->NewGlobalRef(env, g_jobject_newApplication);
}
//LOGI("enter realAppClass");
}
/**
*对应StubApplication的onCreate方法
*目的:将壳apk的application类的实例对象替换成被源dex文件的application类实例对象
* (源dex文件一般是没有继承Application,默认为android.app.application),然后调用源dex文件的Application类对象实例的onCreate方法
* 1.替换ActivityThread.currentActivityThread().mPackages.get(packageName).get().mApplication
* 2.替换ActivityThread.currentActivityThread().mInitialApplication
* 3.替换ActivityThread.currentActivityThread().mBoundApplication.info.mApplication
* 4.替换ActivityThread.currentActivityThread().mAllApplications[i]
* 5.调用原Application的onCreate方法
*/
void native_onCreate(JNIEnv *env, jobject jobject_param_application)
{
//获取android.app.ActivityThread的静态方法currentActivityThread的对象
//java代码:android.app.ActivityThread activityThread=ActivityThread.currentActivityThread();
jobject jobject_currentActivityThread = (*env)->CallStaticObjectMethod(env, g_jclass_ActivityThread, g_jmethodID_ActivityThread_currentActivityThread);
//final ArrayMap<String, WeakReference<LoadedApk>> mPackages= new ArrayMap<String, WeakReference<LoadedApk>>();
/**
*(1)获取壳apk的ActivityThread实例对象的的非静态成员变量mPackages
*mPackages: ArrayMap<String,WeakReference<LoadedApk>> mPackages
*再修改对应的LoadedApk实例对象的非静态成员变量mApplication为被源dex文件的类Application
*java代码: android.util.ArrayMap arrayMap=activityThread.mPackages;
* LoadedApk objLoadedApk=arrayMap.get("thePackagename");
*
*/
//获取android.util.ArrayMap的成员变量mPackages的对象
jobject jobject_arraymap_mPackages =(*env)->GetObjectField(env, jobject_currentActivityThread, g_jfieldID_ActivityThread_mPackages);
//获取壳apk包名字符串
jstring jstring_packageName=(*env)->NewStringUTF(env,g_lpszPackageName);
//LOGI("mPackageName %s",mPackageName);
// 调用arrayMap_get函数 获取WeakReference<LoadedApk>类
jobject jobject_weakReferenceLoadedApk = (*env)->CallObjectMethod(env, jobject_arraymap_mPackages, g_jmethodID_ArrayMap_get, jstring_packageName);
//调用WeakReference_get获取LoadedApk类
jobject jobject_loadedApk = (*env)->CallObjectMethod(env, jobject_weakReferenceLoadedApk, g_jmethodID_WeakReference_get);
//修改LoadedApk中的非静态成员变量mApplication为源dex的Application
(*env)->SetObjectField(env, jobject_loadedApk, g_jfieldID_LoadedApk_mApplication, g_jobject_newApplication);
/**
*(2)修改壳apk应用的ActivityThread实例对象的成员变量mInitialApplication为源dex文件的Application对象实例
*/
(*env)->SetObjectField(env, jobject_currentActivityThread, g_jfieldID_ActivityThread_mInitialApplication,g_jobject_newApplication);
/**
*(3)修改壳apk应用的ActivityThread实例对象的成员变量mBoundApplication中的LoadApk非静态成员变量info实例对象的
*实例成员变量mApplication为源dex的Application对象
*AppBindData mBoundApplication;
*AppBindData{LoadedApk info;}
*LoadedApk{Application mApplication}
*/
// AppBindData mBoundApplication;
jobject jobject_mBoundApplication = (*env)->GetObjectField(env, jobject_currentActivityThread, g_jfieldID_ActivityThread_mBoundApplication);
//AppBindData_info=(*env)->GetFieldID(env, AppBindData, "info", "Landroid/app/LoadedApk;");
jobject jobject_info =(*env)->GetObjectField(env, jobject_mBoundApplication, g_jfieldID_AppBindData_info);
(*env)->SetObjectField(env, jobject_info, g_jfieldID_LoadedApk_mApplication, g_jobject_newApplication);
/* mAllApplications = (*env)->GetFieldID(env,
ActivityThread,
"mAllApplications",
"Ljava/util/ArrayList;");*/
/**
*(4)遍历壳apk应用的ActivityThread实例对象的非静态成员变量mAllApplications中的类Application实例对象
*将壳apk的StubApplication替换为源dexapplication实例对象
*StubApplication-->Application
*ArrayList<Application> mAllApplications
*/
//获取allApplications
jobject jobject_mAllApplications = (*env)->GetObjectField(env, jobject_currentActivityThread, g_jfieldID_ActivityThread_mAllApplications);
//获取ArrayList的大小
int count = (*env)->CallIntMethod(env, jobject_mAllApplications, g_jmethodID_ArrayList_size);
//LOGI("array_size:%d",count);
int index = 0;
while ( index < count )
{
//调用mAllApplication(ArrayList)的get方法获取每一个元素ArrayList.get(index)
jobject jobject_allApplicationItem = (*env)->CallObjectMethod(env, jobject_mAllApplications, g_jmethodID_ArrayList_get, index);
//LOGI("compare: i=%d item=%p", index, v12);
//在类对象mApplication实例中查找壳apk应用的StubApplication类对象
if ( ((*env)->IsSameObject)(env, jobject_param_application, jobject_allApplicationItem) == 1 )//是否等于StubApplication类的对象
{
//LOGI("replace: find same replace");
//调用ArrayList的set方法替换源Application的实例对象
(*env)->CallObjectMethod(env, jobject_mAllApplications, g_jmethodID_ArrayList_set, index,g_jobject_newApplication);
}
// _JNIEnv::DeleteLocalRef(env, v12);
++index;
}
//IDA反编译这里出错,多了一个参数
(*env)->CallVoidMethod(env, g_jobject_newApplication, g_jmethodID_Application_onCreate);
(*env)->DeleteGlobalRef(env, g_jobject_newApplication);
}
// 生成jni函数注册的jni函数table
static JNINativeMethod method_table[] = {
// 被注册的函数的名称(java函数名称)、被注册函数的签名(javah获取)、
// 被注册函数native实现的函数指针
{ "attachBaseContext", "(Landroid/content/Context;)V", (void*)native_attachContextBaseContext},
{ "onCreate","()V",(void*)native_onCreate},
};
static int registerNativeMethods(JNIEnv* env, const char* className,JNINativeMethod* gMethods, int numMethods)
{
jclass clazz;
clazz = (*env)->FindClass(env, className);
if (clazz == 0) {
return JNI_FALSE;
}
//LOGI("gMethods %s,%s,%p\n ",gMethods[0].name,gMethods[0].signature,gMethods[0].fnPtr);
if ((*env)->RegisterNatives(env, clazz, gMethods, numMethods) < 0) {
return JNI_FALSE;
}
return JNI_TRUE;
}
int register_ndk_load(JNIEnv *env)
{
// 对类"com/example/unpack/StubApplication"的函数attachBaseContext和onCreate进行注册
return registerNativeMethods(env, JNIREG_CLASS,
method_table, NELEM(method_table));
}
JNIEXPORT jint JNI_OnLoad(JavaVM* vm, void* reserved)
{
JNIEnv* env = 0;
jint result = -1;
// LOGI("JNI_OnLoad is called");
if ((*vm)->GetEnv(vm, (void**) &env, JNI_VERSION_1_4) != JNI_OK) {
return result;
}
int status=register_ndk_load(env);
if(!status)
{
//LOGI("register call failed");
}
return JNI_VERSION_1_4;
}
第一代Android壳源码--某某公司早期壳代码加固原理分析
本文深入剖析JNI编程技巧,详细讲解如何利用JNI实现动态链接库的调用,包括函数注册、资源文件处理、DEX文件加载及虚拟机环境适配等关键步骤。通过具体示例,展示如何在不同Android版本和虚拟机环境下加载和替换DEX文件,实现应用程序的动态扩展和功能增强。
摘要由CSDN通过智能技术生成