Field of View Changer for Call of Duty: Mw2 (2009)

by nschumac in Teachers > University+

41 Views, 1 Favorites, 0 Comments

Field of View Changer for Call of Duty: Mw2 (2009)

20241122000858_1.jpg

I've been a 'gamer' for quite a long time (currently i don't play as much anymore, but i used to) and occasionally I would run into so called 'Hackers'. I always wondered "how" they made the game change in weird ways, how they controlled it, seemingly magically.

Luckily i was able to talk with one who told to first learn the programming language c++, this is what got me into the whole world of coding.

After having spent a good year learning coding i wasn't really closer to writing a "hack" myself since there are a lot more things that go into it. After trial and error and a lot of 'learning', i finally managed to write something that does something in the game. EUPHORIA!

I'm writing this to get people interested in 'hacking' (it doesn't always have to be bad ;))

The program we're going to create modifies in-game memory of Modern Warfare 2 to give you more or less field of view. (this is not possible in the vanilla game)

Supplies

We will use Visual Studio Community to write our simple C++ "trainer" that will change the in-game field of view of Call of Duty: Mw2 (can be found on Steam).

To find the correct address we write to we need to find them in memory first of all and here is where use IDA a disassembler.


Installing Visual Studio Community

install c++ development kit.PNG
install visualt studio.PNG

Please use the link provided and download the visual studio community installer.

Upon opening the installer please press modify and select the c++ development.

After which you can install visual studio

Might take some time ;)

Downloading Steam and 'installing' Mw2

create steam account.PNG
modern warfare 22009.PNG

Please use the link provided to install the steam installer and run it.

Once it's complete create a steam account (free)

After which, locate the 'store' page (Top Left) and search (Top right) for 'call of duty: modern warfare 2 (2009)'

Locate your library and download the game :)

Install IDA

Use the link and download / install the free version of IDA

Writing a 'Hello World' Console Program in C++

Create new Project.PNG
create empty project.PNG
set name of project.PNG
Don't need to sign in.PNG
Add new File.png
main.cpp file.png
Write hello World progarm.png
Select x86 compilation.png
launch via local windows debugger and see output.png

Open visual Studio Community

Select create a new project -> empty project c++

Name it and maybe select a folder where it should save the project files

Once it's finished opening the new project right click on the solution explorer and add a new 'item'

Add and Open the new .cpp located int he solution explorer

This is a simple c++ program that.

  1. Execution starts in the main function
  2. we include the iostream header which houses the std::cout function (used to print to out stream)
  3. then we return 0; from main
#include <iostream>

int main(int argc, char* argv[]) {

std::cout << "Hello World" << std::endl;

return 0;
}

Then Select the x86 compiler and run it via the local debugger button which will spawn a console and show you hello world :)


CONGRATS you wrote a c++ program!!!

Finding the FOV 'offset' / 'Address'

CONtinue ida 2.png
new file.png
Find lisence download from account in IDA.png
add license.png
Select iw4mp exe.png
Press ok.png
don't try to load PDB files, there are none.png
wait to finish loading.png

Open IDA which should be installed otherwise see step 3.

Launch IDA

Select new which will open the dissassembly window

It might request you to add the license, don't worry download it from your IDA account and select it.

Once open it'll request a file to dissasemble select iw4mp.exe foudn in the common directory of the steamapps

press ok, IDA automatically knows what file type it is from the signature.

Press no, as there are no PDB files to load (these are created normally for debugging purposes, but ofc won't be shipped with the game)

wait for it finish indexing / loading hte exe :)

Finding Cg_fov Dvar Address

Search for strings.png
search for cg_fov, select the one indicated.png
cg_fov double click on register dvar.png
Foudn address of cg_fov.png

Open the subview for strings

This is all strings that IDA found in the executable.

We will search for 'cg_fov' as this is the name of the 'dvar' (diction variable, activision used a dictionary for all these kind of variables and values, things like fov, gamma, a bunch :)) you can find others by searching for cg_ or just by snooping around in the strings and functions.

once found double click the cg_fov string which will bring you to where it is being stored

double click on the function indicated by the sub... next to cg_fov.

this is the function that references this string, thus normally you would look at and see where and why it is being called. I will the reason, it is to register the dvar (this is called once at the start of every new game i believe, or it might be every time you respawn)

You'll be brought inside the function and you'll see this code

fld ds:flt_6845D8
add esp, 50h
push offset aTheFieldOfView ; "The field of view angle in degrees"
push 44h ; 'D' ; int
sub esp, 0Ch
fstp [esp+14h+var_C] ; float
mov dword_AAC20C, eax
fld ds:flt_68A634
fst [esp+14h+var_10] ; float
fstp [esp+14h+var_14] ; float
push offset aCgFov ; "cg_fov"
call sub_5BDBD0 // HERE THE Is the REGISTER_DVAR FUNCTION
fld ds:flt_6EFC18
add esp, 18h
push offset aScaleAppliedTo ; "Scale applied to the field of view"
push 4 ; int
sub esp, 0Ch
fstp [esp+14h+var_C] ; float
mov dword_AAC1F8, eax // HERE THE RESULT OF THE REGISTER DVAR IS RETURN AND STORED
fld ds:flt_6F22A0
fstp [esp+14h+var_10] ; float
fld1
fstp [esp+14h+var_14] ; float
push offset aCgFovscale ; "cg_fovScale"
call sub_5BDBD0

As you can see are claling the register dvar function and loading the result in the address dword_AAC1F8

Finding and Editing Fov From Dvar Address

dboule click function referencing dvar address.png
double click dvar address.png
seeing offset 0xC for FOV.png

Double click the dvar address found in the previous step.

This will bring you to the address of the cg_fov dvar.

From here we can use the references to find where it is being called / used (double click first function)

fstp dword ptr [ebp+0F0h]
call sub_5B7580
fld [esp+0ACh+arg_14]
mov edx, dword_AAC1F8 // cg_fov loaded into edx register
fdiv ds:dbl_67DBE8
add esp, 8
fstp dword ptr [ebp+118h]
fld dword ptr [edx+0Ch] // 0xC is being added -> from previous function this is FOV
fmul ds:dbl_6F24A8
fmul ds:dbl_6F1FE8
fstp [esp+0A4h+var_98]
fld [esp+0A4h+var_98]
call __CItan

Here we see the cg_fov is ebing loaded into register edx, after which 0xC is being added as an offset.

If you analyze the function from step 6 you can see that the value 65.0 (defualt value of fov) is being loaded at this position

thus all we need to do change our fov is

cg_fov dvar = readMemory(0xAAC1F8)

writeMemory(cg_fov_dvar + 0xC, <FOV> );


Hold on to that memory address and offset '0xAAC1F8', '0xC'

Writing a Program That Gets the Pid of a Program by Name

set properties of project.png
Multibyte.png

Before starting to write make sure to set your program to use the multibyte character set instead of the unicode otherwise certain functions might function differently (you can always play around and see)

This code basically iterates over all processes and if you find one that has the same name as the one we are looking for "iw4mp.exe" (modern warfare 2) it will return the pid.

#include <Windows.h> // DWORD and other functions later we will use
// !IMPORTANT include windows before TlHelp32
#include <TlHelp32.h> // header for create tool help 32 snapshot


DWORD getProcessIdOfProgram(std::string processName) {
HANDLE hProc = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, NULL); // snapshot of all // processes
if (hProc == INVALID_HANDLE_VALUE)
return false;

PROCESSENTRY32 processEntry;
processEntry.dwSize = sizeof(PROCESSENTRY32);

DWORD processId = NULL;

if (Process32First(hProc, &processEntry))
{
do // iterate over all processes and find ours
{
if (!strcmp(processName.c_str(), processEntry.szExeFile))
{
processId = processEntry.th32ProcessID;
break;
}
} while (Process32Next(hProc, &processEntry));
}
CloseHandle(hProc);

return processId;
}


int main(int argc, char* argv[]) {
std::cout << getProcessIdOfProgram("iw4mp.exe") << std::endl; // print pid

return 0;
}

Get Game Handle and Write Fov

90 fov.png
FOV.png
20241122000631_1.jpg
20241122000858_1.jpg

I use openprocess to get the gamehandle (access to write to the game)


I created a write process fucntion which will read the the address add the offset and write the value we want a float to fov.

I also created a small loop which listens for keystrokes

  1. the add button closes programm
  2. left and right arrow keys decrease / increase fov
HANDLE getGameHandle(DWORD processId) {
return OpenProcess(PROCESS_VM_WRITE | PROCESS_VM_READ | PROCESS_VM_OPERATION, NULL, processId);

}


template<class T>
inline void WriteProcess(HANDLE gameHandle, DWORD addy, T value, BYTE Offset) {
DWORD newAddy = 0;
ReadProcessMemory(gameHandle, (LPCVOID)addy, (LPVOID)&newAddy, sizeof(newAddy), NULL);
if (newAddy)
WriteProcessMemory(gameHandle, (LPVOID)(newAddy + Offset), (LPCVOID)&value, sizeof(value), NULL);
}

void printFOV(float fovValue) {
system("cls");
printf("FOV: %0.1f", fovValue);
}

int main(int argc, char* argv[]) {

DWORD processId = getProcessIdOfProgram("iw4mp.exe");

if (processId == NULL) {
std::cout << "couldn't find game" << std::endl;
return 1;
}

HANDLE gameHandle = getGameHandle(processId);

if (gameHandle == INVALID_HANDLE_VALUE) {
std::cout << "couldn't open game" << std::endl;
}


float ourFov = 65.0f;

printFOV(ourFov);
while (!GetAsyncKeyState(VK_ADD)) { // while we don't press this key
Sleep(200);
WriteProcess<float>(gameHandle, 0xAAC1F8, 0xC, ourFov);

if (GetAsyncKeyState(VK_RIGHT)) { // if we press this key
ourFov += 5;
printFOV(ourFov);
}
else if (GetAsyncKeyState(VK_LEFT)) { // if we press this key
ourFov -= 5;
printFOV(ourFov);
}

}

return 0;
}