Skip to content

Commit

Permalink
chore: optimize classloader count in heap
Browse files Browse the repository at this point in the history
  • Loading branch information
cinit committed Sep 28, 2024
1 parent fdeb285 commit f7b7ecd
Show file tree
Hide file tree
Showing 4 changed files with 149 additions and 8 deletions.
16 changes: 13 additions & 3 deletions app/src/main/java/io/github/qauxv/util/CustomMenu.kt
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ import io.github.qauxv.util.dexkit.AbstractQQCustomMenuItem
import io.github.qauxv.util.dexkit.DexKit
import net.bytebuddy.ByteBuddy
import net.bytebuddy.android.AndroidClassLoadingStrategy
import net.bytebuddy.android.InjectableDexClassLoader
import net.bytebuddy.implementation.FixedValue
import net.bytebuddy.implementation.MethodCall
import net.bytebuddy.matcher.ElementMatchers
Expand Down Expand Up @@ -86,7 +87,16 @@ object CustomMenu {


private val strategy by lazy {
AndroidClassLoadingStrategy.Wrapping()
AndroidClassLoadingStrategy.Injecting()
}

private lateinit var injectionClassLoader: ClassLoader

private fun getOrCreateInjectionClassLoader(parent: ClassLoader): ClassLoader {
if (!::injectionClassLoader.isInitialized) {
injectionClassLoader = InjectableDexClassLoader(parent)
}
return injectionClassLoader
}

/**
Expand All @@ -108,7 +118,7 @@ object CustomMenu {
.method(ElementMatchers.named(clickName))
.intercept(MethodCall.call { click() })
.make()
.load(absMenuItem.classLoader, strategy)
.load(getOrCreateInjectionClassLoader(absMenuItem.classLoader!!), strategy)
.loaded
return menuItemClass.getDeclaredConstructor(msgClass)
.newInstance(msg)
Expand All @@ -135,7 +145,7 @@ object CustomMenu {
.method(ElementMatchers.named(clickName))
.intercept(MethodCall.call { click() })
.make()
.load(absMenuItem.classLoader, strategy)
.load(getOrCreateInjectionClassLoader(absMenuItem.classLoader!!), strategy)
.loaded
return menuItemClass.getDeclaredConstructor(msgClass).newInstance(msg)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,6 @@
import com.android.dx.dex.cf.CfTranslator;
import com.android.dx.dex.file.ClassDefItem;
import com.android.dx.dex.file.DexFile;
import dalvik.system.BaseDexClassLoader;
import dalvik.system.DexClassLoader;
import io.github.qauxv.util.dyn.MemoryDexLoader;
import net.bytebuddy.description.type.TypeDescription;
import net.bytebuddy.dynamic.loading.ClassLoadingStrategy;
Expand All @@ -41,9 +39,6 @@
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import java.util.jar.JarEntry;
import java.util.jar.JarOutputStream;
import java.util.logging.Logger;

/**
* <p>
Expand Down Expand Up @@ -566,4 +561,50 @@ protected Map<TypeDescription, Class<?>> doLoad(@MaybeNull ClassLoader classLoad
return loadedTypes;
}
}

/**
* An Android class loading strategy that injects a dex file into an existing class loader.
*/
public static class Injecting extends AndroidClassLoadingStrategy {

/**
* Creates a new injecting class loading strategy for Android that uses the default SDK-compiler based dex processor.
*/
public Injecting() {
this(DexProcessor.ForSdkCompiler.makeDefault());
}

/**
* Creates a new injecting class loading strategy for Android.
*
* @param dexProcessor The dex processor to be used for creating a dex file out of Java files.
*/
public Injecting(DexProcessor dexProcessor) {
super(dexProcessor);
}

/**
* {@inheritDoc}
*/
protected Map<TypeDescription, Class<?>> doLoad(@MaybeNull ClassLoader classLoader, Set<TypeDescription> typeDescriptions, byte[] dexFile)
throws IOException {
if (classLoader == null) {
throw new IllegalArgumentException("you need to provide an injectable class loader to use this class loading strategy");
}
if (!(classLoader instanceof IAndroidInjectableClassLoader)) {
throw new IllegalArgumentException("class loader is not injectable");
}
IAndroidInjectableClassLoader injectableClassLoader = (IAndroidInjectableClassLoader) classLoader;
injectableClassLoader.injectDex(dexFile, null);
Map<TypeDescription, Class<?>> loadedTypes = new HashMap<TypeDescription, Class<?>>();
for (TypeDescription typeDescription : typeDescriptions) {
try {
loadedTypes.put(typeDescription, Class.forName(typeDescription.getName(), false, classLoader));
} catch (ClassNotFoundException exception) {
throw new IllegalStateException("Cannot load " + typeDescription, exception);
}
}
return loadedTypes;
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
/*
* QAuxiliary - An Xposed module for QQ/TIM
* Copyright (C) 2019-2024 QAuxiliary developers
* https://github.com/cinit/QAuxiliary
*
* This software is an opensource software: you can redistribute it
* and/or modify it under the terms of the General Public License
* as published by the Free Software Foundation; either
* version 3 of the License, or any later version as published
* by QAuxiliary contributors.
*
* This software is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the General Public License for more details.
*
* You should have received a copy of the General Public License
* along with this software.
* If not, see
* <https://github.com/cinit/QAuxiliary/blob/master/LICENSE.md>.
*/

package net.bytebuddy.android;

import androidx.annotation.NonNull;
import androidx.annotation.Nullable;

public interface IAndroidInjectableClassLoader {

void injectDex(@NonNull byte[] dexBytes, @Nullable String dexName) throws IllegalArgumentException;

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
/*
* QAuxiliary - An Xposed module for QQ/TIM
* Copyright (C) 2019-2024 QAuxiliary developers
* https://github.com/cinit/QAuxiliary
*
* This software is an opensource software: you can redistribute it
* and/or modify it under the terms of the General Public License
* as published by the Free Software Foundation; either
* version 3 of the License, or any later version as published
* by QAuxiliary contributors.
*
* This software is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the General Public License for more details.
*
* You should have received a copy of the General Public License
* along with this software.
* If not, see
* <https://github.com/cinit/QAuxiliary/blob/master/LICENSE.md>.
*/

package net.bytebuddy.android;

import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import dalvik.system.BaseDexClassLoader;
import io.github.qauxv.util.hookimpl.InMemoryClassLoaderHelper;
import java.io.File;
import java.util.Objects;

public class InjectableDexClassLoader extends BaseDexClassLoader implements IAndroidInjectableClassLoader {

/**
* Constructs an instance. Note that all the *.jar and *.apk files from {@code dexPath} might be first extracted in-memory before the code is loaded. This
* can be avoided by passing raw dex files (*.dex) in the {@code dexPath}.
*
* @param dexPath the list of jar/apk files containing classes and resources, delimited by {@code File.pathSeparator}, which defaults to
* {@code ":"} on Android.
* @param optimizedDirectory this parameter is deprecated and has no effect since API level 26.
* @param librarySearchPath the list of directories containing native libraries, delimited by {@code File.pathSeparator}; may be {@code null}
* @param parent the parent class loader
*/
public InjectableDexClassLoader(String dexPath, File optimizedDirectory, String librarySearchPath, ClassLoader parent) {
super(dexPath, optimizedDirectory, librarySearchPath, parent == null ? System.class.getClassLoader() : parent);
}

public InjectableDexClassLoader(ClassLoader parent) {
super("", null, null, parent);
}

@Override
public void injectDex(@NonNull byte[] dexBytes, @Nullable String dexName) throws IllegalArgumentException {
Objects.requireNonNull(dexBytes, "dexBytes");
InMemoryClassLoaderHelper.INSTANCE.injectDexToClassLoader(this, dexBytes, dexName);
}

}

0 comments on commit f7b7ecd

Please sign in to comment.