One of the more frustrating situations as a reverse engineer is when the architecture of your target is not supported by your decompiler. So, today, let’s dive into one of our latest creations: the official Binary Ninja nanoMIPS architecture plugin.
nanoMIPS is a variable-length ISA whose instructions can be 2, 4, or 6 bytes long. It is mainly designed for embedded devices and, as its name suggests, it is more lightweight than the MIPS architecture.
nanoMIPS and MIPS share many assembly instructions, but their encodings are different. As a result, a MIPS disassembler cannot be used for nanoMIPS. We wrote the disassembler from scratch following the ISA documentation and then lifted the nanoMIPS code to LLIL. Thanks to the design of BNIL, this gives us full decompilation of nanoMIPS code “for free”! Below is an example of the HLIL for a simple function that does string XOR. Looking at this, you might not even know that it’s a compiled nanoMIPS function at all.
For reference, you can check out the disassembly of the same function as well:
(The code snippet and the nanoMIPS toolchain used to build it aren’t ours.)
In the above function, the disassembly shows the compiler does division-by-multiplication, a common optimization technique. But, thanks to the flexibility of Binary Ninja’s analysis pipeline, we’ve managed to simplify it to i u% 0xc
without any special treatment for nanoMIPS.
If you work with nanoMIPS code, chances are you have already seen mobile baseband firmware in md1rom
format. For Android phones from several vendors, the md1rom
firmware can be extracted from a rooted device or from the fastboot ROM.
We have developed a Binary View to parse and load any firmware in md1rom
format. As with our other Binary Views, the code is open-source. Interestingly, the md1rom
firmware can also contain symbol names for the majority of functions, which can be quite helpful for analysis. Below is a screenshot for several AES functions in a specific firmware:
The md1rom
Binary View is available in the base Binary Ninja product, starting from version 4.1.5036-dev.
If you load an md1rom
firmware without the nanoMIPS architecture plugin, it will be able to create the segments/sections, and process the symbols within it. Of course, it works best with the nanoMIPS architecture plugin, and you can see from the feature map that Binary Ninja gradually analyzes the code in a 75MB firmware and eventually achieves almost 100% code coverage:
Nobody would complain if we wrapped up the blog post here since most, if not all, real-world nanoMIPS binaries are proprietary. But we’d also like to show off a nanoMIPS crackme before we finish. It is called baby MIPS
and was part of the 0CTF 2020
qualifiers.
The file is an ELF. Looking at the main
function, we can see that the flag has a length of 0x3e
, it must start with flag{
, and it must end with }
.
The code then puts the individual characters of the string between the {}
into a buffer (which is a 9x9 board based on later analysis). The buffer has been pre-populated with some characters, and the characters from the flag are only put into the places that are zero (not populated yet).
This is what the buffer looks like before the operation:
Then, a check
function is called and the program prints Right
or Wrong
depending on the result.
The check
function is simple: It calls another three functions and all of them must return true. They all look similar, so we will only analyze one of them.
Now, we look at check2
as an example. This code iterates over every line of the board and puts all the elements in a line into a buffer. It then calls a function to validate the line elements. Every such validation must succeed.
Inside the validation function, there is a switch case that counts the number of occurrences of certain characters. The characters being counted are acdeqswxz
.
You can see how the switch-case is handled by the versatile brsc
instruction:
There is a jump table at 0x400798
which holds the relative distance to jump. The lwxs
loads a specific entry from it based on the index. Then the brsc
instruction does a relative jump based on the value of that.
If you are not super familiar with nanoMIPS instructions, you can always refer to the LLIL and see how the code works behind the scenes:
Now, let us get back to the crackme. The code checks whether the number of occurrences is all one, effectively checking whether the input string is a tuple of acdeqswxz
.
To sum up, the crackme checks whether each row, column, and square contains a tuple of acdeqswxz
. Now you should see this is a Sudoku, and it can be solved by any Sudoku solver.
Unlike many of our other architecture plugins, the nanoMIPS architecture plugin must be purchased separately from the base product. If you are interested, contact support for a quote.