LordByte Tests CrackMe by Independent (IND)
6 of 6 files
lordbyte
-
This download is an executable MS-DOS program that will not run on a modern computer.
It needs a DOS emulator such as DOSBox-X, Staging;
or a virtualized MS-DOS or FreeDOS system.
Browsers may flag this download as unwanted or malicious. If unsure, scan it with VirusTotal. -
Last modified Nov 6, 2017 5:23:00 PM
MD5 checksum 32ca04e22ded7490b289651caa9435fe
Mime type Zip archive data
Download tests.zip
Size 85 kB
1998
28 items in the archive
- TEST1/
- TEST1/TEST1.COM
- TEST2/
- TEST2/TEST2.COM
- TEST2/TEST2KM.EXE
- TEST2/TEST2KM.PAS
- TEST2/TEST2TUT.TXT
- TEST3/
- TEST3/TEST3.COM
- TEST3/TEST3TUT.TXT
- TEST3/TEST3UNP.COM
- TEST4/
- TEST4/GENPASS.C
- TEST4/GENPASS.EXE
- TEST4/ORG.DAT
- TEST4/SHOWEQU.C
- TEST4/SHOWEQU.EXE
- TEST4/TEST4.COM
- TEST4/TEST4TUT.TXT
- TEST4/TESTCRK.C
- TEST4/TESTCRK.EXE
- TEST4/XOR.DAT
- TEST5/
- TEST5/TEST5.COM
- TEST6/
- TEST6/TEST6.COM
- TEST7/
- TEST7/TEST7.COM
How to crack TEST2 by LordByte
by Crook
27 January 1998
Introduction
TEST2 is the second TEST in the series of CrackMes issued by LordByte.
The higher number is the harder CrackMe is. As you can expect TEST2 is not
really difficult. If you feel it's too easy for you just go ahead and try the
TEST7.
Where you can get it
You can get it from http://crackme.home.ml.org together with zillions
(well, nearly ;) other ones.
Cracking
At first I must say you have to know assembler before trying to read
this. I'll present a complete disassembly of this little piece of code but to
fully understand what I'm talking about you must know what those magic MOVs
and XORs are.
Let's go
(Below is the full source code of TEST2, produced by IDA - Interactive
DisAssembler. If you want to crack more & more progs you should get it. More
details can be found on great FraVia's site: http://fravia.org and the IDA
itself can be found almost everywhere on the Web).
------------------------------------------------------------------------------
jmp short $+2
int 3 ; Trap to Debugger
------------------------------------------------------------------------------
This is just a typical start of LordByte's program ;) The first jump
which is effectively useless. The next instruction is to stop the debugger
execution at this point. It's very useful to us because the original COM was
pcaked using PKLITE and we are able to find entrypoint without unpacking the
program.
------------------------------------------------------------------------------
mov ax, 900h
mov dx, offset intro_str
int 21h ; DOS - PRINT STRING
; DS:DX -> string terminated by "$"
mov ax, 0A00h
mov dx, offset password
int 21h ; DOS - BUFFERED KEYBOARD INPUT
; DS:DX -> buffer
------------------------------------------------------------------------------
This two DOS calls just print the intro message and get the password from
the user. Nothing complicated yet.
------------------------------------------------------------------------------
mov ax, 351Ch
int 21h ; DOS - 2+ - GET INTERRUPT VECTOR
; AL = interrupt number
; Return: ES:BX = value of interrupt vector
mov si, offset old_ofs
mov [si], bx
mov bx, es
mov [si+2], bx
mov si, offset interrupt
mov dx, si
mov ax, 251Ch
int 21h ; DOS - SET INTERRUPT VECTOR
; AL = interrupt number
; DS:DX = new vector to be used for specified interrupt
jmp short next
------------------------------------------------------------------------------
Now we're saving vectors of 1ch interrupt and setting it to our vector.
This interrupt is called (normally) 18.2 times a second allowing you to do
things 'in background'. It's used for some (lame ;) antidebugging later.
------------------------------------------------------------------------------
terminate:
pop bp
add sp, 8
mov ax, 4C00h
int 21h ; DOS - 2+ - QUIT WITH EXIT CODE (EXIT)
; AL = exit code
------------------------------------------------------------------------------
This proc just ends the execution of this program. It's not executed
after playing with vectors because there is a 'jmp short next' jump to 'next'
label below.
------------------------------------------------------------------------------
next:
mov si, offset password+2
mov ax, 6F25h
xor_loop:
xor ax, [si]
cmp byte ptr [si+1], 0
jz anti_loop
cmp byte ptr [si+1], 0Dh
jz anti_loop
inc si
jmp short xor_loop
------------------------------------------------------------------------------
Yo! This is the 'juicy' part of the prog. Let's look closer what is going
on there. Moving 6f25h to AX and then XORing it by every char in the password.
But we must be careful here. Why SI's initial value is 'offset password+2'?
Because the first char at 'offset password' is how many chars can be put in
the buffer (put there before DOS call. It's the buffer length incremented by
one, because of 0Dh char on the end which also must fit into the buffer. DOS
checks this value and gives a beep when you're trying to enter more chars then
it's stated here. In this program maximum password length is 11 = 0Ch - 1),
and the second byte at 'offset password+1' is how many chars user actually has
entered. So we have in AX value of 6f25h and let's assume we enetered 'test'
as a password. Let's have a look at the memory:
offset password: 0C 04 74 65 73 74 0D 00 ....
Notice that 74 65 74 73 = 'test'. SI points to 'offset password+2', which
is 74 65 73 74. Now the AX get XORed (if you don't know this function go and
read about in some book, some paper on the Web or something, otherwise you
won't understand how the crack is being done). But with what? First guess is
7465, but it's wrong. It gets XORed by 6574h. As you can see the order of the
bytes is reversed. That's how it is on x86 processors. Don't ask me why, go to
local Intel office ;). Then after INCrementing SI it's XORed by, yes you
guessed it this time, 7365h.
This process ends when the 0Dh is reached, what is the mark of the end of
the string appended to the string by DOS. All right, ebough about this
snippet, let's go further.
------------------------------------------------------------------------------
anti_loop:
cmp cs:anti_debug, 0
jnz anti_loop
------------------------------------------------------------------------------
This to lines do a simple thing: try to stop an unexpirienced cracker.
Remember what the program did when it was playing vectors? It also set an
interrupt 1ch (which is called, as you can remember, approx. 18 times a
second) to 'interrupt' procedure. It's presented below, but in fact it's a
liitle but farther in the code.
------------------------------------------------------------------------------
interrupt:
dec cs:anti_debug
iret
------------------------------------------------------------------------------
It simply decreases the anti_debug variable. Now you can imagine what's
going on. In the debugger the interrupts are disabled, so the loop never ends.
But when the prog is not traced the interrupts are enabled, so anti_debug is
being decreased so it must reach 0 sometime, and the loop would end then.
------------------------------------------------------------------------------
mov si, offset old_ofs
mov dx, [si]
push ax
mov ax, [si+2]
mov ds, ax
mov ax, 251Ch
int 21h ; DOS - SET INTERRUPT VECTOR
; AL = interrupt number
; DS:DX = new vector to be used for specified interrupt
------------------------------------------------------------------------------
After the anti debugging trick has ended the vectors are reset to the
original values and the value calcuted using xor_loop is pushed to the stack.
------------------------------------------------------------------------------
push cs
pop ds
add ax, 409h
shl ax, 3
sub ax, 4A3Eh
xchg ax, dx
mov ax, 300h
mov bx, 0FFFh
mov cx, 90h
mess_loop:
add ax, bx
xchg ax, bx
loop mess_loop
push bx
push ax
------------------------------------------------------------------------------
mess_loop does nothing but computing in AX c440h and in BX c69fh. This
values are constant on every computer, under any debugger everytime. It could
be replaced with simple MOV AX,C440h MOV BX,C69Fh.
------------------------------------------------------------------------------
mov ax, 3503h
int 21h ; DOS - 2+ - GET INTERRUPT VECTOR
; AL = interrupt number
; Return: ES:BX = value of interrupt vector
mov ax, bx
ror ax, 4
xor ax, 7343h
push ax
------------------------------------------------------------------------------
This one gets the 03h interrupt vector (it is used in debuggers for
breakpointing). It's not used later so we can forget about it. It tries just
to mess up our understanding of the program.
------------------------------------------------------------------------------
push bp
mov bp, sp
mov bx, [bp+4]
xor bx, 0B816h
xor [bp+8], bx
jnz write_wrong
mov ax, 900h
mov dx, offset right_pass
int 21h ; DOS - PRINT STRING
; DS:DX -> string terminated by "$"
jmp terminate
------------------------------------------------------------------------------
At last is the end. What is done here? The BX is loaded with [BP+4]. WTF
is that? Look at this memory location under the debugger. It's C440h (computed
earlier). BX gets XORed with B816h so finally BX is 7C56h. [BP+8] is XORed
with BX and if it's zero it's the right password! If it's not the password is
wrong.
------------------------------------------------------------------------------
write_wrong:
mov dx, offset wrong_pass
mov ax, 900h
int 21h ; DOS - PRINT STRING
; DS:DX -> string terminated by "$"
jmp terminate
start endp
------------------------------------------------------------------------------
Cracking the schema
What do we know about the right password? The value computed by xor_loop
equals to 7C56h. (Only n XOR n = 0). Now, let's think what the xor_loop does
with password. Assume password is 11 (0bh) chars long. Let's write the
password as 'abcdefghijk' plus 0dh char. How AX is XORed with these values?
AX = 6F25h
XORed by: b a
c b
d c
e d
f e
g f
h g
i h
j i
k j
0d k
=====
Should be: 7C56h
Let's notice the following about the XOR: having any given value (x) and a
second value (y) there is just one value which fulfills the equation:
x xor w = y
And we also know the value of w, which is:
w = x xor y
If you don't know why, study so mathemathics.
Now back to our table. Basing on the fact I described above it's enough to
compute 'k' and 'a' value having 'bcdefghij' given by the user! Writing such
a program is not hard and in cracker's jargon it's called keymaker.
6Fh xor (b xor c xor d xor e xor f xor g xor h xor i xor j) xor k xor 0dh = 7Ch
25h xor a xor (b xor c xor d xor e xor f xor g xor h xor i xor j) xor k = 56h
Let's assume
n = b xor c xor d xor e xor f xor g xor h xor i xor j
then
6Fh xor n xor k xor 0dh = 7Ch
25h xor a xor n xor k = 56h
after some calculations you get:
k = 1Eh xor n
a = 73h xor n xor k
because k = 1Eh xor n
a = 73h xor n xor 1Eh xor n
a = 73h xor 1Eh
a = 6Dh = 'm' !
What you see above is a proof that the first char is CONSTANT! Having given 9
chars that would be put inside the password we know the first char is 'm' and
we can compute the last! Here is the program which does the very thing:
program Test2KeyMaker;
var
S : string;
n : Byte;
a, k : Byte;
i : Byte;
begin
Writeln('Simple keymaker for TEST2 by LordByte');
Writeln('Written 27.1.1998 by Crook');
repeat
Write('Enter 9 char string: ');
Readln(S);
until Length(S) = 9;
n := 0;
for i := 1 to 9 do
n := n xor Ord(S[I]);
k := $1e xor n;
a := $6d;
Writeln('The password is: ', Chr(a), S, Chr(k));
end.
Ending
So that's it. You must admit that wasn't so difficult. Take a next TEST
in a row a try to crack it. You surely will learn something.
Crook