-
-
[原创]Android一代加固学习史(1)
-
发表于: 2024-9-12 21:33 1983
-
Android一代加固学习史是一系列文章,将详细介绍Android第一代壳的相关原理,从源码分析,到代码实现。体现了理论结合实战的方法,帮助初学者理解学习。
在本系列中,使用了3种方法加载dex文件(落地加载),从难到易,逐步理解。
这是第一篇文章,稍微会有一些枯燥,主要介绍一些在第一代加固会用到的相关类。初学者可以对其有个大概的印象,建立相关类的体系结构,后面都会用到的哦
这里借用寒冰老师的图,主要是不想再画了(
该图介绍了Android ClassLoader相关的继承关系
这里我们只需要记住下面5点
哦豁,我们发现,大哥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,传递参数并调用父类构造函数
下面介绍重量级选手,处理大部分逻辑的类
可以看到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>();