{"id":21166414,"url":"https://github.com/danielkrupinski/cs2-anticheat","last_synced_at":"2026-03-27T02:54:35.469Z","repository":{"id":175069635,"uuid":"620495425","full_name":"danielkrupinski/cs2-anticheat","owner":"danielkrupinski","description":"Anticheat code found in Counter-Strike 2 binaries.","archived":false,"fork":false,"pushed_at":"2023-08-07T20:29:46.000Z","size":7,"stargazers_count":158,"open_issues_count":1,"forks_count":5,"subscribers_count":14,"default_branch":"master","last_synced_at":"2025-01-21T10:11:38.152Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":null,"has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/danielkrupinski.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2023-03-28T19:49:28.000Z","updated_at":"2025-01-14T05:25:09.000Z","dependencies_parsed_at":"2024-11-20T22:15:06.504Z","dependency_job_id":null,"html_url":"https://github.com/danielkrupinski/cs2-anticheat","commit_stats":null,"previous_names":["danielkrupinski/cs2-anticheat"],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/danielkrupinski%2Fcs2-anticheat","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/danielkrupinski%2Fcs2-anticheat/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/danielkrupinski%2Fcs2-anticheat/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/danielkrupinski%2Fcs2-anticheat/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/danielkrupinski","download_url":"https://codeload.github.com/danielkrupinski/cs2-anticheat/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":243615556,"owners_count":20319728,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-11-20T14:49:52.759Z","updated_at":"2025-12-29T10:09:24.519Z","avatar_url":"https://github.com/danielkrupinski.png","language":null,"funding_links":[],"categories":[],"sub_categories":[],"readme":"# Counter-Strike 2 Anticheat\n\nAnticheat measures found in the binaries of Counter-Strike 2.\nThe analysis is based on the 6 June 2023 update.\n\n## What's new\n\n### 6 June 2023\n\n- new functionality added to CUserMessage_Inventory_Response to collect VMT pointers of entities from entity system\n- new CUserMessageRequestDiagnostic / CUserMessage_Diagnostic_Response to detect debuggers\n\n## Detections which use protobufs\n\n| Request proto | Response proto | What is does |\n| --- | --- | --- |\n| CUserMessageRequestDllStatus | CUserMessage_DllStatus | Trusted Mode |\n| CUserMessageRequestUtilAction | CUserMessage_UtilMsg_Response | Checks ConVars for unathorized modifications |\n| CUserMessageRequestInventory | [CUserMessage_Inventory_Response](#cusermessage_inventory_response) | Checks VMT pointers of global interfaces, checks if read-only sections of game DLLs were modified, checks VMT pointers of game entities\n| CUserMessageRequestDiagnostic | [CUserMessage_Diagnostic_Response](#cusermessage_diagnostic_response) | Debugger detection |\n\n## CUserMessage_Inventory_Response\n\n### **VMT pointer check of global interfaces**\n\n```cpp\n// E8 ? ? ? ? 48 8D 8C 24 ? ? ? ? E8 ? ? ? ? F6 43 20 02 (relative jump) @ client.dll\nvoid collectInterfacesData(CUserMessage_Inventory_Response\u0026 protobuf);\n```\n\nThis function iterates over `g_pInterfaceGlobals` array (name from Source 1 engine) and for every interface fills `InventoryDetail` protobuf:\n\n```text\nmessage InventoryDetail {\n    optional int32 index = 1; // index in the g_pInterfaceGlobals array\n    optional int64 primary = 2; // *g_pInterfaceGlobals[index] (address of the interface)\n    optional int64 offset = 3; // **g_pInterfaceGlobals[index] (address of virtual method table of the interface)\n    optional int64 first = 4; // address of the first function in the VMT\n    optional int64 base = 5;\n    optional string name = 6;\n    optional string base_name = 7;\n    optional int32 base_detail = 8;\n    optional int32 base_time = 9;\n    optional int32 base_hash = 10; // interface name hash\n}\n(fields without a comment are unused)\n```\n\n\u003cdetails\u003e\n\u003csummary\u003eList of checked interfaces\u003c/summary\u003e\n\n```text\nVApplication001\nVEngineCvar007\nVStringTokenSystem001\nTestScriptMgr001\nVProcessUtils002\nVFileSystem017\nVAsyncFileSystem2_001\nResourceSystem013\nResourceManifestRegistry001\nResourceHandleUtils001\nSchemaSystem_001\nResourceCompilerSystem001\nVMaterialSystem2_001\nPostProcessingSystem_001\nInputSystemVersion001\nInputStackSystemVersion001\nRenderDeviceMgr001\nRenderUtils_001\nSoundSystem001\nSoundOpSystemEdit001\nSoundOpSystem001\nSteamAudio001\nVP4003\nLocalize_001\nVMediaFoundation001\nVAvi001\nVBik001\nMeshSystem001\nMeshUtils001\nRenderDevice003\nVRenderDeviceSetupV001\nRenderHardwareConfig002\nSceneSystem_002\nSceneUtils_001\nWorldRendererMgr001\nAssetSystem001\nAssetSystemTest001\nParticleSystemMgr003\nVScriptManager010\nPropertyEditorSystem_001\nMATCHFRAMEWORK_001\nSource2V8System001\nPanoramaUIEngine001\nPanoramaUIClient001\nPanoramaTextServices001\nToolFramework2_002\nPhysicsBuilderMgr001\nVisBuilder_001\nBakedLODBuilderMgr001\nHelpSystem_001\nToolSceneNodeFactory_001\nEconItemToolModel_001\nSchemaTestExternal_Two_001\nSchemaTestExternal_One_001\nAnimationSystem_001\nAnimationSystemUtils_001\nHammerMapLoader001\nMaterialUtils_001\nFontManager_001\nTextLayout_001\nAssetPreviewSystem_001\nAssetBrowserSystem_001\nAssetRenameSystem_001\nVConComm001\nMODEL_PROCESSING_SERVICES_INTERFACE_001\nNetworkSystemVersion001\nNetworkMessagesVersion001\nFlattenedSerializersVersion001\nSerializedEntitiesVersion001\nDemoUpconverterVersion001\nSource2Client002\nSource2ClientUI001\nSource2ClientPrediction001\nSource2Server001\nSource2Host001\nSource2GameClients001\nSource2GameEntities001\nEngineServiceMgr001\nHostStateMgr001\nNetworkService_001\nNetworkClientService_001\nNetworkP2PService_001\nNetworkServerService_001\nToolService_001\nRenderService_001\nStatsService_001\nVProfService_001\nInputService_001\nMapListService_001\nGameUIService_001\nSoundService_001\nBenchmarkService001\nKeyValueCache001\nGameResourceServiceClientV001\nGameResourceServiceServerV001\nSource2EngineToClient001\nSource2EngineToServer001\nSource2EngineToServerStringTable001\nSource2EngineToClientStringTable001\nVPhysics2_Interface_001\nVPhysics2_Handle_Interface_001\nModelDocUtils001\nAnimGraphEditorUtils001\nMODEL_PROCESSING_SCRIPT_INTERFACE_001\nEXPORTSYSTEM_INTERFACE_VERSION_001\nNavSystem001\n```\n\n\u003c/details\u003e\n\n### **Integrity of read-only sections of game DLLs**\n\nThis check is present in `client.dll` under the name \"ComputeInventory2\".\nGame DLLs register themselves by calling `Plat_RegisterModule(moduleHandle)` function from `tier0.dll`. The list of registered modules can be retrieved by calling `Plat_GetRegisteredModules()`.\n\nWhen game server sends [CUserMessageRequestInventory](https://github.com/SteamDatabase/GameTracking-CSGO/blob/49680faef0fbccdead5803e3d559e6a36372ac8f/Protobufs/usermessages.proto#L631-L635) protobuf message to the client, the client responds with [CUserMessage_Inventory_Response](https://github.com/SteamDatabase/GameTracking-CSGO/blob/49680faef0fbccdead5803e3d559e6a36372ac8f/Protobufs/usermessages.proto#L637-L662) message containing info about registered modules.\n\nFor every registered dll:\n\n```cpp\nstruct DllSectionsResult {\n    void* baseOfDll;\n    IMAGE_NT_HEADERS* ntHeaders;\n    void* buffer; // allocated with VirtualAlloc in processDllSections()\n    DWORD timestamp; // from IMAGE_FILE_HEADER::TimeDateStamp\n    DWORD pad; // padding\n    CSHA1 sha1; // sizeof(CSHA1) == 192, seems not to be computed currently\n    CRC32 readOnlySectionsHash;\n};\n\n// E8 ? ? ? ? 8B 8C 24 ? ? ? ? 0F B6 D8 (relative jump) @ client.dll\nbool processDllSections(DllSectionsResult\u0026 output, HMODULE dll);\n```\n\nprocessDllSections() allocates the buffer of size `IMAGE_OPTIONAL_HEADER::SizeOfImage` and does the following:\n\n- copies the headers to the buffer (`IMAGE_OPTIONAL_HEADER::SizeOfHeaders` bytes at dll base)\n- copies every unwritable section (`(IMAGE_SECTION_HEADER::Characteristics \u0026 IMAGE_SCN_MEM_WRITE) == 0`) to the buffer\n- iterates over base relocation table (`IMAGE_DIRECTORY_ENTRY_BASERELOC`) and undoes relocations in the buffer (only `IMAGE_REL_BASED_DIR64` relocations)\n- zeroes import address table (`IMAGE_DIRECTORY_ENTRY_IAT`) and export directory (`IMAGE_DIRECTORY_ENTRY_EXPORT`) in the buffer\n- computes CRC32 of read-only sections in the buffer\n\nprocessDllSections() is called by another function:\n\n```cpp\n// E8 ? ? ? ? 0F B6 C0 85 C0 74 7B (relative jump) @ client.dll\nbool processDll(\n    HMODULE dll,\n    CRC32\u0026 readOnlySectionsHash,\n    char* pdbPath,\n    DWORD\u0026 sizeOfImage,\n    DWORD\u0026 timestamp,\n    void** imageBase\n);\n```\n\nprocessDll() does the following:\n\n- calls processDllSections() and copies the fields of returned DllSectionsResult to the output parameters\n- copies PDB path from debug directory to `pdbPath` output buffer\n\nLater PDB file name is extracted from path and hashed.\n\n[InventoryDetail](https://github.com/SteamDatabase/GameTracking-CSGO/blob/49680faef0fbccdead5803e3d559e6a36372ac8f/Protobufs/usermessages.proto#L638-L649) protobuf is filled with the gathered info.\n\n```text\nmessage InventoryDetail {\n    optional int32 index = 1; // index in the array of registered modules\n    optional int64 primary = 2; // image base from IMAGE_OPTIONAL_HEADER\n    optional int64 offset = 3; // size of image\n    optional int64 first = 4;\n    optional int64 base = 5; // dll handle\n    optional string name = 6;\n    optional string base_name = 7;\n    optional int32 base_detail = 8; // crc32 of read-only sections\n    optional int32 base_time = 9; // timestamp from IMAGE_FILE_HEADER\n    optional int32 base_hash = 10; // PDB filename hash\n}\n(fields without a comment are unused)\n```\n\n## CUserMessage_Diagnostic_Response\n\nAt first `Plat_GetDebugMonitor()` from `tier0.dll` is called and the response is filled with data from DebugMonitor.\n\nThen a thread of id provided through a parameter is opened (`OpenThread(THREAD_ALL_ACCESS, 0, threadId)`) and suspended.\nThread context is then queried and the thread is resumed.\n\nFollowing values are copied from thread context into response:\n\n- Dr0\n- Dr1\n- Dr2\n- Dr3\n- DebugControl\n- Rsp\n- Rip\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdanielkrupinski%2Fcs2-anticheat","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fdanielkrupinski%2Fcs2-anticheat","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdanielkrupinski%2Fcs2-anticheat/lists"}