GenStat 7th Edition

In this tutorial I discuss how we can go about conducting a total analysis of a protection scheme including its 'decision making process'. The decision to take apart any scheme should not be taken lightly, it is certainly the most time-consuming of all the reversing methods and unless you are really interested in breaking completely a target, don't attempt this unless you have a few hours or more to spare ;-). This tutorial will not damage GenStat's developers in anyway, a key generator (1 type of key only) by a group called CROSSFiRE has existed for a good 2 months, leechers can simply download this small program and generate keys to their hearts content.

GenStat's protection is based around the common idea of a license key, these tend to be separated into 2 distinct categories, keys that are generated and that correspond to a specific user provided piece of information or keys that are passed through verification routines to check their validity and type, GenStat is the latter. Starting the application gives us the opportunity to install a license key of our own or a trial license, keys are of the form XXXXXX-XXXXX-XXXXXX-XXXXXX. A disassembly of GenStat.exe in IDA forms a key part of the analysis.

Uncovering the protection scheme is trivial and can be achieved using straightforward breakpoints in SoftICE on an input of your choosing. The top level license validation function is sub_6AAB40() which I renamed as validateLicense(). We start by monitoring what happens if we install a trial 30 day license, we are granted the creation of some keys to further clutter our registry and also the creation of the 12-byte hidden file GenStat.dky and the plain text file GenStat.glx in the /bin directory.

Registry

HKEY_LOCAL_MACHINE\Software\VSN International\GenStat\Seventh Edition\License
Demo Expiry "+30 days"
Demo Install Date "Saturday, 03 July 2004 15:11:26"
Demo Key "85 EA 90 27 65 5B BF 2D 4B 98 A6 63"

* Note that this information may also be copied beneath HKEY_CURRENT_USER.

Files

GenStat.dky contents :

85 EA 90 27 65 5B BF 2D 4B 98 A6 63

GenStat.glx contents :

GenStat License File:
229501-fa090a-6a2dda-9eb33e
Installation Date: Saturday, 03 July 2004 15:11:26

Notice how the format of the Demo Key fits exactly the license key input, 85EA90-27655B-BF2D4B-98A663. If either the Demo Key registry entry or GenStat.dky are removed, a license error will result, if GenStat.glx is removed the program will not find any license. The startup validation reads the plain text license from GenStat.glx.

Demo Key Process

22 95 01 FA 09 0A 6A 2D DA 9E B3 3E
-----------------------------------
22 95  d  e  l  i  q  u  e  s  c  e
22 95 64 65 6C 69 71 75 65 73 63 65 (XOR)
-----------------------------------
22 95 65 9F 65 63 1B 58 BF ED D0 5B

_malloc() below sub_6AA130() allocates 0x400 bytes of memory, this will be used to store an array of 256 pointers to various 12 letter strings which all begin 'XX' (the 'XX' part is disregarded). The 2nd byte of the key (0x95) is used to select the string which will be used. In my example 0x95 selects the string 'XXdeliquesce'. The last 10 bytes are XOR'd against the selected string as shown above.

This result is passed to sub_6A9D40() which performs one of 2 types of re-arrangement on the last 11 bytes; the first bytes highest 4 bits determine which re-arrangement will be selected, if the high 4 bits are set to anything other than 0x1 then case 1 is used.

22 95 65 9F 65 63 1B 58 BF ED D0 5B
 1  2  3  4  5  6  7  8  9 10 11 12 <-- Numeric position
-----------------------------------
 1 10  2  7  3 12  5 11  8  9  4  6 <-- Rearranged position (case 1)
 1  7  4  3 10 12  8  5  9 11  2  6 <-- Rearranged position (case 2)
-----------------------------------
22 ED 95 1B 65 5B 65 D0 58 BF 9F 63 <-- Result (case 1 only)

Next we use the first byte to determine the key type :

:006AAB82 mov al, byte ptr [esp+70h+var_38] ; First byte.
:006AAB86 add esp, 18h
:006AAB89 sar eax, 4 ; / 16
:006AAB8C and eax, 0Fh ; Low 4 bits only
:006AAB8F mov esi, 0Ch
:006AAB94 cmp eax, 2
:006AAB97 jnz short loc_6AABD5 ; Not a Demo Key

So we can see if our first byte is any byte in the range 0x20-0x2F or 0xC0-0xCF we appear to have a Demo Key.

Next GenStat opens the Demo Key entry in the registry and XOR's the last 11 bytes against our result above, there is actually a small bug in this routine which overruns the XOR by 1 byte, the for loop should only run 11 times, not 12 ;-). It is insignificant though.

22 ED 95 1B 65 5B 65 D0 58 BF 9F 63
85 EA 90 27 65 5B BF 2D 4B 98 A6 63 (XOR)
-----------------------------------
22 07 05 3C 00 00 DA FD 13 27 39 00

Next the 7th byte (0xDA) is set to 0x0 and the remaining bytes are summed, if we have a valid key the lowest 8 bytes of the sum result will match the 7th, else you'll be fired with error (8). Passing this check places results into what I've termed 'the GenStat Structure'.

GenStat_Structure {
dw_gs1
dd_gs2
dw_gs3
dw_gs4
dw_gs5
dd_gs6
dd_gs7
dd_gs8
dw_gs9
}

Each of these results is obtained from our 12 byte key as follows :

Result Obtained from / possible meaning Result
dw_gs1 1st byte SAR 4, & 0xF (sign extended from 8-16 bit); Key type 0x2
dd_gs2 1st byte & 0xF; Used in switch at loc_6AACD7, possibly type of expiry or Key type 0x2
dw_gs3 2nd byte SAR 5, & 0x7 (sign extended from 8-16 bit); Quite possibly required to be 0 0x0
dw_gs4 2nd byte SAR 4, & 0x1 (sign extended from 8-16 bit); Unknown, possibly minor version 0x0
dw_gs5 2nd byte & 0xF (sign extended from 8-16 bit); Possibly GenStat version level 0x7
dd_gs6 3rd & 4th bytes; Expiry date 0x53C
dd_gs7 5th & 6th bytes; Unknown 0x0
dd_gs8 7th, 8th, 9th & 10th bytes; Unknown (7th is the checksum) 0x2713FDAD
dw_gs9  11th & 12th bytes; Unknown 0x39

In memory the structure appears like so :

gs1 gs1 xxx xxx gs2 gs2 gs2 gs2 gs3 gs3 gs5 gs5 gs4 gs4 xxx xxx
gs6 gs6 gs6 gs6 gs7 gs7 gs7 gs7 gs8 gs8 gs8 gs8 gs9 gs9

02 00 xx xx 02 00 00 00 00 00 07 00 00 00 xx xx
3C 05 00 00 00 00 00 00 DA FD 13 27 39 00

The 'xx' & 'xxx' entries represent unused areas and are present possibly due to variable alignment.

After producing these results from our key, the program starts checking the structure entries and execution branches accordingly. This is the real crux of GenStat, this tutorial and any other key validation scheme you may encounter, mapping out and tracing all of the available key options; if we document each and every code path we could end up with a myriad of possibilities, writing a key generator based on the information we have about the key at the moment would be risky to say the least.

However there is a better way.....and thats finding the weakest link in the chain, remember at the outset of this tutorial that GenStat 'generates' its own demonstration key.....this generator will tell us everything we ever need to know about the keys structure. Lets remove the registry entry and our 2 GenStat keyfiles and trace the Demo Key generation routine (sub_6AB490); you can find this easily by cross referencing functions from the main key validation routine. Here is what we find :

dw_gs1 & dd_gs2 : both set to 2.
dw_gs3 & dw_gs4 : both set to 0.
dw_gs5 : set to 7.
dd_gs6 : expiry date, current date is transformed and then 0x1E is added.
dd_gs7 : set to 0.
dd_gs8 : low byte is checksum, high 3 are the HD serial number.
dw_gs9 : highest byte of the HD serial number.

This valuable insight means we only really need now to concern ourselves with the key type (dw_gs1's value (5 options)), dd_gs2's value (used in a switch statement (8 options)) and the expiry date (which may or may not be relevant depending on the key type). Lets map out the procedures we will need to write for a key generation function (after writing our generator we will be able to test the effects of various types of keys). As an example lets assume we want to restore our 30 days trial by creating a new Demo Key. Lets draw up firstly our desired results :

22 07 05 42 00 00 E0 FD 13 27 39 00

The only entry that changes is our expiry date; since 0x53C corresponds now to 24 days left, I simply add 6 to make 30 once again, this affects the checksum byte which also requires 6 to be added. So far so good. Now we XOR with the Demo Key entry from the registry :

22 07 05 42 00 00 E0 FD 13 27 39 00
85 EA 90 27 65 5B BF 2D 4B 98 A6 63 (XOR)
-----------------------------------
22 ED 95 65 65 5B 5F D0 58 BF 9F 63

Next our key needs to be re-arranged.

22 ED 95 65 65 5B 5F D0 58 BF 9F 63
----------------------------------- (rearranged)
22 95 65 9F 5F 63 65 58 BF ED D0 5B

And finally XOR'd with the relevant string :

22 95 65 9F 5F 63 65 58 BF ED D0 5B
-----------------------------------
22 95  d  e  l  i  q  u  e  s  c  e
22 95 64 65 6C 69 71 75 65 73 63 65 (XOR)
-----------------------------------
22 95 01 FA 33 0A 14 2D DA 9E B3 3E

We try the key 229501-fa330a-142dda-9eb33e, restart GenStat and another 30 day trial is ours to enjoy.

With all of the information above it is fairly easy to produce a key generator and then by trial and error produce keys for the various key types (dw_gs1 = 1 & dd_gs2 = 2 is what the aforementioned key generator uses). You can request my key generator for examination if you so please; the real irony of GenStat however is that it is actually slightly easier to produce a valid non-Demo or Beta key since these skip the XOR step shown above. As a final note, I'd appreciate any end-users of GenStat letting me know what types of key can actually be purchased, just the first few digits of their license keys sent anonymously would suffice.


Return to Keygens Return to Main Index


© 1998-2004 CrackZ. July 2004.