Jun 17, 2020

Simple crackme tutorial for beginners - 01

Today I selected a basic crackme to demonstrate crackme solving with GDB. Also, we can solve this in the Windows platform with IDA. I found these crack me collection from the GitHub. You can get the source code of crackme01.c and compile on your mashing.

Here I used GCC with an additional command -mpreferred-stack-boundary=2. This will simplify the Assembly output.

[email protected]:~$ nano crackme01.c
[email protected]:~$ gcc crackme01.c -o crackme01 -mpreferred-stack-boundary=2

Now I run it to see the outer working of the program.

[email protected]:~$ ./crackme01
Need exactly one argument.
[email protected]:~$ ./crackme01 hello
No, hello is not correct.

First, it gave me an error saying one argument is needed. So I supplied a random argument. It looks like checking our input against a hardcoded string. So how we can bypass this checking. We can search for strings in the binary to see if there any plain strings.

But now I want to disassemble and check the inner working. Because the purpose of this tutorial is to get familiar with GDB and assembly.

Let's load it in the GDB and disassemble.

(gdb) disass main
Dump of assembler code for function main:
0x08048464 <main+0>:    push   ebp
0x08048465 <main+1>:    mov    ebp,esp
0x08048467 <main+3>:    sub    esp,0x10
0x0804846a <main+6>:    cmp    DWORD PTR [ebp+0x8],0x2
0x0804846e <main+10>:   je     0x8048483 <main+31>
0x08048470 <main+12>:   mov    DWORD PTR [esp],0x80485c0
0x08048477 <main+19>:   call   0x8048388 <[email protected]>
0x0804847c <main+24>:   mov    eax,0xffffffff
0x08048481 <main+29>:   jmp    0x80484f4 <main+144>
0x08048483 <main+31>:   mov    DWORD PTR [ebp-0x4],0x80485db
0x0804848a <main+38>:   mov    eax,DWORD PTR [ebp-0x4]
0x0804848d <main+41>:   mov    DWORD PTR [esp],eax
0x08048490 <main+44>:   call   0x8048368 <[email protected]>
0x08048495 <main+49>:   mov    edx,eax
0x08048497 <main+51>:   mov    eax,DWORD PTR [ebp+0xc]
0x0804849a <main+54>:   add    eax,0x4
0x0804849d <main+57>:   mov    eax,DWORD PTR [eax]
0x0804849f <main+59>:   mov    DWORD PTR [esp+0x8],edx
0x080484a3 <main+63>:   mov    edx,DWORD PTR [ebp-0x4]
0x080484a6 <main+66>:   mov    DWORD PTR [esp+0x4],edx
0x080484aa <main+70>:   mov    DWORD PTR [esp],eax
0x080484ad <main+73>:   call   0x8048398 <[email protected]>
0x080484b2 <main+78>:   test   eax,eax
0x080484b4 <main+80>:   je     0x80484d6 <main+114>
0x080484b6 <main+82>:   mov    eax,DWORD PTR [ebp+0xc]
0x080484b9 <main+85>:   add    eax,0x4
0x080484bc <main+88>:   mov    edx,DWORD PTR [eax]
0x080484be <main+90>:   mov    eax,0x80485e5
0x080484c3 <main+95>:   mov    DWORD PTR [esp+0x4],edx
0x080484c7 <main+99>:   mov    DWORD PTR [esp],eax
0x080484ca <main+102>:  call   0x8048378 <[email protected]>
0x080484cf <main+107>:  mov    eax,0x1
0x080484d4 <main+112>:  jmp    0x80484f4 <main+144>
0x080484d6 <main+114>:  mov    eax,DWORD PTR [ebp+0xc]
0x080484d9 <main+117>:  add    eax,0x4
0x080484dc <main+120>:  mov    edx,DWORD PTR [eax]
0x080484de <main+122>:  mov    eax,0x80485fd
0x080484e3 <main+127>:  mov    DWORD PTR [esp+0x4],edx
0x080484e7 <main+131>:  mov    DWORD PTR [esp],eax
0x080484ea <main+134>:  call   0x8048378 <[email protected]>
0x080484ef <main+139>:  mov    eax,0x0
0x080484f4 <main+144>:  leave
0x080484f5 <main+145>:  ret
End of assembler dump.

In the top of the code after the function prologue, you can see a cmp instruction is checking if the value of [ebp+0x8] is equal to 2. In our stack architecture theory tutorial, we saw this location holds function arguments. So this should be the arguments count AKA argc

So if argc is not equal to two, it pushes a pointer to the stack and call puts function. So we can guess in this pointer there should be a string to display the error message. Let's examine it to see if it true.

(gdb) x/s 0x80485c0
470x80485c0:       "Need exactly one argument."

Yes, this is what we got when no argument is supplied to the binary.

If supply the argument it jumps to the location 0x8048483. What is there?

If we go to that memory address we can see a couple of function calls to strlen and strncmp. Now we know the program definitely compares a hardcoded string with our input.

We know the syntax of strncmp is as follows.

strncmp(string_1, string_2, strlen(string_2))

This is why the Assembly code calculates the string length before it calls strncmp.

Now we know the memory address of the hardcoded password string. We can easily extract it with GDB as following.

(gdb) x/s 0x80485db
0x80485db:       "password1"

So guys we can clearly see the password is "password1". 

[email protected]:~$ ./crackme01 password1
Yes, password1 is correct!

This is actually a really easy one. In the next article, we are going to see some advanced ones. Thank you for reading.

Jun 09
Python Inputs and Outputs

Getting user inputs is an essential part of a program. In python we have several ways to get inputs....

Aug 12
Linux file handling

1) Creating an empty file. We can use touch command for make a new file in Linux. Syntax is....

Mar 21
Assembly system calls

When we talk about computer programs, they do various tasks. Like printing a string to screen,....

Replying to 's comment Cancel reply