[Concept&Code]Get bone positions[MW2]
~ This is also posted in the MW2 source code section ~
Part 1 - some basic stuff you need to know about pointers to structures
if we run that it will say something along the lines of
Originally Posted by retardapp
Now, let's say LOLSTRUCT is the retarded struct of a retarded program written by a retard(/me)
We are interested in c, since that contains the name of the player.
but oh f*ck, we don't have the original struct... No worries tho, we have ollydbg skills =)
Yay!! We found the addies of the struct(403378, 40339C, 4033C0), but we are lazy and don't want to have to use hardcoded addies for all, so what we can do now is:
A. Find the size of the struct, and go from there.
B. Rebuild the struct(structbuilder is a great tool for this), partially or full, and go from there.
A would look like:
Then you could use assembly to access it's members.
B would look like:
In my opinion, B is WAY more attractive, since we can just use it like a normal struct, and it's way easier to access members.
So remember: [u]If the size of your struct is correct you can access each part of the array by specifying it's index[u]
Part 2 - The real deal =)
I'll be using the AlterIW version of MW2 since that's probably what most of you will be using, it works the exact same for 1.1.195!
In order to get a bone's position you first need to have an entity to get the bone from.
So lets find the CEntity, the string we'll be looking for is "%s is dead", the offset is -0x27(which is a direct offset, so scroll up once to see it in it's instruction):
0x00409041-0x27 = 0x0040901A
Scroll up once and you'll have this instruction selected:
So the entity array is located at 0x008F7A78, so control+g there and 'Find references to selected command', pick a random one from the list of references(I took the first one).
What happens is the index of the entity is placed in ESI, then ESI is multiplied by 0x204 and then stored in ESI, and then they add the base address of the array to it.
0x204... IS THE SIZE OF CEntity STRUCT!
Since we won't be doing a whole lot more with CEntity then getting bone positions, we can just declare it like this:
Now we can get an Entity by it's index like this:
Since we're able to get entities now, let's look for "j_head", which is the 'tag'(bone) that is located inside their head(duh ^^)
Search for all referenced text strings -> "j_head", there are two references to "j_head", here is the first one:
The addy it gets mov'd into is always 2 bytes diff, which means tag IDs are probably of the type short.
Here's the start of the same function:
1AFA780 is the first registered tag, 1AFA786 is j_head
1AFA786-1AFA780=6
6/2 = 3
so j_head probably has id 3.
All that is left now is getting tag positions, let's do a string search for GetTagPos.
"AimAssist_GetTagPos: Cannot find tag [%s] on entity %i." doesn't look very promising
"AimTarget_GetTagPos: Cannot find tag [%s] on entity %i." however does!
Fuck yeah! 
So we have GetTagPos now: 0x00433C00.
This is what the function looks like:
Hmm... We have the entity, and the tagid, and vOrigin is where the origin will be stored if the tag is found, so all that's left is TagPointer...
TagPointer is the 2nd argument of GetTagPos, arguments are pushed in reverse order which means EAX contains the tag pointer.
Now what do we know... 00457130 is GetTagPointer! it's return value is stored in EAX and pushed onto the stack for usage in GetTagPos!
GetTagPointer looks like this:
Only 1 argument, so the last thing pushed onto the stack is ClientNumber
And ESI appears to be the CEntity pointer, which is pushed last for GetTagPos(since it's the first argument)
Now we have everything we need to get the bone position!
To use the above code we'll have to change our CEntity struct a bit:
You'll probably notice we now have ClientNumber and iEntityType in there(which is checked against 1 in the 'AimTarget_GetTagPos' function to see if the Entity is a human)
Finally, use GetHeadPos like this:
I hope you guys have learned something from this, since I spent nearly 2 and a half hours on writing it >.>
Credits:
Hell_Demon - Writing all this crap up
Melodia - Wanted me to write this all up for her ^^
Have fun
~ Hell
Part 1 - some basic stuff you need to know about pointers to structures
Code:
#include <windows.h>
#include <iostream>
struct LOLSTRUCT
{
int a; //0x0
int b; //0x4
char c[24]; //0x8
float d; //0x20
}; //size = 0x24
LOLSTRUCT WTF[5]; //Array of LOLSTRUCTS
LOLSTRUCT *lolptr = &WTF[0]; //pointer to LOLSTRUCT array
int main()
{
printf("WTF %x \n\nLOLPTR: %x\n[0] %x\n[1] %x\n[2] %x\n", WTF, lolptr, &lolptr[0], &lolptr[1], &lolptr[2]);
Sleep(10000);
return 1;
}
We are interested in c, since that contains the name of the player.
but oh f*ck, we don't have the original struct... No worries tho, we have ollydbg skills =)
Code:
00401000 >/$ 68 C0334000 PUSH StructTe.004033C0 ; /<%x> = 4033C0 00401005 |. 68 9C334000 PUSH StructTe.0040339C ; |<%x> = 40339C 0040100A |. 68 78334000 PUSH OFFSET StructTe.WTF ; |<%x> = 403378 0040100F |. 68 78334000 PUSH OFFSET StructTe.WTF ; |<%x> = 403378 00401014 |. 68 78334000 PUSH OFFSET StructTe.WTF ; |<%x> = 403378 00401019 |. 68 04214000 PUSH OFFSET StructTe.??_C@_0CK@JGACCNEP@>; |format = "WTF %x LOLPTR: %x [0] %x [1] %x [2] %x " 0040101E |. FF15 9C204000 CALL DWORD PTR DS:[<&MSVCR90.printf>] ; \printf 00401024 |. 83C4 18 ADD ESP,18 00401027 |. 68 10270000 PUSH 2710 ; /Timeout = 10000. ms 0040102C |. FF15 00204000 CALL DWORD PTR DS:[<&KERNEL32.Sleep>] ; \Sleep 00401032 |. B8 01000000 MOV EAX,1 00401037 \. C3 RETN
A. Find the size of the struct, and go from there.
B. Rebuild the struct(structbuilder is a great tool for this), partially or full, and go from there.
A would look like:
Code:
DWORD *GetRetardPtr(int index)
{
return (DWORD*)0x403378 + (0x24*index);
}
B would look like:
Code:
struct LOLREBUILDED
{
char unknown000[8];
char name[24];
char unknown001[4];
};
LOLREBUILDED *ptr = (LOLREBUILDED*)0x403378;
ptr[index].name = "PEDOWALDO=D";
So remember: [u]If the size of your struct is correct you can access each part of the array by specifying it's index[u]
Part 2 - The real deal =)
I'll be using the AlterIW version of MW2 since that's probably what most of you will be using, it works the exact same for 1.1.195!
In order to get a bone's position you first need to have an entity to get the bone from.
So lets find the CEntity, the string we'll be looking for is "%s is dead", the offset is -0x27(which is a direct offset, so scroll up once to see it in it's instruction):
Code:
00409041 |. 68 84527100 PUSH iw4mp_AL.00715284 ; ASCII "%s is dead"
Scroll up once and you'll have this instruction selected:
Code:
00409018 |. 81C6 787A8F00 ADD ESI,iw4mp_AL.008F7A78
Code:
0040246F |. 69F6 04020000 IMUL ESI,ESI,204 00402475 |. 81C6 787A8F00 ADD ESI,iw4mp_AL.008F7A78
0x204... IS THE SIZE OF CEntity STRUCT!
Since we won't be doing a whole lot more with CEntity then getting bone positions, we can just declare it like this:
Code:
struct CEntity
{
char unknown[516]; //0x0
}; //size = 0x204
Code:
CEntity *pEntities = (CEntity*)0x008F7A78;
for(int i=0;i<18;i++)
{
CEntity cEnt = pEntities[i];
//do stuff with cEnt here.
}
Search for all referenced text strings -> "j_head", there are two references to "j_head", here is the first one:
Code:
0045E92A |. 68 EC336F00 PUSH iw4mp_AL.006F33EC ; push "j_head" onto stack 0045E92F |. 66:A3 86A7AF01 MOV WORD PTR DS:[1AFA786],AX ; move the id of the tag(j_head) into 1AFA786 0045E935 |. E8 9634FFFF CALL iw4mp_AL.00451DD0 ; Call registertag
Here's the start of the same function:
Code:
0045E8F0 /$ 68 7C4A6F00 PUSH iw4mp_AL.006F4A7C 0045E8F5 |. E8 D634FFFF CALL iw4mp_AL.00451DD0 0045E8FA |. 68 244C7200 PUSH iw4mp_AL.00724C24 ; ASCII "active" 0045E8FF |. 66:A3 80A7AF01 MOV WORD PTR DS:[1AFA780],AX
1AFA786-1AFA780=6
6/2 = 3
so j_head probably has id 3.
All that is left now is getting tag positions, let's do a string search for GetTagPos.
"AimAssist_GetTagPos: Cannot find tag [%s] on entity %i." doesn't look very promising
"AimTarget_GetTagPos: Cannot find tag [%s] on entity %i." however does!
Code:
005713A5 |. 68 00BC7000 PUSH iw4mp_AL.0070BC00 ; ASCII "AimTarget_GetTagPos: Cannot find tag [%s] on entity "

Code:
00571370 |. A1 D80B7F00 MOV EAX,DWORD PTR DS:[7F0BD8] 00571375 |. 8B8E DC000000 MOV ECX,DWORD PTR DS:[ESI+DC] 0057137B |. 53 PUSH EBX 0057137C |. 0FB71D 86A7AF0>MOVZX EBX,WORD PTR DS:[1AFA786] ; "j_head" pushed onto stack 00571383 |. 50 PUSH EAX 00571384 |. 51 PUSH ECX 00571385 |. E8 A65DEEFF CALL iw4mp_AL.00457130 0057138A |. 8D5424 14 LEA EDX,DWORD PTR SS:[ESP+14] 0057138E |. 52 PUSH EDX 0057138F |. 53 PUSH EBX 00571390 |. 50 PUSH EAX 00571391 |. 56 PUSH ESI 00571392 |. E8 6928ECFF CALL iw4mp_AL.00433C00 ; GetTagPos! 00571397 |. 83C4 18 ADD ESP,18 0057139A |. 85C0 TEST EAX,EAX ; ^ Because EAX is tested, and GetTagPos will return true if it finds the tag 0057139C |. 75 16 JNZ SHORT iw4mp_AL.005713B4 ; ^ if not, it will go to 'Cannot find tag' down below! 0057139E |. 53 PUSH EBX 0057139F |. E8 DC72EFFF CALL iw4mp_AL.00468680 005713A4 |. 50 PUSH EAX 005713A5 |. 68 00BC7000 PUSH iw4mp_AL.0070BC00 ; ASCII "AimTarget_GetTagPos: Cannot find tag [%s] on entity "
This is what the function looks like:
Code:
int GetTagPos( CEntity* pEntity, void *pTagPointer, int TagID, float *vOrigin )
TagPointer is the 2nd argument of GetTagPos, arguments are pushed in reverse order which means EAX contains the tag pointer.
Now what do we know... 00457130 is GetTagPointer! it's return value is stored in EAX and pushed onto the stack for usage in GetTagPos!
GetTagPointer looks like this:
Code:
void *GetTagPointer(int ClientNumber)
Code:
00571375 |. 8B8E DC000000 MOV ECX,DWORD PTR DS:[ESI+DC] ; ESI+0xDC, so 0xDC is the offset to the client number. 0057137B |. 53 PUSH EBX 0057137C |. 0FB71D 86A7AF0>MOVZX EBX,WORD PTR DS:[1AFA786] ; "j_head" pushed onto stack 00571383 |. 50 PUSH EAX 00571384 |. 51 PUSH ECX ;ClientNumber 00571385 |. E8 A65DEEFF CALL iw4mp_AL.00457130
Now we have everything we need to get the bone position!
Code:
#define TAG_HEAD 0x3
int (*pGetTagPos)( CEntity* pEntity, void *pTagPointer, int TagID, float *vOrigin ) = (int (__cdecl *)(CEntity*, void*, int, float*))0x00433C00;
void *(*pGetTagPointer)(int ClientNumber) = (void *(__cdecl *)(int))0x00457130;
void GetHeadPos(CEntity* pEnt, float *pOrigin)
{
D3DXVECTOR3 TagPosition;
void* pTagPointer = GetTagPointer(pEnt->ClientNumber);
pGetTagPos(pEnt, pTagPointer, TAG_HEAD, pOrigin);
}
Code:
struct CEntity
{
char Unknown000[220]; //0x0
int ClientNumber; //0xDC
int iEntityType; //0xE0
char Unknown001[288]; //0xE4
};
Finally, use GetHeadPos like this:
Code:
CEntity *pEntities = (CEntity*)0x008F7A78;
D3DXVECTOR3 HeadPos;
for(int i=0;i<18;i++)
{
if(pEntities[i].iEntityType != 1) continue; //if not human skip it
GetHeadPos(&pEntities[i], &HeadPos);
//Do whatever you want with head position here
}
Credits:
Hell_Demon - Writing all this crap up
Melodia - Wanted me to write this all up for her ^^
Have fun
~ Hell