comrade's bureau


comments?

Lokomotiv Trainer Engine


A trainer is a software program that modifies a game's memory and state to yield otherwise impossible effects - such as unlimited ammo, player invulnerability, and much more. Trainers are usually very small, and very repetitive programs - that is why it is frequent that engines for them are made. A trainer engine usually divides itself from the specifics, and instead provides the facilities to implement and carry out basic functions such as memory access, option timing and repetition, and user interface. Most involved trainer engines also implement advanced techniques such as code-shifting, DMA, triple injection, staple intersections and in-game menus (overlays).
Lokomotiv (aka 'loko') is one of the most feature-rich and easy to program trainer engines out there. Here are its features:

Documentation

Documentation is still in the process of writing (and it will take a long time to do so). If anyone has time and the desire, they are welcome to create a reference for each of loko's instructions. In the mean time, it will be much more beneficial to look at the trainer scripts inside the /trainer directory. Most of the instructions are self-explanatory.

Sample Script

The following is a script for a real trainer for the game "Frogger's Adventures". It was generously donated by pizdabol, and contains advanced techniques such as double injection. You can refer to it while reading the description below.

frogger.inc
;##########################################################################
; pizdabol 2004
;##########################################################################
target		"Frogger's Adventures"
  wnd		"Frogger's Adventures for Win"
  class 	"Frogger's Adventures for Win"
  exec		'FrogADV.exe'
  nfo		'trainers/frogger.nfo'
  onotes	'developer notes'
  message	'If you have Windows NT/2000/XP system, ',\
		'and ran this trainer before the game,',13,10,\
		'you can press F7 to see the trainer menu during the game.'
  overlaykey	'F7',VK_F7
;##########################################################################
option 'inject caves',OPTION_SET+OPTION_HIDDEN+OPTION_FREEZE
  oaddress 02140f1ch
    ocode
      mov dword [02140f2dh], eax
      movsx eax, word [eax+017eh]
      jump 004e2bcfh+7
    ocodeend
  oaddress 004e2bcfh
    ocode
      jump 02140f1ch
    ocodeend
oend
;##########################################################################
option	'inc life',OPTION_ADDWORD
  onotes 'increase lives by 1'
  okey	'F5',VK_F5
  oaddress '$',02140f2dh,'*','+',180h
    owords 1
oend
;##########################################################################
option 'Left',OPTION_SCRIPT+OPTION_HIDDEN
  okey	'',VK_NUMPAD4
  oscript
	mread	02140F31h,OFFSET .nStep,4
	mread	02140f2Dh,OFFSET .nAddress,4
	add	[.nAddress],00h
	mread	[.nAddress],OFFSET .nPos,4
	fld	dword [.nPos]
	fadd	dword [.nStep]
	fstp	[.nPos]
	mwrite	[.nAddress],OFFSET .nPos,4
	retn
	.nAddress  rd 01h
	.nStep	   rd 01h
	.nPos	   rd 01h
  oscriptend
oend
;##########################################################################
option 'Right',OPTION_SCRIPT+OPTION_HIDDEN
  okey	'',VK_NUMPAD6
  oscript
	mread	02140F31h,OFFSET .nStep,4
	mread	02140f2Dh,OFFSET .nAddress,4
	mread	[.nAddress],OFFSET .nPos,4
	fld	dword [.nPos]
	fsub	dword [.nStep]
	fstp	[.nPos]
	mwrite	[.nAddress],OFFSET .nPos,4
	retn
	.nAddress  rd 01h
	.nStep	   rd 01h
	.nPos	   rd 01h
  oscriptend
oend
;##########################################################################
option 'Up',OPTION_SCRIPT+OPTION_HIDDEN
  okey	'',VK_NUMPAD8
  oscript
	mread	02140F31h,OFFSET .nStep,4
	mread	02140f2Dh,OFFSET .nAddress,4
	add	[.nAddress],08h
	mread	[.nAddress],OFFSET .nPos,4
	fld	dword [.nPos]
	fadd	dword [.nStep]
	fstp	[.nPos]
	mwrite	[.nAddress],OFFSET .nPos,4
	retn
	.nAddress  rd 01h
	.nStep	   rd 01h
	.nPos	   rd 01h
  oscriptend
oend
;##########################################################################
option 'Down',OPTION_SCRIPT+OPTION_HIDDEN
  okey	'',VK_NUMPAD2
  oscript
	mread	02140F31h,OFFSET .nStep,4
	mread	02140f2Dh,OFFSET .nAddress,4
	add	[.nAddress],08h
	mread	[.nAddress],OFFSET .nPos,4
	fld	dword [.nPos]
	fsub	dword [.nStep]
	fstp	[.nPos]
	mwrite	[.nAddress],OFFSET .nPos,4
	retn
	.nAddress  rd 01h
	.nStep	   rd 01h
	.nPos	   rd 01h
  oscriptend
oend
;##########################################################################
option '1',OPTION_SET+OPTION_HIDDEN
  okey	'','1'
  oaddress 02140f31h
   ofloats 10.0
oend
;##########################################################################
option '2',OPTION_SET+OPTION_HIDDEN
  okey	'','2'
  oaddress 02140f31h
   ofloats 20.0
oend
;##########################################################################
option '3',OPTION_SET+OPTION_HIDDEN
  okey	'','3'
  oaddress 02140f31h
   ofloats 30.0
oend
;##########################################################################
option '4',OPTION_SET+OPTION_HIDDEN
  okey	'','4'
  oaddress 02140f31h
   ofloats 40.0
oend
;##########################################################################
option '5',OPTION_SET+OPTION_HIDDEN
  okey	'','5'
  oaddress 02140f31h
   ofloats 50.0
oend
;##########################################################################
option '6',OPTION_SET+OPTION_HIDDEN
  okey	'','6'
  oaddress 02140f31h
   ofloats 60.0
oend
;##########################################################################
option '7',OPTION_SET+OPTION_HIDDEN
  okey	'','7'
  oaddress 02140f31h
   ofloats 70.0
oend
;##########################################################################
option '8',OPTION_SET+OPTION_HIDDEN
  okey	'','8'
  oaddress 02140f31h
   ofloats 80.0
oend
;##########################################################################
option '9',OPTION_SET+OPTION_HIDDEN
  okey	'','9'
  oaddress 02140f31h
   ofloats 90.0
oend
;##########################################################################
option '',OPTION_SET
oend
option 'use 1-9 to select jump',OPTION_SET
oend
option 'distance, then numpad',OPTION_SET
oend
option 'arrows to move',OPTION_SET
oend
;##########################################################################

A trainer script is composed of a single target block, and multiple option blocks. The target directive defines the title of the trainer ("Frogger's Adventures"), and starts of the block that describe the trainer target. Lokomotiv can identify the target process by its window title/classname pair ("Frogger's Adventures for Win"), or by its process name ("FrogADV.exe"). Only one of the two have to be present, but it is better to put it in both the window/classname pair, and the process name as backup. The nfo directive takes a path to the text file for the current trainer (relative to loko directory root), and incorporates it inside the executable for later viewing. The text file usually contains information for the user about the current trainer, who made it, and the description of each option. In the underground scene, this is usually know as the "NFO file". The message directive instructs the engine to display a message to the user. Use this to alert the user of important information (such as incompatibility between some two options). The overlay engine (DX8/DX9/OpenGL) is activated by the keyboard key that is specified by the overlaykey directive. The first parameter provides a textual, string representation of the key that is displayed to the user, and the next is the actual constant that tells loko which key to use (one of the VK_* constants in windows.h).

The first option block defines a single option called "inject caves". It is simple write (OPTION_SET), hidden from the trainer menu (OPTION_HIDDEN), and constantly rewritten (OPTION_FREEZE) option. There are multiple addresses inside the option block, which means both will be written to sequentially. The address of both options are fixed values. The first option writes the code inside the ocode block into the location $02140F1C. That means loko will assemble the code, and write its machine-code byte representation into the game's memory. This is very convenient, as you do not have to write code in a hexadecimal form anymore. However, you must be careful that the code's size does not exceed any set bounds. The second address in the same option block writes a simple jmp instruction. Lokomotiv contains a specialized macro called jump (note the extra 'u') that automatically calculates displacement between the current displacement and the target, and writes the proper encoding of jmp. The argument to jump is an absolute address in the target's address space.

The next option is used to increase the number of lives. It is marked as OPTION_ADDWORD, meaning it adds words (16-bit integers). The shortcut key for the option is VK_F5, displayed to the user as 'F5'. The address of the option is a DMA address. DMA stands for dynamic memory allocation, and is used to describe addresses of variables that are dynamic at run-time, but can be retrieved by reading a pointer. The syntax to deal with this kind of situation is very simple with loko - first start off with a fixed address of $002140F2, then read from it, and then add 180h to whatever was read. This is equivalent to doing the following in C:

int *ptr;

ptr = 0x002140F2;
ptr = *ptr;
ptr += 0x180;
// use ptr as the pointer to the number of lives

The amount to add to the memory is set by the owords directive. It is set to 1, meaning that each time the user presses F5, he/she will have one more life added. The number could also be negative. It is also possible to add bytes (8-bit integers), dwords (32-bit integers), qwords (64-bit integers), floats (32-bit decimals), and doubles (64-bit decimals). However, you must use the corresponding data directive, obytes for bytes, odwords for dwords, oqwords for qwords, ofloats for floats, and new odoubles for doubles.

The next four options (Left, Right, Up, and Down) are scripted options - meaning loko will not perform any operations, but will fire off the custom written script when the user presses the appropriate key. The custom written script should be put inside the oscript block (and ended by oscriptend). The script language is the same as x86 assembly, in FASM syntax (similar to NASM/TASM), with a few added on instructions - mread and mwrite. Both are responsible for memory access in the target's address space. The syntax for these instruction is as follows:

mread    address, buffer, length
mwrite   address, buffer, length

address is the address of memory inside the target's address space. The memory is read from that address into buffer. The amount of memory read is set by the length parameter. The mwrite instruction is exactly the same, except the memory is written from buffer into address.
Scrips must end with retn instruction which yields control back to loko. Omitting that instruction will crash the trainer. If the script is to use any variables, they should be declared after retn. Variables are declared using this syntax:

.myvariable1    dd    ?    ; declare a DWORD variable named '.myvariable1'
.myvariable2    dw    ?    ; declare a WORD variable named '.myvariable2'
.myvariable3    db    ?    ; declare a BYTE variable named '.myvariable3'
.myarray1       dd    5,6  ; declare an array of two DWORDs, set to 5 and 6
.myarray2       rw    20   ; declare an uninitialized array of twenty WORDs

Refer to FASM manual for more detailed description. In this particular example, the script declares 3 uninitialized DWORDs named .nAddress, .nStep, .nPos, respectively. Note that all variables and labels must begin with '.' inside the script block. The script reads the .nStep variable from the address $02140F31, and the .nAddress variable from $02140F2D. It then reads .nPos variable from the address pointed to by [.nAddress]. It loads .nPos onto the FPU stack, add the .nStep to it, and then saves it back to .nPos, and back into the target's memory at [.nAddress]. Finally, it returns to loko by using the retn instruction. The equivalent code in C would be:

void* nAddress;
float nPos, nStep;

ReadProcessMemory(hProcess, 0x002140F31, &nStep, 4, 0);
ReadProcessMemory(hProcess, 0x002140F2D, &nAddress, 4, 0);
ReadProcessMemory(hProcess, nAddress, &nPos, 4, 0);
nPos += nStep;
WriteProcessMemory(hProcess, nAddress, &nPos, 4, 0);

The other 3 options (Right, Up, and Down) execute exactly the same script code, but only differ in the arithmetics involving nPos. That is peculiar to the trainer itself, and begs no explanation from this document.

The next nine options are also hidden, and all they do is write a the 'jump distance' into a variable inside the target's memory address space that is later used by the Left, Right, Up and Down scripts.
The last few options have no functional purpose at all. Even though the options are of type OPTION_SET, which is used to perform a single write, there is no address specified, so loko writes nothing. This trick is used to display extra text in the trainer menu to give the user information about the trainer's functionalities. Such information could have been included in the text file specified by the nfo directive, but sometimes it is necessary to have information always displayed. These "dummy options" allow this.

Common Pitfalls

Making Your Own Trainer

To make your own trainer using loko (you are encouraged to do so), it is required to perform a few simple steps:

  1. Make your own trainer script (probably copy an existing one and modify it). Save the script inside the /trainers directory. Include the reference to the trainer file inside the trainers.inc file (in the root directory).
  2. Make your own skin for loko. Skins are saved inside the /skins directory. Again, it is easiest to just copy an existing skin and modify it.
  3. Adjust the options for the trainer in the options.inc file. This file defines the skin that is used, and whether the trainer is compiled in debug or release mode. The debug mode allows you to analyze each step using the loko.log file. This file is not created in the release mode. Furthermore, the release mode toggles EXE encryption, debugger/breakpoint checks and code obsufication. This helps prevent bad people from using spy programs to steal your options.
  4. Run build.cmd

Known Issues

License

The project is released under the BSD license.

About Loko

Lokomotiv was programmed in FASM, a very actively developed and free assembler for the x86 assembly language.
A big thanks goes to very good friend pizdabol who provided some very much needed beta-testing, and tried out the engine since its earliest version. Credits to cokine for the default skin, and ferrex for the overlay option notification font.

Download

loko-0.1.rar (313 KB)
Note: You need WinRAR to extract the file above.

References

Game Hacking BBS - a large and active forum where you can ask questions and exchange ideas about game training.
spookie - homepage of spookie. You can download trainers, tutorials, trainer tools and their source codes.
sheep - homepage of sheep. Appears to be down at the moment.
BRZI'S LAIR - homepage of Macedonian game trainer brzi. Contains many codes, trainers, tutorials and tools.

Comments

[an error occurred while processing this directive]