Find a function's vtable address using IDA Pro MCP. Use this skill when want to get exact virtual address of a class. Triggers: get vftable address, get virtual function table, find vtable, get vtable
Scanned 9/27/2026
Install to Claude Code
npx -y skills add mrc4tt/CS2_VibeSignatures --skill get-vtable-address --agent claude-codeInstalls into .claude/skills of the current project.
Are you the author of Get Vtable Address?
Add the live security badge to your README — it updates automatically with every re-scan.
[](https://www.skillsdirectory.com/skills/mrc4tt-get-vtable-address)More formats (shields.io, HTML) on the badges page.
---
name: get-vtable-address
description: |
Find a function's vtable address using IDA Pro MCP. Use this skill when want to get exact virtual address of a class.
Triggers: get vftable address, get virtual function table, find vtable, get vtable
---
# Get VTable address and size
Find a class's virtual function table by class name. Get its address and size in a single step.
## Prerequisites
- ClassName
## Method
### 1. Get vtable address and size
Run this single Python script using `mcp__ida-pro-mcp__py_eval`, replacing `<CLASS_NAME>` with the actual class name (e.g., `CGameRules`, `CCSPlayer_ItemServices`):
```python
mcp__ida-pro-mcp__py_eval code="""
import ida_bytes, ida_name, idaapi, idautils, ida_segment
class_name = "<CLASS_NAME>"
ptr_size = 8 if idaapi.inf_is_64bit() else 4
vtable_start = None # address of first vfunc pointer
vtable_symbol = ""
is_linux = False
method = ""
# ── Direct symbol lookup ──────────────────────────────────────────────
# Windows: ??_7ClassName@@6B@
win_name = f"??_7{class_name}@@6B@"
addr = ida_name.get_name_ea(idaapi.BADADDR, win_name)
if addr != idaapi.BADADDR:
vtable_start = addr
vtable_symbol = win_name
is_linux = False
method = "direct"
# Linux: _ZTV<len>ClassName (e.g. _ZTV10CGameRules)
if vtable_start is None:
linux_name = f"_ZTV{len(class_name)}{class_name}"
addr = ida_name.get_name_ea(idaapi.BADADDR, linux_name)
if addr != idaapi.BADADDR:
vtable_start = addr + 0x10 # skip offset-to-top + typeinfo ptr
vtable_symbol = f"{linux_name} + 0x10"
is_linux = True
method = "direct"
# ── RTTI / TypeInfo fallback ──────────────────────────────────────────
# Windows: ??_R4ClassName@@6B@ (Complete Object Locator)
if vtable_start is None:
col_name = f"??_R4{class_name}@@6B@"
col_addr = ida_name.get_name_ea(idaapi.BADADDR, col_name)
if col_addr != idaapi.BADADDR:
is_linux = False
rdata_seg = ida_segment.get_segm_by_name(".rdata")
for ref in idautils.DataRefsTo(col_addr):
if rdata_seg and not (rdata_seg.start_ea <= ref < rdata_seg.end_ea):
continue
vtable_start = ref + ptr_size
sym = ida_name.get_name(vtable_start) or f"??_7{class_name}@@6B@"
vtable_symbol = sym
method = "rtti_fallback"
break
# Linux: _ZTI<len>ClassName (typeinfo)
if vtable_start is None:
ti_name = f"_ZTI{len(class_name)}{class_name}"
ti_addr = ida_name.get_name_ea(idaapi.BADADDR, ti_name)
if ti_addr != idaapi.BADADDR:
is_linux = True
for ref in idautils.DataRefsTo(ti_addr):
ott = ida_bytes.get_qword(ref - ptr_size) if ptr_size == 8 else ida_bytes.get_dword(ref - ptr_size)
if ott == 0:
vtable_start = ref + ptr_size
ztv_addr = ref - ptr_size
ztv_name = ida_name.get_name(ztv_addr) or f"_ZTV{len(class_name)}{class_name}"
vtable_symbol = f"{ztv_name} + 0x10"
method = "rtti_fallback"
break
assert vtable_start is not None, f"Cannot find vtable for {class_name}"
# ── Count virtual functions ───────────────────────────────────────────
count = 0
for i in range(1000):
ea = vtable_start + i * ptr_size
# Linux: stop at next vtable / typeinfo symbol boundary
if is_linux and i > 0:
name = ida_name.get_name(ea)
if name and (name.startswith("_ZTV") or name.startswith("_ZTI")):
break
ptr_value = ida_bytes.get_qword(ea) if ptr_size == 8 else ida_bytes.get_dword(ea)
if ptr_value == 0:
if is_linux:
count += 1 # NULL = pure virtual placeholder
continue
else:
break # Windows: NULL = vtable end
if ptr_value == 0xFFFFFFFFFFFFFFFF:
break
func = idaapi.get_func(ptr_value)
if func is not None:
count += 1
continue
flags = ida_bytes.get_full_flags(ptr_value)
if ida_bytes.is_code(flags):
count += 1
continue
break # not a valid function pointer
size_in_bytes = count * ptr_size
print(f"vtable_class: {class_name}")
print(f"vtable_symbol: {vtable_symbol}")
print(f"vtable_va: {hex(vtable_start)}")
print(f"vtable_size: {hex(size_in_bytes)}")
print(f"vtable_numvfuncs: {count}")
print(f"method: {method}")
"""
```
**Lookup order**:
1. Direct symbol: `??_7ClassName@@6B@` (Win) / `_ZTV<len>ClassName` (Linux)
2. RTTI fallback: `??_R4ClassName@@6B@` → DataRefsTo → vtable (Win) / `_ZTI<len>ClassName` → DataRefsTo with offset-to-top==0 → vtable (Linux)
**Platform-specific vfunc counting**:
- **Linux** (`_ZTV` prefix): NULL entries = pure virtual placeholders (keep counting); stops at next `_ZTV`/`_ZTI` symbol
- **Windows** (`??_7` prefix): NULL = vtable end; stops immediately
### 2. Continue with Unfinished Tasks
If we are called by a task from a task list / parent SKILL, restore and continue with the unfinished tasks.
## Output
The skill returns:
- **vtable_class**: The class name
- **vtable_symbol**: The IDA symbol. e.g. `??_7CGameRules@@6B@` or `_ZTV10CGameRules + 0x10`
- **vtable_va**: The start address of the vtable (first vfunc pointer)
- **vtable_size**: Total size of the vtable in bytes
- **vtable_numvfuncs**: Count of virtual function entries in the vtable
- **method**: How the vtable was found (`direct` or `rtti_fallback`)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!