find-CNetworkStringTableContainer_RemoveAllTables
Final-guarantee fallback for the find-CNetworkStringTableContainer_RemoveAllTables preprocessor. Recovers CNetworkStringTableContainer::RemoveAllTables in CS2 engine2.dll / libengine2.so by anchoring
SKILL.md.claude/skills/find-CNetworkStringTableContainer_RemoveAllTables/SKILL.md
GitHubFind CNetworkStringTableContainer_RemoveAllTables (final-guarantee fallback)
Recover CNetworkStringTableContainer::RemoveAllTables in CS2 engine2.dll / libengine2.so using IDA Pro
MCP tools. This is the Agent fallback: it runs only when the preprocessor returned failure.
Realworld Function References
Read the platform-relevant source-owned artifacts before searching in IDA. Treat every address, offset, and index below as a reference-build value; verify each one against the current binary.
bin_artifacts/14191/engine/CNetworkStringTableContainer_vtable.windows.yamlbin_artifacts/14191/engine/CNetworkStringTableContainer_vtable.linux.yamlbin_artifacts/14191/engine/CNetworkStringTableContainer_RemoveAllTables.windows.yamlbin_artifacts/14191/engine/CNetworkStringTableContainer_RemoveAllTables.linux.yamlida_preprocessor_scripts/references/engine/CNetworkStringTableContainer_WriteBaselines.windows.yamlida_preprocessor_scripts/references/engine/CNetworkStringTableContainer_WriteBaselines.linux.yaml
The WriteBaselines references are sibling methods on the same class; they help confirm you are inside
CNetworkStringTableContainer and not in a look-alike container.
Background — what RemoveAllTables does
RemoveAllTables walks the container's table array, releases every live INetworkStringTable through its
deleting destructor (slot 0 called with argument 1), then zeroes the count and frees the array through
g_pMemAlloc. Under a debug log channel it first emits:
"%s: CNetworkStringTableContainer::RemoveAllTables: removing %d tables\n"
The %s is INetworkStringTableContainer::GetName(), a trailing accessor that returns the string stored at
this + 0x28 ("SV" on the server container, "CL" on the client one). Do not confuse that accessor with the
target — it is a different vtable slot and contains no table-walking loop.
Robustness principle — anchor on the string, disambiguate with the vtable
The debug string is the stable semantic anchor, but it is not unique: call sites get inlined into unrelated
functions over time (on 14191 the Windows build has four string xrefs — the vfunc itself plus
SV_InstallHLTVStringTableMirrors and two static initializers). Never accept a "single xref" answer and never
accept the first hit.
The discriminator is the vtable: intersect the string-xref candidate set with
CNetworkStringTableContainer_vtable's entries. Exactly one candidate must survive. This works whether the body
is inlined into the vfunc (14191) or de-inlined behind a string-owning helper (14181), because either way the
vtable slot points at a function that contains — or immediately reaches — the logging call.
Output inventory
| # | Output symbol | Kind | Windows | Linux | Writer skill |
|---|---|---|---|---|---|
| 1 | CNetworkStringTableContainer_RemoveAllTables |
real vtable vfunc | index 13 / offset 0x68 |
index 14 / offset 0x70 |
/write-vfunc-as-yaml |
| 2 | CNetworkStringTableContainer_vtable |
vtable | expected_input, not produced here |
expected_input, not produced here |
— |
Platform gating: the target is produced on both platforms. Offsets/indices above are reference values from build 14191 — derive them from the current binary, do not assume.
Step 0. Skip an existing output
If CNetworkStringTableContainer_RemoveAllTables.<platform>.yaml already exists in the active artifact module
directory and parses to a non-empty mapping, skip that platform. Never derive the artifact path from the binary
directory; use the directory reported by the analyzer (also readable as CS2VIBE_ARTIFACT_DIR).
Step 1. Load the container vtable
ALWAYS Use SKILL /get-vtable-from-yaml with class_name=CNetworkStringTableContainer to obtain the
authoritative vtable_va, vtable_size, vtable_numvfunc, and vtable_entries for the current build. This
CNetworkStringTableContainer_vtable.{platform}.yaml is this skill's expected_input, so it must already exist
when the fallback runs. If /get-vtable-from-yaml errors, STOP and report to the user.
Step 2. Collect the string-xref candidates
Enumerate C strings and resolve every xref to its containing function. Keep the full set — do not stop at the first hit:
mcp__ida-pro-mcp__py_eval code="""
import idautils, ida_nalt, idc, ida_funcs
strings = idautils.Strings(default_setup=False)
strings.setup(strtypes=[ida_nalt.STRTYPE_C], minlen=4)
needle = "CNetworkStringTableContainer::RemoveAllTables: removing %d tables"
cands = set()
for s in strings:
if needle in str(s):
print('string', hex(s.ea), str(s)[:80])
for xr in idautils.XrefsTo(s.ea, 0):
f = ida_funcs.get_func(xr.frm)
if f:
cands.add(f.start_ea)
print(' xref', hex(xr.frm), 'in', hex(f.start_ea), idc.get_func_name(f.start_ea))
print('CANDIDATES', [hex(a) for a in sorted(cands)])
"""
If the string is missing entirely (logging compiled out on this build), STOP and report — the anchor set must be extended; do not guess an address.
Step 3. Intersect with the vtable entries
Keep only candidates that are entries of CNetworkStringTableContainer_vtable:
mcp__ida-pro-mcp__py_eval code="""
import yaml, os, idaapi
d = os.environ.get('CS2VIBE_ARTIFACT_DIR') or os.path.dirname(idaapi.get_input_file_path())
plat = 'windows' if idaapi.get_input_file_path().endswith('.dll') else 'linux'
v = yaml.safe_load(open(os.path.join(d, f'CNetworkStringTableContainer_vtable.{plat}.yaml'), encoding='utf-8'))
entries = {int(str(a), 16): int(k) for k, a in v['vtable_entries'].items()}
cands = {0x0, 0x0} # <-- paste CANDIDATES from Step 2
print('vtable_numvfunc', v['vtable_numvfunc'])
for c in sorted(cands):
print(hex(c), '-> vtable index', entries.get(c, 'NOT IN VTABLE'))
"""
Exactly one candidate must map to an index. Set:
vtable_name = CNetworkStringTableContainer_vtable— the artifact stem, matching what the preprocessor and the trackedbin_artifactsYAML use. Do not write the bare class name here.vfunc_index = <the matching entry index>vfunc_offset = vfunc_index * 8
If zero candidates match, the body is de-inlined behind a helper. Enumerate the functions each candidate calls,
decompile them, and look for the one that returns its first argument (this) and performs the table-freeing
loop; then re-run the vtable intersection on that callee. If more than one candidate matches, or the callee
search is ambiguous, STOP and report which candidates survived — do not pick one by position.
On reference build 14191 this yields Windows index 13 / offset 0x68 and Linux index 14 / offset 0x70.
Step 4. Confirm the candidate is the table-walking vfunc
Decompile the resolved function and confirm both markers:
- a
LoggingSystem_Log(or equivalent) call passing theremoving %d tablesformat; - a loop over the table array that calls the deleting destructor of each element (
(**elem)(elem, 1)) and then zeroes the count and frees the array.
Reject a candidate that only logs or only frees — it is a sibling method or a stale call site.
Rename the function if the database does not already name it that way:
mcp__ida-pro-mcp__rename batch={"func":[{"addr":"<start>","name":"CNetworkStringTableContainer_RemoveAllTables"}]}
Step 5. Generate the signature and write the YAML
- ALWAYS Use SKILL
/generate-signature-for-functionwithaddr=<resolved start>to obtain a fresh, validatedfunc_sig. - ALWAYS Use SKILL
/write-vfunc-as-yamlwith:func_name:CNetworkStringTableContainer_RemoveAllTablesfunc_addr: the resolved function startfunc_sig: the signature from step 1vtable_name:CNetworkStringTableContainer_vtablevfunc_offset:vfunc_index * 8(from Step 3)vfunc_index: from Step 3
Do not write a partial YAML: func_name, func_va, func_rva, func_size, func_sig, vtable_name,
vfunc_offset, and vfunc_index are all required. Emit only the platform currently being processed.
The vtable_name value must be the artifact stem CNetworkStringTableContainer_vtable, not the bare class name —
the preprocessor emits that stem and bin_artifact_contract.py compares the artifact byte-for-byte against the
canonical form.
Failure handling
CNetworkStringTableContainer_vtable.<platform>.yamlmissing → STOP and report (the skill'sexpected_inputis absent).- The
removing %d tablesstring resolves to no function → STOP and report which anchor failed. - More than one candidate survives the vtable intersection → STOP and report the survivors.
- Never emit the symbol on the wrong platform, and never reuse a previous build's
func_va/vfunc_index.
Output YAML filenames
Written under the analyzer-reported artifact module directory, one file per platform:
- Windows (
engine2.dll):CNetworkStringTableContainer_RemoveAllTables.windows.yaml - Linux (
libengine2.so):CNetworkStringTableContainer_RemoveAllTables.linux.yaml
Why this is robust
- The function is anchored by a class-specific debug string, then disambiguated by the vtable entry set, so the extra inline call sites that break a "unique string xref" search cannot mislead it.
- The vtable slot is derived from the resolved address, so a vtable reorder is absorbed instead of repeated.
- Following the de-inline boundary covers the case where the string-owning body moves into a helper that
receives
thisas its first argument.