[DJI-log-discuss] DJI TXT file format

Daniel Flack dflack95 at gmail.com
Sat Jan 18 22:33:50 PST 2020


There is no such thing as a scramble table. The way DJI actually generates
these keys is to use the appropriate bytes (as mentioned above) to
calculate a 64 bit CRC for the specific packet. This is the same general
process you use to _select_ the right row in your scramble table, except,
the CRC64 algorithm takes the same input and returns the actual key without
using a specific tabular lookup.

Here is a quick write-up I did on the reverse engineering from the DJI Go
application where this encryption method is implemented. The above is a
working PoC (minus a few bits, since I haven't actually fully finished my
parser), this simply covers how I got there.

## Scrambling Process
The scrambling process is handled in the library `FRCorkscrew.so`.
Within this library, there are two functions of interest with respect
to the record scrambling, these are named `decryptFRData` and
`encryptFRData`. From the open source documentation, we can glean that
each record is encrypted with an 8-byte XOR key that is repeated over
the length of the record. Looking at the `decryptFRData` function
first, and reviewing some of the Java code that calls the library, the
following line appears to do the decryption:
`decryptData(decryptedBuf, encryptedBuf, encryptedBufLen, a6, a7);`.
Notice the last two arguments are unknown at this stage, looking into
the `decryptData` function a bit may give some insight.
```c
signed int decryptData(char *decBuf, char *encBuf, int bufLen, int
messageId, __int64 a5)
{
  unsigned int firstPacketByte; // r7
  int crcSeed; // r9
  int v10; // ST10_4
  _QWORD *keyBuffer; // r8
  char *v12; // r5
  int v13; // r6

  if ( check != 1 )
    return -1;
  if ( a5 )
    return 0;
  firstPacketByte = *encBuf; //Dereference first byte in encrypted
data, store in local variable
  crcSeed = messageId + firstPacketByte; //Based on the open source
info, the fourth argument is likely the message id here.
  v10 = encBuf[bufLen - 1];
  keyBuffer = malloc(bufLen - 1); //Initialize space for the generated key.
  v12 = malloc(bufLen - 1); //Create temporary local variable for
storing the decrypted data
  /**
   * Calls the key generation function, of note, the last two arguments.
   */
  getKey(keyBuffer, (bufLen - 1), crcSeed, 0x123456789ABCDEF0LL *
firstPacketByte);
  v13 = bufLen - 2;
  /**
   * Decrypts the buffer. Of note, the second argument which skips the
first byte in the encrypted buffer.
   */
  doXor(v12, (encBuf + 1), keyBuffer, v10, v13, v13 >> 31, v10);
  memcpy(decBuf, v12, bufLen - 2); //Move result to the return buffer
  if ( (unsigned __int8)v12[bufLen - 2] != crcSeed ) //Verifies the
last byte buffer matches the seed (a basic validity check)
    v13 = 0;
  if ( keyBuffer )
    free(keyBuffer);
  free(v12);
  return v13;
}
```

Many lines have been renamed above but, the general idea is that there
is a generation function for the key and then the XOR decryption
process. Before we dig deeply into exactly what's happening, let's
look at the `getKey` function.

```c
int getKey(char *keyBuffer, unsigned int keyLength, unsigned __int8
crcSeed, __int64 a4)
{
  signed int v6; // r10
  __int64 crcValue; // r0 MAPDST
  signed int v9; // r6

  v6 = keyLength >> 3;
  crcValue = crc64(crcSeed, (char *)&a4, _stack_chk_guard, 8);
  v9 = 0;
  /**
   * Extra code omitted, following the above, the CRC result value is
used to generate a key the length of the packet.
   */
}
```
As this shows, the key generation is really a CRC64 function. The
initial CRC value is set to the first byte in the encrypted buffer
plus the message id. And the data for which the CRC is calculated is
the result of `0x123456789ABCDEF0 * first_byte`. This is essentially
the encryption/scrambling method. As seen in the actual record
structure below, the packet before encryption looks like:
```
00 00 00 00 00 00 00 FF
ID LN PS [ data ] CK TM

ID: Packet Type ID
LN: Packet data length (data + PS byte + CK byte), for above it is 5.
PS: Pseudo-random generated number to seed encryption
data: The actual record information
CK: The ID+PS bytes to verify proper decryption
TM: Terminator byte, always 0xFF
```
If the above empty data were encrypted (assuming a key of
`\x01\x02\x03\x04`, not actually generated with the CRC algorithm), it
would look like the following:
```
00 00 00 01 02 03 04 FF
```




On Sat, Jan 18, 2020 at 10:18 PM Ross Finlayson <finlayson at live555.com>
wrote:

>
>
> > On Jan 18, 2020, at 7:45 PM, Daniel Flack <dflack95 at gmail.com> wrote:
> >
> > Ross;
> >
> > Finished this up a while ago, thought the website was updated...the
> email today reminded me. It looks like the scramble table is still the
> setup you're documenting on the website. I've attached general PoC code for
> how to derive the scramble keys (using a CRC 64).
>
> I’m sorry.  How, specifically, does this generate the values of the
> ‘scramble table’: the data that’s in
>
> http://djilogs.live555.com/doxygen/html/scrambleTable_8cpp_source.html
> ?  And how could this generate more rows than the 0x1000 (i.e., 4096) rows
> that we already know?
>
>
> Ross Finlayson
> Live Networks, Inc.
> http://www.live555.com/
>
>
> --
> DJI-log-discuss mailing list
> DJI-log-discuss at lists.live555.com
> http://ns.live555.com/mailman/listinfo/dji-log-discuss
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://ns.live555.com/pipermail/dji-log-discuss/attachments/20200118/18b251f4/attachment-0001.htm>


More information about the DJI-log-discuss mailing list