OK, I promised a write-up of my hacking process earlier. I've left out some things I did if they didn't end up anywhere, but feel free to skip the first part still. It's night already and I'm tired and writing in English. Not much can be expected. I finally got to it so here it comes!
Premilinary work
I started with a firmware version 1.14 descrambled with the method previously discovered in this thread. The first thing to do was of course to run binwalk and strings on it. Binwalk found a lot of ARM instructions and the entropy plot seemed sensible. I read the list of strings carefully and found some interesting: MODEL, FACTORYON, FACTORYOFF, MANUFACTUREON, MANUFACTUREOFF. They looked a lot like SCPI commands.
I tried a lot of plausible combinations like :SYSTEM:FACTORYON programmatically. I got just a bunch of false results because the SCPI server crashes easily and starts to do weird things.
I wanted to disassemble the firmware, and luckily the loading address had already been figured out. Search for references to those interesting string constants found something. One function, insted of it's normal thing, sets a variable to 1 if parameter FACTORYON is passed to it while some condition is true. The function usually takes ON, 1, OFF or 0 as a parameter. The DP800 programming manual list only a few of those. I tried all of them them but those returned errors. That was very much expected. Following functions calls for the condition would just find more and more complex code with indirect references.
At this point, I figured out I had been living under a rock, and there's a new decompiler called Ghidra. I wanted to try it so I redid all of my previous work with it. It didn't take much time at all, but neither did it help me any further. I started to look for other commands. I found a one which can set a MODEL or SN, but it checks for the same condition before it does anything.
A dump of RAM would've helped me a lot, and there was a command for it. To use it, I had to get it's name. The names were stored in a tree-like structure which had to be parsed. By chance I came across a simple Perl script for printing DS1054Z command structure. I quickly rewrote it in Python and had a list of commands on the first try. I modified it to print command IDs and conditional parts properly. The command list is attached if you want to have a look.
Now I could start dumping the memory with command :PROJect:MEMOry:READ?. Figuring out it's parameters was easy with a help of a decompiler. The first kilobyte of the flash could be read with :PROJ:MEMO:READ? FLASH,0,1024 and it was sensible. To test it I dumped the flash. There was just the firmware I already had. Luckily the command could also read RAM by changing the first parameter. I tried to read the RAM but the output made no sense. I read the decompiled source again and was sure I was using the command right. Instead, the command either had a placeholder implementation or was missing a call to atoi. It read from the address of the second parameter instead of the numeric value and would just echo back the parameters. I had to do more static analysis without a memory dump.
Decompilation and a hack
The offset and the loading address of the firmware are known thanks to previous efforts in this thread. The array of pointers to command handlers is easy to find. Just find one handler with a known string and follow the cross references. Names of the commands are stored in an another large structure which can be parsed with a script made for DS1054Z. It has pointers to all the command names and is easily found with xrefs.
The command handler which can change the model references strings MODEL an SN. The former is long enough to be found with any string search. The handler calls a function which does the USB drive check. Unless it returs zero, the handler does nothing and returns an error. A pointer to the command can be found by following xrefs back to the command handler array. Based on it's index, name :PROJect:SET can be found from the command name structure.
The USB drive check function has many arbitary values. By calling a second function, it does a memcpy-like operation of 8 bytes from 0x58E0 to an array in the stack and compares those against hard coded constants. The second function has a pattern which looks very much like "allocate, read something, memcpy, free". At least the vectorized memcpy is easy to recognize.
The function which does the reading is unfortunately the most difficult to understand. I uses a lot of pointers and arbitary values. It also has a slightly different style to it. This hints that it might be a part of an OS driver. Strings reveal that the firmware contains version 3.7 of MQX RTOS. It's sources are available, and they contain symbolic values for some of the immediate values used in the fuction. One, MFS_READ_FAULT, is used only in three places. One of those is function MFS_Read_device_sector. It's source matches the decompiler output perfectly. The last thing to do with the code is go back and get the 8 byte value from the disassembly. Some mental math has to be done to get endianness right.
When the value is written to the start of sector 0x58E0 of a USB drive, the command :PROJect:SET will work. I took the easy route and did the file copy trick. Mainly because I didn't bother to check if a valid file system is required or if it's sector 0x58E0 of the drive or a partition or something.
Afterthoughts
I think it took me three or four evenings of messing around with my DP832 in total. Most of it was spend trying to dump the memory and trying some things I've left out. I didn't help that I had never read ARM assembly or used Ghidra before. I think Ghidra is an excelent tool and in some ways better than a, um, free version of another interactive disassembler.
I've decompiled some of the other commands. The unit can be set to some factory mode with command :SYST:BEEP FACTORYON if the magic USB drive is inserted. In that mode the model can be set with :SYST:LOCK DP832A$, but I don't think it enables anything else. :DIGItal:IO commands seemed somewhat interesting by their name, but they don't seem to be doing anything.
The :PROJ:SET command should return OK but crashes the command line if it's send via LAN. I think it safer to test it via USB on other Rigol models. However, on DP832 it seems to be working quite well.