One bright idea after another.

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.

Overview of the iOS Bluetooth stack.

Overview of the iOS Bluetooth stack.

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 @ 115200 baud.

When BlueTool is started by launchd it creates an XPC service called 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:

  1. BlueTool starts the XPC service
  2. 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:

  1. The sandbox should block reads to unauthorized files
  2. 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.


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

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 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:

  1. Analysis of BTServer and BTLEServer
  2. 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.