是否可以通过使用Java Reflection从注入的DEX中获取app的类对象?

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了是否可以通过使用Java Reflection从注入的DEX中获取app的类对象?相关的知识,希望对你有一定的参考价值。

我开始学习Java,我在我的android设备上安装了一个应用程序,这个应用程序只有一个MainActivity(com.example.myapplication.MainActivity)。这个app只有一个MainActivity(com.example.myapplication.MainActivity)。在MainActivity里面有一个public(doSomething1)和一个private(doSomething2)方法。假设我没有应用程序的源文件。我要做的是通过使用Java Reflection从另一个应用程序(DEX)调用这些方法。下面是执行运行时Dex注入的代码。


JNIEnv* (*getJNIEnv)();

/**
* Inject the DEX file and execute the method.
* @param dexPath: The dex file to inject
* @param dexOptDir: Cache path
* @param className: Class name to be executed after injection
* @param methodName: Method name to execute
* @param argc: number of arguments
* @param argv: arguments
* @return
*/

 int invoke_dex_method(const char* dexPath, const char* dexOptDir, const char* className, const char* methodName, int argc, char *argv[]) 

      LOGD("dexPath = %s, dexOptDir = %s, className = %s, methodName = %s\n", dexPath, dexOptDir, className, methodName);
      // Acquisition of JNIEnv
      void* handle = dlopen("/system/lib/libandroid_runtime.so", RTLD_NOW);
      LOGD("dlopen = %x, %s\n", handle, strerror(errno));
      getJNIEnv = dlsym(handle, "_ZN7android14AndroidRuntime9getJNIEnvEv");
      LOGD("dlsym = %x, %s\n", getJNIEnv, strerror(errno));
      JNIEnv* env = getJNIEnv();
      LOGD("JNIEnv = %x\n", env);

      // Call getSystemClassLoader of ClassLoader to get ClassLoader of current process
      jclass classloaderClass = (*env)->FindClass(env,"java/lang/ClassLoader");
      jmethodID getsysloaderMethod = (*env)->GetStaticMethodID(env,classloaderClass, "getSystemClassLoader", "()Ljava/lang/ClassLoader;");
      jobject loader = (*env)->CallStaticObjectMethod(env, classloaderClass, getsysloaderMethod);
      LOGD("loader = %x\n", loader);

      // Read the dex file with DexClassLoader for processing with the current ClassLoader
      jstring dexpath = (*env)->NewStringUTF(env, dexPath);
      jstring dex_odex_path = (*env)->NewStringUTF(env,dexOptDir);
      jclass dexLoaderClass = (*env)->FindClass(env,"dalvik/system/DexClassLoader");
      jmethodID initDexLoaderMethod = (*env)->GetMethodID(env, dexLoaderClass, "<init>", "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/ClassLoader;)V");
      jobject dexLoader = (*env)->NewObject(env, dexLoaderClass, initDexLoaderMethod,dexpath,dex_odex_path,NULL,loader);
      LOGD("dexLoader = %x\n", dexLoader);

      // Load code to execute using DexClassLoader
      jmethodID findclassMethod = (*env)->GetMethodID(env,dexLoaderClass,"findClass","(Ljava/lang/String;)Ljava/lang/Class;");
      jstring javaClassName = (*env)->NewStringUTF(env,className);
      jclass javaClientClass = (*env)->CallObjectMethod(env,dexLoader,findclassMethod,javaClassName);
      if (!javaClientClass) 
              LOGD("Failed to load target class %s\n", className);
              printf("Failed to load target class %s\n", className);
              return -1;
      

      // Get the method to inject
      jmethodID start_inject_method = (*env)->GetStaticMethodID(env, javaClientClass, methodName, "()V");
      if (!start_inject_method) 
              LOGD("Failed to load target method %s\n", methodName);
              printf("Failed to load target method %s\n", methodName);
              return -1;
      

      // Execute method (this method must be a public static void method)
      (*env)->CallStaticVoidMethod(env,javaClientClass,start_inject_method);
      return 0;

上面的代码工作得很好,没有任何问题,方法(第4个param)被调用了。下面是Dex文件中Java类的代码。

public class HookTool 
      public static final String TAG = "INJECT";

      public static void dexInject() throws ClassNotFoundException, IllegalAccessException 
              Log.d(TAG, "This is dex code. Start hooking process in Java world.");

              try 
                      Class<?> activityClass = Class.forName("com.example.myapplication.MainActivity");

                      Method method2 = activityClass.getMethod("doSomething1");
                      Method method1 = activityClass.getDeclaredMethod("doSomething2");

               catch (ClassNotFoundException | NoSuchMethodException e) 
                      e.printStackTrace();
              
      


从上面的代码中我没有得到预期的结果。下面是打印的logcat结果。

D/INJECT   (15393): This is dex code. Start hooking process in Java world.
W/System.err(15393): java.lang.ClassNotFoundException: com.example.myapplication.MainActivity
W/System.err(15393):       at java.lang.Class.classForName(Native Method)
W/System.err(15393):       at java.lang.Class.forName(Class.java:309)
W/System.err(15393):       at java.lang.Class.forName(Class.java:273)
W/System.err(15393):       at net.cimadai.hookerApp.HookTool.dexInject(HookTool.java:43)
W/System.err(15393):       at android.os.MessageQueue.nativePollOnce(Native Method)
W/System.err(15393):       at android.os.MessageQueue.next(MessageQueue.java:143)
W/System.err(15393):       at android.os.Looper.loop(Looper.java:122)
W/System.err(15393):       at android.app.ActivityThread.main(ActivityThread.java:5254)
W/System.err(15393):       at java.lang.reflect.Method.invoke(Native Method)
W/System.err(15393):       at java.lang.reflect.Method.invoke(Method.java:372)
W/System.err(15393):       at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:905)
W/System.err(15393):       at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:700)
W/System.err(15393): Caused by: java.lang.ClassNotFoundException: Didn't find class "com.example.myapplication.MainActivity" on path: DexPathList[[zip file "/data/local/tmp/app-debug.apk"],nativeLibraryDirectories=[/vendor/lib, /system/lib]]
W/System.err(15393):       at dalvik.system.BaseDexClassLoader.findClass(BaseDexClassLoader.java:56)
W/System.err(15393):       at java.lang.ClassLoader.loadClass(ClassLoader.java:511)
W/System.err(15393):       at java.lang.ClassLoader.loadClass(ClassLoader.java:469)
W/System.err(15393):       ... 12 more
W/System.err(15393):       Suppressed: java.lang.ClassNotFoundException: Didn't find class "com.example.myapplication.MainActivity" on path: DexPathList[[directory "."],nativeLibraryDirectories=[/vendor/lib, /system/lib]]
W/System.err(15393):                       at dalvik.system.BaseDexClassLoader.findClass(BaseDexClassLoader.java:56)
W/System.err(15393):                       at java.lang.ClassLoader.loadClass(ClassLoader.java:511)
W/System.err(15393):                       at java.lang.ClassLoader.loadClass(ClassLoader.java:504)
W/System.err(15393):                       ... 13 more
W/System.err(15393):                       Suppressed: java.lang.ClassNotFoundException: com.example.myapplication.MainActivityW/System.err(15393):                                       at java.lang.Class.classForName(Native Method)
W/System.err(15393):                                       at java.lang.BootClassLoader.findClass(ClassLoader.java:781)
W/System.err(15393):                                       at java.lang.BootClassLoader.loadClass(ClassLoader.java:841)
W/System.err(15393):                                       at java.lang.ClassLoader.loadClass(ClassLoader.java:504)
W/System.err(15393):                                       ... 14 more
W/System.err(15393):                       Caused by: java.lang.NoClassDefFoundError: Class not found using the boot class loader; no stack available

所以问题是

  1. 是否可以通过使用Java Reflection从另一个应用中调用方法?
  2. 如果不可以,有什么方法可以让这个工作顺利进行?

你能不能有人给我解释一下?

先谢谢你

答案

我找到了dex注入后获得顶级活动的方法。下面是代码。

public static Activity getTopActivity() 
              Activity topActivity = null;
              try 
                      @SuppressLint("PrivateApi") Class activityThreadClass = Class.forName("android.app.ActivityThread");
                      @SuppressLint("DiscouragedPrivateApi") Method getATMethod = activityThreadClass.getDeclaredMethod("currentActivityThread");
                      Field activitiesField = activityThreadClass.getDeclaredField("mActivities");
                      Object activityThread = getATMethod.invoke(null);
                      activitiesField.setAccessible(true);
                      Object activityClientRecord;
                      if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) 
                              ArrayMap activities = (ArrayMap) activitiesField.get(activityThread);
                              activityClientRecord = activities.valueAt(0);
                      else
                              HashMap activities = (HashMap) activitiesField.get(activityThread);
                              activityClientRecord = activities.values();
                      
                      @SuppressLint("PrivateApi") Class activityClientRecordClass = Class.forName("android.app.ActivityThread$ActivityClientRecord");
                      Field activityField = activityClientRecordClass.getDeclaredField("activity");
                      activityField.setAccessible(true);
                      topActivity = (Activity) activityField.get(activityClientRecord);

               catch (ClassNotFoundException e) 
                      e.printStackTrace();
               catch (NoSuchMethodException e) 
                      e.printStackTrace();
               catch (InvocationTargetException e) 
                      e.printStackTrace();
               catch (IllegalAccessException e) 
                      e.printStackTrace();
               catch (NoSuchFieldException e) 
                      e.printStackTrace();
              
              return topActivity;
      

上面的代码会给你最顶级的活动的实例。

以上是关于是否可以通过使用Java Reflection从注入的DEX中获取app的类对象?的主要内容,如果未能解决你的问题,请参考以下文章

从注冊流程 分析怎样安全退出多个Activity 多种方式(附DEMO)

开始使用Reflection

java 使用Reflection访问私有方法

Java Reflection

Java Reflection 概述

Swift - 反射(Reflection)的介绍与使用样例(附KVC介绍)