Skip to content
Back to skills

Find CFlashbangProjectile Spawn Decompiles

ASecurity

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...

  • 3 stars
  • 0 votes
  • 0 copies
  • 0 views
  • Added September 27, 2026
data

Works with

  • mcp

Security analysis

A100/100

Scanned September 27, 2026

npx -y skills add mrc4tt/CS2_VibeSignatures --skill find-CFlashbangProjectile_Spawn-decompiles --agent claude-code

Installs 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 with every re-scan.

Security grade badge for Find CFlashbangProjectile Spawn Decompiles
[![Security: A — Skills Directory](https://www.skillsdirectory.com/api/skills/mrc4tt-find-cflashbangprojectile-spawn-decompiles/badge)](https://www.skillsdirectory.com/skills/mrc4tt-find-cflashbangprojectile-spawn-decompiles)

More formats (shields.io, HTML) on the badges page. Keep it an A: scan every change in CI with Pro.

Download with Pro
SKILL.md
---
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`.

Attribution

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

Comments

Loading comments…