Analysis of the iOS Bluetooth Stack: BlueTool
On iOS the Bluetooth stack is split into three layers as shown in Figure 1. At the top is the CoreBluetooth framework used by iOS app developers and at the bottom is the Bluetooth hardware itself. In between these two layers is a collection of daemons that implement various aspects of the Bluetooth stack. For example, most Bluetooth Low Energy (BLE) specific functionality is contained in the BTLEServer daemon. BlueTool is one of the daemons running and is primarily responsible for acting as a bridge between the rest of the Bluetooth stack and the hardware. It also likely acts as an internal test tool at Apple during development.
All analysis in this article was carried out on a 5th generation iPod Touch running iOS 8.4 (12H143). My primary focus during analysis was on understanding what functionality BlueTool provides. I was not actively looking for bugs despite finding a couple.
BlueTool I/O Mechanisms
BlueTool is started automatically at boot by launchd and may also be started manually. When it is started automatically by launchd, BlueTool is executed with an undocumented -R
flag that can be seen by either inspecting the running processes or looking at the launchd plist file for the executable. If BlueTool is started manually it will drop the user into an interactive shell as shown in the listing below.
Welcome to BlueTool... Rev 6.0
bluetool-> ?
BlueTool Help
? : Print out all commands and some help.
autobaud : Run automatic baud rate on the selected device.
device : Select the device to communicate over.
hci : Send an HCI command to the Bluetooth device.
bcm : Broadcom specific operations.
msleep : Sleep the caller for the given number of milliseconds
spam : Spam data to the UART
if : if <test> ? a : b
true : true
false : false
echo : echo
power : Power on/off the Bluetooth device.
reset : Reset on/off/pulse the Bluetooth device.
wake : Wake on/off/pulse the Bluetooth device.
Give any commands '-h' for more details.
device returned 3
bluetool-> device -D
Opening com.apple.uart.bluetooth @ 115200 baud.
bluetool->
When BlueTool is started by launchd it creates an XPC service called com.apple.BlueTool
and ignores all input from stdin. Most output is logged to stdout or stderr.
In addition to the interactive shell and XPC service, data is also sent to and received from the Bluetooth hardware. This communication is carried out over a UART connection and uses the Bluetooth Host-Controller Interface (HCI) command set.
XPC Service
When BlueTool is started by launchd the undocumented -R
flag causes two things to happen:
- BlueTool starts the
com.apple.BlueTool
XPC service - BlueTool ignores all commands passed to it via stdin
The XPC service handler first asserts that the message dictionary contains the kBluetoolMessageId
key and then selects a command-specific handler based on its value. Possible values are 2 through 5 inclusive. Note that the values 0 and 1 are not bound to any handler, it is unclear to me why.
kBluetoolMessageId |
Description |
---|---|
0 | Undefined. |
1 | Undefined. |
2 | Process given file path as script. |
3 | Process named script. |
4 | Process \n separated list of commands. |
5 | Return version information. |
Table 1: Bluetool XPC commands
The following are summaries of each command.
XPC Command: 2
Setting kBluetoolMessageId equal to two causes the program to then look for the script
key. The value of this key is a string that contains a path to a script to be run by BlueTool. The script file is simply a single command per line.
During testing I noticed that the file is opened using fopen()
with no checks beforehand to ensure the path is valid. As a result it is possible to read files at other locations. Although this is a directory traversal vulnerability there are two caveats:
- The sandbox should block reads to unauthorized files
- Data read is sent to stderr
Since my testing was on a jailbroken device it is unclear to me if the sandbox had been disabled. I did find it possible to read /etc/passwd
however this should be tested on a non-jailbroken device. Regardless, due to the BlueTool process being started by launchd we typically don’t have control or access to stderr. As a result, this bug is likely not very interesting.
XPC Command: 3
This command allows a client of the XPC service to execute a named script. The three script names I’ve found so far are: boot, init, and deepsleep.
XPC Command: 4
When kBluetoolMessageId is set to 4 BlueTool will then search for a dictionary key of command
. The string associated with this key is then split into substrings using strtok
and passed to the central command processor used by both the XPC service and the interactive shell.
XPC Command: 5
The final command simply replies with the chip model, firmware version, and manufacturer as strings. This command takes no inputs.
BlueTool Commands
As was shown previously, BlueTool supports a number of commands. Most commands accept the -h
flag to show additional information about the command. Two commands that I looked at in particular are spam
and bcm
. Before executing most commands it is expected that you connect to a device via the device
command.
Spam Segfault
Shortly after I began experimenting with the interactive shell I noticed that executing the spam
command lead to a segfault. Being hopeful that I may be able to turn this bug into something useful I decided to investigate it.
Connecting to BlueTool with LLDB and passing the spam
command I found that BlueTool was crashing due to trying to branch to the address 0x00000000
. This happened because I was not opening a device beforehand. Opening a device is required because BlueTool tries to abstract away the hardware to some degree by placing a set of function pointers in memory when opening the device. These function pointers provide hardware specific functionality. Since the spam
command does not first ensure a device is open it will blindly try to branch to the address stored in the function pointer. For more on the function pointers and other data stored when opening a device see the section titled The 0x400 Structure.
Downloading Firmware to the Bluetooth Module
The bcm
command offers a subcommand that allows you to specify the path of a .hcd
file (Broadcom firmware) to download. I am still in the process of dumping and reversing the firmware, however the presence of this command means it is possible to update the Bluetooth module.
The 0x400 Structure
Through out the binary there is heavy use of what I’ve dubbed “the 0x400 structure”. This name came to be because this structure is always referenced by adding 0x400 to a base register. The structure contains the following.
Offset | Value | Description |
---|---|---|
0x400 | socket | UART socket file descriptor |
0x404 | open | boolean value indicating if device is open or closed |
0x408 | 0x19eb1 | write to socket (1 byte max) |
0x40c | 0x19eb9 | write to socket (R2 bytes max) |
0x410 | 0x19ec1 | call select() on socket. |
0x414 | 0x19f41 | a more different write |
0x418 | 0x19fa1 | read H4 HCI event/cmd/data body/header |
0x41c | 0x19e25 | set baud rate |
0x420 | 0x1a175 | close UART socket |
0x424 | 0x1a191 | flush the socket, input and output |
0x428 | 0x1a199 | spam 0xAA to UART |
0x42c | 0x0 | set via STR at 0x19d84 |
Table 2: Description of the 0x400
structure.
The values stored in this structure were obtained by inspecting the memory of a running BlueTool process and mapped to there offset through analysis of the open-device function.
UART and HCI
Communication between BlueTool and the Bluetooth module occurs over a UART connection. The protocol used over this connection is the standard HCI protocol specified by the Bluetooth specification. BlueTool makes use of various vendor-specific HCI commands. Since Broadcom does not seem to release datasheets and documentation describing the various commands the following list was compiled based off strings in the binary as well as the Broadcom patchram tool at https://code.google.com/p/broadcom-bluetooth/.
OpCode | Description |
---|---|
0xFC01 | Write Bluetooth device address |
0xFC18 | Update baud rate |
0xFC1C | Write SCO PCM int |
0xFC1E | Write PCM data format |
0xFC2E | Download mini driver |
0xFC27 | Write sleep mode |
0xFC45 | Write UART clock setting 48 MHz |
0xFC4C | Copy bytes to destination address |
0xFC4E | Unknown |
0xFC6D | Write i2spcm interface parameter |
Table 3: Vendor-specific HCI commands
The default UART device is opened by calling socket()
and connect()
with the device string com.apple.uart.Bluetooth
. Other device nodes found are:
/dev/btpoweroff
/dev/btreset
/dev/btwake
/dev/cu.bluetooth
/dev/tty.bluetooth
/dev/uart.bluetooth
Future Work
Moving forward with my analysis of the iOS Bluetooth stack I see the following as interesting next steps:
- Analysis of BTServer and BTLEServer
- Reverse engineering of the Broadcom firmware blobs
As I have previously mentioned, my goal for this analysis is to document and understand the iOS Bluetooth stack. Both of these next steps will help further my understanding and I hope will provide a basis for others to work off of.