From ede9f1abc3ac15a4dc1e18f754fc364542a3a5b3 Mon Sep 17 00:00:00 2001 From: luoyesiqiu Date: Mon, 4 Dec 2023 21:09:36 +0800 Subject: [PATCH] Optimized merge dex elements logic --- shell/src/main/cpp/dpt.cpp | 63 ++++++++++------- shell/src/main/cpp/dpt.h | 3 +- .../cpp/reflect/dalvik_system_DexPathList.cpp | 16 +++++ .../cpp/reflect/dalvik_system_DexPathList.h | 2 + shell/src/main/cpp/reflect/java_io_File.h | 12 +++- .../main/cpp/reflect/java_util_ArrayList.h | 10 ++- .../java/com/luoyesiqiu/shell/Global.java | 1 - .../java/com/luoyesiqiu/shell/JniBridge.java | 2 +- .../luoyesiqiu/shell/ProxyApplication.java | 8 +-- .../shell/ProxyComponentFactory.java | 60 ++++------------ .../shell/util/ShellClassLoader.java | 69 ------------------- 11 files changed, 97 insertions(+), 149 deletions(-) delete mode 100644 shell/src/main/java/com/luoyesiqiu/shell/util/ShellClassLoader.java diff --git a/shell/src/main/cpp/dpt.cpp b/shell/src/main/cpp/dpt.cpp index 2e4ca98f..e302b3cb 100644 --- a/shell/src/main/cpp/dpt.cpp +++ b/shell/src/main/cpp/dpt.cpp @@ -5,7 +5,7 @@ #include "dpt.h" using namespace dpt; -//缓存变量 + static jobject g_realApplicationInstance = nullptr; static jclass g_realApplicationClass = nullptr; void* zip_addr = nullptr; @@ -22,46 +22,61 @@ static JNINativeMethod gMethods[] = { {"gdp", "()Ljava/lang/String;", (void *) getCompressedDexesPathExport}, {"rcf", "()Ljava/lang/String;", (void *) readAppComponentFactory}, {"rapn", "()Ljava/lang/String;", (void *) readApplicationName}, - {"mde", "(Ljava/lang/ClassLoader;Ljava/lang/ClassLoader;)V", (void *) mergeDexElements}, + {"mde", "(Ljava/lang/ClassLoader;)V", (void *) mergeDexElements}, {"rde", "(Ljava/lang/ClassLoader;Ljava/lang/String;)V", (void *) removeDexElements}, {"ra", "(Ljava/lang/String;)V", (void *) replaceApplication} }; -void mergeDexElements(JNIEnv* env,jclass __unused,jobject oldClassLoader,jobject newClassLoader){ - dalvik_system_BaseDexClassLoader oldBaseDexClassLoader(env,oldClassLoader); - dalvik_system_BaseDexClassLoader newBaseDexClassLoader(env,newClassLoader); - jobject oldDexPathListObj = oldBaseDexClassLoader.getPathList(); - jobject newDexPathListObj = newBaseDexClassLoader.getPathList(); +jobjectArray makePathElements(JNIEnv* env){ + char compressedDexesPathChs[256] = {0}; + getCompressedDexesPath(env,compressedDexesPathChs, 256); + + jstring compressedDexesPath = env->NewStringUTF(compressedDexesPathChs); - dalvik_system_DexPathList newDexPathList(env,newDexPathListObj); - dalvik_system_DexPathList oldDexPathList(env,oldDexPathListObj); + java_io_File file(env,compressedDexesPath); - jobjectArray newClassLoaderDexElements = newDexPathList.getDexElements(); + java_util_ArrayList files(env); + files.add(file.getInstance()); + java_util_ArrayList suppressedExceptions(env); - jobjectArray oldClassLoaderDexElements = oldDexPathList.getDexElements(); + jobjectArray elements = dalvik_system_DexPathList::makePathElements(env, + files.getInstance(), + nullptr, + suppressedExceptions.getInstance()); - jint oldLen = env->GetArrayLength(oldClassLoaderDexElements); - jint newLen = env->GetArrayLength(newClassLoaderDexElements); + return elements; +} - DLOGD("mergeDexElements oldlen = %d , newlen = %d",oldLen,newLen); +void mergeDexElements(JNIEnv* env,jclass __unused, jobject targetClassLoader){ - dalvik_system_DexPathList::Element element(env,nullptr); + jobjectArray extraDexElements = makePathElements(env); - jclass ElementClass = element.getClass(); + dalvik_system_BaseDexClassLoader targetBaseDexClassLoader(env,targetClassLoader); - jobjectArray newElementArray = env->NewObjectArray(oldLen + newLen,ElementClass, nullptr); + jobject originDexPathListObj = targetBaseDexClassLoader.getPathList(); + + dalvik_system_DexPathList targetDexPathList(env,originDexPathListObj); + + jobjectArray originDexElements = targetDexPathList.getDexElements(); + + jsize extraSize = env->GetArrayLength(extraDexElements); + jsize originSize = env->GetArrayLength(originDexElements); + + dalvik_system_DexPathList::Element element(env, nullptr); + jclass ElementClass = element.getClass(); + jobjectArray newDexElements = env->NewObjectArray(originSize + extraSize,ElementClass, nullptr); - for(int i = 0;i < newLen;i++) { - jobject elementObj = env->GetObjectArrayElement(newClassLoaderDexElements, i); - env->SetObjectArrayElement(newElementArray,i,elementObj); + for(int i = 0;i < originSize;i++) { + jobject elementObj = env->GetObjectArrayElement(originDexElements, i); + env->SetObjectArrayElement(newDexElements,i,elementObj); } - for(int i = newLen;i < oldLen + newLen;i++) { - jobject elementObj = env->GetObjectArrayElement(oldClassLoaderDexElements, i - newLen); - env->SetObjectArrayElement(newElementArray,i,elementObj); + for(int i = originSize;i < originSize + extraSize;i++) { + jobject elementObj = env->GetObjectArrayElement(extraDexElements, i - originSize); + env->SetObjectArrayElement(newDexElements,i,elementObj); } - oldDexPathList.setDexElements(newElementArray); + targetDexPathList.setDexElements(newDexElements); DLOGD("mergeDexElements success"); } diff --git a/shell/src/main/cpp/dpt.h b/shell/src/main/cpp/dpt.h index 851861a8..d0f81f5c 100644 --- a/shell/src/main/cpp/dpt.h +++ b/shell/src/main/cpp/dpt.h @@ -39,7 +39,8 @@ void init_app(JNIEnv* env,jclass __unused,jobject context); void readCodeItem(uint8_t *data,size_t data_len); jstring readAppComponentFactory(JNIEnv *env,jclass __unused); jstring readApplicationName(JNIEnv *env, jclass __unused); -void mergeDexElements(JNIEnv* env,jclass __unused,jobject oldClassLoader,jobject newClassLoader); +jobjectArray makePathElements(JNIEnv* env); +void mergeDexElements(JNIEnv* env,jclass __unused,jobject targetClassLoader); void removeDexElements(JNIEnv* env,jclass __unused,jobject classLoader,jstring elementName); void replaceApplication(JNIEnv *env, jclass __unused, jstring originApplication); void replaceApplicationOnActivityThread(JNIEnv *env,jclass __unused, jobject realApplication); diff --git a/shell/src/main/cpp/reflect/dalvik_system_DexPathList.cpp b/shell/src/main/cpp/reflect/dalvik_system_DexPathList.cpp index 8fb49746..a914aa71 100644 --- a/shell/src/main/cpp/reflect/dalvik_system_DexPathList.cpp +++ b/shell/src/main/cpp/reflect/dalvik_system_DexPathList.cpp @@ -4,6 +4,22 @@ #include "dalvik_system_DexPathList.h" +jobjectArray dalvik_system_DexPathList::makePathElements(JNIEnv *env, jobject files, jobject optimizedDirectory, + jobject suppressedExceptions) { + + jclass cls = env->FindClass("dalvik/system/DexPathList"); + + jmethodID jmethodId = env->GetStaticMethodID(cls, + "makePathElements", + "(Ljava/util/List;Ljava/io/File;Ljava/util/List;)[Ldalvik/system/DexPathList$Element;"); + jobjectArray retObj = (jobjectArray)env->CallStaticObjectMethod(cls, + jmethodId, + files, + optimizedDirectory, + suppressedExceptions); + return retObj; +} + jobjectArray dalvik_system_DexPathList::getDexElements() { auto dexElements = static_cast(jni::GetObjectField(m_env, getInstance(), diff --git a/shell/src/main/cpp/reflect/dalvik_system_DexPathList.h b/shell/src/main/cpp/reflect/dalvik_system_DexPathList.h index 183f90c6..3c6fec62 100644 --- a/shell/src/main/cpp/reflect/dalvik_system_DexPathList.h +++ b/shell/src/main/cpp/reflect/dalvik_system_DexPathList.h @@ -25,6 +25,8 @@ namespace dpt{ jobjectArray getDexElements(); void setDexElements(jobjectArray dexElements); + static jobjectArray makePathElements(JNIEnv *env, jobject files, jobject optimizedDirectory, + jobject suppressedExceptions); protected: const char *getClassName() { diff --git a/shell/src/main/cpp/reflect/java_io_File.h b/shell/src/main/cpp/reflect/java_io_File.h index f2df24f2..03d8106a 100644 --- a/shell/src/main/cpp/reflect/java_io_File.h +++ b/shell/src/main/cpp/reflect/java_io_File.h @@ -13,15 +13,25 @@ using namespace dpt::reflect; namespace dpt{ namespace reflect{ class java_io_File : public Reflect{ + private: + const char *className = "java/io/File"; public: java_io_File(JNIEnv *env,jobject obj){ this->m_env = env; this->m_obj = obj; } + java_io_File(JNIEnv *env,jstring pathname){ + this->m_env = env; + jclass FileClass = jni::FindClass(env,className); + this->m_obj = jni::NewObject(env, + FileClass, + "(Ljava/lang/String;)V", + pathname); + } jstring getName(); protected: const char *getClassName() { - return "java/io/File"; + return className; } }; } diff --git a/shell/src/main/cpp/reflect/java_util_ArrayList.h b/shell/src/main/cpp/reflect/java_util_ArrayList.h index 930e7dc8..56380514 100644 --- a/shell/src/main/cpp/reflect/java_util_ArrayList.h +++ b/shell/src/main/cpp/reflect/java_util_ArrayList.h @@ -13,11 +13,19 @@ using namespace dpt::reflect; namespace dpt { namespace reflect { class java_util_ArrayList : public Reflect{ + private: + const char *className = "java/util/ArrayList"; public: java_util_ArrayList(JNIEnv *env,jobject obj){ this->m_env = env; this->m_obj = obj; } + + java_util_ArrayList(JNIEnv *env){ + this->m_env = env; + jclass ArrayListClass = jni::FindClass(env,className); + this->m_obj = jni::NewObject(env, ArrayListClass,"()V"); + } jboolean remove(jobject obj); jobject remove(int i); jboolean add(jobject obj); @@ -25,7 +33,7 @@ namespace dpt { protected: const char *getClassName() { - return "java/util/ArrayList"; + return className; } }; } diff --git a/shell/src/main/java/com/luoyesiqiu/shell/Global.java b/shell/src/main/java/com/luoyesiqiu/shell/Global.java index 8fb07e6d..ef26c9da 100644 --- a/shell/src/main/java/com/luoyesiqiu/shell/Global.java +++ b/shell/src/main/java/com/luoyesiqiu/shell/Global.java @@ -11,6 +11,5 @@ public class Global { public static final String SHELL_SO_NAME = "libdpt.so"; public volatile static boolean sIsReplacedClassLoader = false; public volatile static boolean sNeedCalledApplication = true; - public volatile static boolean sLoadedDexes = false; } diff --git a/shell/src/main/java/com/luoyesiqiu/shell/JniBridge.java b/shell/src/main/java/com/luoyesiqiu/shell/JniBridge.java index 4a8fd02b..7ee003b2 100644 --- a/shell/src/main/java/com/luoyesiqiu/shell/JniBridge.java +++ b/shell/src/main/java/com/luoyesiqiu/shell/JniBridge.java @@ -16,7 +16,7 @@ public class JniBridge { public static native void craa(Context context, String applicationClassName); public static native void ia(Context context); public static native String rcf(); - public static native void mde(ClassLoader oldClassLoader,ClassLoader newClassLoader); + public static native void mde(ClassLoader targetClassLoader); public static native void rde(ClassLoader classLoader,String elementName); public static native String gap(); public static native String gdp(); diff --git a/shell/src/main/java/com/luoyesiqiu/shell/ProxyApplication.java b/shell/src/main/java/com/luoyesiqiu/shell/ProxyApplication.java index 5d6adff9..5032065b 100644 --- a/shell/src/main/java/com/luoyesiqiu/shell/ProxyApplication.java +++ b/shell/src/main/java/com/luoyesiqiu/shell/ProxyApplication.java @@ -7,7 +7,6 @@ import android.util.Log; import com.luoyesiqiu.shell.util.FileUtils; -import com.luoyesiqiu.shell.util.ShellClassLoader; /** * Created by luoyesiqiu @@ -51,11 +50,8 @@ protected void attachBaseContext(Context base) { Log.d(TAG,"ProxyApplication init"); JniBridge.ia(base); - ClassLoader oldClassLoader = base.getClassLoader(); - - ClassLoader shellClassLoader = ShellClassLoader.loadDex(base); - - JniBridge.mde(oldClassLoader,shellClassLoader); + ClassLoader targetClassLoader = base.getClassLoader(); + JniBridge.mde(targetClassLoader); Global.sIsReplacedClassLoader = true; } } diff --git a/shell/src/main/java/com/luoyesiqiu/shell/ProxyComponentFactory.java b/shell/src/main/java/com/luoyesiqiu/shell/ProxyComponentFactory.java index 8ba45ce0..82e26344 100644 --- a/shell/src/main/java/com/luoyesiqiu/shell/ProxyComponentFactory.java +++ b/shell/src/main/java/com/luoyesiqiu/shell/ProxyComponentFactory.java @@ -12,26 +12,21 @@ import android.text.TextUtils; import android.util.Log; +import androidx.annotation.NonNull; + import com.luoyesiqiu.shell.util.EnvUtils; import com.luoyesiqiu.shell.util.FileUtils; -import com.luoyesiqiu.shell.util.ShellClassLoader; import java.lang.reflect.Method; -@TargetApi(28) + @TargetApi(28) public class ProxyComponentFactory extends AppComponentFactory { private static final String TAG = "dpt " + ProxyComponentFactory.class.getSimpleName(); private static AppComponentFactory sAppComponentFactory; - private ClassLoader newClassLoader; private String getTargetClassName(){ return JniBridge.rcf(); } - /** - * 使用App的ClassLoader来加载目标AppComponentFactory. - * @param appClassLoader App的ClassLoader - * @return 目标AppComponentFactory - */ private AppComponentFactory getTargetAppComponentFactory(ClassLoader appClassLoader){ if(sAppComponentFactory == null){ String targetClassName = getTargetClassName(); @@ -48,26 +43,8 @@ private AppComponentFactory getTargetAppComponentFactory(ClassLoader appClassLoa return sAppComponentFactory; } - private ClassLoader init(ClassLoader cl){ - if(!Global.sLoadedDexes){ - Global.sLoadedDexes = true; - - JniBridge.ia(null); - String apkPath = JniBridge.gap(); - String dexPath = JniBridge.gdp(); - Log.d(TAG, "init dexPath: " + dexPath + ",apkPath: " + apkPath); - newClassLoader = ShellClassLoader.loadDex(apkPath,dexPath); - Log.d(TAG,"ProxyComponentFactory init() shell classLoader = " + cl); - Log.d(TAG,"ProxyComponentFactory init() app classLoader = " + newClassLoader); - return newClassLoader; - } - Log.d(TAG,"ProxyComponentFactory init() tail shell classLoader = " + cl); - Log.d(TAG,"ProxyComponentFactory init() tail app classLoader = " + newClassLoader); - return newClassLoader; - } - @Override - public Activity instantiateActivity(ClassLoader cl, String className, Intent intent) throws ClassNotFoundException, IllegalAccessException, InstantiationException { + public Activity instantiateActivity(@NonNull ClassLoader cl, @NonNull String className, Intent intent) throws ClassNotFoundException, IllegalAccessException, InstantiationException { Log.d(TAG, "instantiateActivity() called with: cl = [" + cl + "], className = [" + className + "], intent = [" + intent + "]"); AppComponentFactory targetAppComponentFactory = getTargetAppComponentFactory(cl); @@ -83,7 +60,7 @@ public Activity instantiateActivity(ClassLoader cl, String className, Intent int } @Override - public Application instantiateApplication(ClassLoader cl, String className) throws ClassNotFoundException, IllegalAccessException, InstantiationException { + public Application instantiateApplication(@NonNull ClassLoader cl, @NonNull String className) throws ClassNotFoundException, IllegalAccessException, InstantiationException { Log.d(TAG, "instantiateApplication() called with: cl = [" + cl + "], className = [" + className + "]"); if(!Global.sIsReplacedClassLoader) { if(EnvUtils.getApplicationInfo() == null) { @@ -94,21 +71,19 @@ public Application instantiateApplication(ClassLoader cl, String className) thro FileUtils.unzipLibs(sourceDir,dataDir); JniBridge.loadShellLibs(dataDir,sourceDir); } - ClassLoader appClassLoader = init(cl); + JniBridge.ia(null); AppComponentFactory targetAppComponentFactory = null; String applicationName = JniBridge.rapn(); if(!Global.sIsReplacedClassLoader){ - JniBridge.mde(cl, appClassLoader); + JniBridge.mde(cl); Global.sIsReplacedClassLoader = true; targetAppComponentFactory = getTargetAppComponentFactory(cl); } else{ - targetAppComponentFactory = getTargetAppComponentFactory(appClassLoader); + targetAppComponentFactory = getTargetAppComponentFactory(cl); } - ClassLoader apacheHttpLibLoader = ShellClassLoader.loadDex(Global.APACHE_HTTP_LIB); - JniBridge.mde(cl, apacheHttpLibLoader); Global.sNeedCalledApplication = false; if(targetAppComponentFactory != null) { @@ -149,21 +124,16 @@ public Application instantiateApplication(ClassLoader cl, String className) thro * This method add in Android 10 */ @Override - public ClassLoader instantiateClassLoader(ClassLoader cl, ApplicationInfo aInfo) { + public ClassLoader instantiateClassLoader(@NonNull ClassLoader cl, @NonNull ApplicationInfo aInfo) { Log.d(TAG, "instantiateClassLoader() called with: cl = [" + cl + "], aInfo = [" + aInfo + "]"); - if(aInfo == null) { - throw new NullPointerException("application info is null"); - } FileUtils.unzipLibs(aInfo.sourceDir,aInfo.dataDir); JniBridge.loadShellLibs(aInfo.dataDir,aInfo.sourceDir); - ClassLoader classLoader = init(cl); - AppComponentFactory targetAppComponentFactory = getTargetAppComponentFactory(cl); - JniBridge.mde(cl,classLoader); - JniBridge.rde(classLoader, Global.FAKE_APK_NAME); + JniBridge.ia(null); + AppComponentFactory targetAppComponentFactory = getTargetAppComponentFactory(cl); - Log.d(TAG, "instantiateClassLoader() old classloader = [" + classLoader + "]"); + JniBridge.mde(cl); Global.sIsReplacedClassLoader = true; @@ -179,7 +149,7 @@ public ClassLoader instantiateClassLoader(ClassLoader cl, ApplicationInfo aInfo) } @Override - public BroadcastReceiver instantiateReceiver(ClassLoader cl, String className, Intent intent) throws ClassNotFoundException, IllegalAccessException, InstantiationException { + public BroadcastReceiver instantiateReceiver(@NonNull ClassLoader cl, @NonNull String className, Intent intent) throws ClassNotFoundException, IllegalAccessException, InstantiationException { Log.d(TAG, "instantiateReceiver() called with: cl = [" + cl + "], className = [" + className + "], intent = [" + intent + "]"); AppComponentFactory targetAppComponentFactory = getTargetAppComponentFactory(cl); @@ -195,7 +165,7 @@ public BroadcastReceiver instantiateReceiver(ClassLoader cl, String className, I } @Override - public Service instantiateService(ClassLoader cl, String className, Intent intent) + public Service instantiateService(@NonNull ClassLoader cl, @NonNull String className, Intent intent) throws ClassNotFoundException, IllegalAccessException, InstantiationException { Log.d(TAG, "instantiateService() called with: cl = [" + cl + "], className = [" + className + "], intent = [" + intent + "]"); AppComponentFactory targetAppComponentFactory = getTargetAppComponentFactory(cl); @@ -213,7 +183,7 @@ public Service instantiateService(ClassLoader cl, String className, Intent inten @Override - public ContentProvider instantiateProvider( ClassLoader cl, String className) throws ClassNotFoundException, IllegalAccessException, InstantiationException { + public ContentProvider instantiateProvider(@NonNull ClassLoader cl, @NonNull String className) throws ClassNotFoundException, IllegalAccessException, InstantiationException { Log.d(TAG, "instantiateProvider() called with: cl = [" + cl + "], className = [" + className + "]"); AppComponentFactory targetAppComponentFactory = getTargetAppComponentFactory(cl); if(targetAppComponentFactory != null) { diff --git a/shell/src/main/java/com/luoyesiqiu/shell/util/ShellClassLoader.java b/shell/src/main/java/com/luoyesiqiu/shell/util/ShellClassLoader.java deleted file mode 100644 index 68578f13..00000000 --- a/shell/src/main/java/com/luoyesiqiu/shell/util/ShellClassLoader.java +++ /dev/null @@ -1,69 +0,0 @@ -package com.luoyesiqiu.shell.util; - -import android.content.Context; -import android.util.Log; - -import com.luoyesiqiu.shell.JniBridge; - -import java.io.File; - -import dalvik.system.PathClassLoader; - -/** - * Created by luoyesiqiu - */ -public class ShellClassLoader extends PathClassLoader { - - private static final String TAG = "dpt"; - - public ShellClassLoader(String dexPath, String librarySearchPath,ClassLoader parent) { - super(dexPath, librarySearchPath, parent); - } - - @Override - protected Class loadClass(String name, boolean resolve) { - Log.d(TAG, "try loadClass: name = [" + name + "], resolve = [" + resolve + "]"); - Class clazz = findLoadedClass(name); - if(clazz != null){ - Log.d(TAG, Thread.currentThread().getName() + "," + "loaded class = " + clazz); - return clazz; - } - try { - clazz = findClass(name); - Log.d(TAG, Thread.currentThread().getName() + "," + "loadClass() find1 = " + clazz); - - } - catch (Throwable e){ - try { - clazz = super.loadClass(name, resolve); - Log.d(TAG, Thread.currentThread().getName() + "," + "loadClass() find2 = " + clazz); - } - catch (Throwable e2){ - Log.w(TAG, Thread.currentThread().getName() + "," + "loadClass classLoader: " + super.toString(), e); - } - - } - - return clazz; - } - - public static ClassLoader loadDex(String dexPath){ - return new ShellClassLoader(dexPath ,null,ClassLoader.getSystemClassLoader()); - } - - public static ClassLoader loadDex(String apkPath,String dexPath){ - String abi = EnvUtils.getAbiDirName(apkPath); - String nativePath = null; - if(abi != null) { - File nativeLibPath = new File(apkPath.substring(0, apkPath.lastIndexOf("/")) + File.separator + "lib", abi); - nativePath = nativeLibPath.getAbsolutePath(); - Log.d(TAG, "loadDex() called with: sourcePath = [" + apkPath + "]"); - Log.d(TAG, "loadDex() called with: nativePath = [" + nativePath + "]"); - } - return new ShellClassLoader(dexPath ,nativePath,ClassLoader.getSystemClassLoader()); - } - - public static ClassLoader loadDex(Context context){ - return new ShellClassLoader(JniBridge.gdp(),context.getApplicationInfo().nativeLibraryDir,ClassLoader.getSystemClassLoader()); - } -}