Hi this is Mad typing...

Brains Breaker 2.1 CRACKING

Enclosed file: code.txt (contains key template to copy in the clipboard)

this time we are targeted against a windoze game: Brains Breaker 2.1b.
I already owned this prog on one of my magazine's cd (collected how +ORC 
teach us), but i hadn't took a look at it. 
This is cause i don't want to waste hard disk space to hold unuseful 
programs (but what is a real useful program besides Soft-ICE ?).
And moreover this is a game, and my favorite games are cracking challenges !!!
So, if i hadn't read the Strainer, i would have missed this little piece
of good coding...

WHERE TO START ?
First of all we install the program. Then we run it and played just to see
what means it is unregistered. We spend some time reading the help file,
to retrieve as many info as possible on program registration.
At this point we know:
- program could be registered in two ways: the Full Pack that enables you
  to create puzzles on your own, and the Entry Set that allow us only to play.
- program registration is possible (i mean this is not a crippled one) cause
  we have a "Program Opts" and an "About Box" saying "Unregistered".
- many program limitations due to "Demo version"
So i started to search where can we input our name and a code to register it.
I tried double clicking on bitmaps in the About, pressing Alt-R, calling the
prog with /REGIST, but nothing happens.
This was really the hard part of the cracking, cause we can't derive a good
point where to land in. So i started to seek for a possible flag, checked
at program beginning, and when it puts the DEMO message over a puzzle.
The same thing happens when i searched for messagebox messages like
"This program is running in..." or "Pardon...": this don't uses standard
MessageBox API, so there is no way to breakpoint. (Actually i know where
routines displaying these messages are, but there is no significant jump
in front of them...)
All my tries go wrong... after two days i was disappointed... this was chosen
for the Strainer for a good reason !
Then, i a very lazy session, i started browsing thru program data seeking for
interesting messages. And i got a very interesting one about incorrect data
in the ClipBoard. A bulb will light up !!! This is a new (and nice) way to
get user input, without making a cracker suspicious...
In fact this is one of advice given on +Fravia's page: keep your protection
hidden, and don't produce large messagebox saying a program is unregistered,
cause this will annoy standard users and will give crackers a good breakpoint.

IT'S TIME TO CRACK...
So we load Soft-ICE and we try a breakpoint to see if really the prog uses
clipboard data.
I run my Notepad, write some data, selected it and pressed Ctrl-C to copy
it to Clipboard. Then i switched to Soft-ICE and putted a breakpoint using:
BPX GetClipboardData
(You MUST have a good API reference for Windoze cracking; you can find it
in many places and in my preferred book "Programming Windows 95" by Charles
Petzold and Paul Yao)
I run BrainsBreaker and Soft-ICE wake up. OK, now we have to tame this beast!
It's the right time to produce a dead listing of this prog.
So we fire up our registered IDA (BBRK32.exe is a 600Kb monster), and we
load the prog. Sudden we discover this was written using Borland C++ 4.x/5.x
or C++ Builder. This can take some time to disassemble, so we relax ourselves
before the great battle (we can have a drink...). When IDA has finished its
work, we produce a listing of the disassembly (i use the listing for searches
cause IDA search option sucks).

Ok, after the breakpoint wake up Soft-ICE, we press F11 and we should
land there (420467):
(every API calls is preceded by a j cause Borland uses an internal jump to
call the API, but this is of no importance for us)

420460 loc_420460:	
420460		 push	 1
420462		 call	 j_GetClipboardData
420467		 mov	 esi, eax		EAX points to clipboard
						buffer in memory
420469		 test	 esi, esi		
42046B		 jz	 short loc_420480	clipboard contains no data
42046D		 push	 esi
42046E		 call	 j_GlobalLock
420473		 push	 eax		   clipboard buffer
420474		 push	 ebx		   pointer to memory destination
420475		 call	 sub_414F23	   copy clipboard data
42047A		 push	 esi
42047B		 call	 j_GlobalUnlock
420480 
420480 loc_420480:
420480		 push	 [ebp+var_4]
420483		 push	 edi
420484		 call	 sub_4203C8
420489		 mov	 eax, ebx
42048B 
42048B loc_42048B:			
42048B		 pop	 edi
42048C		 pop	 esi
42048D		 pop	 ebx
42048E		 pop	 ecx
42048F		 pop	 ebp
420490		 retn	 8
420490 sub_42043A	 endp

This routine copy data from the clipboard buffer in memory to BrainsBreaker
data memory to handle it later. For me address DS:6CF9D4 contains pointer
to clipboard data when copied.

On return, from this code, we land in CS:4566D7.

4566D2		 call	 sub_42043A
4566D7		 cmp	 dword ptr [ebp-0Ch], 0
4566DB		 jnz	 short loc_4566F1	here we jump
...
4566F1 loc_4566F1:
4566F1		 push	 dword_4897D4		"BrainsBreaker"
4566F7		 push	 dword ptr [ebp-0Ch]	our inserted data
4566FA		 call	 _strstr
	strstr finds the first occurrence of a string in another
	one. This leave us free to decide data format.
	We use all string comparing to derive what BrainsBreaker
	expect to find in the clipboard.
4566FF		 add	 esp, 8
456702		 test	 eax, eax
456704		 jnz	 short loc_45672E	here we jump
...
45672E		 cmp	 dword ptr [ebx+5Eh], 0
456732		 jle	 short loc_456778	here we jump
...
456778 loc_456778:
456778		 push	 ebx
456779		 call	 sub_4548ED
45677E		 push	 dword ptr [ebp-0Ch]
456781		 push	 ebx
456782		 call	 sub_456AF5

This last routine is where we have all strings checking in our
inserted data. So we study it:

456AF5		 push	 ebp		routine entrypoint
...
456B0E		 push	 eax		"Puzzler"
456B0F		 push	 ebx		our data
456B10		 call	 _strstr
456B15		 add	 esp, 8
456B18		 test	 eax, eax
456B1A		 jnz	 short loc_456B2F
...
456B2F		 lea	 edx, [edi+38h]
456B32		 push	 edx		"User:"
456B33		 push	 ebx		our data
456B34		 call	 _strstr
456B39		 add	 esp, 8
456B3C		 test	 eax, eax
456B3E		 jz	 short loc_456B45
456B40		 mov	 esi, 1
456B45		 lea	 eax, [edi+3Eh]
456B48		 push	 eax		"BrainsBreaker 2."
456B49		 push	 ebx		our data
456B4A		 call	 _strstr
456B4F		 add	 esp, 8
456B52		 test	 eax, eax
456B54		 jz	 short loc_456B5B
456B56		 mov	 esi, 2
456B5B		 test	 esi, esi
456B5D		 jle	 loc_456F8B
456B63		 call	 sub_415EDD	error checking
456B68		 lea	 eax, [edi+38h]
456B6B		 push	 eax		"User:"
456B6C		 push	 ebx		our data
456B6D		 call	 _strstr
456B72		 add	 esp, 8
456B75		 add	 eax, 5		retrieve inserted name after "User:"
456B78		 mov	 [ebp+var_4], eax
456B7B		 push	 [ebp+var_4]
456B7E		 lea	 edx, [edi+4Fh]
456B81		 push	 edx		";" used for name ending
...
456B90		 call	 sub_41502A	calculates name length
456B95		 lea	 eax, [edi+53h]
456B98		 push	 eax		"UserID:"
456B99		 push	 ebx
456B9A		 call	 _strstr
456B9F		 add	 esp, 8
456BA2		 mov	 [ebp+var_10], eax
456BA5		 cmp	 [ebp+var_10], 0
456BA9		 jnz	 short loc_456BC6
...
456BC6		 mov	 edx, [ebp+var_10]
456BC9		 add	 edx, 7		retrieve UserID
...
456BE7		 push	 edx		"Key:"
456BE8		 push	 ebx
456BE9		 call	 _strstr
456BEE		 add	 esp, 8
456BF1		 mov	 [ebp+var_1C], eax
456BF4		 cmp	 [ebp+var_1C], 0
456BF8		 jnz	 short loc_456C20
...
456C20		 mov	 edx, [ebp+var_1C]
456C23		 add	 edx, 4		retrieves key
456C26		 push	 edx
456C27		 lea	 ecx, [edi+4Fh]
456C2A		 push	 ecx		seek ";" at key end
...
456C39		 call	 sub_41502A	calculates key length
456C3E		 lea	 edx, [edi+60h]
456C41		 push	 edx		"Pack:"
456C42		 push	 ebx
456C43		 call	 _strstr
456C48		 add	 esp, 8
456C4B		 mov	 ebx, eax
456C4D		 test	 ebx, ebx
456C4F		 jnz	 short loc_456C82
...
456C82		 add	 ebx, 5		retrieves pack info
456C85		 push	 ebx
456C86		 lea	 eax, [edi+4Fh]
456C89		 push	 eax		seek ";"
...
456C93		 push	 eax		"Full"
...
456CDD		 push	 esi		pack type
456CDE		 push	 [ebp+var_34]	user name
456CE1		 push	 [ebp+var_38]	user id
456CE4		 push	 [ebp+var_3C]	key
456CE7		 push	 ebx
456CE8		 call	 sub_46A161	check if key starts with "$"

At this point we have all elements to create well designed data to put in
the clipboard; this is my data:

BrainsBreaker 2.1 Puzzler
User:Bozo Clown;
UserID:Bozo;
Key:$11111111;
Pack:Full;

We want the Full pack and we inserted a random number of 8 chars for key.
So i wrote this data in Notepad, selected it and copied. Then i run
BrainsBreaker to continue tracing. We got 2 calls at this point:
one at 456D2A and one at 456D31. The second one is very interesting, cause
it is followed by a test eax,eax. This is the code:

456D2A		 call	 dword ptr [ecx+8]
456D2D		 lea	 eax, [ebp+var_40]
456D30		 push	 eax
456D31		 call	 sub_46AA6C		here we return with EAX=0
456D36		 test	 eax, eax
456D38		 jz	 short loc_456D55	so we jump
456D3A		 lea	 edx, [ebp+var_44]
456D3D		 push	 edx
456D3E		 push	 0
456D40		 push	 0
456D42		 push	 ebx
456D43		 mov	 ecx, [ebx]
456D45		 call	 dword ptr [ecx+10h]
456D48		 lea	 eax, [ebp+var_44]
456D4B		 push	 eax
456D4C		 call	 sub_46AA6C
456D51		 test	 eax, eax
456D53		 jnz	 short loc_456D59
456D55		 xor	 eax, eax		we reset EAX
456D57		 jmp	 short loc_456D5E
456D59		 mov	 eax, 1
456D5E		 test	 eax, eax
456D60		 jnz	 short loc_456DCE	
456D62		 test	 ebx, ebx
456D64		 jz	 short loc_456D99

This code should look familiar to every cracker. This is the good old 0/1
comparison, to check if registered/unregistered.
So at line 456D5E i do a: R EAX 1 and pressed F5... hey this is registered
at Full Pack !
Now, we could patch the code here, but i continued tracing just for fun...
What i want to discover is how can EAX be different from 0 at 456D31 exit.
This is routine at 456D31:

46AA6C		 push	 ebp
46AA6D		 mov	 ebp, esp
46AA6F		 mov	 eax, [ebp+arg_0]	if not registered this is
46AA72		 mov	 eax, [eax]		FFFFEA5C
46AA74		 cmp	 eax, dword_489990	000015A4
46AA7A		 setz	 dl
46AA7D		 and	 edx, 1
46AA80		 mov	 eax, edx
46AA82		 pop	 ebp
46AA83		 retn	 4

This routine compares 2 values, so to have EAX setted to 1 on return we
must have [ebp+arg_0] = 000015A4... we continue our back tracing...
This value get setted in 456D2A call (the one just preceding):

459AC1		 push	 ebp			routine entrypoint
459AC2		 mov	 ebp, esp
...
459B86		 push	 ecx
459B87		 lea	 eax, [ebp-20h]
459B8A		 push	 eax
459B8B		 call	 sub_46AA1E
459B90		 push	 dword ptr [ebp-1Ch]
459B93		 call	 @$bdele$qpv	 ; operator delete(void	*)
459B98		 pop	 ecx
459B99		 mov	 edx, [ebp+0Ch]
459B9C		 mov	 ecx, [ebp-20h]		this is value setting
459B9F		 mov	 [edx],	ecx		move it at [ebp+arg_0]

At line 459B9F we find our bad value; our last step is seek where this
is generated. We found this very easily, at the preceding call:
we spy code at 459B8B...

46AA1E		 push	 ebp
46AA1F		 mov	 ebp, esp
46AA21		 mov	 eax, [ebp+arg_0]
46AA24		 mov	 edx, dword_489978
46AA2A		 mov	 ecx, [edx]
46AA2C		 mov	 edx, [ecx]		EDX=000015A4
46AA2E		 mov	 ecx, 1
46AA33		 cmp	 [ebp+arg_4], 0
46AA37		 jnz	 short loc_46AA3C	we don't jump
46AA39		 add	 ecx, 0FFFFFFFEh	ECX=-1
46AA3C		 imul	 edx, ecx		EDX=FFFFEA5C
46AA3F		 mov	 [eax],	edx		we store it
46AA41		 pop	 ebp
46AA42		 retn	 8

This is straight: we want 000015A4 in EDX, so we must change the
conditional jump in a plain JMP.
We do a: CODE ON and write down our byte pattern for patching:
B9 01 00 00 00 83 7D 0C 00 75 03 83 C1 FE 0F AF D1 89 10
We load BBRK32.EXE in HexWorkshop and search for it, obtaining two offsets
for our conditional jump (75 03):
6A037 and 6A05E
These are very close ones, so i suspected the author used the same routine
for checking two different packs. The one we want to patch now is the
first, so we put an EB over the 75 (JNZ -> JMP) to force the jump.
Now thiz game is registered for the Full Pack... enjoy it!

SOME NOTES
When we putted a breakpoint on line 459B8B (where we get the 000015A4 or
FFFFEA5C) and we re-start the prog, we land in the code one more time:
this is a nice clue we are on the right way, in fact this is the first
check the game does to see if it's registered, reading data from Bbrk.ini.

Using the same patch, if you try to register using Pack:Entry, instead of
the Full one, you'll get a partially registered copy: puzzles can be
played without limitations, but in "Program Opts" window it reports not
registered. This is probably due to the fact we must patch other offset
to get a working Entry Pack.
 
If we try to patch other location (6A05E), to get an Entry pack, we receive
an interesting message (this time in a standard MessageBox window):
"Integrity check: program seems to be altered from his original contents."
this means the program contains some CRC checking and can reject from running,
the sad thing (for us crackers ;-) is this protection will not occur when
patching offset 6A037... (however, the prog uses a standard messagebox, so
we can land in very easily... it could only dumbly refuse starting)

However i got a Full Pack, so this is enough for me...
I haven't analyzed this protection in a deeper way, i.e. how the key is 
really checked, cause i've found a weakness in its checking. I believe
code patching is not as elegant as a real understanding of code guts,
but i don't want to create a key generator for this, so i stopped there.
Moreover i did this crack only for educative reason, so we must not
distribute a key generator for a well designed piece of code... if you
like it, buy it (or learn how to crack effectively) !!!

                                        Bye, Mad - 15/V/1998
