Title: Defeating crp-'s 888 with radare2
Date: 2013-08-07 21:14

[crp](http://crackmes.de/users/crp)- is known for his amazing crackmes (See [Tavis Ormandy](http://taviso.decsystem.org/)'s blogposts about them).
I wanted to start with the easiest one : [888](http://crackmes.de/users/crp/888/) ([local mirror]({static}/files/888))
([trace-q](http://crackmes.de/users/crp/trace_q/)'s difficulty is _legendary_, feel free to give it a try) ).


# Overview

```bash
$ ./888
NO
```

Okay.


```bash
$ strings ./888
4e X3Bf
104 ,$GOOD
15a x:^1
196 ^[Y^
1a7 Ht  Ht
1bf 5ARGSu
2da tI-OOPS
```

`GOOD` seems to be the objective.

The binary is likely hand-crafted asm. For this write up,
I'm using

- [radare2]( http://radare.org ) for static analysis
- gdb for dynamic one
- python as a calculator/converter

# Analysis

```nasm
$ r2 ./888
[0x0804823f]> aa
[0x0804823f]> pdf
	0x0804823f     90               nop
	0x08048240     08c0             or al, al
	0x08048242     7403             jz 0x8048247
	0x08048244     7512             jnz 0x8048258
	0x08048246     e868759214       call dword 0x1c96f7b3
```

When a binary is run, every registers from `eax` to `edx`, `esi` and `edi` are set to `0`.
The jump to `0x8048247` is taken.

```nasm
[0x0804823f]> pd@0x8048247
	0x08048247     6875921418       push dword 0x18149275
	0x0804824c     812c2410101010   sub dword [esp], 0x10101010
	0x08048253     e801000000       call dword 0x8048259
[0x0804823f]> pd@0x8048259
	0x08048259     81042408000000   add dword [esp], 0x8
	0x08048260     c3               ret
```

Some esp-related magic also known as a classic push-ret trick.

```python
>>> hex(int("18149275", 16) - int("10101010", 16))
0x8048265
```

`0x8048265` is our next step!

```nasm
[0x0804823f]> pd@0x8048265
	0x08048265     40               inc eax  ; eax = 1
	0x08048266     8f0578830408     pop dword [section_end.undefined]
	0x0804826c     85c0             test eax, eax
	0x0804826e     7415             jz 0x8048285  ; not taken
	0x08048270     6885820408       push dword 0x8048285
	0x08048275     ff0424           inc dword [esp]
	0x08048278     6821820408       push dword 0x8048221
	0x0804827d     81042402000000   add dword [esp], 0x2
	0x08048284     c3               ret
```

Two more `push + ret`:

* `0x8048285 + 0x1 = 0x8048286`
* `0x8048221 + 0x2 = 0x8048223`

`0x8048223` is pushed last, so it will get pop'ed first:

```nasm
[0x0804823f]> pd@0x8048223
	0x08048223     31c0             xor eax, eax
	0x08048225     b032             mov al, 0x32
	0x08048227     b902000000       mov ecx, 0x2
	0x0804822c     fec8             dec al
	0x0804822e     e2fc             loop 0x804822c
	0x08048230     29db             sub ebx, ebx
	0x08048232     b305             mov bl, 0x5
	0x08048234     b90b820408       mov ecx, 0x804820b
	0x08048239     49               dec ecx       ; ecx = 0x804820a
	0x0804823a     e936010000       jmp dword 0x8048375
[0x0804823f]> pd@0x8048375
	0x08048375     cd80             int 0x80
	0x08048377     c3               ret
```

`int 80` is syscall. The loop instruction is a trick to set eax to 0x30 (0x32 - 0x2).

* eax = 0x30
* ebx = 0x5
* ecx = 0x804820a

This can be seen as:

* The 0x30<sup>th</sup> syscall is `_sighandler_t signal(int signum, sighandler_t handler)`
* `0x5` is also known as the `SIGTRAP` signal, mostly used by debuggers.
* `0x804820a` is the handler's address

So, our function looks like:

```C
signal(SIGTRAP, 0x804820a);
```

If you are using dynamic analysis beyond this point,
you should keep in mind that debuggers are also using `SIGTRAP` to manage breakpoints.

One more `ret`, and we land at `0x8048286`

```nasm
[0x0804823f]> pd@0x8048286
	0x08048286     b8d2820400       mov eax, 0x482d2
	0x0804828b     01d8             add eax, ebx        ; eax = 0x482d2 + 0x5
	0x0804828d     29c3             sub ebx, eax        ; ebx = 0x5 - (0x482d2 + 0x5) = -0x482d2
	0x0804828f     01d8             add eax, ebx        ; eax = 0x482d2 + 0x5 + 0x5 - (0x482d2 + 0x5) = 0x5
	0x08048291     f7db             neg ebx             ; ebx = -(-0x482d20) = 0x482d2
	0x08048293     7905             jns 0x804829a       ; taken !
	0x08048295     e903000000       jmp dword 0x804829d
	0x0804829a     68aa820408       push dword 0x80482aa
	0x0804829f     c70584830408908. mov dword [0x8048384], 0xd4a08f90
	0x080482a9     c3               ret

[0x0804823f]> pd@0x80482aa
	0x080482aa     81cb00000008     or ebx, 0x8000000   ; ebx = 0x482d2 | 0x8000000 = 0x80482d2
	0x080482b0     43               inc ebx             ; ebx = 0x80482d3
	0x080482b1     31c3             xor ebx, eax
	0x080482b3     31d8             xor eax, ebx
	0x080482b5     31c3             xor ebx, eax
	0x080482b7     48               dec eax             ; eax = 0x80482d3 - 1 = 0x80482d2
	0x080482b8     53               push ebx
	0x080482b9     93               xchg ebx, eax
	0x080482ba     b033             mov al, 0x33        ; eax = 0x33
	0x080482bc     c0e002           shl al, 0x2         ; eax = 0x33 << 2 = 0xcc
	0x080482bf     8803             mov [ebx], al       ; [ebx] = 0xcc
	0x080482c1     93               xchg ebx, eax
	0x080482c2     5b               pop ebx             ; ebx = 0x5
	0x080482c3     48               dec eax             ; eax = 0x80482d2 - 1 = 0x80482d1
	0x080482c4     750a             jnz 0x80482d0       ; taken
```


The triple xor is a well known trick to swap variables.
The instruction at `0x080482bf` seems to be a self-modification trick: It replaces whatever was at `0x80482d2` with `0xCC`,
which is the `int3` instruction, to generates a `SIGTRAP`.

```nasm
[0x0804823f]> pd@0x80482d0
	0x080482d0     ebf7             jmp 0x80482c9

[0x0804823f]> pd@0x80482c9
	0x080482c9     40               inc eax             ; eax = 0x80482d1 + 1 = 0x80482d2
	0x080482ca     7402             jz 0x80482ce        ; taken

[0x0804823f]> pd@0x080482ca
	0x080482ca     7402             jz 0x80482ce
	0x080482cc     50               push eax            ; push 0x80482d2
	0x080482cd     c3               ret
```

Remember that `0x80482d2` is now `int3`? This will trigger a `SIGTRAP`.

```nasm
[0x0804823f]> pd@0x804820a
	0x0804820a     ff057c830408     inc dword [0x804837c]	; looks like a global variable
	0x08048210     c7058083040803d. mov dword [0x8048380], 0x5b54d103 ; and another one
	0x0804821a     29c0             sub eax, eax          ; eax = 0
	0x0804821c     40               inc eax               ; eax = 1
	0x0804821d     f7d8             neg eax               ; eax = 0xffffffff
	0x0804821f     7802             js 0x8048223          ; taken
```

Your can rename global variables in radare2 with `f`.

```nasm
f globvar.isdebuggerpresent @0x8048380
```

The global variables may be used to remember the presence (or absence) of a debugger.
We already met 0x8048223 : this is where the signal handler for SIGTRAP is set.
Since we've not seen any push-ret trick, this will simply resume after the `int3`, at
`0x80482d3`.

```nasm
[0x0804823f]> pd@0x80482d3
		0x080482d3     a180830408       mov eax, [globvar.isdebuggerpresent] ; debugger-related global variable
	0x080482d8     85c0             test eax, eax
	0x080482da     7449             jz 0x8048325         ; jump to garbage
	0x080482dc     2d4f4f5053       sub eax, 0x53504f4f  ; eax = 0x5b54d103 - 0x53504f4f = 0x80481b4
	0x080482e1     ffd0             call eax
	0x080482e3     6866830408       push dword 0x8048366
	0x080482e8     6825820408       push dword 0x8048225
	0x080482ed     68a1810408       push dword 0x80481a1
	0x080482f2     68544c0508       push dword 0x8054c54
	0x080482f7     0fbae116         bt ecx, 0x16         ; copy ecx (=0) to CF
	0x080482fb     7301             jae 0x80482fe        ; taken !
	0x080482fd     7581             jnz 0x8048280
	0x080482ff     2c24             sub al, 0x24
	0x08048301     49               dec ecx
	0x08048302     cb               retf
	0x08048303     0000             add [eax], al
	0x08048305     8b442404         mov eax, [esp+0x4]
	0x08048309     48               dec eax
	0x0804830a     2d03000000       sub eax, 0x3
	0x0804830f     89442404         mov [esp+0x4], eax

[0x0804823f]> pd@0x80481b4
	0x080481b4     8f0588830408     pop dword [0x8048388]
	0x080481ba     58               pop eax              ; 0xBFFFF758
	0x080481bb     09c0             or eax, eax
	0x080481bd     7407             jz 0x80481c6
	0x080481bf     3541524753       xor eax, 0x53475241  ; eax == "ARGS"
	0x080481c4     75f4             jnz 0x80481ba
	0x080481c6     85c0             test eax, eax
	0x080481c8     7401             jz 0x80481cb         ; taken if eax == "ARGS"
```

This piece of code harvests the stack, looking for the `ARGS` value.
What comes after `ARGS`? `ENV` of course!

```nasm
[0x0804823f]> pd@0x80481cb
	0x080481cb     8b1c24           mov ebx, [esp]
	0x080481ce     09db             or ebx, ebx
	0x080481d0     742e             jz 0x8048200
	0x080481d2     e87bfeffff       call dword 0x8048052
	0x080481d7     3d04000000       cmp eax, 0x4
	0x080481dc     7ced             jl 0x80481cb
	0x080481de     3d40000000       cmp eax, 0x40
	0x080481e3     7fe6             jg 0x80481cb
	0x080481e5     8b0b             mov ecx, [ebx]
	0x080481e7     81e1ffffff00     and ecx, 0xffffff
	0x080481ed     81f96b657900     cmp ecx, 0x79656b ; 0x79656b == "key"
	0x080481f3     75d6             jnz 0x80481cb
	0x080481f5     53               push ebx
	0x080481f6     68cb810408       push dword 0x80481cb
	0x080481fb     e93effffff       jmp dword 0x804813e
```

`0x8048052` is called, and its return value must not be inferior to `4`, nor superior to `0x40`.

```nasm
[0x0804823f]> pd@0x8048052
	0x08048052     877c2404         xchg [esp+0x4], edi
	0x08048056     30c0             xor al, al
	0x08048058     31c9             xor ecx, ecx
	0x0804805a     f7d1             not ecx
	0x0804805c     fc               cld
	0x0804805d     f2ae             repne scasb
	0x0804805f     f7d1             not ecx
	0x08048061     49               dec ecx
	0x08048062     89c8             mov eax, ecx
	0x08048064     877c2404         xchg [esp+0x4], edi
	0x08048068     c20400           ret 0x4
```

This looks like a `strlen` (hint: ask your search engine about `repne scasb`).

So, the previous piece of code is likely searching in _env_ an environment variable
named key, with "key=bleh" not longer than 0x40, and not shorter than 0x4.

```nasm
[0x0804823f]> pd@0x804813e
	0x0804813e     56               push esi
	0x0804813f     51               push ecx
	0x08048140     53               push ebx
	0x08048141     8b742410         mov esi, [esp+0x10]
	0x08048145     56               push esi
	0x08048146     fc               cld
	0x08048147     ac               lodsb
	0x08048148     3c3d             cmp al, 0x3d ; 0x3d == '='
	0x0804814a     7406             jz 0x8048152
```

Mh, a test for a `=` symbol, it seems like our supposition might be right!

```nasm
[0x0804823f]> pd@0x8048152
	0x08048152     56               push esi
	0x08048153     e86dffffff       call dword 0x80480c5  ; atoi()
	0x08048158     85c0             test eax, eax
	0x0804815a     783a             js 0x8048196
	0x0804815c     5e               pop esi
	0x0804815d     31c9             xor ecx, ecx
	0x0804815f     8a5e03           mov bl, [esi+0x3] ; esi+0x3, just after "key" ?
	0x08048162     80eb31           sub bl, 0x31      ; "key" + "1"
	0x08048165     741b             jz 0x8048182
	0x08048167     fecb             dec bl
	0x08048169     7405             jz 0x8048170      ; "key" + "2"
	0x0804816b     e927000000       jmp dword 0x8048197
	0x08048170     810d8c830408020. or dword [0x804838c], 0x2
	0x0804817a     81c104000000     add ecx, 0x4
	0x08048180     eb0a             jmp 0x804818c
	0x08048182     810d8c830408010. or dword [0x804838c], 0x1
	0x0804818c     81c190830408     add ecx, 0x8048390
	0x08048192     8901             mov [ecx], eax
	0x08048194     eb01             jmp 0x8048197
	0x08048196     5e               pop esi
	0x08048197     5b               pop ebx
	0x08048198     59               pop ecx
	0x08048199     5e               pop esi
	0x0804819a     c20400           ret 0x4

	f globvar.keyfound @0x804838c
```


We were wrong, the binary is looking for two variables, `key1` and `key2`.

If it founds them, `globvar.keyfound` is set to 3, because `0 | 0x2 | 0x1 = 3`.

```nasm
[0x0804823f]> pd@0x80480c5
	0x080480c5     56               push esi
	0x080480c6     51               push ecx
	0x080480c7     53               push ebx
	0x080480c8     8b742410         mov esi, [esp+0x10]
	0x080480cc     31c0             xor eax, eax
	0x080480ce     31db             xor ebx, ebx
	0x080480d0     b90a000000       mov ecx, 0xa
	0x080480d5     ac               lodsb
	0x080480d6     08c0             or al, al
	0x080480d8     7412             jz 0x80480ec
	0x080480da     3c30             cmp al, 0x30 ; 0x30 = '0'
	0x080480dc     7c15             jl 0x80480f3
	0x080480de     3c39             cmp al, 0x39 ; 0x39 = '9'
	0x080480e0     7f11             jg 0x80480f3
	0x080480e2     2c30             sub al, 0x30
	0x080480e4     93               xchg ebx, eax
	0x080480e5     f7e1             mul ecx
	0x080480e7     93               xchg ebx, eax
	0x080480e8     01c3             add ebx, eax
	0x080480ea     ebe9             jmp 0x80480d5
	0x080480ec     89d8             mov eax, ebx
	0x080480ee     e904000000       jmp dword 0x80480f7
	0x080480f3     0fbae81f         bts eax, 0x1f
	0x080480f7     5b               pop ebx
	0x080480f8     59               pop ecx
	0x080480f9     5e               pop esi
	0x080480fa     c20400           ret 0x4
```

This seems like to be a converter ascii <-> integers.

Ok, where are we landing now ? At `0x080482e3`, after the `call eax`

```nasm
[0x0804823f]> pd@0x80482fe
	0x080482fe     812c2449cb0000   sub dword [esp], 0xcb49
	0x08048305     8b442404         mov eax, [esp+0x4]
	0x08048309     48               dec eax
	0x0804830a     2d03000000       sub eax, 0x3
	0x0804830f     89442404         mov [esp+0x4], eax
	0x08048313     8b442408         mov eax, [esp+0x8]
	0x08048317     b900010000       mov ecx, 0x100
	0x0804831c     40               inc eax
	0x0804831d     e2fd             loop 0x804831c
	0x0804831f     89442408         mov [esp+0x8], eax
	0x08048323     c3               ret
```

Since I'm pretty lazy, I used gdb from this point, until something cool poped-up.

We land at `0x8048375`, our syscall call. gdb tells us:

  * eax: 0x30
  * ebx: 0x8
  * ecx: 0x08048070

another signal()?

```c
signal(SIGFPE, 0x08048070);
```

`SIGFPE` is triggered by a wrong arithmetic operation, such as a division by zero,
we may likely expect one during the next instructions :)
After this, we lend on `0x804819d`:

```nasm
[0x0804823f]> pd@0x804819d
	0x0804819d     a18c830408       mov eax, [globvar.keyfound] ; global variable
	0x080481a2     2503000000       and eax, 0x3    ; eax = 3
	0x080481a7     48               dec eax         ; eax = 2
	0x080481a8     7409             jz 0x80481b3
	0x080481aa     48               dec eax         ; eax = 1
	0x080481ab     7406             jz 0x80481b3
	0x080481ad     48               dec eax         ; eax = 0
	0x080481ae     7503             jnz 0x80481b3
	0x080481b0     91               xchg ecx, eax   ; ecx = 0
	0x080481b1     f7f1             div ecx         ; division by 0 ! SIGFPE is triggered
	0x080481b3     c3               ret
```

Remember `globvar.keyfound`? This is where is stored our environment-related `check` variable, set to `0x3` if they were found.

```nasm
[0x0804823f]> pd@0x08048070
	0x08048070     57               push edi
	0x08048071     bfb1810408       mov edi, 0x80481b1
	0x08048076     b902000000       mov ecx, 0x2
	0x0804807b     b090             mov al, 0x90
	0x0804807d     f3aa             rep stosb
	0x0804807f     5f               pop edi
	0x08048080     a18c830408       mov eax, [globvar.keyfound]
	0x08048085     2503000000       and eax, 0x3
	0x0804808a     3d03000000       cmp eax, 0x3
	0x0804808f     7532             jnz 0x80480c3
	0x08048091     a190830408       mov eax, [0x8048390]
	0x08048096     8b0d94830408     mov ecx, [0x8048394]
	0x0804809c     81f900100000     cmp ecx, 0x1000  ; ecx < 4096 ?
	0x080480a2     7c1f             jl 0x80480c3
	0x080480a4     3d00100000       cmp eax, 0x1000  ; eax < 4096 ?
	0x080480a9     7c18             jl 0x80480c3
	0x080480ab     f7e1             mul ecx
	0x080480ad     b9               invalid
	0x080480ae     0100             add [eax], eax
```

Wtf is going on at `0x080480ad`, `invalid`?! After some digging, it seems like it's an
[expected behavior](https://github.com/radare/radare2/issues/175) from radare2.

```nasm
[0x0804823f]> b
	0x40

[0x0804823f]> b 0x64

[0x0804823f]> pd@0x08048070
		0x08048070     57               push edi
		0x08048071     bfb1810408       mov edi, 0x80481b1
		0x08048076     b902000000       mov ecx, 0x2
		0x0804807b     b090             mov al, 0x90
		0x0804807d     f3aa             rep stosb
		0x0804807f     5f               pop edi
		0x08048080     a18c830408       mov eax, [globvar.keyfound]
		0x08048085     2503000000       and eax, 0x3
		0x0804808a     3d03000000       cmp eax, 0x3
		0x0804808f     7532             jnz 0x80480c3
		0x08048091     a190830408       mov eax, [0x8048390] ; this is our "key1" value
		0x08048096     8b0d94830408     mov ecx, [0x8048394] ; this is our "key2" value
		0x0804809c     81f900100000     cmp ecx, 0x1000
		0x080480a2     7c1f             jl 0x80480c3
		0x080480a4     3d00100000       cmp eax, 0x1000
		0x080480a9     7c18             jl 0x80480c3
		0x080480ab     f7e1             mul ecx
		0x080480ad     b901000100       mov ecx, 0x10001
		0x080480b2     f7f1             div ecx
		0x080480b4     89d0             mov eax, edx
		0x080480b6     48               dec eax
		0x080480b7     750a             jnz 0x80480c3
		0x080480b9     6875d0534c       push dword 0x4c53d075
		0x080480be     e940000000       jmp dword 0x8048103
		0x080480c3     c3               ret
```

Much better ! For `0x08048091` and `0x08048096`, I used gdb.

```nasm
[0x0804823f]> pd @0x8048103
	0x08048103     812c24474f4f44   sub dword [esp], 0x444f4f47 ; "GOOD"
	0x0804810a     c3               ret

[0x0804823f]> pd@0x80480c3
	0x080480c3     c3               ret
```


Pretty straightforward.

```python
key1 < 4096
key2 < 4096
key1 * key2 % 0x10001 - 1 = 0
```

Here is the keygen in Python:

```python
for key1 in range(4096):
	for key2 in range(4096):
		if key1 * key2 % 0x10001 == 1:
			print key1, key2
```

taviso was right: this crackme was really pleasant.
Thanks crp- !

Ho, btw, this blogpost was accepted as a solution on [crackmes.de]( http://crackmes.de/users/crp/888/solutions/ ).

# Radare2 instead of gdb?

I was told that I could use radare2's debugger instead of gdb.
But at this time, there is no signal support in radare2 :/
Here are a few useful commands if you want to play with it yourself:

- `dcu <tab>` will run until your selection function. You can use `dcu entry0`
	to break at the entrypoint
- `ds` to single-step
- `pd` to print disassembly
- `dr=` to highlight changed registers
