Skip to content

Latest commit

 

History

History
393 lines (286 loc) · 16.7 KB

behringer-deq2496.md

File metadata and controls

393 lines (286 loc) · 16.7 KB

Behringer Ultracurve Pro DEQ2496

Hardware

  • 2x ADSP-21065L "SHARC" - DSPs (PDF)
  • ADSP-BF531 "Blackfin" - microcontroller (PDF)
  • ISSI IC42S16100E-7TL - 2 MB SDRAM (512K * 16-bit * 2-bank = 16Mbit)
  • SST39SF040 - 512 kB flash
  • AK5393VS - ADC, 24 bit 1 kHz to 108 kHz I²S
  • AK4393VF - DAC, 24-bit 108 kHz I²S
  • AK4524 - 24-bit 96 kHz audio codec with differential outputs
  • AK4114 - AES3/SPDIF transceiver

Additional functions

  • Holding the MEMORY button while powering on the unit will reset most of the settings back to the default, but it will keep the presets untouched.

  • Holding the COMPARE and MEMORY buttons while powering on the device will ask whether you want to wipe all the presets. You must press a button to confirm before the presets are wiped.

  • Holding the UTILITY button while powering on the device will enter the bootloader (see below). Power cycle it again to return to normal operation.

Bugs

Firmware 2.5

  • You can use the setMIDIChannel command to set a channel up to 128, even though channels above 16 are invalid. When a channel above 16 is set, the device only responds to device ID 1.

  • The getScreenshot command returns 7 bytes short, so the last 49 pixels of the screenshot are missing. Most screens don't use the last row of pixels so it's not noticeable. You can however see it on RTA page 3.

  • The getSinglePreset command seems to return incomplete data, as the preset title is truncated to 10 characters. Writing a preset can set a title longer than 10 characters however.

Bootloader

The bootloader can be used to reflash the firmware, even if a previous flash was unsuccessful (as long as the part of the flash holding the bootloader was untouched). The official firmware images never appear to touch the boot loader so these should never brick the device.

In the bootloader, the device responds over MIDI to a subset of the normal functions. It also identifies itself with the model DEQ2496V2 BOOTLOAD rather than the usual DEQ2496.

Flash layout

This differs from the official SysEx doc. Perhaps the doc is for hardware V1.

  • 0x00000 - 0x03FFF (16 kB): Bootloader
  • 0x04000 - 0x73FFF (448 kB): Application, only 0x4000-0x5B000 (348 kB) is used
  • 0x74000 - 0x7BFFF (32 kB): User presets
  • 0x7C000 - 0x7DFFF (8 kB): Scratch space (contains old presets)
  • 0x7E000 - 0x7FFFF (8 kB): Boot logo (320×80 1bpp bitmap, 3200 bytes)

The application segment is the only part reflashed by the official firmware releases, which means if the process fails, the bootloader will be left intact so further attempts at reflashing are possible.

Encryption

Run time

There is a lot of obfuscation of the firmware, however it is all done with simple XOR ciphers so it is for the most part trivial to undo.

The application segment in the firmware is XOR encrypted, and this is decrypted by the bootloader during startup. The decryption key is itself encrypted within the bootloader code:

  • Offset 0x3002, length 0x38: Bootloader key
  • Offset 0x303A, length 0x38: Application key, encrypted with bootloader key

The application key can be recovered by XORing it with the bootloader key, which reveals - ORIGINAL BEHRINGER CODE - COPYRIGHT 2004 - BGER/TZ - \x00 (note trailing space followed by a null byte).

This cleartext application key must then be XOR'd over the data in the application segment to reveal the cleartext application code.

Reflash

When the firmware is reflashed via the MIDI interface, multiple levels of encryption are again used. The process happens as follows:

  1. The device receives a MIDI SysEx event number 0x34 ("write firmware block").

  2. The 7-bit SysEx data is converted back to 8-bit data, by taking every eighth byte and using its bits as the MSB bits of the preceeding seven bytes. Thus every eight bytes in the SysEx event produce seven usable bytes. See sevenEightCoder.js for example code.

  3. An XOR cipher with the key TZ'04 is applied to the data block to decode it.

  4. The block structure is now a UINT16BE block number, then a UINT8 crc, followed by 256 bytes of data.

  5. The checksum byte is checked. The Behringer docs call this a CRC but in reality it's a homebrew checksum. See checksumTZ.js for how to calculate it. Contrary to the Behringer docs, the block number is not included in the checksum and only the 256 data bytes are used.

  6. The devices stores the 256 bytes into memory (not flash) and does not respond yet.

  7. After the 16th block has been received (4 kB) another XOR cipher is applied to the 4 kB block as a whole. This algorithm uses the block's destination flash address as the key, rotating the key and flipping some of its bits as it goes. Unlike the earlier ciphers this one works at the 16-bit level rather than at the byte level. See midiFirmwareCoder.js for the implementation.

  8. The 4 kB block is then written to the flash chip and an acknowledgement is sent back as a SysEx event, and a message is shown on the LCD.

  9. Note that the 4 kB block actually written to flash is still encrypted, as the bootloader decrypts it when copying the data from flash into RAM at boot up.

Writing to the special block number 0xFF00 causes the device to display the ASCII content on the LCD screen. The stock firmware image contains extra blocks at the beginning and end of the firmware data to write messages indicating the process is beginning and has completed. When flashing from within the application, the message gets overwritten during the flash process as each 4 kB block written to flash causes a message with the block number to be displayed on the LCD, overwriting the previous message. When flashing from the bootloader however, the message remains visible as a graphical representation of the blocks being flashed is shown instead, which does not overwrite the last message. Thus with some creativity, one could write progress messages throughout the firmware image such that a progress meter or "percentage flashed so far" information is shown throughout the procedure.

Firmware dumps

Images

When extracting firmware images, the official firmware releases typically only update the 'application' portion of the flash chip, leaving the areas in the flash storing the bootloader and user presets unchanged.

When dumping one of these images through the CLI with the firmware examine command, there are a few options:

Index     Offset  Available       Used   % Image name
   -1        0x0     524288     524288 100 (raw dump of flash chip content, see docs)
    0     0x4000     458752     356352  78 Application (raw)
    1     0x4000     458752     356352  78 Application (decrypted)

Extracting image 0 will write the same data to a file that would be written to the flash chip, except that the data will be written to the start of the file (offset 0) but it would go into the flash chip beginning at offset 0x4000, just after the bootloader code.

Extracting image 1 will do the same, however a decryption will be performed to reveal the cleartext code. This data is not written to the flash, but at power on, the bootloader performs this decryption when it copies the application code into RAM. So the data written to the file in this case is what ends up in the device's memory, being executed by the processor. If you intend to disassemble the code, this is the image to use.

Image -1 will provide a full dump the size of the flash chip, with any missing blocks filled with 0xFF bytes, to simulate empty flash blocks. This will result in the application data at offset 0x4000 being written to offset 0x4000 in the file (and it will be encrypted, just as it is in the real flash chip), however any data missing from the source file (e.g. the bootloader code itself) will be replaced with 0xFF bytes in the output file. Although the output file will be the same size as the flash chip, be careful not to flash this if there are missing sections, otherwise you will erase the bootloader and the device will no longer function, until an EEPROM programmer is used to reflash the missing bootloader.

If you have a full firmware dump of the flash chip taken with an EEPROM reader, then image -1 will just give you the same file back again unchanged.

Disassembly

To disassemble the code, a Blackfin disassembler is needed. The GNU GCC project used to have support for the Blackfin ISA, however this is now discontinued. An older version of GCC can still be used to disassemble the code however, and a Docker container exists to make this process very painless.

Once you have Docker installed and the ability to run containers, load the pf0camino/cross-bfin-elf container:

docker run -p 1222:22 -v /home/user/blackfin-projects/:/projects/ --rm=true pf0camino/cross-bfin-elf

Replace /home/user/blackfin-projects/ with a path on the host machine where data will be shared with the Docker container. The firmware files can be put in this folder on the host, where the disassember in the Docker container can read them. The disassembly output inside Docker will also be written here, where it can be accessed on the host machine as well, even after the Docker container has been terminated.

In another shell, connect to the container via SSH:

ssh user@localhost -p 1222
cd /projects

The default password is secret.

To disassemble a raw firmware dump, copy the file into the project folder and then inside Docker, use the objdump command:

bfin-elf-objdump -D -b binary -mbfin bootloader.bin > bootloader.disasm

To compile your own code (untested) you should be able to do something like this:

bfin-elf-gcc -mcpu=bf531 -o example.elf example.cpp
bfin-elf-objcopy -I elf32-bfin -O binary example.elf example.bin

You will need to encrypt the binary before flashing it to the chip.

Bootloader

The bootloader isn't a raw image file but a kind of container holding multiple images (like a .zip file but without any compression). The Blackfin boot sequence reads the headers in this data that dictate which blocks of data are written to which memory address at power up.

The CLI can display these headers but not yet build a bootloader image from raw files:

behringerctl firmware examineBootloader --read bootloader.bin

Entrypoint: 0xffa08000
Index    Address       Size Flags
    0 0xff800000          4 IGNORE flash=8-bit
    1 0xffa08000        278
    2 0xffa08000          2 INIT
    3 0xff800000          4 IGNORE
    4 0x00408000      11024
    5 0x0040ab10      26492 ZEROFILL

For more information on the addresses and flags, refer to the section on booting in the Blackfin architecture manual.

LCD messages

A special SysEx message can be sent to the device to write a message to the LCD screen. This is used to write a message at the start and end of the firmware update process (although strangely it is not used to give progress updates).

When examining a SysEx firmware image (*.syx), these messages are displayed of the form 0="example", 10="test" which means example is written to the LCD screen before any firmware blocks are sent, and test is written to the screen after the 10th block has been sent. Here, a block is defined as a single SysEx message writing a 256-byte block of data.

MIDI interface

SysEx

The official Behringer document is mostly correct, although it's for the DEQ2496v1 rather than v2.

0x26 Unknown

The device sends a response to this message. Its purpose is currently unknown.

0x27 Unknown

The device sends a response to this message. Its purpose is currently unknown.

0x28 Unknown

The device sends a response to this message. Its purpose is currently unknown.

0x2a Unknown

The device sends a response to this message. Its purpose is currently unknown.

0x36 Unknown

The device sends a response to this message. Its purpose is currently unknown.

0x35 writeFlashResponse

No parameters.

After the 16th 256-byte subblock has been written with writeFlashBlock(0x34), the 4 kB block is flashed to the flash chip and writeFlashResponse(0x35) is returned on success.

0x62 Unknown

This event's purpose is currently unknown.

Reflashing safely

Flash addresses 0 to 0x4000 are used to store the bootloader. As long as you never write to these addresses, the chances of bricking the device are tiny. Providing the bootloader remains intact, you will always be able to use it to reflash the rest of the chip by following the official firmware flashing procedure, which steps you through accessing the bootloader and performing the flash. This will allow you to return the device to a working state.

Things like the boot logo (and the bootloader itself) are not part of the official firmware releases, so if you change these you will need to keep a copy of the original in order to restore it should you wish.

If you flash something to the device that does not work, then providing the bootloader is left intact, you can always boot into it and reflash again. The device will never be bricked permanently so long as the bootloader is left alone.

If you do accidentally overwrite the bootloader, then the only way to unbrick the device is to open it up, remove the SST39-series flash chip, and use an external EEPROM programmer to reflash it. These are available cheaply online.

However you will need to reflash the corrupted bootloader at address 0, which means you will need a copy of the bootloader ROM. This is not part of any official firmware release, because the official images do not touch the bootloader. This is to ensure there is no way to brick the device from a failed flash, as a working bootloader means the unit can always be recovered without opening it.

Other options are available if you have the bootloader data but cannot get an EEPROM programmer. Programs like Flashrom support many unconventional devices, and you can always hot swap the flash chip with another DEQ2496 if you can get access to a second one. This is done by putting the good flash chip gently into the socket of the failed unit, powering it up into the bootloader, then carefully with the power on, removing the good chip and putting the original failed chip back in again. Then the bootloader can be reflashed, returning the device to working order.

Examples

Changing the boot logo

This relatively harmless change only reflashes the blocks used for the boot splash screen. If corrupt data is flashed it will not cause problems, just display a corrupted splash screen during boot.

First, extract the existing boot logo from the firmware image. You will need a full dump of the flash chip for this, as the boot logo is not included in any of the official firmware images. This is also a good idea anyway, as having a full firmware dump (and an EEPROM programmer to write it) means you can unbrick the device if any mistakes are made during the reflashing procedure.

behringerctl firmware examine \
    --model DEQ2496v2 \
    --read firmware.deq2496.v2.5.rawdump.bin \
    --extract-index 7 \
    --write bootlogo.png

Modify bootlogo.png with an editor like GIMP, ensuring it remains in the same subformat (colour depth).

Next, convert the modified image into a firmware package. Here we are writing the logo to the address shown by firmware examine, so that we won't be touching any other part of the firmware. This does not flash anything to the device yet, it just produces a .syx file containing the instructions for updating the flash chip.

behringerctl firmware generate \
    --model DEQ2496v2 \
    --read bootlogo.png \
    --address 0x7E000 \
    --messages "0=CHANGING BOOT LOGO, 8=ALMOST THERE..., 16=NEW BOOT LOGO FLASHED" \
    --write pngboot.syx

Next to be safe we should dump this new firmware image to ensure that it looks as expected.

behringerctl firmware examine \
    --model DEQ2496v2 \
    --read pngboot.syx \
    --extract-index 1 \
    --write pngboot-extracted.png

Ensure the address matches the address the original boot logo was extracted from, and that the resulting pngboot-extracted.png contains the expected new picture to be flashed.

If everything looks good, the file can be immediately flashed with your preferred SysEx tool as per the official flashing procedure, or you can continue to use the CLI:

behringerctl devices sendsyx --read pngboot.syx

There is no need to indicate the model number as this is encoded into the .syx file.