几年前,我使用RTL-SDR在浏览器中构建了一个项目,用于从过往飞机获取实时原始数据。由于我想进一步探索使用软件定义无线电,我购买了一台可以接收和发送数据的HackRF One设备,并下载了Universal Radio Hacker开始试用。经过一番尝试后,我预感到,可能可以在浏览器中重新创建一个类似的工具,全部使用 JavaScript,所以我花时间做了这件事!
我的最终目标是回答这个问题……是否有可能使用 JavaScript 来破解汽车?我完全不知道我是否能做到这一点,因为我对 SDR 相关知识的了解仍然很少,但我很高兴与大家分享它确实有效!!😃 这篇博文解释了我解决问题的过程。
⚠️重要说明⚠️ 我写这篇博文只是为了教育目的。尽管使用 SDR 收听和传输数据本身并不违法,但请查阅当地法律,了解在特定频率上传输数据是否需要获得无线电执照。此外,未经明确同意重现本博文中解释的一些实验是违法的。对于您决定如何使用本帖中分享的信息,我不承担任何责任。
演示
在深入了解技术细节之前,您可以查看可用于接收、记录和传输数据的网站,以便使用它来破解门铃、车库门等。下面是一个简短的演示视频,使用它来运行 rolljam/replay 攻击来破解我朋友的汽车(经同意)。
接收数据
为了从 HackRF 接收数据,我首先编写了使用WebUSB API将设备连接到浏览器所需的代码。
在网络上,当连接到 USB 设备时,用户首先需要从弹出窗口中选择它。
以下代码用于在弹出窗口中显示可用设备列表。filters参数值是可选的。如果未指定任何值,弹出窗口将列出当前通过 USB 连接到计算机的所有设备,但如果您传递带有产品和供应商 ID 的对象,它将仅显示您要连接的设备。
const device = await connect();
const connect = async () => {
const dev = await navigator.usb
.requestDevice({
filters: [
// see: https://github.com/mossmann/hackrf/blob/master/host/libhackrf/53-hackrf.rules
{ vendorId: 0x1d50, productId: 0x6089 },
],
})
.catch((e) => null);
return dev;
};
有几种不同的方法可以找到设备的供应商和产品 ID,但在 WebUSB API 上下文中,最简单的方法是chrome://usb-internals将 HackRF 设备插入计算机后在 Chrome 中访问并选择选项卡。您应该会看到一个带有和列Devices的表格,如下所示:vendor IDproduct ID
然后您可以将这些值复制到您的代码中。
一旦在 UI 中选择了该设备,以下代码就会处理连接:
await device.open();
await device.selectConfiguration(1);
await device.claimInterface(0);
可以通过两种方式找到作为配置和接口传递的值,要么通过阅读官方 HackRF SDK中的代码,要么通过在浏览器中检查设备。
对于第一个选项,您可以通读 SDK 的源代码,找到配置和接口的设置位置并查看传递的值。
否则,第二个选项会更快一些,并且依赖于chrome://usb-internals前面提到的页面。如果您单击Inspect表中的按钮,您将获得有关该设备的其他详细信息,包括配置(应该是1)和接口(应该是0)。
此时,浏览器应该已连接到设备,但实际上什么也没有发生。仍需要设置增益、采样率和频率等设置。
设置增益
为了设置 LNA(低噪声放大器)增益,我使用了controlTransferInWebUSB API 的方法。为了准确确定要传递给此方法的值,我参考了官方的 HackRF SDK。在 SDK 中,设置 LNA 增益的函数调用了另一个函数,libusb_control_transfer这让我找到了官方的libusb 文档,其中描述了需要传入的参数。
该libsub_control_transfer函数有 8 个参数。查看官方 HackRF SDK,该函数的使用方式如下:
result = libusb_control_transfer(
device->usb_device,
LIBUSB_ENDPOINT_IN | LIBUSB_REQUEST_TYPE_VENDOR | LIBUSB_RECIPIENT_DEVICE,
HACKRF_VENDOR_REQUEST_SET_LNA_GAIN,
0,
value,
&retval,
1,
0)
我不会在这里深入解释不同的参数,因为它们已经在文档中解释过了,但对这个项目特别重要的是第 2、第 3、第 4 和第 5 个参数。
第二个参数将有助于我们了解是否需要使用controlTransferIn或controlTransferOut来自 WebUSB API。LIBUSB_ENDPOINT_IN表示我们需要使用controlTransferIn。
第三个参数HACKRF_VENDOR_REQUEST_SET_LNA_GAIN将用作requestWebUSB API 中的参数。
第四个(0)将用作参数value,第五个(value,即使用的增益值)将作为index参数。
const result = await device.controlTransferIn(
{
requestType: "vendor",
recipient: "device",
request: HackRF.HACKRF_VENDOR_REQUEST_SET_LNA_GAIN,
value: 0,
index: 40, // The gain can be between 0 and 40
},
1
);
您还可以参考MDN 网络文档来了解有关此功能的更多信息。
此时,应设置 LNA 增益,但需要更多设置。
设置采样率
按照与上一节相同的逻辑,也可以设置采样率,如果您查看下面的代码示例,您可能会注意到略有不同:
const params = new DataView(new ArrayBuffer(8));
params.setUint32(0, freqHz, true);
params.setUint32(4, divider, true);
const result = await device.controlTransferOut(
{
requestType: "vendor",
recipient: "device",
request: HackRF.HACKRF_VENDOR_REQUEST_SAMPLE_RATE_SET,
value: 0,
index: 0,
},
params.buffer
);
首先,使用 设置采样率controlTransferOut。我参考了HackRF SDK 的这一部分来弄清楚,注意它使用的是LIBUSB_ENDPOINT_OUT。
然后,您会注意到索引设置为0,并且代表采样率的数据实际上用作第二个参数,这在MDN 文档controlTransferOut中有描述。
设置频率
现在我已经解释了如何设置 LNA 增益和采样率,设置频率遵循相同的逻辑,并使用以下代码完成:
const data = new DataView(new ArrayBuffer(8));
const freqMhz = Math.floor(freqHz / 1e6);
const freqHz0 = freqHz - freqMhz * 1e6;
data.setUint32(0, freqMhz, true);
data.setUint32(4, freqHz0, true);
const result = await device.controlTransferOut(
{
requestType: "vendor",
recipient: "device",
request: HackRF.HACKRF_VENDOR_REQUEST_SET_FREQ,
value: 0,
index: 0,
},
data.buffer
);
此时,必要的设置已完成,但设备尚未开始监听。
开始接收数据
开始接收数据的最后两个步骤是将设备设置为接收模式,并使用transferInWebUSB API 中的方法指示设备开始将数据传输到 UI。
const setDeviceReceivingMode = async () => {
return await device.controlTransferOut({
requestType: "vendor",
recipient: "device",
request: HackRF.HACKRF_VENDOR_REQUEST_SET_TRANSCEIVER_MODE,
value: HackRF.HACKRF_TRANSCEIVER_MODE_RECEIVE,
index: 0,
});
};
一旦设备处于接收模式,以下代码就会触发数据的传输.
const startRx = async (callback) => {
await setDeviceReceivingMode(); // function defined above
const transfer = async () => {
const result = await device.transferIn(1, HackRF.TRANSFER_BUFFER_SIZE);
if (result) {
callback(new Uint8Array(result.data.buffer));
}
await transfer();
};
await transfer();
};
await startRx((data) => {
console.log(data); // live radio frequency data
});
然后,您可以使用Canvas API来构建此数据的可视化。在我的工具中,我将天线调到 433.92MHz 的频率,按下我买的便宜门铃上的按钮,然后通过查看频谱可视化工具中的数据来确认一切正常。
记录数据
一旦我确认可以接收实时数据,我就会使用文件系统 Web API来实现录制功能。
首先,用户需要选择要记录数据的文件:
let fH;
const selectFile = async () => {
[fH] = await window.showOpenFilePicker();
let writableFile = await fH.createWritable();
return writableFile;
};
然后,当接收数据时,我们可以将其写入文件:
await startRx((data) => { // Function defined in the previous section
if(recording){ // variable set on click in the UI when starting the recording
await writableFile.write(data);
} else {
await writableFile.close();
}
})
就这样!实时数据将保存到所选文件中。
传输数据
一旦接收和记录数据成功,下一步就是将其传输回去。为此,我再次使用文件系统 Web API 加载本地文件中记录的数据,并使用transferOutWeb USB API 传输它。
let file, fH;
const selectFile = async () => {
[fH] = await window.showOpenFilePicker();
file = await fH.getFile();
return file;
};
选择文件后,需要设置设备transmit模式。
await device.controlTransferOut({
requestType: "vendor",
recipient: "device",
request: HackRF.HACKRF_VENDOR_REQUEST_SET_TRANSCEIVER_MODE,
value: HackRF.HACKRF_TRANSCEIVER_MODE_TRANSMIT,
index: 0,
});
然后,需要设置发射增益,方式与本文前面定义的其他设置相同。
await device.controlTransferIn(
{
requestType: "vendor",
recipient: "device",
request: HackRF.HACKRF_VENDOR_REQUEST_SET_TXVGA_GAIN,
value: 0,
index: 40, // Value must be <= 40.
},
1
);
最后,我们可以从文件中获取数据,将其存储到变量中并将其作为第二个参数传递给方法transferOut。
const data = await file.arrayBuffer();
await device.transferOut(2, data);
为了检查它是否正常工作,我还决定使用 Canvas API 来可视化导入的数据,这样我就可以了解记录的信号是什么样的。
此时,您可以在浏览器中接收、记录和传输来自 HackRF 设备的数据,只需使用原始 JavaScript 和浏览器 API!
Rolljam 和重放攻击
我实际上对恶意入侵汽车并不感兴趣,但我想尝试的事情之一是从浏览器运行 rolljam/replay 攻击,这可以通过我在本文其余部分解释的三个功能来完成。
那么 Rolljam 攻击是如何运作的呢?
首先,我们来简单谈谈汽车通常如何打开和关闭。简单来说,要锁定/解锁汽车,通常只需按下遥控器上的按钮即可将代码发送到汽车的接收器。如果汽车识别出该代码,就会锁定或解锁汽车。
1995 年之前,该系统每次都使用相同的代码来锁定汽车,一个代码用于锁定,另一个代码用于解锁。然而,这种系统存在安全风险,因为如果代码被拦截,任何记录信号的人都可以锁定和解锁汽车。您可以看到,使用上述代码以这种方式破解汽车是多么容易。
为了缓解这种情况,1995 年以后生产的大多数汽车都采用了所谓的“滚动码”。锁定和解锁不再使用唯一的代码,而是使用一个代码列表进行轮换,因此当其中一个代码被使用时,它会暂时失效,直到列表轮换并恢复到该代码。
因此,rolljam 攻击包括干扰汽车的接收器,使其无法接收遥控器发送的代码,同时记录该代码,以便攻击者可以重放它。
在本帖中显示的代码上下文中,唯一缺少的是干扰部分。但是,干扰基本上是传输一堆随机数据,因此可以更新传输录制文件的代码示例以改为传输随机值,例如:
const randomData = Array.from({ length: 3670000 }, () =>
Math.floor(Math.random() * 100)
);
for (let i = 0; i < 20; i++) {
await startTx(new Uint8Array(randomData));
}
此外,由于此攻击需要同时设置传输和记录,因此需要两个 HackRF 设备才能工作。一个用于监听遥控器使用的频率并将信号记录到文件中,而另一个将干扰接收器。一旦成功记录信号,其中一个设备就可以重放该信号。
结论
总的来说,这是一个超级有趣的项目,能够验证使用 JavaScript 入侵汽车是可能的,真的很令人兴奋!🎉
尽管这篇文章专门讨论汽车,但它可以用来破解车库门、遥控灯、门铃、一些无人机,理论上任何由射频接收器/发射器远程控制的东西。
该项目的部分工作仍在进行中,我真的很想实现一项功能来分析记录的二进制数据,以便能够进行一些逆向工程,但我还没有开始。
如果您最终获得 HackRF 或其他设备并用它构建了一些东西,请告诉我!
原文始发于微信公众号(Ots安全):使用 JavaScript 入侵汽车(使用 HackRF 在浏览器中运行重放攻击)
- 左青龙
- 微信扫一扫
- 右白虎
- 微信扫一扫
评论