Undocumented HASP Part II
"xDEAD:xBEEF: extending HASP manufacturer's services"
advanced
Advanced
12 March 1998
by bajunny
Courtesy of Fravia's page of reverse engineering
slightly edited
by fravia+
fra_00F3
980312
bajunni
0010
AD
P3
Another great contribution by bajunny. Hasp misteries and vagaries are tackled and resolved. A must reading for all dongle interested serious researchers! This is definitely not for beginners, either you know how to send and check bytes from your PC ports, or you don't (and you better know it :-) If you are a beginner, leave this stuff for a while and delve inside +HCU's project 3until you have at least a rough global knowledge of dongle reversing.
project3
Dongle
There is a crack, a crack in everything That's how the light gets in
Rating
( )Beginner ( )Intermediate (x)Advanced ( )Expert

Second and most probably the final part of MemoHASP reversing saga.
Undocumented HASP - Part II
"xDEAD:xBEEF: extending HASP manufacturer's services"
Written by bajunny


Introduction

Second part of the text deals primarily with accessing HASP memory. Some aspects of software protection are also included (not very organised, though).

Tools required
Brains (obligatory). C compliler (quite optional). JavaScript manual (to land the teapot on).

Program History
See Part I for the first part of the text.

Essay
                              Open mind for a different view
                              And nothing else matter..
          
                                      an old song by Metallica
--------------------------------------------------------------

                    Undocumented HASP - Part II
                              by -bajunny


    4. Memo-  ..stands for memory!
    ------------------------------

  The HASP documentation says that MemoHASP-I has a total incredible 
amount of 112 bytes of memory ready for programmer use, while all happy 
buyers of NetHASP and MemoHASP-IV (which are essentially the same thing 
I believe) can take even more - 496 bytes. 
One can immediately realize, that in both cases HASP had reserved 16 bytes for 
its own use,  because 112 +16 == 128 and 496 +16 == 512 and memory 
manufacturers enjoy power_of_2_numbers. This fact was further
acknowledged by the following fragments of disassembled HASP
driver code:

; API_service#6

. . . 
mov word ptr ds:[bp + 0ea], fff9; 
call 6496
. . .
mov word ptr ds:[bp + 0ea], fff8; 
call 6496
. . .
mov ax, ds:[bp + si + 0d3]       ; HASP memory access commands
. . .
add ax, word ptr ds:[bp + 0ea]   ; reasons for ADD will follow
add ax, 8                        ; !
. . .

  So real addressing for user-accessible memory begins from 
offset 8, while in offsets 0 and 1 resides the "unique HASP ID". 
In fact, the following 6 words are never user by any HASP
software (I didn't look for Net- and Time- devices, though)
so you will be able to place something even more _unique_ here.. 
But you need more details on HASP memory access indeed 
before...

  As we already have seen in the first part of this essay, HASP 
wouldn't give anything useful without proper activation. 
 Memory access is no exception, and this is the sad news. 
The good news is that we actually have already acquired all
necessary items for this activation. I mean the opening combination
that we have found in the first part of this text. But we should stop at
the correct moment: in fact we need to know that after the 15th 
outbyte() is issued, one of  HASP's internal flip-flops 
switches output to query its booleans..
The moment after 14th outbyte() is right. It's amazing, but if
you will try to retrieve Boolean at that moment HASP returns 
only zeros to you - it definitely indicates some change in HASP 
internal state! Change for some memory work! 

  Technically speaking, all memory ops are issued through 
appropriate commands. These commands along with their arguments
and subsequent results are passed to HASP serially - that is
bit by bit by signalling specific bits just like bit0 was
signalled in the outbyte function, forcing HASP to parse its input
as command bytes. 14th byte of opening sequence was used as a 
background for this. - This is the reason why this byte was set
to certain value (x9C) irrelevant of  all other password related 
fiddlings.

  Command length is 10 bit. HASP driver provides the 7 following
values as command ops (all presented as bit strings, x's stand
for address values):
0110xxxxxx - read word from address specified
0101xxxxxx - write word to  -------//--------
0111xxxxxx - erase word at  -------//--------
0100110000 - enable write ops
0100000000 - disable write ops
0100100000 - ? unknown, apparently no effect on Memo-'s
0100000000 - ? the same.. 

So, to read word from address 9, one should send the following
10 bits to MemoHASP: 0110001001. Sending are performed by
signalling bit6, along with bit5 alterations (see my code at 
appendix). Do not forget to begin and end all memory operations by 
signalling bit1.. Resulting word is received also in a serial
manner, see below the code inside hasp_read_word()..

If you happen to torture MemoHASP-IV, just expend these 
command strings by two more bits, allowing full 256 words
address space (and of course call hasp_send_cmd( ..., 12 )!:)

Writing is analogous, but first one must enable write using a 
special command. HASP driver also issues obligatory erase 
before each write, but it's not really necessary: write 
successfully sets both 1s and 0s. One feature for erasure 
and writing: HASP driver has a specific code that waits for 
state change after each of those two operations, I put something 
analogous to wait_till_burned() routine below, though no actual 
wait was ever detected during my own experiments...

If you dare to use my routines to read/write some HASP memory, 
you won't see any expected values, because once again HASP
designers used  some silly obfuscating schemes to conceal 
their data. 
So you need first of all a password number to correctly decipher it. 
I already warned you there could be some ambiguities in password
detection, well, here is the place where they can be encountered.

But in fact this obfuscation CAN HELP to get the right 
password: many areas inside HASP memory holds initial
xFFFF's (you see, erasure sets bits this way) - and this
is especially true for all hidden words at 2..7! They are set
to xFFFF.


Obfuscation algorithm is presented inside the code fragments in 
appendix, it uses simple XOR of 1st word of the password, 
address of word and constant of xFF00 for addresses 0..7 
(well, actually xFFF8..xFFFF inside the HASP driver). 

I played a little with command opcodes, hoping to fish something
interesting, but without success. Maybe you will be more
successful... if so write about it.


    5. HASP ID story.
    -----------------

  The extremely vague HASP documentation warns you that: 
"You cannot order HASP keys with specific ID Numbers. These are 
assigned pseudo-randomly during manufacturing and are 
guaranteed not to repeat".. So if your HASP has some ugly ID
like xA60D:x12F7 you have to live with it?? 
No, no,no! 
Bajunny CAN help you to change it to something "prominent" 
like xDEAD:xBEEF or x1234:x5678 (as in all my HASPs) or 
whatever you like... extending thus the manufacturer's services, 
power of good cracking ;)

  Of course everything is already prepared: HASP ID lives at
the very beginning of memory, words at _0 and _1, this area
is accessible and is not write protected.. You already have all
necessary information to carne your own HASP ID. Nothing much 
to be added to the ID story except to mention that ID is 
checked sometimes by the envelope routines.. But for other crucial 
HASP parts: password and booleans I have no way to modify by 
means of software (but who knows.. what if... we will see ;) 
my wish to invent some mechanism to make one HASP start mocking 
another failed... So end of the story... for now ;)

    6. Conclusion (again too long).
    -------------------------------  
  When writing these words I have to admit to you I finally
got awfully bored of all this HASP super protection. Well, once
more we have to observe the old banal truth, so common in our 
cryptography intense world: security through obscurity is worse than 
no security at all. So what we have in these "ultrastrong" HASP
dongles? What's inside them? Almost nothing interesting. 
In fact, today already exist more decent dongles, documentally presenting 
strong algorithms inside (or I'm still dreaming, are they as easy to 
reverse?) having at least PIC-complexity rather than a simple set 
of flip-flops and memory cells.. 

Well, the HASP people can and will surely argue that they have 
at least implemented a strong software protection. 
It's complete bullshit - all info I have given to you in my essay 
has been gathered by myself through disassembling and experimenting 
with software. No ion-beams or layer etching intervention. 
Nothing in my hands! 
And I didn't encounter _any_ cool anti-debugging tricks! Well, some 
folks say they implemented anti-softicing like disabling 
breakpoints and firing something like "hboot" to your debugger's 
command line.. I don't know.. In fact, I've used winice only to 
get rid of HASP for the InscriberVMP program... and it was my first 
experience with windoze debugging.. Yeah, I use windoze only 
to format something to print on laser or to browse those great pages 
of fravia+ :) - all other experimenting still in good old doze.. 
Where else can one find such a well documented, inexpensive, 
_true_ real-time OS ? :))

  Maybe in the future I will present a  more comprehensive survey of
HASP software protection. All this enveloping stuff, and the like. 
Right now I'm not sure it will ever materialise. Not because
its difficult, but because its very boring, and I've almost spent my
strategic supply of Homeric laughters looking at something like:
"FAS - Full Authorization System" 

This seller's hypo stands for some feature in enveloping: you can assign 
a number to a program and specify a maximum number of activations. 

Sounds good? In fact, this FAS system eats half of your MemoHASP cell 
space - 2 words for each 16 possible program numbers, first word to 
check if corresponding  slot was occupied (xFFFF if not, any other value 
if yes, HASP... puts x0000) while the second word keeps the counter (xFFFF if 
unlimited activations..). Of course you can edit everything with my 
functions below or even using HASPEDIT. 

Well, what should I say? I sip a good cup of tea, observing this moronical 
hype once again and have decided to give up, my dear reader... it's too easy 
and too stupid... 
Even fighting windmills is better than fighting stupidity..

    APPENDIX
    --------
  Here are some fragments to be added to my little cracking
program  from the first part of this work of mine
// --------------------------------------------------- int hasp_answer(void) { return (inportb(PORT +1) & 0x20) ? 1 : 0; } int sticky = 0x9c; // hardcoded, HASP software always sets this value.. void memo_open(void) { int i; outportb( PORT +2, 0x0c); outbyte( 0xc6); for( i=0;i<14;i++) outbyte( tmp[i]); //sticky = tmp[13]; } void memo_close(void) { outbyte( 0xc6); } void tease_bit( int t, int b) { int teaser = 1<<b; if(t) sticky |= teaser; else sticky &= ~teaser; outportb( PORT, sticky); } void hasp_send_word( unsigned cmd, int len) { for( ; len; len--) { tease_bit( cmd & (1<<(len-1)), 6); tease_bit( 1, 5); tease_bit( 0, 5); } } unsigned hasp_read_word( void) { int i; unsigned res = 0; tease_bit(1,5); // a necessary "phase shift".. tease_bit(0,5); for( i=16; i>0; i--) { res |= hasp_answer() <I-1); tease_bit(1,5); tease_bit(0,5); } return res; } int wait_till_burned(void) { time_t t; t = time(0); tease_bit( 1, 1); for( ; time(0)-t<5;) { if(hasp_answer()) { tease_bit( 0, 1); return 0; } } return -1; } . . . void main() { int addr; unsigned short passwd1=0x347f, passwd2=0x13b3; // known from previous invocation of cracker.. . . . // read _all_ HASP memory memo_open(); for( i=0; i<0x40; i++) { tease_bit( 1, 1); hasp_send_word( 0x180 +i, 10); k = hasp_read_word(); tease_bit( 0, 1); printf("%04x ", k ^ passwd1 ^ (i-8) ^ ( i<8 ? 0xff00: 0) ); // obfuscator: ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ if(0==(i+1)%8) printf("\n"); } memo_close(); . . . // read/erase/write _one_ word addr = 0x3f; memo_open(); tease_bit( 1, 1); hasp_send_word( 0x180 +addr, 10); k = hasp_read_word(); tease_bit( 0, 1); printf("original read: %04x\n", k); tease_bit( 1, 1); hasp_send_word( 0x130, 10); tease_bit( 0, 1); // erasure.. tease_bit( 1, 1); hasp_send_word( 0x1c0 +addr, 10); tease_bit( 0, 1); wait_till_burned(); tease_bit( 1, 1); hasp_send_word( 0x180 +addr, 10); k = hasp_read_word(); tease_bit( 0, 1); printf("after erasure read: %04x\n", k); // burn smth in.. tease_bit( 1, 1); hasp_send_word( 0x140 +addr, 10); hasp_send_word( 0x55aa, 16); tease_bit( 0, 1); wait_till_burned(); tease_bit( 1, 1); hasp_send_word( 0x180 +addr, 10); k = hasp_read_word(); tease_bit( 0, 1); tease_bit( 1, 1); hasp_send_word( 0x100, 10); tease_bit( 0, 1); memo_close(); printf("final read: %04x\n", k); } // ------------- end of transmission -----------------

Final Notes
The author encourages you always to look one level deeper,
of course if the time and patience would permit it... if you do, by all means, 
write and document and publicize your findings...


Ob Duh
I wont even bother explaining you that you should BUY any of your target programs if you intend to use them for a longer period than the allowed one or on a greater number of machines than the allowed one. Should you want to STEAL those buggy software instead, you don't need to crack its protection scheme at all: you'll certainly find it on most Warez sites, complete and already regged or undongled, farewell.

You are deep inside fravia's page of reverse engineering, choose your way out:

redhomepage redlinks redsearch_forms red+ORC redstudents' essays redacademy database
redreality cracking redhow to search redjavascript wars
redtools redanonymity academy redcocktails redantismut CGI-scripts redmail_fravia+
redIs reverse engineering legal?