2

I am studying IOLI crackme0x02. It is clear to me that rewriting the jump at 0x8048451 with no instructions the crackme can be patched. However, I want to understand better what the code does. My problem is that I am unable to find where the entered password is located.

I interpreted what radare2 is showing that there are 3 local variables.

│          ; arg int arg_22_2     @ ebp+0x5a
│          ; arg int arg_123      @ ebp+0x1ec
│          ; var int local_1      @ ebp-0x4
│          ; var int local_2      @ ebp-0x8
│          ; var int local_3      @ ebp-0xc
...
|           0x0804840c      c70424618504.  movl $str.Password:, (%esp) ; [0x8048561:4]=0x73736150  LEA str.Password: ; "Password: " @ 0x8048561
|           0x08048413      e804ffffff     calll sym.imp.printf
|           0x08048418      8d45fc         leal -4(%ebp), %eax
|           0x0804841b      89442404       movl %eax, 4(%esp)
|           0x0804841f      c704246c8504.  movl $0x804856c, (%esp)     ; [0x804856c:4]=0x50006425 
|           0x08048426      e8e1feffff     calll sym.imp.scanf
|           0x0804842b      c745f85a0000.  movl $0x5a, -8(%ebp)        ; [0x5a:4]=-1 ; 'Z' ; 90
|           0x08048432      c745f4ec0100.  movl $0x1ec, -0xc(%ebp)     ; [0x1ec:4]=-1 ; 492
|           0x08048439      8b55f4         movl -0xc(%ebp), %edx
|           0x0804843c      8d45f8         leal -8(%ebp), %eax
|           0x0804843f      0110           addl %edx, (%eax)
|           0x08048441      8b45f8         movl -8(%ebp), %eax
|           0x08048444      0faf45f8       imull -8(%ebp), %eax
|           0x08048448      8945f4         movl %eax, -0xc(%ebp)
|           0x0804844b      8b45fc         movl -4(%ebp), %eax
|           0x0804844e      3b45f4         cmpl -0xc(%ebp), %eax
|       ,=< 0x08048451      750e           jne 0x8048461              
|       |   0x08048453      c704246f8504.  movl $str.Password_OK_:__n, (%esp) ; [0x804856f:4]=0x73736150  LEA str.Password_OK_:__n ; "Password OK :)." @ 0x804856f
|       |   0x0804845a      e8bdfeffff     calll sym.imp.printf
|      ,==< 0x0804845f      eb0c           jmp 0x804846d              
|      |`-> 0x08048461      c704247f8504.  movl $str.Invalid_Password__n, (%esp) ; [0x804857f:4]=0x61766e49  LEA str.Invalid_Password__n ; "Invalid Password!." @ 0x804857f
|      |    0x08048468      e8affeffff     calll sym.imp.printf
|      |    ; JMP XREF from 0x0804845f (sym.main)

Since I am not yet familiar with radare2, I put a breakpoint in gdb after the scanf (0x0804842b), and inspected the local variables. It seems that neither they, neither the address they are pointing to are the password.

I have two questions regarding this crackme:

  1. Where is located the entered password?

  2. I do not understand what I get here:

    Breakpoint 1, 0x0804843c in main ()
    (gdb) x/4x $ebp-8
    0xffffd1b0:    0x00000000    0x08048330    0x00000000    0xf7e1a72e
    

    because in gdb x/4x usually prints 4 bytes.

3

It's after the scanf() function it can get a bit confusing however I can explain this for you as I reversed this binary back to it's C code without the source.

The Assembly after scanf():

|           0x08048426      e8e1feffff     call sym.imp.scanf
|           0x0804842b      c745f85a0000.  mov dword [ebp - local_8h], 0x5a ; 'Z'
|           0x08048432      c745f4ec0100.  mov dword [ebp - local_ch], 0x1ec
|           0x08048439      8b55f4         mov edx, dword [ebp - local_ch]
|           0x0804843c      8d45f8         lea eax, dword [ebp - local_8h]
|           0x0804843f      0110           add dword [eax], edx
|           0x08048441      8b45f8         mov eax, dword [ebp - local_8h]
|           0x08048444      0faf45f8       imul eax, dword [ebp - local_8h]
|           0x08048448      8945f4         mov dword [ebp - local_ch], eax
|           0x0804844b      8b45fc         mov eax, dword [ebp - local_4h]
|           0x0804844e      3b45f4         cmp eax, dword [ebp - local_ch]
|       ,=< 0x08048451      750e           jne 0x8048461
|       |   0x08048453      c704246f8504.  mov dword [esp], str.Password_OK_:__n ; [0x804856f:4]=0x73736150 LEA str.Password_OK_:__n ; "Password OK :)." @ 0x804856f
|       |   0x0804845a      e8bdfeffff     call sym.imp.printf
|      ,==< 0x0804845f      eb0c           jmp 0x804846d
|      ||   ; JMP XREF from 0x08048451 (sym.main)
|      |`-> 0x08048461      c704247f8504.  mov dword [esp], str.Invalid_Password__n ; [0x804857f:4]=0x61766e49 LEA str.Invalid_Password__n ; "Invalid Password!." @ 0x804857f
|      |    0x08048468      e8affeffff     call sym.imp.printf
|      |    ; JMP XREF from 0x0804845f (sym.main)
|      `--> 0x0804846d      b800000000     mov eax, 0
|           0x08048472      c9             leave
\           0x08048473      c3             ret

First Part:

|           0x0804842b      c745f85a0000.  mov dword [ebp - local_8h], 0x5a ; 'Z'
|           0x08048432      c745f4ec0100.  mov dword [ebp - local_ch], 0x1e

Here we can see values being "moved" into memory at the base pointer minus 0x8 and minus 0xc. Remember that the 'h' means hex and local means is just the hex value minus the value of ebp or the base pointer. ebp stands for Extended Base Pointer.

To figure out what values are being stored convert the hex to decimal:

0x5a = 90
0x1e = 492

Think of this as storing variable in memory like:

int a = 90
int b = 492

Second Part:

|           0x08048439      8b55f4         mov edx, dword [ebp - local_ch]
|           0x0804843c      8d45f8         lea eax, dword [ebp - local_8h]
|           0x0804843f      0110           add dword [eax], edx
|           0x08048441      8b45f8         mov eax, dword [ebp - local_8h]

Remember that computing something using registers is faster than computing it from memory so before each operation the compiler is going to try to move what it can into the registers to make it's calculations.

We move ebp-0xc -> 0x1e into the edx register then we load the effective address (lea) of ebp-0x8 -> 0x5a.

The add instruction adds the two values and saves them to [eax] which is now the same as a pointer to ebp-0x8 since it's saving it to memory we can assume we are replacing an existing variable with a new computed value because we move the value return from add to the same location in memory effectively overwriting it.

So we can assume again our code is something like this:

int a = 90
int b = 492
a = a + b

Third Part:

|           0x08048444      0faf45f8       imul eax, dword [ebp - local_8h]
|           0x08048448      8945f4         mov dword [ebp - local_ch], eax
|           0x0804844b      8b45fc         mov eax, dword [ebp - local_4h]
|           0x0804844e      3b45f4         cmp eax, dword [ebp - local_ch]
|       ,=< 0x08048451      750e           jne 0x8048461

You may not be familiar with the imul instruction but it simply multiplies two integers. We are multiplying the value of eax which is the return value of the add operation (582 or 90 + 492) then storing the value in eax overwriting it's previous value of 582. Thus we are executing (a + b) * (a + b) or 582 * 582.

We then move the value you enter with scanf() from memory into eax and compare it to (a + b) * (a + b). If the jump afterwards is not equal to zero it will go to 0x8048461 showing invalid password otherwise it will jump to valid password. It will exit if the password is valid or invalid.

Now that you have a better understanding now you can put the source code together like I have:

#include <stdio.h>
int main(int argc, char *argv[]){
        int a,b,c;
        printf("IOLI Crackme Level 0x02\n");
        printf("Password: ");
        scanf("%d", &a);
        b = 90;
        c = 492;
        c = ((b + c) * (b + c));
        if(a == c){
                printf("Password OK :)\n");
        }
        else{
                printf("Invalid Password!\n");
        }
        return 0;
}

Here is my version of the program compiled and it's control flow graph: enter image description here

Hope this helps and good luck with you reverse engineering journey!

1

If you take a look at what happens before scanf, you'll see this:

[0x080483e4]> pd 4 @ 0x08048418
│           0x08048418      8d45fc         lea eax, [ebp-local_1]
│           0x0804841b      89442404       mov dword [esp + 4], eax
│           0x0804841f      c704246c8504.  mov dword [esp], 0x804856c  ; [0x804856c:4]=0x50006425 
│           0x08048426      e8e1feffff     call sym.imp.scanf
[0x080483e4]> psz @ 0x804856c
%d

0x804856c being the address of the format string (here, %d), and ebp-local_1 the offset where your result is stored.

As for you x/4x issue, radare2 will print you 4 words, in hex, namely 0x00000000 0x08048330 0x00000000 0xf7e1a72e.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.