CrypKey Instant v5.0 Part 1 - by CrackZ

Alleged Target :- Crypkey Instant v5.0
Example Programs :- eXtreme DNC Server v4,0,3,0, VIA v5.1 (Electrical Controls Design Program).

Other Crypkey Targets :-

Brief History

I recall a few years back running into very early versions of CrypKey, I didn't study the scheme back then because it was easy to patch either the application or dll side and was in my opinion just another protection that had missed its mark. As with all reverse engineering projects my return was necessitated by the 'waiting for a bus phenomenon', you go 2 years without seeing a protection and then 2 or 3 examples appear at once. CrypKey's authors aren't shy about promoting their product either, the documentation is a mixture of marketing and interesting protection snippets (look at these 2 gems for example). As for the implementation, well that seems to consist entirely of a few dll's binded to the protected application, there are several names to look for, but basically any file named Cryp95xxx.dll or Crpxxxxx.dll is suspicious.

"CrypKey Instant is a highly advanced software licensing and electronic commerce system for your software. With CrypKey Instant, protecting your software is as easy as pointing and clicking, and then watching the flexibility and power you have added to your software sales send your profits skyrocketing upward. —Jim McCartney, Product Visionary"

"The 16-bit DLL is now auto unloaded if there is an abnormal termination. CrypKey uses a 16 bit DLL under Win95/98, and this would stay in memory if there was an abnormal termination, causing problems. See thunkfx.txt for the fix for this problem".

In the earlier versions you could easily identify the imported protection functions and usually patch CALL protection_function with MOV EAX, 0. One particular application that looks of interest from the CrypKey trial is the Site Key generator, for all I know CrypKey could be another FLEXlm with a demo kit housing a compromising key generator. Looking at some of the files we see some interesting .key files, some of these consist of little more than the text 'DO_NOT_DISTURB', one however (Enc.key) is 64 bytes long, one might guess Enc.key is short for encryption key and if 64 bytes translates into a meaningful 512-bit key we can forget reversing. The 512-bit possibility is also supported by CrypKey Instant, combining the Master Key & User Key would also equate to 512-bits.

What we'll see from CrypKey 5.0 are some notable improvements from previous versions, however I'd hardly describe the implementation as 'visionary' and I'd cast serious doubts to CrypKey's claims that your profits will go skyrocketing upwards. In the current version the CrypKey dll's are protected from modification, although we'll see in the case of VIA how the developers effectively compromised the robustness of this feature with their careless implementation. CrypKey's core executables also seem to resist disassembly, W32Dasm page faults when reading the first byte of the CrypKey sections (named 'Have' 'a nice' & 'day!'), a faults on in SoftICE reveals that memory was not allocated for these sections.

Foreground Attack

The theory behind CrypKey protected programs is providing trial functionality to potential purchasers whilst preventing simplistic cracks, there would seem to be no functionality to encrypt code sections which potentially could require long unbreakable keys to decrypt (this trade off of security versus end-user friendliness makes the entire protection fundamentally weak), ultimately a long key algorithm (even proprietary) may only prevent creation of a key generator and not generic attacks against the CrypKey dll's.

My first target is eXtreme DNC Server 4,0,3,0, installation provides a 30-day trial of full functionality, 2 CrypKey dll's are installed in the program directory :- Crp9516a.dll (83,683 bytes, 16th November 1997), Cryp95a.dll (23,552 bytes, 26th October 1997). As we'll soon discover these equate to CrypKey v4.3. Cyp95a.dll exports a total of 27 functions, each of which starts by placing a thunk index in CL :-

CrypKey Name

Thunk Index
Crypkey Name 

Thunk Index 
RTT (Ready To Try) *

register_transfer *

KillLicense *

direct_transfer *

SetNetHandle *

save_site_key *

GetNetHandle *

get_site_code *

get_level *

get_num_multi_users *

get_option *

get_num_copies *

explain_err2 *


get1_rest_info *

explain_err *

get_site_code2 *

ckChallenge *

crypkeyVersion *

get_authorization2 *

systime *

get_authorization *

spc *

end_crypkey *

transfer_in *

init_crypkey *

transfer_out *


Disassembling eXtremeDNCServer.exe reveals that these functions are not imported directly, instead their addresses are retrieved via GetProcAddress(). Looking slowly through the StringRef's we see remnants of previous protection schemes, ElanLM & AscendantLM (possibly a renamed version of the former). I'll list for didactic purposes all of the functions eXtremeDNC has references too :-

crypkeyVersion(), direct_transfer(), end_crypkey(), get_authorization(), get_level(), get_num_copies(), get_num_multi_users(), get_option(), get_restriction_info(), get_site_code(), get_site_code2(), get1_rest_info(), init_crypkey(), KillLicense(), register_transfer(), RTT(), save_site_key(), transfer_in(), transfer_out().

Next we'll run eXtremeDNCServer.exe under BugTrapper (I recommend adding Cryp95a.dll exports and filtering out everything else), we see that only 5 of the above are actually called, crypkeyVersion(), init_crypkey(), get_authorization(), ckChallenge() & get_restriction_info(). Ultimately all call's into Cryp95a.dll thunk down to their namesake 16-bit exports inside Crp9516a.dll, crypkeyVersion() consists of nothing more than a MOV AX, 2B, or in C :-

int crypkeyVersion(void)
  return 43;

In every CrypKey dll I could lay my hands on this value was always 43. Next up is init_crypkey(), which is considerably more interesting as the program starts to verify the .41s/.ent/.key/.lic & .rst files which are created on-the-fly for each protected program, by convention the .lic file consists of the text 'DO NOT MODIFY THIS FILE!!!" & the .41s file is 0 bytes, the .key & .rst files are 26 & 30 bytes in length and consist of 'byte keys', the .ent file contains either DO_NOT_DISTURB or another '16 byte key'. Inside every CrypKey application there are 2 StringRef's which look like keys, they also play a part here and probably in other verification routines. I didn't look at this procedure because everything seemed to check out (even if you tamper with the content of any of these files), all init_crypkey() must do is return the API response (EAX=0), its 5 parameters were 0708, 0, pointer to first key, pointer to second key & szEXTREME.LIC.

Next up for discussion are get_authorization() & get_site_code(), both are single API response type functions, with CrypKey you'll see very quickly that all negative API responses are errors, 0 is good, 1 is for limited days and 2 for limited runs (if applicable to the function). ckChallenge() is one of the few interesting functions, it returns a checksum word which is then verified application side, if these are inconsistent you'll be fired with -106. In the case of eXtremeDNCServer.exe ckChallenge() is called 4 times from 3 different locations. Finally, we have get_restriction_info(), this returns an API response, the number of days the trial is valid for and the amount of time used. Its worth taking a closer look at the parameters pushed to get_restriction_info(), the first 2 pointers are the addresses to return the time related parameters.

eXtreme DNC Server v4,0,3,0

Cracking eXtreme DNC Server is therefore pretty easy, all of the API responses are passed back via EAX and as none of our emulated code needs to do anything complex we can patch all the CrypKey calls inside Cryp95a.dll, in fact we can devise a generic attack. We know that all the CrypKey functions start by placing in CL a thunk index, here's just one example piece of code :-

:100010DE MOV CL, 17 <-- ckChallenge().
:1000110A CALL DWORD PTR [10001065] <-- HERE is the CrypKey intrinsic weakness.

The address of this CALL can be easily redirected to our own code, we'll then clear ECX, get the thunk index at [EBP-4] and wreak all kinds of emulation havoc using the free space at the end of the .text section. This works for 4 out of 5 of our functions, I've marked a * by all the CrypKey functions that can be emulated in this way, it is only get_restriction_info() that doesn't play, the easiest solution in this case is to rewrite the function. All that remains is to crack the 3 application side checks on ckChallenge() and this example of 'product visionary' is gone forever.

CrypKey Emulator (so far, offset 0x32B0, 10003EB0)

MOV CL, [EBP-4] ; Get thunk code.
CMP CL, 9 ; crypkeyVersion()?.
JNZ $+6
MOV EAX, 2Bh ; Correct version.
CMP CL, 17 ; ckChallenge()?.
JNZ $+6
MOV EAX, 1234 ; For recognising application side checks.
XOR EAX, EAX ; Clear CrypKey status by default.

get_restriction_info() (offset 0x51D)

MOV ECX, [ESP+10] ; dd NumDaysUsed.
MOV ECX, [ESP+0C] ; dd NumDaysForEvaluation.
MOV AX, 1Eh ; 30 always.
XOR EAX, EAX ; Clear CrypKey status.

I noticed whilst patching that the DWORD address which the CrypKey functions thunk down via (10001065) was stored immediately after the null-terminated string 'lcryp95_ThunkData16', we'll investigate in the next example whether this is a weakness too.

VIA 5.1b

VIA is my second target using CrypKey, the program allows a 14-day trial of the utilities and AutoCAD components. I started out using Filemr32.exe as a target (it was small and convenient), I selected License/Authorize and saw the familiar CrypKey Lock # / Authorization boxes, I also ran Skw.exe included in the CrypKey demonstration and pasted this code into the generator as the Site Code and got a very interesting response, Skw.exe knows that this key is from another company, the best payoff however is still to come, I knew from many re-installs that the site code key you are prompted with isn't constant .... so theres a random element and an algorithm somehow assigning particular site codes to a CrypKey customer, moreover Skw.exe evidently knows about customers keys too.

Before jumping ahead, lets run Filemr32.exe through BugTrapper, there are no StringRef's to CrypKey dll's, however browsing the imports you'll see some rather intuitively named imports from Viafuncs.dll --> Cryp95a.dll, you'll get some strange message box errors in BugTrapper but you can just cancel them without any problems, I recommend you also select the licensing options from the menu's whilst BugTrapper is running.

From the log you'll see that as well as the CrypKey functions we saw in eXtreme DNC Server we have others which can now be documented, they are :- GetNetHandle(), get1_rest_info(), get_num_copies(), get_num_multi_users(), SetNetHandle() & end_crypkey(). Lets examine them inside SoftICE. GetNetHandle() takes no parameters and I presume by its name and structure returns a handle to a CrypKey network license server, SetNetHandle() probably performs some initialisation, regardless with no CrypKey license server installed both of these return 0. get1_rest_info() takes a single parameter via ESI and is quite peculiar although it is very interesting, the function is called 3 times, I've tabulated the results below :-

ESI | Response
 1       1
 2      0xE
 3       0

By trial and error I quickly discovered that the first response determines the type of license, 0 is a full license (however its not authorized so we are kicked out somewhere else) and 1 is a trial by days, the second return is the number of days we are licensed for and the 3rd the number of days used, in effect its the same as get_restriction_info(). get_num_copies() & get_num_multi_users() are very simple indeed and take no parameters, they return via EAX their namesake i.e. the number of licensed copies & multi-users. We didn't see end_crypkey() in eXtremeDNC Server, I strongly suspect the developers were supposed to call it, or the API guide suggested doing so and they forgot. get1_rest_info() raises an important question, if we allow it to return EAX=0 we also get to document get_option() & get_level() which are called before we are fired with the nagscreen.

get_option() takes 2 parameters, one I assume must be an option number, this is one of the few API's that breaks with CrypKey's API tradition of liking 0, 1 is required for the option to be valid, get_level() takes a single parameter (like get_option() this was 0x16), a 0 of course indicates unlimited usage. Examining VIA therefore enables us to make significant improvements to the emulation, get_num_copies(), get_num_multi_users() & get_option() can all be emulated.

CrypKey Emulator (enhanced)

MOV CL, [EBP-4] ; Get thunk code.
CMP CL, 5 ; get_option()?.
JNZ $+4
CMP CL, 7 ; get1_rest_info()?.
JNZ $+23
MOV ECX, [ESP+4Eh] ; Get response code.
CMP ECX, 1 ; Option type.
JNZ $+4
INC EAX ; Limited by days.
CMP ECX, 2 ; Number of days we can evaluate.
JNZ $+6
MOV EAX, dd NumDaysForEvaluation.
CMP ECX, 3 ; Number of days used.
JNZ $+3
XOR EAX, EAX ; Precautionary.
CMP CL, 9 ; crypkeyVersion()?.
JNZ $+6
MOV EAX, 2Bh ; Correct version.
CMP CL, 13 ; get_num_multi_users()?.
JNZ $+6
MOV EAX, num_multi_users
CMP CL, 14 ; get_num_copies()?.
JNZ $+6
MOV EAX, num_copies
CMP CL, 17 ; ckChallenge()?.
JNZ $+6
MOV EAX, 1234 ; For recognising application side checks.
XOR EAX, EAX ; Clear CrypKey status by default.
XOR EDX, EDX ; Clear EDX by default.

If you've done all this correctly then the program will never expire (remember you've got to manually patch past the ckChallenge() checks or crack them permanently application side), if you wan't to eliminate the nag screen then removing the emulation code for get1_rest_info() is the thing to do. Licensing information will only be available if you patch get_restriction_info() as described above and this throws up a problem with the stack, with VIA this is anomalous because the VIA developers wrote wrapper functions to the CrypKey API and the stack is balanced application side.

It is this fact that effectively compromises VIA's protection or the use of CrypKey anyhow, a cracker simply does not need to touch the CrypKey dll's, attacking the calling procedures is the easiest way to go.

Conclusions - Part 1

Well I hope you've seen how flawed the entire implementation of the CrypKey protection system is, also my documenting of many of the CrypKey functions should help if you ever stumble across this protection, remember too the emulator approach, search with your HEX editor for 'lcryp95_ThunkData16' and redirect the following DWORD to your own routines.

In Part 2 (currently being polished) I investigate the usefulness or (not) of the CrypKey evaluation, I also discuss some of the encryption used by save_site_key() for validating an authorization code, if you've never seen FP operations under 16-bit code using INT 3D then take a look yourself :-).

Return to Main Index

© 1998, 1999, 2000 CrackZ. 7th June 2000.