THE UNTUCHABLES

PRESENT :

Training Tutorial for the PC. By Dr. Detergent / UNT'93

Converted to HTML by Jammer/HBD

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 good 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 21vector.

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


Converted to HTML by Jammer/HBD