PSX Research
This commit is contained in:
parent
2f243976b6
commit
8d34c85c22
13 changed files with 1720 additions and 8 deletions
109
.github/instructions/raw-ne-patch-conversion.instructions.md
vendored
Normal file
109
.github/instructions/raw-ne-patch-conversion.instructions.md
vendored
Normal 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.
|
||||
Loading…
Add table
Add a link
Reference in a new issue