About this
tutorial: Tutorial:Basic keygenning:Hang2000 1.31 Target:Hang2000 1.31(http://www.winograd.com) Tools:SoftICE 3.2x, W32Dasm 8.9x Date:5 Dec 2000 Descriptions&Comments:I am here bringing a very easy target (hopefully)to give a good introduction to any new crackers who cannot keygen yet and those who still have blur visions on the subject of keygen and keygenning. This kind of (easy)essay may not be produced anymore in the future(at least by me), so take this very good chance and learn as much as possible... Protections:(easy)Name/serial Disclaimer:This file is used for educational purposes only. Any misuse of the information presented here is not my responsibility. Copyright information:This tutorial is copyright © ManKind Starting words: |
The process:
Allow me to start by explaining about keygen and how to keygen. A
keygen is an automated program written by cracker to generate
valid registration information for a specific
shareware/commercial program(a generic keygen is sometimes
possible). How it can generate valid registration information?
Well, the cracker first has to have the program he wants to
keygen and using some typical crackers' tools especially a
debugger(SoftICE, no?) as his weapon, he will TRY to understand
how valid registration information is generated in that
program(usually the program takes in a name and a serial, and to
test whether the serial is valid or not, it itself has to
generate a valid serial out of the name before it can compare the
valid serial with the one entered by user). If he succeed to
understand how valid registration information is generated, he
can try to re-code a routine in any programming language that
will generate the same valid registration information. Even if he
is not successful, he still has the options to brutally
crack(patching!) the program by patching, ripping assembly codes
from dead-listing and try to re-compile in an assembler without
actually understanding how the valid registration information is
being generated, sniff out a serial, build an internal(yes!)
keygen in the program itself. However things like building an
internal keygen and advanced patching is too advanced to be
discussed in this essay. Though there exist quite some ways to
keygen, I still consider by understanding the valid registration
information generation routine and re-code it is the most
original, unique and best way. It is also worthwhile to note that
in the WAREZ or cracking scene, keygen is considered the best
crack compared to patching and sniffing a serial. One last thing
I want you to know is that there are actually a variety of
different keygens available, some of them which are
keyfilemaker(that's a variation of keygen!), single serial keygen
and hmm, say generic keygen for several programs...
Now, let's get specific. I will show you from the beginning
till the end how we can keygen this (easy)target of ours. Run
Hang2000 and go to the registration screen(File -> Register,
doh!). As I have told you, it will take in a name and a serial.
Just in case you have not known yet, whenever we try to register
a program, we have to enter some registration information
eventhough we know that the information is not valid. The FAKE
registration information is used to pass the very first check of
the program(a check is a kind of protection to avoid users from
easily registering the program). The check is usually done by
checking the length of user's name and/or serial. Only name
and/or serial with certain length will pass the check. If we do
not pass the check, it is likely that we will immediately be
confronted by message like "Sorry, your registration
information is not valid" which I like to call the bad_boy
message, and when so, the valid registration information will not
be generated at all to be compared with our fake registration
information and to conclude, if that happens we will not be able
to understand how the valid registration information is
generated. Until we dive into the codes, we will not know the
check looks like, but one thing we are sure of is that we must
not have empty(0 in length) name and serial(I am sure out of my
experience :D ). So, in order to not have empty name and serial,
we fill in the following temporary(not fake, as we are gonna
validate a registration based on the following name) registration
information in the appropriate text fields(you can of course
change the following information but my explanation in this essay
will be based on the MY own registration information):
Name:ManKind
Serial#:23199981
(NOTE:selection of 23199981 as serial is not adhered to any
rules, it is just my lucky and usual fake(Oppss, temporary...)
serial from the very beginning of my cracking career :D )
Set a breakpoint on GetDlgItemTextA in SoftICE which is a very
common API function to get texts from text fields(another usual
one is GetWindowTextA) with the following command:
bpx GetDlgItemTextA
With this breakpoint, we will be able to break into our
target's code when it tries to get our name and serial(that is
after we press the Register button) and that will land us quite
near to where valid registration information, in this case, the
valid serial will be generated and later compared with our
temporary serial(actually you don't need to break in here, you
can break in from the program's entry point with Symbol Loader
but that will keep you tracing with F10(step over) for a long
long time and still that does not promise you an exact place
where the real serial is generated, so in fact, breaking into the
program when you have entered your temporary registration
information is the best and most recommended :D ). After setting
the breakpoint, come out of SoftICE and press the Register
button. You will be thrown into SoftICE and you are now in the
middle of the GetDlgItemTextA API function and this is not what
we are interested in, so, press F11 once to return to the caller
of the API function that is you will land inside the process of
Hang2000(disable your breakpoint on GetDlgItemTextA):
(my comment is preceeded by <-- ) (listing obtained from W32Dasm v8.93) (note that some instructions in the listing may differ a little from what may be seen from SoftICE like jne in listing is jnz in SoftICE, sorry for any inconvenience :( ) * Reference To: USER32.GetDlgItemTextA, Ord:0104h | :00406A02 8B3524124100 mov esi, dword ptr [00411224] <-- esi contains the GetDlgItemTextA API function :00406A08 53 push ebx :00406A09 8B9C2410020000 mov ebx, dword ptr [esp+00000210] :00406A10 8D8C248C000000 lea ecx, dword ptr [esp+0000008C] :00406A17 6A28 push 00000028 :00406A19 51 push ecx * Possible Reference to Dialog: DialogID_008A, CONTROL_ID:03EC, "&6" <-- text field for Name | :00406A1A 68EC030000 push 000003EC :00406A1F 53 push ebx :00406A20 FFD6 call esi <-- call GetDlgItemTextA API to get name :00406A22 8D54240C lea edx, dword ptr [esp+0C] :00406A26 6A1E push 0000001E :00406A28 52 push edx * Possible Reference to Dialog: DialogID_008A, CONTROL_ID:03ED, "&8" <-- text field for Serial# | :00406A29 68ED030000 push 000003ED :00406A2E 53 push ebx :00406A2F FFD6 call esi <-- text field for Serial# to get serial :00406A31 8D44240C lea eax, dword ptr [esp+0C] :00406A35 8D8C248C000000 lea ecx, dword ptr [esp+0000008C] :00406A3C 50 push eax :00406A3D 51 push ecx :00406A3E E88D020000 call 00406CD0 <-- *** :00406A43 83C408 add esp, 00000008 :00406A46 663D0100 cmp ax, 0001 <-- compare ax with one :00406A4A 0F85A7000000 jne 00406AF7 <-- jump to bad_boy if ax not equal to 1
There is not much to be said about the above codes. The
important code is the call at address 00406A3E which I have
marked with three asterisks(*). After returning from the call, a
compare will be done to the ax register to see if we have entered
valid registration information. How do I know that the call
marked with 3 asterisks is important and how do I know that the
cmp and jne instructions at address 00406A46 and 00406A4A are the
codes to verify whether we have entered valid registration
information? Well, all the calls before the one marked with 3
asterisks do not look suspicious as they are only there to get
our name and serial while immediately after returning from the
call marked with 3 asterisks, verification is done to see if we
have entered valid registration information(try to enter a fake
name/serial and follow the jump to address 00406AF7 and you will
see how I know that the cmp and jne instructions are the
verification codes) and that makes the call very suspicious. I am
very sure that the whole keygenneration routine(routine to
generate valid registration information) is in that call, so
let's trace into it by pressing F8(trace into) in SoftICE when
the white line of indicator is on address 00406A3E:
* Referenced by a CALL at Addresses: |:00401687 , :00406A3E | :00406CD0 8B4C2404 mov ecx, dword ptr [esp+04] :00406CD4 81EC80000000 sub esp, 00000080 :00406CDA 8D442400 lea eax, dword ptr [esp] :00406CDE 50 push eax :00406CDF 51 push ecx :00406CE0 E8CBFEFFFF call 00406BB0 <-- **** :00406CE5 83C408 add esp, 00000008 :00406CE8 6685C0 test ax, ax :00406CEB 7507 jne 00406CF4 <-- this will always jump unless there is no name entered :00406CED 81C480000000 add esp, 00000080 :00406CF3 C3 ret * Referenced by a (U)nconditional or (C)onditional Jump at Address: |:00406CEB(C) | :00406CF4 53 push ebx :00406CF5 56 push esi :00406CF6 8BB42490000000 mov esi, dword ptr [esp+00000090] <-- move fake/temporary serial to esi :00406CFD 8D442408 lea eax, dword ptr [esp+08] <-- move the valid serial to eax * Referenced by a (U)nconditional or (C)onditional Jump at Address: |:00406D23(C) | :00406D01 8A10 mov dl, byte ptr [eax] <-- move current byte of valid serial to dl :00406D03 8A1E mov bl, byte ptr [esi] <-- move current byte of fake serial to bl :00406D05 8ACA mov cl, dl <-- move dl to cl :00406D07 3AD3 cmp dl, bl <-- compare dl with bl :00406D09 752F jne 00406D3A <-- jump to bad_boy if not equal :00406D0B 84C9 test cl, cl <-- see if cl = null :00406D0D 7416 je 00406D25 <-- if so, jump to good_boy because all serials' bytes have been compared and they are all same :00406D0F 8A5001 mov dl, byte ptr [eax+01] <-- move current+1 byte of valid serial to dl :00406D12 8A5E01 mov bl, byte ptr [esi+01] <-- move current+1 byte of fake serial to bl :00406D15 8ACA mov cl, dl <-- move dl to cl :00406D17 3AD3 cmp dl, bl <-- compare dl with bl :00406D19 751F jne 00406D3A <-- jump to bad_boy if not equal :00406D1B 83C002 add eax, 00000002 <-- update position of eax as 2 bytes have been compared :00406D1E 83C602 add esi, 00000002 <-- do the same to esi :00406D21 84C9 test cl, cl see if cl = null :00406D23 75DC jne 00406D01 <-- jump if not because maybe there are still bytes to be compared * Referenced by a (U)nconditional or (C)onditional Jump at Address: |:00406D0D(C) | :00406D25 33C0 xor eax, eax <-- start of good_boy :00406D27 33C9 xor ecx, ecx :00406D29 85C0 test eax, eax :00406D2B 0F94C1 sete cl \ :00406D2E 5E pop esi > important instructions :00406D2F 668BC1 mov ax, cx / :00406D32 5B pop ebx :00406D33 81C480000000 add esp, 00000080 :00406D39 C3 ret * Referenced by a (U)nconditional or (C)onditional Jump at Addresses: |:00406D09(C), :00406D19(C) | :00406D3A 1BC0 sbb eax, eax <-- start of bad_boy :00406D3C 5E pop esi :00406D3D 83D8FF sbb eax, FFFFFFFF :00406D40 33C9 xor ecx, ecx :00406D42 85C0 test eax, eax \ :00406D44 0F94C1 sete cl > important instructions :00406D47 668BC1 mov ax, cx / :00406D4A 5B pop ebx :00406D4B 81C480000000 add esp, 00000080 :00406D51 C3 ret
See the call I marked with 4 asterisks(*) at address 00406CE0? I suspect that the keygenneration routine is all inside that call because immediately after returning from the call there is no other call and the fake serial is then compared to the valid serial. Where else can the valid serial be generated
other than that call? However, before stepping into that call, I will like to ask you to not to pay attention to the part where the fake and valid serial are compared first, it is just a loop through all the chars of the two serials(never mind if you can't understand the codes of the loop, you just don't have to). Instead, look at address 00406D2B and 00406D42 which I have
marked as important instructions. Why important instructions? That is because the (sete cl) and later (mov ax, cx) instructions will determine whether we are registered or not depending on the value of ax register once we return from this call(we are in a call, remember?) back to address 00406A43. By the way, take a fast look at the jne instruction at address 00406CEB, this is the first check I previously mentioned about that we must not have empty name and serial. We want to make a keygen, don't we? Yes, we do and so let's forget about sniffing the valid serial, and step into the call marked with 4 asterisks by pressing F8:
* Referenced by a CALL at Address: |:00406CE0 | :00406BB0 8B542404 mov edx, dword ptr [esp+04] <-- move name to edx :00406BB4 83EC40 sub esp, 00000040 :00406BB7 83C9FF or ecx, FFFFFFFF :00406BBA 33C0 xor eax, eax :00406BBC 53 push ebx :00406BBD 55 push ebp :00406BBE 56 push esi :00406BBF 57 push edi :00406BC0 8BFA mov edi, edx :00406BC2 33ED xor ebp, ebp :00406BC4 F2 repnz :00406BC5 AE scasb :00406BC6 F7D1 not ecx <-- contains length_of_name+1 :00406BC8 49 dec ecx <-- decrement ecx to get length_of_name :00406BC9 8BD9 mov ebx, ecx <-- move ecx to ebx :00406BCB 6685DB test bx, bx <-- test if name actually exists :00406BCE 7F2D jg 00406BFD <-- jump if it does to good_boy
Since we do enter a name, we will always jump to address
00406BFD. Note that this is another check whether we have entered
a name or not(confirming the facts that fake registration
information is really important for us :D ). Continue tracing
with F10(step over):
* Referenced by a (U)nconditional or (C)onditional Jump at Address: |:00406BCE(C) | :00406BFD 8BFA mov edi, edx :00406BFF 83C9FF or ecx, FFFFFFFF :00406C02 33C0 xor eax, eax :00406C04 8D742410 lea esi, dword ptr [esp+10] :00406C08 F2 repnz :00406C09 AE scasb :00406C0A F7D1 not ecx :00406C0C 2BF9 sub edi, ecx :00406C0E 8BC6 mov eax, esi :00406C10 8BD1 mov edx, ecx :00406C12 8BF7 mov esi, edi :00406C14 8BF8 mov edi, eax :00406C16 C1E902 shr ecx, 02 :00406C19 F3 repz :00406C1A A5 movsd :00406C1B 8BCA mov ecx, edx :00406C1D 83E103 and ecx, 00000003 :00406C20 F3 repz :00406C21 A4 movsb :00406C22 8D4C2410 lea ecx, dword ptr [esp+10] :00406C26 51 push ecx :00406C27 E834090000 call 00407560 :00406C2C 83C404 add esp, 00000004
Lots of code but the above does not do a lot, what it does is
barely uppercase all the characters of our name. How do I know?
Do "d 0075F484" command in SoftICE without the quotes
just before the call instruction at address 00406C27 and
immediately after stepping over the call, the characters of your
name will change to uppercase in the data window(and 1 thing I
want you to note here, though here is quite an inappropriate
place, is that whatever you see in data window with the d command
is considered string while whatever you see in the code window
with the ? command is considered number). Continue tracing and
pay attention now because the following is the start of the
keygenneration routine:
:00406C2F 6685DB test bx, bx <-- test if name exist :00406C32 7E26 jle 00406C5A <-- this will always not jump when we entered a name, think the programmer did something wrong here(newbies who do not understand this statement don't have to) :00406C34 8D4C2410 lea ecx, dword ptr [esp+10] <-- move name to ecx :00406C38 0FBFD3 movsx edx, bx * Referenced by a (U)nconditional or (C)onditional Jump at Address: |:00406C58(C) | :00406C3B 8A01 mov al, byte ptr [ecx] <-- move current byte of name to al :00406C3D 3C41 cmp al, 41 <-- compare it with 65 decimal("A") :00406C3F 7C15 jl 00406C56 <-- jump to 00406C56 to proceed to the next byte of name without calculating anything from the current byte :00406C41 3C5A cmp al, 5A <-- compare it with 90 decimal("Z") :00406C43 7F11 jg 00406C56 <-- refer to comment for address 00406C3F :00406C45 0FBEF0 movsx esi, al <-- move al to esi :00406C48 03EE add ebp, esi <-- add esi to ebp :00406C4A 3C45 cmp al, 45 <-- compare it with 69 decimal("E") :00406C4C 7505 jne 00406C53 <-- jump if al !="69" decimal to 00406C53 to add 3 to ebp :00406C4E 83C505 add ebp, 00000005 <-- add ebp with 5 :00406C51 EB03 jmp 00406C56 <-- fixed jump to 00406C56 * Referenced by a (U)nconditional or (C)onditional Jump at Address: |:00406C4C(C) | :00406C53 83C503 add ebp, 00000003 <-- add ebp with 3 * Referenced by a (U)nconditional or (C)onditional Jump at Addresses: |:00406C3F(C), :00406C43(C), :00406C51(U) | :00406C56 41 inc ecx <-- go to next byte of name :00406C57 4A dec edx <-- decrease the counter by one :00406C58 75E1 jne 00406C3B <-- loop it as long as edx !=0(edx=0 means all bytes of name has been looped through, and <> 0 means not)
Basically the above is a loop for as many times as the length
of our name(but one thing to note is that calculation will only be done to characters of name that are in the range of A-Z) to calculate a temporary value(ebp). Pay extra
attention to the jumps(jmp, jne, jg, jl) and after some time, I
guess you will be able to understand the loop. Anyway the
following example of a loop in C++ may help to illustrate the
loop better:
for(i=0;i64 && temp1 < 91) ebp = ebp + temp1 ; if (temp1 !=69) ebp= ebp + 3 ; else ebp= ebp + 5 ;
When the loop has finished, you will end up here which will
add 13131 decimal to our temporary value, convert the temporary
value into a string and concatenate it with "LJBEPC-"
string(NOTE:the valid serial for my name is LJBEPC-13666). After concantenation, the new string is actually our
valid serial which will be compared to our fake serial as soon as
you return from this call(we are in a call of a call now! :D ):
* Referenced by a (U)nconditional or (C)onditional Jump at Address: |:00406C32(C) | * Possible StringData Ref from Data Obj ->"LJBEPC-" <-- the string | :00406C5A BF78344100 mov edi, 00413478 :00406C5F 83C9FF or ecx, FFFFFFFF :00406C62 33C0 xor eax, eax :00406C64 8B5C2458 mov ebx, dword ptr [esp+58] :00406C68 F2 repnz :00406C69 AE scasb :00406C6A F7D1 not ecx :00406C6C 2BF9 sub edi, ecx :00406C6E 81C54B330000 add ebp, 0000334B <-- add 13131 decimal to ebp :00406C74 8BD1 mov edx, ecx :00406C76 8BF7 mov esi, edi :00406C78 8BFB mov edi, ebx :00406C7A 55 push ebp :00406C7B C1E902 shr ecx, 02 :00406C7E F3 repz :00406C7F A5 movsd :00406C80 8BCA mov ecx, edx :00406C82 8D442414 lea eax, dword ptr [esp+14] :00406C86 83E103 and ecx, 00000003 * Possible StringData Ref from Data Obj ->"%ld" | :00406C89 68C0334100 push 004133C0 :00406C8E F3 repz :00406C8F A4 movsb :00406C90 50 push eax :00406C91 E8C12B0000 call 00409857 :00406C96 8D7C241C lea edi, dword ptr [esp+1C] :00406C9A 83C9FF or ecx, FFFFFFFF :00406C9D 33C0 xor eax, eax :00406C9F 83C40C add esp, 0000000C :00406CA2 F2 repnz :00406CA3 AE scasb :00406CA4 F7D1 not ecx :00406CA6 2BF9 sub edi, ecx :00406CA8 8BF7 mov esi, edi :00406CAA 8BD1 mov edx, ecx :00406CAC 8BFB mov edi, ebx :00406CAE 83C9FF or ecx, FFFFFFFF :00406CB1 F2 repnz :00406CB2 AE scasb :00406CB3 8BCA mov ecx, edx :00406CB5 4F dec edi :00406CB6 C1E902 shr ecx, 02 :00406CB9 F3 repz :00406CBA A5 movsd :00406CBB 8BCA mov ecx, edx :00406CBD 66B80100 mov ax, 0001 :00406CC1 83E103 and ecx, 00000003 :00406CC4 F3 repz :00406CC5 A4 movsb :00406CC6 5F pop edi :00406CC7 5E pop esi :00406CC8 5D pop ebp :00406CC9 5B pop ebx :00406CCA 83C440 add esp, 00000040 :00406CCD C3 ret :00406CCE 90 nop :00406CCF 90 nop
After pressing F10 when the white line of indicator of SoftICE
is on address 00406CCD(the ret instruction), you will be returned
to(this is what happens when you come to a ret instruction when
you are in a call) address 00406CE5. Then, from
there, verification of the validity of serials will be done and
depending on the results a value(1 = serials equal and
registered, 0 = serials not equal and not registered) will be
placed on ax. Since in there we are still in a call(from address 00406A3E),
we will soon return to address 00406A43 where
immediately a final check to see if we are registered or
not(depending on the value of ax) will be done(on ax register,
refer to several lines above)...
Guess what? We have successfully gone through this program's
keygenneration routine(I aplogize for my bad explanation and
writing skills if you still do not understand everything yet, my
only advice is re-read please... :( ) but until here we still
have not keygenned it yet. I have told you that keygen is an
automated program which will generate valid registration
information for us and until we build that keygen we can only say
that we understood the keygenneration routine but not keygenned
it yet. Not much effort is needed to actually build a keygen for
this program as the keygenneration routine is really short and
simple and with the core of the keygen(the heart of the keygen)
halfly-completed(remember the C++ example loop to calculate the
temporary value? that is important part in our keygen for this
program), our work is just like a walk in the park... Below is my
source of the keygen(with comment!) which I compile using
Microsoft Visual C++ 6.0(other compilers may be able to compile
the source, but none has been tested yet :D ):
#include <conio.h> // getche() #include <string.h> // strlen, strupr, etc. #include <stdio.h> // printf, gets, etc. #include <iostream.h> // cin, cout, etc. void main() { char name[255] ; // variable for name int temp1=0, i, name2, ebp=0 ; // some variables printf("=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=\n") ; printf("+ C++ KeyGen for Hang2000 v1.31 +\n") ; // intro printf("+ by ManKind on 5 Dec 2K +\n") ; // intro printf("=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=\n") ; printf("Name: ") ; // wait for input gets(name) ; // store user's name in variable name name2 = strlen(name) ; // get the length of name strupr(name) ; // uppercase all the chars of name for(i=0;i<name2;i++) { // this is the core loop temp1 = name[i] ; // get current byte of name if (temp1 > 64 && temp1 < 91) { ebp = ebp + temp1 ; // add current byte to ebp if it's in the A-Z range if (temp1 != 69) ebp = ebp + 3 ; // if not equal to 69, add 3 to ebp else ebp = ebp + 5 ; // if equal to 69, add 5 to ebp } // boy, the programmer must have some special likings for 'E' } ebp = ebp + 13131 ; // add 13131 decimal to ebp printf("Serial#: LJBEPC-") ; // output the valid registration serial! cout<<ebp<<endl ; getche() ; // wait for user to press Enter before exit } |
As you probably may have known, I am not a good coder and that means the above source is not optimized and some things have to be added to make it a (quite)complete keygen(like check if user entered name or not, besides I know the keygen has some problem with name that has space in between the chracters). Though I can improve it, I have decided not to do so as it is only meant as an example and I don't expect the source to be distributed(for bad purpose, that is to register the program, eww!) at all without this essay. One last thing I want to tell you is that the registration information is kept in a file named wh2k.dat in your windows directory. Finally we have come to the end of this essay. Hope to see you soon on my next tutorial. As usual, contact me if I make any mistake, give me your feedback, comments, suggestions and opinions about this tutorial and my way of presenting it.
Extra notes:
Recently, I have read a few comments by tutorials' readers concerning the quality of cracking tutorials available. Quite a few of them said that there are some tutorials that are useless as they teach nothing and are just a scratch work by the author and I am greatly disturbed by those statements. I am not sure if my essays fall into that kind of category of useless essays but the truth is that I have tried my best to make every of my essays(at least those of my latest ones) resourceful and not just any essays that teach nothing. So, I will like to call out to all my essays' readers(if there are any, hehe :D ) to send me suggestions, comments, opinions and feedbacks so that I know whether my essay and writing style is acceptable to you(that means, you can understand what I try to present, learn something and not bored by my texts) or do they need some change. I am open to positive remarks, I will like to receive critics and is willing to change and improve if my essays and writing style are really that bad rather than continue writing USELESS essays. Thanks(and hope that I am not one of the authors who write useless essays :D )...
Well, whether or not we crack this program is not important at all here. I just want you to understand the approaches I have taken and the reasons for doing so and to learn from the methods I have shown you. However you must be clever enough to take other approaches and methods(based on your logics) when dealing with other targets. Ability to do so will make you a great cracker as tutorials I believe are not used to teach you how to crack only a specific target, they show you the methods, approaches and logics available while cracking and expect you to apply them on other targets with your own intelligence...
Ending:
Thanks and greetz to:
+ORC, +HCU, Sandman, HarvestR, tKC, ytc_, Punisher, Kwai_Lo, TORN@DO,
CrackZ, cLUSTER, LaZaRuS, mISTER fANATIC, yes123, WhizKiD, Volatility,
ACiD BuRN, Eternal Bliss, R!SC, Kwazy Webbit, +Mammon, MisterE, Shadow, Falcon, ^tCM^, WaJ, egis, Borna Janes, CyberBlade, Sheep140/Reclaim, josephCo, Kathras, +tsehp, Predator, tscube, AB4DS(Death), douby, Steinowitz, Lord Soth, seifer666, Latigo, Dawai, Lucifer48, NoodleSpa, Mercution, NeuRaL_NoiSE, Fravia+, [dshadow], yAtEs, Duelist, Alpine, hutch, flag eRRatum, Nitrus, LiQUiD8, +Frog's Print, Muad`Dib, Acid_Cool_178, Iczelion, Razzia, wOODY^dRN, Warezpup, Bomber Monkey, XMen, llama and other crackers, individuals and organisations who have helped me, either directly or indirectly.
Service for Mankind
ManKind
mankind001@bigfoot.com