DEX加载器代码解析

admin 2023年5月2日01:01:43评论34 views字数 2596阅读8分39秒阅读模式


    dex是Android系统下APK存放代码的一种文件格式,按照其独有的格式将代码与代码用到的变量进行布局,类似于JAR文件。dex所存放的代码格式是Dalvik字节码,它是Android平台所识别并执行的字节码,与JAR中使用的JVM字节码不互相兼容。

    dex文件除了直接被编译进APK中以外还可以通过动态加载来执行其中的代码。这使得可以做到很多事情,如:APK瘦身、热修复、插件化、应用加固,而逆向中对dex加载部分主要涉及的是应用加固。这意味着它可以将部分重要的代码加密进dex并动态加载,或在线下载并加载。

dex文件加载所需的类是DexClassLoader,它的原型为:

public DexClassLoader(String dexPath, String optimizedDirectory, String librarySearchPath, ClassLoader parent)
  • dexPath:描述dex文件的路径

  • optimizedDirectory:描述存放优化后的 dex的路径

  • libraryPath:描述存放native 库的路径,可以为空。

  • parent:描述父加载器

加载成功后将会获得一个类加载器,通过这个类加载器可以得到dex中的类。

但在这之前需要获得一个dex,它将装载我们希望动态加载的代码。

现在先新建一个空界面的APP:

DEX加载器代码解析

其中只包含最基础的代码,方便测试Dex加载器。

随后我们找一个不会编译进APP的路径做dex

DEX加载器代码解析

编写如下代码,用一个弹出消息假设它是一个需要被保护的代码, 要注意包路径要与文件夹路径要一致。

DEX加载器代码解析

随后着手生成dex:

  • 由于dex代码中有android的东西因此需要Android SDK。

  • 找到AndroidSDK的目录platformsandroid-你希望的版本android.jar。

  • 将它改为zip文件后解压将其作为classpath。

  • 另外还需要确认jdk和AndroidSDK的环境变量是否配置正确,然后就可以开始生成。

  • 使用命令 javac -classpath 解压AndroidSDK的路径 -source 源java版本 -target 目标java版本。

DEMO:

E:workDexLoaderappsrcdex> javac -classpath D:AndroidSDKplatformsandroid-28androidextra -target 8 -source 8 comexampledexloaderdexSecurity.java

随后会在comexampledexloaderdex目录下生成.class文件。

继续将class打包到dex中,使用命令“dx --dex --output Security.dex .”将当前目录下的所有class打包进dex,并生成 Security.dex文件。

现在我们需要在代码中调用这个dex。

可以将dex放在任何地方来加载,这里演示在APP缓存目录加载。

我准备把dex放在资源目录下,这也是加壳程序经常做的事情。

新建一个assets目录,并将dex放进去,随后我们可以通过方法getAssets获得这个目录中的文件。

DEX加载器代码解析

DEX加载器代码解析    

DexClassLoader的参数需要一个文件目录,而assets不能满足这个参数,因此还需要将assets中的dex文件拷贝到一个临时的目录中。

APP有两个缓存目录,在/data/user/0/包名/cache与/data/user/0/包名/code_cache,分别为通用缓存与代码缓存。可能随着Android版本变化而变化,因此我们需要用代码来获得这些地址。

MainActivity 继承自Context因此可以通过getCacheDir与getCodeCacheDir获得。

得到缓存路径

File cacheDir = this.getCacheDir();File securityCachePath = new File(cacheDir, "Security.dex");

将文件从assets拷贝到临时目录

try (InputStream resourceDex = getAssets().open("Security.dex")) {    try (FileOutputStream cacheDex = new FileOutputStream(securityCachePath)) {        byte[] buffer = new byte[1024];        int length;        while ((length = resourceDex.read(buffer)) > 0) {            cacheDex.write(buffer, 0, length);        }    }} catch (IOException e) {    throw new RuntimeException(e);}

使用DexClassLoader从缓存目录中加载dex,并将优化好的dex放入代码缓存,设置父类类加载器为当前类加载器。

DexClassLoader loader = new DexClassLoader(        securityCachePath.getAbsolutePath(),        this.getCodeCacheDir().getAbsolutePath(),        null,        getClassLoader());

通过加载器的方法loadClass可以获得dex中的某个类。随后通过反射的方式就可以新建实例与调用方法了。

try {    Class<?> securityClass = loader.loadClass("com.example.dexloader.dex.Security");    Method securityRunMethod = securityClass.getDeclaredMethod("Run", Context.class);    securityRunMethod.invoke(null, this);} catch (ClassNotFoundException | IllegalAccessException |         NoSuchMethodException | InvocationTargetException e) {    throw new RuntimeException(e);}

实测结果:

DEX加载器代码解析

之后介绍xposed与frida后将会详细介绍DEX类加载器的底层实现与HOOK加载器脱壳。

原文始发于微信公众号(锋刃科技):DEX加载器代码解析

  • 左青龙
  • 微信扫一扫
  • weinxin
  • 右白虎
  • 微信扫一扫
  • weinxin
admin
  • 本文由 发表于 2023年5月2日01:01:43
  • 转载请保留本文链接(CN-SEC中文网:感谢原作者辛苦付出):
                   DEX加载器代码解析http://cn-sec.com/archives/1698412.html

发表评论

匿名网友 填写信息