more docs
This commit is contained in:
parent
1ad746ba82
commit
a70ec15899
21 changed files with 1357 additions and 25 deletions
|
|
@ -26,6 +26,76 @@ Purpose:
|
|||
| Constructor writes `0x65ab` to object `+0` | `1408:0024..0028` |
|
||||
| `1478:65ab` is method 0 and `1478:65af` is method 1 of the same vtable | `CALLF [BX]` and `CALLF [BX+4]` dispatch paths |
|
||||
|
||||
## Minimum Viable Patch Floor (2026-04-03)
|
||||
|
||||
Fresh live-Ghidra re-checks tighten the lower bound on what an executable-side debugger-unlock patch must do.
|
||||
|
||||
What the live binary still proves:
|
||||
- `1408:0000` is only a constructor. It allocates/initializes the seg1408 debugger object, seeds object `+0` with `0x65ab`, and returns the far pointer in `DX:AX`; it does **not** store that pointer into `1478:659c/659e`.
|
||||
- current instruction searches still show reads of `1478:659c/659e` in seg13a0 and seg1418, but no recovered retail writer that seeds those globals before the interpreter hook runs.
|
||||
- raw debugger-adjacent data at `1478:65ab/65af` still resolves to the two inert retail callbacks `1408:046f` and `1408:0474`; there is no hidden already-live UI-opening target sitting behind the stock vtable.
|
||||
- the interpreter-side pre-call guard at `1418:049e..04b5` only checks whether `1478:659c/659e` is non-null before calling `1408:0053`. The one-shot breakpoint/step gating lives inside `1408:0053`, not at the callsite itself.
|
||||
- both UI wrappers still inherit caller words at `13a0:008f` / `13a0:024a`, so any deferred entry that reuses those wrappers from a non-native caller still needs wrapper-argument sanitization.
|
||||
|
||||
That rules out the common "there must be one tiny direct jump" theories:
|
||||
- a constructor-only retarget is insufficient because the returned far pointer still has to be stored into `1478:659c/659e` somewhere.
|
||||
- a vtable-dword-only patch is insufficient because the retail callbacks at `1478:65ab/65af` are inert shared stubs, and the previously tested shared-slot rewrites already crashed at startup.
|
||||
- a direct `1418:04b5 -> 13a0:020d/0086` retarget is insufficient because once `1478:659c/659e` becomes non-null, that callsite would fire on every eligible interpreter pass unless a private one-shot stub preserves the original `1408:0053` gating semantics.
|
||||
|
||||
Current best conclusion:
|
||||
- the smallest structurally defensible patch family is still the current interpreter-callsite-retarget design (Candidates O/P): one embedded `13e8:230d..232d` body that lazily creates/stores/arms the debugger object, one retarget of `1418:04b5` into the private stub, and one wrapper-argument sanitization site (`13a0:024a` or `13a0:008f`).
|
||||
- anything smaller than that is currently missing at least one required behavior: object creation, global pointer seeding, one-shot deferred gating, or safe wrapper arguments.
|
||||
|
||||
## Practical Candidate Byte Maps
|
||||
|
||||
These are the current live-candidate edits in practical patching terms.
|
||||
|
||||
Important NE note:
|
||||
- for internal far calls, the on-disk opcode bytes stay as placeholder `FF FF 00 00`
|
||||
- the real old/new target change is in the NE fixup entry, not in the immediate bytes themselves
|
||||
- so for those sites below, `raw bytes` may be unchanged while the `fixup target` changes
|
||||
|
||||
### Common edits shared by Candidate O and Candidate P
|
||||
|
||||
| File offset | Live address | What changes | Old value | New value |
|
||||
|-------------|--------------|--------------|-----------|-----------|
|
||||
| `0x0C970D` | `13e8:230d` | Replace the retail `0x410` body with the corrected private bootstrap/stub body | `A0 4F 60 B4 00 F7 D8 1B C0 40 A2 4F 60 80 3E 4F 60 00 74 47 6A FF 6A FF C4 1E D0 4C 26 8A 47 05 50 1E 68 D2 60 6A 00 6A 00 83 EC 06 C7 86 76 FF 00 00 8B 86 76 FF F7 D0 89 86 78 FF C6 86 7A FF 00 6A 00 6A 00 9A FF FF 00 00 83 C4 14 52 50 9A FF FF 00 00 83 C4 08 5F 5E C9 CB 6A FF 6A FF C4 1E D0 4C 26 8A 47 05 50 1E 68 EE 60 6A 00 6A 00 83 EC 06 C7` | `A1 9C 65 0B 06 9E 65 74 10 C4 1E 9C 65 C6 47 75 00 C6 47 74 01 5F 5E C9 CB 6A 00 6A 00 E9 25 00 55 8B EC A1 9C 65 39 46 06 75 16 A1 9E 65 39 46 08 75 0E C4 5E 06 C7 47 74 00 00 6A 00 6A 00 EB 0E 5D CB 90 90 9A FF FF 00 00 83 C4 04 EB 0A 9A FF FF 00 00 83 C4 04 5D CB 0B C2 74 13 A3 9C 65 89 16 9E 65 89 C3 8E C2 C6 47 75 00 C6 47 74 01 5F 5E C9 CB` |
|
||||
| `0x0C9753` | `13e8:2352` | First reused far-call fixup inside the patched `0x410` body | fixup target `1350:0046` | fixup target `1408:0000` |
|
||||
| `0x0CFAB5` | `1418:04b5` | Interpreter debugger callsite retarget | raw bytes `9A FF FF 00 00`; fixup target `1408:0053` | raw bytes `9A FF FF 00 00`; fixup target `13e8:232d` |
|
||||
|
||||
### Candidate O only
|
||||
|
||||
| File offset | Live address | What changes | Old value | New value |
|
||||
|-------------|--------------|--------------|-----------|-----------|
|
||||
| `0x0C975D` | `13e8:235c` | Second reused far-call fixup inside the patched `0x410` body | raw bytes `FF FF 00 00`; fixup target `1320:1588` | raw bytes `FF FF 00 00`; fixup target `13a0:020d` |
|
||||
| `0x0B9C48` | `13a0:0248` | Zero inherited modal-wrapper caller words while preserving the leading local default `PUSH 0` | `6A 00 FF 76 08 FF 76 06` | `6A 00 6A 00 6A 00 90 90` |
|
||||
|
||||
Practical meaning:
|
||||
- the patched `0x410` body now creates/stores the debugger object if needed, or reuses the live one and arms break-next
|
||||
- the interpreter callback at `1418:04b5` no longer enters `1408:0053` directly; it enters the private stub at `13e8:232d`
|
||||
- that private stub eventually uses the repointed second far-call slot at `13e8:235c` to open `usecode_debugger_open_modal`
|
||||
|
||||
### Candidate P only
|
||||
|
||||
| File offset | Live address | What changes | Old value | New value |
|
||||
|-------------|--------------|--------------|-----------|-----------|
|
||||
| `0x0C975D` | `13e8:235c` | Second reused far-call fixup inside the patched `0x410` body | raw bytes `FF FF 00 00`; fixup target `1320:1588` | raw bytes `FF FF 00 00`; fixup target `13a0:0086` |
|
||||
| `0x0B9A8D` | `13a0:008d` | Zero inherited current-unit-wrapper caller words while preserving the leading mode byte `PUSH 1` | `6A 01 FF 76 08 FF 76 06` | `6A 01 6A 00 6A 00 90 90` |
|
||||
|
||||
Practical meaning:
|
||||
- same bootstrap and interpreter-callsite retarget as Candidate O
|
||||
- the only behavioral difference is the final UI target: this one routes to `usecode_debugger_open_for_current_unit` instead of the generic modal wrapper
|
||||
|
||||
### Restore values for the current live-candidate family
|
||||
|
||||
If reverting O/P back to retail, these are the practical targets:
|
||||
- `0x0C970D` / `13e8:230d`: restore the original retail `0x410` body bytes shown above
|
||||
- `0x0C9753` / `13e8:2352`: restore fixup target `1350:0046`
|
||||
- `0x0CFAB5` / `1418:04b5`: restore fixup target `1408:0053`
|
||||
- `0x0C975D` / `13e8:235c`: restore fixup target `1320:1588`
|
||||
- `0x0B9C48` / `13a0:0248`: restore `6A 00 FF 76 08 FF 76 06` if Candidate O was active
|
||||
- `0x0B9A8D` / `13a0:008d`: restore `6A 01 FF 76 08 FF 76 06` if Candidate P was active
|
||||
|
||||
## Attempt Log
|
||||
|
||||
| ID | Patch shape | Mechanical result | Runtime result | Verdict |
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue