Post by dlevere on Sept 18, 2011 8:17:16 GMT -4
Gamehacking Tutorial
Last Update: 2004-04-16
Subject: Dynamically Allocated Chunks of Memory
Basic --> Intermediate Game Hacking
Author: Sain
Notes: This wont work on win95,98,ME, though you can do it another
way. Some person who cares may enlighten you. Not me though
Other Notes: I am not affiliated with any Game Hacking group, nor do I
release trainers. Don't bother asking me to write one for you.
If you want assistance with a problem ask and I might help.
Thanks to: [Sheep], for being the only person around who answered a
question.
-------------------------------------------------------------------------------
Alrighty, lets get started. The target game for todays tutorial is 3D Pinball.
The game is ideal for us, basically because most people will have it (it comes
with Windows XP, 2000 etc), and its easy as hell to train.
Our object today is to train the game using a "code cave" (gah! I hate that
phrase) that we add to the program with our trainer, rather than the usual
process of searching through the program for one that already exists. This is
useful for when you want to do things like using entire c++ functions, or for
launching custom dlls into a process.
oh yeah, and you'll need tsearch or some other memory searcher and a debugger
Step 1 - Find your balls
========================
Okay, this is really simple and shouldn't present a problem for anybody. Use
tsearch or whatever to find the memory location for the number of balls left.
If you have any trouble, try working backwards from 3.
I got 0x90314e as the location for my balls, yours should be different.
Once you have found your balls, whop a 4 in (so we have some balls to play
with) and then fire up autohack (go to the autohack menu and hit the enable
debugger item, then the autohack window item). In the autohack window set
a breakpoint on the address you found (just a write breakpoint will do)
and then go and lose a ball. Once you've lost the ball autohack should
have the line which modified the ball count. It should be remarkably similair
to:
1015f02: mov [esi+0x146],eax
which means move the value in eax into the memory location pointed to by [esi+0x146]
Step 2 - Create a standard Code Cave
====================================
Right, now we know where to insert our jump to the code we are going to inject,
we need to figure out that code and where we are going to inject it. For now
we are going to inject the code at 0x10bc0 (this will change later, when we
flip to a dynamically allocated memory block).
Fire up the tsearch easywrite thing, and put the following in the top section
(don't bother with the <-- stuff)
offset 0x10bc0
cmp eax,0x1 <-- compare the number of balls left to 1
jg 0x10bca <-- if we have more, skip the next line
mov eax,0x3 <-- if we have less, give em 3 balls
mov [esi+0x146],eax <-- write the number of balls left back
jmp 0x1015f08 <-- and return to just after we jumped
here
offset 0x1015f02
jmp 0x10bc0 <-- jump to our injected code above
nop <-- do nothing (for neatness)
and the following in a bottom section:
offset 0x1015f02 <-- restore the origional program code
mov [esi+0x146],eax <-- just moves the number of balls left
into the memory location
Alrighty, try it out. For those whos assembler isn't crash hot, here is whats
going to happen when the game goes to write the new number of balls back: it
jumps to our code, where its compared to 1 (i.e 1 ball remaining). If there is
more than 1 ball left the number is written back as normal, otherwise 3 balls
are written back. That code is much more complex than it needs to be for our
purposes, but it will help to illustrate relative addressing (later).
At this point we have successfully trained the game, using a "code cave" to
"defeat" "DMA" (lots of phrases i hate). Everything beyond this point is
optional, and you'll probably only use it when your injecting complex code,
such as an entire c++ function.
Step 3 - Dynamically Allocated Caves
====================================
This is where things get a little trickier. Take a look at the disassembler
dump for the address 0x1015f02 - it should look something like:
01015f02 E9B9ACFFFE jmp 0x00010BC0
Breaking that down the E9 is the jump instruction and the B9ACFFFE bit is the
address to jump to, or more accurately, the distance to the address to jump to
from the end of the instruction.
This means is the trainer is going to have to figure out what these 4
bytes are at run time. Lucky you - i'm going to provide you with a function to
do that (in c - i'm sure you asm gurus can convert it on your own):
void GenerateJmp(unsigned char *code,unsigned char *JumpDestination,
unsigned char *JumpSource=0)
{
if (!JumpSource) // if the user hasn't specified a source address
JumpSource=code; // assume we are writing at the "code" location
*code++=0xe9; // the jmp command
// the next line calculates the 4 bytes of the destination address
// which is simply the address of the destination - the address of the jmp + 5
*((int *&)code)++=JumpDestination - (JumpSource+5);
}
For those who prefer calls to jmps, change the *code++=0xe9 to *code++=0xe8 and
it will work the same.
Next we use VirtualAllocEx to allocate a hunk of memory for ourselves in the
game's process. VirtualAllocEx has the following definition:
LPVOID VirtualAllocEx(
HANDLE hProcess, // process to allocate memory
LPVOID lpAddress, // desired starting address
SIZE_T dwSize, // size of region to allocate
DWORD flAllocationType, // type of allocation
DWORD flProtect // type of access protection
);
For our purposes we call it like:
DstBlockAddress=VirtualAllocEx(GameHandle,NULL,Number_of_bytes_required,
MEM_COMMIT,PAGE_EXECUTE_READWRITE);
We then have a chunk of memory in the remote preocess allocated just for us
where we can write our code with WriteProcessMemory, pretty much the same as we
normally would, with our trainer adding the jumps on the fly. A function to do
this is as follows:
int CreateDynamicHook(int HookAddress,int RetnAddress,unsigned char *data,int data_size)
{
// At the location specified by HookAddress, insert a jmp to an allocated block
// of memory, insert the bytes contained in data and add a jmp back to the
// location specified by RetnAddress.
unsigned char tmp[6];
LPVOID address;
// attempt to allocate memory in the remote process
address=VirtualAllocEx(phandle,NULL,data_size+5,MEM_COMMIT,PAGE_EXECUTE_READWRITE);
if (address==NULL)
return 0; // failed to allocate mem
// now write the users data
WriteProcessMemory(phandle,(LPVOID)address,data,data_size,NULL);
// add the return address
GenerateJmp(tmp,(unsigned char *)RetnAddress,(unsigned char *)address+data_size);
WriteProcessMemory(phandle,(LPVOID)((int)address+data_size),tmp,5,NULL);
// and finaly the hook into the program
GenerateJmp(tmp,(unsigned char *)address,(unsigned char *)HookAddress);
WriteProcessMemory(phandle,(LPVOID)HookAddress,tmp,5,NULL);
// return the address, so it can be passed to VirtualFreeEx
return (int)address;
}
Of course you should add better error checking, but I can't be stuffed. In
order to use the function you just pass it the hex values from your code block
(the one we created earlier) without a returning jmp and it will do the rest of
the work for you. So, going back to our pinball code from earlier we would
pass in the hex for the following asm:
10bc0 83f801 cmp eax,0x1
10bc3 7f05 jg 0x10bca
10bc5 b803000000 mov eax,0x3
10bca 8986646010000 mov [esi+0x146],eax
note that the jg is still there. This will still function, because the jg used
a relative offset, meaning it says jump over the next 5 bytes. i.e to the 2nd
mov instruction.
The hex we would pass to the function is:
83f8017f05b8030000008986646010000
And thats it, pretty simple really. Some source code to implement a very basic
trainer for pinball is below. There is lots left for the
Note that it is simpler to use the call/retn method, rather than the jmp, jmp
method shown here.
Enjoy
Sain
-------------------------------------------------------------------------------
File: main.cpp
-------------------------------------------------------------------------------
/* Pinball 3D trainer
** By Sain
*/
/*
** Okay, this is really untidy, but hey, its just to demonstrate how to use the
** dynamicly allocated chunk of memory. Use this for whatever you want, if you
** kill something with it, it isn't my problem. The beep will tell you the
** hook has activated, though you'll see that if you still have a debugger
** attached to pinball. Oh yeah, make sure you add a dialog or this wont work.
** Compiles with a bunch of warnings but works fine on VC 7 (or .NET or
** whatever).
**
** If you want to use this method it would be a good idea to adapt the code
** so that all the injected code space is allocated at once, and only the jump
** or call to the injected space is written when the options are activated.
** Its not hard and is left as an exercise for somebody else.
*/
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#include "resource.h"
#define TARGET_WINDOW "3D Pinball for Windows - Space Cadet"
#define TRAINER_NAME "3D Pinball Trainer"
// Globals:
HINSTANCE g_hInst;
HWND hWnd;
bool attached;
HANDLE phandle;
// Prototypes:
BOOL CALLBACK DialogProc (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam);
void InitOther(void);
void InitHotKeys(void);
void HandleHotKeys(int key);
int CreateDynamicHook(int HookAddress,int RetnAddress,unsigned char *data,int data_size);
void GenerateJmp(unsigned char *code,unsigned char *JumpDestination,unsigned char *JumpSource);
void GenerateCall(unsigned char *code,unsigned char *JumpDestination,unsigned char *JumpSource=0);
bool AttachProcess(char *window);
// Code:
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
{
MSG msg;
LPVOID lpMsgBuf;
g_hInst = hInstance;
// this dialog should be hell easy to make, i just used a empty dialog with a static
// item on it telling the user to hit numpad0 to make the trainer do stuff
if (!(hWnd = CreateDialog(hInstance, MAKEINTRESOURCE(IDD_DIALOG1), 0, DialogProc)))
{
FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM,0, GetLastError(), MAKELANGID(LANG_NEUTRAL,
SUBLANG_DEFAULT), (LPTSTR)&lpMsgBuf, 0, NULL );
MessageBox(0, (LPCTSTR)lpMsgBuf, "Error loading", MB_OK|MB_ICONINFORMATION );
LocalFree( lpMsgBuf );
PostQuitMessage(0);
}
ShowWindow(hWnd, SW_SHOWNORMAL);
InitHotKeys();
while(GetMessage(&msg, hWnd, 0, 0))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return msg.wParam;
}
BOOL CALLBACK DialogProc (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
PAINTSTRUCT ps;
HDC hdc;
hWnd=hwnd;
switch (message)
{
case WM_INITDIALOG:
InitOther();
return TRUE;
case WM_HOTKEY:
HandleHotKeys(wParam);
break;
case WM_PAINT: // window background
hdc = BeginPaint(hwnd, &ps);
// TODO: Add any drawing code here...
EndPaint(hwnd, &ps);
break;
case WM_DESTROY:
UnregisterHotKey(hwnd,666);
PostQuitMessage(0);
ExitProcess(0);
return TRUE;
case WM_CLOSE: // kill window on close
EndDialog(hwnd,0);
DestroyWindow (hwnd);
return TRUE;
}
return FALSE;
}
void InitOther(void)
{
// do startup inits here if needed
}
void InitHotKeys(void)
{
// register the hot key so windows knows what to do with it
RegisterHotKey(hWnd,666,0,VK_NUMPAD0);
}
void HandleHotKeys(int key)
{
static bool tog[4]={false,false,false,false};
unsigned char hack[17]={0x83,0xf8,0x01,0x7f,0x05,0xb8,0x03,0x0,0x0,0x0,0x89,0x86,0x46,0x01,0x0,0x0};
unsigned char unhack[6]={0x89,0x86,0x46,0x01,0,0};
static int address;
// first check if we have a handle to the process
if (!attached)
{
if (!AttachProcess(TARGET_WINDOW))
{
MessageBox(hWnd,"Could not attach to process - start the game",TRAINER_NAME,MB_OK);
tog[0]=tog[1]=tog[2]=tog[3]=tog[4]=false;
return;
}
}
// yup, now deal with the hotkey
switch(key)
{
case 666:
tog[0]=!tog[0];
if (tog[0])
{
// allocate memory for the dynamic hook, chuck the hack into it and set up
// the jumps to and from
address=CreateDynamicHook(0x1015F02,0x1015f08,hack,16);
if (!address)
{
attached=false;
return;
}
Beep(850,100);
}
else
{ // remove the hook to the hack
if (!WriteProcessMemory(phandle,(LPVOID)0x1015f02,unhack,6,NULL))
{
attached=false;
return;
}
// now we need to free up the memory we have been allocated
// otherwise we will keep allocating more memory every time
// somebody activates the trainer.
if (!VirtualFreeEx(phandle,(LPVOID)address,0,MEM_RELEASE))
{
Beep(700,100);Beep(750,100);
attached=false;
return;
}
}
break;
}
}
void GenerateJmp(unsigned char *code,unsigned char *JumpDestination,unsigned char *JumpSource=0)
{
if (!JumpSource) // if the user hasn't specified a source address
JumpSource=code; // assume we are writing at the "code" location
*code++=0xe9; // the jmp command
// the next line calculates the 4 bytes of the destination address
// which is simply the address of the destination - the address of the jmp + 5
*((int *&)code)++=JumpDestination - (JumpSource+5);
}
void GenerateCall(unsigned char *code,unsigned char *JumpDestination,unsigned char *JumpSource=0)
{
if (!JumpSource) // if the user hasn't specified a source address
JumpSource=code; // assume we are writing at the "code" location
*code++=0xe8; // the call command
// the next line calculates the 4 bytes of the destination address
// which is simply the address of the destination - the address of the call + 5
*((int *&)code)++=JumpDestination - (JumpSource+5);
}
int CreateDynamicHook(int HookAddress,int RetnAddress,unsigned char *data,int data_size)
{
// At the location specified by HookAddress, insert a jmp to an allocated block
// of memory, insert the bytes contained in data and add a jmp back to the
// location specified by RetnAddress.
unsigned char tmp[6];
LPVOID address;
// attempt to allocate memory in the remote process
address=VirtualAllocEx(phandle,NULL,data_size+5,MEM_COMMIT,PAGE_EXECUTE_READWRITE);
if (address==NULL)
return 0; // failed to allocate mem
// now write the users data
WriteProcessMemory(phandle,(LPVOID)address,data,data_size,NULL);
// add the return address
GenerateJmp(tmp,(unsigned char *)RetnAddress,(unsigned char *)address+data_size);
WriteProcessMemory(phandle,(LPVOID)((int)address+data_size),tmp,5,NULL);
// and finaly the hook into the program
GenerateJmp(tmp,(unsigned char *)address,(unsigned char *)HookAddress);
WriteProcessMemory(phandle,(LPVOID)HookAddress,tmp,5,NULL);
return (int)address;
}
bool AttachProcess(char *window)
/* this function takes the name of a window and attempts to attach to the
** associated project. Returns true on success. Sets the attached flag on
** success.
*/
{
HWND hwnd;
DWORD pid;
attached=false;
hwnd=FindWindow(0,window);
if (!hwnd)
return false;
GetWindowThreadProcessId(hwnd,(unsigned long *)&pid);
phandle=OpenProcess(PROCESS_ALL_ACCESS, FALSE, pid);
if (!phandle)
return false;
attached=true;
return true;
}
Last Update: 2004-04-16
Subject: Dynamically Allocated Chunks of Memory
Basic --> Intermediate Game Hacking
Author: Sain
Notes: This wont work on win95,98,ME, though you can do it another
way. Some person who cares may enlighten you. Not me though
Other Notes: I am not affiliated with any Game Hacking group, nor do I
release trainers. Don't bother asking me to write one for you.
If you want assistance with a problem ask and I might help.
Thanks to: [Sheep], for being the only person around who answered a
question.
-------------------------------------------------------------------------------
Alrighty, lets get started. The target game for todays tutorial is 3D Pinball.
The game is ideal for us, basically because most people will have it (it comes
with Windows XP, 2000 etc), and its easy as hell to train.
Our object today is to train the game using a "code cave" (gah! I hate that
phrase) that we add to the program with our trainer, rather than the usual
process of searching through the program for one that already exists. This is
useful for when you want to do things like using entire c++ functions, or for
launching custom dlls into a process.
oh yeah, and you'll need tsearch or some other memory searcher and a debugger
Step 1 - Find your balls
========================
Okay, this is really simple and shouldn't present a problem for anybody. Use
tsearch or whatever to find the memory location for the number of balls left.
If you have any trouble, try working backwards from 3.
I got 0x90314e as the location for my balls, yours should be different.
Once you have found your balls, whop a 4 in (so we have some balls to play
with) and then fire up autohack (go to the autohack menu and hit the enable
debugger item, then the autohack window item). In the autohack window set
a breakpoint on the address you found (just a write breakpoint will do)
and then go and lose a ball. Once you've lost the ball autohack should
have the line which modified the ball count. It should be remarkably similair
to:
1015f02: mov [esi+0x146],eax
which means move the value in eax into the memory location pointed to by [esi+0x146]
Step 2 - Create a standard Code Cave
====================================
Right, now we know where to insert our jump to the code we are going to inject,
we need to figure out that code and where we are going to inject it. For now
we are going to inject the code at 0x10bc0 (this will change later, when we
flip to a dynamically allocated memory block).
Fire up the tsearch easywrite thing, and put the following in the top section
(don't bother with the <-- stuff)
offset 0x10bc0
cmp eax,0x1 <-- compare the number of balls left to 1
jg 0x10bca <-- if we have more, skip the next line
mov eax,0x3 <-- if we have less, give em 3 balls
mov [esi+0x146],eax <-- write the number of balls left back
jmp 0x1015f08 <-- and return to just after we jumped
here
offset 0x1015f02
jmp 0x10bc0 <-- jump to our injected code above
nop <-- do nothing (for neatness)
and the following in a bottom section:
offset 0x1015f02 <-- restore the origional program code
mov [esi+0x146],eax <-- just moves the number of balls left
into the memory location
Alrighty, try it out. For those whos assembler isn't crash hot, here is whats
going to happen when the game goes to write the new number of balls back: it
jumps to our code, where its compared to 1 (i.e 1 ball remaining). If there is
more than 1 ball left the number is written back as normal, otherwise 3 balls
are written back. That code is much more complex than it needs to be for our
purposes, but it will help to illustrate relative addressing (later).
At this point we have successfully trained the game, using a "code cave" to
"defeat" "DMA" (lots of phrases i hate). Everything beyond this point is
optional, and you'll probably only use it when your injecting complex code,
such as an entire c++ function.
Step 3 - Dynamically Allocated Caves
====================================
This is where things get a little trickier. Take a look at the disassembler
dump for the address 0x1015f02 - it should look something like:
01015f02 E9B9ACFFFE jmp 0x00010BC0
Breaking that down the E9 is the jump instruction and the B9ACFFFE bit is the
address to jump to, or more accurately, the distance to the address to jump to
from the end of the instruction.
This means is the trainer is going to have to figure out what these 4
bytes are at run time. Lucky you - i'm going to provide you with a function to
do that (in c - i'm sure you asm gurus can convert it on your own):
void GenerateJmp(unsigned char *code,unsigned char *JumpDestination,
unsigned char *JumpSource=0)
{
if (!JumpSource) // if the user hasn't specified a source address
JumpSource=code; // assume we are writing at the "code" location
*code++=0xe9; // the jmp command
// the next line calculates the 4 bytes of the destination address
// which is simply the address of the destination - the address of the jmp + 5
*((int *&)code)++=JumpDestination - (JumpSource+5);
}
For those who prefer calls to jmps, change the *code++=0xe9 to *code++=0xe8 and
it will work the same.
Next we use VirtualAllocEx to allocate a hunk of memory for ourselves in the
game's process. VirtualAllocEx has the following definition:
LPVOID VirtualAllocEx(
HANDLE hProcess, // process to allocate memory
LPVOID lpAddress, // desired starting address
SIZE_T dwSize, // size of region to allocate
DWORD flAllocationType, // type of allocation
DWORD flProtect // type of access protection
);
For our purposes we call it like:
DstBlockAddress=VirtualAllocEx(GameHandle,NULL,Number_of_bytes_required,
MEM_COMMIT,PAGE_EXECUTE_READWRITE);
We then have a chunk of memory in the remote preocess allocated just for us
where we can write our code with WriteProcessMemory, pretty much the same as we
normally would, with our trainer adding the jumps on the fly. A function to do
this is as follows:
int CreateDynamicHook(int HookAddress,int RetnAddress,unsigned char *data,int data_size)
{
// At the location specified by HookAddress, insert a jmp to an allocated block
// of memory, insert the bytes contained in data and add a jmp back to the
// location specified by RetnAddress.
unsigned char tmp[6];
LPVOID address;
// attempt to allocate memory in the remote process
address=VirtualAllocEx(phandle,NULL,data_size+5,MEM_COMMIT,PAGE_EXECUTE_READWRITE);
if (address==NULL)
return 0; // failed to allocate mem
// now write the users data
WriteProcessMemory(phandle,(LPVOID)address,data,data_size,NULL);
// add the return address
GenerateJmp(tmp,(unsigned char *)RetnAddress,(unsigned char *)address+data_size);
WriteProcessMemory(phandle,(LPVOID)((int)address+data_size),tmp,5,NULL);
// and finaly the hook into the program
GenerateJmp(tmp,(unsigned char *)address,(unsigned char *)HookAddress);
WriteProcessMemory(phandle,(LPVOID)HookAddress,tmp,5,NULL);
// return the address, so it can be passed to VirtualFreeEx
return (int)address;
}
Of course you should add better error checking, but I can't be stuffed. In
order to use the function you just pass it the hex values from your code block
(the one we created earlier) without a returning jmp and it will do the rest of
the work for you. So, going back to our pinball code from earlier we would
pass in the hex for the following asm:
10bc0 83f801 cmp eax,0x1
10bc3 7f05 jg 0x10bca
10bc5 b803000000 mov eax,0x3
10bca 8986646010000 mov [esi+0x146],eax
note that the jg is still there. This will still function, because the jg used
a relative offset, meaning it says jump over the next 5 bytes. i.e to the 2nd
mov instruction.
The hex we would pass to the function is:
83f8017f05b8030000008986646010000
And thats it, pretty simple really. Some source code to implement a very basic
trainer for pinball is below. There is lots left for the
Note that it is simpler to use the call/retn method, rather than the jmp, jmp
method shown here.
Enjoy
Sain
-------------------------------------------------------------------------------
File: main.cpp
-------------------------------------------------------------------------------
/* Pinball 3D trainer
** By Sain
*/
/*
** Okay, this is really untidy, but hey, its just to demonstrate how to use the
** dynamicly allocated chunk of memory. Use this for whatever you want, if you
** kill something with it, it isn't my problem. The beep will tell you the
** hook has activated, though you'll see that if you still have a debugger
** attached to pinball. Oh yeah, make sure you add a dialog or this wont work.
** Compiles with a bunch of warnings but works fine on VC 7 (or .NET or
** whatever).
**
** If you want to use this method it would be a good idea to adapt the code
** so that all the injected code space is allocated at once, and only the jump
** or call to the injected space is written when the options are activated.
** Its not hard and is left as an exercise for somebody else.
*/
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#include "resource.h"
#define TARGET_WINDOW "3D Pinball for Windows - Space Cadet"
#define TRAINER_NAME "3D Pinball Trainer"
// Globals:
HINSTANCE g_hInst;
HWND hWnd;
bool attached;
HANDLE phandle;
// Prototypes:
BOOL CALLBACK DialogProc (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam);
void InitOther(void);
void InitHotKeys(void);
void HandleHotKeys(int key);
int CreateDynamicHook(int HookAddress,int RetnAddress,unsigned char *data,int data_size);
void GenerateJmp(unsigned char *code,unsigned char *JumpDestination,unsigned char *JumpSource);
void GenerateCall(unsigned char *code,unsigned char *JumpDestination,unsigned char *JumpSource=0);
bool AttachProcess(char *window);
// Code:
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
{
MSG msg;
LPVOID lpMsgBuf;
g_hInst = hInstance;
// this dialog should be hell easy to make, i just used a empty dialog with a static
// item on it telling the user to hit numpad0 to make the trainer do stuff
if (!(hWnd = CreateDialog(hInstance, MAKEINTRESOURCE(IDD_DIALOG1), 0, DialogProc)))
{
FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM,0, GetLastError(), MAKELANGID(LANG_NEUTRAL,
SUBLANG_DEFAULT), (LPTSTR)&lpMsgBuf, 0, NULL );
MessageBox(0, (LPCTSTR)lpMsgBuf, "Error loading", MB_OK|MB_ICONINFORMATION );
LocalFree( lpMsgBuf );
PostQuitMessage(0);
}
ShowWindow(hWnd, SW_SHOWNORMAL);
InitHotKeys();
while(GetMessage(&msg, hWnd, 0, 0))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return msg.wParam;
}
BOOL CALLBACK DialogProc (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
PAINTSTRUCT ps;
HDC hdc;
hWnd=hwnd;
switch (message)
{
case WM_INITDIALOG:
InitOther();
return TRUE;
case WM_HOTKEY:
HandleHotKeys(wParam);
break;
case WM_PAINT: // window background
hdc = BeginPaint(hwnd, &ps);
// TODO: Add any drawing code here...
EndPaint(hwnd, &ps);
break;
case WM_DESTROY:
UnregisterHotKey(hwnd,666);
PostQuitMessage(0);
ExitProcess(0);
return TRUE;
case WM_CLOSE: // kill window on close
EndDialog(hwnd,0);
DestroyWindow (hwnd);
return TRUE;
}
return FALSE;
}
void InitOther(void)
{
// do startup inits here if needed
}
void InitHotKeys(void)
{
// register the hot key so windows knows what to do with it
RegisterHotKey(hWnd,666,0,VK_NUMPAD0);
}
void HandleHotKeys(int key)
{
static bool tog[4]={false,false,false,false};
unsigned char hack[17]={0x83,0xf8,0x01,0x7f,0x05,0xb8,0x03,0x0,0x0,0x0,0x89,0x86,0x46,0x01,0x0,0x0};
unsigned char unhack[6]={0x89,0x86,0x46,0x01,0,0};
static int address;
// first check if we have a handle to the process
if (!attached)
{
if (!AttachProcess(TARGET_WINDOW))
{
MessageBox(hWnd,"Could not attach to process - start the game",TRAINER_NAME,MB_OK);
tog[0]=tog[1]=tog[2]=tog[3]=tog[4]=false;
return;
}
}
// yup, now deal with the hotkey
switch(key)
{
case 666:
tog[0]=!tog[0];
if (tog[0])
{
// allocate memory for the dynamic hook, chuck the hack into it and set up
// the jumps to and from
address=CreateDynamicHook(0x1015F02,0x1015f08,hack,16);
if (!address)
{
attached=false;
return;
}
Beep(850,100);
}
else
{ // remove the hook to the hack
if (!WriteProcessMemory(phandle,(LPVOID)0x1015f02,unhack,6,NULL))
{
attached=false;
return;
}
// now we need to free up the memory we have been allocated
// otherwise we will keep allocating more memory every time
// somebody activates the trainer.
if (!VirtualFreeEx(phandle,(LPVOID)address,0,MEM_RELEASE))
{
Beep(700,100);Beep(750,100);
attached=false;
return;
}
}
break;
}
}
void GenerateJmp(unsigned char *code,unsigned char *JumpDestination,unsigned char *JumpSource=0)
{
if (!JumpSource) // if the user hasn't specified a source address
JumpSource=code; // assume we are writing at the "code" location
*code++=0xe9; // the jmp command
// the next line calculates the 4 bytes of the destination address
// which is simply the address of the destination - the address of the jmp + 5
*((int *&)code)++=JumpDestination - (JumpSource+5);
}
void GenerateCall(unsigned char *code,unsigned char *JumpDestination,unsigned char *JumpSource=0)
{
if (!JumpSource) // if the user hasn't specified a source address
JumpSource=code; // assume we are writing at the "code" location
*code++=0xe8; // the call command
// the next line calculates the 4 bytes of the destination address
// which is simply the address of the destination - the address of the call + 5
*((int *&)code)++=JumpDestination - (JumpSource+5);
}
int CreateDynamicHook(int HookAddress,int RetnAddress,unsigned char *data,int data_size)
{
// At the location specified by HookAddress, insert a jmp to an allocated block
// of memory, insert the bytes contained in data and add a jmp back to the
// location specified by RetnAddress.
unsigned char tmp[6];
LPVOID address;
// attempt to allocate memory in the remote process
address=VirtualAllocEx(phandle,NULL,data_size+5,MEM_COMMIT,PAGE_EXECUTE_READWRITE);
if (address==NULL)
return 0; // failed to allocate mem
// now write the users data
WriteProcessMemory(phandle,(LPVOID)address,data,data_size,NULL);
// add the return address
GenerateJmp(tmp,(unsigned char *)RetnAddress,(unsigned char *)address+data_size);
WriteProcessMemory(phandle,(LPVOID)((int)address+data_size),tmp,5,NULL);
// and finaly the hook into the program
GenerateJmp(tmp,(unsigned char *)address,(unsigned char *)HookAddress);
WriteProcessMemory(phandle,(LPVOID)HookAddress,tmp,5,NULL);
return (int)address;
}
bool AttachProcess(char *window)
/* this function takes the name of a window and attempts to attach to the
** associated project. Returns true on success. Sets the attached flag on
** success.
*/
{
HWND hwnd;
DWORD pid;
attached=false;
hwnd=FindWindow(0,window);
if (!hwnd)
return false;
GetWindowThreadProcessId(hwnd,(unsigned long *)&pid);
phandle=OpenProcess(PROCESS_ALL_ACCESS, FALSE, pid);
if (!phandle)
return false;
attached=true;
return true;
}