Date: 			April 27th, 2004
Target: 		EasyCrack.exe by Kwazy Webbit
Objective: 		Keygen the target
Solution by:		br00t_4_c (brt4c@yahoo.com)
Tools Used:		Ollydbg v1.10, Intel Opcodes Reference (from the MASM32 help files), MASM32 v8 (to code the keygen)
Shouts:			Thanx goes to Reteam.org and Kwazy Webbit for giving me something to do :)

OK, this app is short and sweet so instead of writing a proper tutorial, I'm just going to point 
out the relevant sections of the source code... Please excuse any blatantly stupid errors as I am
a total n00b (and probably more ignorant and stubborn than most -heh) This dissasembly is taken from Ollydbg...


***************************************************************************************************************************

;mmmkay, this is probably the most important section of the program... this is the routine that is called
;when you press the button to check the serial number...

00401000  PUSH EBP
00401001  MOV EBP,ESP
00401003  PUSH ECX
00401004  PUSH ECX
00401005  AND DWORD PTR SS:[EBP-4],0               	;  var1 = 0 -> this is the stack location where we stow our checksum
00401009  PUSH ESI
0040100A  MOV ESI,DWORD PTR SS:[EBP+8]			;  remember that memory locations above ebp contain function params (in this case our user name)
0040100D  PUSH EDI					
0040100E  PUSH ESI                                 	; /String (user name)
0040100F  CALL DWORD PTR DS:[<&KERNEL32.lstrlenA>] 	; \lstrlenA
00401015  MOV EDI,EAX					;  eax contains the length of user name, move into edi
00401017  XOR EDX,EDX					;  zero edx this will be used as a loop counter shortly
00401019  TEST EDI,EDI
0040101B  JLE SHORT EasyCrac.00401047              	;  length check for user name

;!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!

;OK this is the loop where we will hash our user name into a "checksum"...

0040101D  MOVSX ECX,BYTE PTR DS:[EDX+ESI]         	;  var2 = hex value of letter from user name
00401021  ADD DWORD PTR SS:[EBP-4],ECX            	;  var1 = var1 + var2
00401024  MOV DWORD PTR SS:[EBP-8],ECX            	;  var3 = var2
00401027  ROL DWORD PTR SS:[EBP-4],1              	;  var1 = var1 ROL 1
0040102A  MOV EAX,ECX                            	;  var4 = var2
0040102C  IMUL EAX,DWORD PTR SS:[EBP-4]           	;  var4 = var4 IMUL var1
00401030  MOV DWORD PTR SS:[EBP-4],EAX            	;  var1 = var4
00401033  MOV EAX,DWORD PTR SS:[EBP-8]            	;  var4 = var3
00401036  ADD DWORD PTR SS:[EBP-4],EAX            	;  var1 = var1 + var4
00401039  XOR DWORD PTR SS:[EBP-4],ECX            	;  var1 = var1 XOR var2
0040103C  INC EDX
0040103D  CMP EDX,EDI					;  is loop counter equal to the length of user name?
0040103F  JL SHORT EasyCrac.0040101D              	;  if loop counter < length of user name, do another loop

;!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!

00401041  CMP DWORD PTR SS:[EBP-4],0		  	;  once we finish the loop above the stack location ebp-4 (i call it var1) contains our checksum
00401045  JNZ SHORT EasyCrac.00401063              	;  jump is almost always taken unless a zero checksum is generated
00401047  PUSH 0                                   	; /Style = MB_OK|MB_APPLMODAL
00401049  PUSH EasyCrac.0040230C                   	; |Title = "A problem has occurred"
0040104E  PUSH EasyCrac.004022CC                   	; |Text = "There is no valid key for this name, please use another one."
00401053  PUSH DWORD PTR DS:[4023AC]               	; |hOwner = NULL
00401059  CALL DWORD PTR DS:[<&USER32.MessageBoxA>>	; \MessageBoxA
0040105F  XOR EAX,EAX					;  zero eax
00401061  JMP SHORT EasyCrac.0040107F

;!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
;OK now this section is interesting, before we proceed, let's remember that stack locations higher than EBP generally
;contain function parameters. So when we see [EBP+C] referenced repeatedly we know we're probably dealing with a parameter
;to this function that was passed to us by the main routine... so what exactly is in [EBP+C]? Well if you look further
;down in the listing you will see that [EBP+C] is actually the the integer value of the serial number entered by the user.
;This value is obtained by a call to the GetDlgItemInt api function. Another interesting thing about this section of the
;code is that it is setting us up for success or failure later on. The great beauty of this particular protection is that
;they are not so lazy as to compare two strings (i.e. a correct serial and a user-entered serial). What they do instead
;is take the checksum generated from the user name in the loop above and compare that to the difference of the integer 
;value of the user entered serial XORed with the value 0x1337CODE (heh) and the value 0xBADC0DE5 (heh again). Now depending
;on whether or not the user entered serial is correct, the zero flag will be set (or not) on return from this function.
;A little more sophisticated than the usual cmp, jnz fuckery :) By the way, this might be a good time to refer to your opcode
;reference as there are a few important subtleties (i.e. the effects various asm instructions have on flags) that you need
;to be aware of in order to reverse this protection...

;SUB 			(Subtract)
;Usage: 		SUB dest,src
;Modifies flags: 	AF CF OF PF SF ZF
;The source is subtracted from the destination and the result is stored in the destination.

;SBB 			(Subtract with Borrow/Carry)
;Usage:  		SBB dest,src
;Modifies flags: 	AF CF OF PF SF ZF
;Subtracts the source from the destination, and subtracts 1 extra if the Carry Flag is set. Results are returned in "dest".

;NEG 			(Two's Complement Negation)
;Usage:  		NEG dest
;Modifies flags: 	AF CF OF PF SF ZF
;Subtracts the destination from 0 and saves the 2s complement of "dest" back into "dest".


00401063  XOR DWORD PTR SS:[EBP+C],1337C0DE		;  var5 = var5 XOR 0x1337CODE
0040106A  SUB DWORD PTR SS:[EBP+C],BADC0DE5        	;  var5 = var5 SUB 0xBADC0DE5 --> this instruction sets the carry flag if the XORed value in [EBP+C] is <= 0xBADCODE5 and will affect the SBB instruction later
00401071  MOV EAX,DWORD PTR SS:[EBP-4]             	;  var4 = var1
00401074  NOT DWORD PTR SS:[EBP+C]                 	;  var5 = NOT var5
00401077  XOR EAX,DWORD PTR SS:[EBP+C]             	;  var4 = var4 XOR var5
0040107A  NEG EAX                                  	;  var4 = NEG var4 --> note at this point var4 must be 0 in order for the carry flag not to be set
0040107C  SBB EAX,EAX                              	;  var4 = SBB var4, var4
0040107E  INC EAX                                  	;  this sets the zero flag if the sbb instruction made var4 = FFFFFFFF
0040107F  POP EDI
00401080  POP ESI
00401081  LEAVE
00401082  RETN

;OK so in essence what we need here to reverse this algorithm:

;a) take an aribitrary user name
;b) rip the asm in the loop above, use this to create a hash or checksum of the username
;c) a bit of math tells us that: 

;	<checksum from user name> = NOT(<good serial in hex> XOR 0x1337C0DE) - 0xBADC0DE5

;       therefore...

;	<good serial in hex> = (NOT(<checksum from user name> + 0xBADC0DE5)) XOR 0x1337C0DE5

;d) then all we need to do is convert <good serial in hex> to a decimal and we have the correct serial number :)

;!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!

***************************************************************************************************************************

;!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
;everything in this section is pretty much irrelevant, just a couple of length checks and switch statements
to decide which (progressively worse -nice touch Kwazy Webbit :p) diss you will receive if you do not get the correct serial number...

00401083  PUSH EBP
00401084  MOV EBP,ESP
00401086  SUB ESP,34
00401089  MOV EAX,DWORD PTR SS:[EBP+C]
0040108C  SUB EAX,10                               	;  Switch (cases 10..111)
0040108F  PUSH ESI
00401090  PUSH EDI
00401091  JE SHORT EasyCrac.004010BE
00401093  SUB EAX,100
00401098  JE EasyCrac.00401175
0040109E  DEC EAX
0040109F  JE SHORT EasyCrac.004010A5
004010A1  XOR EAX,EAX                              	;  Default case of switch 004010B4
004010A3  JMP SHORT EasyCrac.004010CC
004010A5  MOV EAX,DWORD PTR SS:[EBP+10]            	;  Case 111 (WM_COMMAND) of switch 0040108C
004010A8  SHR EAX,10
004010AB  TEST AX,AX
004010AE  JNZ SHORT EasyCrac.004010C9
004010B0  MOVZX EAX,WORD PTR SS:[EBP+10]
004010B4  SUB EAX,3EA                              	;  Switch (cases 3EA..3EB)
004010B9  JE SHORT EasyCrac.004010D2
004010BB  DEC EAX
004010BC  JNZ SHORT EasyCrac.004010A1
004010BE  PUSH 0 	                                ; /Result = 0; Case 3EB of switch 004010B4
004010C0  PUSH DWORD PTR SS:[EBP+8]                	; |hWnd
004010C3  CALL DWORD PTR DS:[<&USER32.EndDialog>]  	; \EndDialog
004010C9  XOR EAX,EAX
004010CB  INC EAX
004010CC  POP EDI
004010CD  POP ESI
004010CE  LEAVE
004010CF  RETN 10
004010D2  MOV EDI,DWORD PTR SS:[EBP+8]             	;  Case 3EA of switch 004010B4
004010D5  PUSH 32                                  	; /Count = 32 (50.)
004010D7  LEA EAX,DWORD PTR SS:[EBP-34]            	; |
004010DA  PUSH EAX                                 	; |Buffer
004010DB  PUSH 3E8                                 	; |ControlID = 3E8 (1000.)
004010E0  PUSH EDI                                 	; |hWnd
004010E1  CALL DWORD PTR DS:[<&USER32.GetDlgItemTe>	; \GetDlgItemTextA
004010E7  TEST EAX,EAX
004010E9  JNZ SHORT EasyCrac.004010F8
004010EB  PUSH EAX
004010EC  PUSH EasyCrac.004023A4                   	;  ASCII "PFFT."
004010F1  PUSH EasyCrac.00402384                   	;  ASCII "You might want to enter a name?"
004010F6  JMP SHORT EasyCrac.00401117

;!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
;ok this is the important section where we set up and make the call to the serial generation/checking routine...

004010F8  XOR ESI,ESI
004010FA  PUSH ESI                                 	; /IsSigned => FALSE
004010FB  PUSH ESI                                 	; |pSuccess => NULL
004010FC  PUSH 3E9                                 	; |ControlID = 3E9 (1001.)
00401101  PUSH EDI                                 	; |hWnd
00401102  CALL DWORD PTR DS:[<&USER32.GetDlgItemIn>	; \GetDlgItemInt  -> this call finds the integer value of the serial entered by the user remember we use this later on...
00401108  CMP EAX,ESI
0040110A  JNZ SHORT EasyCrac.00401120			;  this jump gets executed so long as we enter a valid numeric serial

;!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
;just an insult for those who dare to enter an improperly formatted serial, skip to the next section...

0040110C  PUSH ESI
0040110D  PUSH EasyCrac.004023A4                   	;  ASCII "PFFT."
00401112  PUSH EasyCrac.00402340                   	;  ASCII "Thats not a proper serial.. Use a normal number between 1 and 2^32"
00401117  PUSH EDI                                 	; |hOwner
00401118  CALL DWORD PTR DS:[<&USER32.MessageBoxA>>	; \MessageBoxA
0040111E  JMP SHORT EasyCrac.004010C9

;!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
;ok so here we start pushing the parmas for our serial generation/checking routine and actually make the call...
;our main check of the validity of our serial number occurs at offset 0040112F...
;OK now it's crucial to remember that the jump condition for a JE is if zero flag is set... 
;so in order to reach our "Good Cracker" message and be successful the zero flag must NOT be set following the return 
from our serial generation/checking routine. So as long as we have generated a serial that has us coming out of that
function with the zero flag unset we're golden :)

00401120  PUSH EAX                                 	;  this is the result of the GetDlgItemInt call
00401121  LEA EAX,DWORD PTR SS:[EBP-34]
00401124  PUSH EAX
00401125  CALL EasyCrac.00401000		   	;  mmmkay, here's our call to our serial checking routine
0040112A  TEST EAX,EAX
0040112C  POP ECX
0040112D  POP ECX
0040112E  PUSH ESI
0040112F  JE SHORT EasyCrac.0040113D			;  <---- HERE'S THE BIG TEST!!!!!
00401131  PUSH EasyCrac.00402338                   	;  ASCII "RIGHT"
00401136  PUSH EasyCrac.0040232C                   	;  ASCII "You got it!"
0040113B  JMP SHORT EasyCrac.00401117
0040113D  MOV EAX,DWORD PTR DS:[4023B4]            	; |
00401142  PUSH EasyCrac.00402324                   	; |Title = "WRONG"
00401147  PUSH 0B                                  	; |
00401149  POP ECX                                  	; |
0040114A  CDQ                                      	; |
0040114B  IDIV ECX                                 	; |
0040114D  PUSH DWORD PTR DS:[EDX*4+402028]         	; |Text
00401154  PUSH EDI                                 	; |hOwner
00401155  CALL DWORD PTR DS:[<&USER32.MessageBoxA>]	; \MessageBoxA
0040115B  INC DWORD PTR DS:[4023B4]
00401161  CMP DWORD PTR DS:[4023B4],0B
00401168  JNZ EasyCrac.004010C9
0040116E  PUSH ESI
0040116F  PUSH EDI
00401170  JMP EasyCrac.004010C3
00401175  MOV EAX,DWORD PTR SS:[EBP+8]             ;  Case 110 (WM_INITDIALOG) of switch 0040108C
00401178  MOV DWORD PTR DS:[4023AC],EAX
0040117D  JMP EasyCrac.004010C9
00401182  PUSH 0                                   ; /lParam = NULL
00401184  PUSH EasyCrac.00401083                   ; |DlgProc = EasyCrac.00401083
00401189  PUSH 0                                   ; |hOwner = NULL
0040118B  PUSH 67                                  ; |pTemplate = 67
0040118D  PUSH 0                                   ; |/pModule = NULL
0040118F  CALL DWORD PTR DS:[<&KERNEL32.GetModuleH>; |\GetModuleHandleA
00401195  PUSH EAX                                 ; |hInst
00401196  CALL DWORD PTR DS:[<&USER32.DialogBoxPar>; \DialogBoxParamA
0040119C  PUSH 0                                   ; /ExitCode = 0
0040119E  CALL DWORD PTR DS:[<&KERNEL32.ExitProces>; \ExitProcess


That's it dudes and dudettes, to see how the keygen algorithm is actually implemented, just check out the source for the
keygen. If you have any questions, or you would like to flame me give me a shout at brt4c@yahoo.com

PEACE :)


