http://www.mastercam.com
:- Webpage (CNC Software).
Reference :- LND's release of Mastercam v8.1 BETA 2 (5th December
2000).
A brief read of Mastercam's webpage should perhaps be in order before you start out or consider working with this document, you'll note under the corporate relations how Mastercam has recently joined the ranks of the BSA, one suspects more as a sop to the corporate shareholders than a legitamate attempt to prevent piracy. Bearing this in mind there will be no ready made cracks here, in fact not a single code snippet (well maybe just a few for reversing illustration).
There are two parts to this discussion, Mastercam's core protection is a MemoHASP-1 dongle, this has been the choice method certainly since v7.x and remains the case today, as it turns out the most interesting part will be the various access codes one has to insert to use several options (I discuss only the STEP file convertor here, however the various axis milling options and AutoCAD option are virtually the same). Historically CNC have stuck with the HASP despite its being cracked repeatedly, v8.x sees the introduction of a NetHASP too. In v7.2 they also tried FLEXlm, of course several licenses soon turned up and v8.1 appears to abandon it for good, as we will see, although neither protection is particularly strong FLEXlm was certainly the weakest link.
Mastercam uses a MemoHASP-1 dongle, white in colour with 112 bytes of memory, you can try reversing it from the main programs but its honestly not worth the effort in code bloat when one can use the very convenient utility CNC Software supply (hasp80.exe), the core of the protection centres around service 50 (32h) which reads word blocks from the dongles memory, hasp80.exe calls only services 1,5 & 50 as I recall, you will need emulation for service 2 haspcode() and service 6 haspID() in the main programs.
As I recall a total of 20h words are read from the dongle (either 0 through 14 or 15 through 20), with hasp80.exe one can work out many of the words, the dongle serial number, the version level flags and user type for example. In essence patching hasp80.exe is not really necessary, its sole purpose is to provide information for the main programs. The main programs must all have their hasp() routine emulated, service 2 is called with a random seed code (of 5), 6h, 64h, 395h, 4923h & FB0Dh if I remember rightly.
Crude arithmetic (similar to the code they suggest in the HASP developers manual is used to check these returns). If you worked at hasp80.exe you will only have about 6 words which you won't know, words 3 & 0Dh are used in checksum calculations and can be safely set to 0, words 1 & 2 are logic NOT checksums of the dongle ID and words 0 & 12h are additional checksums. I'm not going to show you the code for these, after readng a block from the HASP it isn't far to trace to find them.
Emulator in place you should now be past the HASP protection, or at least you think you should be.....this next trap kept me entertained for quite a while, just try to do anything in Mastercam and you'll see immediately theres something very wrong. Indeed CNC Software have implemented integrity checks on the disk file (not the memory image), this actually adds a fair amount of time to the cracking process since the integrity checks are performed only after the program is satisfied with the HASP. To recover therefore the correct integrity values one must appear to have an unpatched disk copy of the program, the long way to do this is evidently breakpointing the hasp() routine, patching in manually all the responses for each service (called at least 3 times) and recover the correct values, this is the way CNC would like you to do it anyhow.
IceDump to the rescue, note in the latest version the /haspcode option for decrypting quickly envelope protected files, you could use it in limited capacity here, maybe it should be extended for other services :-). What you will need is your emulation routine in a file and IceDump's /load switch, breakpoint the hasp() routine, /load in your emulator code and 'find' the integrity checks. I made that last part sound simple, finding the integrity checks isn't though and disassembling all the main programs isn't so much fun. I've developed 2 methods, the pure and savvy method :-
Pure :- Disassemble the programs and search for instances of qsort(), I've never seen more than 2 references and the integrity checks are usually in close proximity.
Savvy :- For some compiler reason the integrity routine seems to always be compiled with EBP as the counter and its initialised to 4 nearby, sometimes a search for just CMP EBP, 4 or MOV EBP, 4 is good enough. One other method is to search in your hex editor for the StringRef 'viewhl.c', compute its offset then search for a sequence where that offset is pushed, e.g. 'viewhl.c' found at 00ABCDEF, search for opcodes '6A EF CD AB 00' i.e. push 00ABCDEF. The integrity checking computes 4 32-bit values from the disk file and copies them into a structure, I suggest you redirect this code elsewhere and patch in the correct values using EBP (the counter) as your guide, in Mill8.exe (v8.1 Beta 2) these values are D84B8D4Bh, D4CF8F74h, FFFFFFFFh & 1AA3C7F5h, the remaining programs must be altered accordingly. This concludes the HASP removal.
This proved to be the most interesting part of the protection. If one selects file, convertors & STEP you will be prompted for an access code, there are also several other options which work just as this with a very minor alteration. The function which validates the access code is inside stepin.dll. The function has 3 distinct parts, the first is an XOR validation of the keycode itself, if one passes this check the program computes a dongle ID and byte identifier. As you might expect the dongle ID computed from the code must match the HASP ID, the byte identifier controls which option the code is valid for, in STEP's case this is 0xB, (the other options are distinguished by this value, 0x6 for AutoCAD, 0xC through 0xE for axis milling options).
Initial analysis of the function confirms it is expecting a 'long double' access code. The first part is a for loop which works like so (this taken from my IDA database) :-
:100101B6 SUB ESP, 14h ; Set up stack frame.
:100101B9 FLD [EBP+arg_0] ; Load floating point value onto the
stack (80-bit).
:100101BC PUSH EBX
:100101BD MOV EBX, [EBP+arg_8]
:100101C0 PUSH ESI
:100101C1 PUSH EDI
:100101C2 MOV ESI, 4
:100101C7 MOV EDI, 5 ; Loop counter.
:100101CC FMUL DS:DBL_10011238 ; Multiply st(0).
:100101D2 FSTP [ESP+20h+var_10] ; Floating point store.
:100101D6 MOV EAX, D, [ESP+20h+var_10+4]
:100101DA MOV ECX, D, [ESP+20h+var_10]
:100101DE PUSH EAX
:100101DF PUSH ECX
:100101E0 CALL ds:floor ; MSVCRT.DLL.
:100101E6 FSTP [ESP+28h+var_8] ; Floating point store.
:100101EA FLD [ESP+28h+var_10] ; Load floating point value.
:100101EE FSUB [ESP+28h+var_8] ; Subtract from st(0).
:100101F2 FMUL DS:DBL_10011230 ; Multiply st(0).
:100101F8 FSTP [ESP+28h+var_28] ; Floating point store.
:100101FB CALL DWORD_1001B4C4 ; Add 0.5 and floor().
:10010201 FLD [ESP+28h+var_8] ; Load floating point onto stack.
:10010205 MOVSX EDX, SI
:10010208 ADD ESP, 8
:1001020B DEC ESI
:1001020C DEC EDI ; Decrement loop.
:1001020D MOV [EDX+EBX], AL ; Save byte.
:10010210 JNZ loc_100101CC
The loop divides the access code by 256, generates a byte by multiplying the right hand side of the decimal point by 256 and the left hand side is fed back into the loop. The end result is an array of 5 bytes (or chars). When this function returns the 2nd and 4th array entries are pushed to a function which performs AND and shifting operations, its worth a look at this code too since it has some important implications :-
:100100AB MOV ECX, [ESP+arg_0] ; The array.
:100100AF AND ECX, 0FFh ; Only a specific char.
:100100B5 MOV EAX, ECX ; Make a copy of it.
:100100B7 AND ECX, 55h
:100100BA SHR EAX, 1
:100100BC AND EAX, 55h
:100100BF SHL ECX, 1 ; Use both copies.
:100100C1 OR EAX, ECX ; Add them together.
:100100C3 RETN
The key part to this function is its reversability, the end result is an important byte in the scheme (a logic NOT is applied to the result later on). Can we therefore recover the input to the function by knowing just its output i.e. is it a 1 to 1 mapping? and can we then write an inverse. The easy way is to feed some values into the function and look at the results, it certainly seems to be 1 to 1, the logic AND 55h is equivalent to turning alternate bytes off. This is where some thought to the compiler might help, this code above could well be written as :-
char x = results[1]; // 2nd array entry.
char y = x; // Make a copy.
x = (x & 85) / 2;
y = (y * 2) & 85;
y += x;
return y;
This sort of code gets optimised readily by most compilers and implied to me that the author (who presumably has an access code generator) did not require this function to be reversed, i..e. the generator knows the input prior to calling this function and does not work back from the output. The result from this function is ^ed with the byte generated from the 4th array entry (same function as shown above, just substitute the AND value 55h for 33h and the shifting value 1 for 2). The 3rd array entry is passed to a function which multiplies by 8 and adds this result mod 256, the lower byte (i.e. the part less than FFh) is ^ed with the result from the 2nd ^ 4th. This final resulting byte is ^ed with the 1st array entry and compared to the 5th, if they match the program calculates the dongle ID and checksum byte for this code.
In order to really understand the full reversal you need access to the target itself. You may try to follow though, this is the code that calculates the dongle ID and checksum byte :-
:1001016D MOV BL, BYTE PTR [ESP+4+arg_0+1] ; BL = 4th
array entry.
:10010171 MOV CL, AL ; CL = 1st array entry.
:10010173 XOR CL, BL ; ^ them (result in CL).
:10010175 MOV BL, BYTE PTR [ESP+4+arg_0+2] ; 3rd array entry.
:10010179 MOV DL, AL ; DL = 1st array entry.
:1001017B XOR CL, 5Ah ; ^ (result is checksum byte).
:1001017E XOR DL, BL ; 1st ^ 3rd (result in DL).
:10010180 MOV BL, BYTE PTR [ESP+4+arg_0+3] ; 2nd array entry.
:10010184 XOR AL, BL ; 1st ^ 2nd (result in AL).
:10010186 XOR DL, 0F0h ; ^ = low byte of dongle ID.
:10010189 XOR AL, 0Fh ; ^ = high byte of dongle ID.
This is the core code, there follows some register clean up and result storing but this is the interesting part. The task therefore is to reverse the array entries required to generate our desired checksum byte and dongle ID, calculate accordingly the correct 5th array entry for the first ^ checksum and then reverse the loop to find the correct access code. The following C source code (compiled under lcc-win32) is your reference (by changing dongle_id or ckbyte one can generate codes for each of the options and any dongle ID).
/* Mastercam v7.x/v8.x access code Generator. @author: CrackZ @date: 05/12/2000. */ #include <stdio.h> int main(void) { // ckbyte = 6 (Adv. AutoCAD), 12 (4 axis mill), 13 (5 axis mill), 14 (C-axis). int ckbyte = 11, results[5]; // Results array. printf("-----------------------------------------\n"); printf("Mastercam access code Generator by CrackZ\n"); printf("-----------------------------------------\n"); // Establish the 2 bytes required for the dongle ID. int dongle_id = 12345; int i = 0; do { dongle_id -= 256; i++; } while (dongle_id >= 256); i ^= 15; dongle_id ^= 240; results[0] = 1; // Set the first result to 1. // This could be considered a seed. results[1] = 1 ^ i; results[2] = 1 ^ dongle_id; ckbyte ^= 90; results[3] = 1 ^ ckbyte; // Use the second array entry. int j = results[1]; int k = results[1]; j = (j / 2) & 85; k = (k & 85) * 2; j = 255 - (j+k); // Leave result in j. // Use the fourth array entry. int l = results[3]; int m = results[3]; l = (l / 4) & 51; m = (m & 51) * 4; l += m; // Leave result in l. j ^= l; // XOR resulting bytes. k = results[2] * 8; l = k / 256; k += l; do { k -= 256; } while (k >= 256); j ^= k; results[4] = j ^ 1; // Fifth array entry. double d = 0; d = 256 + results[4]; for (i = 3; i > -1; i--) { d = (d * 256) + results[i]; } printf("\nThe access code is : %Lf\n", d); return 0; }
So there we have it, an access code generator for any of the particular modules we fancy for our fictional dongle ID 12345. Its an interesting scheme but does demonstrate how such validation schemes can be reversed. If you are still confused with the HASP part I've given below a table describing the contents of each dongle word, this is tantamount really to providing you with a ready made solution.
|
|
|
Checksum. |
|
Logic NOT of dongle ID high. |
|
Logic NOT of dongle ID low. |
|
0 (used in making the Checksums). |
|
Dongle ID high. |
|
Dongle ID low. |
|
Version level (Mill) 0x146. |
|
Milling level (6, 5, 4 or 1). |
|
Version level (Lathe) 0x146. |
|
Lathe level (0). |
|
Version level (Wire) 0x146. |
|
Wire level (1 or 2). |
|
Version level (Solids) 0x146. |
|
0 (used in making Checksums). |
|
Version level (Design) 0x146. |
|
User type (4, 3, 2, 1 or 0). |
|
Dongle expiry (0 = unlimited). |
|
Mill Checksum. |
|
Read by program but not used. |
In conclusion then, I found Mastercam to be an interesting target. The protection scheme isn't bad by any standards, I commend CNC Software for at least a protection strategy. As usual my standard disclaimer applies, so BSA, instead of trying to nuke my site from the web again, perhaps you could pass this valuable study of how a protection gets broken to CNC Software, so that they might improve their protection and thus prevent piracy. Whilst perusing Mastercam's web site I also read the following :-
"CNC Software is very excited about becoming a member of the BSA to step up our effort of stopping the piracy of Mastercam. The piracy problem in our industry is too widespread for CNC to address on its own. Having the BSA and its member organization joining in this effort will allow us to recover lost revenues which we will dedicate back toward Mastercam software development. We are honored to be a part of this world organization dedicated to stopping piracy."
Actually, if you read my site a little you'll see there is nothing honourable or beneficial being a member of the BSA :-).