Seems like we are stalled so I decided to do a little sum up trying to make things a little bit clear.
Unpack
------
To unpack the crackme follows the post by Bilbo
Antidebug(s)
------------
Trying to run the program from Ollydbg you'll surely find out that something is wrong because the application crashes.
Looking at the string references produced by Ida we know that IsDebuggerPresent is called:
Code:
CODE:004141BA 014 push offset str->Isdebuggerpresent ; "IsDebuggerPresent"
CODE:004141BF 018 push edi
CODE:004141C0 01C call GetProcAddress_0 ; Get the address of IsDebuggerPresent
CODE:004141C5 01C mov ebp, eax
CODE:004141C7 01C mov esi, ebp
CODE:004141C9 01C test ebp, ebp
CODE:004141CB 01C jz short loc_4141D1
CODE:004141CD 01C call esi ; Call IsDebuggerPresent. Returns: al = 1 if under debugger, 0 otherwise
...
CODE:004151E1 024 test al, al ; al = value returned by IsDebuggerPresent function
CODE:004151E3 024 jz short loc_4151EF ;
CODE:004151E5 024 push 3039h ; Here if you are under the debugger
CODE:004151EA 028 call FatalExit ; Close everything
CODE:004151EF 024 push offset OutputString ; "s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s"...
CODE:004151F4 028 call OutputDebugStringA
As you can it uses a simple trick, the good old IsDebuggerPresent function. This is not the only trick used by the crackme infact if you try to fool IsDebuggerPresent trick (patching it using an Olly plugin or simply invert the conditional jump at 4151E3) you'll find out the crackme still crashes. Does OutputDebugStringA tell you nothing? Yes, it's a well known Ollydbg vulnerability, I don't remember how it really works but a simple google search (like: ollydbg+OutputDebugString) will do the job. To avoid this trick it's easy and there are lot of ways, I changed the conditional jump at 4151E3.
Run the crackme again hoping to see the dialog but unfortunatly the crackme has another trick:
Code:
CODE:00415A1F xor eax, eax
CODE:00415A21 push ebp
CODE:00415A22 push offset loc_415A4E
CODE:00415A27 push dword ptr fs:[eax]
CODE:00415A2A mov fs:[eax], esp
CODE:00415A2D jmp short near ptr unk_415A3E -------------------
CODE:00415A2F str->Int3Power db 'int 3 power =)',0 |
CODE:00415A3E unk_415A3E db 0CCh ; CODE XREF: CODE:00415A2Dj <--
CODE:00415A3F db 0CCh <------ the debugger stops here
CODE:00415A40 db 0CCh
CODE:00415A41 db 0CCh
CODE:00415A42 db 0CCh
CODE:00415A43 db 0CCh
CODE:00415A44 xor eax, eax
CODE:00415A46 pop edx
CODE:00415A47 pop ecx
CODE:00415A48 pop ecx
CODE:00415A49 mov fs:[eax], edx
CODE:00415A4C jmp short loc_415A5F
...
CODE:00415A5F push offset unk_417928
CODE:00415A64 call TranslateMessage
CODE:00415A69 push offset unk_417928
CODE:00415A6E call DispatchMessageA
CODE:00415A73 push 0
CODE:00415A75 push 0
CODE:00415A77 push 0
CODE:00415A79 push offset unk_417928
CODE:00415A7E call GetMessageA
CODE:00415A83 test eax, eax
CODE:00415A85 jnz short loc_415A1F
It's easy to find out that this part of code is here for one reason only... stop our stepping session.
Ok, we have another trick to find out; I did find it in a second time looking at the disasm. The code is between address 415200 and 415316. I don't post the entire code but mainly it's the usual code used to retrieve if a particular process is running inside our system. The list of the processes is taken with the CreateToolhelp32Snapshot/Module32First/... functions and the check is made over Ollydbg process. The crackme checks for: "OllyDbg.EXE", "OllyDbg.exe", "ollydbg.EXE", "Ollydbg.EXE", "ollydbg.exe". Now you should know why I didn't notice this trick before, I named the file "OLLYDBG.EXE"
Ollydbg is nowadays almost the most used debugger by the masses, lot of programmers put all this tricks inside their applications/crackmes but it's really funny to see how softice won't get fouled by all these tricks
Ok, we can know start with the first level.
First level
-----------
From what I've read Fake51 did find the routine codes passing through the dialog proc while sigint33 used a breakpoint over GetWindowTextA (I really don't know how you did it indeed...). I'll use the usual approach looking at the string references. From all the visible strings this one seems to be what we are looking for: "Congratz! U improve ur newbie-level. Try next one..":
Code:
seg000:00414C1C mov edx, [ebp+var_14]
seg000:00414C1F mov eax, ds:dword_417958
seg000:00414C24 call @System@@LStrCmp$qqrv ; It's pretty obvious that our serial is compared with the right serial
seg000:00414C29 jnz short loc_414C70
seg000:00414C2B push 30h
seg000:00414C2D push offset aHNewbie ; "H! newbie"
seg000:00414C32 push offset aCongratzUImpro ; "Congratz!\rU improve ur newbie-level. Tr"...
Now that we know where the check is placed we can use our debugger and put a breakpoint directly before the LStrCmp call. You can sniff the right serial or you can study the algo. Do you remember what Fake51 said?:
Quote:
First serial routine is simple: First, sum the hex values of every byte of the name (including spaces). Multiply that sum by 7d5h. Then xor it with 10h. Convert the sum to decimal and ascii. Then, grab the first three chars of the name. Append to this the converted sum reached before. Then append the name entered. Presto, serial done. |
Let's go to see the instructions used to perform such an algo:
Code:
CODE:00414B71 0A4 mov edx, ds:dword_417954 <-- edx points to the name
CODE:00414B77 0A4 movzx edx, byte ptr [edx+esi-1] <-- take a char from a name
CODE:00414B7C 0A4 add ebx, edx <-- "sum the hex values of every byte of the name"
CODE:00414B7E 0A4 inc esi
CODE:00414B7F 0A4 dec eax
CODE:00414B80 0A4 jnz short loc_414B71
...
CODE:00414BE0 0A4 mov cl, 3
CODE:00414BE2 0A4 call @@PStrNCat <-- "grab the first three chars of the name"
...
CODE:00414BF8 0A8 imul eax, ebx, 7D5h <-- "Multiply that sum by 7d5h"
CODE:00414BFE 0A8 xor eax, 10h <-- "Then xor it with 10h"
CODE:00414C01 0A8 call sub_406BFC <-- " Convert the sum to decimal and ascii"
...
CODE:00414C17 0B0 call sub_403E9C <-- "Append to this (first 3 chars of the name) the converted sum reached before. Then append the name entered"
Easy

A little thing before level_2.
This part of the crackme contains a breakpoint check, it checks for bpx over MessageBox and GetDlgItemText function. The check is a standard check over the first byte of the function. Let's an example for one of the function:
Code:
CODE:00414A0B 094 push offset str->Messageboxa <-- "MessageBoxA"
CODE:00414A10 098 push offset LibFileName <-- "user32.dll"
CODE:00414A15 09C call LoadLibraryA
CODE:00414A1A 098 push eax
CODE:00414A1B 09C call GetProcAddress_0
CODE:00414A20 09C mov [ebp+var_4], eax <-- eax points to the first byte of MessageBoxA function
CODE:00414A23 09C mov eax, [ebp+var_4]
CODE:00414A26 09C mov ebx, [eax] <-- take the dword pointed by eax
CODE:00414A28 09C cmp bl, 0CCh <-- compare the value of the first byte with 0xCC
CODE:00414A2B 09C jz short loc_414A37 <-- it means: Do you put a 'bpx MessageBoxA'?
CODE:00414A2D 09C add eax, 7 <-- eax points to the second instruction of the function
CODE:00414A30 09C mov ebx, [eax] <-- take the dword pointed by eax
CODE:00414A32 09C cmp bl, 0CCh <-- Do you put a breakpoint over the second instruction?
CODE:00414A35 09C jnz short loc_414A5C
CODE:00414A37 09C push 29Ah
CODE:00414A3C 0A0 call FatalExit <-- Exit if there is a breakpoint
CODE:00414A41 09C jmp short loc_414A5C
This is the classical check but as you can see there is another check for a second breakpoint. To fool -or to prevent- checks like the first one I used to put breakpoint directly inside the code of the function, not exactly on the first byte of it (To reach the function you can do a simple 'u MessageBoxA' under softice or a go-to 'User32.MessageBoxA' under Ollydbg). The aim of the second check used by the crackme is to block this kind of *smart* breakpoint.
Second level
------------
Even at this level you can sniff the serial -byte per byte this time- and you don't have to patch anything. My approach is always the same: "Congratz!\rU fully improve ur newbie-lev".
Code:
seg000:00414CC4 lea eax, [ebp+var_34]
seg000:00414CC7 push eax
seg000:00414CC8 mov ecx, 2 <-- a parameter for the next call
seg000:00414CCD mov edx, esi
seg000:00414CCF mov eax, ds:dword_417958 <-- our serial
seg000:00414CD4 call @System@@LStrCopy$qqrv <-- copy 2 chars from our serial into another buffer
seg000:00414CD9 mov eax, [ebp+var_34] <-- eax points to the 2 chars
seg000:00414CDC push eax
seg000:00414CDD mov eax, ds:dword_417954 <-- eax points to our name
seg000:00414CE2 movzx eax, byte ptr [eax+ebx-1] <-- takes a char from the name
seg000:00414CE7 lea ecx, [ebp+var_38]
seg000:00414CEA mov edx, 2
seg000:00414CEF call sub_406D10 <-- convert the char into two byte (i.e. 'Z'=0x5A becomes "5A"

seg000:00414CF4 mov edx, [ebp+var_38]
seg000:00414CF7 pop eax
seg000:00414CF8 call @System@@LStrCmp$qqrv <-- compare between the first two chars of the serial with the bytes
obtained by the name
seg000:00414CFD jnz loc_414F38 <-- they have to be equal
seg000:00414D03 add esi, 2
seg000:00414D06 inc ebx
seg000:00414D07 mov eax, ds:dword_417954
seg000:00414D0C call @System@_16823
seg000:00414D11 cmp ebx, eax
seg000:00414D13 jnz short loc_414CC4 <-- jump up and check the other bytes
seg000:00414D15 push 30h
seg000:00414D17 push offset aCoolNewbie ; "Cool, newbie"
seg000:00414D1C push offset aCongratzUFully ; "Congratz!\rU fully improve ur newbie-lev"...
Do you understand the algo? Pretty easy, even more than level 1. The serial is obtained by the ascii value of the name. sigint33, you were right saying it's possible to sniff the serial byte a byte

Nothing more to say.
Third level
-----------
New level and new string references: "Normal..NOT FOUND!" and "Normal..in progress"
The first string doesn't help me too much:
Code:
CODE:00414ED9 000 call @StrToInt
CODE:00414EDE 000 mov ds:dword_417944, eax <-- eax is an integer value. We don't know anything else at the moment
CODE:00414EE3 000 xor eax, eax
CODE:00414EE5 000 push ebp
CODE:00414EE6 004 push offset loc_414F02 <-- seh: handler exception
CODE:00414EEB 008 push dword ptr fs:[eax] <-- seh
CODE:00414EEE 00C mov fs:[eax], esp <-- seh
CODE:00414EF1 00C mov eax, ds:dword_417944
CODE:00414EF6 00C call eax <-- the integer should be an address of a function...
CODE:00414EF8 00C xor eax, eax
CODE:00414EFA 00C pop edx
CODE:00414EFB 008 pop ecx
CODE:00414EFC 004 pop ecx
CODE:00414EFD 000 mov fs:[eax], edx
CODE:00414F00 000 jmp short loc_414F38
CODE:00414F02 loc_414F02: ; DATA XREF: sub_4149D4+512o
CODE:00414F07 000 push 30h
CODE:00414F09 000 push offset str->Normal__notFound ; "Normal..NOT FOUND!"
CODE:00414F0E 004 push offset str->UNeedSomeBrainzOrLuck ; "U need some brainz or luck"
CODE:00414F13 008 mov eax, ds:hDlg
CODE:00414F18 008 push eax
CODE:00414F19 00C call [ebp+var_4]
CODE:00414F1C 00C call @@DoneExcept
CODE:00414F21 00C jmp short loc_414F38
The string is showed by a message box but unfortunatly Ida is not able to understand from where it's called. By the way, this piece of code it's not so friendly, the 'call @@DoneExcept' suggests me that seh is used and the instructions between 414ED9 and 414EF6 represent the proof; the crackme calls a function pointed by a value inside eax which is filled at runtime. The use of seh is necessary because from the call you can go everywhere

. The question is: where is the right function to call? And, how is the address abtained?
Let's see... the initial part of the routine (which starts at 414D42, after level_2 routine) checks the length of the serial -16 characters- and takes some chars from our fake serial. These chars are the ones in postion 1,3,5,7,9,11,13,15; they are putted all together into a string which is converted to integer (For example: serial = 1234567890abcdef --> eax=13579ACE).
The integer value represents the address to call, the address of a function inside the crackme. We don't know where the function is located at but we can locate it using the second string "Normal..in progress". Doing some backtrace from the second string we arrive at the beginning of an unreferenced function, which starts at 414514 (Inv, I think you were right
about the first part of the serial

). It's almost obvious that we surely reach this piece of code (otherwise how can we reach the "Normal..in progress" message?); now we know where we'll jump from the 'call eax' at 414EF6.
Ok, it's enough for the moment. Are you able to find out the second part of the serial?
Ciao,
ZaiRoN