Contribute  :  Web Resources  :  Past Polls  :  Site Statistics  :  Downloads  :  Forum  
    BiW ReversingThe challenge is yours    
 Welcome to BiW Reversing
 Monday, August 15 2022 @ 09:01 AM CEST

Solution to haggar's clone keygenme


TutorialsLevel : beginner


In this short tutorial I will show my solution to haggar's clone keygenme. From haggar's comment we know that the target is packed with some (simple) unknown protector and has some obfuscation. The goal (ofcourse) is to create a KeyGen for it.

Haggar rated the keygenme 3/10 and accordingly I will try to target the same audience, meaning beginner-level and not completely newbie-level. Thus I assume that you - the reader - has some prior experience with OllyDBG, manual unpacking and programming in general. This means that I'll try & walk you step-by-step through the process, but I won't explain (for example) howto set a breakpoint in Olly or what a specific command in C# means.

Tools used for Unpacking : PEiD 0.9x, OllyDBG 1.10, Import REConstructor 1.6
Tools used for KeyGen'ing: OllyDBG 1.10 and Visual Studio 2005 (don't kill me !)

The files for this tutorial: attachment
The Clone Keygenme

Part 1 - Unpacking

We start - as always - with analysing our target in PEiD. As you can see from the screenshot I use the unofficial 'leaked' 0.94 version, but any 0.9x version will suffice. First we try the PEiD's Normal Scan and Hardcore Scan and both return 'ASPack 2.12 -> Alexey Solodovnikov'.

No let's try it with the default External Scan of PEiD to make sure:
[MSLRH] v0.1 -> emadicius *

And if you're using haggar's own custom userdb.txt - like me - you'll get:
[MSLRH] v0.2 -> emadicius (h) *

Please note that (h) means 'signature by haggar' :)

So it seems our little MSLRH packer is trying to fool us here ...

Now for the unpacking, I you - like me - have never worked with MSLRH before, it might be wise to search & read some tutorial about it first. When you do, you'll probably find haggar's own tutorial about manual unpacking it on :)

But reading the tutorial first is no fun ofcourse, why not give unpacking it a try first ? It's always good to try & investigate something new yourself, something about a steep learning curve or something :) So fire up PEiD's Generic OEP Finder plugin and ... we get a OEP: 0040101D

Sounds promising, although it could easily be a fake OEP.

Now start OllyDBG, load the target and set a hardware breakpoint on Execution (bpx) on the found OEP (0040101D):

Now pray & run the target ... we break at the bpx and the code looks too good to be fake:

could this really be the real OEP without any other strings attached ?!?

Next start the OllyDump plugin and dump with the following settings:

Normally I use LordPE to dump, but LordPE gives us a nasty error:
"Couldn't grap process memory :("

There is a way around this (as I read in haggar's tutorial about MSLRH when typing this tutorial) choose the 'IntelliDump' engine, dump and ignore the warning about the missing bytes. I originally also tried the 2nd LordPE method, but discarded the dump since the warning about missing bytes popped up ... Anyways, I'd stick with the easy way; OllyDump and no errors/warnings :)

Next up: getting the imports. Start ImpREC, enter the real (relative) OEP: 101D and click 'IAT AutoSearch' and 'Get Imports'. If all went well, you'll have 24 (decimal) valid imports:

Now press 'Fix Dump', save, hope for the best and run it :)

You should have a running, unpacked target now. No manual IAT rebuilding, no manual PE header fixing and alike, just a clean dump & fix. Didn't haggar tell us it was easy ? ...

TIP: It's usually a wise idea to rebuild the dumped .exe with LordPE. Don't forget to remove the obsolete packer-section(s) where possible. I'll skip this because it's fairly simple & out-of-scope.

Part 2 - Analysing the KeyGenMe verification algorithm

Ok, how do we get to the name/serial verification ?

Some general options would be: start tracing at OEP, (peeking in IAT) and guessing the used API, trying to break/pause in the middle of the License-checking routine or backtracking some serial/name/badboy string references.

Let's assume the program is large and try the user32.GetDlgItemTextA-import (which we can see in the IAT).

Note: If you're not familiar with this Windows API: GetDlgItemTextA is the API which returns the ASCII input of a textbox-control and GetDlgItemTextW is it's Unicode counterpart.

Load the unpacked executable into Olly. set a BP at (note: there's no bp checking anymore) at user32.GetDlgItemTextA, run the target, enter some characters in both fields (name: minimal 5 chars, serial: 8 chars exactly) and press the 'Check Serial' button. We break at the GetDlgItemTextA BP.

When we break at the API, just hit [CTRL] + [F9] to run till return (and stepping over the final RETN) and we'll land here (back in the target's code):

We're clearly right on target. As you can see I already entered some comment and labeled some memory addresses to make the code easier to analyse. Usually the more the better, but in this case I only labeled some to keep it alike the disasm you should be looking at right now.

The target reads the entered username (which must be at least 5 chars) and starts doing some weird arithmetic. Scroll down a little and you'll see a second call to user32.GetDlgItemTextA, followed by the conversion of the entered hex-string to it's binary hex-value. If a non-HEX character is encountered we immediately jump to the badboy message.

Right after the hex-conversion the obfuscated serial-checking begins. Lot's of conditional jumps, some undefined opcodes and some calls to a 'nullsub' (= a dummy subroutine which does nothing). At first this looks quite complex, but trace the code a few times and you'll see that hagger was so nice as to only make it look obfuscated, without 'really' obfuscating the program flow (conditionally jumping around and back depending on some register-contents) and without polymorphic/self-modifying code as you find in most modern packers like ASProtect, Armadillo, etc. Quite a relieve, since tracing heavily obfuscated code can be a real pain in the behind :)

I skip the step-by-step analysis of the obfuscated code, because this would take pages, but trace the code a couple of times, or run a logged trace with [CTRL] + [F11] directly after the 2nd call to user32.GetDlgItemTextA and analyse the result. This should give you a good idea of what is happening: nothing fancy. If we cut out the unnecessary obfuscation (the conditional jumps, etc), we're left with the actual serial-verification algorithm:

004012E5     >  33C0             xor     eax, eax                                                                     
004012EE     >  33DB             xor     ebx, ebx                                                                     
004012F4     >  33C9             xor     ecx, ecx                                                                     
004012FD     >  33D2             xor     edx, edx                                                                     
00401302     >  0FB699 C8304000  movzx   ebx, byte ptr [ecx+4030C8]                                                   
0040130D     >  C1E3 04          shl     ebx, 4                                                                       
00401315     .  41               inc     ecx                                                                          
0040131D     >  0FB691 C8304000  movzx   edx, byte ptr [ecx+4030C8]                                                   
00401329     .  03DA             add     ebx, edx                                                                     
0040132F     >  80F3 12          xor     bl, 12                                                                       
00401337     .  80C3 34          add     bl, 34                                                                       
00401341     >  81E3 FF000000    and     ebx, 0FF                                                                     
0040134A     >  41               inc     ecx                                                                          
0040134F     >  03C3             add     eax, ebx                                                                     
00401354     >  C1E0 08          shl     eax, 8                                                                       
00401362     >  0FB699 C8304000  movzx   ebx, byte ptr [ecx+4030C8]                                                   
00401372     >  C1E3 04          shl     ebx, 4                                                                       
0040137D     >  41               inc     ecx                                                                          
0040137E     .  0FB691 C8304000  movzx   edx, byte ptr [ecx+4030C8]                                                   
00401390     >  03DA             add     ebx, edx                                                                     
00401392     .  80F3 56          xor     bl, 56                                                                       
00401398     >  80C3 78          add     bl, 78                                                                       
004013A2     >  81E3 FF000000    and     ebx, 0FF                                                                     
004013B1     >  41               inc     ecx                                                                          
004013B2     .  03C3             add     eax, ebx                                                                     
004013BB     >  C1E0 08          shl     eax, 8                                                                       
004013C9     >  0FB699 C8304000  movzx   ebx, byte ptr [ecx+4030C8]                                                   
004013DE     >  C1E3 04          shl     ebx, 4                                                                       
004013E4     >  41               inc     ecx                                                                          
004013E5     .  0FB691 C8304000  movzx   edx, byte ptr [ecx+4030C8]                                                   
004013F1     .  03DA             add     ebx, edx                                                                     
004013F6     >  80F3 90          xor     bl, 90                                                                       
00401400     >  80C3 AB          add     bl, 0AB                                                                      
00401407     >  81E3 FF000000    and     ebx, 0FF                                                                     
00401414     >  41               inc     ecx                                                                          
00401415     .  03C3             add     eax, ebx                                                                     
00401417     .  C1E0 08          shl     eax, 8                                                                       
00401425     >  0FB699 C8304000  movzx   ebx, byte ptr [ecx+4030C8]                                                   
00401437     >  C1E3 04          shl     ebx, 4                                                                       
00401454     >  41               inc     ecx                                                                          
0040145A     .  0FB691 C8304000  movzx   edx, byte ptr [ecx+4030C8]                                                   
00401461     .  03DA             add     ebx, edx                                                                     
0040146A     >  80F3 CD          xor     bl, 0CD                                                                      
00401474     >  80C3 EF          add     bl, 0EF                                                                      
0040147A     >  81E3 FF000000    and     ebx, 0FF                                                                     
00401480     .  41               inc     ecx                                                                          
00401481     .  03C3             add     eax, ebx                                                                     
0040148A     >  0FC8             bswap   eax                            ;  / Last command of serial-hash calculation  
0040149B     >  8B1D D8304000    mov     ebx, [4030D8]                  ;  Read the hash of Name into EBX             
004014A4     >  3BD8             cmp     ebx, eax                       ;  The MAGIC compare                          
004014AD     >  75 2E            jnz     short 004014DD                 ;  The MAGIC jump to goodboy ...    
004014AF     .  E9 02000000      jmp     004014B6                       ;  The jump to badboy                
Looks pretty straight on, right :) We can now conclude that the target first calculates some magic number (which I will call the 'hash' from now on) from the provided username and then uses some other algorithm to calculate the 'hash' of the provided serialnumber. When both hashes match, we have a valid name/serial combination !

Part 3 - Writing the KeyGen

So now we have all the info we need to create a KeyGen. We don't even have to know what kind of algorithm the target exactly uses, as long as we can:

1) reproduce the 'calculate hash from name' routine. 2) invert the 'calculate hash from serial' routine to 'calculate serial from hash'.

So we'll do just that, not in assembly but in a higher language, since I think that has some additional value for this tutorial. Developing a KeyGen in a higher language has the benefit of being easier to adapt & understand and ofcourse creating a GUI is in most cases also much less work.

I choose C# 2.0 for this KeyGen because I just reinstalled my laptop and only had Visual Studio 2005 installed. C# is also a very clear & easy (and productive) language, almost anyone with some (C, C++, C# or Java) experience should be able to understand it. .Net also has a lot of encryption algorithms built-in, (MD5, SHA-1, AES, etc.) which might come in handy in some cases :)

The downside is that you'll need Microsoft .Net v2.0 to run the KeyGen.exe out-of-the-box, but any real coder should be able to port the logic to another language like C++ in minutes. It's almost copy & paste :) When you have C# v1.x, you should be able to just recompile the source and run it without any changes.

When cloning some KeyGen logic in another language try to keep it as close to the original code as possible, we want the algorithm to work correctly first ... tuning/optimising can be done later. In most cases it can be useful to add the original ASM code as comment, just imagine a new version of the target with almost the same logic; without the comment you probably won't remember what was what anymore and have to start all over again.

Porting the Name2Hash routine is pretty simple & straight forward:

static uint Name2Hash(string name)
	uint eax = 0;	// uint = unsigned 32 byte integer (= DWORD)
	uint ecx = 0;
	uint dl  = 0;

	char[] arrName = name.ToCharArray();

	// Read first 4 chars of serial into the 4 bytes of eax 
	for (int i = 0; i > 8) & 0xFF) > 16) & 0xFF) > 24) & 0xFF);
You can see how the C# code maps to the original ASM 'Name2Hash' nicely. They only fancy thing in the code is the frequent use of the >> and ecx = (ecx & 0xFFFFFF00) + (((ecx & 0x000000FF) + 0x0001) & 0x000000FF); // inc cl ecx = (ecx & 0xFFFF00FF) + (((ecx & 0x0000FF00) + 0x0100) & 0x0000FF00); // inc ch Note how we mask the 24 bits of the 32 bits DWORD with the logic AND (= &) operator and increase only the byte (8 bits) we want:

	(ecx & 0xFFFFFF00)                            = higher 24 bits unchanged
	(((ecx & 0x000000FF) + 0x0001) & 0x000000FF)  = lower 8 bits increased by 1, making sure it's a byte operation
									and doesn't affect the other 24 bits ...
To create an inverted serial verification (in this case 'Hash2Serial') algorithm it's normally best to just reproduce the original verification (Serial2Hash) function first, to verify that you 'ported' the ASM correctly to the higher language and invert the logic afterwards:

static string Serial2Hash(string serial)
	if (serial.Length != 8) throw new ApplicationException("Invalid serial length");

	uint   eax = 0;
	uint   ebx = 0;

	ebx = (uint) Convert.ToByte(serial.Substring(0, 2), 16);    // movzx    ebx, byte ptr [serial+0]  : shl     ebx, 4
                                                                // movzx    edx, byte ptr [serial+1]  : add     ebx, edx
	ebx ^= 0x12;                                                // xor      ebx, 0x12
	ebx  = (ebx + 0x34) & 0xFF;                                 // add      ebx, 0x34                 : and     ebx, 0xFF
	eax  = ebx 
The assembly comment is (almost) the de-obfuscated serial-checking routine we found and we just ported the code to C# notation. No tricks, just 1-on-1 conversion.

Now finally the 'Hash2Serial' function inverted from the above 'Serial2Hash', the 'magic' routine in our case. I won't get into the exact mathematical details since that is way out of scope, but you can see that we:

1) almost (just) reversed the order of the commands and 2) inverted all the arithmetic

The assembly comment should be enough to understand what's going on exactly, just compare it to the code above and it will hopefully all be clear:

static string Hash2Serial(uint hash)
	uint eax = 0;
	uint ebx = 0;
	uint ecx = 0;

	eax = Bswap(hash);                  // bswap    eax

	ebx  = (eax & 0x000000FF);          // mov      ebx, al
	ebx  = (ebx - 0xEF) & 0xFF;         // sub      ebx, 0xEF : and     ebx, 0xFF
	ebx ^= 0xCD;                        // xor      ebx, 0xCD
	ecx  = ebx;                         // mov      ecx, ebx

	eax -= ebx;                         // sub      eax, ebx

	ebx  = (eax & 0x0000FF00) >> 8;     // mov      ebx, ah
	ebx  = (ebx - 0xAB) & 0xFF;         // sub      ebx, 0xAB : and     ebx, 0xFF
	ebx ^= 0x90;                        // xor      ebx, 0x90
	ecx += ebx > 16;    // bswap    eax       : mov     ebx, ah      : bswap    eax
	ebx  = (ebx - 0x78) & 0xFF;         // sub      ebx, 0x78 : and     ebx, 0xFF
	ebx ^= 0x56;                        // xor      ebx, 0x56
	ecx += ebx > 24;    // bswap    eax       : mov     ebx, al
	ebx  = (ebx - 0x34) & 0xFF;         // sub      ebx, 0x34 : and     ebx, 0xFF
	ebx ^= 0x12;                        // xor      ebx, 0x12
	ecx += ebx 
That's it ! We inverted the algorithm and now we can:

1) Calculate the hash for a given name 2) Calculate a serial for a given hash

So we have a KeyGen !

Some valid example serials:

Name : SKiLLa
Serial: 8DBEE868

Name : haggar
Serial: FC7E840

Please see the attached KeyGenMe.exe & KeyGen.cs for an example-implementation ...

Part 4 - Conclusion

Well we defeated the KeyGenMe and created a KeyGen for it. The hard part was inverting the Serial2Hash to the Hash2Serial function and ... writing this tutorial :) At first glance packing a KeyGen with a packer/protector sounds lame, but in real life most targets are packed too, so - for once - it's a good exercise when trying to defeat a KeyGenMe, even at this level. I liked the obfuscation haggar put into it; it looks nasty but is yet pretty harmless, just like the used packer and like the serial-verification scheme. This makes it a well-balanced, realistic entry-level KeyGenMe in my opinion !

Note: If you paid close attention you should have found some 'weakness' in the Name2Hash algorithm: different names can have the same serial ! Since the 5th and all the following characters in your name are just byte-wise added to eachother 'SKiLLa123' and 'SKiLLa321' (for example) will produce the same serial :)

I hope you enjoyed the KeyGenMe and this tutorial, and perhaps even learned something :)

Happy Reversing !

What's Related

Story Options

Solution to haggar's clone keygenme | 9 comments | Create New Account
The following comments are owned by whomever posted them. This site is not responsible for what they say.
Solution to haggar's clone keygenme
Authored by: haggar on Friday, April 14 2006 @ 08:58 PM CEST
Wow, that's nice solution :) Very nice.

Solution to haggar's clone keygenme
Authored by: detten on Saturday, April 15 2006 @ 10:28 AM CEST
A very nice article!
Well explained, nice layout, many people will enjoy learning from this keygenme solution.
congratz :)
Solution to haggar's clone keygenme
Authored by: g3nuin3 on Thursday, April 20 2006 @ 01:47 PM CEST
Well written article :)
Solution to haggar's clone keygenme
Authored by: Fredro on Thursday, May 04 2006 @ 07:08 PM CEST
indeed, it's very good explained and thanks for the recommendation about C# and the whole .NET stuff, maybe I should dig in on this :)

/Fredro aka LazykEY
 Copyright © 2022 BiW Reversing
 All trademarks and copyrights on this page are owned by their respective owners.
Powered By Geeklog 
Created this page in 1.02 seconds