- Webpage (files might be available from me if you ask nicely
I've been watching Cimagrafi from Graphitech for the last 2 years, the protection has always been a MemoHASP-1 dongle which Graphitech update using the HASP RUS facility, this can be seen from both Customer.exe (a very convenient diagnostics program) and the dongle itself (which uses an internal DWORD to tag the version number). In very old v5.0 versions Cimagrafi elected not to use the HASP envelope, this remains the case with their trial version which might be available on the web. Cimagrafi consists of 4 core modules and various accessory dll's which are also protected.
Most of the warez released versions of Cimagrafi have been crudely unpacked and had their HASP routine emulated, the resulting replacement files have virtually always failed to work under any OS except 9x. Early v5.0 builds of Cimagrafi could be broken by rudimentary patching. In this document I'll use once again HASP routine emulating and also show how we can avoid (with some work) unpacking the HASP envelope, the resulting executables will then run happily under any OS. I'll discuss here only Graficad.exe (the other 3 programs work similarly to this), the dll's are currently unenveloped (actually I wonder if HASP's envelope is even capable of protecting them).
The order of events is as follows. Graficad.exe calls its initial HASP routine with services 1 & 2, in this version the initial seed code used is 0x5E3 (passwords are 0x2459 & 0x2CF3), using Icedumps /haspcode switch will enable you to quickly patch in and recover the correct return codes, by patching in the real returns you'll get another message box saying "PLUG NOT FOUND" :-
:006E4854 PUSH EBP
:006E4855 CALL 006E67AF <-- haspreg().
:006E485A POP EBP
There are 2 obstacles therefore, firstly we wan't to have our envelope hasp() routine return the correct values to decrypt the envelope, then we wan't the second decrypted hasp() routine to work as well, all of this without unpacking. Here is how. Firstly lets have a look around in the Graficad.exe for some free 00 bytes, for my purposes I need around 630 bytes for my full hasp() emulator and around 100 for the envelope hasp() call. At raw offset 0x2F5C40 it looks like we have about 600 bytes of free space, this will do for our envelope. Lets sketch out our routine :-
:006FE440 CMP BH, 1 <-- IsHasp()?.
:006FE443 JNZ 006FE449 <-- Try next service.
:006FE445 XOR EAX, EAX
:006FE447 INC EAX <-- EAX=1 (HASP present).
:006FE449 CMP BH, 2 <-- HaspCode()?.
:006FE44C JNZ 006FE496 <-- Unknown service.
:006FE44E CMP ECX, 2459h <-- Is it Cimagrafi's dongle?.
:006FE454 JNZ 006FE496 <-- Wrong dongle.
:006FE456 CMP EDX, 2CF3h <-- Is it Cimagrafi's dongle?.
:006FE45C JNZ 006FE496 <-- Wrong dongle.
:006FE45C CMP EAX, 5E3h <-- Seed code #1.
:006FE463 JNZ 006FE47A <-- Try next seed.
:006FE465 MOV EAX, 8Fh
:006FE46A MOV EBX, 265Dh
:006FE46F MOV ECX, FEFBh
:006FE474 MOV EDX, A17Dh <-- Return codes.
:006FE47A CMP EDX, 5E2h <-- Seed code #2.
:006FE47F JNZ 006FE496 <- Unknown seed.
:006FE481 MOV EAX, D2F8h
:006FE486 MOV EBX, BB7Ch
:006FE48B MOV ECX, 4085h
:006FE490 MOV EDX, F5E8h <-- Return codes.
:006FE496 XOR EAX, EAX
:006FE498 XOR EBX, EBX
:006FE49A XOR ECX, ECX
:006FE49C XOR EDX, EDX <-- Clear all parameters.
Now all we need to do is connect our envelope hasp() to this code. By tracing beneath the hasp() call and subsequently beneath the CALL [EBP+00] instruction at 006E6955 you'll find this code :-
:006F9865 PUSH BP
:006F9867 MOV BP,SP
:006F986A PUSH SI
:006F986C PUSH DI
:006F986E PUSH BP
:006F9870 JMP 006EAF1C <-- really this is CALL.
:006F9875 POP BP
:006F9877 POP DI
:006F9879 POP SI
:006F987B POP BP
The JMP 006EAF1C we'd ideally like to be CALL 006FE440. Firstly we work out the opcodes for our new CALL (E8 CB 4B 00 00), next we pad this to 8 bytes, so we have E8 CB 4B 00 00 66 5D 66. Next in Hiew we go to offset 0x2F1070 (006F9870) and record the 8 bytes currently at this location (64 8B 20 5B 5F 4A 6B C2), now we set a bpm 6F9870 rw in SoftICE to recover the first XOR key which is in EAX. We can see that :-
XOR key from EAX = A4362C8D ^ 5B208B64 (current data) = FF16A7E9
(first 4 bytes of our JMP).
Really we want :- A4362C8D ^ 004BCBE8 = A47DE765 (new data).
By placing these new 4 bytes (remember it will view in your hex editor as reversed i.e. 65 E7 7D A4) we decrypt the correct first 4 bytes of our new CALL, however the nature of the HASP envelope means the next 4 bytes must also be corrected (please note that occasionally it can be a requirement to correct a further 4 bytes and also the scheme isn't necessarily XOR, sometimes its a simple SUB). With a bpm (rw) on 6F9870 you can verify that the next XOR key is A4362CA0. Doing the simple calculations we can work out that :-
XOR key from EAX = A4362CA0 ^ 665D6600 (data we need) = C26B4AA0.
Thus the next 4 bytes which were 5F 4A 6B C2 need to be replaced with A0 4A 6B C2. Voila, the envelope is cracked. Now we can focus in on our 2nd hasp() routine, the one the Cimagrafi developers use :-
:00494732 PUSH EBP
:00494733 CALL 0049666A <-- haspreg().
:00494738 POP EBP
The approach we used before will work again, the only difference is that you will have to bpm one extra time because the XOR decryption occurs in a temporary memory area, note also that this decryption does not slide the key so it is easier to work out the correct bytes. The difference between this hasp() routine and the previous one will be the services called by Cimagrafi's developers, I elected to redirect this to 006FE4A0 since I knew (isn't hindsight a great thing) that certain MemoHASP services weren't called and HaspCode() is only called with a single seed code (0x7CF).
Cimagrafi's dongle protection is based around services 1, 2, 5, 0x32 & 0x33, the most interesting of these are the last 2 as they are block operations. Cimagrafi read 0xE words from the dongle and also verify the status return when writing them back. There are 2 ways to deduce the desired contents, either attack Customer.exe which will give you an indication of all the modules status or deduce the bytes verified by each module. The module by module approach is longer but easier as each time you verify a theory Cimagrafi pops up with a helpful message box.
Herewith I present the dongle contents as I worked them out :-
92 01 00 00 C0 40 07 0F 01 01 01 00 00 00 00 FF
FF FF FF FF FF FF FF FF FF FF FF
The remainder of the dongle memory can be set to 00 although from a dongle dump I have seen in previous versions I think the pattern is repeated later in the dongle. The FFh bytes mainly control module options, the 92 01 word is a signature indicating Cimagrafi's dongle and the 40C00000 DWORD a version identifier. All that remains for you to do is patch in your hasp() emulator to the remaining Cimagrafi protected files (3 .exe's and 15 dll's). Unfortunately though, all of this work will not do you a great deal of good, since Graphitech's product (try the P2P import option to name but one problem) is so bugged it is virtually unusable.