Chapter I - Patching your score
I assume the game is still running, time to run GameHack. Start a new game (File->New Game), choose Beavis as player (it doesn't really play a role which character you choose, but it's fact that Beavis is smarter :P). Now, before starting to spit onto anything (yeah, look at the game), look at your score. It says: 0/20. Switch back to GameHack. Now press on the icon on the upper left ("Select process for hacking").
The process list will show up, look for the name of window of the process we want to train ("Beavis and Butt-head Hock-a-Loogie"). Select it with your mouse and click the OK-Button. Start a new search for an *Exact value*, a type of *4 Bytes* in *Decimal* base. As Value1 you take your current score - remember we had a score of 0/20. So you take '0' and click the OK-Button. Wait until the search has finished (it is rather slow, because it "mirrors" the process to a temporary file on your harddisk). A messagebox will be shown saying "Too many addresses found, they wont't be shown" - click the OK-Button, the addresses have been saved. Switch to the game and try to spit onto the bicycle, your score will be 2/20. Back to GameHack you do a "next search" and feed the textbox with your current score (yes, 2). After you made and drunk your coffee GameHack has found a few addresses, mine has found 3 addresses, they all contain the value "2" of course.
But we need to go on until we have the exact location where Hock-a-Loogie stores the score. We could test it by patching different values into each address, but we don't want to crash the game (though i assume the score sleeps in 412F74). So spit another time onto a bicycle, you will have a score of 4/20, so you can do a next search with the value "4". Most probably you now have the exact address. For my session it was 412F74. So this is where SoftIce comes into play. We want SoftIce to pop up when the game accesses this memory address (412F74) by writing to it. This can be done with the SoftIce command "bpm". It will break on access of a specified address. You can look it up in SoftIce with the help command "h" - just type "h bpm" and SoftIce will display a short description of the bpm command:
Breakpoint on memory access BPM[size] address [R|W|RW|X] [debug register] [IF expression] [DO bp-action] [size] can be one of the following types:
"b" standing for byte "w" standing for word ( = 2 bytes) "d" standing for double ( = 4 bytes)
We need the "double" type; remember we were searching for 4 bytes? For the "address" parameter we take the address GameHack found (412F74), the next parameter must be "W" for write access only, so SoftIce will break if your score increases. What we have to do now is switching back to the game and pressing CTRL-D to get into SoftIce. Be sure the active process is the game (in this case you will see the string "Loogie" on the bottom of the SoftIce "window" at the very right). Then you can bpm on our address (bpmd 412F74 W). Get back to the game (x^) and score ;) Sice pops up:
00403320 833D3C45410000 cmp dword ptr [0041453C],0 00403327 750B jnz 00403334 00403329 8B4C2404 mov ecx,[ESP+04] 0040332D 010D742F4100 add [00412F74],ecx 00403333 C3 ret 00403334 8B442404 mov eax,[ESP+04] 00403338 8B0D742F4100 mov ecx,[00412F74]
SoftIce broke in at virtual address 403333. Look at the code above, especially at VA 40332D. It adds the double stored inside ecx to address 412F74. Remember this address? Yeah, this MUST be the location where the score is stored, as SoftIce broke in immediately after the code at RVA 40332D (and... by the way.. it was the address we were talking of all the time ;). What else can we get from the code? It must be static data, because the code adds ecx ALWAYS to [00412F74] -and- I do not assume that we have a case of self modifying code. What does all that crap mean for our trainer? It means we can for sure add some score by writing into that address directly - no patching of code or seeking for the address, as it is fixed. Now it's time to begin coding the trainer. We will use a hotkey that will add some score. GetAsyncKeyState will be the perfect API for it. So look it up in the Win32 Programmer's reference (win32.hlp):
The GetAsyncKeyState function determines whether a key is up or down at the time the function is called, and whether the key was pressed after a previous call to GetAsyncKeyState.
SHORT GetAsyncKeyState ( int vKey // virtual-key code );
excellent... now the standard stuff for doing a trainer: WriteProcessMemory is the API that we have to use to write stuff to memory. WriteProcessMemory needs a handle to the process to patch.
BOOL WriteProcessMemory( HANDLE hProcess, // handle to process whose memory is written to LPVOID lpBaseAddress, // address to start writing to LPVOID lpBuffer, // pointer to buffer to write data to DWORD nSize, // number of bytes to write LPDWORD lpNumberOfBytesWritten // actual number of bytes written );
This handle can be retreived with OpenProcess, but OpenProcess needs a process identifier .
HANDLE OpenProcess( DWORD dwDesiredAccess, // access flag BOOL bInheritHandle, // handle inheritance flag DWORD dwProcessId // process identifier );This process identifier can be retreived with GetWindowThreadProcessId, and we finally need a handle for the window to pass to GetWindowThreadProcessId.
DWORD GetWindowThreadProcessId( HWND hWnd, // handle of window LPDWORD lpdwProcessId // address of variable for process identifier );
FindWindowEx returns a handle to a window:
HWND FindWindowEx( HWND hwndParent, // handle to parent window HWND hwndChildAfter, // handle to a child window LPCTSTR lpszClass, // pointer to class name LPCTSTR lpszWindow // pointer to window name );
So we finally got the basic APIs for our trainer: FindWindowEx, GetWindowThreadProcessId, OpenProcess, WriteProcessMemory and GetAsyncKeyState. Another API that we will have to use is ReadProcessMemory to read the game's current score to a variable, then add the value we want and finally writing it back.
The ReadProcessMemory function reads memory in a specified process. The entire area to be read must be accessible, or the operation fails. BOOL ReadProcessMemory( HANDLE hProcess, // handle of the process whose memory is read LPCVOID lpBaseAddress, // address to start reading LPVOID lpBuffer, // address of buffer to place read data DWORD nSize, // number of bytes to read LPDWORD lpNumberOfBytesRead // address of number of bytes read );
The memory-patching routine will be triggered after we pressed the hotkey:
invoke GetAsyncKeyState,VK_F12 .if eax == TRUE invoke TrainProc .endif
Writing the trainer with masm32.
First of all you create a little dialog for the trainer. I used M$ VC++ ressource editor, but you can use any other, of course. You may get some on Kaparo's Programmers tools for example. This is how my dialog looks like:
A simple dialog with a caption and some strings, you can add a picture and other stuff, but that's not the point of this tutorial.
IDD_DIALOG1 is a constant value and 101 as default for VC++ Res-edit. We will have to import this Id into the trainer's source to Create the dialog. Now have a look at the sourcecode. Get chapter1 sources and binaries here. Use "make.bat chapter1" to assemble.
As you saw if you had a look at the code, I made a little function (our "main" engine) to add some scorepoints.
Engine_Add Proc lpWndCap: DWORD, lpBaseAddress: DWORD, lpBuffer: DWORD, nToAdd: DWORD lpWndCap is a pointer to a string containing the window's caption (e.g.: WndCap db 'Beavis & Butt-head Hock-a-Loogie',0). lpBaseAddress is a pointer to the address holding the value (in our target: score) we want to increment. lpBuffer is a pointer to a variable that we will use for temporary storage of the value we want to increment. nToAdd represents the number of bytes we want to add to *lpBaseAddress.