The Journal 4 (build

DavidRM Software (current build is now, protection remains unchanged).

I was sent this target via an e-mail request; the reverse engineer concerned had evidently mapped out most of the protection system and had managed to patch it during a live debugging session, all he was missing was that final piece of information to complete the task. The Journal's protection system is based around the insertion of 2 Keys (aptly named Key 1 & Key 2), as it turns out these are 'keys' in more than just name.

The first stage when analysing any protection scheme is to actually *see* the code we need to study, by loading the main Journal4.exe into IDA it doesn't take long to isolate the code that we need:

:00873DDB call sub_72D328 ; must return AL non-zero
:00873DE0 test al, al
:00873DE2 jz short loc_873DF0
:00873DE4 mov dword ptr [ebx+24Ch], 1
:00873DEE jmp short loc_873E12

:00873DF0 loc_873DF0:
:00873DF0 push 0
:00873DF2 mov ecx, offset aRegistrationEr ; "Registration Error"
:00873DF7 mov edx, offset aInvalidRegistr ; "Invalid Registration Keys entered."

We can see that sub_72D328 must return AL non-zero, now lets peer beneath. When analysing code I always advocate keeping focus on the *bigger picture*, studying code is a time-consuming process so we don't want to needlessly navigate through uninteresting sections. We know AL must be returned as non-zero so lets isolate where its value is assigned:

:0072D3B9 mov al, [esp+8+var_8_return_value] ; AL = return value
:0072D3BC pop ecx
:0072D3BD pop edx
:0072D3BE pop esi
:0072D3BF pop ebx
:0072D3C0 retn

All exit points in the function assign AL's value at address 72D3B9, this leads to the following code:

:0072D335 call sub_6F2E48
:0072D33A mov [esp+8+var_8_return_value], al ; write our return value
:0072D33D cmp [esp+8+var_8_return_value], 0
:0072D341 jz short loc_72D3B9 ; jump to exit, AL was zero

Now lets navigate beneath sub_6F2E48, it leads directly to sub_6F1EA4 which is the real protection routine, as per sub_72D328, AL's value is critical:

:006F1F9E mov al, [ebp+var_9_return_value] ; AL = return value
:006F1FA1 pop esi
:006F1FA2 pop ebx
:006F1FA3 mov esp, ebp
:006F1FA5 pop ebp
:006F1FA6 retn 4 ; for argument on stack (StdCall)

The protection can be patched and killed right here, simply replacing 6F1F9E with some instructions that set AL to non-zero will be sufficient. For now take note that sub_6F1EA4 receives a single argument as a parameter. The first part of the code creates an 8 byte buffer from Key 2, each byte in Key 2 providing a nibble via a lookup table:

:006F1ED3 movzx ecx, byte ptr [esi+ecx]
:006F1ED7 mov cl, byte ptr ds:table_8EB003[ecx] ; first nibble
:006F1EDD shl ecx, 4 ; prepare for next nibble
:006F1EE0 mov ebx, eax
:006F1EE2 add ebx, ebx
:006F1EE4 movzx ebx, byte ptr [esi+ebx+1]
:006F1EE9 or cl, byte ptr ds:table_8EB003[ebx] ; second nibble
:006F1EEF mov [edx], cl ; place in buffer

This loop implies Key 2 must have a length of 16. The table mapping for the letters A-Z is as follows (there is also mapping for other characters but they aren't required):

A - 0 B - 8 C - 1 D - 9 E - 2 F - 6
G - 0A H - 0E I - 3 J - 7 K - 0B L - 0F
M - 4 N - 8 O - 0C P - 0 Q - 5 R - 9
S - 0D T - 1 U - 4 V - 7 W - 0A X - 0D
Y - 0F Z - 2        

If we desire an 8 byte buffer of say '12 34 56 78 90 AB CD EF' we can use Key 2 'CEIMQFJBDAGKOSHL'. Lets jump forward to the checking routine:

:006F1F58 lea eax, [ebp+var_18_decryption_result]
:006F1F5B lea edx, [ebp+var_8_J4$B]
:006F1F5E mov ecx, 4
:006F1F63 call sub_403558 ; strncmp
:006F1F68 jnz short set_al_zero
:006F1F6A cmp [ebp+arg_0], 0
:006F1F6E jz short set_al_1
:006F1F70 mov eax, [ebp+var_14]
:006F1F73 and eax, [ebp+arg_0]
:006F1F76 jnz short set_al_1

:006F1F78 xor eax, eax ; failed

Prior to sub_403558 we have EAX, EDX & ECX all loaded with parameters, this is classic Borland fastcall convention, the function itself is almost certainly strncmp or similar. EDX points at a 4 byte string 'J4$B', an identifier most likely selected by The Journals author, EAX points at an 8 byte result (01 AF CC 77 BE 4C 58 43) in the case of the Key 1 (12344321) & Key 2 (CEIMQFJBDAGKOSHL) that I had been using. At this point I navigated back to start studying the code before this check and stumbled upon the string reference 'TBlowFish', this is the kind of blind luck that sometimes occurs when reverse engineering. From Google, TBlowFish would appear to be a component that implements the Blowfish algorithm. At this point the bad 'cracker' mentality in me took over, using CryptoBench I quickly established Key 1 was the Blowfish key used to decrypt Key 2.

Journal 4, CryptoBench

We know that the first 4 bytes of the result must therefore be 'J4$B' or 4A 34 24 42, however there are an additional 4 trailing bytes as well. Initially I set these to 00 00 00 00 since the parameter arg_0 was passed in as 0 during the "insert keys" stage the jump at 6F1F6E always occured; but this turns out not always to be the case. At 6F1F76 if arg_0 was passed in as non-zero we have a bit test check against the last 4 bytes as well. There are a few ways to proceed, one is by cross referencing sub_6F1EA4 and backtracking, the other is a breakpoint during a live debugging session, either approach will work.

Generating a valid Key 1 / Key 2 is therefore fairly easy. Firstly we select our Key 1 to be our Blowfish key (8 bytes long for simplicity), lets use in this example '12345678':

Key 1: 12345678 (31 32 33 34 35 36 37 38).

Next we construct our desired decryption result & encrypt it with our Blowfish key:

4A 34 24 42 FF FF FF FF -(Blowfish Encrypt)-> BD 87 1C 36 0C 08 17 D7

Finally we construct our Key 2 to produce the result of the Blowfish Encrypt (KSBJCOIFAOABCJSJ). When we insert this combination we get several breaks on our bit test check (arguments are 1 & 2), assuming these pass, thank you for registering with extended security appears.

As a further aside I decided to see if I could identify the actual TBlowFish component used and I'm pretty sure I've found it, grab Blowunit.dcu here (7k) and load it into DeDe. Below are just a few names from my IDB. If you want to unregister simply delete the file DRMSJ4.SDF found in the Windows directory.

TBlowFish.BF_Decipher 005293A8
TBlowFish.BF_Encipher 00529210
TBlowFish.BF_Initialize 00529074
TBlowFish.EncipherBLOCK 005295C4
TBlowFish.InitArray 00528F88
TBlowFish.SetKeys 00528FE4
TBlowFish.bf_F 00529014

Return to Keygens Return to Main Index

© 1998-2007 CrackZ. 31st July 2007.