2026Paused Development
CamelBot CS2
C++20 · DirectX 11 · ImGui · Windows kernel · Python · Flask · Next.js 16
Overview
A private CS2 research platform where the memory layer resolves at runtime and the game process never sees a write.
CamelBot CS2 is a private research platform for Counter-Strike 2, four components deep: a Windows kernel driver that reads memory and synthesizes input, an ImGui/DX11 loader that maps the driver and delivers the client without either touching disk, a usermode client whose memory layer resolves itself at runtime, and a licensing backend that fails closed by design. Security here is not a layer on top; it is the architecture. Sold, it would have been a case-farming bot. Built, it was the platform underneath one.
It came together at speed: first commit on June 7, 2026, a working walkbot on day two, v1.0.0 on day twelve, and an invite-only release that filled forty seats. It is paused today and still current, because an autoupdater keeps the memory layer tracking the game. Reviving it is a decision, not a rebuild.
What got built first, and why that order?
The first commit landed at 10:12 on June 7, 2026 and already carried the project's shape: a kernel-driver scaffold and dumped SDK headers. From there the order never wavered: driver, a basic menu, nav parsing with in-game rendering, the walkbot, then everything else. A working walkbot existed on day two; v1.0.0 shipped on day twelve with radar, spectator list and auto-queue. The learnings of the CS2 Walk Bot came along; the code started clean. The lineage is still visible: the Docker services are named walkbot-server and walkbot-website.
Why is the memory layer data, not code?
Because a game that updates weekly cannot wait on a recompiler. The client resolves memory at runtime: 30 signature needles, each byte-verified against the live module before use; a walk of CS2's schema system that lands on 8,040 fields; a ConVar-list walk; and an HTTP refresh of published offsets, cached per game build. The operating manual, in the owner's words: run the game again and everything works. A major update costs a signature file swap, two seconds of work.
Why read-only, with input from the kernel?
The design brief was to stay out of the game's view. Reads go through the driver; nothing writes into the game process, and the client's write helpers were deleted outright. Input never enters the process either. The driver invokes the keyboard and mouse class drivers' own service callbacks, so the game receives hardware-class events, and view movement converts to raw mouse counts with fractional accumulation. Pacing is humanized by arithmetic: a 125 Hz ceiling with randomized jitter, because perfect sub-millisecond bursts are their own signature. Across a whole session the process sees nothing forced: no writes, no injected code paths, no pulled view angles.
How does a session boot?
The loader logs in over certificate-pinned TLS, downloads the driver and the client, and checks both against an HMAC signature derived from the session. The driver is manual-mapped from memory, so it never touches disk, then unlinked from the kernel's driver and device lists. The client launches by process hollowing and inherits an anonymous pipe carrying its credentials; launched bare, it fails safe, sync self-disables, and the walkbot keeps walking on local nav. Inside the game it attaches through the driver: memory reads ride MmCopyVirtualMemory, input rides the class callbacks, and the game thread runs at 128 ticks per second, or 15 when the menu sits idle in power-save.
Why does a farming bot need a license backend?
Because invite-only still means untrusted operators, and the backend is where that distrust becomes protocol. Login is a PBKDF2-HMAC-SHA256 challenge/response at 600,000 iterations, so the password never crosses the wire. HWID binds atomically on first login. Binaries are signed per session and verified before execution. The clients pin the server certificate by SHA-256 fingerprint and refuse to connect on mismatch, and the account website throws on a weak SESSION_SECRET rather than running with a weak key. A Flask server and the Next.js account site co-write one SQLite database in WAL mode, with the crypto mirrored on both sides. Forty seats, filled by invitation. The backend then graduated: extracted, generalized and MIT-licensed as licensekit, tests and all.
What happens when a feature fights the kernel?
Jump-arc prediction was the right idea: the bot needs crouch-jumps onto ledges, timed mid-arc, and a predicted gate pressed space at the right moment. It worked, until the gate oscillated its key presses and took the kernel driver down with it. The revert landed sixteen minutes after the commit. The patchnotes carry the lesson and the plan: prediction returns with hysteresis or not at all. In this codebase, the kernel stays boring, and that rule outranks every feature.
How does the pathfinder learn a map?
A* over the nav mesh, funnel-smoothed, is the engine. On top of it ran an experiment: Q-learning weights that reshaped routes where the map fought the bot, trained by blocking the easy parts so the bot had to find the fast way through the hard ones. It shipped, it worked, and when geometry alone matched its results, the learning loop was cut in thirteen days. Keeping the win and deleting the machinery is the house style.
Where does the work go next?
Two subsystems already graduated: the licensing backend became licensekit and the memory-resolution engine became memschema, both MIT-licensed with their own test suites and CI. The kernel and server experience now points at absenceEngine, where the next build is the anti-cheat, client and server side. And the platform itself is one decision from being current again; tracking the game is the autoupdater's job, not the owner's.
Field notes
- ~56,000 authored lines across ~210 source files, excluding vendored code; the usermode client alone is 39,303 lines in 107 files.
- 278 commits, 244 of them inside the June 7 to 27, 2026 sprint; v1.0.0 shipped 12 days after the first commit.
- The kernel driver is 927 lines of C++: memory reads via MmCopyVirtualMemory, input via kbdclass and mouse-class service callbacks at DISPATCH_LEVEL.
- Runtime memory resolution: 30 byte-verified signature needles, a live schema walk resolving 8,040 fields, and an HTTP offset refresh cached per game build.
- The offline research layer for build 14175: 9 JSON files, ~8.7 MB, holding 16,597 schema fields, 6,649 vtable methods across 110 interfaces, 503 patterns and 273 game events.
- Login is a 600,000-iteration PBKDF2-HMAC-SHA256 challenge/response; the password never crosses the wire; HWID binds atomically on first login; binaries ship HMAC-signed per session.
- Smoke-aware visibility decodes CS2's Morton-encoded 32³ occupancy voxels; per-build C4 timing comes from value-scanning m_flC4Blow.
- Input pacing is a 125 Hz ceiling with randomized jitter, chosen so keystrokes never arrive in machine-perfect bursts.
- Around 500 points in an average deathmatch game, settings-dependent; owner's number, stated as memory.
Ran invite-only, forty seats, never public. The research continues in licensekit, memschema and absenceEngine.



Source
github.com/notdezzi/camelbot-cs2