关于一代壳的一些理解

想解释一下一代壳的脱壳代码为什么这么写,以及一些代码中的细节,解释全在代码注释中!!!

思维有点乱,可能有错误的地方,烦请告知,谢谢!


话不多说,直接看handleBindApplication(),想要详细跟踪Application的创建请阅读Android中Application的创建流程

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
//路径:/frameworks/base/core/java/android/app/ActivityThread.java
private void handleBindApplication(AppBindData data) {
...
/*
这就是为什么在加壳时,在attachBaseContext()方法中替换classLoader时,可以借助mBoundApplication获取loadedApk实例,
这是因为mBoundApplication.info就包含创建好的loadedApk实例,
且在运行makeApplication()方法前就赋值好了(进一步说,是调用Application的attachBaseContext()之前),
就不会存在为空的情况,而mInitialApplication例外。
*/
mBoundApplication = data;
...
/* 创建并获取壳程序的LoadedApk对象 */
data.info = getPackageInfoNoCheck(data.appInfo, data.compatInfo);
...
final ContextImpl appContext = ContextImpl.createAppContext(this, data.info);
...
try {
/*
此处data.info指LoadedApk
makeApplication()里面会调用attachBaseContext()方法
*/
Application app = data.info.makeApplication(data.restrictedBackupMode, null);
/*
这里将壳Application实例赋值到ActivityThread实例的mInitialApplication字段
这也是为什么不在attachBaseContext()方法替换Application实例,
因为就算你替换了,后面壳程序执行完上面的makeApplication()方法后(包括attachBaseContext()方法),
返回到当前方法,然后将壳Application实例赋值给mInitialApplication字段
这样就直接替换了我们的源Application实例
(我尝试了一下这么做,程序正常执行了,是源程序太简单的缘故?)

由于ActivityThread实例的mInitialApplication字段不是在makeApplication()中赋值的
所以我们需要手动给mInitialApplication字段字段赋值源程序Application。

另外请注意,在加壳时,在attachBaseContext()方法中不要通过mInitialApplication获取loadedApk实例,因为此时
代码逻辑还在上面的makeApplication()中,下面的mInitialApplication = app;是没有被执行的,这样一来,
我们获取的mInitialApplication是空的!而应该通过mBoundApplication获取!
*/
mInitialApplication = app;
...
try {
mInstrumentation.onCreate(data.instrumentationArgs);
}
...
try {
/*
callApplicationOnCreate()方法里面会调用Application的onCreate()方法
而makeApplication()方法里面会调用Application的attachBaseContext()方法
根据流程可以知道:Application的attachBaseContext()方法执行时间早于onCreate()方法

因为这里是必走的,所以我们在调用makeApplication()方法创建源程序Application实例时,
设置第二个参数为null,这样在makeApplication()方法就不会调用callApplicationOnCreate()
否则里面调用了,外面再调用就会抛异常
当然也可以简单理解,毕竟源码中调用makeApplication()都设置第二个参数为null,照抄肯定不会错。
*/
mInstrumentation.callApplicationOnCreate(app);
}
...
}
...
}

接下来看makeApplication()方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
//路径:/frameworks/base/core/java/android/app/LoadedApk.java
public Application makeApplication(boolean forceDefaultAppClass,
Instrumentation instrumentation) {
//这就是为什么要先置mApplication为null,否者调用了也不会创建源APK的Application
if (mApplication != null) {
return mApplication;
}

Application app = null;
/*
创建Application的实例需要类名才能反射
显然mApplicationInfo指的是壳程序的Application类名,
所以我们需要手动修改LoadedApk实例中的mApplicationInfo.className的值为源程序Application类名
*/
String appClass = mApplicationInfo.className;

try {
/*
这也是为什么需要先替换ClassLoader再替换Application
因为创建Application需要ClassLoader,ClassLoaer不对那肯定创建失败
*/
java.lang.ClassLoader cl = getClassLoader();
...
ContextImpl appContext = ContextImpl.createAppContext(mActivityThread, this);
/*
里面会调用attachBaseContext()方法
*/
app = mActivityThread.mInstrumentation.newApplication(cl, appClass, appContext);
appContext.setOuterContext(app);
}
...
/*
添加到ActivityThread实例的mAllApplications数组中
所以需要将壳程序的Application从mAllApplications数组中删除
*/
mActivityThread.mAllApplications.add(app);
/*
这里是赋值给LoadedApk实例的mApplication字段,所以我们不需要手
动修改LoadedApk实例的mApplication字段的值。
*/
mApplication = app;

/*传入的第二个参数instrumentation为null,不走这里*/
if (instrumentation != null) {
try {
instrumentation.callApplicationOnCreate(app);
}
...
}
...
return app;
}

接下来看getPackageInfoNoCheck()方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
//路径:/frameworks/base/core/java/android/app/ActivityThread.java
public final LoadedApk getPackageInfoNoCheck(ApplicationInfo ai, CompatibilityInfo compatInfo) {
return getPackageInfo(ai, compatInfo, null, false, true, false);
}

private LoadedApk getPackageInfo(ApplicationInfo aInfo, CompatibilityInfo compatInfo,
ClassLoader baseLoader, boolean securityViolation, boolean includeCode,
boolean registerPackage) {
final boolean differentUser = (UserHandle.myUserId() != UserHandle.getUserId(aInfo.uid));
synchronized (mResourcesManager) {
WeakReference<LoadedApk> ref;
// 根据不同的情况获取缓存的LoadedApk对象
...

LoadedApk packageInfo = ref != null ? ref.get() : null;
if (packageInfo == null || (packageInfo.mResources != null && !packageInfo.mResources.getAssets().isUpToDate())) {
...
//创建LoadedApk对象
packageInfo = new LoadedApk(this, aInfo, compatInfo, baseLoader,
securityViolation, includeCode &&
(aInfo.flags&ApplicationInfo.FLAG_HAS_CODE) != 0, registerPackage);

if (mSystemThread && "android".equals(aInfo.packageName)) {
packageInfo.installSystemApplicationInfo(aInfo,
getSystemContext().mPackageInfo.getClassLoader());
}

if (differentUser) {
// Caching not supported across users
} else if (includeCode) {
/*更新mPackages,即将刚创建的packageInfo加入
这就是为什么可以通过ActivityThread实例中的mPackages字段获取loadedApk实例,
因为在运行makeApplication()之前(进一步说,是调用Application的attachBaseContext()之前),
就已经将创建好的loadedApk实例加入到mPackages中了。
*/
mPackages.put(aInfo.packageName,
new WeakReference<LoadedApk>(packageInfo));
} else {
mResourcePackages.put(aInfo.packageName,
new WeakReference<LoadedApk>(packageInfo));
}
}
return packageInfo;
}
}

关于一代壳的一些理解
http://example.com/2023/12/02/Android安全/关于一代壳的一些理解/
作者
gla2xy
发布于
2023年12月2日
许可协议