浅谈Android里的Hook技术
简介
Hook技术要先提到逆向工程,其主要目的是在不能轻易获得必要的生产信息的情况下,直接从成品分析,推倒出产品的设计原理。逆向分析分为静态分析和动态分析,其中静态分析指的是一种在不执行程序的情况下对程序行为进行分析的技术,而动态分析是指在程序运行时对程序进行调试的技术。Hook技术就属于动态分析,在android中,动态的获取某一个实例的某些方法,这一点大家可能会想倒反射,那么反射和hook到底又有什么区别呢?我们接着文章读下去。
Hook由来
我们知道在正常Andorid系统的代码调用和回调都是按照一定顺序执行的,这里举一个简单的例子
对象A调用类对象,对象B处理后将数据回调给对象A,如果我们采用hook的调用流程,如下
Hook可以是一个方法或者是一个对象,它像一个钩子一样挂在对象A和对象B之间,当对象A调用对象B之前做一下处理(比如修改方法的参数和返回值)。我们知道应用程序进程之间是彼此独立的,应用程序进程和系统进程之间也是如此,想要在应用程序进程更改系统进程的某些行为很难直接实现,有了hook技术,我们就能方便的在进程间进行行为更改。
可以看到Hook可以将自己融入到它所要劫持的对象所在的进程中,成为系统进程的一部分,这样我们就可以通过Hook来更改对象B的行为。被劫持的对象(对象B)称作Hook点,为了保证Hook的稳定性,Hook点一般选择容易找到并且不易变化的对象,静态变量和单例就符合这一条件。
根据HookAPI语言分类
- Hook Java (主要通过反射和代理来实现,应用于SDK开发环境中修改java代码)
- Hook Native (应用于NDK开发环境和系统开发中修改Native代码)
根据Hook进程分类
- 应用程序进程Hook (应用程序进程 Hook只能Hook当前所在的应用程序进程)
- 全局Hook (应用程序进程是Zygote进程fork出来的,如果对Zygote进行Hook,就可以实现Hook系统所有的应用程序进程,这就是全局Hook)
根据Hook的实现方式划分
- 通过反射和代理实现,只能Hook当前的应用程序进程。
- 通过Hook框架来实现,比如Xposed,可以实现全局Hook,但是需要root。
动态代理
这里就不再细讲静态代理了,代理模式分为静态代理和动态代理
- 静态代理:在代码运行前就已经存在了代理累的class编译文件
- 动态代理:在代码运行时通过反射来动态地生成代理类的对象,并确定到底来代理谁。也就是我们在编码阶段不需要知道代理谁,代理谁我们将在代码运行时决定。Java中提供了动态的代理接口InvocationHandler,实现该接口需要重写invoke方法。
Hook例子
来hook一下按钮的点击
private void hookOnClickListener(View view) {
try {
// 得到 View 的 ListenerInfo 对象
Method getListenerInfo = View.class.getDeclaredMethod("getListenerInfo");
getListenerInfo.setAccessible(true);
Object listenerInfo = getListenerInfo.invoke(view);
// 得到 原始的 OnClickListener 对象
Class<?> listenerInfoClz = Class.forName("android.view.View$ListenerInfo");
Field mOnClickListener = listenerInfoClz.getDeclaredField("mOnClickListener");
mOnClickListener.setAccessible(true);
View.OnClickListener originOnClickListener = (View.OnClickListener) mOnClickListener.get(listenerInfo);
// 用自定义的 OnClickListener 替换原始的 OnClickListener
View.OnClickListener hookedOnClickListener = new HookedOnClickListener(originOnClickListener);
mOnClickListener.set(listenerInfo, hookedOnClickListener);
} catch (Exception e) {
Log.e("hook clickLis failed", e+"");
}
}
class HookedOnClickListener implements View.OnClickListener {
private View.OnClickListener origin;
HookedOnClickListener(View.OnClickListener origin) {
this.origin = origin;
}
@Override
public void onClick(View v) {
Toast.makeText(HookTestActivity.this, "hook click", Toast.LENGTH_SHORT).show();
Log.e("aaa","Before click, do what you want to to.");
if (origin != null) {
origin.onClick(v);
}
Log.e("aaa","After click, do what you want to to.");
}
}