A complex, challenging reverse and hijack of a toy tank Nerf gun camera, but the result was we got to shoot the 44Con conference organiser with it!
A remote-controlled Nerf gun with video feed and aiming crosshairs. Who wouldn’t want to reverse the RF and firmware, with a view to hijacking it?
Oddly, rather than using a standard protocol such as Wi-Fi, which is very common in modern drones which utilise a video stream, it uses four fixed channels of RF in the 2.4GHz range. I thought it would be a great example to show a different side of the basics of hardware hacking and use that knowledge to make our own controller. We hope you find the methods and tricks here useful for more mainstream device security reversing!
Taking it apart. What, no SDR?
Initially, we decided to look at it using a USRP, this would have allowed us to analyse the protocol and gain further insight into how the video stream and controls were communicated, but due to the amount of noise in the 2.4GHz range and the lack of available knowledge about the internals of the device, this was found to be not viable.
If in doubt, tear it apart!
This was a risk as the tank was out of production and difficult to buy new, but would allow us to assess what kinds of technology were in use for the radio communication. Upon tearing it apart we noted that there was a conspicuous RF module attached to the board with an Epoxy blob on it, and some integrated circuits for us to analyse. Fortunately there was no need to remove the epoxy!
We also identified an Aveo AV2881 IC and some external memory containing the firmware for the controller. We read the firmware off this, however use of this would not be necessary in reverse engineering the communications.
The firmware was found to be running the full ARM instruction set, something that is uncommon on embedded devices of this type. Commonly, embedded devices of this size running an ARM MCU use an instruction set called Thumb, which uses 16-bit instructions, this is used in order to improve code density over the standard 32-bit instructions used by ARM. This is differentiated by setting the least significant bit of the program counter, due to both instruction sets being aligned to their instruction width.
Incidentally, if you are analysing firmware and you are unsure of the architecture it was built for, the best thing you can do is try to assess whether is it ARM or Thumb code before trying other architectures. It is very easy to identify whether they could be the correct architectures just from the hex dump. If you look at this hexdump from the firmware of the tank controller you will notice that the upper nybble of nearly every fourth byte is an “E”, these denote a number of different operations, including loading, storing, branching and standard arithmetic. When you see a binary file with data represented uniformly like this, you can be reasonably sure that it is running ARM code.
Thumb code is slightly more difficult to identify, mainly due to its smaller instruction set making every bit count, however I tend to identify it via looking for PUSH, POP and Branch and Link instructions. PUSH instructions tend to have a 0xB5 as the second byte, POP instructions tend to use 0xBD and Branch and Link instructions have an 0xFF followed by an 0xF7. There are also BXLR instructions which are identifiable as a hex 0x70 follow by a hex 0x47, however these instructions are far less common. Due to pushes and pops traditionally being at the start and end of function calls, and branch instructions being found in a large number of places, they are useful for identifying code blocks in an unknown binary and confirming whether it is Thumb code.
Gun Controller Logic Analysis
Anyway, back to the controller with a logic analyser: after powering on the controller while analysing the communication with the module, we noted that there seemed to be SPI being used between the two devices. The board is the Master and the Module is the Slave, as is standard for such devices.
Hooking up to SPI
SPI is easy to note as there is always a very visible clock, a chip select being driven high or low depending on the settings. Using the built-in protocol analyser we could see commands being sent to the module and their responses. As we didn’t know the type of module being used at this stage, we decided to assess the commands based on standard usage of other radio modules. By assessing the commands and responses on a per-chip-select basis we managed to note five types of commands in use.
The five sets of commands found:
Register setting commands from 0x00 – 0x3f
Register reading commands from 0x00 – 0x3f, with 0x40 always set
One byte commands were 0x80 is always set
Register read and write commands at 0x45 and 0x05
Analysing these commands allowed us to present the data in a readable format
Analysing the data
Filtering read commands allowed us to view all received data. This showed an incrementing value at the start of each frame which reset, allowing for us to see the start and end of data payloads
Frames can be stitched together, allowing for analysis of each payload in turn
The header of the payload should be assessed first, as this will contain the most relevant data
The main body of this particular payload was made up of seemingly random data:
This could mean it is encrypted (unlikely) or compressed (much more likely)
A compressed payload will have some form of non-random data which could be used to assess the compression being used. An encrypted payload does not require this
Higher up in the payload, there was non-random data, heavily implying that compression was in use on the file:
Options at this point:
If this data was encrypted, it would require assessment of the firmware
The best approach would be to search in the firmware for data relevant to the data being assessed, including loading of the size of it into a register
If this is not viable, search for standard constants or tables used by encryption algorithms, such as AES Sboxes
As a last resort, search in the binary for XOR instructions with a jump instruction following them that jumps to a previous point in the current subroutine, as this will find possible stream ciphers, and some block ciphers in the firmware
However, fortunately the header was found to have all necessary information to avoid the previous steps, showing the following parts in order:
- A 32-bit value denoting the full size of the payload
- A 32-bit CRC for error checking
- A 32-bit value providing the number of blocks the payload was separated into
- And finally a JPEG header
Extracting target video data from the Tank Gun
Removing the first 12 bytes allowed for these JPEG images to be to be viewed, showing that they were 240x180px resolution
A large number of the packets were found to be corrupted, showing broken images and lowering the overall frame rate
This media transport is really poor: the equivalent of drawing a picture on a Post-It note, tearing it to pieces, throwing it at someone, and hoping they can put it back together again. The sheer number of dropped frames between the tank and the controller meant that there were a huge number of corrupted images which lowered the frame rate significantly. The following examples show some of these images we sent from the tank.
This is me getting in some practice:
Tank driving data
Controller data was simpler, with each frame containing all of the data required to control the tank
Followed standard practice for controllers
8-bit values used for analogue controls, with the neutral value being 0x80
Single bits being set and unset for buttons
A single byte checksum used for data integrity, consisting of a sum of all control values
The two analogue controls were found to actually be the speed settings for the two tank treads
The tank crashed upon rapidly changing the speed of the wheels
Controls were only sent when specifically requested by the tank in the received data by setting a bit
So how could we now send commands drive the tank?
In order to find which IC this module was using, we started looking through each of the most common brands of 2.4GHz RF transceivers that could be in use. At the start of this searching we found a very cool device called the IRangeX IRX4, a module for radio control enthusiasts which advertised itself as containing four of these transceivers:
- Texas Instruments CC2500
- Nordic Semiconductor NRF24L01
- Cypress CYRF6936
- Amiccom A7105
However, testing the A7105 by replaying the SPI commands from the logic analyser yielded inconsistent results. We continued to search for more modules but all the ones we found did not meet the exact criteria of the module we found on the controller, we also found that there were a large number of chips in this range that were not available to buy online and didn’t have any public datasheets. So, we bought a spare tank controller and removed the module from it.
Driving the tank from a PC
Initially, we decided to choose between either a Raspberry Pi, or to use an STM32 development board. Using a Raspberry Pi was found to not be effective due to the extremely tight timings required for communication, however an STM32 was perfect.
STM development boards are incredibly effective at prototyping embedded projects as there are a huge number of them each with different capabilities and interfaces attached to the board, they also have an extremely reasonable price point. I elected to use the ST NUCLEO-L496ZG due to its large quantity of memory and built-in USB port, allowing us to make a USB adapter which allowed for control of the tank. The most key thing about them though, is that they are found in many IOT devices and other embedded electronics, and knowledge about how to work with them will provide valuable information on how to hack them.
STM32 boards are extremely easily set up, with tools like the STM Cube allowing for precise definitions of what each pin does
After creating the project, we programmed the board to replay the initialisation SPI commands, and then send and receive the appropriate packets using the protocols specified by our logic analysis
We found that the module starts in Half-Duplex SPI mode and that the registers set one of the general purpose pins to this function
With this in place, we sent the received images to the USB host and received controls back
We then put these images and controls into a browser application:
Upgrading the tank
The track drive motors were not being driven to full speed, which we thought was a little odd. Could we turn them up to 11?
The drive speed was passed here, with 0x80 being the ‘neutral’ value – lower was reverse, higher was forward:
The highest value we saw sent using the controller was 0xD0.
So we sent this:
And it drove about 30% faster!
But then we discovered why the motors weren’t meant to be driven this hard. #fail:
Fuzzing the drive controls
Fuzzing the tank within certain parameters made for entertainment. It would drive & shoot randomly. That was great fun, until it shot my wife. Oops.
Sometimes, IoT reversing projects don’t have a point, but the skills you will learn from them will be very useful on other projects.
So, go pull some smart stuff apart and see what you find. Then have a Nerf gun fight.