Wednesday, August 23, 2006

Code Shifting

--------------------------------------------------------------------------------

Code Shifting

Written: Saturday 11th Nov, 2003
--------------------------------
Revised: Thursday 8th July, 2004
--------------------------------
Subject: Code Shifting
----------------------
Author: spookie
---------------

CodeShifting happens simply when a DLL is dynamically loaded with LoadLibrary() by the game,
and inside this DLL is the game code that you need to edit. This is becoming more and
more common these days because of the amount of games coming out based on generic engines.
To name a few, Max Payne 2, Splinter Cell, Tron 2.0 and Counter-Strike: Condition Zero.
Out of these four chart topping games, only two working trainers were released. There was
an additional obstacle with Tron 2.0, being based on the Lithtech engine, as not only is the
game code in a dynamically loaded dll, it drops this dll in the temp files with a random
name before loading.

Now, to get around this problem we've got to find the address at which our game code dll
has been loaded, and with the help of the ToolHelp32 APIs it's not a hard task. First of
all, you need to know what dll your code is inside...

So for example lets say our cheat address is 008C3627 - We need to find a dll in the
currently running target game that's loaded at an address lower than 008C3627, but with an
image size of more than 008C3627. If you have LordPE handy (http://protools.cjb.net/) you
can get a list of loaded dlls and their base address and image size by selecting the game
process in the upper list and the dlls will then be displayed in the lower list. Or, you
could use a small tool, codeloc, I created for this, available at http://spookie.rom.cd/.
codeloc takes 2 parameters, the first is the game window text or the game window class
name, the second is your cheat location. e.g...

--------------------------------------------------------------------------------code---
codeloc "MaxPayne2" 8C3627

...will result as...

<008C3627> is inside module "X_GameObjectsMFC.dll" with base address <008B0000>
-------------------------------------------------------------------------------/code---

Now we know what dll we're working inside, we have to offset our given cheat address by
the current base address. Given the above example, my cheat address was 8C3627, and the dll
that contains it was loaded at 008B0000, so our cheat address offset would be 13627...

Cheat Address - Base Address = Cheat Offset
8C3627 - 8B0000 = 13627

So now we have our new cheat address we have to get the base address of the dll within our
trainer at runtime. To do this we'll use the ToolHelp32 APIs to cycle through all the
loaded "Modules" of the target game until we find ours.

Here is the code to do so in Delphi...

--------------------------------------------------------------------------------code---
uses TlHelp32;

function GetModuleBaseAddress(iProcId: Cardinal; DLLName: String): Cardinal;
var
hSnap: THandle; // Process snapshot handle.
xModule: ModuleEntry32; // Module information structure.
begin
Result:= 0; // If the result of the function is 0, it didn't find the base address.
// i.e.. the dll isn't loaded.
hSnap:= CreateToolHelp32Snapshot(TH32CS_SNAPMODULE, iProcId); // Creates a module
// snapshot of the
// game process.
xModule.dwSize:= SizeOf(xModule); // Needed for Module32First/Next to work.
If Module32First(hSnap, xModule) Then Begin // Gets the first module.
While Module32Next(hSnap, xModule) Do // Loops through the rest of the modules.
If xModule.szModule = DLLName Then // If this is the module we want...
Result:= Cardinal(xModule.modBaseAddr); // Save the base address it in result.
End;
CloseHandle(hSnap); // Free the handle.
end;
-------------------------------------------------------------------------------/code---

...and here is the same thing in C...

--------------------------------------------------------------------------------code---
#include

DWORD GetModuleBaseAddress(DWORD iProcId, char* DLLName)
{
HANDLE hSnap; // Process snapshot handle.
MODULEENTRY32 xModule; // Module information structure.

hSnap = CreateToolhelp32Snapshot(TH32CS_SNAPMODULE, iProcId); // Creates a module
// snapshot of the
// game process.
xModule.dwSize = sizeof(MODULEENTRY32); // Needed for Module32First/Next to work.
if (Module32First(hSnap, &xModule)) // Gets the first module.
{
while (Module32Next(hSnap, &xModule)) // Loops through the rest of the modules.
{
if (strcmp(xModule.szModule, DLLName) == 0) // If this is the module we want...
{
CloseHandle(hSnap); // Free the handle.
return (DWORD)xModule.modBaseAddr; // return the base address.
}
}
}
CloseHandle(hSnap); // Free the handle.
return 0; // If the result of the function is 0, it didn't find the base address.
// i.e.. the dll isn't loaded.
}
-------------------------------------------------------------------------------/code---

...and finally in masm...

--------------------------------------------------------------------------------code---
GetModuleBaseAddress proc iProcID:DWORD, DLLName:DWORD
LOCAL hSnap:DWORD
LOCAL xModule:MODULEENTRY32
invoke CreateToolhelp32Snapshot, TH32CS_SNAPMODULE, iProcID
mov hSnap,eax
mov xModule.dwSize, sizeof xModule
invoke Module32First, hSnap, addr xModule
test eax, eax
jnz getdll
mov eax, 0
ret
getdll:
invoke Module32Next, hSnap, addr xModule
test eax, eax
jnz checkdll
mov eax, 0
ret
checkdll:
invoke lstrcmpi, DLLName, addr xModule.szModule
test eax, eax
jnz getdll
mov eax, xModule.modBaseAddr
ret
GetModuleBaseAddress endp
-------------------------------------------------------------------------------/code---

These functions require the ProcessID of the game (GetWindowThreadProcessID()), and the
name of the dll you want the base address of. I've learnt the hard way that you should
use this function before doing any injection, not when the game has first started,
because some games like to reload the game dlls when you change level (Counter-Strike: CZ)

In the end, the lpBaseAddress parameter you will send to WriteProcessMemory will be...

--------------------------------------------------------------------------------code---
GetModuleBaseAddress(GAME_PROCESS_ID, "X_GameObjectsMFC.dll") + 0x13627
-------------------------------------------------------------------------------/code---

Any problems just email me at: spo@kie.org.uk

- spookie

Class DynamicArray

Class DynamicArray
'************** Properties **************
Private aData
'****************************************
'*********** Event Handlers *************
Private Sub Class_Initialize()
Redim aData(0)
End Sub
'****************************************
'************ Property Get **************
Public Property Get Data(iPos)
'Make sure the end developer is not requesting an
'"out of bounds" array element
If iPos <> UBound(aData) then
Exit Property 'Invalid range
End If
Data = aData(iPos)
End Property
Public Property Get DataArray()
DataArray = aData
End Property
'****************************************
'************ Property Let **************
Public Property Let Data(iPos, varValue)
'Make sure iPos >= LBound(aData)
If iPos < LBound(aData) Then Exit Property
If iPos > UBound(aData) then
'We need to resize the array
Redim Preserve aData(iPos)
aData(iPos) = varValue
Else
'We don't need to resize the array
aData(iPos) = varValue
End If
End Property
'****************************************
'************** Methods *****************
Public Function StartIndex()
StartIndex = LBound(aData)
End Function
Public Function StopIndex()
StopIndex = UBound(aData)
End Function
Public Sub Delete(iPos)
'Make sure iPos is within acceptable ranges
If iPos <> UBound(aData) then
Exit Sub 'Invalid range
End If
Dim iLoop
For iLoop = iPos to UBound(aData) - 1
aData(iLoop) = aData(iLoop + 1)
Next
Redim Preserve aData(UBound(aData) - 1)
End Sub
'****************************************
End Class