处理 BLE(Bluetooth Low Energy,低功耗蓝牙)设备时常碰到的一个问题就是,确定有哪些信息发送给它。
使用诸如 Ubertooth 之类的蓝牙嗅探器来截获传输数据。这一方法有点麻烦,因为你只有1/3的机会截取到数据流。
使用 bleno 模拟设备,然后截取数据流。
逆向手机APP,然后分析出它的通信协议。
我通常使用第3种方法,通过阅读反编译后的安卓代码分析出它的协议。因为对APP代码进行混淆已经变得越来越常见了,因此使用这种方法有时候会非常痛苦。
我想,是否可以hook函数调用,直接dump对属性进行写操作的方法。有一些框架都能做到这一点,最终我选择了goole搜索排名第一的 Frida 。
这个框架可以对二进制程序进行注入,支持的平台包括Linux, Windows, iOS 和 Android。你可以用JavaScript 写程序来替换Java的调用,但奇怪的是,使用Python来进行Hook。
使用Frida时会碰到的一个问题是,官方文档是基于你已经会使用Frida了而写的。幸好的是这里 还有一些教程,但都不够全面,因此就有了这篇文章。
安装
这是最容易的部分,你可以像安装Python(我使用Python 2.7)的其它包一样使用pip来安装。
pip install frida
因为我平常处理一些硬件上的东西都是在虚拟机上操作的,所以我起初在我的Linux虚拟机上安装,但它会时不时的崩溃和超时,但使用Windows的时候又不会这样。这似乎是因为我虚拟机上的adb版本太低了,所以你首先应该确保你能正常地连接上你的Android设备。
现在我们已经在对应的操作系统上安装好Frida了,接下来需要在Android设备上安装一个服务器。我使用的是Nexus 5,它的系统是Android 6.0.1(当然已经root了)。最好把服务器push到/data/local/tmp这个位置,这样shell用户就能有写权限,且在重启后这个文件依然存在。
C:\Users\dave>adb push frida-server /data/local/tmp
C:\Users\dave>adb shell
shell@hammerhead:/ $ su
root@hammerhead:/ # cd /data/local/tmp
root@hammerhead:/data/local/tmp # ./frida-server &
经过上述操作后,一切就准备就绪了。
安装app
因为我不能给你看我测试的app(因为客户端的权限问题),我使用我同事使用的Marshall amp app,我相信我们不久后就能找到一些关于它的优秀的博文。
第一步是取得它的apk安装文件,并获取程序的进程名。我们不需要写程序,找到进程名的最简单的方法就是查看它在Google Play store上的链接。
为了更方便的安装app,最好是获得它的apk安装文件,然后使用pull命令把这个文件提取到电脑上。网上有一些服务可以解密Google Play的apk文件,并提供下载,但我觉得这不安全。
为了把apk文件提取出来,我们需要找到apk文件的存储位置。它可以通过调用包管理器(pm)命令,使用 -f 参数(显示文件列表):
D:\dave>adb shell pm list packages -f | findstr marshall
package:/data/app/air.com.marshall.gateway-1/base.apk=air.com.marshall.gateway
=号前面的部分是app的路径,这样我们就能使用pull命令把它提取出来了(这一步不需要root权限):
D:\dave>adb pull /data/app/air.com.marshall.gateway-1/base.apk marshall.apk
7157 KB/s (15674806 bytes in 2.138s)
我们还需要app的名称。它一般和包名相同(com.marshall.tone),但也不一定,万无一失的做法是查看进程列表。我们可以用adb shell ps或是frida-ps查看进程列表:(-U 表示服务器不在本机,而需要使用USB连接到Frida服务器)
D:\dave>frida-ps -U | findstr marshall
16182 air.com.marshall.gateway
接下来就可以试着hook对应的方法了,我们首先要找到我们想要hook的方法。通过查Google我们知道Android的 API 中通过BLE进行属性的写操作的方法是android.bluetooth.BluetoothGatt.writeCharacteristic。
我前几次的尝试都不成功,后来我换了个更简单的位置进行Hook。
我使用jadx-gui来搜索反编译代码中writeSettingValue这个关键字出现的位置,在类net.transpose.igniteaneandroid.IgniteANEAndroidExt中找到了android.bluetooth.BluetoothGatt的实例。
从上图可见,方法net.transpose.igniteaneandroid.IgniteANEAndroidExt.writeSettingValue
接收一个byte数组和一boolean类型的参数,返回void,通过传进来的参数再调用writeCharacteristic。
因此,理论上来说,如果我们hook了这个方法,我们就能知道所有通过蓝牙写入的数据。
使用Frida
我们所有的操作都可以在Frida命令行中运行,但使用脚本的话更方便。
Frida控制台基于Node会话,使用JavaScript进行编程来hook方法调用。有一个从Java API可以实现从Java到JavaScript的映射,尽管数据类型有点混乱。
接下来我碰到一个问题,每当我进行hook时,writeCharacteristic 会接收一个byte数组,然后这个数组会被转为JavaScript对象。默认的日志提示是[Object object],没办法得到更多的提示。所以我写了个小程序进行hook并打印元数据。
function enumerateObject(obj)
{
for (key in obj)
{
console.log(key + ": " + obj[key]);
}
}
function newWriteCharacteristic(data)
{
enumerateObject(data);
this.writeCharacteristic(data);
}
function hookIt()
{
var ble=Java.use("net.transpose.igniteaneandroid.IgniteANEAndroidExt ");
ble. writeSettingValue.implementation=newWriteCharacteristic;
}
Java.perform(hookIt);
这段代码会附加到类net.transpose.igniteaneandroid.IgniteANEAndroidExt的定义上,并用我写的方法替换writeSettingValue 方法的实现,它会在把程序控制权传回原来函数时枚举传进来的对象。
frida -U -l c:\users\dave\desktop\marshall-write.js air.com.marshall.gateway
-l选项指定我们的脚本名,而com.marshall.tone是进程名。接下来我使用这个app进行了一次BLE属性写操作(通过选择一个预设设备),控制台输出如下:
从上图可以看到,这个对象有三个属性。
$handle—对象实现的句柄
type—它告诉我们这是个byte数组
length—对象的长度
因为这是个byte数组,因此我们直接用索引进行遍历,如果我们把newWriteCharacteristic(译者注:这里应该为writeCharacteristic,疑为作者笔误)替换为:
function newWriteCharacteristic(data)
{
hexstr="";
for (i=0;i
{
b=(data[i]>>>0)&0xff;
n=b.toString(16);
hexstr += ("00" + n).slice(-2)+" ";
}
console.log("Output: " + hexstr);
this.writeCharacteristic(data);
}
上面这个函数应该会发送一些十六进制值。Frida有内置的十六进制输出函数,但我使用它的时候一直不成功。
重新加载Frida,然后在app上按几个键,就会显示出它发送的消息。
要想实现上图效果,设备首先需要和Marshall amp进行配对。看上图我们的实验似乎已经成功了。
我们可以使用gatttool把上述输出发送给设备,看设备是否能正确的响应消息以验证实验是否成功。
最终,我们的实验成功了,设备状态改变了。
结论
Frida是一个强大的拦截Android app内部组件间消息传递的工具,它可以简化我们的工作。
花一些时间来了解它是值得的,因为它能让BLE的逆向工程更加简单。
如果你想要我进行上述实验的代码,你可以在我们的Github账户。请注意,我非常不喜欢JavaScript的匿名函数嵌套。因此,如果可能的话,我会把匿名函数进行扩展以使代码更具可读性。因此,我的代码相对于其他教程的代码可能会有点长。
如果你想要看到更多的对吉他音箱的研究,可以阅读我同事写的博文
本文章由看雪翻译小组 梦野间 编译 转载请注明来自看雪论坛
关注看雪学院公众号:ikanxue,获取更多干货!