Thank you very much, it's my pleasure to help
So in crossfire WeaponMgr is just an array of pointers, each pointer is pointing to a Weapon struct, last time i checked this array contained
0x1000 weapons some of those pointers are
NULL so you have to check for that, each Weapon struct was of size
0x4BF4 bytes (it may have changed to
0x4DE0 bytes), you need the size of the struct if you want 28_3 bypass more on that later.
Because of that, the idea behind any Weapon hack is to edit the attributes of every weapon in this struct, but be careful because CF has 2 checks for weapon data, each time you equip, change a weapon or die it checks for the current weapon data and makes a client error 28_3 if it was different than normal, and you have to bypass it.
The way i change weapon data is as follows ( i updated the offsets but haven't tested any yet ), i use range, change speed and reload speed as an example ( recoil is a little difficult nowadays) in
CFNA:
Code:
#define WEAPON_MANAGER 0x1515364
#define WEAPON_MANAGER_COUNT 0x1000
#define WEAPON_RANGE 0xBF0 // string: Range
#define WEAPON_FASTSWAP 0x1260 // string: ChangeWeaponAnimRatio
#define WEAPON_FASTRELOAD 0x125C // string: ReloadAnimRatio
// Types
#define WEAPON_TYPE_PISTOL 0
#define WEAPON_TYPE_SHOTGUN 1
#define WEAPON_TYPE_SMG 2
#define WEAPON_TYPE_RIFLE 3
#define WEAPON_TYPE_SNIPER 4
#define WEAPON_TYPE_MG 5
#define WEAPON_TYPE_GRENADE 6
#define WEAPON_TYPE_KNIFE 7
#define WEAPON_TYPE_EXPLOSIVE 9
// Wait for cshell and Get cshell_base code HERE
// Weapon Manager array
DWORD weapon_manager = *(DWORD*)((DWORD)cshell_base + WEAPON_MANAGER);
// Loop over all the weapons
for (int i = 0; i < WEAPON_MANAGER_COUNT; i++)
{
// The current weapon pointer
DWORD weapon = *(DWORD*)(weapon_manager + i * 4); // Size of pointer is 4 bytes, thus i*4
// Skip the current weapon if the pointer is NULL
if (!weapon)
continue;
// You don't have to check for weapon type.
// Read the weapon type
SHORT wtype = *(SHORT*)(weapon + 0x2);
// Skip the weapon if it's a knife or a grenade
if(wtype == WEAPON_TYPE_KNIFE || wtype == WEAPON_TYPE_EXPLOSIVE)
continue;
// Increase the weapon range 100 times.
*(float*)(weapon + WEAPON_RANGE) *= 100.0f;
// Increase change speed 20%.
*(float*)(weapon + WEAPON_FASTSWAP) = 1.2;
// Increase reload speed 20%.
*(float*)(weapon + WEAPON_FASTRELOAD) = 1.2;
}
Important Tip: ( to avoid detection )
You need to hide your DLL module and erase the DOS and NT headers, not just DisableThreadLibraryCalls, if you don't know what that is you can just add it to your code (
DisableThreadLibraryCalls then erase header then hide module then CreateThread), if you don't do that the anti-cheat (XIGNCODE) should be able to
detect your DLL this may be the cause of error:
Code:
BOOL APIENTRY DllMain(HMODULE hModule, DWORD dwReason, LPVOID lpReserved)
{
if (dwReason == DLL_PROCESS_ATTACH)
{
DisableThreadLibraryCalls(hModule);
EraseHeaders(hModule);
HideModule(hModule);
CreateThread(NULL, NULL, (LPTHREAD_START_ROUTINE)MainThread, NULL, NULL, NULL);
}
return TRUE;
}
I will refer you to this thread by @
Flengo :
https://www.mpgh.net/forum/showthread.php?t=478124
Other Tips:
- I usually debug using Beep(500, 500) not MessageBox.
- If you really need to output text, make a simple logger just open a file as write and just fprintf.
- Put your DLL in a folder with no "hack" names, and change the DLL name for something random it may not be important but something to try, it may be the name of your DLL or the injector.
- If you play in simulation alone (single sim. match) i think the check for weapons will not run, so use sim. as a sandbox.
A hint about how to bypass client_error_28_3:
there is a function that uses WeaponMgr, it's GetWeaponByIndex, if you find it you can replace two calls for GetWeaponByIndex and bypass the checks by returning the unchanged (original) weapon data

, i haven't searched for it after the update yet.
As before, I hope that this information helps you
- - - Updated - - -
Read this before the top one.
Actually this is not the right way to find WeaponMgr.
Introduction
In CF when doing static analysis the game needs to get ReloadAnimRatioattribute from the loaded file, thus when we search for WeaponIndex we expect a value to be loaded from the rez files into our WeaponMgr, in other words the strings are just clues to find our addresses, you don't change the add, we need to get the WeaponMgr array, and some operation happens on it when loading weapon data, so we look arround and find our addresses.
Here are the steps:
1. Double click on the string "WeaponIndex" in the string window:
2. Choose jump to xref or click X:
3. Go to the reference:
4. You will find this string pushed:
5.Go about 30 instructions down you will find:
mov eax, dword_address
this address is WeaponMgr:
6. thus, WeaponMgr = address - base = 0x66715364 - 0x65200000 = 0x1515364
ReloadAnimRatio string is passed to the function that loads the attributes from the file, essentially CF says get me the value of ReloadAnimRatio from the file, then it loads it in the Weapon struct in a defined offset from the beginning of that struct (essentially writing struct data member), so we need to know this offset in order for us to change it later (after load).
Here are the steps:
Double click "ReloadAnimRatio" string:
Click jump to xref or press X:
Choose some one then double click:
1-> is our push string
2-> is our WeaponMgr (you can get it from here instead of WeaponIndex)
3-> is our ReloadAnimRation offset (0x125C), essentially a float offseted 0x125C bytes from the start of the weapon struct
0x125C is a plain offset ( not an address ), it's an offset from the beginning of the weapon struct
Now i think you can understand the top post.
I hope this helps
