Version 3.2 Notes
Version 4.25.006b Notes
Version v5.25/6.0 by Underpaid Worker Bee
After much demand I've decided to publish this tutorial. ALLDATA is a specialist program used by motor car service engineers/shops to diagnose faults and order parts for maintenance. In fact my showing you how to crack it black and blue by emulating the main hasp() routine won't cost the company anything, without the 20 or so data CD's which compliment the program a crack is pretty much useless. ALLDATA uses a TimeHASP dongle (blue in colour), this features a real-time clock (a slight problem to emulate as it turns out) and 16 bytes of memory.
In HASP tutorials before I've recommended other approaches but in this instance we'll cut right to the chase. A bpx on FreeEnvironmentStringsA will always find you the main hasp() routine in any 32-bit HASP, better still the HASP service trademark "cmp bh, 32" is even better for deadlistings. So grab W32Dasm and disassemble ace.exe (I have my own program for finding the HASP scheme so you can rest assured this is where its at). Here's the main routine, (just memorise this code pattern) :-
:0041AE4F CMP BH, 32 <-- Check for higher HASP services.
:0041AE52 JB 0041AE59
:0041AE5F CALL 0046524F <-- Main HASP() routine.
:0041AE68 MOV DWORD PTR [EDI], EAX <-- Par1 returned and stored.
:0041AE6D MOV DWORD PTR [EDI], EBX <-- Par2 returned and stored.
:0041AE72 MOV DWORD PTR [EDI], ECX <-- Par3 returned and stored.
:0041AE77 MOV DWORD PTR [EDI], EDX <-- Par4 returned and stored.
This is a single instance of the hasp() routine, if you search for "cmp bh, 32" in W32Dasm you'll also find it at address 004A1A9F. ALLDATA is slightly different to some HASP's in that you'll need to trace the CALLer's higher, you'll soon find the real main callers at address 004A0206, there are 16 calls into the dongle (all of which we will emulate).
Lets take a look at them, or moreover ready your copy of haspman.pdf :-).
:004A013E PUSH 00006B47 <-- HASP Password 2.
:004A0143 PUSH 00003815 <-- HASP Password 1.
:004A014A PUSH 00000000 <-- Seed Code.
:004A014C PUSH 00000001 <-- Service 1 (IsHasp()).
:004A0153 MOV EAX,[EBP-10] <-- EAX holds return Par1 value.
:004A0156 CMP DWORD PTR [EAX+18],00 <-- It should be 1.
:004A015A JNZ 004A016F <-- Dongle connected jump.
This is incredibly stupid, you don't even need to use SoftICE because the HASP passwords are laid out in front of your very eyes, besides which hasp() service 1 only returns 1 parameter (a 0 or 1). Emulating this service won't prove any problem, we'll just MOV EAX, 1 or XOR EAX,EAX / INC EAX and there will always be a HASP present. Lets follow the code execution.
:004A018B PUSH 00006B47 <-- Password 2.
:004A0190 PUSH 00003815 <-- Password 1.
:004A019C PUSH 00000135 <-- and give the cracker the seed code.
:004A019E PUSH 00000002 <-- Service 2 (HaspCode).
:004A019E CALL 004A0206 <-- DumbHASP routine.
:004A01A3 MOV EAX,[EBP-10] <-- EAX is Par1.
:004A01A6 CMP DWORD PTR [EAX+18],0000B940 <-- Check return code.
:004A01AD JZ 004A01C2 <-- Good dongle.
Again, the structure is too easy and I think you ought really verify another return code developers :-), manipulate it a little or do something. Again this will present us with no problems, the return codes are Par1=B940, Par2=1240, Par3=D824 & Par4=3865. Lets examine all the other calls, I've listed them below with some notes.
Address HASP Service Code Notes -------- ----------------- ----- 004A0350 49h (73) GetDate 004A03BA 47h (71) GetTime 004A0493 4Dh (77) ReadBlock 004A0515 4Ch (76) WriteBlock 004A05DA 1 IsHasp 004A062A 2 HaspCode (different passwords used 7A75 & 1EB3). 004A06F6 32h (50h) ReadBlock (also 004A0A8E) 004A0784 33h (51h) WriteBlock (also 004A0B1C) 004A084C 1 IsHasp 004A089C 2 HaspCode (different passwords again 4AD2 & 40C9). 004A093F 49h (73) GetDate 004A09A9 47h (71) GetTime
As you can no doubt see ALLDATA's developers certainly made their protection secure by checking the dongle a lot. Lets think about our emulation routine, one of our potential problems are the 2 calls to Service 2 using different Passwords, this is a smart move because our emulation routine must be able to identify which set of return codes are to be used. Services 32/33/4C/4D will prove trivial, all we'll need to do is XOR ECX,ECX (Par3) and return. This leaves us with the TimeHASP specific services 47/49 which both return 4 parameters, we'll need to see how these are checked, obviously emulating the HASP real-time clock is not for the scope of this essay (however both GetLocalTime & GetSystemTime are imported so it isn't that difficult by any means).
I believe the easiest thing we can do with services 47/49 is too patch in a valid set of return codes and then crack the verification checks which are most likely elsewhere (obviously tracking our own values will be easy). The service 2 problem can actually be gotten around easily because the registers hold the HASP passwords (ECX Password1, EDX Password2). Lets construct the emulator code (recall BH holds service number).
0137:0046524F 80FF01 CMP BH,01 <-- Service 1 (IsHasp).
0137:00465252 7507 JNZ 0046525B
0137:00465254 B801000000 MOV EAX,00000001 <-- Par1 = 1.
0137:00465259 EB7C JMP 004652D7
0137:0046525B 80FF02 CMP BH,02 <-- Service 2 (HaspCode).
0137:0046525E 7534 JNZ 00465294
0137:00465260 81F915380000 CMP ECX,00003815 <-- Determine passwords used.
0137:00465266 7416 JZ 0046527E
0137:00465268 B8095B0000 MOV EAX,00005B09 <-- Par1.
0137:0046526D BBBF9D0000 MOV EBX,00009DBF <-- Par2.
0137:00465272 B997480000 MOV ECX,00004897 <-- Par3.
0137:00465277 BAF3910000 MOV EDX,000091F3 <-- Par4.
0137:0046527C EB5B JMP 004652D9
0137:0046527E B840B90000 MOV EAX,0000B940 <-- Par1.
0137:00465283 BB40120000 MOV EBX,00001240 <-- Par2.
0137:00465288 B924D80000 MOV ECX,0000D824 <-- Par3.
0137:0046528D BA65380000 MOV EDX,00003865 <-- Par4.
0137:00465292 EB45 JMP 004652D9
0137:00465294 80FF32 CMP BH,32 <-- Service 32 (ReadBlock).
0137:00465297 7506 JNZ 0046529F
0137:00465299 33C9 XOR ECX,ECX <-- Clear Par3.
0137:0046529B 33DB XOR EBX,EBX <-- Clear service.
0137:0046529D EB3A JMP 004652D9
0137:0046529F 80FF33 CMP BH,33 <-- Service 33 (WriteBlock).
0137:004652A2 7506 JNZ 004652AA
0137:004652A4 33C9 XOR ECX,ECX <-- As service 32.
0137:004652A6 33DB XOR EBX,EBX
0137:004652A8 EB2F JMP 004652D9
0137:004652AA 80FF47 CMP BH,47 <-- Service 47 (GetTime).
0137:004652AD 750E JNZ 004652BD
0137:004652AF 33C0 XOR EAX,EAX <-- Seconds.
0137:004652B1 33DB XOR EBX,EBX <-- Minutes.
0137:004652B3 43 INC EBX <-- 1 Minute.
0137:004652B4 33C9 XOR ECX,ECX <-- Status.
0137:004652B6 BA08000000 MOV EDX,00000008 <-- Hour.
0137:004652BB EB1C JMP 004652D9
0137:004652BD 80FF49 CMP BH,49 <-- Service 49 (GetDate).
0137:004652C0 7513 JNZ 004652D5 <-- Then it was 4C/4D.
0137:004652C2 B801000000 MOV EAX,00000001 <-- Day.
0137:004652C7 BB03000000 MOV EBX,00000003 <-- Month (March).
0137:004652CC 33C9 XOR ECX,ECX <-- Status.
0137:004652CE BA63000000 MOV EDX,00000063 <-- Year (99).
0137:004652D3 EB04 JMP 004652D9
0137:004652D5 33C9 XOR ECX,ECX <-- Clear Par3.
0137:004652D7 33DB XOR EBX,EBX
0137:004652D9 C3 RET <-- Return from emulator.
Although this would seem to do all that we need there are evidently several screws loose when we run ALLDATA, these problems evidently occur because of our emulator (in fact I made a silly error here!, there are actually 3 calls to service 2 and I've emulated only 2 of them), I'll leave that correction to you (Par1=4EB7, Par2=EB6C, Par3=BC58 & Par4=9B84). Naturally I chose the lazy way to fix this *see below*.
:0049FA1A JZ 0049FA2A <-- Force to jump (EB 0E + padding
Patching this solves one of our problems, we must now test the data CD's compatibility (btw I'm using a CD from May 1998). Obviously by having our emulator report the 1st March 1999 any checks on the CD date validity will need fixing. There appear also to be several options (Emissions, Estimating & Network) which will prove trivial to enable. To eliminate these headaches we'll just bpx for the emulator code.
The time expiry on the CD's can be fixed by patching 0048B5EA & 0048B621 to JMP. Using the StringRef's we can also enable the aforementioned 3 options, NOP the 3 conditional jumps at (00426F96, 00426FB7 & 00426FD8 and force the obvious JNZ at 0047AA28). This completes our work and now we can use the Estimating option as well as any legacy CD's all without the dongle, you'll also find that by "emulating" the main dongle routine the program runs significantly faster :-).
For a TimeHASP this isn't a bad protection, lots of checks mean that "heuristic debugger users" are going to fail miserably, this is also one of the few HASP implementations I've seen that supports different HASP passwords. Sadly there are some inherent weaknesses, after disassembling with W32Dasm I found the protection in a matter of seconds and the actual checks aren't strong enough, force crackers to work backwards and understand the code completely, don't just blindly pass your arguments to hasp(), generate them using maths routines, and check at least 2 of the returns, except delay the checking of the 2nd one.
There is no doubt in my mind that ALLDATA is a worthwhile product, anyone who assembles 20+ CD's full of car information gets my respect, only legitimate or previous users of this program would benefit financially from cracking it. I will not release any ready-made crack, if you are determined enough you can at least earn the right to use it.
After reversing v3.0 of ALLDATA last year, many end-users e-mailed me with questions about how to test the program without the HASP. On reflection, my discussion of emulating the HASP (see above) was rather brief and assumed a certain amount of pre-requisite knowledge of the various HASP services.
ALLDATA v3.2 represents little change from v3.0, what I will present here is a much more 'reversing orientated' approach and I'll show how instead of doing untidy patches to enable the options we can fully reverse the dongles contents and code a single emulation routine. The dongle used by ALLDATA v3.2 is a TimeHASP, this has a real-time clock and 16 bytes of memory, whilst studying this target I discovered that hard-wiring in bad dates and times is a very bad thing to do especially zero'ing all the return parameters, doing so will wind you up with page faults at several addresses.
The 5 main services called are 1, 2, 47, 49 & 4D. Service 1 is trivial to emulate and Service 2 is the old HASP seed code algorithm (the new HASP 4 is different!), the seed codes remain unchanged so you can refer to the v3.0 tutorial to save my typing. The 4x services are TimeHASP specific and take note here that service 4Dh reads byte blocks and not words as per MemoHASP. To emulate services 47 & 49 the easiest and most convenient thing to do is to CALL GetLocalTime() (already imported by ALLDATA), the resulting structure provides all the responses needed by these services (see Barudan Punchant's tutorial for the code).
Service 4Dh reads a block from the dongle, usually its 4 bytes but on one occasion its 10h (the full dongle contents). These contents control various options and aspects of ALLDATA. In v3.0 I patched the option checks for Estimating, Network & Emissions, in fact this is controlled by a DWORD from the dongle, just below is the layout I'm using as notation :-
01 01 02 02 03 03 04 04 05 05 06 06 07 07 08 08 <--
8 words == 16 bytes.
The option DWORD is formed from the bytes 04040303 in the diagram above. This is AND'd with hard-wired in values to determine option availability, with basic knowledge of the AND logic gate and a few seconds in SoftICE/W32Dasm one can deduce 60001500h as being one of many valid DWORD's for all 3 options. The dongle Key ID is controlled by the DWORD 02020101 and is converted to decimal, you can patch in something prominent if you so wish.
05050606 & 07070808 control the disk sets you are allowed to use. 07070808 can actually be set to 0, its used as a 2nd DWORD option to deal with the possibility that you might have only specific manufacturers disks licensed. The lower byte of 0505 controls the disk set option, the range of possible values are shown below :-
01 = Standard, 02 = Parts, 03 = Undercar, 04 = DDB, 05 = Year range (All), 06 = Date range (All), 08 = Undercar Range
The higher byte if below C8h toggles a specific disk set, so 08 toggles disk set 8 (Ford Chrysler), 0Ah disk set 10 (General Motors) etc, when above C8h this byte toggles the range of Makes :-
C9h = All, CAh = Domestic, CBh = Import, CCh = GM, CDh = Ford, CEh = Chrysler, CFh = Asian, D0 = European.
It is clear therefore that 0505 should be C905 for maximum use (unless you have some pressing desire to limit your usage).
0606 controls the dates of valid disk sets, this is of critical importance if you want to use old ALLDATA CD-ROM's, thats the main criticism many e-mail correspondents seem to have with ALLDATA, they charge a fairly hefty subscription to continually update the CD set and most customers find there are very few benefits, I guess because most customers in the real world don't own new cars.
Initially the best option looks to be 'Latest Discs' but this is not the case if you want to use previous data CD's. As mine are from 1998 this code must be examined. I won't describe the maths (004235BC onwards), its pretty ordinary and 7A01 is one possibility of several you might consider. All that you need do now is write a HASP emulation routine in the usual fashion as we have established what our 16 bytes of dongle memory should be.
After much demand from several legitimate ALLDATA users, I've looked at this fairly recent version too :-). Several small things have changed. ALLDATA from as far back as I can remember have always had several HASP options (I recall at least 3 password pairs sometime back in v3.0). v4.25 seems to support yet more HASP's, clear proof if you ever needed any that ALLDATA are buying far too many HASP's and not really learning a great deal each time. I didn't count all the password pairs as I've been using 3815/6B47 for every version thus far, I know this particular pair is a TimeHASP. I think there are maybe 6 valid pairs and possibly a random number of fake calls. Scanning the disassembly I reckon some of these 'new dongles' are MemoHASP's (there are certainly references to services 50/51), in fact if I'd read the help file (duh!) I'd know that they are MemoHASP-1's ('blue & white keys').
v4.25 must still support the TimeHASP I describe above, however I'm pretty sure there is now an activation code required to keep the subscription active, the core contents of the dongles memory have not changed, the positions I describe in v3.2 are still current. The reason for my assumption that the authorization code is now needed relates to the starting date of the subscription (controlled using a single word). I looked briefly at the code that calculates the 2 dates (start and expire) and there would seem to be no word value that will ever produce a starting date which is earlier than the expiry date, there is also no way it seems to validate data CD's from before 1988.
This means for v4.25 we cannot emulate completely the HASP's contents, there is a necessity to make a small additional patch (described here) :-
:00424D4E ADD EBX, 000007C4 <-- Expiry Date.
:00424D54 ADD EDI, 000007C4 <-- Starting Date.
If you change the ADD EDI, 7C4h to something more appropriate, like ADD EDI, 78Bh you can fix the starting date to 1978.
There are also 2 other small changes, ALLDATA's developers call HaspCode() with a few more seed codes (just emulate HaspCode() and this isn't an issue), there is also a change regarding the option DWORD, ALLDATA v4.25 appears to have a new option named CSP, I don't know what this is, if I were to hazard a guess I'd say it was some sort of support option, changing the DWORD from 60001500h to 60003500h enables it. (Ace.exe, 1,577,984 bytes).
Its always interesting to follow the evolution of a protection scheme ... I used CrackZ notes from previous versions to help me with this, since its only my 2nd attempt at a Windows program. Lets say I was on a long hiatus, and Alldata with HASP intrigued me enough ... After reading about some of the things possible with HASP, and some of Alldata's previous protections, I was mightily disappointed with this protection. No envelope, no self modifying code, no SoftICE checks, no CRC on the executable. This took me about 3 days to crack.
The hasputil routine was at 478808 in this version.
Using SoftICE, set a bpx on freeenvironmentstringsa. When it breaks at an address inside ace.exe, you can set a bpx on 478808 or 47AA11.
BH will have the function call.
01 - is a hasp key connected. Returns 01 in eax
02 - check key by using valid HASP passwords. ECX and EDX will have the password. EAX will have a seed number. after the call, EAX should contain a check value based on the seed. There are a bunch of fake? calls made along with the real ones. (Some of them might be calls to check a different dongle version; ie MemoHASP, etc).
Current valid passwords : 40C9 / 4AD2, seed 135 returns 4EB7
40C9 / 4AD2, seed 53 returns A2FF in EAX, (3815, 6B47 was an older one).
47 - Get key time. Returns EAX - seconds, EBX - minutes, EDX
Tricky, tricky weasels.. They do multiple checks on this routine to make sure the key timer is working. You must increment the seconds on every call in your emulation routine!.
49 - Get key date. Returns eax - day ebx - month edx - year
32 - Read a block of memory from the key. ESI will contain the number of words to read. EAX will contain the address to store them to. When ESI = 2, just return the key number. When ESI = 7C, return the following structure :
01 00 00 00 FC 35 12 60 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 05 C9 02 FC 00 00 00 00
The key I borrowed was from v6.0. All these fields were blank, as you'll see in the next doc. So, I had to find most of this by searching the code.
01 00 00 00 - the key number.
FC 35 12 60 - FC and 12 make up the 'key valid until' dates. 35 and 60 turn on all functions : Emissions, CSP, Estimating, Network.
05 C9 02 FC - 05 turns on all models sets. C9 turns on all makes. 02 and FC turn on the valid dates for the discs you can use.
After each return from one of the above calls: 47,49,32, you must zero out ecx.
You can use 16585 and 19154 as the keys in hasputil to view an Alldata HASP 4 dongle. (Try 14357 and 27463 if they don't work). Here's the routine :
478808: cmp bh,1 jnz 478812 mov eax,1 cmp bh,2 jnz 47883d cmp cx,40c9 jnz 47883d cmp dx,4ad2 jnz 47883d cmp ax,135 jnz 478832 mov eax,4eb7 jnz 47883d cmp ax,53 jnz 47883d mov eax,a2ff cmp bh,47 jnz 478856 inc eax cmp eax,3c jl 47884f mov eax,0 xor ecx,ecx mov edx,8 cmp bh,49 jnz 47886c mov eax,1 mov ebx,1 xor ecx,ecx mov edx,66 cmp bh,32 jnz 478900 cmp esi,2 jnz 478881 mov dword ptr ds:[eax],0102 cmp esi,7c jnz 478900 mov dword ptr ds:[eax],01000000 mov dword ptr ds:[eax+4],601235fc mov dword ptr ds:[eax+8],00000000 mov dword ptr ds:[eax+c],0 mov dword ptr ds:[eax+10],0 mov dword ptr ds:[eax+14],0 mov dword ptr ds:[eax+18],0 mov dword ptr ds:[eax+1c],0 mov dword ptr ds:[eax+20],fc02c905 mov dword ptr ds:[eax+24],0 xor ecx,ecx ret 478900 xor ecx,ecx 478902 ret
Some notes : A little more fun, and a lot tougher - at least for me. Alldata has completely revamped their protection from the last few versions. If you look at an actual HASP dongle from version 6.0, it only has 4 bytes of data in it - these happen to be the HASP key number itself. There is still no envelope on the code, no self modifying code, no encrypted code and no CRC check. To top it off, all the protection is in a module called... take a guess... productsecurity.dll.
So, this should have been easy right? Well, for some people maybe... but 2 weeks later... and its still not finished. It might have been easier if I had a valid subscription key, but I didn't. And just in case Alldata is reading, the HASP dongle ID used in the analysis below is made up. (And take some of the advice above, please), this isn't going to be a full fledged tutorial; they take too long to write...
When you start the program, it probes the dongle to get the
HASP key number. It loads a file from the CD called adsubs.dat.
This files contains a list of all the valid keys, for all their
customers at the time of the CD print. (At least I think it does).
You are asked to enter your subscription key. (16 digits long). You can have multiple keys, to turn on different options.
The HASP key number is mashed with a magic number, to create an 8 byte 'key'. This key is then used to create an rc4 stream table. See: 1000BA10. The subscription key is then encrypted using the cipher stream table. So, your subscription key is only valid for your HASP key. Pretty neat.
If you happen to enter a valid key, it gets encrypted (different pw from the one in adsubs.dat), and stored in the registry.
We first emulate the HASP routine as normal. It's at location b837 in productsecurity.dll. There's a ton of relocations in this area. You'll have to modify the relocation table. Most of my routine below isn't used by this incarnation of Alldata, but I'm too lazy to modify and cut it down from the previous version. Remember that all the protection is stored in encrypted keys in the registry. We sideswipe a memcopy call, and move in our own values at 13a9584 call 13ab907.
Last step : There are a bunch of checks to make sure the registry keys are OK. These are checked every time you go to choose a menu item that is protected.
At locations :
xxx7308 nop nop xxx7656 nop nop xxx90d5 change to jmp xxx91b2 change to jmp
Basically, any time you see a call to xxx9034, its doing a check. There were a few more that I didn't jump around, but it hasn't affected program use yet. A keygen, combined with the HASP emulation routine would be a better solution to this, but I haven't gotten around to investigating keygens yet ... Soon :-).
xxxb837: cmp bh,1 jnz 13ab841 mov eax,1 cmp bh,2 jnz 13ab86c cmp cx,40c9 jnz 13ab86c cmp dx,4ad2 jnz 13ab86c cmp ax,0135 jnz 13ab861 mov eax,4eb7 jnz 13ab86c cmp ax,53 jnz 13ab86c mov eax,a2ff cmp bh,47 jnz 13ab885 inc eax cmp eax,3c jl 13ab87e mov eax,0 xor ecx,ecx mov edx,8 cmp bh,49 jnz 13ab89b mov eax,1 mov ebx,1 xor ecx,ecx mov edx,66 cmp bh,32 jnz 13ab904 nop nop nop nop cmp esi,2 jnz 13ab8b0 mov dword ptr ds:[eax],00000102 cmp esi,7c jnz 13ab904 mov dword ptr ds:[eax],01000000 mov dword ptr ds:[eax+4],601235fc mov dword ptr ds:[eax+8],00000000 mov dword ptr ds:[eax+c],0 mov dword ptr ds:[eax+10],0 mov dword ptr ds:[eax+14],0 mov dword ptr ds:[eax+18],0 mov dword ptr ds:[eax+1c],0 mov dword ptr ds:[eax+20],fc02c905 mov dword ptr ds:[eax+24],0 xor ecx,ecx ret 13ab907 mov ebx,[esp+04] nop nop mov dword ptr [ebx],00000001 mov dword ptr [ebx+04],0 mov dword ptr [ebx+08],000000c9 mov dword ptr [ebx+0c],00060007 mov dword ptr [ebx+10],10e10000 ret