前言
在对加固应用进行Hook时,如果直接对应用中的函数进行Hook,则会报下图所示的ClassNotFoundException错误.
出现上述错误的原因就是类加载器ClassLoader在加固应用启动时切换导致的问题,我们知道App中所有类都是由对应的ClassLoader加载到ART虚拟机的,如果ClassLoader不正确,那么一定找不到对应的类.LSPosed在注入进程时App的Application类并未完成加载,这也就导致真实用于加载App业务相关类的ClassLoader并未出现,最终导致无法完成App业务函数的Hook.
下面提供两种方式来对加固应用进行Hook.
测试Apk
example.apk
方式一
通过对Android下的加固应用进行分析,可以知道壳程序总是通过在应用进程最先获得执行权限的Application类中的attachBaseContext和onCreate函数中完成对真实Dex的释放以及ClassLoader的切换.故我们可以通过对加固应用Application类的attachBaseContext或onCreate函数进行Hook,来得到真实App的上下文,再通过上下文来获取真实代码释放后的ClassLoader,用于后续的函数Hook.
其示例代码如下:
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
|
package com.example.luoxposeddemo;
import android.app.Application;
import android.content.Context;
import android.util.Log;
import de.robv.android.xposed.IXposedHookLoadPackage;
import de.robv.android.xposed.XC_MethodHook;
import de.robv.android.xposed.XposedBridge;
import de.robv.android.xposed.XposedHelpers;
import de.robv.android.xposed.callbacks.XC_LoadPackage;
public class LuoHook implements IXposedHookLoadPackage {
private final String TAG = "[LuoXposed]";
@Override
public void handleLoadPackage(XC_LoadPackage.LoadPackageParam lpparam) throws Throwable {
if (lpparam.packageName.equals("com.hay.dreamlover")) {
Log.i(TAG, "Enter Hook");
//com.stub.StubApp
Class applicationClazz = lpparam.classLoader.loadClass("com.stub.StubApp");
XposedHelpers.findAndHookMethod(applicationClazz, "attachBaseContext", Context.class, new XC_MethodHook() {
@Override
protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
super.beforeHookedMethod(param);
Log.i(TAG, "Enter com.stub.StubApp.attachBaseContext");
}
@Override
protected void afterHookedMethod(MethodHookParam param) throws Throwable {
super.afterHookedMethod(param);
Log.i(TAG, "Leave com.stub.StubApp.attachBaseContext");
Context context = (Context) param.args[0];
//获取真实业务代码的classLoader
ClassLoader finalClassLoader = context.getClassLoader();
Class initActivityClazz = finalClassLoader.loadClass("com.fanwe.hybrid.activity.InitActivity");
XposedBridge.hookAllMethods(initActivityClazz, "onCreate", new XC_MethodHook() {
@Override
protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
super.beforeHookedMethod(param);
Log.i(TAG, "attachBaseContext Enter com.fanwe.hybrid.activity.InitActivity.onCreate");
}
@Override
protected void afterHookedMethod(MethodHookParam param) throws Throwable {
super.afterHookedMethod(param);
Log.i(TAG, "attachBaseContext Leave com.fanwe.hybrid.activity.InitActivity.onCreate");
}
});
}
});
}
}
}
|
方式二
上面的方式一不具有通用性,一旦加固厂商改变相应继承Application类的类名,那上面的方式就失效了.下面介绍一种通用的解决任意加固应用函数的Hook方式.
Android App的启动流程如下:
App被Zygote进程孵化后,通过ActivityThread.main函数进入App的世界.ActivityThread这个类十分重要,它会根据ActivityManager发送的请求对activities、broadcast Receviers等操作进行调度和执行.其中performLaunchActivity()函数用于响应Activity相关的操作.另外ActivityThread类中还存在着一个Application类型的mInitialApplication成员,应用程序中有且只有一个Application组件,而Application对象中就存储着当前的ClassLoader,考虑到App在响应Activity消息时,真实App的代码已经被释放到内存中,此时通过mInitialApplication成员获取应用当前的ClassLoader,即可完成对真实App业务代码的Hook.
其示例代码如下:
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
|
package com.example.luoxposeddemo;
import android.app.Application;
import android.content.Context;
import android.util.Log;
import de.robv.android.xposed.IXposedHookLoadPackage;
import de.robv.android.xposed.XC_MethodHook;
import de.robv.android.xposed.XposedBridge;
import de.robv.android.xposed.XposedHelpers;
import de.robv.android.xposed.callbacks.XC_LoadPackage;
public class LuoHook implements IXposedHookLoadPackage {
private final String TAG = "[LuoXposed]";
@Override
public void handleLoadPackage(XC_LoadPackage.LoadPackageParam lpparam) throws Throwable {
if (lpparam.packageName.equals("com.hay.dreamlover")) {
Log.i(TAG, "Enter Hook");
Class activityThreadClazz = lpparam.classLoader.loadClass("android.app.ActivityThread");
XposedBridge.hookAllMethods(activityThreadClazz, "performLaunchActivity", new XC_MethodHook() {
@Override
protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
super.beforeHookedMethod(param);
Log.i(TAG, "Enter android.app.ActivityThread.performLaunchActivity");
Application mInitialApplication = (Application) XposedHelpers.getObjectField(param.thisObject, "mInitialApplication");
ClassLoader finalClassLoader = mInitialApplication.getClassLoader();
Class initActivityClazz = finalClassLoader.loadClass("com.fanwe.hybrid.activity.InitActivity");
//Log.i(TAG, initActivityClazz.toString());
XposedBridge.hookAllMethods(initActivityClazz, "onCreate", new XC_MethodHook() {
@Override
protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
super.beforeHookedMethod(param);
Log.i(TAG, "performLaunchActivity Enter com.fanwe.hybrid.activity.InitActivity.onCreate");
}
@Override
protected void afterHookedMethod(MethodHookParam param) throws Throwable {
super.afterHookedMethod(param);
Log.i(TAG, "performLaunchActivity Leave com.fanwe.hybrid.activity.InitActivity.onCreate");
}
});
}
@Override
protected void afterHookedMethod(MethodHookParam param) throws Throwable {
super.afterHookedMethod(param);
Log.i(TAG, "Leave android.app.ActivityThread.performLaunchActivity");
}
});
}
}
}
|
至此,无论任何类型的App,只要存在一个Activity,按照上述方式进行Hook,理论上都可以完美解决.