ALLDATA v3.0 - Tutorial

http://www.alldata.com - Webpage.
Files available on request (3Mb's).

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 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 the main hasp() routine will only ever return 1 parameter (Par1) anyhow. Emulating Service 1 won't prove any problem, we'll just MOV EAX, 1 or XOR EAX,EAX / INC EAX and there will always be a HASP. 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 beyond the scope of this essay (however both GetLocalTime & GetSystemTime are imported so it isn't impossible by any means :) ).

I believe the best 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).

Complete Emulator - From SoftICE

:u cs:eip l 150
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 bytes).

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 :).

Protection Notes

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 uses 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 legitamate 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.

You have finished reading another tutorial courtesy of CrackZ's Reverse Engineering Page.
Find a quick way back to more documents with this link.

Return to Main Index, Dongles.


© 1999 CrackZ. 18th March 1999.