Wednesday, April 12 2006 @ 11:57 PM CEST Contributed by: SKiLLa Views: 11806
Level : beginner
Introduction
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 !)
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 www.reversing.be :)
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:
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:
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:
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 :)
Thanks