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'
FUN_00400814();
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]
FUN_004007c0();
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
fgets(local_54,0x40,stdin);
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.