Files
onionHEN/docs/shellui-injection.md

104 lines
4.4 KiB
Markdown

# ShellUI injection contract
This document defines the active ShellUI injection path, its synchronization
rules, and its failure behavior.
## Runtime flow
```text
daemon: cmd_enable_toolbox() [daemon/source/daemon_inject.cpp]
|- get_shellui_pid() find SceShellUI
`- Inject_Toolbox(pid, shellui.elf) [libNineS/src/main.c]
`- inject_elf(proc, elf) [libNineS/src/injector.c]
|- set_ucred_to_ptrace() once for the inject window
|- pt_attach(SceShellUI)
|- init_remote_function_pointers()
|- elfldr_load()
|- elfldr_payload_args()
|- map and copy the bootstrap stager
|- map and copy the SCEFunctions block
|- pt_call2(bootstrap) -> remote pthread_create(elf_main)
`- pt_detach() and restore the caller authid
```
The remote stager terminates with `int3`. `pt_call2` waits for that stop before
restoring the interrupted ShellUI registers.
## Readiness identity
After every required hook is ready, `shellui.elf` writes its PID to:
```text
/system_tmp/onionhen/ready/toolbox
```
The daemon waits up to 45 seconds for the expected PID. The marker is an
instance identity, not a boolean flag:
- matching PID means the current ShellUI instance is initialized;
- missing or different PID requires injection into the current process;
- a PID change during the handshake invalidates the acknowledgement.
`ToolboxInject` serializes the identity check, injection, and readiness wait.
Cold start and util reinjection therefore cannot ptrace the same ShellUI
process concurrently.
After Rest Mode, daemon observes the new `NPXS40087` process through SceSysCore
`NOTE_EXEC`, waits for `libSceNpTrophy.sprx` and `libSceNpTrophy2.sprx`, applies
`rest_mode.resume_reinject_delay_seconds`, and then runs the same serialized
injection flow. The extra delay is rest-resume only; cold start and retries
skip it.
## Injection invariants
| Area | Contract |
|------|----------|
| Authid | `inject_elf` enters one `PTRACE_AUTHID` window and restores the caller's authid on every exit path |
| ptrace | Calls execute inside that credential window without per-call authid changes |
| Remote calls | `pt_call`, `pt_call2`, and `pt_syscall` use an ABI-aligned private stack below the interrupted frame and outside the 128-byte red zone |
| Register restore | `pt_call2` waits for the stager stop before restoring saved registers |
| Mapping | Null, `MAP_FAILED`, mprotect, and copyin failures abort injection |
| Attach state | A successful detach clears the attached state; cleanup detaches whenever an attach completed |
| Return value | Any failed stage returns failure to the daemon and cannot publish Toolbox readiness |
Single-step return detection compares against the private entry stack pointer.
The invariant is `rsp % 16 == 8` at the remote call boundary.
## Hook installation safety
ShellUI remains active while the payload thread installs hooks. Two mechanisms
keep callbacks coherent during that window:
- `TrampolineArena` uses non-fixed mmap hints and page-sized bump allocation.
Every trampoline has a unique, non-overlapping address within rel32 reach of
its target. Allocation fails closed when no valid mapping is available.
- Hook callbacks remain pass-through while lifecycle state is `Installing`.
The payload publishes `Ready` only after all detours and shared dependencies
are initialized.
Controller, navigation, render, registry, capture, and resource hooks call
their original functions until the lifecycle barrier reaches `Ready`.
## Failure behavior
An injection failure has these observable results:
1. The daemon receives a failed injection result.
2. The target is detached when attachment succeeded.
3. The daemon authid is restored.
4. The expected PID is not published as ready.
5. A later serialized request may retry the current ShellUI instance.
The injector does not kill or respawn ShellUI as part of this operation.
## Source map
| Responsibility | Source |
|----------------|--------|
| Request serialization and PID readiness | `source/daemon/source/daemon_inject.cpp` |
| Injection orchestration | `source/libNineS/src/main.c` |
| ELF mapping and stager launch | `source/libNineS/src/injector.c` |
| Remote ptrace calls | `source/libonion_elfldr/source/pt.c` |
| Trampoline allocation | `source/libonion_detour/source/trampoline_arena.cpp` |
| Hook lifecycle barrier | `source/shellui/src/hook_lifecycle.cpp` |