Hook属于android 中的黑科技,一般在逆向研究中使用较多,这几天技术分享会需要分享这个点。趁此机会来try一try。
先说一下Hook 意思,让大家有个初步的认识,要不一脸懵逼,Hook 简单类似网络传输中的中间人拦截,我拦截app中的原始方法,自己定义一个方法,替换原始的东西,实现我不可描述的目的,大白话就是这样,但是实际过程和应用还是比较复杂的。
常见的使用场景,举几个栗子:
App登录劫持,一般用户手动点击“登录”按钮才会将用户名和密码信息发送至服务器端去验证账号与密码是否正确。这样就很简单了,居心叵测的人只需要找到开发者在使用EditText控件的getText方法后进行网络验证的方法,Hook该方法,就能劫持到用户的账户与密码了。App 首页注入广告,app 启动的时候加载 HomeActivity 肯定要执行onCreate 方法, 劫持首页的onCreate 方法,在里面注入弹窗广告,获得广告收入。比如玩一款游戏App ,修改里面额金币数,可以反编译App ,找到具体的类和方法,插入自己的方法就可以实现。
听起来很牛b,但是要实现,需要掌握很多的逆向技术和其它需要技术,可是非常不易的。那我就不吹牛了,简单介绍下基础的hook 使用。
1.Hook 点击事件onClick。
2.Hook 通知事件NotificationManager。
1.1 Hook的对象一般是静态对象或者是单例,一般是Hook系统的方法,需要看系统的源码,这里就不分析了。
1.2 首先初始化一个点击的view,设置点击监听事件:
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
TextView tv = (TextView) findViewById(R.id.sample_text);
tv.setText(stringFromJNI());
tv.setOnClickListener(this);
try {
// 1.点击事件拦截
hookOnClickListener(tv);
// 2. 通知拦截
// hookNotificationManager(this);
} catch (Exception e) {
e.printStackTrace();
}
}
图1
1.2 view tv 的点击监听替换 方法hookOnClickListener(tv),这个方法实现如下
public static void hookOnClickListener(View view) throws Exception {
// 1.反射得到 ListenerInfo 对象
Method getListenerInfo = View.class.getDeclaredMethod("getListenerInfo");
getListenerInfo.setAccessible(true);
Object listenerInfo = getListenerInfo.invoke(view);
//2.得到原始的 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);
// 3.用 Hook代理类 替换原始的 OnClickListener
View.OnClickListener hookedOnClickListener = new HookedClickListenerProxy(originOnClickListener);
mOnClickListener.set(listenerInfo, hookedOnClickListener);
}
点击事件的 代理类HookedClickListenerProxy 实现如下:
class HookedClickListenerProxy implements View.OnClickListener {
private View.OnClickListener origin;
public HookedClickListenerProxy(View.OnClickListener originOnClickListener) {
this.origin = originOnClickListener;
// this=context;
}
@Override
public void onClick(View v) {
Toast.makeText(v.getContext(), " 拦截后 after Hook Click Listener",
Toast.LENGTH_SHORT).show();
if (origin != null) {
origin.onClick(v);
}
}
}
从中可见拦截中自己重写了onClick方法, 吐司了一下,并且继续调用了原始的点击方法。 原始的onClick方法如下:
@Override
public void onClick(View v) {
Toast.makeText(v.getContext(), "原始的 Click Listener", Toast.LENGTH_SHORT).show();
}
1这个时候重新启动app, 点击后会发现先弹 拦截后的吐司,再弹原始的吐司,拦截成功。
2.1 Hook 通知事件 ,还得看源码,分析分析需要拦截那个类和方法。然后套路和上面 类似,首先在初始化时定义通知 类,hookNotificationManager 实现该方法:
public static void hookNotificationManager(final Context context) throws Exception {
NotificationManager notificationManager = (NotificationManager)
context.getSystemService(Context.NOTIFICATION_SERVICE);
Method getService = NotificationManager.class.getDeclaredMethod("getService");
getService.setAccessible(true);
// 【1】得到系统的 sService
final Object originService = getService.invoke(notificationManager);
Class iNotiMagClz = Class.forName("android.app.INotificationManager");
// 【2】得到我们的动态代理对象
Object proxyNotiMag =
Proxy.newProxyInstance(context.getClass().getClassLoader(),new
Class[]{iNotiMagClz}, new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
String name = method.getName();
if (args != null && args.length > 0) {
for (Object arg : args) {
}
}
Toast.makeText(context.getApplicationContext(), "检测到有人发通知了", Toast.LENGTH_SHORT).show();
// 操作交由originService 处理,不拦截通知
return method.invoke(originService, args);
// 拦截通知,什么也不做
// return null;
// 或者是根据通知的 Tag 和 ID 进行筛选
}
});
// 【3】偷天换日,使用 proxyNotiMag 替换系统的 sService
Field sServiceField = NotificationManager.class.getDeclaredField("sService");
sServiceField.setAccessible(true);
sServiceField.set(notificationManager, proxyNotiMag);
}
通过以上步骤就将hookNotificationManager 类成功替换 ,在动态代理对象的回调方法invoke中实现我能不可描述的目的。技术没有问题,问题是使用技术的人。
Hook 技术就简单介绍这么点,叙述不全再所难免,还有常用的 一些Hook库,后面有时间再介绍。