在Android中动态加载类

在Android中动态加载类

较为复杂

编写Hello类

package main;
public class Hello {
    public static void main(String[] args) {
        System.out.println("Hello World");
    }
    public String welcome(){
        return "zsh";
    }
}

运行

java Hello.java

编译运行

javac Hello.java
# 将Hello.class 放入main目录
java main.Hello

or

javac -d . Hello.java
java main.Hello

使用R8

D:\SoftWare\Android\Sdk\build-tools\36.0.0> ./d8 C:\Users\29051\Desktop\main\Hello.class --output C:\Users\29051\Desktop\main.zip

push到cache目录

/data/data/io.github.okhttplearn/cache/classes.dex

命令非常短少

Usage: d8 [options] [@<argfile>] <input-files>
 where <input-files> are any combination of dex, class, zip, jar, or apk files
 and each <argfile> is a file containing additional arguments (one per line)
 and options are:
  --debug                 # Compile with debugging information (default).
  --release               # Compile without debugging information.
  --output <file>         # Output result in <file>.
                          # <file> must be an existing directory or a zip file.
  --globals <file>        # Global synthetics <file> from a previous intermediate compilation.
                          # The <file> may be either a zip-archive of global synthetics or the
                          # global-synthetic files directly.
  --globals-output <file> # Output global synthetics in <file>.
                          # <file> must be an existing directory or a non-existent zip archive.
  --lib <file|jdk-home>   # Add <file|jdk-home> as a library resource.
  --classpath <file>      # Add <file> as a classpath resource.
  --min-api <number>      # Minimum Android API level compatibility (default: 1).
  --pg-map <file>         # Use <file> as a mapping file for distribution.
  --intermediate          # Compile an intermediate result intended for later
                          # merging.
  --file-per-class        # Produce a separate dex file per class.
                          # Synthetic classes are in their own file.
  --file-per-class-file   # Produce a separate dex file per input .class file.
                          # Synthetic classes are with their originating class.
  --no-desugaring         # Force disable desugaring.
  --desugared-lib <file>  # Specify desugared library configuration.
                          # <file> is a desugared library configuration (json).
  --desugared-lib-pg-conf-output <file>
                          # Output the Proguard configuration for L8 to <file>.
  --main-dex-rules <file> # Proguard keep rules for classes to place in the
                          # primary dex file.
  --main-dex-list <file>  # List of classes to place in the primary dex file.
  --main-dex-list-output <file>
                          # Output resulting main dex list in <file>.
  --force-enable-assertions[:[<class name>|<package name>...]]
  --force-ea[:[<class name>|<package name>...]]
                          # Forcefully enable javac generated assertion code.
  --force-disable-assertions[:[<class name>|<package name>...]]
  --force-da[:[<class name>|<package name>...]]
                          # Forcefully disable javac generated assertion code.
                          # This is the default handling of javac assertion code
                          # when generating DEX file format.
  --force-passthrough-assertions[:[<class name>|<package name>...]]
  --force-pa[:[<class name>|<package name>...]]
                          # Don't change javac generated assertion code. This
                          # is the default handling of javac assertion code when
                          # generating class file format.
  --force-assertions-handler:<handler method>[:[<class name>|<package name>...]]
  --force-ah:<handler method>[:[<class name>|<package name>...]]
                          # Change javac and kotlinc generated assertion code
                          # to invoke the method <handler method> with each
                          # assertion error instead of throwing it.
                          # The <handler method> is specified as a class name
                          # followed by a dot and the method name.
                          # The handler method must take a single argument of
                          # type java.lang.Throwable and have return type void.
  --thread-count <number> # Use <number> of threads for compilation.
                          # If not specified the number will be based on
                          # heuristics taking the number of cores into account.
  --map-diagnostics[:<type>] <from-level> <to-level>
                          # Map diagnostics of <type> (default any) reported as
                          # <from-level> to <to-level> where <from-level> and
                          # <to-level> are one of 'info', 'warning', or 'error'
                          # and the optional <type> is either the simple or
                          # fully qualified Java type name of a diagnostic.
                          # If <type> is unspecified, all diagnostics at
                          # <from-level> will be mapped.
                          # Note that fatal compiler errors cannot be mapped.
  --android-platform-build
                          # Compile as a platform build where the runtime/bootclasspath
                          # is assumed to be the version specified by --min-api.
  --art-profile <input> <output>
                          # Rewrite human readable ART profile read from <input> and write to <output>.
  --startup-profile <file>
                          # Startup profile <file> to use for dex layout.
  --version               # Print the version of d8.
  --help                  # Print this message.

方法调用

val dexFile = File(context.cacheDir, "classes.dex")
val dexClassLoader = DexClassLoader(dexFile.absolutePath, null, null, context.classLoader)
val loadedClass: Class<*> = dexClassLoader.loadClass("main.Hello")
val instance: Any = loadedClass.getDeclaredConstructor().newInstance()
val welcomeMethod: Method = loadedClass.getMethod("welcome")
val result: String? = welcomeMethod.invoke(instance) as? String
Log.i(TAG, "WorldScreen -> result: $result, codeCacheDir: ${context.codeCacheDir}")
val mainMethod: Method = loadedClass.getMethod("main", Array<String>::class.java)
val result1 = mainMethod.invoke(instance, arrayOf<String>("zsh1", "zsh2"))
Log.i(TAG, "WorldScreen -> result1: $result1")

你会惊奇的发现,niubility
你会发现zygote64做了什么奇怪的动作.

  • MD5校验
  • 输出The ClassLoaderContext is a special shared library.
  • Increasing code cache capacity to 1024KB

而且生成了一个奇怪的目录oat.
image
这些文件有什么作用呢?
想要解密这些,还是相当复杂的需要了解art虚拟机的知识.

加载Activity

HomeActivity.java
源码

package io.github.okhttplearn;

import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
import android.widget.TextView;

import androidx.activity.ComponentActivity;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;

public class HomeActivity extends ComponentActivity {
    public static void startActivity(@NonNull Context context){
        context.startActivity(new Intent(context, HomeActivity.class));
    }
    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        final var textView = new TextView(this);
        textView.setText("Hello World");
        setContentView(textView);
    }
}

编译为class

javac -d . --class-path "D:\SoftWare\gradle\caches\8.13\transforms\574a8184410d7b8ae96f1a300e7be7af\transformed\activity-1.12.0-beta01\jars\classes.jar;D:\SoftWare\Android\Sdk\platforms\android-36\android.jar;D:\SoftWare\gradle\caches\modules-2\files-2.1\androidx.annotation\annotation-jvm\1.9.1\b17951747e38bf3986a24431b9ba0d039958aa5f\annotation-jvm-1.9.1.jar;D:\SoftWare\gradle\caches\modules-2\files-2.1\androidx.lifecycle\lifecycle-common-jvm\2.10.0-beta01\8cfb34614ace574aa320aa599cd8c1c38f7af268\lifecycle-common-jvm-2.10.0-beta01.jar;D:\SoftWare\gradle\caches\8.13\transforms\b4ccea56d1a29eae461b218513391a62\transformed\lifecycle-viewmodel\jars\classes.jar;D:\SoftWare\gradle\caches\8.13\transforms\bb8cbac7eee9253b3febd6aaae993046\transformed\savedstate\jars\classes.jar;D:\SoftWare\gradle\caches\8.13\transforms\63e23a81dd393afa409023b926aac907\transformed\navigationevent\jars\classes.jar;D:\SoftWare\gradle\caches\8.13\transforms\a3d3a03ff93f54630cda5b7e612d3b45\transformed\core-1.16.0\jars\classes.jar;D:\SoftWare\gradle\caches\modules-2\files-2.1\org.jetbrains.kotlin\kotlin-stdlib\2.2.21\fa374a986e128314c3db00a20aae55f72a258511\kotlin-stdlib-2.2.21.jar" HomeActivity.java

编译为dex

./d8 --intermediate --globals-output "C:\Users\29051\Desktop\gs" --file-per-class-file --lib  "D:\SoftWare\gradle\caches\8.13\transforms\574a8184410d7b8ae96f1a300e7be7af\transformed\activity-1.12.0-beta01\jars\classes.jar;D:\SoftWare\Android\Sdk\platforms\android-36\android.jar;D:\SoftWare\gradle\caches\modules-2\files-2.1\androidx.annotation\annotation-jvm\1.9.1\b17951747e38bf3986a24431b9ba0d039958aa5f\annotation-jvm-1.9.1.jar;D:\SoftWare\gradle\caches\modules-2\files-2.1\androidx.lifecycle\lifecycle-common-jvm\2.10.0-beta01\8cfb34614ace574aa320aa599cd8c1c38f7af268\lifecycle-common-jvm-2.10.0-beta01.jar;D:\SoftWare\gradle\caches\8.13\transforms\b4ccea56d1a29eae461b218513391a62\transformed\lifecycle-viewmodel\jars\classes.jar;D:\SoftWare\gradle\caches\8.13\transforms\bb8cbac7eee9253b3febd6aaae993046\transformed\savedstate\jars\classes.jar;D:\SoftWare\gradle\caches\8.13\transforms\63e23a81dd393afa409023b926aac907\transformed\navigationevent\jars\classes.jar;D:\SoftWare\gradle\caches\8.13\transforms\a3d3a03ff93f54630cda5b7e612d3b45\transformed\core-1.16.0\jars\classes.jar;D:\SoftWare\gradle\caches\modules-2\files-2.1\org.jetbrains.kotlin\kotlin-stdlib\2.2.21\fa374a986e128314c3db00a20aae55f72a258511\kotlin-stdlib-2.2.21.jar" C:\Users\29051\Desktop\io\github\okhttplearn\HomeActivity.class --output C:\Users\29051\Downloads\io.zip

注入DexList

路漫漫其修远兮,吾将上下而求索!

posted @ 2025-12-07 22:23  爱情丶眨眼而去  阅读(4)  评论(0)    收藏  举报