Platform index / Digital mixing console
Behringer / Midas X32 / M32
The container is solved to the byte and every payload inside it is encrypted — so the update image is simultaneously the most transparent and the least readable in this index. It is also the only platform here where a third-party project runs its own operating system on the hardware.
The load-bearing finding. The update image carries integrity but no authenticity. Its two header digests are plain MD5 and both reproduce exactly — md5(" - designed by Thomas Zint - " ‖ image with bytes 96–112 zeroed) for the whole image, and md5(directory) with a placeholder constant re-planted at offset 112 — yet nothing in the image is signed. That is why a third-party OS boots on a stock console: the bootloader checks that an image is intact, never that Behringer wrote it.
Verdict. A firm negative on payload content and a complete positive on everything around it. The container is solved to the byte, the manifest is a board-level map of a decade-old product line, and the update path turns out to authenticate nothing — which is the finding that matters, because it is why this platform has the most third-party open-source activity of anything in this index. The cipher stands.
Method
Static analysis of one public firmware update image, dcp_corefs_release-4.13-20240415.update (release 4.13, 2024-04-15), plus the published release notes. No hardware was involved and no console was opened. Findings were then cross-checked against the OpenX32 project's published material, which is written by people who do have the hardware — that cross-check corrected three claims and is recorded in the traps below. The scene-file findings come from a separate basis: .scn files read directly, with the enumerations settled by falsification against a known-good fixture rather than by finding a table.
Artifacts examined
dcp_corefs_release-4.13-20240415.update, 7,829,504 bytes, as published by the manufacturer- The seven-page release-note PDF accompanying that version
dcp_compiler.planddcp_decompiler.pl, published by the OpenX32 project with the original author's permission- A
.scnscene file from a 32-channel console — about 2,100 lines of line-oriented ASCII, and the fixture every enumeration below was checked against
No manufacturer firmware, binaries or documentation are redistributed here. Artifacts are referenced by name and version only. See the method and legal statement for the basis on which this analysis was performed and the boundaries it observes.
Hardware architecture
| Item | Finding | Confidence |
|---|---|---|
| CPU | NXP i.MX253, ARM926EJ-S. Nothing in the firmware image names it — this comes from the OpenX32 project, which runs Linux on the part | Public |
| DSP | 2 × Analog Devices ADSP-21371 SHARC. DSP1 carries the 40-channel mix path, DSP2 the effects — which is exactly the split the image's mixdsp_image_48k_2 and fxdsp_image_48k_2 members imply | Public |
| DSP images | Two separate images, both tagged 48k, so per-sample-rate DSP builds. The mixing image (50,776 B) is larger than the effects image (32,256 B), suggesting the effects engine is a framework loading coefficient sets rather than 54 discrete algorithms | Inferred |
| FPGA | Xilinx Spartan-3A XC3S1400, second-sourced to a Lattice ECP5 during the component shortage. The image ships both bitstreams side by side and the 4.11 release notes say "Support new ADAT card fpga chipset"; OpenX32 states the shortage as the reason outright | Public |
| Audio I/O | 112 in / 112 out — 32 XLR in, 16 XLR out, 16 UltraNet, 8 aux, 32 card, 40 DSP | Public |
| Board controllers | Five named beyond the fader and channel-edit boards: core_A, userctrl_A, m32c_A, mcu_ui_A, rack_A. The three at ≈33 KB are close enough in size to suggest one firmware base built for three boards | Inferred |
| Fader boards | Revision letters A, M, O, R and T on the left and right banks, three on the middle bank. Revision A images are ≈34 KB and M/O/R/T ≈36 KB — a 6% step that suggests a different microcontroller part, not just a different PCB | Inferred |
| Product variants | Eight .init files in one image — X32, X32C, X32P, X32RACK, X32CORE, M32, M32C, M32R. Their sizes fall into three groups that pair each Behringer model with its Midas equivalent | Verified |
Software architecture
| Item | Finding | Confidence |
|---|---|---|
| Container | "DCP" — a 128-byte header record followed by 197 member records of 128 bytes each, little-endian, then block-packed payloads. Name (87 bytes + NUL), start block (×512), length in bytes, then 32 bytes of digest. Fully solved; the layout closes to the byte against the file length | Verified |
| Container — digests | The 32-byte field is two MD5s, not one 32-byte hash: md5(first 512 bytes, zero-padded) then md5(whole member), both computed over the plaintext before encryption. Confirmed against the published 2012 compiler and verified by reproducing both header instances exactly | Verified |
| Version string | 4.13-0-g2e3bea4e at offset 0 — git describe output, so releases are built straight from a tag | Verified |
| Members | 197: 32 in binary/, 76 channel presets, 54 effect presets, 6 monitor, 4 routing, 4 index files, 11 in prefs/, 9 splash bitmaps, and one 2,829,090-byte styles/ablesque.rsrc.z | Verified |
| The resource member | styles/ablesque.rsrc.z is 36% of the image and by far the largest object in it — larger than the application, both FPGA bitstreams and both DSP images combined. The X32 boot screen's "Loading resource file …" is presumably this. Encrypted like everything else, so the .z is unconfirmed as zlib | Verified |
| Payloads | All 197 encrypted. Entropy 8.000 bits/byte on the 1.49 MB application, χ² = 235.7, and 0 repeated 16-byte blocks out of 487,743 across the whole image. Member lengths are not block-aligned, so this is a stream cipher or a stream mode, not ECB or CBC | Verified |
| Keystream | No reuse. The 39,176 bytes of inter-member padding are almost certainly plaintext zeros and are encrypted too, giving raw keystream at known offsets — and all of those regions are distinct, with no period detectable up to 64 KiB | Verified |
| Key derivation | The published compiler derives a per-member key as md5(version string ‖ member name ‖ " - designed by Thomas Zint - "), unpacked to four 32-bit words, alongside the member's start block. Every one of those inputs is plaintext in the shipped image. The encryption step itself is absent from the published source, and eight natural constructions over that key material score at chance against the padding | Inferred |
| The application | dcpapp.bin, 1,491,840 bytes. There is no kernel, filesystem or library member anywhere in the image, so the stock software is most likely bare-metal or an RTOS rather than a Linux userland — a real architectural difference from the HD96 and DM7, which both ship a rootfs measured in tens of megabytes | Inferred |
| Third-party OS | OpenX32 replaces all of it with Linux 6.18 LTS, BusyBox and an LVGL interface, booting from USB via the stock DCP bootloader | Public |
| What the console writes to disk | No container at all. A scene is line-oriented ASCII: one OSC-like path per line followed by whitespace-separated tokens, double-quoted where a value contains spaces. No compression, no encryption, no checksum — about 2,100 lines for a 32-channel console. The header line carries a format version (#2.7#, the only one seen), the scene name, a note field, and a recall-scope bitmap that has not been decoded. The console with the least readable firmware in this index writes the most readable state file in it | Verified |
| The patch chain is three hops, and all three matter | connector → routing block → input slot → channel source → channel. /config/routing/IN names five blocks — four of eight connectors each onto input slots 1–32, plus the rear aux inputs, which sit outside the slot space — and the blocks are freely assignable, so slots 25–32 can sit on AES50-B. Each channel then selects which slot feeds it, also freely: in the fixture six channels select a slot no other channel uses, and six slots reach no channel at all. A straight "channel N takes XLR N" reading is wrong on this console and gives no sign of being wrong | Verified |
| Head-amp state is keyed by connector, not by channel | 128 flat entries of gain and phantom — 0–31 the local inputs, 32–79 AES50-A, 80–127 AES50-B — stored whether or not the show uses them. Preamp state therefore belongs to the socket and survives a repatch, which is the opposite of how a channel-keyed console behaves and the thing most likely to be got wrong when converting between the two | Verified |
| The output enumeration, settled by falsification | 0 off, 1–3 the main bus, 4–19 mix bus 1–16, 20–25 matrix 1–6, 26–57 channel direct outs. The competing reading — 1–16 mix, 17–22 matrix — is not rejected on preference but because the fixture contains output values 23 and 24, which under it decode to matrix 7 and matrix 8 on a console with six matrices. The accepted mapping is then corroborated positively: Ultranet port 1 decodes to channel 1's direct out tapped pre-EQ, which is the conventional default, and all 42 output rows in the fixture land inside the mapped range with no fallback taken. Values 58 and above are unmapped and are reported rather than guessed | Verified |
Update path & security model
| Item | Finding | Confidence |
|---|---|---|
| Image integrity | Two MD5 digests in the header — one over the directory, one over the whole image with a fixed seed string prepended. Both verified. MD5 and a published constant, so this is a corruption check, not a security boundary | Verified |
| Image authenticity | None found. No signature, no certificate, no asymmetric key anywhere in the container. That an unrelated project's self-built images boot on unmodified consoles is the practical demonstration | Inferred |
| Payload confidentiality | A competently applied stream cipher, unbroken here. No reused keystream, no ECB structure, no short period, and no plaintext anywhere except the directory | Verified |
| Where the key is | Not, as this analysis first concluded, only on the console. The published compiler shows the key deriving from fields that ship in the clear in every image. What is missing is the construction that turns that key into a keystream | Inferred |
These rows describe how a platform validates a firmware image, because that is a structural fact about its architecture. They are not a vulnerability disclosure and no exploit, bypass or circumvention technique is published here. Licensing and entitlement mechanisms are out of scope throughout — seescope boundaries.
What is programmable
- 1The DCP bootloader, via a self-built container
The format is fully documented above and the payloads inside a self-built image need no encryption, because the bootloader does not require any. A
.runextension executes from RAM and reverts on power-cycle;.updatewrites to the internal SD card. This is the route OpenX32 takes and it needs no exploit — only the format. - 2The two SHARCs and the FPGA, over internal SPI
Both DSPs and the FPGA are configured by the main CPU over SPI at boot, so whatever controls the CPU controls them. The toolchains are the obstacle rather than the access: CrossCore for the SHARCs and Xilinx ISE 14.7 or Lattice Diamond for the fabric.
- 3The encrypted payloads
Not currently reachable. The container around them is solved, the key derivation inputs are known, and there are two independent offline oracles for testing a candidate — but the cipher construction is not published and was not recovered here.
- 4The scene file, which is a text file
Everything else on this list is behind a stream cipher or an undocumented SPI link. The console's own state is not: it is ASCII, unsigned and unchecksummed, so reading a show, rewriting a patch and handing it back is a text-processing job. Our own PatchFerret ships the adapter. The limit is knowledge rather than access — the output routing blocks on the serial links name groups of definitions rather than individual sources, and expanding those into per-connector rows is the one piece of work that would say what is actually on each stage-box return.
Third-party open projects
Independent open-source work on this platform, recorded because it is prior art and because in places it confirms or corrects findings above. These are other people's projects: the descriptions are ours, the work is theirs, and nothing from them is reproduced here.
OpenX32
A complete replacement operating system for the X32 and M32: Linux 6.18 LTS and BusyBox on the i.MX253, an LVGL interface at 30 fps, and the console's own audio path under its own control. It self-assesses at roughly 55–65% of the stock firmware's features and boots from a USB stick without recompiling anything.
- Drives both FPGA variants (Spartan-3A and ECP5) and both SHARCs over internal SPI, with 1:1 routing across all 112 inputs and outputs, and gate, 4-band EQ and compressor per channel running in the main DSP.
- AES50 works on the Lattice builds, including using the aux channel for headamp control — the AES50 core is a separate VHDL project in the same organisation.
- It publishes
dcp_compiler.pl, the original 2012 Behringer tool, with the author's permission. That is the single most useful artifact on this platform: it documents the container format from the vendor's side and names the key-derivation inputs. - It does not decrypt vendor payloads and does not need to — its own images are built plaintext, which is what demonstrates that the bootloader never checks authenticity.
OpenMixerControl
The control application shared by OpenX32 and OpenWING, abstracting three different consoles behind one codebase and also building for a PC via SDL2.
- This is the part that makes the family interesting structurally: the surface, the metering and the mix model are portable, and only the hardware layer is per-console.
- It is where the X32 work and the WING work actually converge — the same application is the target on both platforms.
Licence note: these projects are GPL-3.0. Facts about them — part numbers, what boots, what does not — are not copyrightable and are recorded freely. Their source is not a source for our own differently-licensed work, and no code from them is used in it.
Traps
Mistakes this analysis actually made, or came close to making. They are recorded because each one produces a plausible-looking wrong answer rather than an obvious failure.
- Counting the header as one of the members. The header's count field reads 197, and it is tempting to read that as one header record plus 196 members. It is not: the header occupies its own 128 bytes and 197 members follow. Getting this wrong drops the last member — which here is the largest object in the image — and the bytes it occupied then look like an unexplained 2.8 MB tail past the end of the table. That produced a confident, wholly fictitious finding, complete with three hypotheses about what the mystery region might be. The correct accounting closes to the byte with nothing left over, and that exact closure is the check that would have caught it immediately.
- Assuming a 32-byte digest field is a 32-byte digest. Every SHA-256 and SHA-512 candidate was tested against it and all were rejected, which correctly suggested the digests were over plaintext — but for the wrong reason. The field is two 16-byte MD5s, and the first covers only the leading 512 bytes. A test that had split the field in half would have identified the algorithm on the spot.
- Recomputing a self-referential digest without reconstructing the state it was computed in. The header's directory digest is
md5(directory)taken while offset 96 was still zeros and offset 112 heldmd5("DCP bootloader")as a placeholder. Hash it as it sits in the shipped file and it mismatches — which reads exactly like "this is not a digest of the table", the wrong conclusion. Both header digests reproduce once the write order is modelled. - Reading entropy ≈ 8.0 as proof of encryption. The largest member is named
.zand compressed data measures the same as encrypted data on every statistic used here. What actually settles it is the MD5 oracle: the stored digests are over plaintext, so if a member were merely compressed its digests would reproduce from the bytes as they sit. For all 197, they do not. - Treating a third-party project as competition rather than as instrumentation. Three claims here — the DSP split, the second-source FPGA and the digest format — were confirmed or corrected against OpenX32's published material in minutes. A project with the hardware on the bench is the cheapest verification available to an analysis that has only a file.
- Tokenising the scene file without respecting quotes.
""is a real, empty token — most channels are unnamed — and dropping it shifts every field after it on that line, which produces a plausible decode rather than an error. - Counting output rows by counting lines.
/outputs/main/NNis followed by its own/delayline and/outputs/p16/NNby an/iQline, so a naive count double-counts two of the five output sections.
Open questions
- What construction turns the derived key into a keystream? The inputs are known and the padding gives a known-plaintext oracle at 39,176 known offsets, so a candidate can be confirmed or rejected entirely offline, with no console involved.
- Does
styles/ablesque.rsrc.zdecompress to a resource bundle once decrypted, and does it carry the parameter model? At 36% of the image it is the largest single object and nothing is known about its contents. - Do the two per-member MD5s let a decryption be confirmed member by member? They are over plaintext, so yes in principle — the first covers only the leading 512 bytes, which makes partial verification cheap.
- What changed between 4.12 and 4.13? Diffing two releases would show which members moved and the release notes say what changed functionally, mapping members to features without decrypting anything. It costs one more download and remains the highest-value next action.
- The output routing blocks on the AES50 links, the card slot and the local outputs. They name groups of output definitions rather than individual sources, so expanding them needs a second resolution pass — the largest single gap in the scene format, and the one that would tell an engineer what each stage-box return carries.
- Whether the M32 file is byte-identical to the X32's. Believed to be, never verified — and the same assumption applied to the X-Air range is weaker still, since the consoles have different I/O counts, so a sniff would succeed and the output could be quietly wrong.
- Older scene-format versions. Only
#2.7#has been seen, and nothing is known about what changed before it.
Status of this entry. Substantial findings recorded, but whole subsystems remain unexamined. Nothing on this page has been verified against physical hardware — no unit of this platform has been opened, connected to or modified.