$npx -y skills add HLND2T/CS2_VibeSignatures --skill find-CCSBotManager_AddBot_BotNavIgnoreFind and patch the g_pNavMesh null-check inside CCSBotManager_AddBot in CS2 binary using IDA Pro MCP. This patch removes the navigation mesh requirement so bots can be added even without a nav mesh loaded. Use this skill when reverse engineering CS2 server.dll or libserver.so to
| 1 | # CCSBotManager_AddBot_BotNavIgnore Patch Workflow |
| 2 | |
| 3 | ## Overview |
| 4 | |
| 5 | Locate the `g_pNavMesh` null-check at the beginning of `CCSBotManager_AddBot` and generate a patch that converts the conditional `jz` (jump-if-zero) into an unconditional `jmp` past the early-return, so bots can be added even when no navigation mesh is loaded. |
| 6 | |
| 7 | The target code pattern in pseudocode: |
| 8 | ```c |
| 9 | if ( !g_pNavMesh || !*(_BYTE *)(g_pNavMesh + 264) ) // <-- patch target: skip this check |
| 10 | return 0; |
| 11 | ``` |
| 12 | |
| 13 | In assembly, the first branch of this check is: |
| 14 | ```asm |
| 15 | mov rax, cs:g_pNavMesh |
| 16 | ... |
| 17 | test rax, rax |
| 18 | jz loc_XXXXXXXX ; <-- patch this: jz -> jmp to loc after the nav check |
| 19 | ``` |
| 20 | |
| 21 | The `jz` is a near conditional jump (`0F 84 rel32` — 6 bytes). Patching it to `jmp rel32` (`E9 <new_rel32> 90` — 5 bytes + 1 NOP) makes it always jump past both the null-pointer check and the `g_pNavMesh + 264` byte check, landing at the code that continues bot creation. |
| 22 | |
| 23 | The jump target should be the `loc_` label that is reached after both nav mesh checks pass (the code right after the second `jz` that also targets the same early-return). |
| 24 | |
| 25 | ## Prerequisites |
| 26 | |
| 27 | - `CCSBotManager_AddBot` must already be identified. Use SKILL `/get-func-from-yaml` with `func_name=CCSBotManager_AddBot` to load its address. If YAML does not exist, run SKILL `/find-CCSBotManager_AddBot-AND-g_pCSBotManager-AND-g_pNavMesh` first. |
| 28 | |
| 29 | ## Location Steps |
| 30 | |
| 31 | ### 1. Get CCSBotManager_AddBot Function Address |
| 32 | |
| 33 | **ALWAYS** Use SKILL `/get-func-from-yaml` with `func_name=CCSBotManager_AddBot`. |
| 34 | |
| 35 | If the skill returns an error, stop and report to user. |
| 36 | |
| 37 | ### 2. Decompile and Locate the Nav Mesh Check |
| 38 | |
| 39 | Decompile the function: |
| 40 | |
| 41 | ``` |
| 42 | mcp__ida-pro-mcp__decompile(addr="<func_va>") |
| 43 | ``` |
| 44 | |
| 45 | In the decompiled output, search for the nav mesh check pattern at the very beginning of the function body. The key indicators are: |
| 46 | - `if ( !g_pNavMesh || !*(_BYTE *)(g_pNavMesh + 264) )` or equivalent |
| 47 | - Immediately followed by `return 0;` |
| 48 | |
| 49 | Note the address annotation on the if-statement line. |
| 50 | |
| 51 | ### 3. Disassemble the Function Prologue |
| 52 | |
| 53 | Disassemble the first ~30 instructions of the function to find the exact `test rax, rax` + `jz` pair: |
| 54 | |
| 55 | ``` |
| 56 | mcp__ida-pro-mcp__disasm(addr="<func_va>", max_instructions=30) |
| 57 | ``` |
| 58 | |
| 59 | Look for this assembly pattern: |
| 60 | ```asm |
| 61 | mov rax, cs:g_pNavMesh |
| 62 | ... |
| 63 | test rax, rax |
| 64 | jz loc_XXXXXXXX ; early return if g_pNavMesh == NULL |
| 65 | cmp byte ptr [rax+108h], 0 |
| 66 | jz loc_XXXXXXXX ; early return if nav mesh not loaded |
| 67 | ``` |
| 68 | |
| 69 | Record: |
| 70 | - **patch_va**: Address of the first `jz` instruction (the `test rax, rax` / `jz` pair) |
| 71 | - **jump_target**: The address of the code right AFTER the second `jz` — this is where execution continues when both checks pass. This is the label we want to jump to unconditionally. |
| 72 | |
| 73 | ### 4. Determine Patch Bytes |
| 74 | |
| 75 | Read the original bytes of the `jz` instruction: |
| 76 | |
| 77 | ``` |
| 78 | mcp__ida-pro-mcp__get_bytes(regions={"addr": "<patch_va>", "size": 6}) |
| 79 | ``` |
| 80 | |
| 81 | The `jz` is a near conditional jump: `0F 84 rel32` (6 bytes). |
| 82 | |
| 83 | Compute the new unconditional jump to the target (the code after both nav checks): |
| 84 | - `new_rel32 = jump_target - (patch_va + 5)` |
| 85 | - `patch_bytes` = `E9 <new_rel32_le> 90` |
| 86 | |
| 87 | Where `<new_rel32_le>` is the 4-byte little-endian encoding of `new_rel32`. |
| 88 | |
| 89 | ### 5. Generate Patch Signature |
| 90 | |
| 91 | **ALWAYS** Use SKILL `/generate-signature-for-patch` to generate and validate the signature. |
| 92 | |
| 93 | Required context for the skill: |
| 94 | - `func_name`: `CCSBotManager_AddBot` |
| 95 | - `func_va`: From step 1 |
| 96 | - `patch_va`: Address of the `jz` instruction from step 3 |
| 97 | - `original_instruction`: e.g., `jz loc_1802A1485` |
| 98 | - `patched_instruction`: e.g., `jmp loc_1802A104F` + `nop` |
| 99 | - `description`: `Remove g_pNavMesh null-check in CCSBotManager_AddBot - patch conditional jz to unconditional jmp to skip nav mesh validation` |
| 100 | |
| 101 | ### 6. Write YAML Output |
| 102 | |
| 103 | **ALWAYS** Use SKILL `/write-patch-as-yaml` to persist the results. |
| 104 | |
| 105 | Required parameters: |
| 106 | - `patch_name`: `CCSBotManager_AddBot_BotNavIgnore` |
| 107 | - `patch_sig`: The validated signature from step 5 |
| 108 | - `patch_bytes`: The computed patch bytes from step 4 |
| 109 | - `patch_sig_disp`: From step 5 result (omit if 0) |
| 110 | |
| 111 | ## Output YAML Files |
| 112 | |
| 113 | - `CCSBotManager_AddBot_BotNavIgnore.windows.yaml` |
| 114 | - `CCSBotManager_AddBot_BotNavIgnore.linux.yaml` |