$npx -y skills add HLND2T/CS2_VibeSignatures --skill write-vtable-as-yamlWrite vtable analysis results as YAML file beside the binary using IDA Pro MCP. Use this skill after locating a vtable to persist the results in a standardized YAML format.
| 1 | # Write VTable as YAML |
| 2 | |
| 3 | Persist vtable analysis results to a YAML file beside the binary using IDA Pro MCP. |
| 4 | |
| 5 | ## Prerequisites |
| 6 | |
| 7 | Before using this skill, you should have: |
| 8 | 1. Located the target vtable address |
| 9 | 2. Identified the class name for the vtable |
| 10 | |
| 11 | ## Required Parameters |
| 12 | |
| 13 | | Parameter | Description | Example | |
| 14 | |-----------|-------------|---------| |
| 15 | | `vtable_class` | Class name for the vtable | `CSource2Server` | |
| 16 | | `vtable_va` | Virtual address of the vtable | `0x182B8D9D8` | |
| 17 | |
| 18 | ## Optional Parameters |
| 19 | |
| 20 | | Parameter | Description | Example | |
| 21 | |-----------|-------------|---------| |
| 22 | | `vtable_symbol` | The IDA symbol name for the vtable | "??_7CBaseEntity@@6B@" | |
| 23 | |
| 24 | ## Method |
| 25 | |
| 26 | ```python |
| 27 | mcp__ida-pro-mcp__py_eval code=""" |
| 28 | import idaapi |
| 29 | import ida_bytes |
| 30 | import ida_name |
| 31 | import os |
| 32 | import yaml |
| 33 | |
| 34 | # === REQUIRED: Replace these values === |
| 35 | vtable_class = "<vtable_class>" # e.g., "CBaseEntity" |
| 36 | vtable_va = <vtable_va> # e.g., 0x182B8D9D8 |
| 37 | # ====================================== |
| 38 | |
| 39 | # === OPTIONAL: Replace these values === |
| 40 | vtable_symbol = "<vtable_symbol>" # e.g., "??_7CBaseEntity@@6B@" or "_ZTV11CBaseEntity + 0x10" or "off_180XXXXXX" |
| 41 | # ====================================== |
| 42 | |
| 43 | input_file = idaapi.get_input_file_path() |
| 44 | dir_path = os.path.dirname(input_file) |
| 45 | |
| 46 | if input_file.endswith('.dll'): |
| 47 | platform = 'windows' |
| 48 | image_base = idaapi.get_imagebase() |
| 49 | else: |
| 50 | platform = 'linux' |
| 51 | image_base = 0x0 |
| 52 | |
| 53 | vtable_rva = vtable_va - image_base |
| 54 | |
| 55 | # Handle Linux vtables (skip RTTI metadata) |
| 56 | vtable_name = ida_name.get_name(vtable_va) or "" |
| 57 | if vtable_name.startswith("_ZTV"): |
| 58 | vtable_va = vtable_va + 0x10 |
| 59 | vtable_rva = vtable_va - image_base |
| 60 | |
| 61 | # Determine pointer size and count virtual functions |
| 62 | ptr_size = 8 if idaapi.inf_is_64bit() else 4 |
| 63 | vtable_entries = [] |
| 64 | |
| 65 | for i in range(1000): |
| 66 | if ptr_size == 8: |
| 67 | ptr_value = ida_bytes.get_qword(vtable_va + i * ptr_size) |
| 68 | else: |
| 69 | ptr_value = ida_bytes.get_dword(vtable_va + i * ptr_size) |
| 70 | |
| 71 | if ptr_value == 0 or ptr_value == 0xFFFFFFFFFFFFFFFF: |
| 72 | break |
| 73 | |
| 74 | func = idaapi.get_func(ptr_value) |
| 75 | if func is None: |
| 76 | flags = ida_bytes.get_full_flags(ptr_value) |
| 77 | if not ida_bytes.is_code(flags): |
| 78 | break |
| 79 | |
| 80 | vtable_entries.append(ptr_value) |
| 81 | |
| 82 | count = len(vtable_entries) |
| 83 | vtable_size = count * ptr_size |
| 84 | |
| 85 | # Build YAML data structure |
| 86 | yaml_data = { |
| 87 | 'vtable_class': vtable_class, |
| 88 | 'vtable_symbol': vtable_symbol, |
| 89 | 'vtable_va': hex(vtable_va), |
| 90 | 'vtable_rva': hex(vtable_rva), |
| 91 | 'vtable_size': hex(vtable_size), |
| 92 | 'vtable_numvfunc': count, |
| 93 | 'vtable_entries': {i: hex(entry) for i, entry in enumerate(vtable_entries)} |
| 94 | } |
| 95 | |
| 96 | yaml_path = os.path.join(dir_path, f"{vtable_class}_vtable.{platform}.yaml") |
| 97 | with open(yaml_path, 'w', encoding='utf-8') as f: |
| 98 | yaml.dump(yaml_data, f, default_flow_style=False, sort_keys=False, allow_unicode=True) |
| 99 | print(f"Written to: {yaml_path}") |
| 100 | """ |
| 101 | ``` |
| 102 | |
| 103 | ## Output File Naming Convention |
| 104 | |
| 105 | The output YAML filename follows this pattern: |
| 106 | - `<vtable_class>_vtable.<platform>.yaml` |
| 107 | |
| 108 | Examples: |
| 109 | - `server.dll` → `CSource2Server_vtable.windows.yaml` |
| 110 | - `libserver.so` / `libserver.so` → `CSource2Server_vtable.linux.yaml` |
| 111 | |
| 112 | ## Output YAML Format |
| 113 | |
| 114 | `CSource2Server_vtable.windows.yaml` - Example for CSource2Server vtable on Windows: |
| 115 | |
| 116 | ```yaml |
| 117 | vtable_class: CSource2Server |
| 118 | vtable_symbol: off_180XXXXXX # Symbol in IDA to CSource2Server's vtable |
| 119 | vtable_va: 0x182B8D9D8 # Virtual address - changes with game updates |
| 120 | vtable_rva: 0x2B8D9D8 # Relative virtual address (VA - image base) - changes with game updates |
| 121 | vtable_size: 0x2D8 # VTable size in bytes - changes with game updates |
| 122 | vtable_numvfunc: 97 # Number of virtual functions - changes with game updates |
| 123 | vtable_entries: # Every virtual functions starting from vtable[0] |
| 124 | 0: 0x180C87B20 # vtable[0] - changes with game updates |
| 125 | 1: 0x180C87FA0 # vtable[1] - changes with game updates |
| 126 | 2: 0x180C87FF0 # vtable[2] - changes with game updates |
| 127 | ``` |
| 128 | |
| 129 | `CSource2Server_vtable.linux.yaml` - Example for CSource2Server vtable on linux: |
| 130 | |
| 131 | ```yaml |
| 132 | vtable_class: CSource2Server |
| 133 | vtable_symbol: _ZTV14CSource2Server + 0x10 # Symbol in IDA to CSource2Server's vtable |
| 134 | vtable_va: '0x2261dd8' # Virtual address - changes with game updates |
| 135 | vtable_rva: '0x2261dd8' # Relative virtual address (VA - image base) - changes with game updates |
| 136 | vtable_size: '0x310' # VTable size in bytes - changes with game updates |
| 137 | vtable_numvfunc: 98 # Number of virtual functions - changes with game updates |
| 138 | vtable_entries: # Every virtual functions starting from vtable[0] |
| 139 | 0: '0x16ea780' # vtable[0] - changes with game updates |
| 140 | 1: '0x16e9b50' # vtable[1] - changes with game updates |