Target :- Customer.exe (included with Cimagrafi v5.07+).
If you are at all au fait with HASP you will know that the protection takes 2 forms, the API through which hasp() services are actioned and Aladdin's fully blown PE encryptor known as the envelope, which relies on response codes from the dongle to do the decryption. I chose Customer.exe for 2 reasons, firstly its an enveloped target as good as any other, and secondly we are here to learn about reverse engineering as opposed to stealing Cimagrafi (a rather good design program which is sadly protected by a HASP).
HASP's envelope checks for the presence of SoftICE using a very familiar trick, CreateFileA for the video driver //./SIWVID, however before we start delving into vast amounts of code I recommend that you PEDump the target.
Section Table 01 .text VirtSize: 00020AEE VirtAddr: 00001000 raw data offs: 00001000 raw data size: 00021000 relocation offs: 00000000 relocations: 00000000 line # offs: 00000000 line #'s: 00000000 characteristics: 60000020 CODE MEM_EXECUTE MEM_READ 02 .rdata VirtSize: 00006194 VirtAddr: 00022000 raw data offs: 00022000 raw data size: 00007000 relocation offs: 00000000 relocations: 00000000 line # offs: 00000000 line #'s: 00000000 characteristics: C0000040 INITIALIZED_DATA MEM_READ MEM_WRITE 03 .data VirtSize: 00006908 VirtAddr: 00029000 raw data offs: 00029000 raw data size: 00003000 relocation offs: 00000000 relocations: 00000000 line # offs: 00000000 line #'s: 00000000 characteristics: C0000040 INITIALIZED_DATA MEM_READ MEM_WRITE 04 _TEXT_HA VirtSize: 0000FC88 VirtAddr: 00030000 raw data offs: 0002C000 raw data size: 00010000 relocation offs: 00000000 relocations: 00000000 line # offs: 00000000 line #'s: 00000000 characteristics: C0000040 INITIALIZED_DATA MEM_READ MEM_WRITE 05 .rsrc VirtSize: 0000E258 VirtAddr: 00040000 raw data offs: 0003C000 raw data size: 0000F000 relocation offs: 00000000 relocations: 00000000 line # offs: 00000000 line #'s: 00000000 characteristics: 40000040 INITIALIZED_DATA MEM_READ 06 .protect VirtSize: 000275F4 VirtAddr: 0004F000 raw data offs: 0004B000 raw data size: 00028000 relocation offs: 00000000 relocations: 00000000 line # offs: 00000000 line #'s: 00000000 characteristics: E0000020 CODE MEM_EXECUTE MEM_READ MEM_WRITENote that _TEXT_HA is the HASP API section, we'll see this later, and .protect the actual envelope, sometimes these sections have their names changed (e.g. .protectzz) yet the envelope always equates to something around 160k. As you might expect the envelope is heavily encrypted (disassembling will give you about 300h of the initial round of decryption which works on the principal of sliding an XOR key through many small rounds of code and then calling the newly decrypted routines instantly).
The decryption is a real mess and there is nothing to be gained in studying it, in fact tracing it too low in SoftICE will result in a horrid crash. What the envelope subsequently does is call its own decrypted hasp() routine to check for a specific dongle before using the response codes to decrypt and hand control to the original program. The first step is to kill the anti-SoftICE trick I described above, after which you should bpx for FreeEnvironmentStringsA which is called in most 32-bit HASP protections immediately after the CALL to the main hasp() routine. Here's how it looks :-
0137:0045A8E5 CALL [EBP+00] <-- This is hasp() for real. 0137:0045A8E8 PUSHAD <-- Save all registers. 0137:0045A8E9 LEA ESI,[0045E1B5] 0137:0045A8EF PUSH DWORD PTR [ESI] 0137:0045A8F1 LEA ESI,[KERNEL32!FreeEnvironmentStringsA] 0137:0045A8F7 CALL [ESI] 0137:0045A8F9 POPAD <-- Restore registers (you break here). 0137:0045A8FA RET 0137:004587E4 PUSH EBP <-- Now set a bpx here. 0137:004587E5 CALL 0045A73F <-- Highest level hasp(). 0137:004587EA POP EBPWhen you return the hasp() envelope routine has just been executed for its first time, its always IsHasp, so set EAX=1 and bpx for the next service. Sure enough the next service is 2 (in BH), ECX holds vendor password 1, EDX vendor password 2 & EAX the seed code. In this case we have EAX=1DFE, ECX=2459 & EDX=2CF3, after the call to hasp() again EAX through EDX = 0, they should actually be filled with the required response codes for the given seed. In all Win32 envelopes I've seen its been possible to recover these responses by simple tracing (not that we actually need to).
Using the HaspCode algorithm we can find the desired responses, so patch in EE2D, 8F91, 8F9F & ED0F, now F5 and you'll get the check repeated, patch in the responses once again. The 3rd check decrements the seed code by 1 so you need to patch in DBA5, BF7E, 1FDA & 62DB (returns for seed 1DFD). It is these values that will be used for the decryption. I recommend you now disable all the breakpoints and bpx for VirtualAlloc, in different versions of the envelope WriteProcessMemory is also extensively used. After this breaks, place the data window at the RVA of the first section (its usually 401000), now watch slowly, the idea of un-enveloping is as follows :-
i) get the raw sizes and offsets of the decrypted sections and dump them to files (I cannot recommend IceDump enough for this purpose).
ii) establish the location of the import table and real entry point.
Tracing slowly through should alert you immediately as to what happens, VirtualAlloc is used to allocate an area of memory (1000 or 8000 usually), the various sections are then committed and decrypted, just trace slowly through this until you can see the critical code.
0137:00453549 MOV EAX,[EBP-0C] <-- Size of allocated memory (0x8000). 0137:0045354C PUSH EAX <-- Push it on the stack for function. 0137:0045354D MOV EAX,[EBP-08] <-- VirtualAlloc area. 0137:00453550 PUSH EAX 0137:00453551 MOV EAX,[EBP-24] <-- RVA of area being allocated. 0137:00453554 PUSH EAX 0137:00453555 CALL 00453830 <-- Committ section and write encrypted data to it. 0137:004535B5 CALL 0045362B <-- Now decrypt it.At the end of this loop you should have noted the following details and have dumped the following :-
Section RVA Size Raw Location Size in bytes ---------------------------------------------------------- .text x401000 x205B3 x1000 132,531 .rdata x422444 x45DF x22444 17,887 .rdata x426B78 x15E3 x26B78 5,603 .data x429000 x2F85 x29000 12,165 _TEXT_HA x430000 xFA01 x2C000 64,001You can now paste these decrypted files over the original bytes, but go very careful when translating the _TEXT_HA RVA into a raw offset in the actual file. If you are doing this part with UltraEdit, I advise you take it slowly to avoid errors.
0137:0045476E MOV EAX,[EBP+08] <-- Real Entry Point (00407EAA). 0137:00454771 PUSH EAX 0137:00454772 POP ESP 0137:00454773 POPAD <-- Maybe worthwhile as a search string.In this case (which is somewhat anomalous), the import table isn't unpacked to its own dedicated section, instead portions of it span both of the .rdata sections we dumped earlier (this means you must redump and paste the entire .rdata section at the entry point 00407EAA, because GetProcAddress is used after the decryption to reconstruct the import table). The other Cimagrafi programs do have dedicated .idata & .reloc sections which means simple fixing in the header will suffice. You can now change the entry point to 00407EAA and our newly decrypted customer.exe will run (you must also decrypt the .protect section because our import table points too it).
Now you should now be able to get customer.exe into IDA, with only a few errors (all in .protect unsurprisingly). You'll also get a lot of library recognition. If you've followed my instructions carefully you can proceed to reverse the protection as normal (services 1, 5,6 & 50). Because of the anomalous nature of this target I described above it isn't actually worthwhile removing the .protect section, although in all of the other Cimagrafi targets it certainly is. There also is actually a good reason to crack this target, intercepting service 50 will allow you to discover a lot about various words and bytes in the dongle :-).
As a general rule when decrypting the HASP envelope, find i) the unpacking routines, ii) the encrypted sections and iii) the import table loop & entry point, once at the entry point you can dump everything and then apply the necessary PE fixups. This should mean that for this type of HASP envelope (version 6/6.1) reconstruction should take no longer than 5 minutes.
Return to Main Index, Dongles.