Title: Defeating qcrk5
Date: 2012-08-02 16:14

IOLI's crackme are now boring, let's try something else : [qcrk5]({static}/files/qcrk5) from Qnix.

Getting infos
=============

    :::bash
    $ file qcrk5
    qcrk5: ELF 32-bit LSB executable, Intel 80386, version 1 (SYSV), statically linked, for GNU/Linux 2.4.1, stripped

Okay, stripped.

    :::bash
    $ strings qcrk5
    [...]
    Usage : %s <password> Using %s
    Correct, Cracked !!
    Wrong!
    [...]

Okay, a shitload of strings, definitively not in asm, maybe gcc ?
Let's try : GCC writes a .comment section that contains the GCC version
string (the same string you get if you run *gcc --version*);

    :::bash
    $ readelf -p .comment qcrk5

    String dump of section '.comment':
    [ 1] GCC: (GNU) 3.4.4 (Gentoo 3.4.4-r1, ssp-3.4.4-1.0, pie-8.7.8)
    [...]

Yep, gcc.

    :::bash
    $ readelf -a qcrk5 | grep Entry
    Entry point address: 0x8048110

start
=====

Let's see what is at *0x8048110*:

    :::asm
    xor ebp, ebp
    pop esi
    mov ecx, esp
    and esp, 0xFFFFFFF0h
    push eax
    push esp
    push edx
    push offset sub_80486E0
    push offset sub_8048680
    push ecx
    push esi
    push offset sub_8048208
    call sub_8048340

Looks like a standard *start* function. Our main should be at *0x8048208*

main
====

    :::asm
    / function: main (306)
    [...] ;prologue
    | 0x08048224 c745fca03d7f4b mov dword [ebp-0x4], 0x4b7f3da0
    | 0x0804822b c744240c00000000 mov dword [esp+0xc], 0x0
    | 0x08048233 c744240801000000 mov dword [esp+0x8], 0x1
    | 0x0804823b c744240400000000 mov dword [esp+0x4], 0x0
    | 0x08048243 c7042400000000 mov dword [esp], 0x0
    | 0x0804824a e801680000 call dword fcn.0804ea50 ; a function with 0, 0, 1, 0 in parameters, seems familiar...
    | 0x0804824f 85c0 test eax, eax
    | ,=< 0x08048251 790d jns loc.08048260
    | | 0x08048253 b801000000 mov eax, 0x1
    | | 0x08048258 8945ec mov [ebp-0x14], eax
    | ,==< 0x0804825b e9d5000000 jmp dword loc.08048335 ; jump to the end of the main
    | |`-> 0x08048260 837d0802 cmp dword [ebp+0x8], 0x2 ; ebp+0x8 is argc, so: argc != 2 ?
    | ,===< 0x08048264 742b jz loc.08048291
    | || 0x08048266 8b450c mov eax, [ebp+0xc]
    | || 0x08048269 8b00 mov eax, [eax]
    | || 0x0804826b 8b15b4f30a08 mov edx, [0x80af3b4]
    | || 0x08048271 89442408 mov [esp+0x8], eax
    | || 0x08048275 c744240488820908 mov dword [esp+0x4], str.Usagespassword ; tells you how to us the crackme
    | || 0x0804827d 891424 mov [esp], edx
    | || 0x08048280 e8ab120000 call dword fcn.08049530
    | || 0x08048285 c7042400000000 mov dword [esp], 0x0
    | || 0x0804828c e87f090000 call dword fcn.08048c10 ; go to some spagetti code, and exit
    | `---> 0x08048291 8b450c mov eax, [ebp+0xc]
    | | 0x08048294 83c004 add eax, 0x4
    | | 0x08048297 8b00 mov eax, [eax]
    | | 0x08048299 890424 mov [esp], eax
    | | 0x080482a1 8945f8 mov [ebp-0x8], eax ; ebp-0x8 = our_key
    | | 0x080482a4 8d45f8 lea eax, [ebp-0x8]
    | | 0x080482a7 830005 add dword [eax], 0x5 ; ebp+0x8 += 5
    | | 0x080482aa 8d45f8 lea eax, [ebp-0x8]
    | | 0x080482ad 830060 add dword [eax], 0x60 ; ebp-0x8 += 0x60
    | | 0x080482b0 8b55f8 mov edx, [ebp-0x8] ; edx = ebp-0x8
    | | 0x080482b3 89d0 mov eax, edx
    | | 0x080482b5 c1e008 shl eax, 0x8 ; ebp-0x8 <<= ebp-0x8
    | | 0x080482b8 29d0 sub eax, edx ; ebp-0x8 -= edx
    | | 0x080482ba 8945f8 mov [ebp-0x8], eax
    | | 0x080482bd 8b45f8 mov eax, [ebp-0x8]
    | | 0x080482c0 69c090909000 imul eax, eax, 0x909090 ; ebp-0x8 *= 0x909090
    | | 0x080482c6 8945f8 mov [ebp-0x8], eax
    | | 0x080482c9 8b450c mov eax, [ebp+0xc]
    | | 0x080482cc 83c004 add eax, 0x4
    | | 0x080482cf 8b00 mov eax, [eax]
    | | 0x080482d1 8b15b4f30a08 mov edx, [0x80af3b4] ; edx = 0x80af3b4
    | | 0x080482d7 89442408 mov [esp+0x8], eax
    | | 0x080482db c74424049f820908 mov dword [esp+0x4], str.Usings
    | | 0x080482e3 891424 mov [esp], edx
    | | 0x080482e6 e845120000 call dword fcn.08049530 ; print the ("Using %s", our_key) message
    | | 0x080482eb 8b45fc mov eax, [ebp-0x4]
    | | 0x080482ee 3b45f8 cmp eax, [ebp-0x8] ; ebp-0x8 == 0x4b7f3da0 ?
    | ,====< 0x080482f1 7521 jnz loc.08048314 ; jumps to the end of the main
    | | | 0x080482f3 a1b4f30a08 mov eax, [0x80af3b4]
    | | | 0x080482f8 c7442404a9820908 mov dword [esp+0x4], str.Correct,Cracked!!
    | | | 0x08048300 890424 mov [esp], eax
    | | | 0x08048303 e828120000 call dword fcn.08049530 ; Great success !
    | | | 0x08048308 c7042400000000 mov dword [esp], 0x0
    | | | 0x0804830f e8fc080000 call dword fcn.08048c10
    | `----> 0x08048314 a1b4f30a08 mov eax, [0x80af3b4]
    | | 0x08048319 c7442404be820908 mov dword [esp+0x4], str.Wrong!
    | | 0x08048321 890424 mov [esp], eax
    | | 0x08048324 e807120000 call dword fcn.08049530
    | | 0x08048329 c7042400000000 mov dword [esp], 0x0
    | | 0x08048330 e8db080000 call dword fcn.08048c10
    | `--> 0x08048335 8b45ec mov eax, [ebp-0x14]
    | 0x08048338 c9 leave
     0x08048339 c3 ret

Nothing hardcore.

Keygen
======

Here is a dead simple keygen:

    :::c
    #include <stdio.h>

    int main(int argc, char* argv[]) {
        unsigned int var_8, tmp, i;

        printf("qcrk5 crackmen");
        printf("[*] Computing...n");

        for(i=1; i<0xffffffff; ++i){
            var_8 = 5 + 0x60 + i;
            tmp = var_8;
            var_8 = var_8 << 8;
            var_8 -= tmp;
            var_8 *= 0x0909090;

            if(var_8 == 0x4b7f3da0){
                printf("[*] Key found: %d\n", i);
                break;
            }
        }
        return 0;
    }

Result:

    :::bash
    $ a.out
    qcrk5 crackme
    [*] Computing...
    [*] Key found: 91867153

    $ qcrk5 91867153
    Using 91867153
    Correct, Cracked !!

fcn.0804ea50
============

    :::c
    / function: fcn.0804ea50 (87)
    [...]
    | 0x0804ea67 8b7514 mov esi, [ebp+0x14]
    | ,=< 0x0804ea6a 7703 ja loc.0804ea6f
    | | 0x0804ea6c 8d75f4 lea esi, [ebp-0xc]
    | `-> 0x0804ea6f b81a000000 mov eax, 0x1a ; this is a call to ptrace
    !
    | 0x0804ea74 cd80 int 0x80
    | ; ptrace ()
    | 0x0804ea76 3d00f0ffff cmp eax, 0xfffff000
    | 0x0804ea7b 89c6 mov esi, eax
    | 0x0804ea7d 772f ja loc.0804eaae
    [...]

As you may know (or not), if you try to call *ptrace(PTRACE_TRACEME,
...)* on a binary that is already traced (with something like gdb,
strace, ...), this will return -1. This is a dead simple anti-debugging
method.

Example
-------

    :::c
    #include <stdio.h>
    #include <sys ptrace.h>

    int main(){
        printf("%ld\n", ptrace(PTRACE_TRACEME, 0, 1, 0));
        return 0;
    }

Result:

    :::bash
    $ gcc ptace.c && ./a.out
    0
    $ gdb a.out
    Reading symbols from /home/jvoisin/a.out...(no debugging symbols found)...done.
    (gdb) r
    Starting program: /home/jvoisin/a.out
    -1
    [Inferior 1 (process 13632) exited normally]
    (gdb)

Conclusion
==========

Nothing funky nor complex, just an occasion to introduce the (classic)
ptrace trick.
