http://www.graphitech.net - Webpage.
Files available on request.
Welcome once again to a dongle tutorial, this time I'm dealing with HASP (produced by Aladdin Knowledge Systems). HASP in its various guise is regarded by most crackers as the most difficult dongle to beat unless of course you have access to the original dongle, then comparing responses is obviously easy. HASP's come in many flavours but I'd loosely categorize them into 2 groups, those that use return codes and those using a HASP internal clock.
A lot of HASP API information is available at ftp://ftp.hasp.com, if you are really interested in the dongle itself then download the entire CD documentation, else be content with some of the lone PDF's. When you attempt a HASP. knowing the precise specification will help enormously, disassembling is also a must and for this W32Dasm probably won't cut it, so whip out IDA. Implementations of HASP vary but don't be alarmed if you find that a bpio -h 378 rw doesn't snap, in most cases forget working back from the nag box (although I'll touch upon a HASP feature regarding nag strings a little later).
With Sentinel's not installing the dongle drivers can be a positive advantage, however the reverse is true with HASP, install that vxd for now then remove it later. I'll start now looking at the target, there are 4 exe's in total but I'll only focus on graficad.exe for now, the theory being once you crack 1 then the others will fall also. Launching the program produces "PLUG NOT FOUND", its created with an enticing MessageBoxA, just note that address and forget working back, you'll never do it.
A bpio -h 378 rw won't fire, so we'll use bpx CreateFileA assuming that the program has to open the vxd/dongle driver to commence communications. Keep allowing the breaks until the message box appears and make a note of the address of the last place you can intercept. Its at this code.
:004AB91C CALL [KERNEL32!CreateFileA] <-- 2 breaks here.
Once you reach here disable the breakpoint and start tracing with F10. Use your F12
sparingly (maybe 5 times) to get an entry point at 004A99DE, any further and
its the message box.
:004A99DE MOV EAX, [EBP-4]
:004A99E1 MOV [EAX+12h], BL <-- Maybe an interesting flag, check API reference.
:004A99E4 CALL SUB_402A58 <-- Function x
:004A99E9 MOV EAX, 0FFh
:004A99EE CALL SUB_402D08 <-- Function y, (return value of EAX is interesting).
:004A99F3 MOV EDX, [EBP-4] <-- Might be worth 'watching' EBP-4.
:004A99F6 MOV [EDX+11h], AL <-- Store AL.
:004A99F9 CMP ds:dword_50C7FC,0 <-- 1st dongle response.
:004A9A00 JNZ 004A9A29 <-- This location now has to be checked.
:004A9A02 CMP ds:dword_50C800,0 <-- 2nd dongle response.
:004A9A09 JNZ 004A9A29 <-- Again.
:004A9A0B CMP ds:dword_50C804,0 <-- 3rd dongle response.
:004A9A12 JNZ 004A9A29 <-- The final check.
When you think over code such as this its always worth putting names to each
function and speculating what each line actually does. The JNZ 004A9A29 is
evidently critical to the codes path, when you trace through you'll see
that each dword is set to 0, shortly after a call displays "PLUG NOT FOUND", so
one of these conditional jumps must happen. The question as to which is possibly
easy to solve.
:004A9A29 MOV EAX, ds:dword_50C800 <-- Its this dword.
:004A9A2E DEC EAX <-- less 1.
:004A9A2F SUB EAX, 2 <-- less another 2.
:004A9A32 JB 004A9A38 <-- "ILLEGAL PLUG" also at this stage.
:004A9A34 JZ 004A9A55 <-- "ILLEGAL PLUG" at this stage, but good when we patch.
:004A9A36 JMP 004A9A72 <-- "PLUG NOT FOUND" - unavoidable exception.
To really understand this you need to really feel the next section of code. Jumping via either the JB or JZ allows code execution to continue via the JMP 004A9A8B although a function call displays "ILLEGAL PLUG" before we continue. Note the 3 corresponding flag values with regards to the JB and JZ, [EAX+10h] - flagged as 0 or 1, [EAX+8] - 32h or 4Dh and [EAX+0Ch] - 33h or 4Ch. The JZ is the 'real' good jump, why?, well would any serious programmer bother with the JZ precise check i.e. dword_50C800 = 3, if a simple JB was good enough, [EAX+10h] flagged to 1 (i.e. true) also adds strength to this conclusion.
So we'll make a preliminary patch here, we'll use the address space of all those compares and JNZ's to 004A9A29 to MOV dword_50C800 to 3 then make the CMP and JNZ, in fact you could just force the JMP because the zero-flag was unset anyhow. You'll be able to download zipped GIF files illustrating the patches I've made in Hiew at the end of this tutorial.
Now when we run graficad.exe we get "ILLEGAL PLUG", that message appears beneath CALL SUB_4A9FC4 which is where we'll commence our next probes. Note that if you used W32Dasm the first part of this function isn't disassembled correctly. Lets not forget however to search for any other instances of dword_50C800.
:004A9C2A CALL SUB_4AA3B8 <-- Read dongle again maybe.
:004A9C2F CMP ds:dword_50C800, 0 <-- Check.
:004A9C36 JNZ 004A9C4D <-- Needless to say this must jump.
In this instance we can't make as elegant a patch as before because of address space restrictions, although you could possibly use the space between 004A9C38 and 004A9C42. Back into SoftICE and lets trace yet again below 004A9FC4, the first section of this call sets 11 word locations offset from EBX to 0 in a horrendously inefficient manner (I could recode this in significantly less bytes), and the first JNZ 004AA01D is safe to allow. Tracing on, this next section obviously has to be looked at closely.
:004AA04B PUSH offset dword_50C7FC
:004AA050 PUSH offset dword_50C800 <-- Recognise this.
:004AA055 PUSH offset dword_50C804 <-- This will be checked.
:004AA05A PUSH offset dword_50C808 <-- This can only be dongle data.
:004AA070 CALL SUB_4AA3B8 <-- Now we know this reads the dongle.
:004AA075 CMP WORD PTR [EBX], 192h <-- The inevitable compare.
:004AA07A JZ 004AA094 <-- Jump_nice_dongle_return.
Slowly we are breaking this protection. When we did our search earlier for other
locations checking dword_50C800 I speculated that SUB_4AA3B8 might be a dongle
reading function and this code here confirms that suspicion. We can now do 2 more
things, firstly we'll patch in the correct return code here to fix this code and
2nd we'll search for any other instances of SUB_4AA3B8. When I patched this note
how I shamelessly used address space to ensure the zero flag was set by
the compare.
Before we do this lets just follow the code a little further. Another check looms at this code.
:004AA094 CMP ds:dword_50C804, 0 <-- Check dongle data for 0.
:004AA09B JZ 004AA0B5 <-- Good_jump.
After patching this lets return back a step or 2 and do some searching for
SUB_4AA3B8, we'll find it again at this code (although I'm dubious as to
whether it needs fixing here).
:004A9EAB CALL SUB_4AA3B8 <-- Dongle read.
:004A9EA0 TEST BL,BL <-- Test BL=0.
:004A9EA2 JZ 004A9EAB <-- Not jumping here is bad.
We run graficad.exe now but still the message box says "ILLEGAL PLUG", in fact what I patched finally I only found after some serious thought. We know that SUB_4AA3B8 reads the dongle, now in terms of execution flow the last check to be executed is the code just above, so lets use the Symbol Loader and reach 004A9EA0 with a timely g command. The JZ works as we would like, no need to patch (possibly because in every HASP API guide BL just returns 0 if a LPT port can be found), the next function call (SUB_4A9DA4) is the one to investigate.
:004A9DAF TEST EDX,EDX
:004A9DB1 JNZ 004A9DD3 <-- Good_jump_address.
:004A9DB3 TEST ECX,ECX
:004A9DB5 JNZ 004A9DD3 <-- Good_jump_address.
:004A9DB7 TEST EAX,EAX <-- Check_for_0.
:004A9DB9 JNZ 004A9DD3 <-- Good_jump_and_best_to_force.
:004A9DC9 CALL SUB_4A96D4 <-- Else_call_this_function.
This code really explains itself, one of these JNZ's will have to be forced
over the bad call which we've avoided before. I'd suggest forcing the last JNZ, the
reason being that before these checks only EAX's value was set (DWORD PTR [EBP+14])
and its unlikely general purpose registers such as ECX/EDX would retain their
values so deep into the checking procedure. This concludes a tricky reversing
section, the other 3 files are eminently similar.
:004A9713 ADD ESI,03 <-- ESI used as jump variable and value determined here.
:004A9716 CMP ESI,0E <-- Maximum number of possibilities.
:004A9719 JA 004A97E7 <-- Quick Exit.
:004A971F JMP DWORD PTR [4*ESI+offset] <-- Jump table into error strings.
Needless to say that when you crack a HASP properly you'll never reach this code. As a small final exercise you might like to remove the DEMO and EXP string from the exe purely for aesthetic purposes.