PSX Research

This commit is contained in:
Marco 2026-04-13 16:50:28 +02:00
commit 8d34c85c22
13 changed files with 1720 additions and 8 deletions

View file

@ -0,0 +1,109 @@
---
description: 'Workflow for converting live Ghidra NE patch experiments into raw executable patcher definitions'
applyTo: '**/patch_*.ps1, **/patch_*.md, **/docs/**/*.md'
---
# Raw NE Patch Conversion
Use these instructions when a live Ghidra byte-patch experiment needs to become a reusable raw executable patch in a PowerShell patcher.
## Goal
- Treat the live Ghidra patch as the prototype, not the shipping artifact.
- Convert the final behavior into raw file-byte edits and NE relocation edits that a patcher script can apply and restore deterministically.
- Do not depend on the Ghidra export processor to preserve byte edits unless that exact processor path has already been verified on the target binary.
## Required Capture From Ghidra
Before writing the patcher definition, capture all of these from the live session:
- Final target program name and whether it was a writable copy.
- Every patched selector address and the final intended behavior at that site.
- Exact original bytes and exact patched bytes for any plain code/data window.
- Every far call or far jump target change, including both the source address and the final target selector:offset.
- Any overwritten helper window boundaries, especially when the patch replaces existing functions instead of using empty space.
- Any branch-family coverage requirements, such as seasonal paths or alternate message lanes.
If the patch needs to be restorable, also capture the exact original bytes and the exact original relocation-record payloads.
## NE Conversion Rules
### Selector To Segment Index
For these Crusader NE executables, convert a selector-style code address to the NE segment index with:
```text
segment_index = ((selector - 0x1000) / 8) + 1
```
Use the executable's NE header to resolve the segment table entry, alignment shift, segment file offset, segment length, and relocation table location.
### Raw Code/Data Bytes
For a plain byte rewrite inside one code segment:
```text
raw_file_offset = segment_file_offset + segment_relative_offset
```
Use the exact original byte window for verification and restore.
### Relocation-Bearing Far Calls
For NE `CALLF` and other relocation-backed far operands:
- Patch the relocation record, not the placeholder immediate bytes in the opcode.
- The relocation source offset is the operand location, which starts one byte after the `9A` opcode.
- Keep the opcode bytes as `9A FF FF 00 00` unless the instruction itself is being replaced.
- Restore must write the retail relocation payload back verbatim.
### Overwritten Windows With Existing Relocations
If you overwrite a helper window that already contains relocation-backed far calls:
- Enumerate every relocation record whose source offset falls inside the overwritten range.
- Either preserve that source offset as a far-call slot in the new helper or retarget it explicitly.
- Never leave a stale relocation entry attached to bytes that are no longer a relocation-bearing instruction operand.
- Prefer reusing existing relocation slots over inventing new ones.
This is the safest way to convert a live in-memory proof patch into a raw NE patch without rewriting relocation tables structurally.
## Patcher-Script Rules
- Define each raw patch site with `Offset`, `Original`, and `Enabled` bytes.
- For large helper windows, keep the full original byte block in the script so restore is exact.
- For relocation-record edits, store the full 8-byte record image, not only the target segment/offset words.
- Group related sites into one named patch family with one status function and one apply/restore function.
- Status must accept both retail bytes and fully patched bytes. Any other state is unknown and must block writes.
- Restore must always write the complete retail state for the whole family, even if an older or partial patch was enabled first.
## Validation Checklist
After converting a Ghidra patch into a raw patcher definition:
1. Verify the raw offsets against the current retail executable.
2. Verify every `Original` byte block matches the live unpatched file.
3. Apply the patch through the script, then re-read every site and confirm the `Enabled` bytes match exactly.
4. Confirm all overlapping relocation-backed callsites decode to the intended targets.
5. If the patch has alternate trigger branches, test each one or document the untested branch explicitly.
6. Keep the runtime test order staged so failures isolate bootstrap, UI open, and seeded-runtime behavior separately.
## Documentation Requirements
When a patch graduates from Ghidra prototype to raw patcher support:
- Update the working note with the final raw offsets and what each site does.
- Record any helper-window overwrite boundaries and reused relocation slots.
- Record any export limitation that forced the raw-patch conversion.
- If MCP friction caused extra work, append a concise entry to the wishlist with the missing capability and the manual fallback.
## Example Pattern
The Regret `debug menu 2.0` patch is the reference model for this workflow:
- one raw helper-window rewrite inside segment `13f8`
- one wrapper relocation retarget into that helper
- two helper relocation retargets that reuse the overwritten window's existing far-call slots
- multiple `loosecannon` fixup retargets so all trigger branches land in the debugger modal path
Use that pattern when a live Ghidra proof patch needs to become a stable, restorable patcher option.