Fighting Cheaters in Call of Duty 4
Cheating in multiplayer games has always been a thing that triggered me the most while playing them. During the time I was managing a game server network used by hundreds of players every day I learned some neat tricks to fight the cheaters back.
Ethical Disclaimer
This post contains information that can be used to cheat (and avoid being caught red-handed) in Call of Duty 4 (CoD4) multiplayer servers that require latest official version of the game. This also applies to servers with the default anticheat software enabled at both ends of communication. Without a need to modify any of the game files.
I decided to finally publish this post because the game it’s mostly related to has been released in 2007 and the latest official patch (version 1.7) has been released in June 2008, 14 years ago. All the information presented here can be found on the internet anyway.
Intro
Most new games nowadays don’t have option to let people host and manage (and often modify) their own dedicated servers. However, a decade ago the situation was a complete opposite. Before the age of automated matchmaking, you had to manually find and join a server to play. Usually there were a lot of active servers but not all of them were given a top-notch care from the administrators in all matters.
Some issues were related to keeping the chat civilized. Others revolved around abusing some gameplay mechanisms. However, the worst problems were always related to detecting and removing people who cheated. It’s a complex topic thus I see why it’s usually ignored or delegated to the game’s developers and calling it a day.
All the information in this post is based on my knowledge gathered over the years of hosting various game servers between 2009 and 2016. The most interesting years were definitely the final ones under the Morning Star Gaming1 brand, where we had access to high-end hardware that allowed us to deliver lag-free experience for thousands of unique players over the years.
In order to visualize some of the topics better, I fetched old backups so that I could use real-world data. The logs I will use come from the CoD4 servers that were used for competitive gameplay between 2014-07-01 and 2015-07-01.
PunkBuster
PunkBuster is anti-cheat software suite created by Even Balance back in 2000. It was first used in Return to Castle Wolfenstein and since then in over 30 other titles including games from Battlefield, Call of Duty and Far Cry series. CoD4 has an optional (as in possible to be forced by each game server to let people in) PunkBuster integration.
PunkBuster works in client-server architecture to allow real-time memory and file scans. It also allows to stream globally issued bans from the master servers2 to the participating servers.
PunkBuster was rarely updated, even at the peak of its popularity, therefore only the most obvious and popular cheats were possible to detect this way. The fact that PunkBuster worked in userland didn’t help in case of more sophisticated hacks.
But wait, there’s more!
Built-in Cheat Detection
There’s an undisclosed database of known programs used for cheating that PunkBuster looks for. They can target either the game or the anti-cheat itself.
You can find some examples from my logs (sorry for the MM.DD.YYYY date format but all excerpts are copied as-is) below:
[08.15.2014 17:59:58] VIOLATION (GAMEHACK) #89182: sSs*R|neon (slot #1) Violation (GAMEHACK) #89182 [6fcfb7e796d093e7b781a94e81f10015(VALID) 89.12.208.250:28960]
[08.28.2014 12:18:15] VIOLATION (GAMEHACK) #89182: DK'NEON (slot #8) Violation (GAMEHACK) #89182 [1c8d0554b041a5bf4b81ba7657dcc329(VALID) 89.12.241.2:28960]
[09.02.2014 17:31:45] VIOLATION (PB HACK) #139015: |G4E|SaschaDE (slot #1) Violation (PB HACK) #139015 [24cede36831db30128d586a3387319d8(VALID) 188.195.155.156:28960]
First two entries show a generic GAMEHACK
entries, the last one indicates a cheat that interfered with the PunkBuster (PB HACK
).
All violation log lines are pretty much similar.
They consist of a timestamp, violation type, nick and game slot of a player in question, violation details (they are the same as violation type in case of these built-in detections), GUID (a unique identifier of a player derived from the game’s CD-KEY) and client’s IP.
Screenshots
PunkBuster offers a somewhat limited but still sometimes useful functionality of taking screenshots of player’s screen and sending it back to the server. It can be used, but not limited, to spot (wallhacks)[https://en.wikipedia.org/wiki/Cheating_in_online_games#World-hacking] (a class of cheats designed to reveal enemies in some ways, not necessarily making the walls invisible ;). Unfortunately, as with other aspects of PunkBuster, this feature was designed when commonly used screen resolutions were much smaller. As a result, we end up with a small fraction of the screen available or a decimated picture that convey no meaning.
Example of a screenshot containing a wallhack. |
There were also hacks to disable some or all of the screenshot functionalities. Beside the hacks there were also bugs in game, DirectX or PunkBuster itself that prevented taking proper screenshots.
Black screen. Either a bug in game, PunkBuster or a deliberate doing. |
Screenshot of my foobar2000 instead of the game. |
Example of a screenshot blocker that hides player info. |
Examples of other cheats found with screenshots:
Aimhack. |
Crosshair hack, probably just an all-in-one hack because the line on the right is most likely a part of a bounding box around enemy. |
Custom textures are indicator of modified game files. There’s also an ESP (Extra Sensory Perception) hack that shows info about enemies other than just their position. |
Hack config. |
Radar hack. Positions of all enemies are always shown on the radar, not only when scanned by in-game UAV or when shooting without a silencer. Achievable with setting read-only g_CompassShowEnemies to 1 . |
An overlay of a Mombot hack. |
CVAR Violations
Now we can move on to the less known features of PunkBuster. As an administrator of a server you can also define a list of game variables (CVARs) to scan periodically. They can be set in couple different modes: value range (both in and out) as well as string inclusion/exclusion.
Here’s a general definition from the docs and an excerpt from the config file we used back in the days (;
denotes a comment in PunkBuster config syntax):
;PB_SV_Cvar [Cvar_name] [IN/OUT/INCLUDE/EXCLUDE] [Param1] [optional_Param2]
;melee checks
pb_sv_cvar aim_automelee_lerp IN 40
pb_sv_cvar aim_automelee_region_height IN 240
pb_sv_cvar aim_automelee_region_width IN 320
pb_sv_cvar aim_automelee_enabled IN 1
pb_sv_cvar aim_automelee_range IN 128
pb_sv_cvar aim_automelee_debug IN 0
pb_sv_cvar player_meleerange IN 64
;disabled anti-aliasing (more on that later!)
pb_sv_cvar r_aasamples IN 1
;misc
pb_sv_cvar developer IN 0
pb_sv_cvar sv_cheats IN 0
pb_sv_cvar g_compassshowenemies IN 0
pb_sv_cvar r_fullbright IN 0
pb_sv_cvar com_maxfps IN 40 333
This method has worked great for some time. It allowed us to find cheaters that used cvar unlocking hacks – programs that basically removed the read-only status from some variables which values, when changed, could give someone an unfair advantage.
And guess what?
CoD4 has a built-in method of changing the read-only variables!
It’s possible through a side-effect of a quite complicated scripting function for loading translations from files to variables.
Namely, SelectStringTableEntryInDvar foo foo sv_cheats 1
command would fail to find a foo
in foo
so it would just forcibly set sv_cheats
to 1
despite the variable being set as read-only.
This bug hasn’t been officially patched.
And as far as I can tell, it even made to the next installment of the game!
Below you can find some examples of variables violations and stats from the period covered by the logs:
[10.16.2014 20:15:04] VIOLATION (CVAR) #9001: testing stuff (slot #1) Cvar sv_cheats = 1 [db4a323740c24b9a31915c3e636e8fdb(VALID) 217.99.224.187:28960]
[07.15.2014 18:48:58] VIOLATION (CVAR) #9001: mM#neon (slot #2) Cvar developer = 1 [c108d2381c62d8e0aadc17ffa941bfc2(VALID) 89.13.148.61:28960]
[09.22.2014 18:41:58] VIOLATION (CVAR) #9001: Dougmau5 (slot #7) Cvar r_fullbright = 1 [9ed7ac09b6b429a2f04357d3419a658d(VALID) 81.131.93.97:28960]
[09.02.2014 15:36:00] VIOLATION (CVAR) #9001: Fire (slot #3) Cvar aim_automelee_range = 255 [babaa765e4257430197cce9b0c8a008a(VALID) 31.183.36.255:55195]
[09.18.2014 13:36:49] VIOLATION (CVAR) #9001: spec (slot #1) Cvar cl_yawspeed = 22500 [de20c946db20b3f64758ee24b48c4c23(VALID) 89.14.116.144:28960]
[08.12.2014 12:57:59] VIOLATION (CVAR) #9001: LS|Wojtix. (slot #8) Cvar j_lodScaleRigid = 4 [cf108bcb2a4ca15408e5b78e79d5a62e(VALID) 89.230.231.36:28960]
[09.27.2014 21:34:08] VIOLATION (CVAR) #9001: Mokova (slot #5) Cvar fx_enable = 0 [01174c7e83fd6cc9a52db198899ac0c9(VALID) 96.22.12.28:28960]
[08.27.2014 13:02:21] VIOLATION (CVAR) #9001: sSs*|Asqwe:3 (slot #1) Cvar cg_hudDamageIconHeight = 0 [ffa81a6a32c10f23b68a6e65f87512d8(VALID) 86.111.104.134:28960]
[08.29.2014 18:57:15] VIOLATION (CVAR) #9001: RAGE HorNed (slot #5) Cvar phys_bulletSpinScale = -1 [2bfe80aa39ec04249a2897370f7301c5(VALID) 95.52.34.145:28960]
[09.16.2014 00:22:20] VIOLATION (CVAR) #9001: ^3[^0ms^3]^0nor (slot #1) Cvar cl_forceavidemo = 1 [9126e989c56bfbc92307082716646ab8(VALID) 86.16.229.77:28960]
[08.21.2014 22:00:25] VIOLATION (CVAR) #9001: =sQ=TEST CFG GG (slot #7) Cvar cg_nomip = 128 [4e7dbd87cf2bb93d7359334939aa463d(VALID) 193.106.161.132:26456]
[08.12.2014 15:15:13] VIOLATION (CVAR) #9001: neon (slot #8) Cvar phys_bulletUpBias = 0 [6fcfb7e796d093e7b781a94e81f10015(VALID) 89.12.93.110:28960]
[07.08.2014 13:02:06] VIOLATION (CVAR) #9001: M][K*knifer (slot #5) Cvar cl_punkbuster = 0 [89377397ef9633e2b30c1f0f7ad02e3d(VALID) 81.241.85.246:28960]
- Total CVAR violations: 994
- Unique CVAR violators: 296
Top offenders:
62 8b2fcad7b931f60ebfff1631ab97562a
29 6fcfb7e796d093e7b781a94e81f10015
26 5f58a8d513865b56df363d380d72f71d
21 ffa81a6a32c10f23b68a6e65f87512d8
20 3d689aa282bf6ade29d809f4e8ec7c1b
r_aasamples
equal to 1
means that the anti-aliasing feature in the game’s renderer is disabled.
It became a problem in newer Windows versions when it came to screenshots taken by PunkBuster.
All of them came back pink when anti-aliasing was enabled so we decided to disallow it altogether.
Pink screen caused by enabled anti-aliasing on Windows 8. |
There’s also an on-demand version of this functionality that allows you to just peek at the values without issuing kicks or bans. It was useful early on, to collect as much data about cheaters as possible without alerting them.
[01.03.2015 18:29:23] Malfunctioning PB Client (slot 12) - Ignoring Cvar Queries
[07.29.2014 10:53:58] 5 Cvar Queries Sent
[07.29.2014 10:53:58] [From #1 b7e8(VALID) {RR}andr] ai_meleerange = "64"
[07.29.2014 10:53:58] [From #7 143b(VALID) =sQ= Savie] ai_meleerange = "99999999"
[07.29.2014 10:53:58] [From #3 54b4(VALID) *LEU*Romeo] ai_meleerange = "64"
[07.29.2014 10:53:59] [From #4 2c81(VALID) [VS-UK]psychOl] ai_meleerange = "99999999"
[07.29.2014 10:53:59] [From #2 c664(VALID) =sQ= SKiLLzZ] ai_meleerange = "64"
Do you remember when I said that this method worked great for some time?
It was fine until cheaters learned that they could modify strings in game’s executable to use different names for the variables we were enforcing values for.
For example, the variable aim_automelee_ranga
should not exist at all because it’s real name is supposed to be aim_automelee_range
:
[07.06.2014 19:26:01] VIOLATION (CVAR) #9001: god#D1veR (slot #1) Cvar aim_automelee_ranga = 155 [f289fb8c7131e39cafc4fe6c8552d040(VALID) 81.4.194.85:28960]
But how did we even realize this was the case?
Bind Scans
Lo and behold, here come the bind scans. This little functionality allowed us to poll players’ configs for known game actions assigned (or “bound” in gamers jargon) to keys. On some occasion they also revealed illegal macros used to perform actions in sequence that were impossible otherwise.
Cheaters sometimes assigned extra stuff to the normal movement or attack keys. That way we could reveal some of the custom variable names that their modified game clients actually used.
I’m sorry but I couldn’t find full logs, only partial messages from admins communication because bind scans were performed on-demand and weren’t logged to the main log file. Here’s what I managed to salvage for the sake of this post:
[07.01.2015 18:04:14] [From #4 9ee6(VALID) rOSTA|FFF] [Q = weapnext; wait 2; weapprev; wait 5; +melee; com_maxfps 80; -melee;wait 33; com_maxfps 333; wait 18; +activate; wait 2; -activate][1 key binding match found]
[07.01.2015 18:04:53] [From #4 9ee6(VALID) rOSTA|FFF] [3 = zzz_popokills_mamba 129][W = +forward;z_loh 0;zz_kokokillsmamba 0;zz_tutu 0;zz_lalala 0;sv_clientsidebullets 0;r_znear 30;r_znear_depthhack 4;bg_bobmax 0;fx_drawclouds 0][2 key binding matches found]
[07.24.2014 12:24:29] [From #10 426f(VALID) dUb crizis] [E = +melee][KEY_F3 = aim_automelee_ranga 165][KEY_F4 = aim_automelee_ranga 0][3 key binding matches found]
[07.07.2014 16:10:36] [From #7 2543(VALID) BT#XtronS] [F = +activate][V = weapnext; wait 2; weapprev; wait 5; +melee; com_maxfps 125; -melee; wait 40; com_maxfps 333; wait 18; +activate; wait 2; -activate][2 key binding matches found]
Z = "SelectStringTableEntryInDvar FPS FPS r_fullbright 1"
I can assure that neither zzz_popokills_mamba
nor zz_lalala
or even zz_kokokillsmamba
are built-in variables ;)
They are, however, matching the lengths of their original names because they were patched directly in game’s binary without changing any of the program’s logic.
So how do we deal with those modified clients?
MD5Tool
The last tool in our arsenal is a mechanism that allows to run periodic checksum scans on files in player’s game directory. Unfortunately, the limits imposed on the frequency and size of scans were the major PITA. You can check at most 2048 bytes in a single scan, every 30 seconds. Just to scan a relatively small – 3330048 bytes – game executable you’d need a player to stay on the server for 12 hours3. There are also other game files that could use some scanning, for example asset packs, to eliminate alteration of textures (that could lead, for instance, to walls or grass transparency).
In order to overcome the limitations I first generated a sequence of the checksums on a vanilla game. Then I modified the game executable to alter the variable names I deemed the most important for us and generated another set of checksums. Final lists were then diffed and I came up with a final list of 15 offsets that could be scanned in 7 minutes:
;generated by andr (2014-10-10 18:41)
;iw3mp.exe v1.6
pb_sv_md5toolempty
pb_sv_md5toolfreq 30
pb_sv_md5tool a "" v iw3mp.exe SZ3330048 AT2738176 LEN2048 928C325DC37E804F3C77B6D2E46FF294
pb_sv_md5tool a "" v iw3mp.exe SZ3330048 AT2926592 LEN2048 B2C3332CBA37E374C867883AB179F68D
pb_sv_md5tool a "" v iw3mp.exe SZ3330048 AT2914304 LEN2048 4E833D47539C77F6E6597467745DE83E
pb_sv_md5tool a "" v iw3mp.exe SZ3330048 AT2959360 LEN2048 7B5555B4E8ECE6D9ECAFD4D93CF88B10
pb_sv_md5tool a "" v iw3mp.exe SZ3330048 AT2934784 LEN2048 70CD05CA9BE840A07B3E8024A2CBC5B8
pb_sv_md5tool a "" v iw3mp.exe SZ3330048 AT2947072 LEN2048 FC8D1E1A568BE2C639E03BDC7C179061
pb_sv_md5tool a "" v iw3mp.exe SZ3330048 AT2951168 LEN2048 F8D39DF25CB8ECF3752162A6BCCD6F97
pb_sv_md5tool a "" v iw3mp.exe SZ3330048 AT2953216 LEN2048 BE4B6F1A3E6CF5F395D7C703BA373D29
pb_sv_md5tool a "" v iw3mp.exe SZ3330048 AT2992128 LEN2048 CD114E133DB909032914F846A0D041BA
pb_sv_md5tool a "" v iw3mp.exe SZ3330048 AT3020800 LEN2048 ED047C9BFD0EE6068B3FA48E8BB17611
pb_sv_md5tool a "" v iw3mp.exe SZ3330048 AT3026944 LEN2048 5063B2EE918E3DF1CE90CF51830A43EF
pb_sv_md5tool a "" v iw3mp.exe SZ3330048 AT3061760 LEN2048 FD246F355BCD68D85126833A1D93F670
pb_sv_md5tool a "" v iw3mp.exe SZ3330048 AT3115008 LEN2048 90160CB306D7F13D2C8F08F272ABD2A9
pb_sv_md5tool a "" v iw3mp.exe SZ3330048 AT3168256 LEN2048 34D42E2241D52143CE8707CA9A95E0DE
pb_sv_md5tool a "" v iw3mp.exe SZ3330048 AT3162112 LEN2048 FEE4B1D001F2DFEA1C3A0C7F394D0E26
An excerpt from logs that indicates MD5Tool violations:
[10.11.2014 16:41:47] VIOLATION (MD5TOOL) #9002: cg_lan 1 (slot #1) MD5Tool Mismatch: iw3mp.exe (len=2048) [2b9ab1fbc7cdc8ee11a2a26249b4413e(VALID) 195.242.125.179:28960]
[10.11.2014 20:50:00] VIOLATION (MD5TOOL) #9002: BASEDGOD (slot #4) MD5Tool Mismatch: iw3mp.exe (len=2048) [6cfb244345650db8b2837bf151ff8742(VALID) 107.209.190.143:28965]
[07.05.2014 15:18:21] VIOLATION (MD5TOOL) #9002: [ms] (slot #1) MD5Tool Mismatch: main/iw_13.iwd (len=2048) [af3d2e82f247c3be99a17f9ffc873c30(VALID) 83.85.114.231:28960]
[07.05.2014 15:21:24] VIOLATION (MD5TOOL) #9002: [ms] (slot #1) MD5Tool Mismatch: main/iw_05.iwd (len=2048) [af3d2e82f247c3be99a17f9ffc873c30(VALID) 83.85.114.231:28960]
- Total MD5Tool violations: 294
- Unique MD5Tool violators: 49
Top offenders:
26 1f97d04c79e3cc150232cb8d196843bb
22 e4afe5a995f9701a6e29dace7d109348
15 68a1304d02b53d6a62003dc7337639c4
15 26956b1198b880bd6001eece83dea4a0
14 9126e989c56bfbc92307082716646ab8
Unfortunately, as with countless other anti-cheat measures, this one is particuarly easy to overcome.
The message that is shown in logs was also present in a message on the automatically issued “kick” when the violation was detected.
As you probably figured out by now, running the game from foo.exe
, while keeping unmodified iw3mp.exe
at the same time, would let you fly under the radar.
Closing Words
It was a fun couple of years I spent doing work that’s barely visible but much needed by the community. However, I still don’t get it why would anyone want to cheat in a game. What’s to gain?
Dealing with those people, especially after they’ve been caught red-handed, was draining a lot of energy. Constant “ban appeals”4 and comebacks with new game copies and names, just to do same shit over and over again…
-
Morning Star Gaming, MSG for short, was a community that evolved from Knives Out CoD4 clan, that originally revolved around a specific game mode that allowed players to use only melee weapons to kill their enemies. At the peak of its active years MSG hosted dozens of game servers in multiple games including ones from Call of Duty series, Minecraft and Counter Strike as well as forums, voice and chat services. MSG disbanded somewhere in 2017 or 2018. ↩︎
-
Master servers are usually some special servers that coordinate some operation. In this case they are responsible for relaying global ban info and update information to game servers. In games, master servers are often used to gather heartbeats from all active game servers in order to later display a complete server list to players. ↩︎
-
Assuming this method would work continuously between map changes in game, which I’m not convinced it would. Alternatively, you’d have to set suuuuuch loooong rounds… ↩︎
-
I’ve always advised against giving second chances to proven cheaters. Not that it helped a lot… They came back anyway and cheated again, with no remorse or any resocialization. For some reason bans (and later unbans) are more likely to work as expected in case of non-cheating violations, like a use of improper language in chat or nicknames. It has probably something to do with the irreversibly broken minds that do not use logic to make decisions. Sometimes I wonder whether people who cheat in video games are also people who steal, extort or do other illegal shit because they are completely deranged. I guess I will have to add psychology studies to my TODO list to find out. ↩︎