Solution-Vik3790-Keygenme
Description
Download link: http://crackmes.de/users/vik3790/keygenme/
The keygen is composed of a 32 bit executable (Arrow-keygenme.exe and an image folder, containing an image: arrows.jpg.
When you start the program, nothing happens :(
Analysis
Start
Let's open the executable in IDA-Pro. The start function pushes DialogFunc as lpDialogFunc parameter at offset 0x40100E:
.text:00401000 public start
.text:00401000 start proc near
.text:00401000 push 0 ; lpModuleName
.text:00401002 call GetModuleHandleA
.text:00401007 mov hInstance, eax
.text:0040100C push 0 ; dwInitParam
.text:0040100E push offset DialogFunc ; lpDialogFunc
.text:00401013 push 0 ; hWndParent
.text:00401015 push 65h ; lpTemplateName
.text:00401017 push hInstance ; hInstance
.text:0040101D call DialogBoxParamA
.text:00401022 push eax ; uExitCode
.text:00401023 call ExitProcess
.text:00401023 start endp
DialogFunc
Graph Overview
The DialogFunc function graph overview is as follows:
arrows.jpg
A quick overview of the code shows that the program is trying to access an image (arrows.jpg):
.text:0040106C push 0 ; hTemplateFile
.text:0040106E push FILE_FLAG_OVERLAPPED ; dwFlagsAndAttributes
.text:00401073 push OPEN_EXISTING ; dwCreationDisposition
.text:00401075 push 0 ; lpSecurityAttributes
.text:00401077 push FILE_SHARE_READ ; dwShareMode
.text:00401079 push GENERIC_READ ; dwDesiredAccess
.text:0040107E push offset FileName ; "arrows.jpg"
.text:00401083 call CreateFileA
If we move the file arrows.jpg from the image directory to the parent location, where the program has been copied, the program now shows a graphical interface:
sub_401221
Starting from offset 004011A1, the local time is saved to SystemTime via a call to GetLocalTime at offset 0x4011A6:
.text:004011A1 push offset SystemTime ; local time saved to SystemTime
.text:004011A6 call GetLocalTime
Starting from offset 0x4011AB, byte 0x40302F is pushed as argument to sub_401221 and the function is called at offset 0x4011B0.
.text:004011AB push offset byte_40302F
.text:004011B0 call sub_401221
Below is the code of the sub_401221 function:
.text:00401221 sub_401221 proc near
.text:00401221
.text:00401221 byte_40302F = dword ptr 8
.text:00401221
.text:00401221 push ebp
.text:00401222 mov ebp, esp
.text:00401224 xor eax, eax ; eax = 0
.text:00401226 mov ax, SystemTime.wYear ; ax = year
.text:0040122C rol eax, 10h ; eax = year << 0x10
.text:0040122F xor ebx, ebx ; ebx = 0
.text:00401231 mov bx, SystemTime.wMonth ; bx = month
.text:00401238 rol ebx, 8 ; ebx = month << 0x8
.text:0040123B or eax, ebx ; eax = eax | ebx
.text:0040123D mov bx, SystemTime.wHour ; bx = hour
.text:00401244 or eax, ebx ; eax = eax | ebx
.text:00401246 mov ebx, [ebp+byte_40302F] ; \
.text:00401249 mov [ebx], eax ; / eax saved to location 0x40302F
.text:0040124B leave
.text:0040124C retn 4
.text:0040124C sub_401221 endp
This function is actually building the following byte:
Byte 0x40302F ┌────┬────┬───────┐ │ 13 │ 02 │ E0 07 │ └────┴────┴───────┘ │ │ │ │ │ └─ 0x07E0: current year (2016) │ └─ 0x02: current month (February) └── 0x13: current hour (6pm)
Validate user format
GetDlgItemTextA
Starting from offset 0x401045, GetDlgItemTextA gets the username, which is pushed as argument to sub_40124F and the function is called at offset 0x40105A:
.text:00401045 push 20h ; cchMax
.text:00401047 push offset String ; lpString
.text:0040104C push 3EAh ; nIDDlgItem => ID #1006 (Name)
.text:00401051 push [ebp+hDlg] ; hDlg
.text:00401054 call GetDlgItemTextA
.text:00401059 push eax ; eax = my_user
.text:0040105A call sub_40124F ; validate_user_format
sub_40124F
The function layout is as follows:
First, it checks that the username is 6 characters long:
.text:00401252 cmp [ebp+my_user], 6 ; len(my_user) = 6
.text:00401256 jnz short INVALID_FMT
Then, it checks whether the first 2 characters are in the range [a-z] and adds the sum of these characters to AL at offset 0x401273 (remember that EAX initially contains the length of the username, at offset 0x401059).
.text:00401258 mov ecx, 2
.text:0040125D
.text:0040125D loc_40125D:
.text:0040125D cmp byte ptr [ecx+4030ABh], 61h ; 'a'
.text:00401264 jge short loc_401268
.text:00401266 jmp short INVALID_FMT
.text:00401268 ; ---------------------------------------------------------------------------
.text:00401268
.text:00401268 loc_401268:
.text:00401268 cmp byte ptr [ecx+4030ABh], 7Ah ; 'z'
.text:0040126F jle short loc_401273
.text:00401271 jmp short INVALID_FMT
.text:00401273 ; ---------------------------------------------------------------------------
.text:00401273
.text:00401273 loc_401273:
.text:00401273 add al, [ecx+4030ABh] ; al += my_user[i]
.text:00401279 loop loc_40125D ; my_user[0] and my_user[1]
.text:00401279 ; should be [a-z]
Starting from offset 0x40127B, the program now checks the last 4 characters (they should be numeric) and adds the value of these characters in AH at offset 0x401296.
.text:0040127B mov ecx, 4
.text:00401280
.text:00401280 loc_401280:
.text:00401280 cmp byte_4030AD[ecx], 30h ; '0'
.text:00401287 jge short loc_40128B
.text:00401289 jmp short INVALID_FMT
.text:0040128B ; ---------------------------------------------------------------------------
.text:0040128B
.text:0040128B loc_40128B:
.text:0040128B cmp byte_4030AD[ecx], 39h ; '9'
.text:00401292 jle short loc_401296
.text:00401294 jmp short INVALID_FMT
.text:00401296 ; ---------------------------------------------------------------------------
.text:00401296
.text:00401296 loc_401296:
.text:00401296 add ah, byte_4030AD[ecx] ; ah += my_user[i]
.text:0040129C loop loc_401280 ; my_user[2], my_user[3],
.text:0040129C ; my_user[4], my_user[5]
.text:0040129C ; should be [0-9]
Below is an example with the username gj7400:
┌────────────────────┬──────┬──────┬─────────┐ │ │ AH │ AL │ AX │ │ │ │ │ (cumul) │ ├─────────┬──────────┼──────┼──────┼─────────┤ │ length │ 0x06 │ │ │ 0x0006 │ ├─────────┼──────────┼──────┼──────┼─────────┤ │ alpha │ 0x6a (j) │ │ 0x6a │ 0x0070 │ │ │ 0x67 (g) │ │ 0x67 │ 0x00d7 │ ├─────────┼──────────┼──────┼──────┼─────────┤ │ numeric │ 0x30 (0) │ 0x30 │ │ 0x30d7 │ │ │ 0x30 (0) │ 0x30 │ │ 0x60d7 │ │ │ 0x34 (4) │ 0x34 │ │ 0x94d7 │ │ │ 0x37 (7) │ 0x37 │ │ 0xcbd7 │ └─────────┴──────────┴──────┴──────┴─────────┘
In our example, 9xCBD7 is actually the 2nd part of the expected key, as we will see later.
Key
sub_4012A6
The program then calls sub_4012A6 at offset 0x4010C3. The function looks like this:
.text:004012A6 make_key proc near
.text:004012A6 lea edi, byte_40302F ; 0x1302E007
.text:004012AC mov ah, [edi+3] ; ah = 0x07
.text:004012AF mov al, [edi+2] ; al = 0xE0
.text:004012B2 xor ebx, ebx ; ebx = 0
.text:004012B4 mov bx, ax ; bx = 0x07E0
.text:004012B7 xor eax, eax ; eax = 0
.text:004012B9 mov al, [edi+1] ; al = 0x02
.text:004012BC imul bx, ax ; bx = 0x07E0 * 0x02 = 0x0FC0
.text:004012C0 xor eax, eax ; eax = 0
.text:004012C2 mov al, [edi] ; al = 0x13
.text:004012C4 add ebx, eax ; ebx = 0x0FC0 + 0x13 = 0xFD3
.text:004012C6 rol ebx, 10h ; ebx = 0xFD3 << 0x10 = 0xfd30000
.text:004012C9 mov eax, key ; eax = key (2nd part)
.text:004012CE mov bh, ah
.text:004012D0 mov bl, al
.text:004012D2 mov key, ebx ; ebx = concat(ebx, key_2ndpart)
.text:004012D8 retn
.text:004012D8 make_key endp
The code manipulates byte_40302F and then concatenates the result with the previous key (2nd part). In our example, it makes:
┌────────┬────────┐ │ 0x0fd3 │ 0xcbd7 │ => FD3CBD7 └────────┴────────┘ ▲ ▲ │ └── sub_40124F └── sub_4012A6
Final check
The key is transformed to hexadecimal with capital letters at offset 0x4010D8 and the key is compared to the expected one at offset 0x4010FE:
.text:004010C8 push key
.text:004010CE push offset aIx ; "%IX"
.text:004010D3 push offset expected_key ; LPSTR
.text:004010D8 call wsprintfA ; convert to Hex with capital characters
.text:004010DD add esp, 0Ch
.text:004010E0 push 20h ; cchMax
.text:004010E2 push offset my_key ; lpString
.text:004010E7 push 3E9h ; nIDDlgItem
.text:004010EC push [ebp+hDlg] ; hDlg
.text:004010EF call GetDlgItemTextA
.text:004010F4 push offset my_key ; lpString2
.text:004010F9 push offset expected_key ; lpString1
.text:004010FE call lstrcmpA
.text:00401103 or eax, eax
.text:00401105 jnz short loc_401119
.text:00401107 push offset aCorrectNowYouK ; "Correct, Now you know what to do; don¦t"...
.text:0040110C push 3F1h ; nIDDlgItem
.text:00401111 push [ebp+hDlg] ; hDlg
Solution
Keygen
#!/usr/bin/env python
import sys
import re
from datetime import datetime
def make_serial(username):
eax = len(username)
# AL
for i in range(2):
eax += ord(username[i])
# AH
for i in range(2,6):
eax += ord(username[i]) * 0x100
d = datetime.now()
return "%X%X" % (d.year * d.month + d.hour, eax)
if __name__ == '__main__':
if len(sys.argv) != 2:
print "Usage: %s <username>" % sys.argv[0]
sys.exit()
if re.match('[a-z]{2}[0-9]{4}', sys.argv[1]):
print make_serial(sys.argv[1])
else:
print "[ERROR] Invalid format ([a-z]{2}[0-9]{4})"
sys.exit()
Usage examples
$ ./keygen.py aldeid [ERROR] Invalid format ([a-z]{2}[0-9]{4}) $ ./keygen.py gJ2345 [ERROR] Invalid format ([a-z]{2}[0-9]{4}) $ ./keygen.py gj7400 FD1CBD7
Comments
Keywords: assembly reverse-engineering crackme vik3790