Rebels Trial-Cracker Test 1.0 by Rebels (RBS)
2 of 3 files
kouger
-
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:43 PM
MD5 checksum 01b3e002a290fef5cfbfe354803a018f
Mime type Zip archive data
Download RBSTRIAL.zip
Size 10 kB
1998 January 9
7 items in the archive
- RBSTRIAL/
- ARASHA/
- ARASHA/ARASHA.TXT
- ARASHA/RBSDECOD.EXE
- ARASHA/RBSDECOD.PAS
- ARASHA/RBSTRIAL.COM
- RBSTRIAL.COM
HOW TO CRACK RBSTRIAL.COM
A word of caution before we begin: this method is likely not the most
efficient as it's the first program I ever cracked. The good thing is
that it works though.
Alright, pick up a copy of RBSTRIAL.COM from Lord Caligo's site and
let's start. Run the program a couple of time to see the sequence of
actions. Since the file is very small (308 bytes), you might as well
print the entire disassembled code and have a look. Here's a
disassembly:
241E:0100 6B4F5547 IMUL CX,[BX+55],+47
241E:0104 45 INC BP
241E:0105 52 PUSH DX
241E:0106 21D2 AND DX,DX
241E:0108 4D DEC BP
241E:0109 5A POP DX
241E:010A E98A00 JMP 0197
....
241E:0190 F6D8 NEG AL
241E:0192 FEC3 INC BL
241E:0194 EB3F JMP 01D5
241E:0196 90 NOP
241E:0197 B409 MOV AH,09
241E:0199 BA0D01 MOV DX,010D
241E:019C CD21 INT 21
241E:019E BA8001 MOV DX,0180
241E:01A1 B40A MOV AH,0A
241E:01A3 CD21 INT 21
241E:01A5 42 INC DX
241E:01A6 8BF2 MOV SI,DX
241E:01A8 AC LODSB
241E:01A9 1C0A SBB AL,0A
241E:01AB 754D JNZ 01FA
241E:01AD 90 NOP
241E:01AE 90 NOP
241E:01AF 2BC0 SUB AX,AX
241E:01B1 33D2 XOR DX,DX
241E:01B3 46 INC SI
241E:01B4 BFE101 MOV DI,01E1
241E:01B7 BBA59D MOV BX,9DA5
241E:01BA B90B00 MOV CX,000B
241E:01BD 51 PUSH CX
241E:01BE F8 CLC
241E:01BF AC LODSB
241E:01C0 750F JNZ 01D1
241E:01C2 90 NOP
....
241E:01D1 02C3 ADD AL,BL
241E:01D3 72BB JB 0190
241E:01D5 0FAFCB IMUL CX,BX
241E:01D8 02CD ADD CL,CH
241E:01DA 2AC1 SUB AL,CL
241E:01DC AA STOSB
241E:01DD 750D JNZ 01EC
....
241E:01EC 86DF XCHG BL,BH
241E:01EE 59 POP CX
241E:01EF E2CC LOOP 01BD
241E:01F1 FD STD
241E:01F2 BECF01 MOV SI,01CF
241E:01F5 B90D00 MOV CX,000D
241E:01F8 F3 REPZ
241E:01F9 A6 CMPSB
241E:01FA B409 MOV AH,09
241E:01FC BA0802 MOV DX,0208
241E:01FF 7425 JZ 0226
241E:0201 90 NOP
241E:0202 90 NOP
241E:0203 CD21 INT 21
241E:0205 EB18 JMP 021F
....
241E:021F B8004C MOV AX,4C00
241E:0222 CD21 INT 21
....
241E:0226 BA6B01 MOV DX,016B
241E:0229 EBD8 JMP 0203
(The code segment will likely be different on your machine.) Now,
examine the code and trace through it with a debugger. Consider the
following section of the code:
241E:0197 B409 MOV AH,09
241E:0199 BA0D01 MOV DX,010D
241E:019C CD21 INT 21
241E:019E BA8001 MOV DX,0180
241E:01A1 B40A MOV AH,0A
241E:01A3 CD21 INT 21
If we look at the contends of address 010Dh, we see it contains the
introductory text. The first INT 21 call prints this text to the
screen. The second INT 21 call gets the user input and saves it at
0180h. If you dump the contents of memory starting at 0180h, you will
see your input. Now look at the next few lines:
241E:01A5 42 INC DX
241E:01A6 8BF2 MOV SI,DX
241E:01A8 AC LODSB
241E:01A9 1C0A SBB AL,0A
241E:01AB 754D JNZ 01FA
INC DX will set DX equal to 0180h+1h = 0181h. The contents of 0181h
will be the length of the user's input (when interrupt 21 saves the user
input, it precedes the data with the length of the user input). Thus,
the next three lines check whether the user entered 0ah or 10 characters
for the password. If the user did not, the computer will jump to 01fah
which, upon examination, prints out "Incorrect Password" and terminates
the program. We just learned our first clue: the correct password is 10
characters long.
Look at the next few lines:
241E:01AD 90 NOP
241E:01AE 90 NOP
241E:01AF 2BC0 SUB AX,AX
241E:01B1 33D2 XOR DX,DX
241E:01B3 46 INC SI
241E:01B4 BFE101 MOV DI,01E1
241E:01B7 BBA59D MOV BX,9DA5
241E:01BA B90B00 MOV CX,000B
241E:01BD 51 PUSH CX
241E:01BE F8 CLC
241E:01BF AC LODSB
241E:01C0 750F JNZ 01D1
After clearing the AX and DX registers, SI is incremented. SI should
now be pointing to the SECOND character of our input (remember this fact
for later). This is because the preceding LODSB command already
increased SI from 181h to 182h. SI is now 183h, which is where our
second input charater is. The next few lines initialize some
registers.
Note that DI is initialized to 01E1h. The LODSB will load AL with our
second input character. If that character is not equal to zero (which
it shouldn't unless you used escape codes in your input), we will jump
to 01D1h. Now comes the interesting part. Examine the next piece of
code:
241E:01D1 02C3 ADD AL,BL
241E:01D3 72BB JB 0190
241E:01D5 0FAFCB IMUL CX,BX
241E:01D8 02CD ADD CL,CH
241E:01DA 2AC1 SUB AL,CL
241E:01DC AA STOSB
241E:01DD 750D JNZ 01EC
....
241E:01EC 86DF XCHG BL,BH
241E:01EE 59 POP CX
241E:01EF E2CC LOOP 01BD
241E:01F1 FD STD
241E:01F2 BECF01 MOV SI,01CF
241E:01F5 B90D00 MOV CX,000D
241E:01F8 F3 REPZ
241E:01F9 A6 CMPSB
Here's the code for the JB instruction at 01D3:
241E:0190 F6D8 NEG AL
241E:0192 FEC3 INC BL
241E:0194 EB3F JMP 01D5
Hmmm. This looks like an encryption procedure. It's modifying each of
our input characters according to some algorithm and storing it
beginning at 01E1h (recall that DI was initialized to 01E1h). After the
encryption (i.e. starting at address cs:01F1h), you will see that the
program compares our encrypted password to the something (REPZ CMPSB).
Here is where the program checks to see whether we entered the correct
password. Now, let's figure out where the correct password is stored.
Since the direction flag is set (STD instruction at cs:01F1h), we know
that the REPZ CMPSB will compare bytes BACKWARDS from memory. Thus, we
know that the last character of the correct password is at ds:01CFh,
which is what the SI register is initialized to at cs:01F2h. The CX
register is initialized to Dh right before the comparison. This means
that the REPZ CMPSB instructions will compare a maximum of Dh
characters. Therefore, the beginning of the correct password is at
ds:01CFh - Dh + 1h = 01C3. Well, let's see what's there by dumping the
contents of memory beginning at ds:01C3:
241E:01C3 90 0A 56 7B D9 21 56 8A-CF F5 1F 61 86 34 02 C3
There's only one problem. That data isn't exactly the correct
password.
It's the ENCRYPTED correct password. Remember that the program first
encrypts our input and then compares our ENCRYPTED input with the above
data. Now, you might think, "that's simple, let's just decrypt the
data". You're on the right track but, unfortunately, decrypting the
password is much more complicated that you probably think.
By now, you must be thinking, if we just change CS:01FF from JZ 0226 to
JMP 0226, the program will say "Correct Password" for anything we type
in. Sure, we could do that, but kOUGER!'s challenge was for us to to
find out what the real password is.
To figure out what the password is, we must COMPLETELY understand what
the encryption algorithm is. So, sit back and examine the code
carefully. Examine it until you FULLY understand what's going on.
First, one character of our input is loaded into AL. Next, BL is added
to it. Then, if the addition caused an unsigned overflow (i.e. if AL +
BL > 255 causing the carry flag to be set), AL is negated and BL is
incremented by one. Next, the sum of the high and low bytes of CX*BX is
subtracted from AL to complete the encryption. After storing our
encrypted character, the high and low bytes of BX are exchanged and the
next character goes through the same process. Let's formalize this
algorithm mathematically. First, define a function f(x) which returns
the sum of the high and low bytes of its argument, x. For example,
f(2FB4h) = 2Fh + B4h = E3h. Here's how the encryption alorithm looks:
If AL + BL <= 255: If AL + BL > 255:
output = input + BL - f(BX*CX) output = -(input+BL) - f[(BX+1)*CX]
Let's re-arrange the equations to solve for the input (i.e. this is the
decryption algorithm):
If AL + BL <= 255: If AL + BL > 255:
input = output - BL + f(BX*CX) input = -(output - f[(BX+1)*CX])-BL
No problem, you say. Just substitute the values for the correct
encrypted password for "output" and solve for "input". While this seems
simple, it has two problems. Here's the first problem: given BX and CX,
there are up to two possible inputs which would yield the same output.
One for when AL + BL <= 255 and the jump at CS:01D3h is not taken, and
one for when AL + BL > 255 and jump at CS:01D3h is taken. For example,
if we want to find the correct input for 0Ah (i.e. the second character
of the correct encrypted password), we get either 42h or 39h which
corresponds to "B" and "9" respectively. We could choose either one and
proceed to decode the next character. The problem with this is that if
we chose the wrong one, we may find that the later characters we decode
fall outside the typeable range of ASCII characters. If we are VERY
lucky and chose the correct path through the decryption, we will get the
correct password. However, for 10 characters, there are 2^10 = 1024
different paths. That makes this option a little unattractive. The
second problem however renders this option even less attractive.
The astute readers will notice that the correct password is 10
characters in length whereas the encryption and compare routines encrypt
and compare 12 characters. Thus, the encryption procedure also encrypts
the two bytes following our 10 character input beginning at address
DS:0182h. Upon examination, we see that the program fixes these two
bytes to 0Dh and 00. When these two bytes are encrypted, they will be
compared to 1Fh and 61h (check the dump of the correct encrypted
password to see where these numbers come from). To fully understand
what this means, you have to realize that how each character is
encrypted depends on how the previous characters were encrypted. This
is because the BX register is changed according to whether the jump at
CS:01D3h is taken. Thus, the correct password is one which follows a
path which causes BX to equal a value which will correctly encode the
11th and 12th bytes to 1Fh and 61h, respectively.
Now, not only do we have to worry about taking a path which yields an
ASCII typeable password, but we must also make sure that it sets BX to a
value which correctly encodes the next two bytes. Solution: we will
write a program which will go through all the possible paths and see
whether one password gives us the same encryption as the correct
encrypted password. Before going any further, recall the encryption
procedure begins with the SECOND input character, not the first. Thus,
the first character of our input is never encrypted at all. Although
the password compare procedure actually compares the address where our
first input character would have been stored had it been encrypted, we
note that that address is fixed at 90h. Fortunately, 90h is exactly
what it is compared to in the password compare procedure. This gives us
the second clue to the password: it doesn't matter what you type for the
first character.
Alright, here's the pascal program which goes through all the possible
2^9 = 512 (remember, the first character is irrelevant) paths. It will
print out only those passwords which meet both of the following
criteria:
(1) All characters fall within the typeable ASCII range
(2) Characters 11 and 12 (i.e. those "hard-wired" to 0Dh and 00) are
correctly encoded to 1Fh and 61h, respectively.
Note also that this program prints only characters 2 to 10 of the
correct password(s) as the first character is irrelevant.
Program RbsTrialDecode (Input,Output);
Function LowByte(Value : word) : word;
Begin
LowByte := Value - (Value div 256)*256;
End;
Function HighByte(Value : word) : word;
Begin
HighByte := Value div 256;
End;
Function Encode(Var InBX : word;
InCX : word;
CorrectInput : byte) : byte;
Var
BX,
CX : word;
CorrectOutput,
BH,
BL,
CH,
CL,
TempByte : byte;
Begin
BX := InBX;
CX := InCX;
BH := HighByte(InBX);
BL := LowByte(InBX);
CorrectOutPut := CorrectInput + BL;
If (CorrectOutput < CorrectInput) Then
Begin
CorrectOutput := (CorrectOutput XOR 255) + 1;
BL := BL + 1;
End;
BX := 256*BH + BL;
CX := CX*BX;
CL := LowByte(CX);
CH := HighByte(CX);
CorrectOutput := CorrectOutput - CH - CL;
BH := HighByte(BX);
BL := LowByte(BX);
BX := 256*BL + BH;
InBX := BX;
Encode := CorrectOutput;
End;
Function Decode(Var InBX : word;
InCX : word;
CorrectOutput : byte;
NoJump : boolean) : Byte;
Var
BX,
CX : word;
CorrectInput : byte;
BH,
BL,
CH,
CL,
TempByte : byte;
Begin
If NoJump Then
Begin
CX := InCX;
BX := InBX;
BL := LowByte(InBX);
BH := HighByte(InBX);
CX := CX * BX;
CL := LowByte(CX);
CH := HighByte(CX);
TempByte := CL + CH;
CorrectInput := CorrectOutput + TempByte - BL;
BX := BH + 256*BL;
TempByte := CorrectInput + BL;
If TempByte >= CorrectInput Then
Begin
Decode := CorrectInput;
InBX := BX;
End
Else
Decode := 0;
End
Else
Begin
CX := InCX;
BX := InBX;
BL := LowByte(BX);
BH := HighByte(BX);
BL := BL + 1;
BX := BL + BH*256;
CX := BX * CX;
CL := LowByte(CX);
CH := HighByte(CX);
TempByte := CL+CH;
CorrectInput := ((CorrectOutput + TempByte) XOR 255) + 1 -
LowByte(InBX);
BX := BH + 256*BL;
TempByte := CorrectInput + LowByte(InBX);
If TempByte < CorrectInput Then
Begin
Decode := CorrectInput;
InBX := BX;
End
Else
Decode := 0;
End;
End;
Const
EncodedPassword : array[2..11] of byte =
(31,245,207,138,86,33,217,123,86,10);
Var
InBX,
InCX,
Counter1,
Counter2,
Mask,
Temp : word;
CorrectInput,
a,b : byte;
GoodPassword,
NoJump : boolean;
PasswordString : String;
Begin
Writeln;
For Counter1 := 0 to 511 do
Begin
Mask := 1;
InBX := 40357;
GoodPassword := True;
PasswordString := '';
For Counter2 := 1 to 9 do
Begin
Temp := Counter1 and Mask;
NoJump := Temp <> 0;
InCX := 12 - Counter2;
CorrectInput := Decode(InBX,InCX,EncodedPassword[InCX],NoJump);
If (CorrectInput <= 126) and (CorrectInput >= 32) Then
PasswordString := PasswordString + Chr(CorrectInput)
Else
Begin
PasswordString := PasswordString + Chr(CorrectInput);
GoodPassword := False;
End;
Mask := Mask * 2;
End {for};
a := Encode(InBX,2,13);
b := Encode(InBX,1,0);
If GoodPassword and (a = 31) and (b = 97) Then
Writeln(PasswordString);
End {for};
End.
When we run the program, we see that there are two possible passwords:
BS-Party!
i]5XTrty!
Remember that these are the 2nd to 10th characters and we can enter
anything for the first character. It is likely that kOUGER!'s intended
password was "RBS-Party!". Running the program and typing these
passwords, we see that we have correctly "cracked" the program. Looking
back now that I know the password, I think that it may be possible for
someone to figure out the password by hand (i.e. without writing a
program as I did) if he could see the pattern leading to the word
"Party!".