Skip to content

Commit

Permalink
Optimized merge dex elements logic
Browse files Browse the repository at this point in the history
  • Loading branch information
luoyesiqiu committed Dec 4, 2023
1 parent a848d69 commit ede9f1a
Show file tree
Hide file tree
Showing 11 changed files with 97 additions and 149 deletions.
63 changes: 39 additions & 24 deletions shell/src/main/cpp/dpt.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
#include "dpt.h"

using namespace dpt;
//缓存变量

static jobject g_realApplicationInstance = nullptr;
static jclass g_realApplicationClass = nullptr;
void* zip_addr = nullptr;
Expand All @@ -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");
}
Expand Down
3 changes: 2 additions & 1 deletion shell/src/main/cpp/dpt.h
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
16 changes: 16 additions & 0 deletions shell/src/main/cpp/reflect/dalvik_system_DexPathList.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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<jobjectArray>(jni::GetObjectField(m_env,
getInstance(),
Expand Down
2 changes: 2 additions & 0 deletions shell/src/main/cpp/reflect/dalvik_system_DexPathList.h
Original file line number Diff line number Diff line change
Expand Up @@ -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() {
Expand Down
12 changes: 11 additions & 1 deletion shell/src/main/cpp/reflect/java_io_File.h
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
};
}
Expand Down
10 changes: 9 additions & 1 deletion shell/src/main/cpp/reflect/java_util_ArrayList.h
Original file line number Diff line number Diff line change
Expand Up @@ -13,19 +13,27 @@ 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);
void add(int i,jobject obj);

protected:
const char *getClassName() {
return "java/util/ArrayList";
return className;
}
};
}
Expand Down
1 change: 0 additions & 1 deletion shell/src/main/java/com/luoyesiqiu/shell/Global.java
Original file line number Diff line number Diff line change
Expand Up @@ -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;

}
2 changes: 1 addition & 1 deletion shell/src/main/java/com/luoyesiqiu/shell/JniBridge.java
Original file line number Diff line number Diff line change
Expand Up @@ -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();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@
import android.util.Log;

import com.luoyesiqiu.shell.util.FileUtils;
import com.luoyesiqiu.shell.util.ShellClassLoader;

/**
* Created by luoyesiqiu
Expand Down Expand Up @@ -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;
}
}
Expand Down
60 changes: 15 additions & 45 deletions shell/src/main/java/com/luoyesiqiu/shell/ProxyComponentFactory.java
Original file line number Diff line number Diff line change
Expand Up @@ -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();
Expand All @@ -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);
Expand All @@ -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) {
Expand All @@ -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) {
Expand Down Expand Up @@ -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;

Expand All @@ -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);

Expand All @@ -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);
Expand All @@ -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) {
Expand Down
Loading

0 comments on commit ede9f1a

Please sign in to comment.