The DJI Assistant PC application and the FC communicate over the USB VCP, the PC application sends commands to FC which then responds. The commands and response packets take the form:
55 AA 55 AA LL LL 00 00 00 CC CC ...... CR CR
55 AA 55 AA - Fixed, four byte start of packet
LL - 16 bit little endian, the length of the packet excluding the four byte start of packet
CC 16 bit little endian, the identity of the command (or response)
CR 16 bit little endian, the CRC of the packet excluding the start of packet
The commands and their responses are identified by a 16 bit number, typically the response identity is the command identity + 1. For example, the Poll command identity is 0x0101 and the response identity is 0x0102
The DJI bootloader offers very few commands but the applications support a very large number. I don't intend to document all of them but instead offer a few of the more interesting commands and their responses here
Poll (Command 0x0101, response 0x0102) The response contains the ten digit serial number and the four byte 'device version' mentioned in the previous post. If the DJI bootloader is being polled then the four bytes contain 0x00000060
Fetch License (Command 0x25C, response 0x25D) The response contains the ten digit serial number, the 32 ASCII characters representing the MD5 digest of the license file and the 16 license flags, also expressed as ASCII chars 0 or 1 (both of these are described in the previous post)
Write License (Command 0x25E, response TBD) TBD
Reset Attempt Counter (Command 0x0260, no response) This command resets the 'serial number attempt counter' to 30. This counter is decremented by one if an invalid md5 hash is sent to the FC
As mentioned previously, the CRC used is the CRC-ITU 16 bit CRC, there is a slight twist here however. The seed used when calculating the CRC changes depending on the FC type. So far I have found the following:
The FC is based on the LPC1768, an Atmel At88SC3216 crypto memory and several sensors that are irrelevant to this post. On the PCB are programming pads that connect to the LPC1768 pins that are required for in system programming (ISP). With some probing it was possible to determine which pad is connected to which LPC1768 pin.
During boot it was observed that the UART is active (115200 8N1), when running the 'lite' version of the FC firmware the message “active security ok!” along with some other strings were captured. If the AT88SC is disabled (shorting SDA and SCL pins) the message changes to “active security failed!”.
By holding P2.10 low while releasing reset the LPC1768 boot ROM enters ISP mode, in the samples I have examined code readout protection (CRP) is set to level 2. CRP2 allows a very limited set of ISP commands, principally CRP2 allows us to erase the LPC1768's flash memory.
In normal operation the LPC1768's boot ROM checks for a valid image in flash, if one is found then control of the CPU passes to user code at flash offset 0, this is the DJI bootloader. Normally the DJI bootloader checks for the presence of an application (a simple blank check on sector 10) and if this succeeds then control of the CPU is passed to the application.
If the application is not present the bootloader starts up its USB interface as a virtual comm port (VCP) and waits commands from the host computer. By linking the F1 & F2 servo connectors we can force the bootloader into recovery mode, the application presence check is skipped and the bootloader starts up the VCP.
Updating The Application Firmware
Updates are downloaded and applied by the PC software DJI Assistant, the PC software performs a check on the currently installed version of the FC application and makes the decision whether to fetch and apply an update.
Using Wireshark it is possible to capture and extract the downloaded binary. When the DJI bootloader is in recovery mode only 3 commands are necessary to download the firmware update and it is relatively straightforward to write a tool to apply an update repeatedly.
The update downloaded from the DJI servers is encrypted and downloaded 'as-is' to the FC in 256 byte chunks, each chunk is acknowledged by the FC. The FC decrypts each chunk and copies it to flash starting at sector 10.
An interesting timing leak was revealed here, the LPC1768 has asymmetric flash sectors. The first 16 sectors are 4KB in size, the remainder are 32KB. The FC typically takes 7ms to process a 256 byte chunk and send an acknowledgement. Some chunks take much longer (70ms) suggesting that the next flash sector is being erased. By comparing the acknowledgement times, amount of data transferred and the flash sector sizes it is possible to deduce that the update is being programmed into flash sectors starting at sector 10. This was subsequently confirmed by reverse engineering the DJI bootloader.
Obtaining The Update Key
I attached wires to the programming pads discussed above and then put the FC into recovery mode, after transferring 4KB of update data I pulled P2.10 low and reset the LPC1768. Using the PC application “Flash Magic” I erased the whole flash and the CRP setting, I was then able to dump the LPC1768's RAM. Searching the RAM dump I identified the AES inverse S-Box and close to it is the update key.
Decrypting the application update using openssl's AES-128-CBC mode yielded a binary file that was mostly correct but had 16 bytes of incorrectly decrypted data every 256 bytes. Further experimentation shows that the application is encrypted in 256 byte chunks with the same key and IV. A simple Python script allows me to encrypt & decrypt the application update
Next I performed a simple test to determine if the FC application is authenticated by the bootloader. I modified the “active security ok!” string, re-encrypted...