什么是Hook
拦截事件并监控事件的传输。
常用的Hook框架
- 要root权限,直接Hook系统,可以干掉所有的App
- 免root权限,但是只能Hook自身,对系统其他App无能为力
常见Hook方案
Xposed
通过替换/system/bin/app_process
程序控制Zygote,在开机时完成所有的劫持,在原执行前后加上自定义代码。(支持Java代码)
Cydia Substrate
一个代码修改平台,可以修改任何进程的代码。(支持Java、C/C++)
Legend
免Root的一个Apk Hook框架。直接构造出新旧方法对应的虚拟机数据结构,然后替换信息写到内存中。
使用Java反射实现API Hook
通过对Android平台的虚拟机注入和Java反射的方式,来改变Android虚拟机调用函数的方式(ClassLoader),从而达到Java函数重定向的目的。
使用反射Hook setOnClickListener
新建一个OnClickListener类,传入原本的OnClickListener,通过代理,在原先的onCLick之前和之后,可执行相关的代码。 然后,通过反射,替换View的setOnClickListener中的ListenerInfo 。
class HookedOnClickListener implements View.OnClickListener {
private View.OnClickListener origin;
HookedOnClickListener(View.OnClickListener origin) {
this.origin = origin;
}
@Override
public void onClick(View v) {
Toast.makeText(MainActivity.this, "hook click", Toast.LENGTH_SHORT).show();
Log.i(TAG,"Before click, do what you want to to.");
if (origin != null) {
origin.onClick(v);
}
Log.i(TAG,"After click, do what you want to to.");
}
}
使用Hook拦截应用内的通知
可以通过替换NotificationManager的sService这个静态变量,并使用动态代理,来拦截不需要的通知(有些SDK会自己发通知)。
动态代理部分代码如下:
new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
if (args != null && args.length > 0) {
for (Object arg : args) {
log.debug("type:{}, arg:{}", arg != null ? arg.getClass() : null, arg);
}
}
// 操作交由 sService 处理,不拦截通知
// return method.invoke(sService, args);
// 拦截通知,什么也不做
return null;
// 或者是根据通知的 Tag 和 ID 进行筛选
}
});
总结
Hook 的选择点:静态变量和单例,因为一旦创建对象,它们不容易变化,非常容易定位。
Hook 过程:
寻找 Hook 点,原则是静态变量或者单例对象,尽量 Hook public 的对象和方法。
选择合适的代理方式,如果是接口可以用动态代理。
偷梁换柱——用代理对象替换原始对象。
Android 的 API 版本比较多,方法和类可能不一样,所以要做好 API 的兼容工作。