Reverse Engineering a .NET Application

While I was at Texas Furry Fiesta 2024, I bought an LED name badge from one of the booths. It was a neat little thing, it was a monochrome 11×44 display and they put my name on it. It also came with instructions on how to program the display myself, and unfortunately, the program it used only ran on Windows. Being on Linux, this was a bit of an inconvenience. I could probably run it through Wine (spoilers: it didn’t work), but it would be nicer if I had a native application. I decided, just for fun, to look at the application and see what it’s made with.

Oh, it’s DotNet?

Part 1: It’s DotNet

Before I installed the application, I renamed the installer to .zip and opened it in a file archive browser. Within the archive was a file containing strings used by the install wizard, and one of them mentioned updating DotNet if the application didn’t work. In this state, I couldn’t figure out where the files were in this state, so I installed using Wine into an easily accessible directory, and threw the .exe into Rider and DotPeek. From here, I discover a couple of things:

  • This application was compiled for .Net Framework 4.0
  • There are only a couple of classes
  • This is a Winforms app (that also uses a proprietary commercial skinning library)
  • Most of the code is contained within…

…the roughly 8 THOUSAND line main form class. Granted, 1.5k of those lines are hardcoded bitmap image(s), but that’s a master class. At least it isn’t as big as some of Terraria’s classes.

Part 2: Sloppy Code

The monolithic main form class isn’t the only problem with the code that I had to sift through. I had to figure out where exactly it was constructing the packets that get sent over USB, and that was challenging considering how much of a tangled mess it all was. There were actually four different packet construction methods, covering monochromatic and RGB displays being sent over USB and COM. I accidentally started looking at the mono COM method, but quickly moved to the correct mono USB method. Once I found the correct method, most of the packet format was readily apparent, just a bunch of packing flags and numbers into bytes, but one of the bytes didn’t make much sense. In the original program, each message had four options for brightness: 100%, 75%, 50%, and 25%, essentially just a number from 0 to 3. Four options can be encoded into two bits. Since there are 8 messages total, it could be reasoned that this gets stored in two bytes, or 16 bits, one byte for the first 4 messages and one byte for the last 4, but that’s not how the program behaved. The code for computing the brightness byte looked something like this

C#
        byte num1 = 0;
        for (int index = 0; index < 8; index++)
          num1 = (byte) (Messages[index].Brightness << 4 & 0x11110000);
        byteList.Add(num1);

There are seemingly multiple bugs here

  • First, instead of compounding the result of the messages, it just sets it to the new value. In essence, only the 8th message’s value matters
  • Second, this is only one byte, and you need two to store all 8 brightness values. Additionally, this is getting left shifted by 4 and having the lower half truncated, so this actually could only potentially store two messages worth of values.
  • Finally, the original program doesn’t actually allow you to choose a brightness value for each message, only a global brightness value, so where the hell is this code getting a brightness value for each message?

In the end, I ended up completely ignoring the brightness byte, and just left it as it is because it works as it is. Once I started moving towards copying image data, it became obtuse again. More strange code structure, but I was able to cut out most of it by making guesses as to what corresponded to the X and Y of the image within the original code, and making the guess that since it’s monochrome, with 1bit per pixel, it was likely stored as one 8 pixel horizontal strip per byte.

Part 3: Isn’t USB so much FUN?

The name badges transfer data over USB serial, and as such I needed to find a library to do that. I was a bit apprehensive about my options, but ended up sticking with HIDSharp. I wrote an implementation that constructs the entire packet and just sent it raw over USB serial, but this didn’t work. I ended up pulling out Wireshark to look into other issues as well, and figured out that packets needed to be split into 64 byte chunks. So I did that, it mostly worked, but seemingly randomly the image on the display would go up or down a pixel when it shouldn’t have. Going back to the guess I made earlier, that each 8 pixel strip was one byte, this lead me to suspect that somehow a byte was either being added or being dropped, which caused the image to vertically shift. The Wireshark packet readings showed malformed packets that were 1 byte too short, which reaffirmed my suspicion. I was stumped by this one for awhile, I debugged my code and checked to make sure it was always 64 bytes long, but eventually figured out that the hid library will chop off the first byte if it’s a 0, and only if it’s a 0, for whatever reason. I changed my code so that it prepends an extra 0 if it starts with a 0, and it worked. At this point, barring the brightness byte (which I’ve ignored for now), everything works.

Conclusion

Once I got the packet format and sending data over USB to work, it mostly came down to writing a good and usable interface for my program. As is usual for me, I chose to use Godot for my interface since I’m familiar with it and it’s cross platform. The final project can be found here.


Posted

in

by

Tags: