390 lines
16 KiB
Plaintext
390 lines
16 KiB
Plaintext
![]() |
Welcome to XLogic's Assembly KeyGen tutorial.
|
||
|
|
||
|
What you will need:
|
||
|
TASM 3.0 or higher (Comes with TD).
|
||
|
A Good Text Editor (EDIT.COM).
|
||
|
A Good Dos Debugger (I will use TD, S-ICE and DG are also Acceptable).
|
||
|
A File access monitor (SCANF is included).
|
||
|
An ASM Command Listing (go buy a book).
|
||
|
A GOOD (intermediate) knowledge of ASM. <-- Don't bug me for help.
|
||
|
X-Tract 1.51 (included).
|
||
|
|
||
|
1. Introduction.
|
||
|
This was the first key generator I ever wrote. It taught me more about
|
||
|
assembly than any book I've read, considering that I originally
|
||
|
learned assembly from debugging stuff.
|
||
|
|
||
|
This is a very easy keygen to do, but you use the same process to write
|
||
|
other much more complex keygens.
|
||
|
|
||
|
The best way of writing a keygen in my experience is:
|
||
|
1. Debug, dissasemble, do whatever is necessary to UNDERSTAND what the hell
|
||
|
the program is doing.
|
||
|
2. Extract the relevant code, reverse it or whatever needs doing to make it
|
||
|
ready for the keygen.
|
||
|
3. Write the keygen.
|
||
|
4. Use a key you made, debug the program, and make sure it works 101%.
|
||
|
|
||
|
2. Let's Get Started!
|
||
|
Ok, look at the program, how it runs. See if it prompts for a reg number,
|
||
|
if it looks for a key file, any way it gets registration in.
|
||
|
|
||
|
With X-Tract, there are no prompts for reg numbers, or anything.
|
||
|
Ok, so now you run a file access monitor, to check if it looks for a reg
|
||
|
key. (I have included a good file access monitor with the package, SCANF.
|
||
|
To make it put file access up on the screen, use "scanf con" to run it)
|
||
|
Bingo. It looks for X-TRACT.KEY.
|
||
|
|
||
|
Now the debugging starts. First create a file called X-TRACT.KEY in the
|
||
|
same directory as X-Tract, and load your debugger. Step through with the
|
||
|
file access montior loaded to see where it opens the key file.
|
||
|
|
||
|
You trace over the following call, and the file monitor tells you that the
|
||
|
key has been opened.
|
||
|
|
||
|
cs:366B 2EA27908 mov cs:[0879],al
|
||
|
cs:366F 9C pushf
|
||
|
cs:3670 E8D71A call 514A
|
||
|
|
||
|
Now you restart debugging and trace one instruction into that function call.
|
||
|
You trace over the first function call, and notice that it displays the
|
||
|
startup banner:
|
||
|
X-TRACT (tm) Executable File Extractor Version 1.51 7-26-95
|
||
|
Copyright 1994-95 by Pablo Carboni. All Rights Reserved.
|
||
|
|
||
|
The next block of code uses an Int 21h call.
|
||
|
You notice that this is what opens the file.
|
||
|
Int 21h's usage is as follows:
|
||
|
ah = 3D ; Open file Handle for file access
|
||
|
al = 02 ; file access code
|
||
|
dx = filename offset ; Where the filename is in memory
|
||
|
Int 21h ; Try to open the file
|
||
|
jnb ok ; If the file exists, and has been opened, jump
|
||
|
jmp error ; If the file hasn't been opened, jump.
|
||
|
|
||
|
So what this block of code does is try and open the file.
|
||
|
cs:5150 B8023D mov ax,3D02
|
||
|
cs:5153 BAFC21 mov dx,21FC
|
||
|
cs:5156 CD21 int 21
|
||
|
cs:5158 7303 jnb 515D
|
||
|
cs:515A E9ED00 jmp 524A
|
||
|
|
||
|
Since you made a file called X-TRACT.KEY before you started debugging
|
||
|
it should jump on the first jump.
|
||
|
You then end up here:
|
||
|
cs:515D 2EA3D805 mov cs:[05D8],ax
|
||
|
cs:5161 B43F mov ah,3F
|
||
|
cs:5163 2E8B1ED805 mov bx,cs:[05D8]
|
||
|
cs:5168 B99B00 mov cx,009B
|
||
|
cs:516B BABD00 mov dx,00BD
|
||
|
cs:516E CD21 int 21
|
||
|
cs:5170 3D9B00 cmp ax,009B
|
||
|
cs:5173 7303 jnb 5178
|
||
|
cs:5175 E9D200 jmp 524A
|
||
|
|
||
|
The first line stores the files handle for future access from AX to
|
||
|
memory location 05D8h.
|
||
|
|
||
|
Now, we see another Int 21h call coming up. This time it is as follows:
|
||
|
ah = 3Fh ; Read Data from the open filehandle in BX
|
||
|
bx = [05D8h] ; get the file handle we saved just before.
|
||
|
cx = 9Bh ; How many bytes we want to try and read.
|
||
|
dx = 0BDh ; Where we want to put the read bytes.
|
||
|
Int 21h ; Do the deed
|
||
|
|
||
|
When this returns, AX will be the actual number of bytes read from the file.
|
||
|
So what is this next piece of code doing?
|
||
|
cs:5170 3D9B00 cmp ax,009B
|
||
|
cs:5173 7303 jnb 5178
|
||
|
cs:5175 E9D200 jmp 524A
|
||
|
Its checking if it could read 9Bh bytes from the file, and if it could,
|
||
|
continue.
|
||
|
|
||
|
As you should have noticed, if any of the previous tests have failed, they
|
||
|
jump to location 524Ah. Keep this in mind when you are debugging the code.
|
||
|
|
||
|
Now you hit this big lump of code:
|
||
|
cs:5178 BEBD00 mov si,00BD
|
||
|
cs:517B BF5521 mov di,2155
|
||
|
cs:517E B99A00 mov cx,009A
|
||
|
cs:5181 0E push cs
|
||
|
cs:5182 1F pop ds
|
||
|
cs:5183 0E push cs
|
||
|
cs:5184 07 pop es
|
||
|
cs:5185 FC cld
|
||
|
cs:5186 F3A6 rep cmpsb
|
||
|
cs:5188 7403 je 518D
|
||
|
cs:518A E9BD00 jmp 524A
|
||
|
rep cmpsb....... hmmm, a byte-by-byte compare statement.
|
||
|
how this is called is:
|
||
|
cx = Number of bytes to compare
|
||
|
DS:DI = First lot of bytes to compare
|
||
|
ES:SI = Second lot of bytes to compare
|
||
|
rep cmpsb ; do the compare
|
||
|
je continue ; jump here it the same
|
||
|
jmp error ; jump here if not the same
|
||
|
|
||
|
So what should be at the start of the rego key?
|
||
|
Whatever is at ES:DI. The other location has what was in the rego key
|
||
|
that you created.
|
||
|
|
||
|
Now you get to this:
|
||
|
cs:518D AC lodsb
|
||
|
cs:518E 3CE0 cmp al,E0
|
||
|
cs:5190 741B je 51AD
|
||
|
And a few more with different compares. It is checking if one of these
|
||
|
is equal to the byte it loaded from DS:DI (its there from after the 1st
|
||
|
compare).
|
||
|
|
||
|
Could it be checking for the type of registration? Lets continue and see.
|
||
|
cs:51AD 2EA28008 mov cs:[0880],al
|
||
|
cs:51B1 B43F mov ah,3F
|
||
|
cs:51B3 2E8B1ED805 mov bx,cs:[05D8]
|
||
|
cs:51B8 B92A00 mov cx,002A
|
||
|
cs:51BB BABD00 mov dx,00BD
|
||
|
cs:51BE CD21 int 21
|
||
|
cs:51C0 3D2A00 cmp ax,002A
|
||
|
cs:51C3 7403 je 51C8
|
||
|
cs:51C5 E98200 jmp 524A
|
||
|
|
||
|
It saves the byte it just checked to cs:880h.
|
||
|
Then it does what it did before with Int 21h, it reads 2Ah bytes from the
|
||
|
file to ds:00BDh. If it could read 2Ah bytes, it continues on, otherwise
|
||
|
it quits.
|
||
|
|
||
|
Now would be a good time to get out of the debugger and make the key file.
|
||
|
9Bh + 2Ah bytes (add the two cx vaules from the file read Int 21h's) is
|
||
|
equal to 197 bytes, so now would be a good time to get out, make a file of
|
||
|
size 197 bytes, With the first lot of data it compared (that string of
|
||
|
bytes), then either a E0h, E1h, E2h or E3h, which it looked for. I will
|
||
|
use a E0h.
|
||
|
|
||
|
You've got your registration key half done. After making it, you
|
||
|
should be able to pass all of the tests it performs to the point where we
|
||
|
left off. If you had problems, have a look at the key that I provided
|
||
|
called XLOGIC.REG to get some hints.
|
||
|
|
||
|
Now you hit this:
|
||
|
cs:51C8 BEBD00 mov si,00BD
|
||
|
cs:51CB 8BFE mov di,si
|
||
|
cs:51CD B92A00 mov cx,002A
|
||
|
cs:51D0 2E8A1E8008 mov bl,cs:[0880]
|
||
|
cs:51D5 AC lodsb
|
||
|
cs:51D6 32C3 xor al,bl
|
||
|
cs:51D8 AA stosb
|
||
|
cs:51D9 80EB22 sub bl,22
|
||
|
cs:51DC E2F7 loop 51D5
|
||
|
|
||
|
Now before i tell you, try and work out what this does.
|
||
|
|
||
|
Here is what it is doing:
|
||
|
1. si = 0BDh ;the location to start from
|
||
|
2. di = si ;set the second location to start from
|
||
|
3. cx = 2Ah ;how many times to loop
|
||
|
4. bl = cs:880h ;get that byte that it checked for earlier
|
||
|
5. lodsb ;load a byte from ES:SI into al, increment si by 1
|
||
|
6. xor al,bl ;xor al by bl
|
||
|
7. stosb ;store al to DS:DI, increment di by 1
|
||
|
8. sub bl,22 ;decrement bl by 22h
|
||
|
9. loop 5 ;loop cx times.
|
||
|
|
||
|
Now, step through this, watching what this does.
|
||
|
What does it do?
|
||
|
It decripts the data after the 0E0h, in the keyfile, using the above process.
|
||
|
|
||
|
If you can't understand this, just keep watching it and debugging it,
|
||
|
because if you can't understand this, you won't be able to write a keygen.
|
||
|
|
||
|
Now it has tried to decrypt your name, and what we're about to look at.
|
||
|
|
||
|
Check this code out:
|
||
|
cs:51DE BEBD00 mov si,00BD
|
||
|
cs:51E1 8BFE mov di,si
|
||
|
cs:51E3 B92800 mov cx,0028
|
||
|
cs:51E6 2E8A1E8008 mov bl,cs:[0880]
|
||
|
cs:51EB 33D2 xor dx,dx
|
||
|
cs:51ED 33C0 xor ax,ax
|
||
|
cs:51EF AC lodsb
|
||
|
cs:51F0 03D0 add dx,ax
|
||
|
cs:51F2 E2FB loop 51EF
|
||
|
cs:51F4 2E3B14 cmp dx,cs:[si]
|
||
|
cs:51F7 7551 jne 524A
|
||
|
|
||
|
Whats it doing?
|
||
|
You should understand the first 7 lines, actually, you should understand
|
||
|
the whole thing if you have a good grasp of Assembly.
|
||
|
|
||
|
It adds all the bytes together of what "Should" be you name, into dx.
|
||
|
Then it compares dx to the number stored at cs:si. This is what is called
|
||
|
a CRC check. This is only a simple one, and all it does is check if any
|
||
|
of the bytes in the string have changed.
|
||
|
|
||
|
If it is the same, it is ok, and continues, otherwise it quits.
|
||
|
|
||
|
Now it sets cs:2220 to 01h, to tell the program it is registered.
|
||
|
cs:51FC 2EC606202201 mov cs:byte ptr [2220]
|
||
|
Then it checks the 0E0h byte. This is where we find out what it does.
|
||
|
cs:5202 2E803E8008E0 cmp cs:byte ptr [0880]
|
||
|
cs:5208 7424 je 522E
|
||
|
So we let it jump:
|
||
|
cs:522E BA9B22 mov dx,229B
|
||
|
cs:5231 E81D00 call 5251
|
||
|
cs:5234 C3 ret
|
||
|
And it prints on the screen "REGISTERED VERSION".
|
||
|
So what do the other "0E?h" values do?
|
||
|
Go back and try the others to find out for yourself.
|
||
|
|
||
|
IMPORTANT.
|
||
|
If you don't understand all of the above, go over and over it until you do.
|
||
|
|
||
|
|
||
|
2. Writing The KeyGen.
|
||
|
|
||
|
Now I am going to get lazy. I will tell you what the steps are,
|
||
|
give you my commented source file, and leave you go from there.
|
||
|
|
||
|
Here is what it is doing:
|
||
|
1. Open the Rego File.
|
||
|
2. Read the Header.
|
||
|
3. Check it.
|
||
|
4. Read the Rego Name and CRC.
|
||
|
5. Decrypt them.
|
||
|
6. Calculate the CRC.
|
||
|
7. Check the CRC.
|
||
|
8. Display the Rego type.
|
||
|
9. Continue on with the program.
|
||
|
|
||
|
Here is what you have to do:
|
||
|
1. Read the Rego Name.
|
||
|
2. Calculate the CRC.
|
||
|
3. Encript the Rego name and CRC.
|
||
|
3. Store the Rego type.
|
||
|
4. Write the whole block (including the header) to the Rego file.
|
||
|
|
||
|
Now for the assembly file:
|
||
|
-------------------------------------------------------------------------------
|
||
|
.386p
|
||
|
seg_a segment byte public use16
|
||
|
assume cs:seg_a, ds:seg_a
|
||
|
|
||
|
org 100h
|
||
|
|
||
|
xtract_keygen Proc Far
|
||
|
start:
|
||
|
mov dx,offset title_text ;load the startup banner
|
||
|
call print_text ;print it on the screen
|
||
|
mov dx,offset max_ent_length ;load the text entry offset
|
||
|
mov ah,0Ah ;function=get text string
|
||
|
int 21h ;get the text
|
||
|
cmp byte ptr entry_length,01h ;check if mor than 1
|
||
|
;character was entered
|
||
|
jae short reg_type_sel ;jump if 1 or more
|
||
|
mov dx,offset no_entry ;not enough was entered
|
||
|
jmp short exit ;jump to exit
|
||
|
|
||
|
reg_type_sel:
|
||
|
mov dx,offset reg_type_text ;load rego type selection
|
||
|
call print_text ;display it
|
||
|
xor ax,ax ;clear ax
|
||
|
int 16h ;get a char from the keybd
|
||
|
int 29h ;display it
|
||
|
cmp al,31h ;check if its 1
|
||
|
jl reg_type_sel ;jmp if lower than
|
||
|
cmp al,34h ;check if its 4
|
||
|
ja reg_type_sel ;jmp if above
|
||
|
continue:
|
||
|
add al,0AFh ;add 0AFh to input, to get
|
||
|
;"E" value.
|
||
|
mov reg_type,al ;store it in the rego type
|
||
|
mov dl,0Ah ;1 These lines store
|
||
|
mov dh,al ;2 the rego type
|
||
|
mov bx,offset max_ent_length ;3
|
||
|
mov [bx],dx ;4
|
||
|
call make_key ;make the key
|
||
|
mov dx,offset done_text ;load done text
|
||
|
exit:
|
||
|
call print_text ;display the output result
|
||
|
retn ;exit to dos/windoze
|
||
|
xtract_keygen endp
|
||
|
|
||
|
print_text proc near ;put text up on the
|
||
|
mov ah,9 ;screen
|
||
|
int 21h
|
||
|
retn
|
||
|
print_text endp
|
||
|
|
||
|
make_key proc near
|
||
|
mov si,offset name_input ;this should look
|
||
|
mov dx,si ;familiar :)
|
||
|
mov cx,28h ;
|
||
|
mov bl,reg_type ;
|
||
|
xor dx,dx ;
|
||
|
xor ax,ax ;
|
||
|
crc_loop: ;
|
||
|
lodsb ;
|
||
|
add dx,ax ;
|
||
|
loop crc_loop ;
|
||
|
mov bx,offset checksum_dat ;
|
||
|
mov [bx],dx ;store the crc
|
||
|
|
||
|
mov si,offset name_input ;this should also look
|
||
|
mov di,si ;familiar :)
|
||
|
mov cx,2Ah ;
|
||
|
mov bl,reg_type ;
|
||
|
encription_loop: ;
|
||
|
lodsb ;
|
||
|
xor al,bl ;
|
||
|
stosb ;
|
||
|
sub bl,22h ;
|
||
|
loop encription_loop ;
|
||
|
|
||
|
mov ah,3Ch ;open the file to write
|
||
|
mov dx,offset key_name ;
|
||
|
int 21h ;
|
||
|
xchg bx,ax ;put filehand in bx
|
||
|
mov ah,40h ;write the key to disk
|
||
|
mov dx,offset key_data ;
|
||
|
mov cx,0C5h ;
|
||
|
int 21h ;
|
||
|
mov ah,3Eh ;close the file handle
|
||
|
int 21h ;
|
||
|
retn
|
||
|
make_key endp
|
||
|
|
||
|
title_text db 'X-Tract 1.51 Key File Generator by XLogic', 0Dh, 0Ah
|
||
|
name_prompt db 'Enter your name: $'
|
||
|
reg_type_text db 0Dh,0Ah,'Please Choose Registration Type:',0Dh,0Ah
|
||
|
db '1. Registered Version',0Dh,0Ah
|
||
|
db '2. Beta-Test Version',0Dh,0Ah
|
||
|
db '3. Distro-Site Version',0Dh,0Ah
|
||
|
db '4. Special Version',0Dh,0Ah
|
||
|
db 'Enter the number corresponding to the type: $'
|
||
|
no_entry db 0Dh,0Ah,'You must enter a name.$'
|
||
|
done_text db 0Dh,0Ah,'Key file X-TRACT.KEY created.$'
|
||
|
key_name db 'X-TRACT.KEY',0
|
||
|
reg_type db 0
|
||
|
key_data dd 073696854h,020736920h,072756F79h,067657220h,072747369h,06F697461h,0656B206Eh,06F662079h
|
||
|
dd 02D582072h,043415254h,050202E54h,07361656Ch,064202C65h,06F6E206Fh,069642074h,069727473h
|
||
|
dd 065747562h,021746920h,063280A0Dh,039312029h,062203439h,06F572079h,02C79646Fh,065754220h
|
||
|
dd 020736F6Eh,065726941h,041202C73h,04E454752h,0414E4954h,06150202Eh,0206F6C62h,06576694Ch
|
||
|
dd 06F532073h,06877656Dh,020657265h,054206E49h,054206568h,02E656D69h
|
||
|
db 0Dh
|
||
|
max_ent_length db 26h
|
||
|
entry_length db 0
|
||
|
name_input db 40 dup ('$')
|
||
|
checksum_dat db 2 dup (0)
|
||
|
seg_a ends
|
||
|
end start
|
||
|
-------------------------------------------------------------------------------
|
||
|
|
||
|
Ok, there it is in all its glory. I hope you've learned something from this,
|
||
|
and if you did, let me know. If you didn't, good for you. Tell me how to
|
||
|
improve this tutorial.
|
||
|
|
||
|
I can be contacted in #PC97 or #cracking on EFNET.
|
||
|
|
||
|
Cya Round.
|
||
|
|
||
|
XLogic.
|
||
|
|