Solution-DevAstatoR-What-do-I-want

From aldeid
Jump to navigation Jump to search

Description

Objective

The objective of this crackme (http://crackmes.de/users/devastator/what_do_i_want/) is to find the value of 2 fields to reveal a password. This challenge is interesting because it requires some reverse engineering of the code itself to understand what is required.

My solution

Below is the output of my script:

$ python decrypt.py 
*1st field: 131313
*2nd field: information

Code analysis

Start function

Let's load the executable into IDA-Pro and go to the start funtion. The DialogBoxParamA function is called at 0x40100F and uses calls the DialogFunc function as lpDialogFunc parameter.

.text:00401000 start proc near
.text:00401000 xor     eax, eax
.text:00401002 push    eax             ; dwInitParam
.text:00401003 push    offset DialogFunc ; lpDialogFunc
.text:00401008 push    eax             ; hWndParent
.text:00401009 push    81h             ; lpTemplateName
.text:0040100E push    eax             ; hInstance
.text:0040100F call    ds:DialogBoxParamA
.text:00401015 xor     eax, eax
.text:00401017 inc     eax
.text:00401018 retn
.text:00401018 start endp

First field

In the following code extract, we can see that SendMessageA is called at 0x401089 (EDI set to SendMessageA at 0x501055). The Msg parameter should be WM_GETTEXT (0x0D) to get the value of the field.

.text:00401055 mov     edi, ds:SendMessageA
[REMOVED]
.text:00401066 push    3EAh            ; nIDDlgItem
.text:0040106B push    [ebp+hDlg]      ; hDlg
.text:0040106E call    esi ; GetDlgItem
.text:00401070 mov     esi, eax
.text:00401072 lea     eax, [ebp+lParam] ; EAX = field1
.text:00401075 push    eax
.text:00401076 call    ds:StrToIntA    ; convert field1 to int
.text:0040107C lea     ecx, [ebp+var_18]
.text:0040107F push    ecx             ; var_18 = field1
.text:00401080 push    0Ch             ; wParam
.text:00401082 sub     eax, 200E4h     ; EAX - 0x200E4 = 0xD (WM_GETTEXT)
.text:00401087 push    eax             ; Msg (WM_GETTEXT)
.text:00401088 push    esi             ; hWnd
.text:00401089 call    edi ; SendMessageA

As the value 0x200E4 is substracted from EAX to get 0x0D, we can compute EAX as follows:

EAX = 0x200E4 + 0xD = 0x200F1 (hex) = 131313 (dec)

The first field should be set to 131313

Second field

Mysterious function

There are several approaches to find the second field. But a quick analysis of the code leads to the following lines:

.text:004010BD                 push    offset ModuleName ; "user32"
.text:004010C2                 call    ds:GetModuleHandleA
.text:004010C8                 cmp     eax, ebx        ; EBX = 0
.text:004010CA                 jz      loc_40116F      ; check if GetModuleHandleA failed (compare to 0)
.text:004010D0                 lea     ecx, [ebp+var_18] ; var_18 must be a function of user32.dll
.text:004010D3                 push    ecx             ; lpProcName
.text:004010D4                 push    eax             ; hModule
.text:004010D5                 call    ds:GetProcAddress
.text:004010DB                 cmp     eax, ebx        ; EBX = 0
.text:004010DD                 mov     [ebp+arg_8], eax ; arg_8 = GetProcessAddress(user32.unknown_function?)
.text:004010DD                                         ; See at 0x40116A call [ebp+arg_8] just after a string.
.text:004010DD                                         ; arg_8 could be "MessageBoxA"?

We see that user32.dll is imported at 0x4010C2 with the call to GetModuleHandleA and that a function of this library is imported at 0x4010D5 with a call to GetProcAddress. This last function has 2 parameters:

  • hModule: handle to user32.dll (EAX as output of GetModuleHandleA)
  • lpProcName: function to call (ECX gets the value of var_18 at 0x4010D0)

We still need to understand what function is called here. At 0x4010DD, we see that EAX (the return value) is saved to arg_8. Later in the code, we also see string manipulation just before a call to arg_8:

At this stage, we can assume that arg_8 is the user32.MessageBoxA function. Let's use this assumption and come back to our previous code extract. If arg_8 is indeed "MessageBoxA", var_18 at 0x4010D0 also represents this string because this is the lpProcName parameter as explained earlier.

So we need to understand how var_18 is set.

var_18

As shown below, var_18 actually results from XOR operations at 0x4010B3 (xor byte ptr [ebp+ecx+var_18], al).

Each character is XOR'ed with AL which is the character of String2 corresponding to the same index. And String2 is a key:

.text:0040108B mov     esi, offset unk_402078 ; key = 24 0B 15 1C 13 0A 04 36 06 17 2F 64 64
.text:00401090 lea     edi, [ebp+String2]
.text:00401093 movsd                   ; 24 0B 15 1C (dword)
.text:00401094 movsd                   ; 13 0A 04 36 (dword)
.text:00401095 movsd                   ; 06 17 2F 64 (dword)
.text:00401096 movsw                   ; 64 00 (word)

Let's reverse the logic. As we assumed the resulting string should be "MessageBoxA", here is what we have:

var_18 M
0x4d
e
0x65
s
0x73
s
0x73
a
0x61
g
0x67
e
0x65
B
0x42
o
0x6f
x
0x78
A
0x41
String2 0x24 0x0B 0x15 0x1C 0x13 0x0A 0x04 0x36 0x06 0x17 0x2F 0x64 0x64
XOR 0x69
i
0x6e
n
0x66
f
0x6f
o
0x72
r
0x6d
m
0x61
a
0x74
t
0x69
i
0x6f
o
0x6e
n

The second field should be set to "information".

My script

#!/usr/bin/env python
print "*1st field: %s" % str(0x200E4 + 0xD)

key = [0x24, 0x0B, 0x15, 0x1C, 0x13, 0x0A, 0x04, 0x36, 0x06, 0x17, 0x2F, 0x64, 0x64]
s = "MessageBoxA"
print "*2nd field: %s" % ''.join([chr(ord(i) ^ key[c]) for c, i in enumerate(s)]

Comments

Keywords: assembly x86 reverse-engineering crackme devastator