Reverse Engineering the iBeacon Profile
When Apple announced iBeacons at WWDC back in June, many of us developers were excited about them. Apple made a demo program available, AirLocate, which could make an iPhone or iPad act as an iBeacon for development purposes. But there were lots of problems with using this for development.
First, many of us don't have a pile of extra iOS devices lying around -- especially the latest and greatest ones with the Bluetooth LE chipset (iPhone 4s+, iPad 3rd generation+) needed to run AirLocate. Unless you have two such devices (one to act as an iBeacon and the other to host your app under development), you have to buy another one for $600+. Second, you have to be a member of the iOS Developer Program to get Air Locate on your iOS devices. That usually isn't the case for Android developers. Finally, what if you want to build an iBeacon on your own hardware? Apple so far hasn't told us how to do that.
The first step to solving these problems is to figure out how iBeacons work. That means it's time to do a little reverse engineering.
The Reverse Engineering Procedure
Knowing that iBeacons use Bluetooth LE, we figured you could use a bluetooth sniffer to figure out what is going on. Texas Instruments sells a Bluetooth LE designed for development tasks like sniffing, so we ordered one to see what we could do.
- iPad 3rd generation
- Apple's AirLocate program, installed on the iPad
- Windows computer
- Texas Instruments CC2540 Bluetooth LE dongle, inserted into the Windows computer
- TI SmartRF Packet Sniffer tool, loaded on the Windows computer
We started up the packet sniffer tool, selected "Bluetooth LE" as the protocol, and started a capture. Not much was going on. Then we fired up AirLocate, configured it to act as an iBeacon using AirLocate's default proximityUUID E2C56DB5-DFFB-48D2-B060-D0F5A71096E0, major 0 and minor 0. As soon as we did this, we started seeing new bluetooth packets once per second.
The results on the screen were encouraging. It showed the raw hex bytes in the captured packets, and it was obvious that they included the proximityUUID. But we needed to export the a full capture file to see if this was consistently true.
The packet sniffer tool let us output its capture in a binary format that I had to write Python script to parse. Once I did, I could see the contents of the individual packets as hex bytes:
0 1 0us (+ 0.000ms) [ 47] d6 be 89 8e 40 24 5 a2 17 6e 3d 71 2 1 1a 1a ff 4c 0 2 15 e2 c5 6d b5 df fb 48 d2 b0 60 d0 f5 a7 10 96 e0 0 0 0 0 c5 52 ab 8d 38 a5 0 2 31252us (+ 31.252ms) [ 47] d6 be 89 8e 40 24 5 a2 17 6e 3d 71 2 1 1a 1a ff 4c 0 2 15 e2 c5 6d b5 df fb 48 d2 b0 60 d0 f5 a7 10 96 e0 0 0 0 0 c5 52 ab 8d 38 a5 0 3 66253us (+ 35.001ms) [ 47] d6 be 89 8e 40 24 5 a2 17 6e 3d 71 2 1 1a 1a ff 4c 0 2 15 e2 c5 6d b5 df fb 48 d2 b0 60 d0 f5 a7 10 96 e0 0 0 0 0 c5 52 ab 8d 38 a5 0 4 100004us (+ 33.751ms) [ 47] d6 be 89 8e 40 24 5 a2 17 6e 3d 71 2 1 1a 1a ff 4c 0 2 15 e2 c5 6d b5 df fb 48 d2 b0 60 d0 f5 a7 10 96 e0 0 0 0 0 c5 52 ab 8d 38 a5 0 5 132502us (+ 32.498ms) [ 47] d6 be 89 8e 40 24 5 a2 17 6e 3d 71 2 1 1a 1a ff 4c 0 2 15 e2 c5 6d b5 df fb 48 d2 b0 60 d0 f5 a7 10 96 e0 0 0 0 0 c5 52 ab 8d 38 a5 0 6 167505us (+ 35.003ms) [ 47] d6 be 89 8e 40 24 5 a2 17 6e 3d 71 2 1 1a 1a ff 4c 0 2 15 e2 c5 6d b5 df fb 48 d2 b0 60 d0 f5 a7 10 96 e0 0 0 0 0 c5 52 ab 8d 38 a5
As you can see from the above, the same packet is repeated over and over again. And this packet contains the profile UUID of the iBeacon (E2C56DB5-DFFB-48D2-B060-D0F5A71096E0)
# Packet Header Starts Here d6 be 89 8e # Access address for advertising data (this is always the same fixed value) 40 # Advertising Channel PDU Header byte 0. Contains: (type = 0), (tx add = 1), (rx add = 0) 24 # Advertising Channel PDU Header byte 1. Contains: (length = total bytes of the advertising payload + 6 bytes for the mac address.) 05 a2 17 6e 3d 71 # Mac address.... note this does not seem to be the actual bluetooth mac of the iPad -- apparently it gets spoofed by iOS # Actual Advertising Data Starts Here 02 01 1a 1a ff 4c 00 02 15 # Apple's static prefix to the advertising data -- this is always the same e2 c5 6d b5 df fb 48 d2 b0 60 d0 f5 a7 10 96 e0 # iBeacon ProximityUUID 00 00 # major (LSB first) 00 00 # minor (LSB first) c5 # The 2's complement of the calibrated Tx Power # Actual Advertising Data Ends Here 52 ab 8d 38 a5 # CRC
To validate that this was what was going on, we tried changing different values of major, minor and the profileUUID, and they changed exactly as we expected.
Success! We had reverse engineered the iBeacon bluetooth profile.