onsdag den 14. december 2011

Game hacking: VTable hooking explanation and example

When hacking a game, you will sometimes need to hook some of the engine's functions. In case of the function that you want to hook is in a so called virtual method table(class with virtual functions), hooking the functions is very easy.
A virtual function is essentially a function in a class, that allows inherited classes to overwrite that function. On assembly level, this means that the compiler generates a table of pointers to functions, which are then called instead of calling the function directly. This allows inherited classes to easily overwrite the virtual function in the class instance. ex:

2521a783
2521a825
25220052
25228252
As you can see, it consists merely pointers, that point to the functions themselves.
(I am working on my paint skills):







With this information, we can actually easily replace the address that exists within the vmt with a pointer to our own function, while preserving the original function pointer for later use.
I've coded a little example code here just for the sake of consistency;

 
class VirtualTable
{
public:

virtual void VirtualFunction01( void );
};


void VirtualTable::VirtualFunction01( void )
{
cout << "VirtualFunction01 called" << endl;
}

//we use this to store and call the original function inside our custom one
typedef void ( __thiscall* VirtualFunction01_t )( void* thisptr );
VirtualFunction01_t g_org_VirtualFunction01;

//our custom function
void __fastcall hk_VirtualFunction01( void* thisptr, int edx )
{
cout << "Custom function called" << endl;

//call the original function
g_org_VirtualFunction01(thisptr);
}


int _tmain(int argc, _TCHAR* argv[])
{
VirtualTable* myTable = new VirtualTable();

//get the pointer to the actual virtual method table from our pointer to our class instance
void** base = *(void***)myTable;

DWORD oldProtection;
//one way to remove page protection(not the best but this is an example only)
VirtualProtect( &base[0], 4, PAGE_EXECUTE_READWRITE, &oldProtection );
//save the original function
g_org_VirtualFunction01 = (VirtualFunction01_t)base[0];
//overwrite
base[0] = &hk_VirtualFunction01;
//restore page protection
VirtualProtect( &base[0], 4, oldProtection, 0 );

//call the virtual function (now hooked) from our class instance
myTable->VirtualFunction01();

system("pause");

return 0;
}
Output:
Custom function called
VirtualFunction01 called

fredag den 25. november 2011

Reversing Valve's Source Engine functions: Getting Weapon Information without using Valve Source SDK

I have lately found myself in the need of being able to get the exact data about weapons that Valve uses in their own functions. Due to Valve's SDK being outdated, I decided to reverse the functions that the Source Engine uses to to get information from a file. In this post I will be using OllyDBG, aswell as IDA.
So, to find the place where they actually use the functions/the weapon informations, I simply looked in the SDK provided by Valve(which is now outdated) for a place to start, and found a function called FX_FireBullets. This function calls different functions when it needs to access information about weapons:
v12 = sub_10242390(a4);//call to WeaponIDToAlias
if ( !v12 )
{
    DevMsg("FX_FireBullets: weapon alias for ID %i not found\n", a4);
    return;
}
sub_10322A70(&v37, 0x80u, "weapon_%s", v12);//call to a function similar to sprintf_s
v13 = sub_10173BE0((int)&v37);//call to LookupWeaponInfoSlot

if ( v13 == (unsigned __int16)sub_10110B90() )
{
    DevMsg("FX_FireBullets: LookupWeaponInfoSlot failed for weapon %s\n", &v37);
    return;
}

v14 = (int)sub_10173B00(v13);//call to GetFileWeaponInfoFromHandle
 These functions are essentially all we need(weapon ID's excluded) to get info about weapons, and as you can see, they are very easy to find in your favorite debugger, or IDA, since there are a nice pair of strings that we can find. So if we fire up a debugger and search for one of those strings, we  will see the following disassembly(commented):
MOV EBX,DWORD PTR SS:[ESP+140]; "weapon's ID"
PUSH EBX
CALL client.57153130; "call to WeaponIDToAlias"
ADD ESP,4
TEST EAX,EAX
JNZ SHORT client.57141D9B
PUSH EBX
PUSH client.574AB288; "ASCII 'FX_FireBullets: weapon alias for ID %i not found'"
CALL DWORD PTR DS:[insert garbage here]; "call to DevMsg"
ADD ESP,8
POP EDI
POP ESI
POP EBP
POP EBX
ADD ESP,120
RETN
PUSH EAX; "the return value of WeaponIDToAlias(weapon's alias) is stored in EAX"
PUSH client.574AADF0; "ASCII 'weapon_%s'"
LEA ECX,DWORD PTR SS:[ESP+B8]
PUSH 80
PUSH ECX
CALL client.572351A0; "prints 'weapon_'+alias into a char array"
LEA EDX,DWORD PTR SS:[ESP+C0]; "char array"
PUSH EDX
CALL client.57083BF0; "call to LookupWeaponInfoSlot(returnvalue is an unsigned short)"
ADD ESP,14
MOVZX EDI,AX; "AX is used because of the short's small size"
CALL client.56FBBCF0; "OR AX, 0x0FFFF"
CMP DI,AX; "is the return value valid?"
JNZ SHORT client.57141DF1
LEA EAX,DWORD PTR SS:[ESP+B0]
PUSH EAX
PUSH client.574AB24C; "ASCII 'FX_FireBullets: LookupWeaponInfoSlot failed for weapon %s'"
CALL DWORD PTR DS:[insert garbage here]; "call to DevMsg"
ADD ESP,8
POP EDI
POP ESI
POP EBP
POP EBX
ADD ESP,120
RETN
PUSH EDI; "LookupWeaponInfoSlot return value"
CALL client.57083B10; "GetFileWeaponInfoFromHandle"
ADD ESP,4
Essentially we want to call those functions. Easiest way to get a pointer to them would be to make a signature scan for each individual call. This can be done very easily with a plugin for OllyDBG such as SigMaker, or a similar plugin for IDA. Here are the signatures I made with OllyDBG+SigMaker v.4('?' is a wildcard obviously):
weaponIDToAlias: client.dll "\x8B\x4C\x24\x04\x33\xC0\x39\x0C\xC5????\x74\x0B"
lookupWeaponInfoSlot: client.dll "\x8B\x44\x24\x04\x83\xEC\x08\x85\xC0\x74\x18"
getFileWeaponInfoFromHandle: client.dll "\x66\x8b\x44\x24\x04\x66\x3b\x05????\x73"

Now we have valid pointers to these functions. How do we use them, you ask? I pasted some code below, to show how you can use these functions.
const char* GetWeaponAlias( int weaponID )
{
 static void* weaponIDToAlias = 0;

 if ( !weaponIDToAlias )
 {
  weaponIDToAlias = Global::Get()->Memory()->FindPattern( GetModuleHandle( "client.dll" ), "\x8B\x4C\x24\x04\x33\xC0\x39\x0C\xC5????\x74\x0B" );
 }

 char* weaponAlias = 0;

 _asm
 {
  PUSH weaponID
  CALL weaponIDToAlias
  ADD ESP, 4
  MOV weaponAlias, EAX
 }

 return weaponAlias;
}

void* GetSDKWeaponInfo( void* weapon )
{
 static void* lookupWeaponInfoSlot = 0, getFileWeaponInfoFromHandle = 0;

 if ( !lookupWeaponInfoSlot )
 {
  lookupWeaponInfoSlot = Global::Get()->Memory()->FindPattern( GetModuleHandle( "client.dll" ), "\x8B\x44\x24\x04\x83\xEC\x08\x85\xC0\x74\x18" );
 }

 if ( !getFileWeaponInfoFromHandle )
 {
  getFileWeaponInfoFromHandle = Global::Get()->Memory()->FindPattern( GetModuleHandle( "client.dll" ), "\x66\x8b\x44\x24\x04\x66\x3b\x05????\x73" );
 }

 char weaponName[128];

 sprintf_s( weaponName, sizeof( weaponName ), "weapon_%s", GetWeaponAlias( GetWeaponID( weapon ) ) );

 void* weaponInfo = 0;
 _asm
 {
  LEA EDX, weaponName
  PUSH EDX
  CALL lookupWeaponInfoSlot
  ADD ESP, 4
  MOVZX EDI, AX
  PUSH EDI
  CALL getFileWeaponInfoFromHandle
  ADD ESP, 4
  MOV weaponInfo, EAX
 }

 return weaponInfo;
}

struct WeaponInfo_s
{
 int weaponID;
 int penetration;
 int damage;
 float maxRange;
 float rangeModifier;
 int bulletsPerShot;
 int ammoType;
 float penetrationRange;
 float penetrationPower;
};


WeaponInfo_s GetWeaponInfo( void* localPlayer, void* weapon )
{
 static void* getBulletTypeParameters = 0;

 if ( !getBulletTypeParameters )
 {
  getBulletTypeParameters = 0/*find it yourself if you need it*/;
 }

 void* sdkWeaponInfo = GetSDKWeaponInfo( weapon );

 WeaponInfo_s weaponInfo;

 weaponInfo.penetration = *reinterpret_cast<int*>( (size_t)sdkWeaponInfo + 0x884 );
 weaponInfo.damage = *reinterpret_cast<int*>( (size_t)sdkWeaponInfo + 0x888 );
 weaponInfo.maxRange = *reinterpret_cast<float*>( (size_t)sdkWeaponInfo + 0x88C );
 weaponInfo.rangeModifier = *reinterpret_cast<float*>( (size_t)sdkWeaponInfo + 0x890 );
 weaponInfo.bulletsPerShot = *reinterpret_cast<int*>( (size_t)sdkWeaponInfo + 0x894 );
 weaponInfo.ammoType = *reinterpret_cast<int*>( (size_t)sdkWeaponInfo + 0x6c0 );

 weaponInfo.weaponID = GetWeaponID( weapon );


 if ( weaponInfo.weaponID == WP_M4A1 )//The following was inside FX_Firebullets after these functions were called.
 {
  bool specialWeaponMode = *reinterpret_cast<bool*>( (size_t)weapon + 0x9d8 );

  if ( specialWeaponMode == 1 )
  {
   weaponInfo.rangeModifier = .95f;
  }
 }

if ( weaponInfo.weaponID == WP_GLOCK18 )
 {
  bool specialWeaponMode = *reinterpret_cast<bool*>( (size_t)weapon + 0x9d0 );

  if( specialWeaponMode )
  {
   int burstShotsRemaining = *reinterpret_cast<int*>( (size_t)weapon + 0x9dc );

   if ( burstShotsRemaining > 0 )
   {
    weaponInfo.damage = 18;
    weaponInfo.rangeModifier = .9f;
   }
  }
 }

 if ( weaponInfo.weaponID == WP_USP45 )
 {
  bool specialWeaponMode = *reinterpret_cast<bool*>( (size_t)weapon + 0x9d0 );

  if ( specialWeaponMode )
  {
   weaponInfo.damage = 30;
  }
 }

 _asm
 {
  LEA ECX, weaponInfo.penetrationRange
  PUSH ECX
  LEA EDX, weaponInfo.penetrationPower
  PUSH EDX
  PUSH weaponInfo.ammoType
  MOV ECX, localPlayer
  MOV ECX, DWORD PTR DS:[ECX]
  CALL getBulletTypeParameters
 }

 return weaponInfo;
}