Solution-DevAstatoR-What-do-I-want
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