- Introduced `seg043_boundary_repair.json` to manage function boundaries in segment 043. - Created `read_file.py` for reading and printing file content size. - Added `resolve_bb4f.py` to resolve specific function call targets. - Implemented `resolve_top_targets.py` to find resolved NE targets for top-called wrapper functions. - Added `script_contents.txt` to summarize NE relocation far calls. - Updated `tier4_ghidra.txt`, `tier4_ghidra_check.txt`, `tier4_output.txt`, and `tier4_result.txt` with function call statistics. - Created `tier5_errors.txt` for error logging and `tier5_output.txt` for additional function call statistics. - Established `tools` directory with helper scripts for the Ghidra project, including CLI and common functionalities. - Implemented command-line interface in `cli.py` for various project operations. - Added `common.py` for shared functions and configurations across tools. - Introduced `validate_fixups.py` to validate NE relocation fixups against known addresses.
4.8 KiB
4.8 KiB
| applyTo |
|---|
| ** |
Crusader Ghidra Workflow
- Active target is the raw full-EXE Ghidra program
CRUSADER-RAW.EXEunless explicitly stated otherwise. - Use Ghidra MCP tools for analysis, decompilation, renaming, comments, and xref work.
- Keep analysis batches small: prefer 1-5 functions, labels, or comments at a time.
- Avoid speculative renames. Prefer names that are supported by one of these:
- verified raw mapping from standalone segment work
- direct string evidence
- clear call/field behavior in decompiler or disassembly
- xref relationships to already-named functions
- When porting names from standalone segment extracts into
CRUSADER-RAW.EXE, use only verified base mappings.
Verified Raw Mapping Rules
seg001raw base =0x6E570seg021raw base =0x87170- Porting formula:
raw_full_exe_flat = verified_segment_base + standalone_segment_relative_offset seg001andseg021both contain a keyboard handler; keep the seg001 name asseg001_input_keyboard_handlerto avoid collision.
Working Method
- Prefer a single decompile call first.
- If the decompiler collapses to thunk-heavy output, use one disassembly lookup to confirm the wrapper or parameter setup.
- When
decompile_functionoutput is too large (>~50KB), the result is written to a temp JSON file thatread_filereturns as empty{}. Usedisassemble_functioninstead — it returns inline assembly directly and is fully navigable for large functions. - Add a short decompiler comment when a rename is mapped from verified notes so the provenance stays visible in Ghidra.
- Keep
crusader_decompilation_notes.mdupdated after each verified batch. - Record raw-import addresses alongside original segment-relative offsets when porting names.
- Always use
rename_function_by_address—rename_function(by name) fails with "must have required property 'old_name'" and is broken. Use"function_address": "000c:XXXX"format.
PyGhidra Fallback
- Use the local PyGhidra toolkit in
tools/pyghidra_crusaderwhen MCP is missing an operation such as function creation, deletion, or batched scripted edits. - The workspace-local Python environment for this toolkit is
.venv-pyghidra311, created fromC:\Users\Maddo\.pyenv\pyenv-win\versions\3.11.6\python.exeand installed from the bundled Ghidra 11.3.2 offline packages. - Default install dir for the toolkit is
I:\Apps\ghidra_11.3.2_PUBLIC. - Invoke the toolkit with
\.venv-pyghidra311\Scripts\python.exe -m tools.pyghidra_crusader ...from the repo root. - Keep PyGhidra batches small too: prefer one focused repair plan or 1-5 direct edits at a time.
- Write operations require the Ghidra project to open successfully. If
Crusader.lockis present because the GUI owns the project, close Ghidra first or operate on a project copy.
Current Verified Raw-Import Ports
0006:e5d0=cursor_update_hoverfrom seg0010x00600008:7377=entity_count_by_type_afrom seg0210x02070007:28ce=shot_entity_allocfrom seg0010x435e0007:2a19=shot_entity_freefrom seg0010x44a90007:2bc9=projectile_init_vectorfrom seg0010x46590007:3001=entity_fire_weaponfrom seg0010x4a910007:3088=fire_weapon_from_cursorfrom seg0010x4b180007:30e8=projectile_check_hitfrom seg0010x4b780007:319e=projectile_step_updatefrom seg0010x4c2e0007:3298=projectile_trace_rayfrom seg0010x4d280007:371d=projectile_update_tickfrom seg0010x51ad0007:4009=projectile_apply_hitfrom seg0010x5a99
Named 000e: Functions (direct analysis — not segment-ported)
Parser Cluster (000e:34xx–38xx)
000e:345e=record_table_init000e:34cc=record_table_destroy000e:35c6=record_table_release_buffer000e:35ef=record_table_next_slot000e:3639=record_table_parse_buffer000e:3798=record_parser_read_line000e:38f8=record_parser_find_marker
RIFF/Animation Cluster (000e:03xx–2xxx)
000e:2a28=riff_find_chunk_by_type(RIFF LIST/RIFF walker; FourCC match at chunk+8)000e:2104=animation_start(finds "movi" chunk, inits timing ring buffer, kicks advance)000e:12f4=animation_advance_frame(fixed-point 0x1000 timer stepper, ring buffer update)000e:103f=animation_tick(guard wrapper — checks +0xd4 != -1, calls advance_frame)000e:06f7=anim_load_audio_frame(checks "01wb" chunk tag 0x62773130, copies audio into ring buffer)
Constructor/Assert Helpers (000e:22xx–29xx)
000e:223d=assert_alive_sentinel(expects +0xd4 == -1; traps on mismatch)000e:2777=animation_ctor_variant_a(alloc + init flags + chained init/assert/finalize)000e:2860=animation_ctor_variant_b(variant A with extra +0x109 init)000e:2969=animation_ctor_variant_c(default static flag profile +0x4c=0xd)