Android Hook入门

Android Hook入门

Hook 英文意思是钩子,可以把一段执行着的代码钩下来,然后加入我们自己的逻辑,最后在放回去。比如我们可以Hook住一段系统代码,在执行系统代码之前加入我们自己的逻辑。

Hook技术主要用到java反射和java动态代理两个知识点,下面来个简单的例子,我们来Hook一个按钮的点击事件

1
2
3
4
5
6
7
Button button = findViewById(R.id.btn_click);
button.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Toast.makeText(getApplicationContext(),((Button) v).getText(),Toast.LENGTH_SHORT).show();
}
});

点击按钮的时候,我们如何能在不改变上面代码的前提下,在Toast弹出之前执行一些别的逻辑呢?比如现在Toast弹出的是按钮上的字,我们动态的把子给它改了

思路就是我们通过反射拿到系统中的OnClickListener的对象,通过动态代理,创建一个该对象的代理对象,这个代理对象中就可以写一些别的逻辑啦,最后把这个代理对象通过反射设置回系统中来个偷梁换柱就可以啦。

OK下面开始按照步骤来

第一步

反射拿到设置的OnClickListener对象,上面的代码中,我们进入setOnClickListener方法可以看到

1
2
3
4
5
6
public void setOnClickListener(@Nullable OnClickListener l) {
if (!isClickable()) {
setClickable(true);
}
getListenerInfo().mOnClickListener = l;
}

这里把我们传进来的回调对象设置给了getListenerInfo().mOnClickListener。

1
2
3
4
5
6
7
ListenerInfo getListenerInfo() {
if (mListenerInfo != null) {
return mListenerInfo;
}
mListenerInfo = new ListenerInfo();
return mListenerInfo;
}

getListenerInfo()方法返回一个ListenerInfo对象,它内部就保存了我们传过来的mOnClickListener回调对象。我们功过反射执行getListenerInfo方法就能拿到ListenerInfo对象了如下

1
2
3
4
5
6
Class<?> viewClass = Class.forName("android.view.View");
//需要拿到setOnClickListener方法set过去的对象
Method getListenerInfoMethod = viewClass.getDeclaredMethod("getListenerInfo");
getListenerInfoMethod.setAccessible(true);
//本质是ListenerInfo对象
Object listenerInfo = getListenerInfoMethod.invoke(view);

拿到了ListenerInfo对象,在通过它拿到它的成员变量mOnClickListener

1
2
3
Class<?> listenerInfoClass = Class.forName("android.view.View$ListenerInfo");
Field onClickListenerField = listenerInfoClass.getField("mOnClickListener");
final Object onClickListener = onClickListenerField.get(listenerInfo);

第二步

使用动态代理定义一个代理的onClickListener,在回调函数中添加一些别的逻辑

1
2
3
4
5
6
7
8
9
10
Object proxyClickListener = Proxy.newProxyInstance(MainActivity.class.getClassLoader(),
//需要将监听的方法
new Class[]{View.OnClickListener.class}, new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Button button = new Button(MainActivity.this);
button.setText("我就点你");
return method.invoke(onClickListener, button);
}
});

在InvocationHandler方法中就可以添加别的逻辑了,这里添加了Toast用来测试,最后返回一个代理的OnClickListener对象。

第三步

把代理的OnClickListener对象设置回系统中,在第一步中已经拿到了mOnClickListener对象,最后把它给改了就好了。

1
onClickListenerField.set(listenerInfo,proxyClickListener);

OK 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
/**
* hook
* @param view 需要hook的view
*/
private void hook(View view) {
try {
Class<?> viewClass = Class.forName("android.view.View");
//需要拿到setOnClickListener方法set过去的对象
Method getListenerInfoMethod = viewClass.getDeclaredMethod("getListenerInfo");
getListenerInfoMethod.setAccessible(true);
Object listenerInfo = getListenerInfoMethod.invoke(view);

Class<?> listenerInfoClass = Class.forName("android.view.View$ListenerInfo");
Field onClickListenerField = listenerInfoClass.getField("mOnClickListener");
final Object onClickListener = onClickListenerField.get(listenerInfo);

Object proxyClickListener = Proxy.newProxyInstance(MainActivity.class.getClassLoader(),
//需要将监听的方法
new Class[]{View.OnClickListener.class}, new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Button button = new Button(MainActivity.this);
button.setText("我就点你");
return method.invoke(onClickListener, button);
}
});
onClickListenerField.set(listenerInfo,proxyClickListener);
} catch (Exception e) {
e.printStackTrace();
}
}

# 架构

コメント

Your browser is out-of-date!

Update your browser to view this website correctly. Update my browser now

×