Tutorial How to find lots of engine functions in IDA (black ops 1)

Hexui Undetected CSGO Cheats Sinkicheat PUBG Cheat

Mystic

Newbie
Full Member
Jan 21, 2017
41
2,393
1
Game Name
Call of Duty: Black ops
Tutorial Link
N/A
How long you been coding/hacking?
2 years
Coding Language
C/C++, x86 asm
With Rakes recently announced full time commitment to GH, and my rekindled interest in game hacking, I decided I want to make a tutorial here. Here we go...

I'm going to use black ops 1 for this, but as all the cod games are built on the exact same engine, you should be able to do this in all of them, or most of them excluding maybe the newest ones.

Things to keep in mind:
1. Call of Duty is based on a modified version of the Quake 3 engine ie IdTech3
2. Quake 3 Arena is open source, so its available as a reference as essentially 90% of the CoD engine source.
3. The *essential* cod guide: Guide - How to Hack Call of Duty Games & Quake Engine Games



Go ahead and open the game executable in IDA, you should already know how to do this.

The method:
This method just uses strings existing in the Quake source code that persisted into call of duty games. In Quake, the CARMACK prefixed functions in "Client Game" as "CG_". This enables us to find LOTS of engine functions very quickly as a lot of them log debug info and they reference their own name.

Press Shift + F12 to open the strings window.
Press CTRL + F to filter strings.
Like mentioned before, the prefix used for a lot of engine functions is "CG_" so we will filter based on that.
You will notice that dvars are also prefixed with cg, but they are lowercase. Right click on the filter box and click "Match case", or don't if you also want to see dvar names.
Your output will look something like this:
1582087774271.png


For this example I am going to choose CG_Obituary. Double click on the string you want.
You should see something like this:
1582087924218.png

Normally when you want to find where a string is xref'd, you would click on the "aCg_obituary", but there are actually no xrefs to it. For some reason, all of these strings are prefixed by \x15 and if you look this up on the ascii table you find its referred to as a "Negative Acknowledgment". IDA doesn't put this byte and the ascii string tailing it as one thing, so we actually have to find xrefs to this byte.
Upon doing so we get an xref, and we can jump to it and spam F5.
1582088310928.png

And here we go we just found and decompiled CG_Obituary and we didn't have to do any debugging or reverse engineering at all its just right there.
1582088410707.png


This looks pretty bad, the decompiled output SHOULD show us the ascii string. Luckily fixing this in IDA is pretty easy. Highlight both the "byte" and the trailing string, and press U to undefine them both. Then, highlight all of the bytes that should be part of the string like so:
1582150707724.png

With this selection, press A to define an ascii string. Now in the decompiler output, it will show the string properly.
1582150847900.png

Much better!

Now you can just scroll to the top (or press page up a few times to jump to the top) and rename the function.

A few extra tips:
You can also do this same technique for other kinds of engine functions. For example, rendering functions are prefixed with "R_".
Looking at the quake source I linked above, you can decipher the parameters of a lot of these functions much easier. They are modified for this game, they dont match 100%, but its definitely helpful to have the q3arena source pulled up.

I looked so much on other forums for how to find these engine functions, and most of the people finding them were using a leaked PDB. This is way faster and easier. Hope this helps and I'll probably be making more small tutorials like this and hopefully get better at making them now. I'll see about making some video tutorials, and I'd also like to make some videos for the GH youtube channel if possible it sounds like fun.
 

Rake

Cesspool Admin
Administrator
Jan 21, 2014
12,133
78,998
2,392
Glad to have you back, awesome little tutorial, I will add a link in the main COD guide
 
  • Like
Reactions: Mystic

xxxtarnatiiion

Full Member
Dec 6, 2019
47
573
1
hey, thanks for your tutorial, there aren't many out there. I've been wondering if we can use this kind of function ("cg" so dvars?) in-game.
1584267025419.png

or like "cg_thirdPersonMode"

edit: that's bo2's exe. btw
 

Mystic

Newbie
Full Member
Jan 21, 2017
41
2,393
1
hey, thanks for your tutorial, there aren't many out there. I've been wondering if we can use this kind of function ("cg" so dvars?) in-game.
View attachment 9275
or like "cg_thirdPersonMode"

edit: that's bo2's exe. btw
Yes you actually can and this is how I prefer to find them. The way the call of duty games use these dvars is they just have what is pretty much a massive global list of `dvar_t` object pointers. For an example real quick, Ill show how to find and use cg_fov (since a common tool for cod, especially older ones without fov sliders is a fov mod).

Same thing as before, search for the name of the dvar you want
1584275073516.png


then doublel click it and find the string in IDA-view
1584275135540.png


Next, find an xref. Rarely, some of these dvar names will come up with more just a single ref. If that is the case for you, find where it gets pushed onto the stack.
1584275340812.png

This takes us to some sort of an "initialization" function.
1584275502411.png

I will not assume that everybody reading this will have the decompiler plugin for ida, so ill walk you through the disassembly a bit.
1584276403461.png

At 1, This is calling the dvar initialization function. The significance of this function to us is that it takes a string (for the name), and a value to intialize the dvar to. It returns a pointer to a dvar structure. Remember this its important in a second: IT RETURNS A *POINTER* to a dvar STRUCTURE, not the value of the dvar itself.

The function, sub_679020, returns this pointer in the EAX register.

On where I have indicated 2, the return value (the dvar pointer) is copied to dword_2ff6888. This is the global pointer to this cg_fov dvar structure.
Now, we have to find the offset into the structure that the actual value we want to be able to change is. I happen to know off the top of my head that the offset for COD7 Black Ops 1 is 0x18, but I think its important to show how you would find that, and I guess I will show code on how to use the dvar as well.

This might be a bit lengthy now, but when reverse engineering you must have a thought process. I will explain mine and why this method works.
We see that we call a function to create a dvar, it returns a pointer to that dvar, and then that pointer gets stored in a global variable *for later use*. This variable is obviously used elsewhere in the code, so lets find a xref to it, and find out *how* it is used.
1584278088628.png

the first xref takes us to:

1584278074513.png

We should notice 3 important things. First, the pointer is being loaded into eax. Next, 0x18 is being added to the pointer and then it is dereferenced. The value pointed to by [eax + 0x18] is then moved into *a floating point register*. The first two things are fairly universal, however the last part about it being moved into a floating point register is not going to be. Some dvars contain float values, others contain integer or string or some other data type, this one just happens to be a floating point value (which makes sense because its your fov right). Now with this information it shouldn't be too tricky to use this in your code.


use_dvars.cpp:
uintptr_t* cg_fov_struct = *(uintptr_t**)0x02FF6888;
*(float*)(cg_fov_struct + 0x6) = 90.f; // set fov to 90.0
There is one thing you need to look out for, and that is that the offset shown in IDA is gonna be in terms of how many *BYTES*, while what you compile in your c++ code is going to be adding sizeof(pointer_type)*offset. So in this case, the size of a uintptr_t is 4 bytes, so we need to add (0x18 is 24 in decimal) 24/4 = 6 to the base pointer to access the actual fov value. If that is confusing to you, another way to accomplish the same thing is
alternate_way.cpp:
BYTE* cg_fov_struct = *(BYTE**)0x02FF6888;
*(float*)(cg_fov_struct + 0x18) = 90.f;
This way you don't have to think, you can just copy the offset IDA tells you and plug it in.
Let me know if you have any more questions.
 
  • Love
Reactions: xxxtarnatiiion

xxxtarnatiiion

Full Member
Dec 6, 2019
47
573
1
Let me know if you have any more questions.
wow… thank you for taking the time to explain all this to me, it was very clear.

1584290881382.png


i figured it all out for mw3 btw

now, i got another question that may be irrelevant, it's about a crash when i unhook (midfunction) in bo2, it crashes the game, do you have time to help me a bit?
 

Mystic

Newbie
Full Member
Jan 21, 2017
41
2,393
1
Yes you actually can and this is how I prefer to find them. The way the call of duty games use these dvars is they just have what is pretty much a massive global list of `dvar_t` object pointers. For an example real quick, Ill show how to find and use cg_fov (since a common tool for cod, especially older ones without fov sliders is a fov mod).

Same thing as before, search for the name of the dvar you want
View attachment 9276

then doublel click it and find the string in IDA-view
View attachment 9277

Next, find an xref. Rarely, some of these dvar names will come up with more just a single ref. If that is the case for you, find where it gets pushed onto the stack.
View attachment 9278
This takes us to some sort of an "initialization" function.
View attachment 9279
I will not assume that everybody reading this will have the decompiler plugin for ida, so ill walk you through the disassembly a bit.
View attachment 9280
At 1, This is calling the dvar initialization function. The significance of this function to us is that it takes a string (for the name), and a value to intialize the dvar to. It returns a pointer to a dvar structure. Remember this its important in a second: IT RETURNS A *POINTER* to a dvar STRUCTURE, not the value of the dvar itself.

The function, sub_679020, returns this pointer in the EAX register.

On where I have indicated 2, the return value (the dvar pointer) is copied to dword_2ff6888. This is the global pointer to this cg_fov dvar structure.
Now, we have to find the offset into the structure that the actual value we want to be able to change is. I happen to know off the top of my head that the offset for COD7 Black Ops 1 is 0x18, but I think its important to show how you would find that, and I guess I will show code on how to use the dvar as well.

This might be a bit lengthy now, but when reverse engineering you must have a thought process. I will explain mine and why this method works.
We see that we call a function to create a dvar, it returns a pointer to that dvar, and then that pointer gets stored in a global variable *for later use*. This variable is obviously used elsewhere in the code, so lets find a xref to it, and find out *how* it is used.
View attachment 9282
the first xref takes us to:

View attachment 9281
We should notice 3 important things. First, the pointer is being loaded into eax. Next, 0x18 is being added to the pointer and then it is dereferenced. The value pointed to by [eax + 0x18] is then moved into *a floating point register*. The first two things are fairly universal, however the last part about it being moved into a floating point register is not going to be. Some dvars contain float values, others contain integer or string or some other data type, this one just happens to be a floating point value (which makes sense because its your fov right). Now with this information it shouldn't be too tricky to use this in your code.


use_dvars.cpp:
uintptr_t* cg_fov_struct = *(uintptr_t**)0x02FF6888;
*(float*)(cg_fov_struct + 0x6) = 90.f; // set fov to 90.0
There is one thing you need to look out for, and that is that the offset shown in IDA is gonna be in terms of how many *BYTES*, while what you compile in your c++ code is going to be adding sizeof(pointer_type)*offset. So in this case, the size of a uintptr_t is 4 bytes, so we need to add (0x18 is 24 in decimal) 24/4 = 6 to the base pointer to access the actual fov value. If that is confusing to you, another way to accomplish the same thing is
alternate_way.cpp:
BYTE* cg_fov_struct = *(BYTE**)0x02FF6888;
*(float*)(cg_fov_struct + 0x18) = 90.f;
This way you don't have to think, you can just copy the offset IDA tells you and plug it in.
Let me know if you have any more questions.
I forgot to mention you can also use Cbuf_AddText as if you were using the console as a developer. Its a bit easier in my opinion, it just looks like this:
cbufaddtext.cpp:
Cbuf_AddText(0, "cg_fov 90"); // set Fov to 90
 

Mystic

Newbie
Full Member
Jan 21, 2017
41
2,393
1
wow… thank you for taking the time to explain all this to me, it was very clear.

View attachment 9283

i figured it all out for mw3 btw

now, i got another question that may be irrelevant, it's about a crash when i unhook (midfunction) in bo2, it crashes the game, do you have time to help me a bit?
Are you using the latest GuidedHacking tutorial on hooking? If you are and its still crashing, what function are you hooking and what is the code that you are using to unhook? (Also it might be helpful to make a Help thread, so the information is searchable by other people easier)
 

xxxtarnatiiion

Full Member
Dec 6, 2019
47
573
1
Are you using the latest GuidedHacking tutorial on hooking? If you are and its still crashing, what function are you hooking and what is the code that you are using to unhook? (Also it might be helpful to make a Help thread, so the information is searchable by other people easier)
i actually am (i think?)

hook.h:
bool SHook(void* dst, void* src, int len, DWORD& jmp) {
    if (len < 5)
        return FALSE;

    jmp = reinterpret_cast<DWORD>(dst) + len;

    DWORD lpProtection, lpRelative, lpTemporary;

    VirtualProtect(dst, len, PAGE_EXECUTE_READWRITE, &lpProtection);
    memset(dst, 0x90, len);
    lpRelative = ((DWORD)src - (DWORD)dst) - 5;

    *(BYTE*)dst = 0xE9;
    *(DWORD*)((DWORD)dst + 1) = lpRelative;

    VirtualProtect(dst, len, lpProtection, &lpTemporary);
    return TRUE;
}
i'm hooking this function that accesses all entities' health in the game (so the game stores every enemy base in a register)
1584292229152.png


and when i try to unhook (i'm patching bytes at my jump) (before calling freelib&exit…) my jump is patched so the function is back to normal, but the game freezes and crash

and yep if you don't have any idea rn i'll make one
thanks!
 

Mystic

Newbie
Full Member
Jan 21, 2017
41
2,393
1
i actually am (i think?)

hook.h:
bool SHook(void* dst, void* src, int len, DWORD& jmp) {
    if (len < 5)
        return FALSE;

    jmp = reinterpret_cast<DWORD>(dst) + len;

    DWORD lpProtection, lpRelative, lpTemporary;

    VirtualProtect(dst, len, PAGE_EXECUTE_READWRITE, &lpProtection);
    memset(dst, 0x90, len);
    lpRelative = ((DWORD)src - (DWORD)dst) - 5;

    *(BYTE*)dst = 0xE9;
    *(DWORD*)((DWORD)dst + 1) = lpRelative;

    VirtualProtect(dst, len, lpProtection, &lpTemporary);
    return TRUE;
}
i'm hooking this function that accesses all entities' health in the game (so the game stores every enemy base in a register)
View attachment 9284

and when i try to unhook (i'm patching bytes at my jump) (before calling freelib&exit…) my jump is patched so the function is back to normal, but the game freezes and crash

and yep if you don't have any idea rn i'll make one
thanks!
Yea I think you should make a thread, I don't see anything wrong with the hook code (although you only show your hook function, not the unhook function) and if you physically looked at the asm in the debugger and saw that when it unhooked it was all the proper code but it still crashes then you have a bit more debugging to do. You can mention me in the thread and ill see if I can help out with this.
 
  • Like
Reactions: xxxtarnatiiion

Mystic

Newbie
Full Member
Jan 21, 2017
41
2,393
1
i'm writing it rn,
while we're here, i think what i don't get the most is how can we find like cbuf_addtext like you said ?
You can find it through string references like before:
1584294277451.png

its only xrefed once, and its in the Cbuf_AddText function:
1584294312218.png

and the function definition is:
1584294548795.png

Hope that helps! you can pretty much issue any console command to the game that it recognizes and it will work as intended for the most part. Saves u a lot of time if you just want to set the value of a dvar that you already know the name of. One thing that can modify MANY things is better than one thing that modifies 1 thing right? xD
 

Mystic

Newbie
Full Member
Jan 21, 2017
41
2,393
1
it is ahah

ctrl+f = "cbuf" || "addtext" (don't have any ref… bo2 btw)
View attachment 9288
hmm... see if you can trace back to it from this:
1584295369799.png

copy and past this "fade 0 0 0 0 3\n" and see if you can find it there. Otherwise, you might need to do some harder reversing to find that function in bo2. I looked online and it seems like it does exist in bo2, so its not been removed, but I guess they removed the string references in it to make it a bit trickier or it might have just worked out that way perhaps. Another thing, make sure when you use the string filter that you don't accidentally have it set to match cases which would cause the Cbuf string to not show up. I do have bo2 as well so if I absolutely have to I can pop it open and try myself and show how I did it.
Ill add extra calls to the function I found so if the first one is a flop, you can try more than one thing, maybe one of them will work for ya:
1584295713353.png

1584295746775.png

1584295776100.png

I would be very surprised if that last one doesnt work.
Hope this helps let me know how it goes!
 

xxxtarnatiiion

Full Member
Dec 6, 2019
47
573
1
I would be very surprised if that last one doesnt work.
the last one saved my life lol
1584298455024.png

"vid_restart" is casted as an int tho.. do I have to try this sub anyway ?

EDIT: i found cbuf_addtext in mw3 with vid_restart too, i'll try the one i found in bo2 now
 
Last edited:
  • Like
Reactions: Mystic

Mystic

Newbie
Full Member
Jan 21, 2017
41
2,393
1
the last one saved my life lol
View attachment 9294
"vid_restart" is casted as an int tho.. do I have to try this sub anyway ?
Yea so IDA will sometimes cast char* to ints because it just isn't too smart sometimes. If you disassemble that function, I bet its gonna look something like this:
1584299015364.png

and then you should have it
Also, to help fix IDA's little mistake, press y on the function and change the type of the second parameter from "int" to "char*" like this:
1584299093020.png
 

xxxtarnatiiion

Full Member
Dec 6, 2019
47
573
1
Also, to help fix IDA's little mistake, press y on the function and change the type of the second parameter from "int" to "char*" like this:
View attachment 9296
wow thx you just learned me a lot of precious things!

so i didnt managed to find bo2's addtext, heres how I do it anyway
1584301311863.png


then call it
1584301339485.png


drawGun or fov dont work both, so i assume i'm not using the good address

(but i did find the correct one for mw3, i'm destroying the game btw)
 

Mystic

Newbie
Full Member
Jan 21, 2017
41
2,393
1
wow thx you just learned me a lot of precious things!

so i didnt managed to find bo2's addtext, heres how I do it anyway
View attachment 9297

then call it
View attachment 9298

drawGun or fov dont work both, so i assume i'm not using the good address

(but i did find the correct one for mw3, i'm destroying the game btw)
What could be occurring is that they added some kind of cheat protections to those commands and you might have to manually modify those values. You can get away with a lot, but not everything there. Because there were 2 things indicating it is the correct function, the chance of what you found in Bo2 not being Cbuf_AddText is low, it (most likely) just has protections that are blocking the use of the commands you want. I haven't tried anything with bo2, but if you are working with multiplayer then I would expect hardly any of those console commands to actually work, its just gonna ignore it. You should still be able to get away with directly modifying the dvars though.

EDIT: I also wanted to mention that in black ops 1, where this function DOES work as intended, there is some strange behavior with it that could be related to this issue. If multiple calls to it are made with different commands, only the first command takes effect. I have no idea why. Rapid successive calls to Cbuf_AddText with the same exact command behave normally, but if the command is different than the previous calls, it has no effect. This was in Black ops 1 singleplayer/zombies mode and I don't currently have any work around other than directly modifying the dvars.

Also one other thing, make sure your strings are null terminated.
 
Last edited:

Mystic

Newbie
Full Member
Jan 21, 2017
41
2,393
1
What could be occurring is that they added some kind of cheat protections to those commands and you might have to manually modify those values. You can get away with a lot, but not everything there. Because there were 2 things indicating it is the correct function, the chance of what you found in Bo2 not being Cbuf_AddText is low, it (most likely) just has protections that are blocking the use of the commands you want. I haven't tried anything with bo2, but if you are working with multiplayer then I would expect hardly any of those console commands to actually work, its just gonna ignore it. You should still be able to get away with directly modifying the dvars though.
u should write more tuts ur good at it ;)
Oh why thank you, I plan on it in the future
u should write more tuts ur good at it ;)

edit: almost forgot, do u know what registerTag or getTagPos are for ?
Yea RegisterTag is for "caching" the tag (as I understand it, basically "enable" the tag on a certain entity if it isn't already, but I'm not exactly sure myself!), and GetTagPos gets the 3D world coordinates for the position of the tag. If you don't know what "tag" refers to exactly, its basically just a way to store information about the player's "skeleton". perhaps in some hacks you have seen the skeleton esp and you can see other peoples "bones". Search for the string "j_head" in IDA and you will be able to hop around and find those two functions. As a disclaimer, I haven't done a lot with these 2 functions and I'm not the most qualified to talk about them, but if you google around I'm sure you can find more info about them. Hope that gives you the gist of what those 2 are for. Good luck!
And to put it very simply: "Tag" = bones like head, neck, arm, wrist.
 
  • Like
Reactions: xxxtarnatiiion
Attention! Before you post:

Read the How to Ask Questions Guide
99% of questions are answered in the Beginner's Guide, do it before asking a question.

No Hack Requests. Post in the correct section.  Search the forum first. Read the rules.

How to make a good post:

  • Fill out the form correctly
  • Tell us the game name & coding language
  • Post everything we need to know to help you
  • Ask specific questions, be descriptive
  • Post errors, line numbers & screenshots
  • Post code snippets using code tags
  • If it's a large project, zip it up and attach it

If you do not comply, your post may be deleted.  We want to help, please make a good post and we will do our best to help you.

Community Mods