NSA's Ghidra - Can It Do MIPS?

You won't find the solution to the challenge here. That is against root-me.org rules. We are simply looking at a ghidra analysis of the executable.

The previous test on Ghidra impressed me, and so I decided to test it on another simple crackme challenge from root-me.org

In this case the executable is MIPS ELF:

$ file ch27.bin 
ch27.bin: ELF 32-bit LSB executable, MIPS, MIPS32 rel2 version 1 (SYSV), dynamically linked, interpreter /lib/ld-uClibc.so.0, stripped

Lets see if Ghidra can handle it:

And it has no problem with this. In fact, the decompiler may make this crackme challenge kind of a boring exercise - IF the code is correct. Let's try!

  size_t sVar1;
  int local_58;
  char local_54 [4]; 
  char local_50; 
  char local_4f; 
  char local_4e; 
  char local_4d; 
  char local_43; 
  char local_42; 
  undefined4 local_c;
  puts("crack-me for Root-me by s4r");
  puts("Enter password please");
  fgets(local_54,0x40,stdin);                    // read up to 0x40 characters from 
                                                 // user entry (including 0)
                                                 // note that local_54 is technically
                                                 // only has space for 4 characters
  sVar1 = strlen(local_54);                      // how many chars actually got entered
  *(undefined *)((int)&local_58 + sVar1 + 3) = 0;// terminate string at 
                                                 // local_58+strlen+3 with 0
  sVar1 = strlen(local_54);
  if (sVar1 == 0x13) {                           // the password has to be 0x13 = 19 chars long
    local_58 = 8;                                // initialize local_58 to 8
    while (local_58 < 0x11) {                    // just a for loop from 8 to 16
      if (local_54[local_58] != 'i') {           // local_54[8] to local_54[16] have to be 'i'
        return 0;
      local_58 = local_58 + 1;                   // just a for loop from 8 to 16
    if (local_42 == 's') {                       // local_54[18]
      if (local_43 == 'p') {                     // local_54[17]
        if (local_4d == 'm') {                   // local_54[7]
          if ((local_54[2] == 'n') && (local_4e == 'n')) { // local_54[2] and local_54[6]
            if (local_54[0] == 'c') {            // local_54[0]
              if (local_54[1] == 'a') {          // local_54[1]
                if (local_54[3] == 't') {        // local_54[3]
                  if (local_50 == 'r') {         // local_54[4]
                    if (local_4f == 'u') {       // local_54[5]
                      return local_c;

I left out all the else statement, because they all go to the same function, which fails the password check.

Lets look at the local variables

char local_54 [4]; 
  char local_50; 
  char local_4f; 
  char local_4e; 
  char local_4d; 
  char local_43; 
  char local_42; 

The stack grows downward here, and the naming of the variables is a bit unfortunate, because they are named by how far down in the stack they are. Here a bit of the disassembly window:

Stack[-0x4]:4  local_4 
Stack[-0x8]:4  local_8 
Stack[-0xc]:4  local_c 
Stack[-0x10]:4 local_10
Stack[-0x42]:1 local_42
Stack[-0x43]:1 local_43
Stack[-0x4c]:1 local_4c
Stack[-0x4d]:1 local_4d
Stack[-0x4e]:1 local_4e
Stack[-0x4f]:1 local_4f
Stack[-0x50]:1 local_50
Stack[-0x51]:1 local_51
Stack[-0x52]:1 local_52
Stack[-0x53]:1 local_53
Stack[-0x54]:1 local_54
Stack[-0x58]:4 local_58
Stack[-0x60]:4 local_60

So local_53 means 0x53 down from the stack pointer. This is important, because it implies that


allows us to overwrite the other local variables. Hence,

  char local_54 [4]; //local_54[0..3]
  char local_50;     //local_54[4]
  char local_4f;     //local_54[5] 
  char local_4e;     //local_54[6]
  char local_4d;     //local_54[7]
  char local_4c;     //local_54[8] 
  char local_4b;     //local_54[9]
  char local_4a;     //local_54[10]
  char local_49;     //local_54[11]
  char local_48;     //local_54[12]
  char local_47;     //local_54[13]
  char local_46;     //local_54[14]
  char local_45;     //local_54[15]
  char local_44;     //local_54[16]
  char local_43;     //local_54[17] 
  char local_42;     //local_54[18] 
  undefined4 local_c;//local_54[19] extra space for 0 termination

and now the password is easy to deduce. local_54[8] to local_54[16] have to be 'i's and the other characters are specified in the if statements.

The decompiler made this very very easy in deed. I rarely look at MIPS assembly code and this would have been very tedious to solve for me without the pseudocode.