首页
社区
课程
招聘
[原创]Android一代加固学习史(1)
发表于: 2024-9-12 21:33 1983

[原创]Android一代加固学习史(1)

2024-9-12 21:33
1983

Android一代加固学习史是一系列文章,将详细介绍Android第一代壳的相关原理,从源码分析,到代码实现。体现了理论结合实战的方法,帮助初学者理解学习。
在本系列中,使用了3种方法加载dex文件(落地加载),从难到易,逐步理解。

这是第一篇文章,稍微会有一些枯燥,主要介绍一些在第一代加固会用到的相关类。初学者可以对其有个大概的印象,建立相关类的体系结构,后面都会用到的哦

这里借用寒冰老师的图,主要是不想再画了(
图片描述
该图介绍了Android ClassLoader相关的继承关系
这里我们只需要记住下面5点

c8aK9s2c8@1M7q4)9K6b7g2)9J5c8W2)9J5c8X3q4G2M7%4m8^5M7X3g2X3i4K6u0W2j5$3!0E0i4K6u0r3j5h3&6V1M7X3!0A6k6q4)9J5k6o6p5K6i4K6u0W2x3q4)9J5k6e0m8Q4y4h3k6J5x3#2)9J5c8Y4S2J5k6h3k6Q4x3V1k6D9K9h3u0U0L8%4u0W2i4K6u0r3k6r3q4D9N6X3W2C8i4K6u0r3M7%4u0U0i4K6u0r3L8h3q4A6L8W2)9J5c8X3A6S2N6X3q4Q4x3V1k6V1j5h3I4$3K9h3E0Q4x3V1k6K6P5i4y4@1k6h3#2Q4x3V1k6p5k6i4S2o6L8r3q4K6M7@1I4G2j5h3c8W2M7W2)9J5k6h3A6S2N6X3p5`.
图片描述

哦豁,我们发现,大哥DexClassLoader啥都没实现,直接用父类BaseDexClassLoader的构造函数,并传递相关的参数(源码有解释),其他兄弟是不是也是这样的呢?

我们来看看二哥 PathClassLoader
ad4K9s2c8@1M7q4)9K6b7g2)9J5c8W2)9J5c8X3q4G2M7%4m8^5M7X3g2X3i4K6u0W2j5$3!0E0i4K6u0r3j5h3&6V1M7X3!0A6k6q4)9J5k6o6p5K6i4K6u0W2x3q4)9J5k6e0m8Q4y4h3k6J5x3#2)9J5c8Y4S2J5k6h3k6Q4x3V1k6D9K9h3u0U0L8%4u0W2i4K6u0r3k6r3q4D9N6X3W2C8i4K6u0r3M7%4u0U0i4K6u0r3L8h3q4A6L8W2)9J5c8X3A6S2N6X3q4Q4x3V1k6V1j5h3I4$3K9h3E0Q4x3V1k6K6P5i4y4@1k6h3#2Q4x3V1k6b7j5i4c8Z5b7$3I4S2M7%4y4x3L8$3q4V1k6i4u0Q4x3X3g2B7j5i4k6S2
图片描述
PathClassLoader 是Android默认使用的类加载器,APP中的四大组件等类由其加载,在后面我们也会用代码来验证。
跟大哥一样的性格,把重担都交父类BaseDexClassLoader,传递参数并调用父类构造函数

e97K9s2c8@1M7q4)9K6b7g2)9J5c8W2)9J5c8X3q4G2M7%4m8^5M7X3g2X3i4K6u0W2j5$3!0E0i4K6u0r3j5h3&6V1M7X3!0A6k6q4)9J5k6o6p5K6i4K6u0W2x3q4)9J5k6e0m8Q4y4h3k6J5x3#2)9J5c8Y4S2J5k6h3k6Q4x3V1k6D9K9h3u0U0L8%4u0W2i4K6u0r3k6r3q4D9N6X3W2C8i4K6u0r3M7%4u0U0i4K6u0r3L8h3q4A6L8W2)9J5c8X3A6S2N6X3q4Q4x3V1k6V1j5h3I4$3K9h3E0Q4x3V1k6K6P5i4y4@1k6h3#2Q4x3V1k6u0L8V1#2W2L8h3!0J5P5f1c8W2P5p5y4D9j5i4y4K6e0r3!0S2k6r3g2J5i4K6u0W2K9X3q4$3j5b7`.`.
图片描述
InMemoryDexClassLoader(Android8+)可以从内存中加载dex
不再废话,重担都交父类BaseDexClassLoader,传递参数并调用父类构造函数
下面介绍重量级选手,处理大部分逻辑的类

6e9K9s2c8@1M7q4)9K6b7g2)9J5c8W2)9J5c8X3q4G2M7%4m8^5M7X3g2X3i4K6u0W2j5$3!0E0i4K6u0r3j5h3&6V1M7X3!0A6k6q4)9J5k6o6p5K6i4K6u0W2x3q4)9J5k6e0m8Q4y4h3k6J5x3#2)9J5c8Y4S2J5k6h3k6Q4x3V1k6D9K9h3u0U0L8%4u0W2i4K6u0r3k6r3q4D9N6X3W2C8i4K6u0r3M7%4u0U0i4K6u0r3L8h3q4A6L8W2)9J5c8X3A6S2N6X3q4Q4x3V1k6V1j5h3I4$3K9h3E0Q4x3V1k6K6P5i4y4@1k6h3#2Q4x3V1k6n7j5i4y4W2c8r3g2^5b7$3I4S2M7%4y4x3L8$3q4V1k6i4u0Q4x3X3g2B7j5i4k6S2
我们先看一下他的构造函数,他的三个儿子都在调用

图片描述
可以看到BaseDexClassLoader有多个构造函数,我们向下找到

观察一下参数,发现跟子类传递的十分相同。

这么多代码,其实我们只用关注一行代码,其他的都可以不管。

可以发现将相关的dexPath传给了DexPathList类。
那么,我们接下来需要跟踪 DexPathList 类,看看他在搞什么。
这里区分一下:DexPathList 和 DexClassLoader 是不一样的,不要看混了哈

a0dK9s2c8@1M7q4)9K6b7g2)9J5c8W2)9J5c8X3q4G2M7%4m8^5M7X3g2X3i4K6u0W2j5$3!0E0i4K6u0r3j5h3&6V1M7X3!0A6k6q4)9J5k6o6p5K6i4K6u0W2x3q4)9J5k6e0m8Q4y4h3k6J5x3#2)9J5c8Y4S2J5k6h3k6Q4x3V1k6D9K9h3u0U0L8%4u0W2i4K6u0r3k6r3q4D9N6X3W2C8i4K6u0r3M7%4u0U0i4K6u0r3L8h3q4A6L8W2)9J5c8X3A6S2N6X3q4Q4x3V1k6V1j5h3I4$3K9h3E0Q4x3V1k6K6P5i4y4@1k6h3#2Q4x3V1k6p5k6i4S2b7j5i4c8Z5e0r3W2K6N6q4)9J5k6h3A6S2N6X3p5`.
DexPathList是DexClassLoader和BaseDexClassLoader等类加载器用于处理DEX文件路径的一个内部类。当使用DexClassLoader或BaseDexClassLoader加载DEX文件时,DexPathList起到了关键的作用。
DexPathList在BaseDexClassLoader的构造函数中被创建。构造DexPathList时,需要提供DEX文件的路径、优化目录、库路径以及父类加载器等参数。

内容代码那么多,我们只需要关注他的一个成员:
DexPathList有一个私有的final成员变量dexElements,是一个Element数组,包含了所有DEX文件的Element对象,每个Element对象对应一个DEX文件。
到这里,有没有读者恍然大悟,为什么很多文章都指向了 Element 对象。这里就是原因,一个Element对象对应一个Dex文件。
Dex文件是我们加固的对象,分析逻辑的对象,能拿到Dex文件,还害怕不能分析吗?
所以,Element对象对我们来说很重要
图片描述

那么,与Element对象大大相关的方法就是makeDexElements()
在DexPathList的构造函数中,会调用makeDexElements()方法来加载DEX文件。这个方法会遍历提供的DEX文件路径列表,并为每个DEX文件创建一个Element对象,然后将这些Element对象添加到dexElements数组中。

当类加载器需要加载一个类时,会通过DexPathList的loadClass()方法来实现。这个方法会遍历dexElements数组中的每个Element对象,并尝试从对应的DEX文件中加载类。一旦找到需要加载的类,就会返回该类的Class对象。
同时,这里需要大家记住: Element 类是DexPathList的内部类,在后面的学习中,我们将使用反射来获取Element对象,获取的方法会有一些不同。

3f0K9s2c8@1M7q4)9K6b7g2)9J5c8W2)9J5c8X3q4F1k6s2u0G2K9h3c8^5M7X3g2X3i4K6u0W2j5$3!0E0i4K6u0r3z5g2)9J5k6e0m8Q4x3X3f1H3i4K6g2X3M7U0y4Q4x3V1k6^5M7X3g2X3i4K6u0r3L8r3W2T1j5$3!0J5k6g2)9J5c8X3c8S2L8s2k6A6K9#2)9J5c8Y4y4J5j5#2)9J5c8X3#2S2K9h3&6Q4x3V1k6B7j5i4k6S2i4K6u0r3k6r3q4D9N6X3W2C8i4K6u0r3M7%4W2K6N6r3g2E0i4K6u0r3c8r3g2^5f1r3q4@1K9p5I4A6M7%4c8Q4x3X3g2B7j5i4k6S2i4K6t1K6y4U0l9$3
在DexPathList的构造函数中,会调用makeDexElements()方法来加载DEX文件。这个方法会遍历提供的DEX文件路径列表,并为每个DEX文件创建一个Element对象,然后将这些Element对象添加到dexElements数组中。
图片描述

Element类有一个很重要的成员

DexFile类,相信读者们知道,DexFile代表了一个dex文件,在脱壳加固我们都会用到dex文件。

Cross Reference: /libcore/dalvik/src/main/java/dalvik/system/DexFile.java (androidxref.com)

很重要的方法:getClassNameList() 通过这个方法获得 ClassLoader加载的所有类名列表

在介绍完这些常见的类(加固会用到)
我们再来学习他们的祖爷爷:ClassLoader 抽象基类

6e0K9s2c8@1M7q4)9K6b7g2)9J5c8W2)9J5c8X3q4G2M7%4m8^5M7X3g2X3i4K6u0W2j5$3!0E0i4K6u0r3j5h3&6V1M7X3!0A6k6q4)9J5k6o6p5K6i4K6u0W2x3q4)9J5k6e0m8Q4y4h3k6J5x3#2)9J5c8Y4S2J5k6h3k6Q4x3V1k6D9K9h3u0U0L8%4u0W2i4K6u0r3L8$3A6D9N6h3&6A6i4K6u0r3M7%4u0U0i4K6u0r3L8h3q4A6L8W2)9J5c8X3A6S2N6X3q4Q4x3V1k6B7j5i4k6S2i4K6u0r3L8r3q4F1k6#2)9J5c8V1y4D9j5i4y4K6e0r3!0S2k6r3g2J5i4K6u0W2K9X3q4$3j5b7`.`.
CLassLoader类包含了许多公共的方法和属性,这些方法和属性,其子类都可以使用,比如:获取父加载器的parent方法
图片描述

还有一些属性,当然在后面文章中,我们遇到的时候再去学习,今天这篇文章先构建一个大概的逻辑。

图片描述

输出日志

验证了四大组件由 PathClassLoader 类加载器加载

如果一个APP没有加壳,就会由PathClassLoader类加载
为什么APP没有加壳,就由PathClassLoader加载呢?
不用着急,这个问题在后面讲解都会理解哈哈

这里提出一个小问题,我们如何获得 当前ClassLoader的类名列表?
提供一个思路,串起这篇文章讲的内容:
BaseDexClassLoader的3个子类任选一个 -> BaseDexClassLoader -> DexPathList -> Element -> DexFile -> getClassNameList()
在下篇,我们将使用代码解决这个问题
文章书写不易,换您手中一个赞不过分吧^_^

//第一个参数dex文件路径 ,必填的参数
//第二个参数编译优化存放路径,选个app可以访问的路径即可
//第三个参数so库的搜索路径 ,一般为null
//第四个参数为父classLoader,一般需要填写
53      public DexClassLoader(String dexPath, String optimizedDirectory,
54              String librarySearchPath, ClassLoader parent) {
//调用父类:BaseDexClassLoader的构造函数
55          super(dexPath, null, librarySearchPath, parent);
56      }
//第一个参数dex文件路径 ,必填的参数
//第二个参数编译优化存放路径,选个app可以访问的路径即可
//第三个参数so库的搜索路径 ,一般为null
//第四个参数为父classLoader,一般需要填写
53      public DexClassLoader(String dexPath, String optimizedDirectory,
54              String librarySearchPath, ClassLoader parent) {
//调用父类:BaseDexClassLoader的构造函数
55          super(dexPath, null, librarySearchPath, parent);
56      }
150      public BaseDexClassLoader(String dexPath,
151              String librarySearchPath, ClassLoader parent, ClassLoader[] sharedLibraryLoaders,
152              ClassLoader[] sharedLibraryLoadersAfter,
153              boolean isTrusted) {
154          super(parent);
155          // Setup shared libraries before creating the path list. ART relies on the class loader
156          // hierarchy being finalized before loading dex files.
157          this.sharedLibraryLoaders = sharedLibraryLoaders == null
158                  ? null
159                  : Arrays.copyOf(sharedLibraryLoaders, sharedLibraryLoaders.length);
160          this.pathList = new DexPathList(this, dexPath, librarySearchPath, null, isTrusted);
161 
162          this.sharedLibraryLoadersAfter = sharedLibraryLoadersAfter == null
163                  ? null
164                  : Arrays.copyOf(sharedLibraryLoadersAfter, sharedLibraryLoadersAfter.length);
165          // Run background verification after having set 'pathList'.
166          this.pathList.maybeRunBackgroundVerification(this);
167 
168          reportClassLoaderChain();
169      }
150      public BaseDexClassLoader(String dexPath,
151              String librarySearchPath, ClassLoader parent, ClassLoader[] sharedLibraryLoaders,
152              ClassLoader[] sharedLibraryLoadersAfter,
153              boolean isTrusted) {
154          super(parent);
155          // Setup shared libraries before creating the path list. ART relies on the class loader
156          // hierarchy being finalized before loading dex files.
157          this.sharedLibraryLoaders = sharedLibraryLoaders == null
158                  ? null
159                  : Arrays.copyOf(sharedLibraryLoaders, sharedLibraryLoaders.length);
160          this.pathList = new DexPathList(this, dexPath, librarySearchPath, null, isTrusted);
161 
162          this.sharedLibraryLoadersAfter = sharedLibraryLoadersAfter == null
163                  ? null
164                  : Arrays.copyOf(sharedLibraryLoadersAfter, sharedLibraryLoadersAfter.length);
165          // Run background verification after having set 'pathList'.
166          this.pathList.maybeRunBackgroundVerification(this);
167 
168          reportClassLoaderChain();
169      }
public BaseDexClassLoader(String dexPath,String librarySearchPath,
                          ClassLoader parent, ClassLoader[] sharedLibraryLoaders,
             ClassLoader[] sharedLibraryLoadersAfter,
             boolean isTrusted)
public BaseDexClassLoader(String dexPath,String librarySearchPath,
                          ClassLoader parent, ClassLoader[] sharedLibraryLoaders,
             ClassLoader[] sharedLibraryLoadersAfter,
             boolean isTrusted)
this.pathList = new DexPathList(this, dexPath, librarySearchPath, null,isTrusted);
this.pathList = new DexPathList(this, dexPath, librarySearchPath, null,isTrusted);
public BaseDexClassLoader(String dexPath, File optimizedDirectory,
        String librarySearchPath, ClassLoader parent) {
    super(parent);
    this.pathList = new DexPathList(this, dexPath, librarySearchPath, optimizedDirectory);
}
public BaseDexClassLoader(String dexPath, File optimizedDirectory,
        String librarySearchPath, ClassLoader parent) {
    super(parent);
    this.pathList = new DexPathList(this, dexPath, librarySearchPath, optimizedDirectory);
}
private final Element[] dexElements;
 
public DexPathList(ClassLoader definingContext, String dexPath,
        String libraryPath, File optimizedDirectory) {
    ...
    this.definingContext = definingContext;
    this.dexElements = makeDexElements(splitDexPath(dexPath), optimizedDirectory,suppressedExceptions);
    ...
}
private final Element[] dexElements;
 
public DexPathList(ClassLoader definingContext, String dexPath,
        String libraryPath, File optimizedDirectory) {
    ...
    this.definingContext = definingContext;
    this.dexElements = makeDexElements(splitDexPath(dexPath), optimizedDirectory,suppressedExceptions);
    ...
}
private static Element[] makeDexElements(ArrayList<File> files, File optimizedDirectory, ArrayList<IOException> suppressedExceptions) {
    // 1.创建Element集合
    ArrayList<Element> elements = new ArrayList<Element>();
    // 2.遍历所有dex文件(也可能是jar、apk或zip文件)
    for (File file : files) {
        ZipFile zip = null;
        DexFile dex = null;
        String name = file.getName();
        ...
        // 如果是dex文件
        if (name.endsWith(DEX_SUFFIX)) {
            dex = loadDexFile(file, optimizedDirectory);
 
        // 如果是apk、jar、zip文件(这部分在不同的Android版本中,处理方式有细微差别)
        } else {
            zip = file;
            dex = loadDexFile(file, optimizedDirectory);
        }
        ...
        // 3.将dex文件或压缩文件包装成Element对象,并添加到Element集合中
        if ((zip != null) || (dex != null)) {
            elements.add(new Element(file, false, zip, dex));
        }
    }
    // 4.将Element集合转成Element数组返回
    return elements.toArray(new Element[elements.size()]);
}
private static Element[] makeDexElements(ArrayList<File> files, File optimizedDirectory, ArrayList<IOException> suppressedExceptions) {
    // 1.创建Element集合
    ArrayList<Element> elements = new ArrayList<Element>();

[注意]看雪招聘,专注安全领域的专业人才平台!

收藏
免费 7
支持
分享
最新回复 (0)
游客
登录 | 注册 方可回帖
返回