During CS443 Software Security, my group explored a practical question: what can be learned about software security by observing and modifying a running program's memory? We chose Counter-Strike: Global Offensive as a lab target because games expose a useful mix of runtime state, graphics behavior, pointer-heavy data structures, and anti-cheat considerations.

The result was an external Python tool that attached to a local CS:GO process and modified the game's glow effect for entities in an offline/private-server environment. In common gaming language this resembles a wallhack, but the purpose of the project was not to build or release a cheat. It was a controlled software security exercise about process memory, reverse engineering, and runtime state manipulation.

This write-up intentionally omits full runnable source code, current game offsets, anti-cheat bypass steps, and replication instructions. The goal is to document the security concepts and engineering lessons without publishing a usable game cheat.
CS:GO offline lab screenshot showing green glow silhouettes visible through Mid Doors

Glow state visible through Mid Doors.

CS:GO offline lab screenshot showing red glow silhouettes visible through a wall near Middle

Entity outlines remain visible behind world geometry.

Demo outcome from the controlled lab: the modified glow state made player entities visible even when doors or walls were between the camera and the player. These frames were captured offline/private-server only.

Conceptual model

External process, internal game state

The tool was external: it did not inject a DLL or draw an overlay. It interacted with the game through process memory from a separate Python process.

Project Context

CS:GO is a multiplayer tactical shooter where the client maintains local information about players, teams, visibility, and rendering state. From a software security perspective, this makes it a useful environment for studying how runtime memory can become an attack surface. If a process exposes meaningful state in memory, another process with sufficient access may be able to read or modify it.

Our project focused on the glow effect. CS:GO uses a glow-related structure internally to determine whether certain entities should be visually highlighted and how that highlight should be rendered. By locating the relevant memory structures and writing controlled values to them, we could make player entities glow through the game engine's own rendering behavior.

That distinction mattered: this was not an overlay drawing boxes on top of the screen, and it was not an injected DLL running inside the game process. It was an external memory manipulation tool. The Python script used the pymem library to open the target process, read values from game memory, and write updated glow parameters back into memory.

Scope and Ethics

The project was performed in an offline/local/private-server setting for a university module. It was not designed for online cheating, was not distributed as a public tool, and did not explore anti-cheat bypass implementation. This boundary is important because the same technical ideas that make process memory research interesting can also be misused.

The careful framing matters here. The valuable part is not "I made a cheat." The valuable part is understanding how software stores runtime state, how another process can interact with that state, and what this implies for secure software design.

Technical Approach

The tool followed a classic external memory workflow:

  1. Attach to the local game process from Python.
  2. Resolve the base address of the relevant loaded module.
  3. Use previously discovered memory relationships to locate runtime structures.
  4. Iterate over player entities in memory.
  5. Read metadata such as team identity and glow index.
  6. Write updated color, alpha, and render flags into the glow structure.

The most interesting part was not the final glow effect itself, but the chain of reasoning needed to get there. We had to connect static-looking offsets to dynamic runtime objects, understand why module base addresses matter, and reason about pointer traversal across multiple structures.

Runtime flow

From process attach to game-rendered glow

This is the safe, high-level version of the workflow. Exact addresses and version-specific values are intentionally excluded.

Why module bases matter

Modern programs do not always load modules at the same address every time. Address Space Layout Randomization means a hardcoded absolute address is fragile. Instead, the script resolved the base address of the relevant game module at runtime and treated known offsets as relative locations from that base.

Why entity lists matter

The game maintains a list-like structure containing references to entities. In plain English, an entity is just something the game is tracking: a player, weapon, grenade, or other object in the world. Each entity has its own bundle of properties, such as health, team, position, weapon state, and rendering-related values. By walking the entity list, the tool could inspect multiple players without needing to know about them individually ahead of time.

Memory model

The entity list is a directory of game objects

A simplified mental model: the entity list is like a table of contents. Each entry points to a game object, and that object contains fields the game uses to update logic and rendering.

Why NetVars matter

Networked variables, often called NetVars in Source-engine reverse engineering discussions, describe fields that are synchronized to clients. For this project, they helped explain why a specific property could be found at a consistent relative position within an entity structure. Conceptually, this turns memory from a raw byte array into a map of meaningful fields.

We also used Valve's public Source SDK 2013 as a reference model for how Source-engine entities and rendering-related structures are represented in C++. It was not a direct map of CS:GO's live memory layout, but it helped us understand the vocabulary: entity handles, object definitions, glow colors, alpha values, and render-when-occluded style flags.

How the Tool Worked

At runtime, the Python script attached to the CS:GO process and located the game's client module. It then repeatedly read the pointer to the glow manager and walked through player entities. For each entity, it checked whether the pointer looked valid, read the team value, read the glow index, and wrote color values into the corresponding glow record.

In simplified terms, the flow looked like this:

attach to game process
resolve client module base

loop:
    read glow manager pointer
    for each entity slot:
        read entity pointer
        read team metadata
        read glow index
        choose a color based on team
        update glow parameters
        enable render-when-occluded flag

This is deliberately simplified. The actual project depended on version-specific memory relationships discovered during analysis. Publishing a complete, current offset table would make the write-up unnecessarily operational, so I am leaving that out.

Implementation screenshots

The actual script was written in Python using pymem. Rather than only showing pseudocode, these implementation screenshots show the practical work behind the proof of concept: studying the SDK-style glow structure, scanning for relevant runtime relationships, walking entity slots, and writing the selected glow state back into memory.

Screenshot of a C++ glow object manager and glow object definition structure used as a reference model
Glow structure reference

This was used to understand the shape of the glow system: each glow entry links back to an entity handle and stores color, alpha, and render flags. The SDK helped connect abstract memory values to meaningful fields.

Screenshot of Python code used to scan a client module byte sequence and resolve runtime relationships
Resolving runtime relationships

This part reads the loaded client module and searches for byte patterns so the script can derive important runtime relationships instead of relying only on fixed absolute addresses.

Screenshot of Python code walking entity slots and reading team and glow metadata
Walking the entity list

The loop iterates through player slots, checks that each entity pointer is valid, reads team metadata, and retrieves the glow index that maps a player entity to its corresponding glow record.

Screenshot of Python code branching on team metadata and writing glow color state
Writing visual state

After the script identifies the team and glow record, it updates color and visibility-related values. This is what made the game render the highlight effect through its own glow system.

The implementation combined reverse-engineering notes with a working external Python script. The key engineering challenge was keeping the memory reads stable enough to walk entities safely, identify the right glow record, and update only the rendering state needed for the lab demonstration.

External memory, not injection

This was an external tool because it ran as a separate Python process and interacted with CS:GO through process memory APIs exposed through pymem. There was no DLL injected into the game, and there was no custom overlay drawing on top of the window. The game rendered the glow effect because the tool modified the values that the game already used for that visual behavior.

Read and write behavior

The read side was used to understand game state: where entities were, which team they belonged to, and which glow record corresponded to them. The write side modified visual parameters such as color, transparency, and whether the glow should be rendered when an entity was occluded.

Anti-Cheat Context

CS:GO uses Valve Anti-Cheat, usually referred to as VAC. That made anti-cheat a useful part of the discussion, not because the project attempted to bypass it, but because it showed why privilege levels matter. VAC-style monitoring is commonly discussed as a user-mode model, which means it operates in the same broad Ring 3 space as normal applications. Stricter anti-cheat systems, such as Riot Vanguard, use kernel-mode drivers that run at Ring 0.

The practical difference is visibility and trust. A Ring 3 anti-cheat observes from the application layer, while a Ring 0 driver sits closer to the operating system and can monitor lower-level behavior. That can make detection stronger, but it also makes the privacy and security trade-off much more sensitive.

Privilege model

Where anti-cheat monitoring can sit

The x86 privilege model includes Rings 0 through 3, but modern desktop security discussions usually focus on Ring 3 user-mode code versus Ring 0 kernel-mode code.

This comparison reframes the project as a trust-boundary exercise. The external Python process operated from user mode, the game client also ran in user mode, and a user-mode anti-cheat has to reason about behavior from that same general layer. Kernel anti-cheat systems move closer to the operating system itself, which can increase detection coverage but also increases the sensitivity of the system being trusted.

What I Learned

Runtime memory is a security boundary

This project made it clear that runtime memory is not just an implementation detail. If sensitive state exists client-side and can be interpreted or modified by another process, it becomes part of the security model. That lesson applies beyond games: desktop apps, thick clients, and local agents can all expose meaningful state in memory.

Reverse engineering is mostly interpretation

Finding an address is only the beginning. The harder part is understanding what that address represents, how it relates to nearby structures, and whether it remains stable across runs or updates. The project forced me to think about memory as a set of relationships rather than isolated values.

Version drift is real

A memory relationship that works for one game build may break after an update. This is a useful reminder that tools built on internal implementation details are brittle. From a defensive perspective, this brittleness is also one reason software vendors can raise the cost of abuse by changing layouts, adding validation, and reducing trust in client-side state.

Ethics changes how technical work should be shared

I think the project is worth discussing, but not in a copy-paste tutorial format. The responsible version is a conceptual write-up: explain the memory model, the engineering approach, the security implications, and the lessons learned, while withholding details that would help someone reproduce the cheat against a live game.

Final Reflection

Looking back, this CS443 project was a useful introduction to process memory manipulation and reverse engineering. It connected software security theory to something visible: changing memory values affected what the game rendered on screen.

The main takeaway was not simply that a wallhack-style proof of concept can be built. The more important takeaway was that client-side software often contains assumptions about who can read or modify local state. Studying those assumptions in a controlled lab made me more aware of how runtime memory, process access, and trust boundaries shape real-world security.