Skip to content

Pointer chain checks - #311

Open
Krilliac wants to merge 1 commit into
mangoszero:masterfrom
Krilliac:claude/add-custom-pointer-check-XcR4N
Open

Pointer chain checks#311
Krilliac wants to merge 1 commit into
mangoszero:masterfrom
Krilliac:claude/add-custom-pointer-check-XcR4N

Conversation

@Krilliac

@Krilliac Krilliac commented May 5, 2026

Copy link
Copy Markdown
Contributor

This change is Reviewable

@Krilliac
Krilliac marked this pull request as draft May 5, 2026 16:13
@codacy-production

codacy-production Bot commented May 5, 2026

Copy link
Copy Markdown

Up to standards ✅

🟢 Issues 0 issues

Results:
0 new issues

View in Codacy

🟢 Metrics 0 duplication

Metric Results
Duplication 0

View in Codacy

NEW Get contextual insights on your PRs based on Codacy's metrics, along with PR and Jira context, without leaving GitHub. Enable AI reviewer
TIP This summary will be updated as you push new changes.

@AppVeyorBot

Copy link
Copy Markdown

@AppVeyorBot

Copy link
Copy Markdown

@AppVeyorBot

Copy link
Copy Markdown

@AppVeyorBot

Copy link
Copy Markdown

@billy1arm
billy1arm marked this pull request as ready for review June 2, 2026 04:42
Copilot AI review requested due to automatic review settings June 2, 2026 04:42

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR appears intended to introduce/support “pointer chain checks” for Warden, but the code changes themselves don’t implement a new Warden check type; instead it adds a contrib SQL seed/example file describing a new check type (244 / 0xF4) plus a small header-include change.

Changes:

  • Add a new contrib SQL file (pointer_chain_examples.sql) with example warden table rows for a proposed POINTER_CHAIN_CHECK type.
  • Add an extra standard library include in WardenWin.h.
  • No functional logic changes are evident in WardenWin.cpp / WardenCheckMgr.cpp beyond the diff re-rendering.

Reviewed changes

Copilot reviewed 2 out of 4 changed files in this pull request and generated 3 comments.

File Description
src/game/Warden/WardenWin.h Adds a standard header include (currently unused as written).
src/game/Warden/WardenWin.cpp No functional change observed in the shown diff.
src/game/Warden/WardenCheckMgr.cpp No functional change observed in the shown diff; review focused on DB-driven type handling.
contrib/warden/pointer_chain_examples.sql Adds example SQL seeds/documentation for a new proposed Warden check type.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines 29 to 31
#include "Warden.h"
#include <vector>

Comment on lines +1 to +5
-- ============================================================================
-- POINTER_CHAIN_CHECK (type 244 / 0xF4) example seeds for the `warden` table.
--
-- Wire format on the client side is identical to MEM_CHECK (243 / 0xF3); the
-- server walks a multi-hop pointer dereference chain across consecutive Warden
Comment on lines +112 to +123
uint16 id = fields[0].GetUInt16();
uint16 build = fields[1].GetUInt16();
uint8 checkType = fields[2].GetUInt8();
std::string data = fields[3].GetString();
std::string checkResult = fields[4].GetString();
uint32 address = fields[5].GetUInt32();
uint8 length = fields[6].GetUInt8();
std::string str = fields[7].GetString();
std::string comment = fields[8].GetString();

WardenCheck* wardenCheck = new WardenCheck();
wardenCheck->Type = checkType;
PR mangoszero#311's branch had diverged from mangoszero/master with an unrelated history
that could no longer be merged. Re-base the pointer-chain-check feature (Warden
CUSTOM/POINTER_CHAIN_CHECK 244: multi-hop pointer-deref scanning with optional
signature-detect invert-match) cleanly onto the current master tip.

Squashes the three original feature commits; no upstream history is discarded
beyond the stale divergent base. Warden.h enum, WardenWin.{h,cpp} handling,
WardenCheckMgr loaders, and the contrib/warden SQL examples are all preserved.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01GD6LV6acKwGd426dDQGPDF
@Krilliac
Krilliac force-pushed the claude/add-custom-pointer-check-XcR4N branch from 2fb9aed to 012748e Compare July 2, 2026 22:13
@AppVeyorBot

Copy link
Copy Markdown

@MadMaxMangos

Copy link
Copy Markdown
Member

@codex review

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 012748eb2a

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +332 to +336
// If a POINTER_CHAIN_CHECK chain is mid-walk, consume one slot for it (do not pop a new id).
if (_pointerChainActive)
{
_currentChecks.push_back(_pointerChainInFlight.checkId);
continue;

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Stop enqueueing the active chain into every remaining slot

With the checked default Warden.NumMemChecks = 3, starting or resuming a pointer chain causes every remaining loop iteration to append the same check ID. The request therefore contains multiple identical reads for one hop, while HandleData advances the single global chain state for each duplicate response; later responses are interpreted as subsequent hops or as terminal bytes, producing false failures and potentially leaving Warden stuck in STATE_REQUESTED_DATA. Schedule the active chain only once per request.

Useful? React with 👍 / 👎.

VALUES
(10002, 5875, 244, '',
'0000000000', -- TODO: per-OS captured bytes of kernel32!GetTickCount prologue
0x00C2D154, 5, '',

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Add an offset to dereference the IAT slot

This row uses an empty offset list, which StartPointerChain treats as a zero-hop check, so it reads five bytes directly from the IAT slot rather than dereferencing the slot and reading the GetTickCount prologue. An operator who replaces the placeholder with clean prologue bytes as instructed will therefore flag every clean client; this one-hop example needs a 0x0 offset.

Useful? React with 👍 / 👎.

VALUES
(10003, 5875, 244, '',
'04', -- TYPEID_PLAYER
0x00B41414, 1, '0x0,0xAC,0x14',

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Remove the extra zero offset from the object chain

The implementation dereferences the base before consuming the first offset, so this leading 0x0 makes the chain read objMgrInstance + 0 as a pointer and then follow vtable + 0xAC. It never reads the documented objMgrInstance + 0xAC linked-list head, causing the terminal 04 comparison to target unrelated code memory; the offsets for the described chain should begin with 0xAC.

Useful? React with 👍 / 👎.

if (isMemCheck)
{
if ((it->second->Type == MEM_CHECK) || (it->second->Type == MODULE_CHECK))
if ((it->second->Type == MEM_CHECK) || (it->second->Type == MODULE_CHECK) || (it->second->Type == POINTER_CHAIN_CHECK))

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Exclude pointer chains from the other-check list

GetWardenCheckIds(false) unconditionally returns every check type, so adding pointer chains to the memory category also leaves them in _otherChecksTodo. That path never calls StartPointerChain, yet serialization reads _pointerChainInFlight; consequently a pointer row is either sent a second time using another chain's state or, when memory checks are disabled, sent with the constructor's zero address and length. The response then fails the active-chain ID check and returns without restoring Warden to its resting state.

Useful? React with 👍 / 👎.

Comment on lines +634 to +635
int cmp = memcmp(buff.contents() + buff.rpos(), rs->Result.AsByteArray(0, false),
_pointerChainInFlight.finalLength);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Preserve fixed-width expected bytes for terminal comparisons

For expected results containing leading zero bytes, BigNumber does not reliably retain the configured byte width or ordering; the supplied 00003000 signature is one such case. This comparison nevertheless reads finalLength bytes from AsByteArray(0, false), so these checks compare against altered bytes and can read beyond the returned allocation when its encoded value is shorter. Pointer-check results need fixed-width byte storage or explicit length validation before this memcmp.

Useful? React with 👍 / 👎.

{
// Intermediate hop: read 4-byte LE pointer, advance chain, await next cycle.
uint32 ptr;
memcpy(&ptr, buff.contents() + buff.rpos(), sizeof(uint32));

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Decode intermediate pointers as little-endian values

On a supported big-endian server host, this raw memcpy interprets the Windows client's little-endian pointer bytes in host order, reversing every intermediate address and making all non-zero-hop chains read unrelated memory or fail. Use the repository's endian-aware ByteBuffer extraction or apply EndianConvert after copying.

Useful? React with 👍 / 👎.

Comment on lines +97 to +100
-- Caveat: kernel32.dll is part of the OS, so the prologue bytes vary across
-- Windows versions. In practice you'd seed `result` per-OS or use a small
-- whitelist via multiple check rows. Listed here as a textbook IAT-hook
-- pattern; consider it a template, not a drop-in.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Do not model an OS whitelist as independent rows

The suggested multiple-row whitelist cannot work with this scheduler: every row for the same client build is independently queued and a mismatch on any one row is penalized. If operators add several GetTickCount prologues for different Windows versions as advised, each client matches at most one and fails all the others; the implementation needs explicit alternative-result semantics or an OS-specific selection key rather than separate ordinary checks.

Useful? React with 👍 / 👎.

Comment on lines +136 to +139
(10003, 5875, 244, '',
'04', -- TYPEID_PLAYER
0x00B41414, 1, '0x0,0xAC,0x14',
'Pointer chain: ObjMgr -> first object -> typeId byte (detect object spoof)');

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Avoid requiring the list head to be a player

Even after correcting the offsets, this row requires the first object in the Object Manager list to have TYPEID_PLAYER, while the immediately preceding note acknowledges that the list head is not necessarily the local player. A clean client whose head is a unit, game object, item, or other object type will therefore fail the 04 comparison and receive the default Warden penalty; the chain must locate the local GUID first or check a genuinely invariant field.

Useful? React with 👍 / 👎.

Comment on lines +352 to +358
if (peek && peek->Type == POINTER_CHAIN_CHECK)
{
StartPointerChain(peek);
if (!_pointerChainActive)
{
// Malformed chain; loader should have filtered it. Skip without scheduling.
continue;

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Reject malformed chains while loading checks

Malformed offset strings are detected only after the check ID has been popped for an individual session, at which point the code silently omits the check from the request. Because LoadWardenChecks does not perform the validation claimed by this comment, a database typo is still reported as a loaded check but disables that protection for every client and repeatedly generates runtime log noise; reject or quarantine the row during loading instead.

Useful? React with 👍 / 👎.

@MadMaxMangos MadMaxMangos self-assigned this Aug 13, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Development

Successfully merging this pull request may close these issues.

4 participants