Target:		Crackme #2.1
Author:		ReWolf
Code:		Assembly
Protection:	-Anti-Tools thread (WARNING: Windows XP users, see prelim before running!)
                -XOR encrypted routines and strings, 
                -Fake Keychecking algorithm
Cracked By:	Kreet
Tools Used:	PEiD, FSG 2.0 unpacker, IDA, HexWorkshop, XorCrypt 1.0

Preliminary:

I usually just assume that a crackme is safe to run, and usually they are... but in this case there a slight exception!  This crackme has an anti-tools routine that becomes particularly deadly at least on this Windows XP system, since one of the "tools" it searches for the presence of is psapi.dll, which on XP is unfortunately also used by WinLogon.exe.  The system just wont run without WinLogon.exe, so if you run this crackme on an XP machine, it will probably be hosed.  Whoops.

So first order of business will have to be disabling that protection, before we can even proceed analyzing the rest of the program.
First, let's pop it in PEiD, which says it is packed with FSG 2.0.  We'll just unpack that with FSG 2.0 Unpacker from ProTools.  Now we can pop it in IDA.
Do a quick check on the imports this program uses, yes it has a referece to TerminateProcess.  NOP (0x90) out the call to OpenProcess, TerminateProcess, and the parameters pushed on the stack for both those calls.  Good to go, it now plays nicely with the other processes on the system.

Analyzing the Program:

There are a couple of tricky things going on in this program.  If you were just to leap right into the program without looking, you might run into a few of ReWolf's traps.  Among them are:
   - you won't find any strings in the program, so no searching for "good cracker" or "bad cracker" messages.  This is because certain sections of the program are encrypted
   - he does not give feedback for bad serials
   - a fake keycheck algorithm!

Dealing with XOR encryption:
Around his Messagebox calls, and the KeyCheck routine, he has a wrapper function that temporarily decrypts the segment the data is in, pushes it, then encrypts it again right after to keep away prying eyes in a debugger, like this:
		push	6
		push	offset aString
		call	xor_crypt
		push	offset aString
...
		push	6              ;length
		push	offset aString
		call	xor_crypt
Inside xor_crypt, we see he is encrypting / decrypting everything with 0xDEADFACE ;)
If you want, you can decrypt the strings and code in the program and disable the crypto routine pretty simply.  I have a small utility I wrote a while back for fun which ended up being slightly useful.  Just find the offset you want to XOR in your disassembler, bring up Hex Workshop, copy the bytes (length = the int passed to xor_crypt*4) into XORCrypt, type in CEFAADDE as the XOR key (DEADFACE backwards!), hit "crypt", "copy" and then "paste special" back in Hex Workshop.  Then, overwrite DEADFACE in the program with 0 so it doesnt mangle up the stuff we already decrypted.

Fake KeyCheck Algorithm:

  If you check the Check button procedure (by tracing GetDlgItemTextA), you'll find this
		push	41h		; nMaxCount
		push	offset usrName	; lpString
		push	101		; nIDDlgItem
		push	[ebp+hWndParent] ; hDlg
		call	GetDlgItemTextA
		test	eax, eax
		jz	short get_out	; user must put	in a name (duh)
		push	41h		; nMaxCount
		push	offset usrSerial ; lpString
		push	102		; nIDDlgItem
		push	[ebp+hWndParent] ; hDlg
		call	GetDlgItemTextA
		cmp	eax, 64		; serial must be 64 chars long
		jnz	get_out
		mov	ds:inputFlag, 1
		push	offset serial_hash
		push	offset usrSerial
		call	strlen
		push	eax
		push	offset usrSerial
		call	__RIPEMD128	; _RIPEMD128()
		push	offset usrName
		call	strlen
		push	eax
		push	offset usrName
		push	offset name_hash
		call	__MD5HASH
		push	offset serial_hash
		push	offset name_hash
		call	compare_hashes	; Returns 1 if hashes are equal; 0 otherwise
		test	eax, eax

loc_402B55:	
		jz	get_out
...
below that, he has a Congratulations! message.  So it would appear, that he is comparing the RIPEMD-128 hash of your serial, to the MD5 hash of your name... and if they are not equal, you lose.  Somebody might have tried to jump the gun on this one and do a RIPEMD128 collision attack on the MD5 hash of the name, or just tried bruteforcing it... but, both those would take quite a while, and reversing it so far was too easy!  Turns out it's a fake.
At runtime, the conditional jump
		jz	get_out 
that would check the results of the compare_hashes function at runtime is rewritten to
		jmp	get_out

by the WM_INITDIALOG proc:
		mov	dword ptr ds:loc_402B55, 0FFFF1DE9h ; rewrite jz to jmp	get_out
		push	[ebp+hWndParent]
		pop	ds:hWnd		; give the threadproc a	handle to our window
		jmp	short get_out

So, if this code can never give a "Good cracker" message, where's the real one?

The Real Keychecking Algorithm

If you were to glance through the DlgProc, you would find nothing there.  The only other place it could be hiding is the ThreadProc, which you might assume was just for killing cracking tools (or you might have seen the other MessageBox calls in there :)
He communicates between the main thread and the AntiTools / Keychecking thread with a pair of byte flags:
4043D2 inputFlag
0 = input is not valid
1 = input is valid
and 40440B statusFlag
0 = thread has ended
1 = program starting up
2 = dialogbox has ended

statusFlag is used so the thread knows when to clean up and quit, and the program, in turn, knows when the thread is ready to die.  But inputFlag is the more important of the two here. It is set when the user input is confirmed to be present and of the proper length, then in the thread when this flag is set, a different branch is taken:
		cmp	ds:inputFlag, 1
		jnz	sleeeeeep
		push	offset namehash	; destination
		push	offset usrName
		call	strlen
		push	eax		; length
		push	offset usrName	; source
		call	__RIPEMD128@0	; _RIPEMD128()
		push	97
		push	offset KeyCheck
		call	xor_crypt
		call	KeyCheck
		push	97
		push	offset KeyCheck
		call	xor_crypt
		test	eax, eax
		jz	short badserial
Ah, here we are, we prep our KeyCheck algorithm with a RIPEMD128 hash of the name, decrypt it, call it, encrypt it and check the results left in eax.
So let's take a look at the decrypted copy then (finally!):

KeyCheck	proc near
		pusha
		xor	ecx, ecx

shr_loop:
		shr	byte ptr ds:namehash[ecx*2], 1
		inc	ecx
		cmp	ecx, 7
		jnz	short shr_loop
		xor	ecx, ecx

zero_loop:
		mov	byte ptr ds:hash_zeroes[ecx*2],	0
		inc	ecx
		cmp	ecx, 7
		jnz	short zero_loop	; divide even bytes by 2, 0 out	odd bytes

		mov	edi, offset usrSerial
		mov	cl, 17
		xor	ebx, ebx

subtract_loop:
		sub	ds:usrSerial[ebx], cl ;	subtracts 17-20	from usrSerial in a loop
		inc	cl
		cmp	cl, 21
		jz	short resetto17

continuelooping:			; CODE XREF: KeyCheck+40j
		inc	ebx
		cmp	ebx, 64
		jnz	short subtract_loop
		jmp	short convertbytes

resetto17:				; CODE XREF: KeyCheck+34j
		mov	cl, 17
		jmp	short continuelooping

convertbytes:
		xor	ebx, ebx
		inc	ebx
		mov	esi, offset usrSerial
		mov	edi, 404310h	; magic1
		call	MultAsc2Hex
		push	2
		pop	ebx
		mov	edi, 404318h	; magic2
		call	MultAsc2Hex
		push	3
		pop	ebx
		mov	edi, 404320h	; magic3
		call	MultAsc2Hex
		push	4
		pop	ebx
		mov	edi, 404328h	; magic4
		call	MultAsc2Hex
		push	4
		pop	ebx
		mov	edi, 404330h	; magic5
		call	MultAsc2Hex
		push	5
		pop	ebx
		mov	edi, 404338h	; magic6
		call	MultAsc2Hex
		push	6
		pop	ebx
		mov	edi, 404340h	; magic7
		call	MultAsc2Hex

		xor	ebx, ebx	; fpu loop loops 7 times, incrementing ebx by 2

fpu_loop:
		fild	word ptr ds:namehash[ebx] ; load byte from name	hash
		xor	ecx, ecx
		inc	ecx

exp_loop:
		fimul	word ptr ds:namehash[ebx]
		inc	ecx
		cmp	ecx, 7
		jnz	short exp_loop	; raise	it to the 7th power
		fild	word ptr ds:namehash[ebx] ; load it again in next register
		xor	ecx, ecx
		inc	ecx

exp_loop2:
		fimul	word ptr ds:namehash[ebx]
		inc	ecx
		cmp	ecx, 6
		jnz	short exp_loop2	; raise byte to 6th power
		fild	qword ptr ds:magic1
		fmulp	st(1), st	; multiply it by nh^6
		fsubp	st(1), st	; subtract nh^7	from that result
					; result1 = nh^7- magic1*nh^6
		fild	word ptr ds:namehash[ebx] ; load same byte again
		xor	ecx, ecx
		inc	ecx

exp_loop3:
		fimul	word ptr ds:namehash[ebx]
		inc	ecx
		cmp	ecx, 5
		jnz	short exp_loop3	; raise byte to 5th
		fild	qword ptr ds:magic2
		fmulp	st(1), st	; multiply magic2 * nh^5
		faddp	st(1), st	; add that result to result1
					; result2 = result1 + magic2 * nh^5
		fild	word ptr ds:namehash[ebx]
		xor	ecx, ecx
		inc	ecx

exp_loop4:
		fimul	word ptr ds:namehash[ebx]
		inc	ecx
		cmp	ecx, 4
		jnz	short exp_loop4	; raise	it to the 4th
		fild	qword ptr ds:magic3
		fmulp	st(1), st	; multiply it by nh^4
		fsubp	st(1), st	; result3 = result2 - magic3 * nh^4
		fild	word ptr ds:namehash[ebx]
		xor	ecx, ecx
		inc	ecx

exp_loop5:
		fimul	word ptr ds:namehash[ebx]
		inc	ecx
		cmp	ecx, 3
		jnz	short exp_loop5	; cube it
		fild	qword ptr ds:magic4 ; load magic4
		fmulp	st(1), st	; multiply it by nh^3
		faddp	st(1), st	; result4 = result3 + magic4*nh^3

		fild	word ptr ds:namehash[ebx]
		fimul	word ptr ds:namehash[ebx] ; square it
		fild	qword ptr ds:magic5
		fmulp	st(1), st	; multiply it by nh^2
		fsubp	st(1), st	; result5 = result4 - magic5 * nh^2
		fild	word ptr ds:namehash[ebx]
		fild	qword ptr ds:magic6
		fmulp	st(1), st
		faddp	st(1), st	; result6 = result5 + magic6 * nh
		fild	qword ptr ds:magic7
		fsubp	st(1), st	; result7 = result6 - magic7
		fistp	dword ptr ds:temp ; temporary var for comparing	contents of fpu	register
		cmp	dword ptr ds:temp, 0 ; check if the whole equation = 0
		jnz	short badkey
		add	ebx, 2
		cmp	ebx, 14
		jnz	fpu_loop
		popa
		xor	eax, eax
		inc	eax
		retn

badkey:
		popa
		xor	eax, eax
		retn
KeyCheck	endp

Okay!  So we now have our whole problem set right before us!
We have
x^7 - magic1*x^6 + magic2*x^5 - magic3*x^4 + magic4*x^3 - magic5*x^2 + magic6*x - magic7 = 0
where x is the (supplied) root of this 7th degree polynomial.  And our "magic" variables are the coefficients of this polynomial, supplied by different length chunks of the serial (2 bytes, 3, 4, 5, 5, 6, 7).  We just have to find the coefficients of the polynomial (given the roots supplied by the even bytes of the RIPEMD hash of the name).
There are a few different ways to attack this problem, and the way I chose to attack it is probably the most basic.  Basically, if you have the roots of a polynomial, you can put them back into a factorization of the polynomial like this:
(x-a)(X-b)(x-c)(x-d)(x-e)(x-f)(x-g)
where a-g are the 7 supplied roots from the namehash.
We could manually multiply this out and factor out the terms for each coefficient, but that would be long, painful, and above all, ugly.  Or you can skip that whole process and find combinations:

magic1 = sum (7C1) = (a+b+c+d+e+f+g)
magic2 = sum (7C2) = (ab+ac+ad+ae+af+ag+bc+bd ...)
magic3 = sum (7C3) = (abc+abd+abe...)
magic4 = sum (7C4) = (abcd+abce+...)
magic5 = sum (7C5) = (abcde+...)
magic6 = sum (7C6) = (abcdef+...)
magic7 = sum (7C7) = (abcdefg)

Tada! Now just mash em together into a 32 byte serial, convert to 64 hex chars, add 17-20 in a loop and you're done!  Check my keygen source for an unoptimized version of this method.

Till next time,

Kreet