Find and identify the CBaseModelEntity_SetModel and CBaseEntity_SetGravityScale functions in CS2 binary using IDA Pro MCP. Use this skill when reverse engineering CS2 server.dll / libserver.so to locate the model-assignment routine by scanning for its distinctive fixed byte signature directly (two near-identical sibling wrappers exist and must be disambiguated by matching the reference signature's exact tail bytes, including the following function's leading byte). Trigger: CBaseModelEntity_Se...
Scanned 9/27/2026
Install to Claude Code
npx -y skills add mrc4tt/CS2_VibeSignatures --skill find-CFlashbangProjectile_Spawn-decompiles --agent claude-codeInstalls into .claude/skills of the current project.
Are you the author of Find CFlashbangProjectile Spawn Decompiles?
Add the live security badge to your README — it updates automatically with every re-scan.
[](https://www.skillsdirectory.com/skills/mrc4tt-find-cflashbangprojectile-spawn-decompiles)More formats (shields.io, HTML) on the badges page.
---
name: find-CFlashbangProjectile_Spawn-decompiles
description: |
Find and identify the CBaseModelEntity_SetModel and CBaseEntity_SetGravityScale functions in CS2 binary using
IDA Pro MCP. Use this skill when reverse engineering CS2 server.dll / libserver.so to locate the model-assignment
routine by scanning for its distinctive fixed byte signature directly (two near-identical sibling wrappers exist
and must be disambiguated by matching the reference signature's exact tail bytes, including the following
function's leading byte).
Trigger: CBaseModelEntity_SetModel, CBaseEntity_SetGravityScale
disable-model-invocation: true
---
# Find CBaseModelEntity_SetModel and CBaseEntity_SetGravityScale
Locate `CBaseModelEntity_SetModel` and `CBaseEntity_SetGravityScale` in CS2 `server.dll` / `libserver.so` using
IDA Pro MCP tools.
## Preconditions — never overwrite an existing output
This skill runs only as the fallback after the preprocessor fails, and the preprocessor may already have written
some outputs before failing. **Before doing any work, check `bin/<gamever>/server/` for each output YAML and skip
every target whose file already exists.** Only produce the missing ones. Overwriting is a regression, not a
refresh: a 14174b run of this skill clobbered a correct `CBaseEntity_SetGravityScale.linux.yaml`
(`0xd4e870`, size `0x35b`) with the wrong adjacent wrapper described below.
## Method
### 1. Locate Candidates via Direct Byte-Pattern Scan
Both target functions share an almost byte-identical body shape (thin wrapper: fetch a value via a vcall through
a global singleton, then tail-jump into a shared internal setter). Scan directly:
```text
mcp__ida-pro-mcp__find_bytes patterns=["55 48 89 E5 53 48 89 FB 48 83 EC ?? 48 8D 05 ?? ?? ?? ?? 48 8B 38 48 8B 07 FF 50 ?? 48 89 DF 48 8B 5D ?? C9 48 89 C6 E9"]
```
This returns **two** hits on the 14168 Linux binary, 0x30 bytes apart — `CBaseModelEntity_SetModel` and
`CBaseEntity_SetGravityScale` (or a similarly-shaped sibling wrapper) — which must be disambiguated in step 2.
### 2. Disambiguate via Exact Tail-Byte Match
Fetch ~50-64 raw bytes at both hits:
```text
mcp__ida-pro-mcp__get_bytes regions=[{"addr":"<hit1>","size":64}]
mcp__ida-pro-mcp__get_bytes regions=[{"addr":"<hit2>","size":64}]
```
Both bodies are structurally identical (`push rbp; mov rbp,rsp; push rbx; mov rbx,rdi; sub rsp,8; lea rax,
<global>; mov rdi,[rax]; mov rax,[rdi]; call [rax+imm8]; mov rdi,rbx; mov rbx,[rbp-8]; leave; mov rsi,rax; jmp
<shared_setter>`) and differ only in the RIP-relative `lea` displacement (pointing at slightly different global
slots) and in `4` bytes of padding immediately after the trailing `E9 <rel32>` jmp. The reference signature for
`CBaseModelEntity_SetModel` is long enough to include those 4 padding bytes (`CC CC CC CC`) **plus the first byte
of the next function** (`55`, a fresh `push rbp`). Only the correct candidate has a `push rbp`-prologued function
immediately following its own padding — the sibling candidate's next function starts differently (e.g. `48 8B 47
...`), so its tail byte disagrees with the reference and it is rejected.
> Linux 14168 reference: `CBaseModelEntity_SetModel` at `0x15d94c0` (size `0x2c`) — its padding is immediately
> followed by `55` (a new function's `push rbp`), matching the reference exactly through the last byte.
> `CBaseEntity_SetGravityScale` at `0x15d94f0` (size `0x2c`, adjacent) — its padding is followed by `48 8B 47 10
> ...`, which disagrees with the reference's trailing `55` and correctly disqualifies it as the `SetModel` match.
### 2b. Adjudicate with xrefs — THIS is the load-bearing test, not the tail bytes
The step-2 tail-byte test is **not build-stable** and must never be used alone. On 14174b it silently picked the
wrong function on Windows: the 14174-era reference signature (trailing `... CC*15 48 8B 01`) matched at rva
`0xb101c0`, while the true `SetModel` had moved to rva `0xb122a0` (trailing `... CC*15 48 89 5C 24`). Both were
unique matches; only one was the right function.
Run this on every surviving candidate:
```text
mcp__ida-pro-mcp__xrefs_to addrs=["<cand1>","<cand2>"]
```
`CBaseModelEntity::SetModel` has **many code xrefs (~20+)**, and among them are always
`CFlashbangProjectile_Spawn` (this skill's own preprocessor anchor) and `ScriptBinding_CBaseModelEntity_SetModel`.
The decoy sibling has **only data xrefs** (typically 2), one of them from `CBaseModelEntity_GetScriptDescInternal`
— it is a script-desc table entry with no code callers at all. Pick the candidate with the code xrefs; if
`CFlashbangProjectile_Spawn` is not among them, you have the wrong function.
> 14174b ground truth — Linux: `0x15dfd00` (22 code xrefs) vs decoy `0x15dfd30` (2 data xrefs).
> Windows: `0x180b122a0` (22 code xrefs) vs decoy `0x180b101c0` (2 data xrefs).
### 3. Confirm via Decompile
```text
mcp__ida-pro-mcp__decompile addr="<SetModel_candidate_addr>"
```
Both candidates decompile to the same shape:
```c
__int64 __fastcall sub(__int64 a1)
{
__int64 v1 = (*(__int64 (__fastcall **)(__int64))(*(_QWORD *)g_pSomeSingleton + 0x68))(g_pSomeSingleton);
return sub_SharedSetter(a1, v1);
}
```
i.e. each fetches a value from a global engine-singleton via a fixed vtable slot, then forwards `(this, fetched
value)` into one shared internal setter (`sub_15D90F0` on this build) used by both. This matches "internally
precaches/loads a resource and calls a dispatch through the resource's own interface" for `SetModel` (fetching a
default/error-model index or pointer via the model-manager singleton, then delegating to the internal
model-assignment routine also used by other model-setting overloads). Both siblings being structurally identical
thin forwarders is consistent with `SetModel()` (zero-argument overload defaulting to an error/default model) and
`SetGravityScale()` sharing a similar "fetch-default-then-delegate" code-generation pattern purely by coincidence
of compiler output shape — the byte-level disambiguation in step 2 is what actually separates them, not the
decompile shape (which is inconclusive on its own).
### 4. Generate Function Signature
**ALWAYS** Use SKILL `/generate-signature-for-function` with `addr=<SetModel_candidate_addr>` to generate a robust
and unique `func_sig` for `CBaseModelEntity_SetModel`. Repeat for `CBaseEntity_SetGravityScale` once independently
confirmed (best-effort; no ground truth signature was available for it this session).
### 5. Write IDA Analysis Output as YAML
**ALWAYS** Use SKILL `/write-func-as-yaml` for `CBaseModelEntity_SetModel` with the address/signature from
steps 1-4. Repeat for `CBaseEntity_SetGravityScale`.
## Function Characteristics
### CBaseModelEntity_SetModel
- **Purpose**: Sets an entity's render model, `CBaseModelEntity::SetModel()` — the observed overload takes no
meaningful model-name argument on this build's compiled body (the sole detected parameter is `this`); it fetches
a value (likely a default/error model index) via a global model-manager singleton vcall and forwards to a shared
internal model-assignment routine.
- **Binary**: `server.dll` / `libserver.so`
- **Parameters**: `(CBaseModelEntity *this)` as decompiled on this build (the string-taking overload, if a
separate one exists, was not located this session — this is the zero/default-argument shape matching the
reference signature).
- **VTable**: none — concrete method; internally performs one vcall through a global singleton at a fixed offset
(`+0x68` on this build).
### CBaseEntity_SetGravityScale
- **DO NOT identify this as the wrapper adjacent to `SetModel`.** That earlier guess was wrong. The byte-adjacent
sibling (`0x15d94f0` on 14168, `0x15dfd30` on 14174b) is a `0x2c`-byte forwarder that is *some other* setter —
it is **not** `CBaseEntity::SetGravityScale`. Step 2's tail-byte test only tells you which candidate is
`SetModel`; it says nothing about what the rejected candidate is.
- **Purpose**: Sets an entity's gravity scale multiplier.
- **Binary**: `server.dll` / `libserver.so`
- **Actual shape**: a large function (~`0x35b` bytes on Linux 14174/14174b), whose body opens with a float compare
against a member (`comiss xmm0, [rdi+0x6DC]`-style) rather than a singleton vcall + tail-jump. Linux 14174b
ground truth: `0xd4e870`, `func_size 0x35b`.
- **Preferred source**: `find-CBaseEntity_RegisteredScriptFuncs` resolves it authoritatively through the VScript
function-registration table. If that skill already wrote the YAML, leave it alone (see Preconditions).
## Discovery Strategy
1. The shared wrapper shape (`push rbp; mov rbp,rsp; push rbx; mov rbx,rdi; sub rsp,imm8; lea rax,<global>; ...;
jmp <shared_setter>`) is distinctive enough to scan directly with `find_bytes`, but is **not unique** — it
returns 2 hits since the compiler reuses this exact forwarding shape for at least one other setter.
2. Disambiguation relies on matching the reference signature's *tail*, deliberately including 4 bytes of
inter-function padding plus the first byte of whatever function follows — this turns an otherwise-ambiguous
26-instruction match into a fully unique one, since only one of the two candidates happens to be immediately
followed by a `push rbp`-prologued function.
3. Decompiling both is useful for understanding *what* the function does but does **not** by itself distinguish
`SetModel` from `SetGravityScale` on this build, since both compile to structurally identical forwarding
bodies — byte-level tail matching against the reference is the load-bearing step here.
This is robust because the reference signature was deliberately generated long enough (by
`make_signature_for_function`'s natural growth-to-uniqueness behavior) to include the next function's leading
byte, which is exactly the minimum information needed to break the tie between the two candidates.
## Output YAML Format
The output YAML filenames depend on the platform:
- `server.dll` -> `CBaseModelEntity_SetModel.windows.yaml`, `CBaseEntity_SetGravityScale.windows.yaml`
- `libserver.so` -> `CBaseModelEntity_SetModel.linux.yaml`, `CBaseEntity_SetGravityScale.linux.yaml`
Fields (both files): `func_name`, `func_va`, `func_rva`, `func_size`, `func_sig`.
> Linux 14168 reference: `CBaseModelEntity_SetModel` func_sig =
> `55 48 89 E5 53 48 89 FB 48 83 EC ? 48 8D 05 ? ? ? ? 48 8B 38 48 8B 07 FF 50 ? 48 89 DF 48 8B 5D ? C9 48 89 C6 E9 ? ? ? ? CC CC CC CC 55`,
> `func_va = 0x15d94c0`, `func_size = 0x2c`. `CBaseEntity_SetGravityScale` (unverified) `func_va = 0x15d94f0`,
> `func_size = 0x2c`.
Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.
No comments yet. Be the first to comment!