ίά Ϋ άάά ά ²ΫΫΫΫΫά άί ²²ΫΫΫΫΫέ άΫί ²²ΫΫΫΫΫΫΫά άάΫΫί ά άά ²²ΫΫΫΫΫΫΫίίΫάάάάάΫΫΫΫίίί ί ί ά ²²²Ϋ²²ΫΫΫΫάά Ϋίίίί ήΫ ²²ΫΫΫΫΫ²ΫΫίΫίίάέ Ϋ ²ΫΫΫΫΫΫΫΫάΫάάΫΫΫ ί²Ϋ ²ΫΫΫΫΫΫΫΫΫΫΫΫΫΫΫΫά ϊ ά άί ²ΫΫΫΫΫΫΫΫΫΫΫΫΫΫΫΫΫΫΫά ή ²ΫΫΫΫΫΫΫΫΫΫΫΫΫέΫΫίίΫΫΫά ²ΫΫΫΫΫΫΫΫΫΫΫΫΫΫΫάΫΫάάΫΫί ή Ϋ ²ΫΫΫΫΫΫΫΫΫΫΫΫΫΫΫΫΫΫΫά ήέ ή ²ΫΫΫΫΫΫΫΫΫΫΫΫΫΫΫΫΫΫί ΫΫ ή ²ΫΫΫΫΫΫΫΫΫΫΫΫΫΫΫΫΫά ΫΫά έ ά ²²ΫΫΫΫΫΫΫΫΫΫΫΫΫΫΫΫΫ ήΫΫί έά ά²²άάί²ΫΫΫΫΫΫΫΫΫΫΫΫΫΫ²ί ήΫέέ Ϋέ ί άά ί²²ΫΫΫΫΫΫΫΫΫΫΫΫΫΫΫΫΫΫά άάΫίήέ Ϋέ ΫΫΫ² ί²ΫΫΫΫΫίΫΫΫΫΫΫΫΫΫΫΫΫ άΫΫΫΫΫέήέ Ϋέ ήΫΫΫ² ίΫΫΫΫΫά°ΫΫΫΫΫΫΫΫΫέ ΫΫΫΫΫΫΫ Ϋ Ϋ ΫΫΫΫ² ίΫΫΫΫΫάΫΫΫΫΫΫΫί ΫΫΫΫΫΫΫέ Ϋ Ϋ ΫΫΫ ά ίΫΫΫΫΫΫάίί ΫΫΫΫΫΫΫί έ ήΫ ΫΫΫάΫ ίΫΫΫΫΫΫΫά ήΫΫΫΫΫΫά Ϋ ΫΫΫΫάί²ά ί ίΫΫΫΫΫΫΫά ΫΫΫΫΫΫ²²Ϋά άΫ ΫΫΫΫΫΫΫΫ ίά ίίίΫΫΫΫΫΫΫάάά ήΫΫΫΫΫΫ²²έ Ϋ άΫΫΫΫΫΫΫΫ °ί ίίίίΫΫΫΫΫάάά ΫΫΫΫΫΫΫ² ί ά²ΫΫΫΫΫΫΫέ ίίίΫΫΫΫΫάάά ίίΫΫ²²έ ά±²ΫΫΫΫΫΫΫ ίίίίΫΫΫάάά ίίά ±²²ΫΫΫΫΫΫΫέ ίίίίά άά ά°±²²ΫΫΫΫΫΫ ί ίί°±ΫΫΫΫΫέ ίΫά ²°°ΫΫΫΫ ή²°ΫΫΫέ Ϋ²Ϋίίί ίί ίΫί ίΫί ά Ϋ Ϋ Ϋ άίίίίάί Ϋ Ϋ ά άά ίάάά άά ά ά άά ήέάά άά ήέ άά ήέ άάά ίΫάά Ϋά άΫ Ϋ Ϋ ά ί Ϋί Ϋ ήέ ήέ Ϋ ί ήέ ήέ άί ά ήέί Ϋ ήέάί άίί ίίΫά ίίά άίί ήέ ήέ ήέ άάάί ίάάί ήέάάί Ϋ Ϋ ίάάΫίά Ϋ άάί Ϋ ίάάά άίάάάάί 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 ϊ ] - Ί ΣΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔ½ ΣΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔΔ½