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;
}
if we run that it will say something along the lines of
Originally Posted by
retardapp
WTF 403378
LOLPTR 403378
[0] 403378
[1] 40339C
[2] 4033C0
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 =)
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
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:
Code:
DWORD *GetRetardPtr(int index)
{
return (DWORD*)0x403378 + (0x24*index);
}
Then you could use assembly to access it's members.
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";
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):
Code:
00409041 |. 68 84527100 PUSH iw4mp_AL.00715284 ; ASCII "%s is dead"
0x00409041-0x27 = 0x0040901A
Scroll up once and you'll have this instruction selected:
Code:
00409018 |. 81C6 787A8F00 ADD ESI,iw4mp_AL.008F7A78
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).
Code:
0040246F |. 69F6 04020000 IMUL ESI,ESI,204
00402475 |. 81C6 787A8F00 ADD ESI,iw4mp_AL.008F7A78
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:
Code:
struct CEntity
{
char unknown[516]; //0x0
}; //size = 0x204
Now we can get an Entity by it's index like this:
Code:
CEntity *pEntities = (CEntity*)0x008F7A78;
for(int i=0;i<18;i++)
{
CEntity cEnt = pEntities[i];
//do stuff with cEnt here.
}
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:
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
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:
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
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!
Code:
005713A5 |. 68 00BC7000 PUSH iw4mp_AL.0070BC00 ; ASCII "AimTarget_GetTagPos: Cannot find tag [%s] on entity
"
Fuck yeah!
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
"
So we have GetTagPos now: 0x00433C00.
This is what the function looks like:
Code:
int GetTagPos( CEntity* pEntity, void *pTagPointer, int TagID, float *vOrigin )
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:
Code:
void *GetTagPointer(int ClientNumber)
Only 1 argument, so the last thing pushed onto the stack is 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
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!
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 = pGetTagPointer(pEnt->ClientNumber);
pGetTagPos(pEnt, pTagPointer, TAG_HEAD, pOrigin);
}
To use the above code we'll have to change our CEntity struct a bit:
Code:
struct CEntity
{
char Unknown000[220]; //0x0
int ClientNumber; //0xDC
int iEntityType; //0xE0
char Unknown001[288]; //0xE4
};
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:
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
}
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