Android蓝牙之Gatt Hook
来源:互联网 发布:mac u盘装系统 sierra 编辑:程序博客网 时间:2024/05/16 18:10
许多人可能对Hook技术有些陌生,其实从字面意思上理解这就类似一个钩子,挂在了系统中的某些地方,然后当执行流程经过该处时会被勾住,可以选择放行或截获,或做点手脚偷偷改改参数,或记录日志,或检查权限,或post到别的上下文去执行,应用场景还挺多。
本文会重点讨论蓝牙相关的Hook,要全局监测所有BLE蓝牙设备的操作,对于不那么活跃的设备我们会断开连接并释放资源,毕竟蓝牙通信信道是有限的。那么如何全局监测所有蓝牙设备的操作呢?可以在所有操作设备的地方打点,不过这样太麻烦,给代码搞乱了不说,即便漏了也不知道。好一点的办法是封装一套接口供所有人调用,打点在接口内做好,外部不用关心。不过如果有人不守规矩不走这套接口则依然会漏了。接下来本文会提出一种新的解决思路,不用到处打点,也不用封装这么一套公共接口。其核心原理就是在系统api内部挂一个钩子,用户操作设备总要调系统api的,这样就能被我们拦截到并且万无一失。现在的问题是如何在系统内部打入这么一个钩子而不被察觉呢?
第一步,我们需要调研这个钩子下在哪里。我们注意到BLE设备操作都需要有gatt句柄,我们就以gatt为入口,先看这个gatt是在哪里获取的,如下:
public BluetoothGatt connectGatt(Context context, boolean autoConnect, BluetoothGattCallback callback) { BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter(); IBluetoothManager managerService = adapter.getBluetoothManager(); try { IBluetoothGatt iGatt = managerService.getBluetoothGatt(); if (iGatt == null) { // BLE is not supported return null; } BluetoothGatt gatt = new BluetoothGatt(context, iGatt, this); gatt.connect(autoConnect, callback); return gatt; } catch (RemoteException e) {Log.e(TAG, "", e);} return null;}
这个gatt核心其实是IBluetoothGatt,是IBluetoothManager调用getBluetoothGatt返回的。而这个IBluetoothManager是BluetoothAdapter中的,我们将目光转移到BluetoothAdapter中,如下:
public static synchronized BluetoothAdapter getDefaultAdapter() { if (sAdapter == null) { IBinder b = ServiceManager.getService(BLUETOOTH_MANAGER_SERVICE); if (b != null) { IBluetoothManager managerService = IBluetoothManager.Stub.asInterface(b); sAdapter = new BluetoothAdapter(managerService); } else { Log.e(TAG, "Bluetooth binder is null"); } } return sAdapter;}
可见BluetoothAdapter是个单例,且IBluetoothManager是从ServiceManager中获取的。我们再将目光转移到ServiceManager,如下:
public static IBinder getService(String name) { try { IBinder service = sCache.get(name); if (service != null) { return service; } else { return getIServiceManager().getService(name); } } catch (RemoteException e) { Log.e(TAG, "error in getService", e); } return null;}
这里我们发现ServiceManager中所有的IBinder都缓存在sCache中,这是个静态全局变量,是个下钩子的好入口。
到这里开始讲核心思路了,首先通过反射拿到ServiceManager中的IBluetoothManager的IBinder,动态生成一个代理对象塞回ServiceManager的sCache中。这样BluetoothAdapter初始化时拿到的就是个代理的IBinder。
public static void hook() { Method getService = ServiceManagerCompat.getService(); IBinder iBinder = HookUtils.invoke(getService, null, BLUETOOTH_MANAGER); IBinder proxy = (IBinder) Proxy.newProxyInstance(iBinder.getClass().getClassLoader(), new Class<?>[]{IBinder.class}, new BluetoothManagerBinderProxyHandler(iBinder)); HashMap<String, IBinder> cache = ServiceManagerCompat.getCacheValue(); cache.put(BLUETOOTH_MANAGER, proxy);}
到这里事情还远没完,我们最终的目的是拦截所有关于IBluetoothGatt的调用。接下来看BluetoothAdapter中拿到这个IBluetoothManager的IBinder之后调用了IBluetoothManager.Stub.asInterface来将这个IBinder转为IBluetoothManager,我们看asInterface的实现:
public static android.bluetooth.IBluetoothManager asInterface(android.os.IBinder obj) { if ((obj == null)) { return null; } android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR); if (((iin != null) && (iin instanceof android.bluetooth.IBluetoothManager))) { return ((android.bluetooth.IBluetoothManager) iin); } return new android.bluetooth.IBluetoothManager.Stub.Proxy(obj);}
这里传入的参数IBinder可能是Binder,也可能是BinderProxy。如果是Binder则queryLocalInterface返回的是Binder自身,如果是BinderProxy则queryLocalInterface返回的是null。所以我们看到如果是Binder实体,则这里直接返回自身,如果是Binder代理,则这里会封装一个业务代理对象返回。所以我们可以在这里拦截queryLocalInterface,返回一个代理的IBluetoothManager对象。如下:
private static class BluetoothManagerBinderProxyHandler implements InvocationHandler { private IBinder iBinder; private Class<?> iBluetoothManagerClaz; private Object iBluetoothManager; BluetoothManagerBinderProxyHandler(IBinder iBinder) { this.iBinder = iBinder; this.iBluetoothManagerClaz = HookUtils.getClass("android.bluetooth.IBluetoothManager"); Class<?> stub = HookUtils.getClass("android.bluetooth.IBluetoothManager$Stub"); Method asInterface = HookUtils.getMethod(stub, "asInterface", IBinder.class); this.iBluetoothManager = HookUtils.invoke(asInterface, null, iBinder); } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { if ("queryLocalInterface".equals(method.getName())) { return Proxy.newProxyInstance(proxy.getClass().getClassLoader(), new Class<?>[] {IBinder.class, IInterface.class, iBluetoothManagerClaz}, new BluetoothManagerProxyHandler(iBluetoothManager)); } return method.invoke(iBinder, args); }}
当BluetoothAdapter拿到这个代理的IBluetoothManager对象后,调用getBluetoothGatt时,我们需要再次拦截返回一个gatt的代理对象,如下:
private static class BluetoothManagerProxyHandler implements InvocationHandler { private Object iBluetoothManager; private Class<?> bluetoothGattClaz; private Object bluetoothGatt; BluetoothManagerProxyHandler(Object iBluetoothManager) { this.iBluetoothManager = iBluetoothManager; this.bluetoothGattClaz = HookUtils.getClass("android.bluetooth.IBluetoothGatt"); Class<?> stub = HookUtils.getClass("android.bluetooth.IBluetoothManager"); Method method = HookUtils.getMethod(stub, "getBluetoothGatt"); this.bluetoothGatt = HookUtils.invoke(method, iBluetoothManager); } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { if ("getBluetoothGatt".equals(method.getName())) { return Proxy.newProxyInstance(proxy.getClass().getClassLoader(), new Class<?>[] {IBinder.class, IInterface.class, bluetoothGattClaz}, new BluetoothGattProxyHandler(bluetoothGatt)); } return method.invoke(iBluetoothManager, args); }}
拿到这个gatt的代理对象之后,我们就可以拦截其所有api,监测设备的一切活动,如下:
private static class BluetoothGattProxyHandler implements InvocationHandler { private Object bluetoothGatt; BluetoothGattProxyHandler(Object bluetoothGatt) { this.bluetoothGatt = bluetoothGatt; } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { BluetoothLog.v(String.format("IBluetoothGatt method: %s", method.getName())); return method.invoke(bluetoothGatt, args); }}
这里只是打了一行日志而已,我们可以添加更复杂的监控逻辑。到这里所有设备的gatt操作都会被拦截下来了,而且做的神不知鬼不觉。
我们以上的这些下钩子的过程都可以封装到一个类中供别人调用,即便APP是多进程架构,只要一行代码就可以监控该进程中所有Gatt操作,并立即报到主进程中。
类似的还可以给网络请求下钩子,比如插件中不允许有自己的网络请求,必须全部走主APP的api,那么就可以通过这种手段拦截掉所有的网络请求。
- Android蓝牙之Gatt Hook
- Android蓝牙4.0之GATT
- Android蓝牙开发GATT协议
- Android Gatt 蓝牙连接通信
- [BLE]低功耗蓝牙之GAP、GATT
- Android 蓝牙低功耗Ble Gatt浅谈
- Android蓝牙源码分析——Gatt连接(一)
- Android蓝牙源码分析——Gatt写设备
- Android蓝牙源码分析——Gatt的Connection ID
- 蓝牙【GATT】协议介绍
- 蓝牙GAtt详解
- 蓝牙:GATT,属性,特性,服务
- BLE蓝牙GAP、GATT介绍
- 蓝牙 BLE GATT 剖析(二)-- GATT UUID and 举例
- Android BLE低功耗蓝牙开发(上)关于GATT服务器的理论与搭建
- Android Gatt连接流程源码分析之ClientIf注册
- ble之gatt server
- Android BLE Gatt Error
- 加载web view的时候 Domain=WebKitErrorDomain Code=101 “The operation couldn’t be completed. (WebKitErrorDo
- 关于failed to load the JNI 的问题
- 关于目标的简化
- JavaWeb学习总结(六)---使用kaptcha生成验证码
- 【设计模式】行为模式之迭代器Iterator
- Android蓝牙之Gatt Hook
- LruCache 源码解析
- c++ string,const char*,char *相互转换
- Phalcon框架启动流程(部分源码)分析
- 移动端上滑加载更多
- LintCode(M)最小调整代价(待续)
- Spring MVC拦截器的使用
- 自己动手之WPscan windows版 (wordpress scan)
- python opencv 使用 Matplotlib显示图像