Solution-Thunder-cls-Sticky-Crackme
Description
Download
Very funny crackme (especially the way to validate the serial) where you will have to bypass several anti-debugging tricks.
Download link: http://crackmes.de/users/thunder_cls/sticky_crackme/download
- Type: PE32 executable (GUI) Intel 80386, for MS Windows
- MD5: 46609f011a7ccda9e382e8dcc22ea6c1
- SHA1: 0cdfe3695d802d6e4d7a5b782f969ca18ddf6eca
Running the crackme
When you run the program without argument, nothing happens. Providing an argument would help, provided you know what to write :)
Analysis
Basic static analysis
Analyzing this crackme requires some organization because they are many functions. A good start is to proceed with a basic static analysis to find information that we could then confirm with a detailed static analysis and a dynamic approach. For this purpose, I recommend the strings command as well as the pescanner.py script.
Here are some interesting information:
Information | Description |
---|---|
The program is likely to be developped in Borland C++ |
Presence of following strings: Borland C++ - Copyright 2002 Borland Corporation Software\Borland\Delphi\Locales SOFTWARE\Borland\Delphi\RTL Software\Borland\Locales |
The program is likely to use notepad.exe |
Presence of following strings: Notepad notepad.exe Software\Microsoft\Notepad |
Interesting banners |
Seen in the strings: Thunder Sticky Crackme v1.0 Autor...: Thunder Protection....: Funny -=[ Cuban Cracking Project 2010 ]=- Serial: Thunder Sticky Crackme v1.0 Autor...: Thunder Protection....: Funny HEY!!!...YOU FOUND ME DUDE ;) Here's some info for you Release info..: - Coded in C++ (BC++ 6) - Some lame & home-made antidbg (threads) - Just write on notepad your "serial" - Tip1: 6C656E6768742873657269616C293D3D 3078354500616E20 - Tip2: 7469746C653D3D757365722B6461792D 6D6F6E74682D28796561722B31352900 - Tip3: 6E6F206D6F72652C2069742069732065 617379206D616E00 - Make a working keygen and tut -=[ Cuban Cracking Project 2010 ]=- |
Anti-debugging tricks? |
debugger disassembler decompiler w32dasm pe explorer ollydbg olly... |
Possible argument? |
-noSound |
Interesting imports |
At this stage, we have gathered several information that could enable to assess the following hypothesis:
- the program is probably expecting an argument (-noSound)?
- the combination of strings related to "notepad" and the IAT WinExec could be used to open notepad automatically
- The content of a process will be read in memory
- The crackme has keylogging features (GetAsyncKeyState)
- The crackme is likely to have anti-debugging capabilities
Also one of the banners provides additional information:
hex | Translation | Description |
---|---|---|
6C656E6768742873657269616C293D3D3078354500616E20 | lenght(serial)==0x5E | The serial should have a length of 94 |
7469746C653D3D757365722B6461792D6D6F6E74682D28796561722B31352900 | title==user+day-month-(year+15) | The title (we don't know what title yet) should be the concatenation of the username and datetime info |
6E6F206D6F72652C2069742069732065617379206D616E00 | no more, it is easy man | OK :) |
Arguments
Graph overview
Starting our analysis with the WinMain function, here is the graph overview:
As you have noticed, there are 3 possible arguments, 1 of which being invalid, as we will detail in the next sections.
noSound
Starting from offset 0x4014A4, the length of the argument provided to the program is computed and saved to EAX. Possible values are 11 and 16. However, a test is performed at offset 0x4014C9 to check whether the argument is -noSound, which has an inconsistent length as compared to previous tests. This argument will never be accepted by the program.
That said, it gives us a valuable information. Is there any sound in the program? Indeed, we won't detail it here, but there is a call to sub_402BF4 at offset 0x4014E1, a function that reads a wav and loops over it.
.text:004014A4 call _strlen
.text:004014A9 pop ecx
.text:004014AA mov [ebp+len_arg], ax
.text:004014AE cmp [ebp+len_arg], 0Bh ; \ len(arg) = 11?
.text:004014B3 jz short loc_4014C0 ; /
.text:004014B5 cmp [ebp+len_arg], 10h ; \ len(arg) = 16?
.text:004014BA jnz loc_4015CD ; /
.text:004014C0
.text:004014C0 loc_4014C0:
.text:004014C0 push offset s2 ; '-noSound'
.text:004014C5 lea eax, [ebp+s1]
.text:004014C8 push eax ; s1
.text:004014C9 call _strcmp
.text:004014CE add esp, 8
.text:004014D1 test eax, eax
.text:004014D3 jz short loc_4014E6
.text:004014D5 push 1
.text:004014D7 push 0F6Eh
.text:004014DC push offset aExtendedModuleBl ; "Extended Module: blinded (loop) \x"...
.text:004014E1 call f_play_music
LetUsStick
Starting from offset 0x4014E6, tests are performed against each character of the provided argument to check whether it is -LetUsStick, as detailed below:
.text:004014E6 loc_4014E6:
.text:004014E6 cmp [ebp+arg], 2Dh ;
.text:004014E6 ; arg[0] = '-'
.text:004014EA jnz loc_401573
.text:004014F0 cmp byte ptr [ebp-47h], 4Ch ; arg[1] = 'L'
.text:004014F4 jnz short loc_401573
.text:004014F6 xor eax, eax
.text:004014F8 mov al, [ebp-46h] ; arg[2] = 0x4E + 0x14 ^ 0x7 = 'e'
.text:004014FB xor eax, 7
.text:004014FE sub eax, 14h
.text:00401501 cmp eax, 4Eh
.text:00401504 jnz short loc_401573
.text:00401506 cmp byte ptr [ebp-45h], 74h ; arg[3] = 't'
.text:0040150A jnz short loc_401573
.text:0040150C xor eax, eax
.text:0040150E mov al, [ebp-44h] ; arg[4] = 0x59 - 0x18 ^ 0x14 = 'U'
.text:00401511 xor eax, 14h
.text:00401514 add eax, 18h
.text:00401517 cmp eax, 59h
.text:0040151A jnz short loc_401573
.text:0040151C cmp byte ptr [ebp-43h], 73h ; arg[5] = 's'
.text:00401520 jnz short loc_401573
.text:00401522 xor eax, eax
.text:00401524 mov al, [ebp-42h] ; arg[6] = 0x28E / 0x3 - 0x87 = 'S'
.text:00401527 add eax, 87h
.text:0040152C imul eax, 3
.text:0040152F cmp eax, 28Eh
.text:00401534 jnz short loc_401573
.text:00401536 cmp byte ptr [ebp-41h], 74h ; arg[7] = 't'
.text:0040153A jnz short loc_401573
.text:0040153C xor eax, eax
.text:0040153E mov al, [ebp-40h] ; arg[8] = (0x3E ^ 0x7) + 0x30 = 'i'
.text:00401541 sub eax, 30h
.text:00401544 xor eax, 7
.text:00401547 cmp eax, 3Eh
.text:0040154A jnz short loc_401573
.text:0040154C cmp byte ptr [ebp-3Fh], 63h ; arg[9] = 'c'
.text:00401550 jnz short loc_401573
.text:00401552 xor eax, eax
.text:00401554 mov al, [ebp-3Eh] ; arg[10] = (0xF1ACA - 0xA517A) / 0x1987 + 0x3B = 'k'
.text:00401557 sub eax, 3Bh
.text:0040155A imul eax, 1987h
.text:00401560 add eax, 0A517Ah
.text:00401565 cmp eax, 0F1ACAh
.text:0040156A jnz short loc_401573
.text:0040156C call f_open_notepad ; Function called with
.text:0040156C ; '-LetUsStick' arg
.text:00401571 jmp short loc_4015CB
When this argument is provided, the program calls sub_40168C (renamed f_open_notepad) at offset 0x40156C.
TellMeSomeThing
A similar mechanism is implemented later to test whether the argument is -TellMeSomeThing:
.text:00401573 loc_401573:
.text:00401573 cmp [ebp+arg], 2Dh ; arg[0] = '-'
.text:00401577 jnz short loc_4015C4
.text:00401579 xor eax, eax
.text:0040157B mov eax, [ebp-47h] ; arg[1] = 0x6C6CAF9E ^ 0xCACA = 0x6c6c6554 ('Tell')
.text:0040157E xor eax, 0CACAh
.text:00401583 cmp eax, 6C6CAF9Eh
.text:00401588 jnz short loc_4015C4
.text:0040158A xor eax, eax
.text:0040158C mov eax, [ebp-43h] ; arg[5] = 0xC0C0 ^ 0x6F53A58D = 0x6f53654d ('MeSo')
.text:0040158F xor eax, 0C0C0h
.text:00401594 cmp eax, 6F53A58Dh
.text:00401599 jnz short loc_4015C4
.text:0040159B xor eax, eax
.text:0040159D mov eax, [ebp-3Fh] ; arg[9] = 0xC0CA ^ 0x6854A5A7 = 0x6854656d ('meTh')
.text:004015A0 xor eax, 0C0CAh
.text:004015A5 cmp eax, 6854A5A7h
.text:004015AA jnz short loc_4015C4
.text:004015AC xor eax, eax
.text:004015AE mov eax, [ebp-3Bh] ; arg[13] = 0xCBCB ^ 0x67A5A2 = 0x676e69 ('ing')
.text:004015B1 xor eax, 0CBCBh
.text:004015B6 cmp eax, 67A5A2h
.text:004015BB jnz short loc_4015C4
.text:004015BD call f_notepad_help ; function called with
.text:004015BD ; '-TellMeSomeThing' arg
When this argument is provided, the program calls sub_401988 (renamed f_notepad_help) at offset 0x4015BD. It displays the help banner detailed in the introduction:
f_open_notepad (sub_40168C)
Notepad banner
As we noticed in the strings, there is a banner loaded to ESI at offset 0x40169E and copied to lparam with a rep movsd at offset 0x4016AE:
Later in the code at offset 0x4017D7, the notepad will be opened with a call to WinExec:
.text:004017D0 loc_4017D0: ; uCmdShow
.text:004017D0 push 3
.text:004017D2 push offset CmdLine ; "notepad.exe"
.text:004017D7 call WinExec
.text:004017DC call GetCurrentProcessId
.text:004017E1 push eax ; dwProcessId
.text:004017E2 push 0 ; bInheritHandle
.text:004017E4 push 1F0FFFh ; dwDesiredAccess
.text:004017E9 call OpenProcess
.text:004017EE mov [ebp+hProcess], eax
.text:004017F4 push offset ProcName ; "FindWindowA"
.text:004017F9 push offset ModuleName ; "user32.dll"
.text:004017FE call GetModuleHandleA
.text:00401803 push eax ; hModule
.text:00401804 call GetProcAddress
.text:00401809 mov [ebp+lpBaseAddress], eax
.text:0040180F cmp [ebp+lpBaseAddress], 0
.text:00401816 jz short loc_401876
At offset 0x4018EB, the banner will be displayed in the notepad window with a call to SendMessageA with the Msg parameter set to WM_SETTEXT (0xC):
.text:004018D7 lea edx, [ebp+lParam]
.text:004018DD push edx ; lParam
.text:004018DE push 1F1h ; wParam
.text:004018E3 push WM_SETTEXT ; Msg
.text:004018E5 push notepad_window ; hWnd
.text:004018EB call SendMessageA ; WM_SETTEXT
.text:004018EB ; Write banner to notepad
This is where you will have to enter the serial:
Anti-debug trick
There is an anti-debug trick based on calls to GetTickCount at 2 different positions in the code:
.text:004016EF call GetTickCount
.text:004016F4 mov [ebp+gettickcount], eax ; 1st call to GetTickCount
; .... [SNIP] ...
.text:004018A9 call GetTickCount ; 2nd call to GetTickCount
.text:004018AE sub eax, [ebp+gettickcount]
.text:004018B4 mov [ebp+_gettickcount], eax
.text:004018BA cmp notepad_window, 0
.text:004018C1 jz loc_401980
.text:004018C7 cmp [ebp+_gettickcount], 1F4h ; \ check whether prgm
.text:004018D1 ja loc_401978 ; / being debugged
; [PATCH] NOP
Call StartAddress
At the end of this function, StartAddress is called via CreateThread:
.text:00401958 lea eax, [ebp+ThreadId]
.text:0040195E push eax ; lpThreadId
.text:0040195F push 0 ; dwCreationFlags
.text:00401961 push 0 ; lpParameter
.text:00401963 push offset StartAddress ; lpStartAddress
.text:00401968 push 0 ; dwStackSize
.text:0040196A push 0 ; lpThreadAttributes
.text:0040196C call CreateThread
.text:00401971 mov dword_4220CC, eax
.text:00401976 jmp short loc_401980
StartAddress (sub_401BE0)
Check key length
Starting from offset 0x401CD3, there is a loop that gets the serial entered in the notepad and saves it to src (renamed key):
.text:00401CD3 loc_401CD3:
.text:00401CD3 lea eax, [ebp+lParam] ; [BP] Enter serial
.text:00401CD9 push eax ; lParam
.text:00401CDA push 252h ; wParam
.text:00401CDF push WM_GETTEXT ; Msg
.text:00401CE1 push notepad_window ; hWnd
.text:00401CE7 call SendMessageA ; WM_GETTEXT (get serial)
.text:00401CEC call GetTickCount
.text:00401CF1 mov [ebp+gettickcount], eax
.text:00401CF4 mov [ebp+counter_k], 1F0h ; k = 496
.text:00401CF4 ; (offset in notepad to get serial)
.text:00401CFB xor edx, edx
.text:00401CFD mov [ebp+counter_j], edx ; j = 0
.text:00401D00 jmp short loc_401D1B
.text:00401D02 ; ---------------------------------------------------------------------------
.text:00401D02
.text:00401D02 loc_401D02:
.text:00401D02 mov ecx, [ebp+counter_k] ;
.text:00401D02 ; ecx = k
.text:00401D05 mov al, byte ptr [ebp+ecx+lParam]
.text:00401D0C mov edx, [ebp+counter_j]
.text:00401D0F mov serial[edx], al ; src = serial (memory location 0x423470)
.text:00401D15 inc [ebp+counter_k]
.text:00401D18 inc [ebp+counter_j]
.text:00401D1B
.text:00401D1B loc_401D1B:
.text:00401D1B mov ecx, [ebp+counter_k] ;
.text:00401D1B ; ecx = k
.text:00401D1E cmp byte ptr [ebp+ecx+lParam], 0
.text:00401D26 jz short loc_401D2E
.text:00401D28 cmp [ebp+counter_j], 5Eh ; while(j<94)
.text:00401D28 ; len(serial) = 94
.text:00401D2C jle short loc_401D02
.text:00401D2E
.text:00401D2E loc_401D2E:
.text:00401D2E mov eax, [ebp+counter_j]
.text:00401D31 mov serial[eax], 0 ; add trailing null at the end of the serial
Later in the code, there is a check of this key length to ensure it is 94 characters long:
.text:00401D50 loc_401D50:
.text:00401D50 popa
.text:00401D51 xor [ebp+counter_j], 0FE0h ; j = 0x5E ^ 0xFE0 = 0xfbe
.text:00401D58 sub [ebp+counter_j], 87h ; j = 0xfbe - 0x87 = 0xf37
.text:00401D5F mov edx, [ebp+counter_j] ; edx = 0xf37
.text:00401D62 mov ecx, edx ; ecx = 0xf37
.text:00401D64 shl edx, 3 ; edx = 0xf37 << 0x3 = 0x79b8
.text:00401D67 sub edx, ecx ; edx = 0x79b8 - 0xf37 = 0x6a81
.text:00401D69 mov [ebp+counter_j], edx ; j = 0x6a81
.text:00401D6C cmp [ebp+counter_j], 6A81h ; \
.text:00401D73 jnz loc_401E24 ; / ensure that length(serial) = 0x5E
Validate the title of notepad
Two functions are used to validate the title of the notepad window:
- At offset 0x401D8B, sub_4020D4 (renamed f_notepad_title) is called. This function computes a string (expected notepad title) based on the Windows user name and the system time, encrypts and saves it.
- At offset 0x401DC6, sub_4023C0 (renamed f_validate_title) is called. This function encrypts the title of the notepad window where the serial is entered, using the same algorithm, and check whether both encrypted strings match.
.text:00401D86 push offset Buffer ; username
.text:00401D8B call f_notepad_title ; encrypt notepad title
; ...[SNIP]...
.text:00401DC6 call f_validate_title
.text:00401DCB cmp al, 1 ; f_validate_title
.text:00401DCB ; should return 1
.text:00401DCD jnz short loc_401E24
Check spaces in serial
There is a call to sub_401E3C (renamed f_serial_spaces) at offset 0x401D79. This function checks that the serial contains spaces at given positions, as we will see later.
.text:00401D79 call f_serial_spaces
.text:00401D7E test al, al ; \ f_serial_spaces should
.text:00401D80 jz loc_401E24 ; / return 1
Check other characters in serial
There are 2 functions to check that the serial characters (non space) are correct:
- sub_401FDC (renamed f_serial_nonspace) called at offset 0x401DAD
- sub_40235C (renamed f_validate_serial_chars) called at offset 0x401DBD
.text:00401DAC loc_401DAC:
.text:00401DAC popa
.text:00401DAD call f_serial_nonspace
.text:00401DB2 call GetTickCount
.text:00401DB7 sub eax, [ebp+gettickcount]
.text:00401DBA mov [ebp+_gettickcount], eax
.text:00401DBD call f_validate_serial_chars
.text:00401DC2 cmp al, 1 ; f_validate_serial_chars
.text:00401DC2 ; should return 1
.text:00401DC4 jnz short loc_401E24
Anti-debug tricks
Starting from offset 0x401D2E, there is another anti-debug trick, based on PEB.NtGlobalFlag:
.text:00401D2E loc_401D2E:
.text:00401D2E mov eax, [ebp+counter_j]
.text:00401D31 mov serial[eax], 0
.text:00401D38 pusha
.text:00401D39 mov eax, large fs:30h ; eax = PEB
.text:00401D40 db 3Eh
.text:00401D40 mov eax, [eax+68h] ; eax = PEB.NtGlobalFlag
.text:00401D44 and eax, 70h ; \ check whether prgm
.text:00401D47 jz short loc_401D50 ; / is being debugged
.text:00401D47 ; [PATCH] NOP
.text:00401D49 mov is_key_valid, 1
There is something interesting here that you should begin to understand. Each time there is an anti-debug trick, the program checks whether it is being debugged. In this case, it will terminate. Otherwise, it will set a flag to 1. This is what I've renamed is_key_valid.
Another anti-debug trick starts at offset 0x401D92. This time, it only checks whether the FLG_HEAP_ENABLE_TAIL_CHECK flag is set.
.text:00401D92 mov eax, large fs:30h ; eax = PEB
.text:00401D99 db 3Eh
.text:00401D99 mov eax, [eax+18h] ; eax = PEB.NtGlobalFlag
.text:00401D9D db 3Eh
.text:00401D9D mov eax, [eax+10h] ; FLG_HEAP_ENABLE_TAIL_CHECK flag set?
.text:00401DA1 test eax, eax ; \ check whether prgm
.text:00401DA3 jz short loc_401DAC ; / being debugged
.text:00401DA3 ; [PATCH] NOP
.text:00401DA5 mov is_key_valid, 1
And to finish the anti-debug tricks seen in this function, there is also a GetTickCount test:
.text:00401CEC call GetTickCount
.text:00401CF1 mov [ebp+gettickcount], eax
; ... [SNIP] ...
.text:00401DB2 call GetTickCount
.text:00401DB7 sub eax, [ebp+gettickcount]
.text:00401DBA mov [ebp+_gettickcount], eax
; ... [SNIP] ...
.text:00401DCF cmp [ebp+_gettickcount], 200h
.text:00401DD6 ja short loc_401E24 ; [PATCH] Patch with NOP
Notepad title validation
f_notepad_title (sub_4020D4)
This function takes the username (Windows username of logged in user) as argument. The code gathers the system time to build a string that will be concatenated to the username. The resulting string is then copied to dest at offset 0x4022DB.
.text:004020D4 f_notepad_title proc near
.text:004020D4
.text:004020D4 SystemTime = _SYSTEMTIME ptr -78h
.text:004020D4 counter_i = dword ptr -68h
.text:004020D4 strlen_title = dword ptr -64h
.text:004020D4 var_60 = dword ptr -60h
.text:004020D4 gettickcount = dword ptr -5Ch
.text:004020D4 var_58 = dword ptr -58h
.text:004020D4 var_48 = word ptr -48h
.text:004020D4 index_i = dword ptr -3Ch
.text:004020D4 var_34 = byte ptr -34h
.text:004020D4 var_30 = byte ptr -30h
.text:004020D4 var_2C = byte ptr -2Ch
.text:004020D4 var_28 = byte ptr -28h
.text:004020D4 var_24 = byte ptr -24h
.text:004020D4 var_20 = byte ptr -20h
.text:004020D4 var_1C = byte ptr -1Ch
.text:004020D4 var_18 = byte ptr -18h
.text:004020D4 var_14 = byte ptr -14h
.text:004020D4 var_10 = byte ptr -10h
.text:004020D4 var_C = byte ptr -0Ch
.text:004020D4 var_8 = byte ptr -8
.text:004020D4 var_4 = byte ptr -4
.text:004020D4 windows_username= dword ptr 8
.text:004020D4
.text:004020D4 push ebp
.text:004020D5 mov ebp, esp
.text:004020D7 add esp, 0FFFFFF88h
.text:004020DA mov eax, offset stru_424E84
.text:004020DF call @__InitExceptBlockLDTC
.text:004020E4 mov [ebp+var_48], 8
.text:004020EA lea eax, [ebp+var_4]
.text:004020ED call unknown_libname_34 ; Borland Visual Component Library & Packages
.text:004020F2 inc [ebp+index_i] ; index_i = 1
.text:004020F5 mov [ebp+var_48], 14h
.text:004020FB lea edx, [ebp+SystemTime]
.text:004020FE push edx ; lpSystemTime
.text:004020FF call GetLocalTime ; get datetime
.text:00402104 call GetTickCount
.text:00402109 mov [ebp+gettickcount], eax
.text:0040210C mov [ebp+var_48], 20h
.text:00402112 lea eax, [ebp+var_14]
.text:00402115 call unknown_libname_34 ; Borland Visual Component Library & Packages
.text:0040211A push eax
.text:0040211B inc [ebp+index_i] ; index_i = 2
.text:0040211E mov edx, offset asc_424D16 ; "-"
.text:00402123 lea eax, [ebp+var_10]
.text:00402126 call sub_421050
.text:0040212B inc [ebp+index_i] ; index_i = 3
.text:0040212E lea edx, [ebp+var_10]
.text:00402131 push edx
.text:00402132 lea eax, [ebp+var_C] ; this
.text:00402135 mov dx, [ebp+SystemTime.wDay] ; dx = current day
.text:00402139 call @System@AnsiString@$bctr$qqrus ; System::AnsiString::AnsiString(ushort)
.text:0040213E inc [ebp+index_i] ; index_i = 4
.text:00402141 pop edx
.text:00402142 pop ecx
.text:00402143 call @System@AnsiString@$badd$xqqrrx17System@AnsiString ; System::AnsiString::operator+(System::AnsiString &)
.text:00402148 lea eax, [ebp+var_14]
.text:0040214B push eax
.text:0040214C lea eax, [ebp+var_1C]
.text:0040214F call unknown_libname_34 ; Borland Visual Component Library & Packages
.text:00402154 push eax
.text:00402155 inc [ebp+index_i] ; index_i = 5
.text:00402158 lea eax, [ebp+var_18] ; this
.text:0040215B mov dx, [ebp+SystemTime.wMonth] ; dx = current month
.text:0040215F call @System@AnsiString@$bctr$qqrus ; System::AnsiString::AnsiString(ushort)
.text:00402164 mov edx, eax
.text:00402166 inc [ebp+index_i] ; index_i = 6
.text:00402169 pop ecx
.text:0040216A pop eax
.text:0040216B call @System@AnsiString@$badd$xqqrrx17System@AnsiString ; System::AnsiString::operator+(System::AnsiString &)
.text:00402170 lea eax, [ebp+var_1C]
.text:00402173 push eax
.text:00402174 lea eax, [ebp+var_24]
.text:00402177 call unknown_libname_34 ; Borland Visual Component Library & Packages
.text:0040217C push eax
.text:0040217D inc [ebp+index_i] ; index_i = 7
.text:00402180 mov edx, offset asc_424D18 ; edx = '-' (separator)
.text:00402185 lea eax, [ebp+var_20]
.text:00402188 call sub_421050
.text:0040218D inc [ebp+index_i] ; index_i = 8
.text:00402190 lea edx, [ebp+var_20]
.text:00402193 pop ecx
.text:00402194 pop eax
.text:00402195 call @System@AnsiString@$badd$xqqrrx17System@AnsiString ; System::AnsiString::operator+(System::AnsiString &)
.text:0040219A lea edx, [ebp+var_24]
.text:0040219D push edx
.text:0040219E lea eax, [ebp+var_2C]
.text:004021A1 call unknown_libname_34 ; Borland Visual Component Library & Packages
.text:004021A6 push eax
.text:004021A7 inc [ebp+index_i] ; index_i = 9
.text:004021AA movzx edx, [ebp+SystemTime.wYear] ; edx = current year
.text:004021AE add edx, 0Fh ; Add 15 to current year
.text:004021B1 lea eax, [ebp+var_28] ; this
.text:004021B4 call @System@WideString@$bctr$qqrul ; System::WideString::WideString(ulong)
.text:004021B9 mov edx, eax
.text:004021BB inc [ebp+index_i] ; index_i = 10
.text:004021BE pop ecx
.text:004021BF pop eax
.text:004021C0 call @System@AnsiString@$badd$xqqrrx17System@AnsiString ; System::AnsiString::operator+(System::AnsiString &)
.text:004021C5 lea edx, [ebp+var_2C]
.text:004021C8 lea eax, [ebp+var_4]
.text:004021CB call sub_421180
.text:004021D0 dec [ebp+index_i] ; index_i = 9
.text:004021D3 lea eax, [ebp+var_2C]
.text:004021D6 mov edx, 2
.text:004021DB call sub_421150
.text:004021E0 dec [ebp+index_i] ; index_i = 8
.text:004021E3 lea eax, [ebp+var_28]
.text:004021E6 mov edx, 2
.text:004021EB call sub_421150
.text:004021F0 dec [ebp+index_i] ; index_i = 7
.text:004021F3 lea eax, [ebp+var_24]
.text:004021F6 mov edx, 2
.text:004021FB call sub_421150
.text:00402200 dec [ebp+index_i] ; index_i = 6
.text:00402203 lea eax, [ebp+var_20]
.text:00402206 mov edx, 2
.text:0040220B call sub_421150
.text:00402210 dec [ebp+index_i] ; index_i = 5
.text:00402213 lea eax, [ebp+var_1C]
.text:00402216 mov edx, 2
.text:0040221B call sub_421150
.text:00402220 dec [ebp+index_i] ; index_i = 4
.text:00402223 lea eax, [ebp+var_18]
.text:00402226 mov edx, 2
.text:0040222B call sub_421150
.text:00402230 dec [ebp+index_i] ; index_i = 3
.text:00402233 lea eax, [ebp+var_14]
.text:00402236 mov edx, 2
.text:0040223B call sub_421150
.text:00402240 dec [ebp+index_i] ; index_i = 2
.text:00402243 lea eax, [ebp+var_10]
.text:00402246 mov edx, 2
.text:0040224B call sub_421150
.text:00402250 dec [ebp+index_i] ; index_i = 1
.text:00402253 lea eax, [ebp+var_C]
.text:00402256 mov edx, 2
.text:0040225B call sub_421150
.text:00402260 mov [ebp+var_48], 2Ch
.text:00402266 lea eax, [ebp+var_34]
.text:00402269 call unknown_libname_34 ; Borland Visual Component Library & Packages
.text:0040226E push eax
.text:0040226F inc [ebp+index_i] ; index_i = 2
.text:00402272 mov edx, offset asc_424D1A ; edx = ' - ' (separator)
.text:00402277 lea eax, [ebp+var_30]
.text:0040227A call sub_421050
.text:0040227F mov edx, eax
.text:00402281 inc [ebp+index_i] ; index_i = 3
.text:00402284 mov eax, [ebp+windows_username]
.text:00402287 pop ecx
.text:00402288 call @System@$badd$qqrpxcrx17System@AnsiString ; System::operator+(char *,System::AnsiString &)
.text:0040228D lea edx, [ebp+var_34]
.text:00402290 push edx
.text:00402291 lea eax, [ebp+var_8]
.text:00402294 call unknown_libname_34 ; Borland Visual Component Library & Packages
.text:00402299 mov ecx, eax
.text:0040229B inc [ebp+index_i] ; index_i = 4
.text:0040229E lea edx, [ebp+var_4]
.text:004022A1 pop eax
.text:004022A2 call @System@AnsiString@$badd$xqqrrx17System@AnsiString ; System::AnsiString::operator+(System::AnsiString &)
.text:004022A7 dec [ebp+index_i] ; index_i = 3
.text:004022AA lea eax, [ebp+var_34]
.text:004022AD mov edx, 2
.text:004022B2 call sub_421150
.text:004022B7 dec [ebp+index_i] ; index_i = 2
.text:004022BA lea eax, [ebp+var_30]
.text:004022BD mov edx, 2
.text:004022C2 call sub_421150
.text:004022C7 mov [ebp+var_48], 14h
.text:004022CD lea eax, [ebp+var_8] ; this
.text:004022D0 call @System@AnsiString@c_str$xqqrv ; System::AnsiString::c_str(void)
.text:004022D5 push eax ; src
.text:004022D6 push offset dest ; title = username + ' - ' + day + '-' + month + '-' + (year+15)
.text:004022DB call _strcpy
The expected format of the title is as follows:
┌──────┬───────┬─────┬─────┬───────┬─────┬─────────┐ │ User │ ' - ' │ day │ '-' │ month │ '-' │ year+15 │ └──────┴───────┴─────┴─────┴───────┴─────┴─────────┘ ▲ ▲ ▲ └────────────┼─────────────┘ │ separators (notice that the 1st one contains spaces)
Starting from offset 0x4022E3, the length of the resulting string is computed and characters are XOR'ed with 0xE.
.text:004022E3 push offset dest ; s
.text:004022E8 call _strlen
.text:004022ED pop ecx
.text:004022EE mov [ebp+strlen_title], eax
.text:004022F1 xor edx, edx
.text:004022F3 mov [ebp+counter_i], edx ; i = 0
.text:004022F6 mov ecx, [ebp+counter_i]
.text:004022F9 cmp ecx, [ebp+strlen_title] ; \ while (i < len(title))
.text:004022FC jge short loc_402313 ; /
.text:004022FE
.text:004022FE loc_4022FE:
.text:004022FE mov eax, [ebp+counter_i]
.text:00402301 xor dest[eax], 0Eh ; title[i] ^= 0xE
.text:00402308 inc [ebp+counter_i]
.text:0040230B mov edx, [ebp+counter_i]
.text:0040230E cmp edx, [ebp+strlen_title]
.text:00402311 jl short loc_4022FE
This encrypted string will be used later by f_validate_title (sub_4023C0).
Notice that there is also an anti-debug trick in this function, based on GetTickCount:
.text:00402104 call GetTickCount
.text:00402109 mov [ebp+gettickcount], eax
; ...[SNIP]...
.text:00402313 loc_402313:
.text:00402313 call GetTickCount ; antidebug trick
.text:00402318 sub eax, [ebp+gettickcount]
.text:0040231B mov [ebp+tickcount], eax
.text:0040231E cmp [ebp+tickcount], 20h
.text:00402322 jbe short loc_40232C ; [PATCH] patch with unconditional jump (EB 08)
.text:00402324 push 0
.text:00402326 call f_terminate
f_validate_title (sub_4023C0)
This function encrypts the title of the notepad window with the same algorithm as the one used in f_notepad_title (sub_4020D4) and checks whether the resulting string matches the one previously computed.
Starting from offset 0x4023CE, the length of the previously encrypted title is computed and the current title of the notepad window is gathered and saved to lParam:
.text:004023CE push offset dest ; dest = encrypted title
.text:004023D3 call _strlen
.text:004023D8 pop ecx
.text:004023D9 mov [ebp+len_encrypted_title], eax
.text:004023DC lea eax, [ebp+lParam]
.text:004023DF push eax ; lParam
.text:004023E0 push 32h ; wParam
.text:004023E2 push WM_GETTEXT ; Msg
.text:004023E4 push hWnd ; hWnd
.text:004023EA call SendMessageA ; WM_GETTEXT
; Gather title of notepad window and save it in lParam
Then, each character of the notepad window title is XOR'ed with 0xE and a test ensures that each character match the ones of the encrypted string computed previously.
.text:004023F7 xor ecx, ecx
.text:004023F9 mov [ebp+counter_i], ecx ; i = 0
.text:004023FC jmp short loc_402401
.text:004023FE ; ---------------------------------------------------------------------------
.text:004023FE
.text:004023FE loc_4023FE:
.text:004023FE inc [ebp+counter_i]
.text:00402401
.text:00402401 loc_402401:
.text:00402401 mov eax, [ebp+counter_i]
.text:00402404 movsx edx, byte ptr [ebp+eax+lParam]
.text:00402409 xor edx, 0Eh
.text:0040240C mov ecx, [ebp+counter_i]
.text:0040240F movsx eax, dest[ecx] ; eax = encrypted_title[i]
.text:00402416 cmp edx, eax ; title of notepad window matches?
.text:00402418 jnz short loc_402424
.text:0040241A mov edx, [ebp+counter_i]
.text:0040241D cmp byte ptr [ebp+edx+lParam], 0
.text:00402422 jnz short loc_4023FE
Here again, there is an anti-debug trick based on GetTickCount:
.text:004023C6 call GetTickCount ; 1st call to GetTickCount
.text:004023CB mov [ebp+gettickcount], eax
; ...[SNIP]...
.text:00402424 call GetTickCount ; 2nd call to GetTickCount
.text:00402429 sub eax, [ebp+gettickcount] ; compute difference between both calls
.text:0040242C mov [ebp+tickcount], eax
; ...[SNIP]...
.text:00402437 cmp [ebp+tickcount], 20h
.text:0040243B jnb short loc_402441 ; [PATCH] patch with NOP
Serial validation
f_serial_spaces (sub_401E3C)
This function checks that there are 15 spaces in the serial, and that they are placed at the expected positions.
Starting from offset 0x401E48, a list of 15 values is loaded from memory location 0x4242AC (serial_spaces:
.text:00401E48 mov esi, offset serial_spaces
.text:00401E4D lea edi, [ebp+serial_spaces]
.text:00401E50 mov ecx, 0Fh ; ecx = 15
.text:00401E55 rep movsd
;...[SNIP]...
.data:004242AC serial_spaces dd 5, 0Bh, 0Fh, 12h, 19h, 22h, 27h, 2Ah, 2Dh, 35h, 40h
.data:004242AC dd 46h, 4Ch, 50h, 56h
Then we can see a loop that checks whether characters of the serial at the offsets of the array are all equal:
.text:00401EA1 loc_401EA1:
.text:00401EA1 movsx eax, [ebp+counter_i] ; eax = i
.text:00401EA5 mov edx, [ebp+eax*4+serial_spaces] ; edx = serial_spaces[i*4]
.text:00401EA9 mov [ebp+var_54], edx ; var_54 = serial_spaces[i*4]
.text:00401EAC movsx ecx, [ebp+counter_i] ; ecx = i
.text:00401EB0 mov eax, [ebp+ecx*4+var_38] ; var_38 = serial_spaces[i*4+4]
.text:00401EB4 mov [ebp+var_58], eax ; var_58 = serial_spaces[i*4+4]
.text:00401EB7 xor eax, eax ; eax = 0
.text:00401EB9 mov ebx, [ebp+var_54] ; ebx = serial_spaces[i*4]
.text:00401EBC mov edx, [ebp+var_58] ; edx = serial_spaces[i*4+4]
.text:00401EBF mov al, [edx+ebp-0B8h] ; al = serial[edx]
.text:00401EC6 cmp [ebx+ebp-0B8h], al ; serial[ebx] = serial[edx]?
.text:00401ECD jnz short loc_401ED7
This code ensures that the following condition is met:
serial[5] = serial[11] = serial[15] = serial[18] = serial[25] = serial[34] = serial[39] = serial[42] = serial[45] = serial[53] = serial[64] = serial[70] = serial[76] = serial[80] = serial[86]
Later in the code, the variable should_be_0x11D6, initialized to 0 at offset 0x401E65, is manipulated as follows:
.text:00401F0B loc_401F0B:
.text:00401F0B popa
.text:00401F0C xor eax, eax
.text:00401F0E xor ecx, ecx
.text:00401F10 mov al, [ebp+var_B3] ; al = serial[5]
.text:00401F16 mov cl, [ebp+var_AD] ; cl = serial[11]
.text:00401F1C add al, cl ; al = serial[5] + serial[11]
.text:00401F1E movzx eax, al
.text:00401F21 add [ebp+should_be_0x11D6], eax ; should_be_0x11D6 = serial[5] * 2
.text:00401F24 mov al, [ebp+var_A9] ; al = serial[15]
.text:00401F2A mov cl, [ebp+var_A6] ; cl = serial[18]
.text:00401F30 add al, cl ; al = serial[15] + serial[18]
.text:00401F32 movzx eax, al
.text:00401F35 add [ebp+should_be_0x11D6], eax ; should_be_0x11D6 = serial[5] * 4
.text:00401F38 mov al, [ebp+var_9F] ; al = serial[25]
.text:00401F3E mov cl, [ebp+var_96] ; cl = serial[34]
.text:00401F44 add al, cl ; al = serial[25] + serial[34]
.text:00401F46 movzx eax, al
.text:00401F49 add [ebp+should_be_0x11D6], eax ; should_be_0x11D6 = serial[5] * 6
.text:00401F4C mov al, [ebp+var_91] ; al = serial[39]
.text:00401F52 mov cl, [ebp+var_8E] ; cl = serial[42]
.text:00401F58 add al, cl ; al = serial[39] + serial[42]
.text:00401F5A movzx eax, al
.text:00401F5D add [ebp+should_be_0x11D6], eax ; should_be_0x11D6 = serial[5] * 8
.text:00401F60 mov al, [ebp+var_8B] ; serial[45]
.text:00401F66 mov cl, [ebp+var_83] ; serial[53]
.text:00401F6C add al, cl
.text:00401F6E movzx eax, al
.text:00401F71 add [ebp+should_be_0x11D6], eax ; should_be_0x11D6 = serial[5] * 10
.text:00401F74 mov al, [ebp-120] ; serial[64]
.text:00401F77 mov cl, [ebp+var_72] ; serial[70]
.text:00401F7A add al, cl
.text:00401F7C movzx eax, al
.text:00401F7F add [ebp+should_be_0x11D6], eax ; should_be_0x11D6 = serial[5] * 12
.text:00401F82 xor edx, edx
.text:00401F84 mov al, [ebp+var_6C] ; serial[76]
.text:00401F87 mov cl, [ebp+var_68] ; serial[80]
.text:00401F8A mov dl, [ebp+var_62] ; serial[86]
.text:00401F8D add al, cl
.text:00401F8F add al, dl
.text:00401F91 movzx eax, al
.text:00401F94 add [ebp+should_be_0x11D6], eax ; should_be_0x11D6 = serial[5] * 15
.text:00401F97 xor [ebp+should_be_0x11D6], 2010h ; should_be_0x11D6 ^= 0x2010
.text:00401F9E add [ebp+should_be_0x11D6], 0B0B0h ; should be 0x11D6 += 0B0B0h
.text:00401FA5 sub [ebp+should_be_0x11D6], 0C0CAh ; should_be_0x11D6 -= 0xC0CA
.text:00401FAC
.text:00401FAC loc_401FAC:
.text:00401FAC call GetTickCount
.text:00401FB1 sub eax, [ebp+gettickcount]
.text:00401FB4 mov [ebp+_gettickcount], eax
.text:00401FB7 cmp [ebp+should_be_1], 0
.text:00401FBC jz short loc_401FD1
.text:00401FBE cmp [ebp+should_be_0x11D6], 11D6h ; should_be_0x11D6 = 0x11D6 ?
.text:00401FC5 jnz short loc_401FD1 ; serial[5] = ((0x11D6 + 0xC0CA - 0xB0B0) ^ 0x2010) / 15
.text:00401FC5 ; = 0x20 (SPACE character)
This algorithm is as follows:
(serial[5] * 15 ^ 0x2010) + 0xB0B0 - 0xC0CA = 0x11D6
We can reverse it to find the value of serial[5]:
serial[5] = ((0x11D6 + 0xC0CA - 0xB0B0) ^ 0x2010) / 15 = 0x20
As we know that 0x20 is the space character, we conclude that the serial should have 15 spaces, located at offsets pointed to by the array.
At thi stage, the serial should look like this:
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 ┌─┬─┬─┬─┬─┐ ┌─┬─┬─┬─┬─┐ ┌─┬─┬─┐ ┌─┬─┐ ┌─┬─┬─┬─┬─┬─┐ ┌─┬─┬─┬─┬─┬─┬─┬─┐ ┌─┬─┬─┬─┐ ┌─┬─┐ ┌─┬─┐ ┌─┬─┬─┬─┬─┬─┬─┐ ┌─┬─┬─┬─┬─┬─┬─┬─┬─┬─┐ ┌─┬─┬─┬─┬─┐ ┌─┬─┬─┬─┬─┐ ┌─┬─┬─┐ ┌─┬─┬─┬─┬─┐ ┌─┬─┬─┬─┬─┬─┬─┐ │?│?│?│?│?│ │?│?│?│?│?│ │?│?│?│ │?│?│ │?│?│?│?│?│?│ │?│?│?│?│?│?│?│?│ │?│?│?│?│ │?│?│ │?│?│ │?│?│?│?│?│?│?│ │?│?│?│?│?│?│?│?│?│?│ │?│?│?│?│?│ │?│?│?│?│?│ │?│?│?│ │?│?│?│?│?│ │?│?│?│?│?│?│?│ └─┴─┴─┴─┴─┘ └─┴─┴─┴─┴─┘ └─┴─┴─┘ └─┴─┘ └─┴─┴─┴─┴─┴─┘ └─┴─┴─┴─┴─┴─┴─┴─┘ └─┴─┴─┴─┘ └─┴─┘ └─┴─┘ └─┴─┴─┴─┴─┴─┴─┘ └─┴─┴─┴─┴─┴─┴─┴─┴─┴─┘ └─┴─┴─┴─┴─┘ └─┴─┴─┴─┴─┘ └─┴─┴─┘ └─┴─┴─┴─┴─┘ └─┴─┴─┴─┴─┴─┴─┘
Notice that we also have in this function. The 1st one checks whether PEB.BeingDebugged is set:
.text:00401EF5 mov eax, large fs:30h ; PEB
.text:00401EFC db 3Eh
.text:00401EFC mov eax, [eax+2] ; PEB.BeingDebugged
.text:00401F00 test al, al ; \ check whether prgm
.text:00401F02 jz short loc_401F0B ; / being debugged
.text:00401F02 ; [PATCH] NOP
And the 2nd one one is based on GetTickCount:
.text:00401E83 call GetTickCount
.text:00401E88 mov [ebp+gettickcount], eax
;...[SNIP]...
.text:00401FAC call GetTickCount
.text:00401FB1 sub eax, [ebp+gettickcount]
.text:00401FB4 mov [ebp+tickcount], eax
;...[SNIP]...
.text:00401FC7 cmp [ebp+tickcount], 20h ; \ check whether prgm
.text:00401FCB jnb short loc_401FD1 ; / being debugged
.text:00401FCB ; [PATCH] NOP
f_serial_nonspace (sub_401FDC)
This function checks the remaining characters of the serial (the ones that are not spaces).
It starts by counting how many non-space characters are present in the serial provided in the notepad window.
.text:00401FE7 mov [ebp+gettickcount], eax
.text:00401FEA push offset serial ; src
.text:00401FEF lea eax, [ebp+dest] ; dest = serial
.text:00401FF2 push eax ; dest
.text:00401FF3 call _strcpy
.text:00401FF8 add esp, 8
.text:00401FFB xor edx, edx
.text:00401FFD mov [ebp+counter_j], edx ; j = 0
.text:00402000 xor ecx, ecx
.text:00402002 mov [ebp+counter_k], ecx ; k = 0
.text:00402005
.text:00402005 loc_402005:
.text:00402005 cmp [ebp+counter_j], 5 ;
.text:00402005 ; skip space at byte #5
.text:00402009 jz short loc_402071
.text:0040200B cmp [ebp+counter_j], 0Bh ; skip space at byte #11
.text:0040200F jz short loc_402071
.text:00402011 cmp [ebp+counter_j], 0Fh ; skip space at byte #15
.text:00402015 jz short loc_402071
.text:00402017 cmp [ebp+counter_j], 12h
.text:0040201B jz short loc_402071
.text:0040201D cmp [ebp+counter_j], 19h
.text:00402021 jz short loc_402071
.text:00402023 cmp [ebp+counter_j], 22h
.text:00402027 jz short loc_402071
.text:00402029 cmp [ebp+counter_j], 27h
.text:0040202D jz short loc_402071
.text:0040202F cmp [ebp+counter_j], 2Ah
.text:00402033 jz short loc_402071
.text:00402035 cmp [ebp+counter_j], 2Dh
.text:00402039 jz short loc_402071
.text:0040203B cmp [ebp+counter_j], 35h
.text:0040203F jz short loc_402071
.text:00402041 cmp [ebp+counter_j], 40h
.text:00402045 jz short loc_402071
.text:00402047 cmp [ebp+counter_j], 46h
.text:0040204B jz short loc_402071
.text:0040204D cmp [ebp+counter_j], 4Ch
.text:00402051 jz short loc_402071
.text:00402053 cmp [ebp+counter_j], 50h
.text:00402057 jz short loc_402071
.text:00402059 cmp [ebp+counter_j], 56h
.text:0040205D jz short loc_402071
.text:0040205F mov eax, [ebp+counter_j] ; if char is not a SPACE
.text:00402062 mov dl, [ebp+eax+dest]
.text:00402066 mov ecx, [ebp+counter_k]
.text:00402069 mov serial_chars[ecx], dl ; if char not space serial_char[i] = serial[i]
.text:00402069 ; serial_char at 0x428B84
.text:0040206F jmp short loc_402074
.text:00402071 ; ---------------------------------------------------------------------------
.text:00402071
.text:00402071 loc_402071:
.text:00402071 dec [ebp+counter_k] ; if char = SPACE
.text:00402074
.text:00402074 loc_402074:
.text:00402074 inc [ebp+counter_j]
.text:00402077 inc [ebp+counter_k]
.text:0040207A cmp [ebp+counter_k], 4Fh ; \ while (k < 79)
.text:0040207E jl short loc_402005 ; /
All characters of the serial entered in the notepad window are XOR'ed with 0xF:
.text:0040209B popa
.text:0040209C xor eax, eax
.text:0040209E mov [ebp+counter_i], eax ; i = 0
.text:004020A1 loc_4020A1:
.text:004020A1 mov edx, [ebp+counter_i]
.text:004020A4 xor serial_chars[edx], 0Fh ; serial_chars[i] ^= 0xF
.text:004020A4 ; serial_chars at 0x428B84
.text:004020AB inc [ebp+counter_i] ; i++
.text:004020AE cmp [ebp+counter_i], 4Fh ; while(i<79)
.text:004020B2 jl short loc_4020A1
Notice that there are 2 anti-debug tricks in this function. The 1st one is based on PEB.ProcessHead.ForceFlags:
.text:00402081 mov eax, large fs:30h ; eax = PEB
.text:00402088 db 3Eh
.text:00402088 mov eax, [eax+18h] ; eax = PEB.ProcessHeap
.text:0040208C db 3Eh
.text:0040208C mov eax, [eax+10h] ; ForceFlags (Windows XP only)
.text:00402090 test eax, eax ; Check whether ForceFlags field (offset 0x10 relative to ProcessHeap) is 0
.text:00402092 jz short loc_40209B ; [PATCH] NOP
The 2nd one is base on GetTickCount:
.text:00401FE2 call GetTickCount
.text:00401FE7 mov [ebp+gettickcount], eax
; ...[SNIP]...
.text:004020B4 call GetTickCount
.text:004020B9 sub eax, [ebp+gettickcount]
.text:004020BC mov [ebp+tickcount], eax
.text:004020BF cmp [ebp+tickcount], 20h
.text:004020C3 jbe short loc_4020CD ; [PATCH] patch with unconditional jump (EB 08)
.text:004020C5 push 0
.text:004020C7 call f_terminate
f_validate_serial_chars (sub_40235C)
This function checks whether the serial is valid. It compares the encrypted characters of the serial (see f_serial_nonspace (sub_401FDC)) with bytes saved at memory location 0x4242E8.
.text:00402380 mov [ebp+counter_i], eax ; i = 0
.text:00402383 jmp short loc_402388
.text:00402385 ; ---------------------------------------------------------------------------
.text:00402385
.text:00402385 loc_402385:
.text:00402385 inc [ebp+counter_i]
.text:00402388
.text:00402388 loc_402388:
.text:00402388 mov edx, [ebp+counter_i]
.text:0040238B mov cl, serial_chars[edx] ; cl = serial_chars[i]
.text:00402391 mov eax, [ebp+counter_i]
.text:00402394 cmp cl, [ebp+eax+encrypted_serial] ; serial_chars[i] = encrypted_serial[i]?
.text:00402398 jz short loc_402385
; ...[SNIP]...
.data:004242E8 encrypted_serial db 41h, 7Ah, 61h, 6Ch, 2 dup(6Eh), 61h, 6Bh, 6Ah, 7Ch
.data:004242E8 db 7Fh, 60h, 7Dh, 6Ah, 63h, 6Ch, 6Eh, 62h, 66h, 61h, 60h
.data:004242E8 db 7Bh, 7Dh, 6Eh, 75h, 6Eh, 6Bh, 60h, 23h, 7Fh, 7Ah, 6Ah
.data:004242E8 db 7Ch, 0E6h, 63h, 7Bh, 6Ah, 6Ch, 60h, 61h, 6Bh, 7Ah, 6Ch
.data:004242E8 db 6Ah, 0F5h, 61h, 66h, 6Ch, 6Eh, 62h, 6Ah, 61h, 7Bh, 6Ah
.data:004242E8 db 67h, 6Eh, 6Ch, 66h, 6Eh, 6Bh, 60h, 61h, 6Bh, 6Ah, 63h
.data:004242E8 db 60h, 7Ch, 60h, 7Bh, 7Dh, 60h, 7Ch, 69h, 7Ah, 6Ah, 7Dh
.data:004242E8 db 60h, 61h, 21h
Here again, there is an anti-debug trick:
.text:00402376 call GetTickCount
.text:0040237B mov [ebp+gettickcount], eax
; ...[SNIP]...
.text:0040239A call GetTickCount
.text:0040239F sub eax, [ebp+gettickcount]
.text:004023A2 mov [ebp+tickcount], eax
.text:004023A5 cmp [ebp+counter_i], 4Fh ; i = 79?
.text:004023A9 jnz short loc_4023B5
As we know the expected bytes as well as the algorithm (XOR with 0xF), it's easy to find the expected password (see script).
Solution
Title
As seen previously, the notepad window should be saved with the following name:
WindowsUserName - day-month-(year+15)
Example:
Crack Me - 7-2-2031
Serial
The serial can be found with the following python script:
#!/usr/bin/env python
serial_spaces = [0x05, 0x0B, 0x0F, 0x12, 0x19, 0x22, 0x27, 0x2A,
0x2D, 0x35, 0x40, 0x46, 0x4C, 0x50, 0x56]
encrypted_serial = [0x41, 0x7A, 0x61, 0x6C, 0x6E, 0x6E, 0x61, 0x6B,
0x6A, 0x7C, 0x7F, 0x60, 0x7D, 0x6A, 0x63, 0x6C,
0x6E, 0x62, 0x66, 0x61, 0x60, 0x7B, 0x7D, 0x6E,
0x75, 0x6E, 0x6B, 0x60, 0x23, 0x7F, 0x7A, 0x6A,
0x7C, 0xE6, 0x63, 0x7B, 0x6A, 0x6C, 0x60, 0x61,
0x6B, 0x7A, 0x6C, 0x6A, 0xF5, 0x61, 0x66, 0x6C,
0x6E, 0x62, 0x6A, 0x61, 0x7B, 0x6A, 0x67, 0x6E,
0x6C, 0x66, 0x6E, 0x6B, 0x60, 0x61, 0x6B, 0x6A,
0x63, 0x60, 0x7C, 0x60, 0x7B, 0x7D, 0x60, 0x7C,
0x69, 0x7A, 0x6A, 0x7D, 0x60, 0x61, 0x21]
secret = []
c = 0
for i in encrypted_serial:
if c in serial_spaces:
secret.append(0x20)
c += 1
secret.append(i ^ 0xF)
c += 1
print ''.join([chr(i) for i in secret])
It provides us with the following output:
Nunca andes por el camino trazado, pues él te conduce únicamente hacia donde los otros fueron.
As soon as this serial has been entered, the content is changed to:
Comments
Keywords: crackme assembly reverse-engineering Thunder_cls Sticky