2615 lines
102 KiB
Plaintext
2615 lines
102 KiB
Plaintext
пм
|
||
л
|
||
ммм м
|
||
Влллллм мп
|
||
ВВлллллн млп
|
||
ВВлллллллм ммллп
|
||
м мм ВВлллллллпплмммммллллппп
|
||
п п м ВВВлВВллллмм лпппп
|
||
ол ВВлллллВллплппмн
|
||
л Вллллллллмлммллл
|
||
пВл Вллллллллллллллллм њ м
|
||
мп Влллллллллллллллллллм о
|
||
Влллллллллллллнллпплллм
|
||
Влллллллллллллллмллммллп о л
|
||
Влллллллллллллллллллм он о
|
||
Вллллллллллллллллллп лл о
|
||
Влллллллллллллллллм ллм н
|
||
м ВВллллллллллллллллл оллп нм
|
||
мВВммпВллллллллллллллВп олнн лн п мм
|
||
пВВллллллллллллллллллм ммлпон лн лллВ
|
||
пВлллллплллллллллллл млллллнон лн олллВ
|
||
плллллмАлллллллллн ллллллл л л ллллВ
|
||
плллллмлллллллп лллллллн л л ллл м
|
||
пллллллмпп лллллллп н ол лллмл
|
||
плллллллм оллллллм л ллллмпВм
|
||
п плллллллм ллллллВВлм мл лллллллл
|
||
пм ппплллллллммм оллллллВВн л млллллллл
|
||
Ап пппплллллммм лллллллВ п мВлллллллн
|
||
ппплллллммм ппллВВн мБВллллллл
|
||
пппплллммм ппм БВВлллллллн
|
||
ппппм мм мАБВВлллллл
|
||
п ппАБлллллн
|
||
плм ВААлллл
|
||
оВАлллн
|
||
лВлппп
|
||
пп
|
||
плп плп м л л л мппппмп
|
||
л л м мм пммм мм м м мм онмм мм он мм он ммм плмм
|
||
лм мл л л м п лп л он он л п он он мп м онп л онмп мпп пплм
|
||
ппм мпп он он он мммп пммп онммп л л пммлпм л ммп л пммм мпммммп
|
||
|
||
|
||
PRESENT :
|
||
|
||
ЩЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЛ
|
||
К К
|
||
К Training Tutorial for the PC. By Dr. Detergent / UNT'93 К
|
||
К К
|
||
ШЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭМ
|
||
|
||
|
||
|
||
|
||
|
||
|
||
Table of contents
|
||
ЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭ
|
||
|
||
|
||
|
||
Section:
|
||
ЭЭЭЭЭЭЭЭ
|
||
|
||
|
||
1 - Introduction.
|
||
2 - Before starting.
|
||
3 - Debugging through.
|
||
4 - Different file compression techniques.
|
||
5 - Once inside the game's code.
|
||
6 - Most common training byte structure composition.
|
||
7 - Searching for the most common training byte structure composition.
|
||
8 - Problems finding the most common training byte structure composition.
|
||
9 - Setting the break points.
|
||
10 - Once you have found the trainer data.
|
||
11 - Making a "hard-cheat"
|
||
12a - Generic trainer interfacing routine.
|
||
12b - Interfacing to the game's keyboard routine.
|
||
12c - Finding the game's keyboard handling routine.
|
||
12d - Prince of Persia II keyboard handling routine listing.
|
||
13 - Handling different DS values.
|
||
14 - Writing the trainer loader or TSR code.
|
||
15 - Generic TSR self-removal routine.
|
||
16a - Generic trainer code interfacing routine.
|
||
16b - Prince of Persia II interactive trainer interfacing routine.
|
||
16c - Finding the runtime CS:IP of the keyboard handling routine.
|
||
16d - Comparing the program's current IP.
|
||
16e - Interfacing with different keyboard handling routines.
|
||
17 - Fox Ranger Interactive 9 option trainer routine listing.
|
||
18 - Legend of Myra Interactive 10 option trainer routine listing.
|
||
19 - Interactive TSR/loader trainer examples.
|
||
20 - Summary.
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
ЩЭЭЭЭЭЭЭЭЭЭЭЭЭЛ
|
||
К Section 1 К
|
||
ШЭЭЭЭЭЭЭЭЭЭЭЭЭМ
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
Introduction:
|
||
ЭЭЭЭЭЭЭЭЭЭЭЭЭ
|
||
|
||
|
||
Every game player has at some point during gameplay, wished that he/she had
|
||
more lives/energy/weapons/time etc - just to be able to finish that level or
|
||
see the game's ending for that matter.
|
||
|
||
|
||
Have you ever played a game for months, each time getting better and better,
|
||
finally you make it to the last level only to find that the monster at the
|
||
end is impossible to kill even with all your past experience?
|
||
|
||
|
||
Ever play a game for 5 hours and finally get to the last level - just before
|
||
the ending intro, and suddenly get killed by some small rodent - and have to
|
||
restart all over?!
|
||
|
||
|
||
Well I am sure you have experienced the above. This is why hackers/crackers
|
||
developed a kind of "Training Aid" if you want to call it that. The
|
||
terminology training means to bring an individual to a higher degree of
|
||
success through practice. In computer terms, the phrase training was
|
||
developed by hackers on the C-64/Amiga.
|
||
|
||
|
||
Whenever someone played a game and couldn't finish it, when using a trainer,
|
||
the person could train on the last level and become proficient in the skills
|
||
required in mastering that level, then he could turn off the trainer and try
|
||
his newly acquired skills in the real thing.
|
||
|
||
|
||
The term CHEAT as some people refer to, is not a good description of what a
|
||
real trainer actually is. Most trainers are interactive, meaning that they
|
||
let you toggle certain things on/off or select different items during game
|
||
play - a cheat however, mostly gives you straight away unlimited lives/items
|
||
etc and rarely let's you "train" while playing the game.
|
||
|
||
|
||
Training games has been going on for ages. It started back as far as the
|
||
C-64, maybe even further. It has seen it's days on all the computers that
|
||
one can use to play games.
|
||
|
||
|
||
Training was revolutionized mostly on the Amiga computer. The games for the
|
||
Amiga are outstanding, and so are the trainers. I have seen trainers for
|
||
some games that I thought were the game itself.
|
||
|
||
|
||
On the PC, training started the day that games were designed for it. Since
|
||
it's early days, training on the PC has revolutionized from small
|
||
"cheats/character editors" to todays interactive, multi-functional, user
|
||
defined, mega-trainers.
|
||
|
||
|
||
This Training Tutorial was written for those who always wondered how one
|
||
makes trainers on the PC, and it portrays this art to you. Never the less,
|
||
it all depends on you - how good you are with understanding the assembly
|
||
language and programming.
|
||
|
||
|
||
I'm confident that your average ASM programmer or hacker will find the
|
||
information herein helpful and useful.
|
||
|
||
|
||
Through my many years of training on the PC, and after developing more than
|
||
285+ trainers/cracks, I have learned lots of different tricks and methods
|
||
that I will reveal to you in this training tutorial.
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
ЩЭЭЭЭЭЭЭЭЭЭЭЭЭЛ
|
||
К Section 2 К
|
||
ШЭЭЭЭЭЭЭЭЭЭЭЭЭМ
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
Before starting:
|
||
ЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭ
|
||
|
||
|
||
First, you must have a general knowledge of debugging software and ASM
|
||
programming. Before you begin to even think about training a certain game,
|
||
ask yourself the following questions :
|
||
|
||
|
||
1) Has a trainer already been released for that game - sometimes you spend
|
||
hours training a game only to find out just before you are about to
|
||
spread it world-wide, that there already is a trainer out for it - and
|
||
it is even better then yours!
|
||
|
||
2) Is the game trainable - if yes, what items/things can it be trained for?
|
||
You will be surprised how many requests I had to make trainers for
|
||
games that are not trainable - like text adventure games! - so make sure
|
||
that at least something in your game is trainable.
|
||
|
||
3) Is it worth it - are you going to spend 5 hours training a shareware
|
||
pacman-type game?!
|
||
|
||
4) Can you handle the code - do you think you can get by the game's nasty
|
||
encryption/anti-debugging routines or script-compiler type code?
|
||
|
||
5) Will you be able to make the trainer - do you think that you can write
|
||
the code that will integrate your trainer with the game and be able to
|
||
modify the necessary data? Sure you can find the necessary data to
|
||
alter in the program, but can you write up a TSR or a loader that can
|
||
integrate itself to the game's code and modify the necessary data
|
||
locations?
|
||
|
||
|
||
Once you have asked yourself the above questions, and feel confident about
|
||
your answers, then proceed to section 3.
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
ЩЭЭЭЭЭЭЭЭЭЭЭЭЭЛ
|
||
К Section 3 К
|
||
ШЭЭЭЭЭЭЭЭЭЭЭЭЭМ
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
Debugging Through:
|
||
ЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭ
|
||
|
||
|
||
This section deals with debugging the game program. It will outline the
|
||
various debuggers you can use, various compression methods and how to get
|
||
through them.
|
||
|
||
|
||
Before we even begin - what debugger are you going to use? Ok, I will
|
||
make this question simpler - if you have a 386+ CPU, what debugger are you
|
||
going to use?
|
||
|
||
|
||
That's right - SOFT-ICE 2.52+!. This is the best debugger. Below are
|
||
some other debuggers listed in priority order, you might consider using:
|
||
|
||
|
||
|
||
Turbo Debugger 386+
|
||
Code View 386+
|
||
Any other 386+ virtual mode debugger
|
||
Turbo Debugger 286-
|
||
Code View 286-
|
||
Periscope
|
||
Debug.exe
|
||
|
||
|
||
|
||
Ever since I have been cracking/training games, I have never seen a more
|
||
powerful or complete debugger than Soft-Ice. Now don't think that you have
|
||
to have Soft-Ice to train, I have used debug on my XT / CGA to train
|
||
some complex VGA games, - not even being able to see the screen, and having
|
||
the trainer tested on my friends VGA, back in the old days.
|
||
|
||
|
||
You can train a game with ANY debugger and still make a good trainer at
|
||
that. But using soft-ice will speed up the process extremely and yield the
|
||
best results. That's why in some examples here I will use Soft-Ice as the
|
||
main debugger.
|
||
|
||
|
||
Ok, so you are not lame, and do have a 386+ chip, vga, extended memory,
|
||
and soft-ice loaded. Now you have your game neatly installed and ready to
|
||
be debugged and trained.
|
||
|
||
|
||
Now you have to find the start-up file. This is the EXE or COM loader that
|
||
starts the game. This is where you will find the necessary data to train
|
||
your game - 95% of the time. Remember that the EXE/COM loader can load up
|
||
an OVR or a BIN file, so if the start-up file is really small like 5 k, then
|
||
you know for sure it's going to load in some overlay code.
|
||
|
||
|
||
Ok, so use soft-ice's LDR.EXE to load up the start-up file (or debug the
|
||
file with another debugger). Now unassemble the first instructions and
|
||
study the code. Try to determine if the code is compressed by something
|
||
like LZEXE, PKLITE, DIET, EXEPACK, Secure-Wrap or some other COM/EXE file
|
||
compressor.
|
||
|
||
|
||
You can skip this step if you know how to write a loader or a TSR.
|
||
Otherwise, this step gives you an idea if you can or can't train the
|
||
program - if you can't write a TSR or loader, then how can you interface and
|
||
change the necessary data in the game if it's encrypted? (Even if you knew
|
||
at what location the lives decrementing instruction etc is, searching the
|
||
EXE/COM file with a hex editor to do a hard-train will yield no results)
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
ЩЭЭЭЭЭЭЭЭЭЭЭЭЭЛ
|
||
К Section 4 К
|
||
ШЭЭЭЭЭЭЭЭЭЭЭЭЭМ
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
Different file compression techniques:
|
||
ЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭ
|
||
|
||
|
||
Here are some examples of the different compression techniques used on
|
||
COM/EXE files:
|
||
|
||
|
||
ЩЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЛ
|
||
К PKLITE 1.20 (COM File) К
|
||
ШЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭМ
|
||
|
||
|
||
0100 B8BDE2 MOV AX,E2BD
|
||
0103 BA2284 MOV DX,8422
|
||
0106 3BC4 CMP AX,SP
|
||
0108 7367 JNB 0171
|
||
010A 8BC4 MOV AX,SP
|
||
010C 2D4403 SUB AX,0344
|
||
010F 25F0FF AND AX,FFF0
|
||
0112 8BF8 MOV DI,AX
|
||
0114 B9A200 MOV CX,00A2
|
||
0117 BE7C01 MOV SI,017C
|
||
011A FC CLD
|
||
011B F3 REPZ
|
||
011C A5 MOVSW
|
||
011D 8BD8 MOV BX,AX
|
||
011F B104 MOV CL,04
|
||
0121 D3EB SHR BX,CL
|
||
0123 8CD9 MOV CX,DS
|
||
0125 03D9 ADD BX,CX
|
||
0127 53 PUSH BX
|
||
0128 33DB XOR BX,BX
|
||
012A 53 PUSH BX
|
||
012B CB RETF ;*** RETF Instruction ***
|
||
012C 0C01 OR AL,01
|
||
012E 50 PUSH AX
|
||
012F 4B DEC BX
|
||
0130 4C DEC SP
|
||
0134 20436F AND [BP+DI+6F],AL
|
||
0137 7072 JO 01AB
|
||
0139 2E CS:
|
||
|
||
|
||
* Note the RETF instruction at 012B. This instruction when encountered in
|
||
the beginning of the code like this, nearly always gives an indication
|
||
that the file is compressed.
|
||
|
||
|
||
(The code after 012B is just compressed garbage. When you see garbage
|
||
after a RETF instruction, found in the beginning of the code, than you are
|
||
nearly sure that the file is compressed by something)
|
||
|
||
|
||
ЩЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЛ
|
||
К LZEXE 0.91 (EXE File) К
|
||
ШЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭМ
|
||
|
||
|
||
000E 06 PUSH ES
|
||
000F 0E PUSH CS
|
||
0010 1F POP DS
|
||
0011 8B0E0C00 MOV CX,[000C]
|
||
0015 8BF1 MOV SI,CX
|
||
0017 4E DEC SI
|
||
0018 89F7 MOV DI,SI
|
||
001A 8CDB MOV BX,DS
|
||
001C 031E0A00 ADD BX,[000A]
|
||
0020 8EC3 MOV ES,BX
|
||
0022 FD STD
|
||
0023 F3 REPZ
|
||
0024 A4 MOVSB
|
||
0025 53 PUSH BX
|
||
0026 B82B00 MOV AX,002B
|
||
0029 50 PUSH AX
|
||
002A CB RETF ;*** RETF instruction ***
|
||
002B 2E CS:
|
||
002C 8B2E0800 MOV BP,[0008]
|
||
|
||
|
||
* Note the RETF instruction at 002A.
|
||
|
||
|
||
ЩЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЛ
|
||
К DIET 1.10a (COM File) К
|
||
ШЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭМ
|
||
|
||
|
||
0100 BE5409 MOV SI,0954
|
||
0103 BFDC13 MOV DI,13DC
|
||
0106 B91404 MOV CX,0414
|
||
0109 3BFC CMP DI,SP
|
||
010B 7204 JB 0111
|
||
010D B44C MOV AH,4C
|
||
010F CD21 INT 21
|
||
0111 FD STD
|
||
0112 F3 REPZ
|
||
0113 A5 MOVSW
|
||
0114 FC CLD
|
||
0115 8BF7 MOV SI,DI
|
||
0117 BF0001 MOV DI,0100
|
||
011A AD LODSW
|
||
011B AD LODSW
|
||
011C 8BE8 MOV BP,AX
|
||
011E B210 MOV DL,10
|
||
0120 E96D12 JMP 1390
|
||
0123 64 DB 64 \
|
||
0124 6C DB 6C / Garbage from here onwards.
|
||
|
||
|
||
ЩЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЛ
|
||
К EXEPACK ??? (EXE File) К
|
||
ШЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭМ
|
||
|
||
|
||
|
||
0010 8BE8 MOV BP,AX
|
||
0012 8CC0 MOV AX,ES
|
||
0014 051000 ADD AX,0010
|
||
0017 0E PUSH CS
|
||
0018 1F POP DS
|
||
0019 A30400 MOV [0004],AX
|
||
001C 03060C00 ADD AX,[000C]
|
||
0020 8EC0 MOV ES,AX
|
||
0022 8B0E0600 MOV CX,[0006]
|
||
0026 8BF9 MOV DI,CX
|
||
0028 4F DEC DI
|
||
0029 8BF7 MOV SI,DI
|
||
002B FD STD
|
||
002C F3 REPZ
|
||
002D A4 MOVSB
|
||
002E 50 PUSH AX
|
||
002F B83400 MOV AX,0034
|
||
0032 50 PUSH AX
|
||
0033 CB RETF ;*** RETF instruction ***
|
||
0034 8CC3 MOV BX,ES
|
||
0036 8CD8 MOV AX,DS
|
||
0038 48 DEC AX
|
||
0039 8ED8 MOV DS,AX
|
||
|
||
|
||
* Note the RETF instruction at 0033.
|
||
|
||
|
||
ЩЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЛ
|
||
К Some other compression method (EXE File) К
|
||
ШЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭМ
|
||
|
||
|
||
000C 8CD3 MOV BX,SS
|
||
000E 8EC3 MOV ES,BX
|
||
0010 8CCA MOV DX,CS
|
||
0012 8EDA MOV DS,DX
|
||
0014 8B0E0800 MOV CX,[0008]
|
||
0018 8BF1 MOV SI,CX
|
||
001A 83EE02 SUB SI,+02
|
||
001D 8BFE MOV DI,SI
|
||
001F D1E9 SHR CX,1
|
||
0021 FD STD
|
||
0022 F3 REPZ
|
||
0023 A5 MOVSW
|
||
0024 53 PUSH BX
|
||
0025 B82E00 MOV AX,002E
|
||
0028 50 PUSH AX
|
||
0029 8B2E0A00 MOV BP,[000A]
|
||
002D CB RETF ;*** RETF instruction ***
|
||
002E B80010 MOV AX,1000
|
||
0031 3BC5 CMP AX,BP
|
||
0033 7602 JBE 0037
|
||
0035 8BC5 MOV AX,BP
|
||
0037 2BE8 SUB BP,AX
|
||
0039 2BD0 SUB DX,AX
|
||
003B 2BD8 SUB BX,AX
|
||
003D 8EDA MOV DS,DX
|
||
003F 8EC3 MOV ES,BX
|
||
0041 B103 MOV CL,03
|
||
0043 D3E0 SHL AX,CL
|
||
0045 8BC8 MOV CX,AX
|
||
0047 D1E0 SHL AX,1
|
||
0049 48 DEC AX
|
||
004A 48 DEC AX
|
||
004B 8BF0 MOV SI,AX
|
||
004D 8BF8 MOV DI,AX
|
||
004F F3 REPZ
|
||
0050 A5 MOVSW
|
||
0051 0BED OR BP,BP
|
||
0053 75D9 JNZ 002E
|
||
0055 FC CLD
|
||
0056 8EC2 MOV ES,DX
|
||
0058 8EDB MOV DS,BX
|
||
|
||
|
||
* Note the RETF instruction at 002D.
|
||
|
||
|
||
So basically you get the picture. Now to trace through the code to the part
|
||
where the whole program uncompresses itself is really easy.
|
||
|
||
|
||
First, always remember to TRACE or PROCEED through any RETF instruction.
|
||
In most cases there is only one RETF instruction to trace or proceed
|
||
through. Then once you traced or proceeded through it, you will be either
|
||
at CS:0000 or somewhere else. The next step is simple too - Just
|
||
unassemble the code until you find the following instruction:
|
||
|
||
|
||
|
||
CS:
|
||
JMP FAR [BX]
|
||
|
||
|
||
|
||
Once found, simply go to the address containing CS:, then trace or proceed
|
||
through. Now you should have the clean uncompressed code. If you did not
|
||
find the above instruction, then try looking for another RETF instruction.
|
||
Once found go to it and trace or proceed through and you should have the
|
||
clean uncompressed code.
|
||
|
||
|
||
Remember some files may be compressed with 2 compression programs
|
||
(for "added protection" as software authors think!). If so, simply perform
|
||
the above steps twice.
|
||
|
||
|
||
Finding the part of the program that the file starts up at is helpful in 2
|
||
ways :
|
||
|
||
|
||
1) You are sure that the program didn't start executing any instructions
|
||
yet - like moving lives/energy etc variables into memory.
|
||
|
||
|
||
2) You can note the CS, DS, or any other memory variables so that when you
|
||
do write up a TSR or a loader, you will be able to interface it easier.
|
||
|
||
|
||
Note:
|
||
ЭЭЭЭЭ
|
||
|
||
|
||
(You need to know the program's current CS,DS upon startup, if you are
|
||
going to write the generic tsr or loader trainer interfacing routine as
|
||
outlined in section 12a).
|
||
|
||
|
||
If you don't care about getting to the program's very beginning, and just
|
||
want to get through the uncompression as fast as possible, then if using
|
||
soft-ice, set a break point on INT 21 - it will break in when the program
|
||
does a DOS VER check, a memory allocation call or any other function using
|
||
INT 21. All games and most programs have INT 21's present in their program
|
||
code.
|
||
|
||
|
||
Some of you might even want to start the game, get the game fully running,
|
||
and then break into the debugger. I use this method. Doing this has some
|
||
goo d points and some bad points. The good points might be that the CS,DS
|
||
values are already redefined and you more or less see where the program
|
||
keeps it's data values. Some bad points might be that you have skipped
|
||
past the value-initialization routine (of the lives etc). If you are
|
||
unexperienced, then I recommend that you do not use this method.
|
||
|
||
|
||
Some software programmers put anti-debugging routines in their software code
|
||
to deter hackers/crackers from cracking their software. This works to the
|
||
trainer's disadvantage - sometimes. I'm not going to describe the various
|
||
anti-debugging methods and their antidotes in this training tutorial - learn
|
||
all about it in the upcoming "Cracking on the PC - THE mega tutorial!"
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
ЩЭЭЭЭЭЭЭЭЭЭЭЭЭЛ
|
||
К Section 5 К
|
||
ШЭЭЭЭЭЭЭЭЭЭЭЭЭМ
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
Once inside the game's code:
|
||
ЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭ
|
||
|
||
|
||
Ok, so you are in the program now. What now? A lot of people have asked
|
||
me what's harder to do - crack a game or make a trainer for it. Well it
|
||
depends really. Some games can be cracked in 5 minutes, while making a good
|
||
trainer can take 8+ hours. But in general I think making trainers is a bit
|
||
more difficult than cracking. Mostly because making a trainer will always
|
||
consume more time - at LEAST 1 hour to find/make/write/package the
|
||
trainer.
|
||
|
||
|
||
Also, when cracking a game, you isolate the protection in a certain area of
|
||
the program, then focus all your attention on it and crack it. When
|
||
training a program, you are looking through everything, everywhere,
|
||
gathering all sorts of unnecessary data before finding the right bytes, let
|
||
alone understanding the game code operation.
|
||
|
||
|
||
But once again, there are short cuts to everything. This is why training
|
||
might be easier that cracking after all. Every program uses more or less
|
||
the same technique to decrement/increment your lives/energy/ammo/inventory
|
||
items, etc.
|
||
|
||
|
||
Through my years of training, I have narrowed it down to the most common
|
||
byte structure composition, as outlined in section 6.
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
ЩЭЭЭЭЭЭЭЭЭЭЭЭЭЛ
|
||
К Section 6 К
|
||
ШЭЭЭЭЭЭЭЭЭЭЭЭЭМ
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
Most common training byte structure composition:
|
||
ЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭ
|
||
|
||
|
||
= DECREMENTING =
|
||
|
||
|
||
Decrementing or subtracting means to decrease a certain thing. The game
|
||
uses various decrementing instructions to decrement (or subtract if you
|
||
like) your lives/energy/time/ammo/weapons/inventory items etc.
|
||
|
||
|
||
The following is a list of the most common decrementing instructions that
|
||
games use:
|
||
|
||
|
||
|
||
DEC WORD PTR [1234] - лллл
|
||
In HEX : FF 0E 34 12
|
||
|
||
DEC BYTE PTR [1234] - лллл
|
||
In HEX : FE 0E 34 12
|
||
|
||
SUB WORD PTR [1234],XX - ББ
|
||
In HEX : 83 2E 34 12 XX
|
||
|
||
SUB BYTE PTR [1234],XX - А
|
||
In HEX : 80 2E 34 12 XX
|
||
|
||
SUB [1234],AX - ББ
|
||
In HEX : 29 06 34 12
|
||
|
||
SUB [1234],DX - А
|
||
In HEX : 29 16 34 12
|
||
|
||
|
||
|
||
= INCREMENTING =
|
||
|
||
|
||
Incrementing or adding means to add a value to a certain thing. The game
|
||
uses various incrementing instructions to increment (or add if you like)
|
||
your current level/energy/time/ammo/weapons/inventory items etc. The
|
||
following is a list of the most common incrementing instructions that games
|
||
use:
|
||
|
||
|
||
|
||
INC WORD PTR [1234] - лллл
|
||
In HEX : FF 06 34 12
|
||
|
||
INC BYTE PTR [1234] - лллл
|
||
In HEX : FE 06 34 12
|
||
|
||
ADD WORD PTR [1234],XX - ВВВ
|
||
In HEX : 83 06 34 12 XX
|
||
|
||
ADD BYTE PTR [1234],XX - ББ
|
||
In HEX : 80 06 34 12 XX
|
||
|
||
|
||
|
||
Legend :
|
||
ЭЭЭЭЭЭЭЭ
|
||
|
||
|
||
|
||
лллл - Very common - nearly 100% probability.
|
||
|
||
ВВВ - Common - about 70% probability.
|
||
|
||
ББ - Likely - about 40% probability.
|
||
|
||
А - Sometimes - about 10% probability.
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
ЩЭЭЭЭЭЭЭЭЭЭЭЭЭЛ
|
||
К Section 7 К
|
||
ШЭЭЭЭЭЭЭЭЭЭЭЭЭМ
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
Searching for the most common training byte structure composition:
|
||
ЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭ
|
||
|
||
|
||
By now you should be in the program viewing the uncompressed code. Simply
|
||
start searching for the above bytes - depending for what you are looking
|
||
for. For starting out, you should not be concerned with searching for the
|
||
incrementing or adding instructions - these instructions are used for
|
||
incrementing levels in the game, or inventory, etc. Training for those
|
||
options is much harder at first, so stick to the decrementing instructions.
|
||
|
||
|
||
So now start searching for the most common decrementing instruction - mainly
|
||
the DEC WORD PTR [XXXX]. I will use this example because it's the most
|
||
common decrementing instruction that you will find. Obviously you can, and
|
||
you should, search for the other less common decrementing instructions too.
|
||
|
||
|
||
The following search example can be used to search for all the decrementing
|
||
and incrementing instructions.
|
||
|
||
|
||
|
||
Example:
|
||
ЭЭЭЭЭЭЭЭ
|
||
|
||
|
||
|
||
S CS:0 L FFFF FF 0E (Works with most debuggers)
|
||
|
||
|
||
|
||
Note:
|
||
ЭЭЭЭЭ
|
||
|
||
|
||
We only search for the first 2 bytes of the DEC/INC instruction because the
|
||
3rd and 4th bytes contain the value of the address where the DEC/INC is
|
||
going to take place.
|
||
|
||
|
||
To make things simpler, let's assume that you are searching for the above
|
||
example (DEC WORD PTR [XXXX]). I will use this example from now on.
|
||
Remember, you can apply this example the same way to search or process all
|
||
the other DEC/INC instructions, as outlined in section 6.
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
ЩЭЭЭЭЭЭЭЭЭЭЭЭЭЛ
|
||
К Section 8 К
|
||
ШЭЭЭЭЭЭЭЭЭЭЭЭЭМ
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
Problems finding the most common training byte structure composition:
|
||
ЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭ
|
||
|
||
|
||
If you didn't find anything, or just a few DEC's that are not related to
|
||
anything, then it's because of the following :
|
||
|
||
|
||
1) You are looking in the wrong CS. Some games have many different CS
|
||
values. If the program's current CS is 1200, and you search for the DEC
|
||
bytes and find about 4, and then run the program, break in again, and
|
||
notice CS is 2245, and search for those bytes again, you might find 30
|
||
or more, so make sure you search all the possible CS values in the game.
|
||
|
||
|
||
It's hard to break in, just hoping to find the next CS value in the
|
||
game - if any. A good technique is to search like this:
|
||
|
||
|
||
Find the lowest CS value in the game, - eg: 0900.
|
||
|
||
|
||
Then search CS:0 l FFFF FF 0E
|
||
Then search 2000:0 l FFFF FF 0E
|
||
Then search 3000:0 l FFFF FF 0E
|
||
Then search 4000:0 l ffff FF 0E
|
||
|
||
|
||
Etc - get it? If the CS is always high during the game, and you never
|
||
seem to be able to break in when it's lower, then start the search at
|
||
about 0800, and then proceed higher.
|
||
|
||
|
||
2) The second possibility (if you didn't find anything after searching for
|
||
all the listed decrementing instructions) is that the game is using a
|
||
different decrementing instruction.
|
||
|
||
|
||
3) The third possibility is that the game's code is a script-compiler type
|
||
code. You can forget about training this type of game - even if you are
|
||
an experienced trainer maker. But if you can train it, then you belong
|
||
to the TOP-GUN trainer makers!
|
||
|
||
|
||
The Script-compiler type code is found in such games from Sierra,
|
||
Delphine Software, Lucas Arts and CVS. It is a programming method
|
||
which uses pre-defined scripts to run certain program operations.
|
||
Everything from producing the sound on the sound blaster to drawing the
|
||
graphics on the screen is done all in the same program loop - using
|
||
different scripts. Therefore training, or even cracking this type of
|
||
game is really a pain, but never the less, can be done.
|
||
|
||
|
||
|
||
|
||
|
||
|
||
ЩЭЭЭЭЭЭЭЭЭЭЭЭЭЛ
|
||
К Section 9 К
|
||
ШЭЭЭЭЭЭЭЭЭЭЭЭЭМ
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
Setting the break-points:
|
||
ЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭ
|
||
|
||
|
||
From the search, you should have found quite a few of those decrementing
|
||
instructions in the game's loader. If you are using soft-ice, note the
|
||
current CS and write it down. Then unassemble that address, study the code
|
||
and make sure it's a valid decrement, then set a break point on execution
|
||
(BPX in soft-ice) for about the first 8 of the found DEC WORD PTR [XXXX]
|
||
instructions.
|
||
|
||
|
||
The reason for unassembling the found instructions first and then putting a
|
||
break point on execution - as with soft-ice, or a CC, as with other
|
||
debuggers, is because the bytes FF 0E can represent any other code or data
|
||
value in the program. When you unassemble that address where the bytes FF
|
||
0E where found and study the code, if you see that the prior or following
|
||
instructions are garbage or don't make sense, then don't bother setting a
|
||
break point on that address since it's not going to be executed anyway.
|
||
|
||
|
||
If you are not using soft-ice, do the above but instead of setting a break
|
||
point on execution, replace the first byte of the DEC instruction with CC -
|
||
so it will look like this :
|
||
|
||
|
||
|
||
Example:
|
||
ЭЭЭЭЭЭЭЭ
|
||
|
||
|
||
Original found instruction : FF 0E 34 12
|
||
1st byte replaced by CC : CC 0E 34 12
|
||
|
||
|
||
|
||
This will put an INT 3 at the beginning of that instruction. Your debugger
|
||
should break on INT 3 when it executes that instruction. Do this for about
|
||
the first 8 of the found DEC WORD PTR [XXXX] instructions.
|
||
|
||
|
||
Now why did we do the above? Well we want to see which one of the
|
||
decrementing instructions decrements the lives/energy/timer etc values. So
|
||
the next step is to run the program.
|
||
|
||
|
||
Get by the introduction screen, etc and start playing the game. (If your
|
||
debugger breaks in even before you get to the game, then simply remove that
|
||
break point on execution from that address - if using soft-ice, or
|
||
replace the CC value with FF. This is done since that instruction won't
|
||
have anything to do with decrementing your lives/energy/timer etc in the
|
||
game - since the game has not even started yet.
|
||
|
||
|
||
Once your game starts, and the debugger breaks in right away - simply run
|
||
the program again. If the same thing happens more than about 3 times, and
|
||
it always happens at the same address, then remove the break point on
|
||
execution from that address - if using soft-ice, or replace the CC value
|
||
with FF. The reason for this is because the game might be using that
|
||
instruction to do something else other than decrementing your
|
||
lives/energy/timer etc.
|
||
|
||
|
||
The next step is to try and get killed, or use your gun and waste a few
|
||
bullets, or do something like that - to see if any inventory options,
|
||
gadgets, energy bars, life counters, etc, are being decremented. If they
|
||
are, you will suddenly find yourself in the debugger. Suppose you just got
|
||
shot and even before you saw your energy bar decrease, the debugger broke
|
||
in. The first thing that you do is write down that address - CS:XXXX. Then
|
||
see what value is being decremented at that address.
|
||
|
||
|
||
|
||
Example:
|
||
ЭЭЭЭЭЭЭЭ
|
||
|
||
|
||
The debugger broke in at 45C8:
|
||
|
||
|
||
1170:45C7 RET
|
||
1170:45C8 DEC WORD PTR [0320]
|
||
|
||
|
||
|
||
Now simply view what is at that address (There is no CS:, ES:, or SS:
|
||
above the 45C8 instruction, so you know the default is DS:):
|
||
|
||
|
||
D DS:320 (Using soft-ice, or use your debugger's dump command)
|
||
|
||
|
||
|
||
If you energy bar has for example 6 energy bars, and the value at DS:0320 is
|
||
06, then you know you could very well have found the address where the game
|
||
stores your current energy value.
|
||
|
||
|
||
Now the next step is to check if that address is indeed the current energy
|
||
value storage address. Simply enter FF at DS:320 and then run the game
|
||
again - notice anything different - more energy bars? If so, you found it.
|
||
If not, then maybe you still found it, but there is another routine that
|
||
updates the screen with the current energy value.
|
||
|
||
|
||
So the next step is to NOP out that DEC instruction at that address. But
|
||
instead of doing that, simply replace the first 2 bytes of the DEC
|
||
instruction (FF 0E) with EB 02 - thus jumping to the next instruction. This
|
||
is useful if you ever want to restore that DEC instruction back - all you
|
||
have to do is replace EB 02 with FF 0E.
|
||
|
||
|
||
If you NOP it out completely, not only do you have to put NOP 4 times, but
|
||
you are erasing the address value of the DEC instruction so unless you wrote
|
||
down the address, you will have to restart the program to restore back that
|
||
instruction.
|
||
|
||
|
||
Ok, so you replaced the FF 0E with EB 02. Now run the game and notice if
|
||
some things are different - does the timer still go down, or are the enemies
|
||
still moving etc. Now get your energy to go down. If you notice it go
|
||
down, keep on getting hit until the whole energy bar declines. If it does,
|
||
and you are still alive, then the game has 2 separate routines for storing
|
||
and displaying the energy bar. (Maybe another DEC instruction, which you
|
||
have not yet processed, is responsible for this).
|
||
|
||
|
||
If you died, then try something else now. Try to waste some
|
||
bullets/inventory items etc. If they all decrement and nothing is different
|
||
in the game, than that DEC instruction does something else in the game.
|
||
|
||
|
||
Repeat the search command, as outlined in section 7, and process the next 8
|
||
DEC instructions. Do this until you have gone through them all. You should
|
||
find at least some decrementing instructions which decrease something
|
||
like the energy/lives/timer/enemy energy/inventory etc. If not, then search
|
||
for the next most common decrementing string, mainly the FE 0E -
|
||
DEC BYTE PTR [XXXX]
|
||
|
||
|
||
If you don't find anything there, proceed again with the next most common
|
||
decrementing string - until you find something. If you still don't seem
|
||
to be able to find anything worthwhile, then refer to section 8.
|
||
|
||
|
||
|
||
NOTE :
|
||
ЭЭЭЭЭЭ
|
||
|
||
|
||
The code for some new games is written in a way that whenever you set a
|
||
break point on a certain address, the debugger won't break there. Instead
|
||
it will produce an error, or simply will skip over that break point and
|
||
continue running the program as if nothing happened.
|
||
|
||
|
||
This is especially noticeable when using soft-ice's BPX command. So if you
|
||
really think that you have found the right DEC/INC instruction, but
|
||
soft-ice does not break in, then use the same method of putting a break
|
||
point as for the other debuggers - by putting a EB 02 there at that address.
|
||
Now see if any changes occur in the game play.
|
||
|
||
|
||
What I sometimes noticed when debugging this type of game is that after I
|
||
set a break point on a certain dec instruction, run and play the game, get
|
||
hit, and notice that my energy/lives etc don't go down - and soft-ice
|
||
does not break in. What happens there is that the game's code jumps over
|
||
the dec instruction which has a break point on it, thus never executing it.
|
||
If you encounter this, then it is yet another indication that the game is
|
||
using this type of weird coding.
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
ЩЭЭЭЭЭЭЭЭЭЭЭЭЭЭЛ
|
||
К Section 10 К
|
||
ШЭЭЭЭЭЭЭЭЭЭЭЭЭЭМ
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
Once you have found the trainer data:
|
||
ЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭ
|
||
|
||
|
||
So once you have found the locations where the game keeps all the goodies
|
||
- like your live/energy/timer value, etc, make sure you write down the
|
||
location of the DEC/INC instruction, and what memory area it decrements
|
||
or increments.
|
||
|
||
|
||
Once you become more experienced with training, you might take some time to
|
||
study the code next to the DEC/INC instructions and see if there are any
|
||
other goodies - like making your man be totally invincible to everything
|
||
etc.
|
||
|
||
|
||
There are also some built-in tricks that game authors put in - like a secret
|
||
cheat mode option etc, so the work might already be done for you. Sometimes
|
||
all that it takes is the value 01 at some memory location - and you have
|
||
everything set to unlimited etc.
|
||
|
||
|
||
A good way of finding this sort of thing is to trace into the decrementing
|
||
routine and study the code at the start of that routine - if they have a
|
||
CMP WORD/BYTE and then a JZ to the end of that routine, this could very well
|
||
be that WORD/BYTE you have been looking for. And if that's set, the whole
|
||
routine is bypassed and therefor there will be no decrementation of whatever
|
||
it was going to decrement.
|
||
|
||
|
||
Another thing you might check for, if looking for a secret built-in trainer
|
||
option in the game, is to check to see what command line parameters the game
|
||
checks for. Sometimes game authors put in secret command line parameter
|
||
options that activate the already built-in trainers. A good example is Wing
|
||
Commander from Origin. They have a secret command line parameter that
|
||
activates the game's built-in trainer.
|
||
|
||
|
||
Checking to see for what command line parameters the game checks is very
|
||
easy to do with soft-ice. Simply use LDR.EXE and load up the game's loader
|
||
with some garbage parameter string.
|
||
|
||
|
||
|
||
Example:
|
||
ЭЭЭЭЭЭЭЭ
|
||
|
||
|
||
LDR GAME.EXE testing
|
||
|
||
|
||
|
||
Now once in soft-ice, set a break point on memory range (BPM) at DS:0082 -
|
||
which points to your command line parameter "testing". Then run the program
|
||
and see what your "testing" string is compared to.
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
ЩЭЭЭЭЭЭЭЭЭЭЭЭЭЭЛ
|
||
К Section 11 К
|
||
ШЭЭЭЭЭЭЭЭЭЭЭЭЭЭМ
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
Making a "hard-cheat":
|
||
ЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭ
|
||
|
||
|
||
If you don't know anything about writing a loader or a tsr, then you might
|
||
consider making a "hard-cheat" - this means that you simply will HEX edit
|
||
the game's loader file and search for the bytes that make up the DEC
|
||
instruction(s) and nop them out. To do this, you can use the following
|
||
method:
|
||
|
||
|
||
Write down the HEX string that the decrementing instruction is composed of.
|
||
|
||
|
||
|
||
Example:
|
||
ЭЭЭЭЭЭЭЭ
|
||
|
||
|
||
You found this code :
|
||
|
||
|
||
15FF C3 RET ;Returns somewhere
|
||
1600 FF0E0734 DEC WORD PTR [3407] ;This is your dec
|
||
1604 833E073400 CMP WORD PTR [3407],+00 ;This CMP's it
|
||
1609 7415 JZ 160C ;This JMPS if Zero
|
||
160B C3 RET ;Returns somewhere
|
||
160C C606020301 MOV BYTE PTR [0302],01 ;This sets a byte
|
||
1611 C3 RET ;Returns somewhere
|
||
|
||
|
||
|
||
Now simply note the byte composition at 1600 - FF 0E 07 34. You might
|
||
also want to take note of the following bytes (83 3E 07 34 00 74 15 C3) just
|
||
to be sure you have the correct address when you search for them.
|
||
|
||
|
||
Remember thou, lots of games have more than 1 DEC/INC instruction, so it
|
||
might be a good idea to search for only the first 4 bytes that compose that
|
||
decrement/increment instruction, that way you will find them all.
|
||
|
||
|
||
So now you wrote down those bytes. Quit the game and use a hex editor or
|
||
debug.exe etc, and search the game's exe or com file for those bytes. Once
|
||
found, nop them out and save the file. If you are using debug.exe to make
|
||
the changes, and want to edit an EXE file, make sure that you rename the EXE
|
||
file to an extension like DAT, prior to debugging it. This is because you
|
||
can't write to EXE/HEX files with debug.exe.
|
||
|
||
|
||
|
||
Note:
|
||
ЭЭЭЭЭ
|
||
|
||
|
||
Look at the instruction above, at address 160C - MOVE BYTE PTR [0302],01.
|
||
Whenever the word at DS:[3407] is 0, the byte at DS:[0302] is set to 1.
|
||
What do you think this does? Here is the advantage of studying the game's
|
||
code around the DEC/INC instructions.
|
||
|
||
|
||
The game will check to see if the byte at DS:0302 is 1 and then it will
|
||
display "GAME OVER" or something like that - but if you nop out that
|
||
MOVE BYTE PTR [0302],01 instruction, your lives/energy/ammo/time etc will
|
||
still go down, but the game won't end or you will still have unlimited ammo
|
||
etc - because, in this example, the game checks somewhere in the program,
|
||
the byte at DS:0302, not the value of DS:3407 to make it's decision whether
|
||
to end or continue the game, etc.
|
||
|
||
|
||
Entering 1 byte (00) at CS:1610 as for the above example, will not only save
|
||
you 4 nops at CS:1600, but might even make you a better trainer using a
|
||
"No-Touch" or invincible mode option - because the game might always "think"
|
||
that you are alive etc.
|
||
|
||
|
||
As you will see, there are many ways of training a game.
|
||
|
||
|
||
|
||
Note:
|
||
ЭЭЭЭЭ
|
||
|
||
|
||
If you don't know how to write TSR's or loaders, then study the interactive
|
||
TSR and loader trainer examples included in this training tutorial package.
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
ЩЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЛ
|
||
К Section 12a К
|
||
ШЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭМ
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
Generic trainer interfacing routine:
|
||
ЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭ
|
||
|
||
|
||
By now you should have your addresses written neatly down on a piece of
|
||
paper. What now? Next step is interfacing your trainer with the game's
|
||
code.
|
||
|
||
|
||
There are many ways to interface your code into the game's code. I will
|
||
show you just the best one. I have seen so many people playing around with
|
||
the timer, having their own keyboard handling routines, hooking onto lots of
|
||
unnecessary interrupts - all this just to make a lousy 2 option "trainer".
|
||
Not only does this type of programming slow down the game, but it is much
|
||
harder and longer to write up this garbage code.
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
ЩЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЛ
|
||
К Section 12b К
|
||
ШЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭМ
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
Interfacing to the game's keyboard routine:
|
||
ЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭ
|
||
|
||
|
||
The following routine is the routine I use in all my trainers, and sometimes
|
||
cracks. Using this method, you can interface your code into practically any
|
||
software for the PC. It is by far the cleanest and best way to interface
|
||
your trainer into the game.
|
||
|
||
|
||
Practically all the new games today have their own keyboard handling
|
||
routine. The method in interfacing a trainer for those games who don't have
|
||
their own keyboard handling routine, is discussed in section 16e.
|
||
|
||
|
||
By now you should be still in the game. If you are not, simply restart the
|
||
game, and start playing it. Then break in with your debugger and set a
|
||
break-point on INT 9. To find the game's keyboard handling routine using
|
||
soft-ice, all you have to do is use the command BPINT 9, re-run the program
|
||
and press any key. You should now be in the game's keyboard handling
|
||
routine.
|
||
|
||
|
||
|
||
Note:
|
||
ЭЭЭЭЭ
|
||
|
||
|
||
Make sure you write down some bytes composing the beginning of the keyboard
|
||
handling routine. You will need them to search for that same routine again
|
||
- as referred to in the example, in section 14.
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
ЩЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЛ
|
||
К Section 12c К
|
||
ШЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭМ
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
Finding the game's keyboard handling routine:
|
||
ЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭ
|
||
|
||
|
||
The game usually saves the original INT 9 vector address and then redefines
|
||
the INT 9 vector address to point to it's keyboard handling routine. So
|
||
when you start debugging the game, trace it all the way until you notice
|
||
the INT 9 vector being redirected to another location. This is the location
|
||
that I'm referring to.
|
||
|
||
|
||
If you have problems finding the routine in the game's program code which
|
||
redirects INT 9, then you can do the following:
|
||
|
||
|
||
Start and play the game, then break in with your debugger and view the INT 9
|
||
vector address currently in the vector table (at 0000:0022).
|
||
|
||
|
||
|
||
Example:
|
||
ЭЭЭЭЭЭЭЭ
|
||
|
||
|
||
After you dumped 0000:0022 you see the following:
|
||
|
||
|
||
0000:0022 1F 10 20 AC XX XX XX XX XX XX XX XX XX XX XX XX
|
||
A B C D
|
||
|
||
|
||
Your main concern is with the first 4 bytes. I have named them A,B,C,D.
|
||
Now to find out where the game's keyboard handling routine points to, simply
|
||
view it this way:
|
||
|
||
|
||
BA:DC - now replace each letter with the value it stands for:
|
||
|
||
|
||
101F:AC20 - simple ey! - so if you set a break point on this address, and
|
||
then press any key, you will be right in the game's keyboard routine.
|
||
|
||
|
||
|
||
Once you set a break point on INT 9 or at the beginning of the keyboard
|
||
handling routine, run the game, and press any key. Your debugger should
|
||
break in. Now study the code. Below, in section 12d, is an example of the
|
||
beginning of a typical keyboard handling routine (taken from Prince of
|
||
Persia II).
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
ЩЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЛ
|
||
К Section 12d К
|
||
ШЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭМ
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
Prince of Persia II keyboard handling routine listing:
|
||
ЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭ
|
||
|
||
|
||
165D 1E PUSH DS ;Save current DS
|
||
165E 50 PUSH AX ;Save current AX
|
||
165F 53 PUSH BX ;Save current BX
|
||
1660 B8C03F MOV AX,3FC0 ;Move data-area value into AX
|
||
1663 8ED8 MOV DS,AX ;Move AX to DS
|
||
1665 E460 IN AL,60 ;*** Read keyboard port ***
|
||
1667 8AD8 MOV BL,AL ;Move read value in AL to BL
|
||
1669 D0C0 ROL AL,1 ;etc
|
||
166B 2401 AND AL,01 ;etc
|
||
|
||
|
||
Most keyboard handling routines have the same structure as the above. Note
|
||
the instruction at 1660 - MOV AX,3FC0 - this is the games data segment
|
||
address. This value is then moved to DS. Lots of games use this technique.
|
||
|
||
|
||
The above technique helps us a lot because your trainer doesn't always have
|
||
to find out what the game's current DS value is. This instruction is
|
||
nearly always present in the keyboard handling routines of most games.
|
||
|
||
|
||
The reason for this is as follows. Whenever you press a key in the game,
|
||
the game's current DS can be anything - because INT 9 will interrupt the
|
||
current operation of the program and execute the keyboard handling routine,
|
||
- with the DS value being whatever it was just before the INT 9 was called.
|
||
That is why the program has to reset the current DS address with the
|
||
predetermined DS address where it always keeps the key press values.
|
||
|
||
|
||
Most often, the DS address value used in the keyboard handling routine, is
|
||
the SAME as the DS address value for which the game uses to store it's
|
||
lives/energy/ammo etc values. - Sometimes this is not so. (Read "Handling
|
||
different DS values", outlined in section 13 for explanations how to cope
|
||
when the game's keyboard handling routine's DS value is different from
|
||
the DS value where the game keeps the energy/lives/ammo etc, values).
|
||
|
||
|
||
Once you have found the game's keyboard handling routine, your main concern
|
||
is the address where the keyboard port is read in - with the instruction
|
||
IN AL,60. Put a break point on that address and run the game. Press any
|
||
key now and you should be in the debugger. Write down the current IP where
|
||
the IN AL,60 instruction is. (You will need that IP value later on when
|
||
writing the trainer interfacing routine).
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
ЩЭЭЭЭЭЭЭЭЭЭЭЭЭЭЛ
|
||
К Section 13 К
|
||
ШЭЭЭЭЭЭЭЭЭЭЭЭЭЭМ
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
Handling different DS values:
|
||
ЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭ
|
||
|
||
|
||
Remember that the DS that the keyboard handling routine uses to store it's
|
||
data is not always the same DS that the game keeps the
|
||
lives/energy/ammo/timer etc values at. If the DS value in the keyboard
|
||
handling routine is different from the DS value where the game keeps your
|
||
lives/energy etc values, then you will have to do the following :
|
||
|
||
|
||
Write down the DS value that the keyboard handling routine uses and the DS
|
||
value that the game uses to store your lives/energy/ammo/timer etc values.
|
||
Quit the program and calculate how much to add or subtract from the keyboard
|
||
routine's DS value, to obtain the DS value that the game uses to store it's
|
||
lives/energy etc, values at. I use debug.exe to do the calculations.
|
||
|
||
|
||
|
||
Example:
|
||
ЭЭЭЭЭЭЭЭ
|
||
|
||
|
||
|
||
DS in the keyboard handling routine is 2CF0. The DS value where the game
|
||
keeps your lives/energy etc values is 1345 (which is lower than 2CF0, so
|
||
you will subtract it from 2CF0).
|
||
|
||
Using debug.exe:
|
||
|
||
|
||
A 100
|
||
XXXX:0100 MOV AX,2CF0
|
||
XXXX:0104 SUB AX,1345
|
||
|
||
|
||
|
||
Now simply proceed through those 2 instructions and note the AX value after
|
||
the SUB instruction. Write it down. In this example the value of AX after
|
||
subtraction is 19AB. So in your loader/tsr code you could do the following:
|
||
|
||
|
||
PUSH AX ;Save current AX value
|
||
PUSH DS ;Save current DS value
|
||
MOV AX,19AB ;Move 19AB to AX (the calculated value as shown above)
|
||
SUB DS,AX ;Subtract the game's current keyboard DS value with the
|
||
calculated AX value. Now DS will equal the DS value where
|
||
the game keeps the lives/energy etc values at.
|
||
|
||
|
||
|
||
Remember also that you don't necessary have to use DS always - the game can
|
||
be using CS to store your current lives/energy etc values - if so, simply
|
||
modify the above routine to work with CS. The above trick works for every
|
||
possible address, so you will always find your data.
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
ЩЭЭЭЭЭЭЭЭЭЭЭЭЭЭЛ
|
||
К Section 14 К
|
||
ШЭЭЭЭЭЭЭЭЭЭЭЭЭЭМ
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
Writing the trainer loader or TSR code:
|
||
ЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭ
|
||
|
||
|
||
By now you should have all the training-related information on paper. It
|
||
should include :
|
||
|
||
|
||
1) The addresses where the lives/energy etc are stored (XXXX:YYYY) - (not
|
||
the actual DEC/INC instruction address locations, but the addresses that
|
||
the DEC/INC instructions modify).
|
||
|
||
|
||
2) The value to add/decrement to/from the above addresses. (If you want
|
||
to increase your energy, for example, to full, note what value
|
||
represents energy full at that address, so when you later on define the
|
||
trainer keys, and select the energy-boost key, you will know what value
|
||
to add to the energy storage address to boost up the energy to max).
|
||
|
||
|
||
3) The address of the program's keyboard handling routine and the IP of the
|
||
IN AL,60 instruction. (If there is no IN AL,60 instruction in the
|
||
keyboard handling routine, then write down the address of the IP of your
|
||
chosen instruction to replace with CD 21 - for more information refer to
|
||
"Interfacing with different keyboard handling routines", outlined in
|
||
section 16e).
|
||
|
||
|
||
You should have 2 addresses of the program's keyboard handling routine.
|
||
The first one should be the address that you check/interface your
|
||
trainer code into the game's keyboard handling routine.
|
||
|
||
|
||
To get this address, start up your debugger, debug the game's loader and
|
||
set a break point on INT 21 - if using soft-ice, or trace the program to
|
||
the first INT 21. Then once you are there, don't trace into the INT 21,
|
||
just merely search for the keyboard handling routine using the program's
|
||
current CS. (You should have previously noted some of the bytes which
|
||
compose the beginning of the keyboard handling routine. Refer to the
|
||
"Note", back in section 12b).
|
||
|
||
|
||
|
||
Example:
|
||
ЭЭЭЭЭЭЭЭ
|
||
|
||
|
||
You are looking for the following bytes : E4 60 8A D8 D0 C0
|
||
|
||
|
||
S CS:0 L FFFF E4 60 8A D8 D0 C0
|
||
|
||
|
||
|
||
If you find nothing, try DS, ES, or SS. If you still find nothing, then
|
||
search higher in memory like 2000, 3000, 4000 etc. (Refer to section 9 for
|
||
more information on searching for data).
|
||
|
||
|
||
If you still don't find those bytes, then the keyboard handling routine in
|
||
the program might still be compressed or encrypted. Run the program for a
|
||
bit and then retry the above steps.
|
||
|
||
|
||
|
||
Note :
|
||
ЭЭЭЭЭЭ
|
||
|
||
|
||
If you didn't find the routine while searching with CS, DS, ES, or SS,
|
||
but found it when you searched the higher memory, like 3000 for example,
|
||
then you will have to do either one of the following:
|
||
|
||
|
||
3a) Set a break point on the next INT 21, or run the game for a bit, then
|
||
reset the break point back to INT 21. Then try again to search for the
|
||
keyboard routine's bytes - only using CS, DS, ES, or SS. If you still
|
||
don't find anything, then resort to step 3b.
|
||
|
||
|
||
3b) Since the keyboard handling routine address cannot be found using the
|
||
current CS, DS, ES or SS, you won't be able to interface your trainer
|
||
code using the above registers. You will have to use the method
|
||
described in section 13, and use the memory range address at which you
|
||
DID manage to find the keyboard handling routine with (eg: 0800:XXXX or
|
||
3000:XXXX etc).
|
||
|
||
|
||
|
||
The second address should be the game's current CS:IP when the game is
|
||
running. (For more information, refer to "Finding the runtime CS:IP of the
|
||
keyboard handling routine", outlined in section 16c).
|
||
|
||
|
||
|
||
Note:
|
||
ЭЭЭЭЭ
|
||
|
||
|
||
The 2 addresses described above, can be the same - the game's CS can be the
|
||
same at startup and once it's running, but if it's not, then follow the
|
||
above steps to obtain those 2 addresses. You will need them later on to
|
||
write the generic trainer code interfacing routine, as outlined in section
|
||
16a.
|
||
|
||
|
||
|
||
4) If the DS value in the program's keyboard handling routine is different
|
||
from the address where the game keeps the lives/energy etc values, as
|
||
outlined in section 13, you should have both the program's keyboard
|
||
handling routine's DS value and the address (XXXX:YYYY) where the
|
||
program keeps the lives/energy etc values at.
|
||
|
||
|
||
(You should also have calculated out what the final DS value should be
|
||
for the above. (For more information refer to "Handling different DS
|
||
values", outlined in section 13)).
|
||
|
||
|
||
5) The keyboard key press scan value that you will compare later on in your
|
||
trainer code, for your defined trainer keys.
|
||
|
||
|
||
|
||
Once you have necessary information as stated above, then you are ready for
|
||
the next step.
|
||
|
||
|
||
|
||
The loader or TSR that you are going to use has to be able to hook onto an
|
||
existing interrupt and redefine it's vector to your trainer routine. I
|
||
usually hook onto INT 21, but in theory you can hook onto any interrupt you
|
||
wish. But hooking onto INT 21 is preferable because of 2 things :
|
||
|
||
|
||
|
||
1) All the games will use INT 21 at some point in the game - therefor
|
||
activating your defined INT 21 routine, which in turn integrates itself
|
||
directly to the game's code.
|
||
|
||
|
||
2) There won't be much "confusion" once the game is running. - Games very
|
||
seldomly execute INT 21's during game play - so your INT 21 "interface"
|
||
will not slow-down/conflict with any game playing operations.
|
||
|
||
|
||
|
||
Also loader-trainers are better than writing TSR-trainers mainly because
|
||
they are "cleaner" - sure they both hook onto certain interrupts, but a
|
||
loader always restores it's hooked interrupt(s) upon exiting the program,
|
||
and in most cases, uses less memory.
|
||
|
||
|
||
If you don't know how to write loaders, or prefer to write TSR's, then I
|
||
suggest that you also include a self-removal option in your TSR - either
|
||
user requested or upon program termination. You can use the routine
|
||
outlined in section 15.
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
ЩЭЭЭЭЭЭЭЭЭЭЭЭЭЭЛ
|
||
К Section 15 К
|
||
ШЭЭЭЭЭЭЭЭЭЭЭЭЭЭМ
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
Generic TSR self-removal routine:
|
||
ЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭ
|
||
|
||
|
||
0100 1E PUSH DS
|
||
0101 50 PUSH AX
|
||
0102 52 PUSH DX
|
||
0103 06 PUSH ES
|
||
0104 0E PUSH CS
|
||
0105 1F POP DS
|
||
0106 A12C00 MOV AX,[002C]
|
||
|
||
|
||
0106 : Get the DOS environment segment address.
|
||
|
||
|
||
0109 8EC0 MOV ES,AX
|
||
010B B449 MOV AH,49
|
||
010D CD21 INT 21
|
||
|
||
|
||
010D : Free the allocated memory.
|
||
|
||
|
||
010F C5167801 LDS DX,[0200]
|
||
|
||
|
||
010F : Load pointer using DS - from DS:[0200] (DS=CS). This is done to
|
||
restore the original INT 21 vector. The original vector was saved at
|
||
CS:0200.
|
||
|
||
|
||
0113 B82125 MOV AX,2521
|
||
0116 CD21 INT 21
|
||
|
||
|
||
0116 : Hook and restore back the original INT 21 vector.
|
||
|
||
|
||
The below routine removes the TSR from the memory block:
|
||
|
||
|
||
0118 8CC8 MOV AX,CS
|
||
011A 48 DEC AX
|
||
011B 8ED8 MOV DS,AX
|
||
011D C70601000000 MOV WORD PTR [0001],0000
|
||
0123 07 POP ES
|
||
0124 5A POP DX
|
||
0125 58 POP AX
|
||
0126 1F POP DS
|
||
0127 CF IRET
|
||
|
||
|
||
0200 0000 0000 ;Original INT 21 vector saved here
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
ЩЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЛ
|
||
К Section 16a К
|
||
ШЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭМ
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
Generic trainer code interfacing routine:
|
||
ЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭ
|
||
|
||
|
||
The way this routine works is by hooking itself to the game's code, mainly
|
||
at the address where the IN AL,60 - keyboard port read instruction is. It
|
||
replaces the original bytes of that instruction (E4 60) with CD 21. Every
|
||
time you press any key during the game, your trainer routine is executed
|
||
instantaneously.
|
||
|
||
|
||
I will take you step by step through the next example, taken from the
|
||
Interactive 8 option trainer for Prince of Persia II.
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
ЩЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЛ
|
||
К Section 16b К
|
||
ШЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭМ
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
Prince of Persia II interactive trainer interfacing routine:
|
||
ЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭ
|
||
|
||
|
||
0100 9C PUSHF ;Push Flag
|
||
0101 55 PUSH BP ;Push BP
|
||
0102 1E PUSH DS ;Push DS
|
||
0103 89E5 MOV BP,SP ;Move current SP to BP
|
||
0105 8E5E08 MOV DS,[BP+08] ;Move current CS to DS.
|
||
|
||
|
||
The principle of operation of the 0105 instruction is as follows.
|
||
|
||
|
||
Whenever an interrupt is called, the original FLAGS, CS, and IP are pushed
|
||
into the stack. Now if you move the current SP to BP, then move the value
|
||
at SS:[BP+08] to DS, you will get the program's current CS.
|
||
|
||
|
||
0108 26 ES:
|
||
0109 813E6516E460 CMP WORD PTR [1665],60E4
|
||
|
||
|
||
0109 : Compare the values at address ES:1665 to 60 E4.
|
||
|
||
|
||
Why ES:? Remember that your trainer routine is hooked onto INT 21. Now
|
||
whenever the game starts up - the very first INT 21 executed, will either
|
||
be the dos version checking INT 21, or a memory allocating INT 21 etc.
|
||
|
||
|
||
Now every time ANY INT 21 is executed in the game, if the current CS:IP is
|
||
let's say at 2300:1200, and the keyboard handling routine's IN AL,60 address
|
||
is at 14FA:0377, then you won't be able to interface your code to the
|
||
keyboard handling routine's code, since the program's current CS is way
|
||
higher than 14FA.
|
||
|
||
|
||
The reason I used the ES value is because the value was just perfect - I
|
||
found the E4 60 bytes at address 1665 when searching with ES:, but couldn't
|
||
find them when searching with CS, DS, or SS. (You see, it's a good idea
|
||
to search with DS, ES, or SS first - if unable to find anything using CS,
|
||
before adverting to the procedures outlined in section 13).
|
||
|
||
|
||
But in most cases you will be able to interface directly to the game's
|
||
keyboard handling routine with the program's current CS value. (For more
|
||
information, refer to step 3 in "Writing the trainer loader or tsr",
|
||
outlined in section 14).
|
||
|
||
|
||
0109 : The instruction at 0109 is checking if at ES:1665 the bytes E4 60
|
||
exist - if they do, it means that's where the instruction IN AL,60
|
||
is. (Refer to "Prince of Persia II keyboard handling routine
|
||
listing", outlined in section 12d).
|
||
|
||
|
||
|
||
010F 7507 JNZ 0118 ; If it is not, then jump to the exit
|
||
portion of your routine.
|
||
|
||
|
||
0111 26 ES:
|
||
0112 C7066516CD21 MOV WORD PTR [1665],21CD
|
||
|
||
|
||
|
||
0112 : Else, replace the instruction at ES:1665 with CD 21 (your already
|
||
hooked INT 21 handling routine).
|
||
|
||
|
||
0118 817E066715 CMP WORD PTR [BP+06],1567
|
||
011D 7405 JZ 0124
|
||
|
||
|
||
0118 : Compare if SS:BP+06 (which is the game's current IP BEFORE it
|
||
entered your INT 21 hooked routine) to 1567. This routine is
|
||
comparing if the INT 21 instruction is YOURS or if it's some other
|
||
INT 21 instruction used by the game. This is accomplished by
|
||
comparing the game's current IP (instruction pointer) to 1567. If it
|
||
is indeed YOUR inserted INT 21 routine calling, then the trainer
|
||
JMPS to it's trainer routine (which starts here at 0124).
|
||
|
||
|
||
In this example, you will notice that the IP is different from the address
|
||
1665 - it's 1567. Why? Simple, because when the game runs, the CS was
|
||
different from ES - which you previously used to insert the CD 21 with.
|
||
(For more information regarding the IP, refer to section 16c).
|
||
|
||
|
||
The routine below, restores DS,BP,FLAGS and jumps to the original INT 21
|
||
vector:
|
||
|
||
|
||
011F 1F POP DS
|
||
0120 5D POP BP
|
||
0121 9D POPF
|
||
0122 EB77 JMP 019B
|
||
|
||
|
||
The above routine is executed due to one of the following:
|
||
|
||
|
||
1) Either the E4 60 value was not found at the specified address or;
|
||
|
||
|
||
2) The program's current IP is not pointing to your inserted INT 21 IP
|
||
address. (This might be another INT 21 instruction that the game is
|
||
currently using somewhere else - so the trainer code will restore DS, BP
|
||
and the FLAGS, and jump to the original INT 21 saved vector, thus
|
||
letting the game do whatever it wanted to.
|
||
|
||
|
||
Else, the following code is executed :
|
||
|
||
|
||
0124 1F POP DS
|
||
|
||
|
||
0124 : Restore the program's current DS. In this case the keyboard handling
|
||
routine's DS value is the same for where the game keeps it's key
|
||
presses and where it stores the value of your current
|
||
energy/time/level etc, values. That's why I restored DS right here,
|
||
so my trainer can use it later on. (And also note that for this
|
||
trainer example, you don't need to use the procedures listed in
|
||
"Handling different DS values", as outlined in section 13)
|
||
|
||
|
||
0125 E460 IN AL,60
|
||
|
||
|
||
|
||
0125 : This instruction reads the keyboard port. This instruction has to be
|
||
present in the trainer code, since you replaced it with CD 21 in the
|
||
game's code, remember?
|
||
|
||
|
||
|
||
The following code compares the key press to the function keys defined for
|
||
the trainer, and jumps correspondingly:
|
||
|
||
|
||
|
||
0127 3C3B CMP AL,3B
|
||
0129 741F JZ 014A
|
||
012B 3C3C CMP AL,3C
|
||
012D 7422 JZ 0151
|
||
012F 3C3D CMP AL,3D
|
||
0131 745D JZ 0190
|
||
0133 3C3E CMP AL,3E
|
||
0135 7421 JZ 0158
|
||
0137 3C3F CMP AL,3F
|
||
0139 7433 JZ 016E
|
||
013B 3C40 CMP AL,40
|
||
013D 7436 JZ 0175
|
||
013F 3C43 CMP AL,43
|
||
0141 743D JZ 0180
|
||
0143 3C44 CMP AL,44
|
||
0145 7441 JZ 0188
|
||
|
||
|
||
If some other key was pressed, which is not used by the defined trainer
|
||
keys, the following routine is executed. It merely restores the BP, FLAGS
|
||
and IRETS back to the program. The AX value however was not saved in the
|
||
beginning of the routine, and is always different upon returning back to
|
||
the program. There, it is used by the game to determine what keys were
|
||
pressed. (The original DS value was restored earlier remember?).
|
||
|
||
|
||
0147 5D POP BP
|
||
0148 9D POPF
|
||
0149 CF IRET
|
||
|
||
|
||
The following instructions change the data values in the program's DS to
|
||
train the game. The trainer data was derived using the same techniques as
|
||
outlined in this training tutorial.
|
||
|
||
|
||
014A C606865C01 MOV BYTE PTR [5C86],01
|
||
014F EBF6 JMP 0147
|
||
0151 C606865C00 MOV BYTE PTR [5C86],00
|
||
0156 EBF7 JMP 014F
|
||
0158 C6060D5C21 MOV BYTE PTR [5C0D],21
|
||
015D C6064D5C21 MOV BYTE PTR [5C4D],21
|
||
0162 C606CD5C21 MOV BYTE PTR [5CCD],21
|
||
0167 C6060D5D21 MOV BYTE PTR [5D0D],21
|
||
016C EBE8 JMP 0156
|
||
016E C6062A5EFF MOV BYTE PTR [5E2A],FF
|
||
0173 EBF7 JMP 016C
|
||
0175 C6062C5E01 MOV BYTE PTR [5E2C],01
|
||
017A FE06465E INC BYTE PTR [5E46]
|
||
017E EBF3 JMP 0173
|
||
0180 C706825C9300 MOV WORD PTR [5C82],0093
|
||
0186 EBF6 JMP 017E
|
||
0188 C706825CC101 MOV WORD PTR [5C82],01C1
|
||
018E EBF6 JMP 0186
|
||
0190 C706925C1919 MOV WORD PTR [5C92],1919
|
||
0196 EBF6 JMP 018E
|
||
0198 90 NOP
|
||
0199 90 NOP
|
||
019A 90 NOP
|
||
019B EA00000000 JMP XXXX:XXXX ;Jump back to the original INT 21
|
||
vector.
|
||
|
||
|
||
|
||
For a better understanding of the above code, study the Prince of Persia II
|
||
interactive 8 option TSR or loader trainer examples (PP2T-TSR.COM and
|
||
PP2T-LDR.COM and their DOC - PP2T-T&L.DOC). Both are included in this
|
||
training tutorial package. They correspond exactly to the above example.
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
ЩЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЛ
|
||
К Section 16c К
|
||
ШЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭМ
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
Finding the runtime CS:IP of the keyboard handling routine:
|
||
ЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭ
|
||
|
||
|
||
To always know what the current IP will be once the program is running,
|
||
simply set a break point in the program's keyboard handling routine, press a
|
||
key, and once your debugger breaks in, note the address of the keyboard
|
||
handling routine.
|
||
|
||
|
||
Write down the address of the IN AL,60 instruction. If the game doesn't use
|
||
INT 9 or IN AL,60 in it's keyboard handling routine, then refer to
|
||
"Interfacing with different keyboard handling routines", as outlined in
|
||
section 16e).
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
ЩЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЛ
|
||
К Section 16d К
|
||
ШЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭМ
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
Comparing the program's current IP:
|
||
ЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭ
|
||
|
||
|
||
When comparing the program's current IP - like in the above example in
|
||
section 16b, at 0118, you have to remember that the program's current IP
|
||
points to the address AFTER the interrupt was called.
|
||
|
||
|
||
|
||
Example:
|
||
ЭЭЭЭЭЭЭЭ
|
||
|
||
|
||
If the code looks like this :
|
||
|
||
|
||
0100 E460 IN AL,60
|
||
0102 88C3 MOV BL,AL
|
||
|
||
|
||
And you replace E4 60 with CD 21:
|
||
|
||
|
||
0100 CD21 INT 21
|
||
0102 88C3 MOV BL,AL
|
||
|
||
|
||
|
||
Then once the program executes your interrupt 21, and you check for the
|
||
program's current IP as described above, make sure you compare the IP to
|
||
0102! - the instruction right after the INT 21 - because after all, once the
|
||
program exits from your INT 21 routine via the IRET instruction, it doesn't
|
||
return back to 0100, it returns to the next following instruction.
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
ЩЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЛ
|
||
К Section 16e К
|
||
ШЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭМ
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
Interfacing with different keyboard handling routines:
|
||
ЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭ
|
||
|
||
|
||
All games have a keyboard handling routine. But some rare OLD games might
|
||
not use INT 9 to handle their key presses, or they might not use the IN
|
||
AL,60 instruction - just INT 16 for checking key presses.
|
||
|
||
|
||
So what's the problem there? Again, simply find a 2 byte instruction
|
||
somewhere right after the INT 16 instruction that you can replace with CD
|
||
21, and you are in business.
|
||
|
||
|
||
|
||
Example:
|
||
ЭЭЭЭЭЭЭЭ
|
||
|
||
|
||
0100 30E4 XOR AH,AH
|
||
0102 CD16 INT 16 ;here is the game's INT 16
|
||
0104 88C3 MOV BL,AL
|
||
0106 80EB11 SUB BL,11
|
||
|
||
|
||
|
||
There is a nice instruction at 0104 that you can change to CD 21. Then all
|
||
you have to do in your INT 21 hooked routine is to execute that instruction
|
||
somewhere in the beginning or the end of your code - doesn't really matter
|
||
where, but make sure BL equals the AL key press value, once your routine
|
||
IRETS back to 0106.
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
ЩЭЭЭЭЭЭЭЭЭЭЭЭЭЭЛ
|
||
К Section 17 К
|
||
ШЭЭЭЭЭЭЭЭЭЭЭЭЭЭМ
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
Below is another example, taken from the Fox Ranger interactive 9 option
|
||
trainer. Notice that at 010A, there is no E4 60 (IN AL,60). I'm hooking
|
||
INT 21 at some address which is in the game's program loop - you see, it can
|
||
be done in lots of different ways, as described above in "Interfacing with
|
||
different keyboard handling routines", in section 16e. I will only explain
|
||
the important stuff in the following example:
|
||
|
||
|
||
|
||
Fox Ranger interactive 9 option trainer routine listing:
|
||
ЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭ
|
||
|
||
|
||
0100 9C PUSHF
|
||
0101 55 PUSH BP
|
||
0102 1E PUSH DS
|
||
0103 89E5 MOV BP,SP
|
||
0105 8E5E08 MOV DS,[BP+08]
|
||
0108 26 ES:
|
||
0109 813EBA1DB000 CMP WORD PTR [1DBA],00B0
|
||
010F 7507 JNZ 0118
|
||
|
||
|
||
0109 : Compare ES:[1DBA] to 00 B0, if not, restore DS,BP,FLAGS and jump to
|
||
the original INT 21 vector.
|
||
|
||
|
||
0111 26 ES:
|
||
0112 C706BA1DCD21 MOV WORD PTR [1DBA],21CD
|
||
|
||
|
||
0112 : Hook your defined INT 21 routine at ES:[1DBA]
|
||
|
||
|
||
0118 817E06BC1C CMP WORD PTR [BP+06],1CBC
|
||
011D 7405 JZ 0124
|
||
|
||
|
||
0118 : Compare current SS:[BP+06] (BP=SP) which is the program's current IP
|
||
to 1CBC - if it's at your INT 21 IP, then jmp to the trainer routine,
|
||
else restore DS,BP,FLAGS and jump to the original INT 21 vector
|
||
(as in 011F-0122).
|
||
|
||
|
||
011F 1F POP DS
|
||
0120 5D POP BP
|
||
0121 9D POPF
|
||
0122 EB7D JMP 01A1
|
||
0124 8CDD MOV BP,DS
|
||
0126 06 PUSH ES
|
||
0127 1F POP DS
|
||
0128 A0722D MOV AL,[2D72]
|
||
|
||
|
||
0128 : Move into AL the key press which the game stored at ES:[2D72] - (As
|
||
you see, the game does not always have to have the IN AL,60
|
||
instruction, for you to be able to interface your trainer with the
|
||
keyboard. As long as you find out where the game stores it's key
|
||
presses, you will always be in business.
|
||
|
||
|
||
The following compares the key press to see if it's one of the defined
|
||
trainer key presses:
|
||
|
||
|
||
012B 3C4A CMP AL,4A
|
||
012D 7459 JZ 0188
|
||
012F 3C4E CMP AL,4E
|
||
0131 744B JZ 017E
|
||
0133 3C26 CMP AL,26
|
||
0135 7461 JZ 0198
|
||
0137 3C30 CMP AL,30
|
||
0139 7427 JZ 0162
|
||
013B 3C20 CMP AL,20
|
||
013D 742A JZ 0169
|
||
013F 3C32 CMP AL,32
|
||
0141 7412 JZ 0155
|
||
0143 3C21 CMP AL,21
|
||
0145 744B JZ 0192
|
||
0147 3C24 CMP AL,24
|
||
0149 7425 JZ 0170
|
||
014B 3C1E CMP AL,1E
|
||
014D 7428 JZ 0177
|
||
014F B000 MOV AL,00
|
||
0151 1F POP DS
|
||
0152 5D POP BP
|
||
0153 9D POPF
|
||
0154 CF IRET
|
||
|
||
|
||
The following is the training routine. The training data was derived using
|
||
the same techniques as outlined in this training tutorial.
|
||
|
||
|
||
0155 A2802F MOV [2F80],AL
|
||
0158 893E812F MOV [2F81],DI
|
||
015C 893E832F MOV [2F83],DI
|
||
0160 EBED JMP 014F
|
||
0162 C606BF2F01 MOV BYTE PTR [2FBF],01
|
||
0167 EBF7 JMP 0160
|
||
0169 C6067A2F01 MOV BYTE PTR [2F7A],01
|
||
016E EBF7 JMP 0167
|
||
0170 C606244305 MOV BYTE PTR [4324],05
|
||
0175 EBF7 JMP 016E
|
||
0177 C6067E2F01 MOV BYTE PTR [2F7E],01
|
||
017C EBF7 JMP 0175
|
||
017E 8EDD MOV DS,BP
|
||
0180 C70660DD0900 MOV WORD PTR [DD60],0009
|
||
0186 EBF4 JMP 017C
|
||
0188 8EDD MOV DS,BP
|
||
018A C70660DDB304 MOV WORD PTR [DD60],04B3
|
||
0190 EBF4 JMP 0186
|
||
0192 FE06792F INC BYTE PTR [2F79]
|
||
0196 EBF8 JMP 0190
|
||
0198 FE06762F INC BYTE PTR [2F76]
|
||
019C EBF8 JMP 0196
|
||
019E 90 NOP
|
||
019F 90 NOP
|
||
01A0 90 NOP
|
||
01A1 EA00000000 JMP XXXX:XXXX ; Jump back to the original INT 21
|
||
vector.
|
||
|
||
|
||
|
||
For a better understanding, study the Fox Ranger interactive 9 option
|
||
tsr trainer example (FRT-TSR.COM and read it's DOC - FRT-TSR.DOC) included
|
||
in this trainer package. It corresponds exactly to the above example.
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
ЩЭЭЭЭЭЭЭЭЭЭЭЭЭЭЛ
|
||
К Section 18 К
|
||
ШЭЭЭЭЭЭЭЭЭЭЭЭЭЭМ
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
Below is another example, taken from the Legend of Myra Interactive 10
|
||
option trainer:
|
||
|
||
|
||
|
||
Legend of Myra Interactive 10 option trainer routine listing:
|
||
ЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭ
|
||
|
||
|
||
0100 9C PUSHF
|
||
0101 55 PUSH BP
|
||
0102 1E PUSH DS
|
||
0103 89E5 MOV BP,SP
|
||
0105 8E5E08 MOV DS,[BP+08]
|
||
0108 813E250E8AD8 CMP WORD PTR [0E25],D88A
|
||
010E 7506 JNZ 0116
|
||
|
||
|
||
0108 : Compare the word at CS:[0E25] (DS=CS because of the instruction at
|
||
105) to D8 8A. Skip the following instruction if not zero.
|
||
|
||
|
||
0110 C706250ECD21 MOV WORD PTR [0E25],21CD
|
||
|
||
|
||
0110 : Interface your INT 21 hooked routine at CS:[0E25]
|
||
|
||
|
||
0116 817E06270E CMP WORD PTR [BP+06],0E27
|
||
011B 7406 JZ 0123
|
||
|
||
|
||
0116 : Compare the word at SS:[BP+06] (which is the program's current IP) to
|
||
0E27. Note that the address of the program's current IP and the
|
||
address where you inserted your CD 21 word is identical - you see,
|
||
sometimes the program's current CS can be the same at startup, and
|
||
during game play. (Refer to step 3 in "Writing the trainer loader or
|
||
tsr code", outlined in section 14).
|
||
|
||
(The reason for the 2 byte increase from 0E25 to 0E27 now, is because
|
||
the program's current IP points to the next following instruction
|
||
after your INT 21. (For more information on this, refer to
|
||
"Comparing the program's current IP", outlined in section 16d).
|
||
|
||
|
||
The next instructions listed below restore DS,BP,FLAGS and jump back to the
|
||
original INT 21 vector - if the compare at either 0108 or 0116 failed.
|
||
|
||
|
||
011D 1F POP DS
|
||
011E 5D POP BP
|
||
011F 9D POPF
|
||
0120 E97F00 JMP 01A2
|
||
|
||
|
||
The instructions below move into AL the key presses taken from the program's
|
||
keyboard key press storage data area and compare them to the trainer defined
|
||
keys:
|
||
|
||
|
||
0123 1F POP DS
|
||
0124 5D POP BP
|
||
0125 50 PUSH AX
|
||
0126 A0E409 MOV AL,[09E4]
|
||
0129 3C3B CMP AL,3B
|
||
012B 743B JZ 0168
|
||
012D 3C3C CMP AL,3C
|
||
012F 743D JZ 016E
|
||
0131 3C3D CMP AL,3D
|
||
0133 7427 JZ 015C
|
||
0135 3C3E CMP AL,3E
|
||
0137 743B JZ 0174
|
||
0139 3C3F CMP AL,3F
|
||
013B 7425 JZ 0162
|
||
013D 3C40 CMP AL,40
|
||
013F 7415 JZ 0156
|
||
0141 3C41 CMP AL,41
|
||
0143 7435 JZ 017A
|
||
0145 3C42 CMP AL,42
|
||
0147 7437 JZ 0180
|
||
0149 3C43 CMP AL,43
|
||
014B 7440 JZ 018D
|
||
014D 3C44 CMP AL,44
|
||
014F 7435 JZ 0186
|
||
0151 58 POP AX
|
||
0152 9D POPF
|
||
0153 EB44 JMP 0199
|
||
0155 90 NOP
|
||
|
||
|
||
The following is the training routine. The training data was derived using
|
||
the same techniques as outlined in this training tutorial.
|
||
|
||
|
||
0156 C646F643 MOV BYTE PTR [BP-0A],43
|
||
015A EB38 JMP 0194
|
||
015C C646F6CE MOV BYTE PTR [BP-0A],CE
|
||
0160 EB32 JMP 0194
|
||
0162 C646F65E MOV BYTE PTR [BP-0A],5E
|
||
0166 EB2C JMP 0194
|
||
0168 C646F691 MOV BYTE PTR [BP-0A],91
|
||
016C EB26 JMP 0194
|
||
016E C646F693 MOV BYTE PTR [BP-0A],93
|
||
0172 EB20 JMP 0194
|
||
0174 C646F6CD MOV BYTE PTR [BP-0A],CD
|
||
0178 EB1A JMP 0194
|
||
017A C646F6C3 MOV BYTE PTR [BP-0A],C3
|
||
017E EB14 JMP 0194
|
||
0180 FE06EE1F INC BYTE PTR [1FEE]
|
||
0184 EBCB JMP 0151
|
||
0186 C606FD3D01 MOV BYTE PTR [3DFD],01
|
||
018B EBC4 JMP 0151
|
||
018D C606D01F64 MOV BYTE PTR [1FD0],64
|
||
0192 EBBD JMP 0151
|
||
0194 58 POP AX
|
||
0195 31C0 XOR AX,AX
|
||
0197 EBB9 JMP 0152
|
||
0199 C606E40900 MOV BYTE PTR [09E4],00
|
||
019E 88C3 MOV BL,AL
|
||
01A0 CF IRET
|
||
01A1 90 NOP
|
||
01A2 EA00000000 JMP XXXX:XXXX ;Jump back to the original INT 21
|
||
vector.
|
||
|
||
|
||
|
||
For a better understanding, study the Legend of Myra interactive 10 option
|
||
tsr trainer example (LOMT-TSR.COM and read it's DOC - LOMT-TSR.DOC) included
|
||
in this training package. It corresponds exactly with the above example.
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
ЩЭЭЭЭЭЭЭЭЭЭЭЭЭЭЛ
|
||
К Section 19 К
|
||
ШЭЭЭЭЭЭЭЭЭЭЭЭЭЭМ
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
Interactive TSR/loader trainer examples:
|
||
ЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭ
|
||
|
||
|
||
This training tutorial comes with 4 interactive trainer examples (in COM
|
||
format) There are 3 interactive TSR trainer examples and 1 interactive
|
||
trainer loader example, included with this training tutorial. The ASM code
|
||
structure of all 4 interactive trainer examples, is identical to the ASM
|
||
code structure of the examples outlined in this documentation. Even the
|
||
trainer code found in each of the 4 trainer examples, starts at CS:0100
|
||
- exactly as listed here in this training tutorial.
|
||
|
||
|
||
This was done so that you will be able to study the examples listed here and
|
||
then refer to the actual interactive TSR/loader trainer examples.
|
||
|
||
|
||
You should note however, that all the 3 TSR trainers use the same install
|
||
checking routine - to see if they have already been previously installed
|
||
in memory. Remember to only install one at a time.
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
ЩЭЭЭЭЭЭЭЭЭЭЭЭЭЭЛ
|
||
К Section 20 К
|
||
ШЭЭЭЭЭЭЭЭЭЭЭЭЭЭМ
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
Summary:
|
||
ЭЭЭЭЭЭЭЭ
|
||
|
||
|
||
By studying the above examples and the actual trainer program examples
|
||
(PP2T-TSR.COM, FRT-TSR.COM, LOMT-TSR.COM, PP2T-LDR.COM) included in this
|
||
trainer tutorial package, you will learn how to write trainers for the PC,
|
||
or at least broaden your knowledge on this topic.
|
||
|
||
|
||
I hope this trainer tutorial helps all you boys out there who always
|
||
wondered how it's done. Maybe now I can retire for good since you boys will
|
||
be making all the trainers from now on!
|
||
|
||
|
||
|
||
Anyways...
|
||
|
||
|
||
|
||
From the boys at UNT, : Take care & have PHUN!
|
||
|
||
|
||
|
||
Dr. Detergent / UNT'93
|
||
|
||
|
||
|
||
############################################################################
|
||
|
||
By the way...
|
||
|
||
|
||
I'm a pilot currently without a job! - if you are an owner of a
|
||
charter company or a flight school, or have contacts in the aviation
|
||
industry, and know of a job opening, then by all means contact me -
|
||
through the UNTOUCHABLES!
|
||
|
||
Got a valid flight instructor's rating, multi-engine, glider lic,
|
||
certified on 10 types of aircraft, and other goodies.
|
||
|
||
Ready and willing to relocate ANYWHERE!
|
||
|
||
And if you think that my cracking/training/programming is good,
|
||
then wait till you see me fly!
|
||
|
||
############################################################################
|
||
|
||
|
||
|
||
еЭЭЭ[ THE UNTOUCHABLES CREW ]ЭЭЭИ
|
||
жФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФЗ
|
||
жФФФФФФФФФ ФФ њ њ ФФ ФФФФФФФФФФФФФФФФФФФФФФФФФФФФ ФФ њ њ ФФ ФФФФФФФФЗ
|
||
К К
|
||
К ЎФФФФФФФФФФФФФФФФФФФФФФФФФ№ UNTOUCHABLES №ФФФФФФФФФФФФФФФФФФФФФФФФФФФЏ К
|
||
К К
|
||
К Bandieto Mr. Fizz The Psychiatrist The Whistler К
|
||
К К
|
||
К Booper Chester Code Breaker Dark Knight Dr. Detergent Faceless К
|
||
К К
|
||
К Fenris Wolf Silver V Spyke the Impaler The Bandit К
|
||
К К
|
||
К Wayward Ford Prefect К
|
||
К К
|
||
К ЎФФФФФФФФФФФФФФФФФФФФФФФ№ The Courier Team №ФФФФФФФФФФФФФФФФФФФФФФФФФЏ К
|
||
К К
|
||
К Nightblade Vertigo К
|
||
К К
|
||
К August Spies Cable Dr. Donnatello Macgyver Minotaur К
|
||
К К
|
||
К Mirage Quazar Satch Shadowhawk Sinclair Specs К
|
||
К К
|
||
К Tasslehoff Burrfoot The Invid The Predator The Roamer Torgall К
|
||
К К
|
||
гФФФФФФФФФФФФФФФФФФ ФФ њ њ ФФ ФФФФФФФФФФФФФФФФФН
|
||
гФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФН
|
||
|
||
еЭЭЭ[ UNTOUCHABLES BOARDS ]ЭЭЭИ
|
||
еЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭИ
|
||
еЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭИ
|
||
Г < BOARD NAME > Г < NUMBER > Г < SYSOP > Г < NODES/POSITION > Г
|
||
ЦЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЕ
|
||
Г - [ ALL HQs ] - Г
|
||
ГФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФГ
|
||
Г Apocalypse ........ Г ITS-PRI-VATE Г The Whistler Г 5 Г World ..... HQ Г
|
||
Г The Dark Palace ... Г ITS-PRI-VATE Г Escape Key .. Г 10 Г Courier ... HQ Г
|
||
Г The Burning Church Г ITS-PRI-VATE Г -aD! ........ Г 3 Г Canadian .. HQ Г
|
||
ГФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФГ
|
||
Г - [ MEMBER BOARDS ] - Г
|
||
ГФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФГ
|
||
Г House of the R/Sun Г 703-406-8920 Г Dark Knight . Г 2 Г UNTNET HUB Г
|
||
Г Members Only ...... Г ITS-PRI-VATE Г Chester ..... Г 4 | Member - Board Г
|
||
Г MidWest Exchange .. Г ITS-PRI-VATE Г Silver V .... Г 5 | Member - Board Г
|
||
Г Pristine Towers ... Г ITS-PRI-VATE Г Vertigo ..... Г 2 Г Member - Board Г
|
||
ГФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФГ
|
||
Г - [ SITES ] - Г
|
||
ГФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФГ
|
||
Г Power Base ........ Г +49-XXX-XXXXXX Г Powerlite ... Г 1 | Distro .. Site Г
|
||
Г The GodsLand ...... Г 410-360-3598 Г Crash ....... Г 1 | Distro .. Site Г
|
||
Г The Land's End .... Г 703-XXX-XXXX Г Rogue Trader Г 1 | Distro .. Site Г
|
||
Г Twilight Zone ..... Г 504-XXX-XXXX Г Jack Flash .. Г 3 | Distro .. Site Г
|
||
Г Xcess Unlimited ... Г +49-XX-XXXXXXX Г Creme ....... Г 2 Г Distro .. Site Г
|
||
дЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭО
|
||
дЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭЭО
|
||
|
||
|
||
|
||
еЭЭЭ[ PLEASE NOTE ]ЭЭЭИ
|
||
жФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФЗ
|
||
жФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФЗ
|
||
К К
|
||
К We are now accepting applications, please pick up an application at any К
|
||
К UNTOUCHABLES HQ, or contact us on our VMB. К
|
||
К К
|
||
К 1-800-328-3440 450 (After 6PM -EST) К
|
||
К ж З К
|
||
К К If you like and use a software, please take it upon yourself to buy К К
|
||
К К it. Supporting quality programmers is in all of our interest. К К
|
||
К г Н К
|
||
гФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФН
|
||
гФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФН
|
||
|
||
жФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФЗ
|
||
жФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФЗ
|
||
К - [ њ U њ N њ T њ O њ U њ C њ H њ A њ B њ L њ E њ S њ ] - К
|
||
гФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФН
|
||
гФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФФН
|
||
|