mirror of
https://github.com/Momoko-Ayase/Senbei.git
synced 2026-09-19 03:57:59 -04:00
refactor: consolidate platform engines into senbei-engine
This commit is contained in:
+25
-133
@@ -1,163 +1,55 @@
|
||||
# Usage
|
||||
|
||||
```
|
||||
senbei <file|folder> [--out DIR] [-v|--verbose] [-q|--quiet]... [--scan-all]
|
||||
[--no-log] [--no-pause] [-V|--version] [-h|--help]
|
||||
```text
|
||||
senbei <file|folder> [--out DIR] [-v|--verbose] [-q|--quiet]... [--scan-all] [--no-log] [--no-pause] [-V|--version] [-h|--help]
|
||||
```
|
||||
|
||||
Real runs print `Senbei <version>` once at start. Use `-V` / `--version` to
|
||||
print the version and exit.
|
||||
## Single File
|
||||
|
||||
## Single file
|
||||
|
||||
The decrypted image is written under `<parent>/unpack/` with `.unpack` inserted
|
||||
before the extension. A `senbei-<timestamp>.log` is written in the same
|
||||
directory. With `--out DIR`, both the output and the log go into `DIR` instead:
|
||||
The output is written below `<parent>/unpack/` with `.unpack` inserted before the extension. `--out DIR` changes both the output and log directory.
|
||||
|
||||
```cmd
|
||||
senbei app.exe
|
||||
:: -> unpack\app.unpack.exe
|
||||
:: -> unpack\senbei-YYYYMMDD-HHMMSS.log
|
||||
|
||||
senbei app.exe --out C:\out
|
||||
:: -> C:\out\app.unpack.exe
|
||||
:: -> C:\out\senbei-YYYYMMDD-HHMMSS.log
|
||||
```
|
||||
|
||||
Pointing senbei directly at an il2cpp `global-metadata.dat` rewrites its
|
||||
obfuscated method tokens back to the contiguous per-module range il2cpp
|
||||
expects; the output is `global-metadata.unpack.dat`, written only when tokens
|
||||
actually changed. Only metadata format version 31 is rewritten; other versions
|
||||
are reported and left untouched.
|
||||
For `global-metadata.dat`, Senbei writes `global-metadata.unpack.dat` only when method tokens change. Unsupported metadata versions remain untouched and are reported as skipped.
|
||||
|
||||
## Android targets
|
||||
## Android Targets
|
||||
|
||||
Senbei also restores Android (AArch64) protected shared libraries and app
|
||||
packages:
|
||||
Protected `.so` files are restored from their encrypted payload sections and written as `libil2cpp.unpack.so` or the corresponding input name. APK, APKS, and XAPK files are treated as containers: their manifests are read first, nested APKs are followed when necessary, and only `.so` and exact `global-metadata.dat` entries are extracted.
|
||||
|
||||
- **`.so`** — a protected library is hollowed out on disk: its original
|
||||
sections live in an encrypted payload appended to the file, and senbei
|
||||
rebuilds the static image from it. Output: `libil2cpp.unpack.so`.
|
||||
- **`.apk`** — entries are extracted to a temporary workspace and
|
||||
content-probed like loose files; protected libraries and metadata blobs
|
||||
inside are restored to `<out>/<apk name>/<entry path>`.
|
||||
- **`.apks` / `.xapk`** — split-package bundles; each nested `.apk` is opened
|
||||
and searched the same way, under `<out>/<bundle name>/<split name>/...`.
|
||||
If a restored library contains embedded metadata, the unwrapped blob is written beside it as `global-metadata.unpack.dat`. Identical loose and package entries are restored once, preferring the loose file.
|
||||
|
||||
When a restored il2cpp library carries its metadata embedded in its data
|
||||
section (no standalone `global-metadata.dat` in the app at all), senbei
|
||||
unwraps the blob and writes it next to the library as
|
||||
`global-metadata.unpack.dat`. One observed packaging variant wraps the blob in
|
||||
a per-word XOR layer whose keys are generated at runtime and stored nowhere;
|
||||
senbei ships the keystream recovered from the one build known to use it and
|
||||
content-probes for it — builds with a different keystream are silently
|
||||
skipped (the library itself is still fully restored).
|
||||
## Folder Mode
|
||||
|
||||
The same content may appear loose in a folder, in its `.apk`, and in a bundle
|
||||
side by side: identical content is restored once, at the loose file's
|
||||
destination. A restored library is validated structurally by the restore
|
||||
itself (the rebuild refuses inconsistent layouts); a protected library that
|
||||
fails validation counts as an error, not a suspect.
|
||||
Folder mode walks recursively, skips directories named `unpack`, and mirrors recognized outputs below `<root>/unpack/` or `--out DIR`. Windows candidates are `.exe`, `.dll`, and `global-metadata.dat`; Android candidates are `.so` and `global-metadata.dat`. A matching `.exe._` or `.dll._` payload is consumed by its stub and is excluded from the skipped count.
|
||||
|
||||
## Folder mode
|
||||
The summary has the form `12 unpacked · 3 skipped · 0 errors · 1 suspect · 2 metadata`; the package count is appended when packages were opened. Each file is isolated so one failed target does not stop the folder run.
|
||||
|
||||
Senbei walks the directory recursively, skips any subdirectory literally named
|
||||
`unpack`, and unpacks every file it recognises as protected (by content, not
|
||||
extension — renamed files and `.bak` backups are still found; packages are the
|
||||
one exception, recognised by extension plus the zip magic because they are
|
||||
containers). Results land under `<root>/unpack/` (or `--out DIR`), mirroring
|
||||
the input tree's relative paths. The run log is written **in that same out
|
||||
directory**:
|
||||
## Integrity Check
|
||||
|
||||
```cmd
|
||||
senbei "C:\Games\MyGame"
|
||||
:: -> C:\Games\MyGame\unpack\...
|
||||
:: -> C:\Games\MyGame\unpack\senbei-YYYYMMDD-HHMMSS.log
|
||||
```
|
||||
PE outputs are checked for valid headers, section ranges, entry-point mapping, readable import names, relocation requirements, and managed metadata signatures. Android outputs are validated during ELF restoration, including decoded container sizes, fixup bounds, and rebuilt dynamic tables.
|
||||
|
||||
Folder mode also picks up `global-metadata.dat` files and external-companion
|
||||
`._` payloads: a module whose `<name>._` sibling matches its header region is
|
||||
spliced with the companion automatically (no flag needed) and unpacked as one
|
||||
image, with the output named for the stub.
|
||||
|
||||
Each file is processed in isolation: an error or panic on one file is caught,
|
||||
counted, and logged, and the run continues. Folder mode finishes with a summary
|
||||
line, then duration:
|
||||
|
||||
```
|
||||
12 unpacked · 3 skipped · 0 errors · 1 suspect · 2 metadata
|
||||
done in 1234 ms
|
||||
```
|
||||
|
||||
The `packages` count appears (as `· N packages`) only when Android app
|
||||
packages were processed.
|
||||
|
||||
## Integrity check
|
||||
|
||||
(PE outputs only — Android restores carry their own structural validation; see
|
||||
[Android targets](#android-targets).)
|
||||
|
||||
A successful unpack is not always a runnable one: a layout heuristic can pick
|
||||
the wrong offset and leave the entry-point stub or import strings encrypted, so
|
||||
the pipeline reports success but the OS loader faults at runtime (typically
|
||||
`0xC0000005`, STATUS_ACCESS_VIOLATION). To catch this, senbei runs a static
|
||||
sanity check over every output it produces — inspecting the bytes alone, with
|
||||
no reference image and no execution.
|
||||
|
||||
It flags only defects that cannot occur in a correctly unpacked image:
|
||||
|
||||
- malformed DOS/PE headers, bad optional-header magic, implausible section
|
||||
count, zero `SizeOfImage`, or section raw-data ranges that run past EOF;
|
||||
- an entry point that doesn't map into a section, isn't in an executable
|
||||
section, or whose stub is all zeros or all `0xCC` int3 padding (the classic
|
||||
left-encrypted symptom);
|
||||
- a native (unmanaged) DLL with no base-relocation directory — it cannot
|
||||
survive being mapped at a non-preferred base;
|
||||
- **any** import descriptor whose DLL name doesn't resolve or isn't readable
|
||||
ASCII (imports left encrypted) — the whole table is walked, not just the
|
||||
first entry;
|
||||
- for a managed assembly, a COR20 header whose `cb` isn't `0x48` or a
|
||||
MetaData stream missing its `BSJB` signature (the CLR would reject the
|
||||
image outright).
|
||||
|
||||
The entry-point and import checks are skipped for managed assemblies, whose
|
||||
native EP and import stub are legitimately not what the native loader expects.
|
||||
|
||||
The check is deliberately conservative: a clean report is **not** a proof of
|
||||
correctness, but a non-clean report is a reliable "this is broken" signal. A
|
||||
suspect file is still written (the bytes are the best available) and flagged —
|
||||
single-file mode prints a warning to stderr, folder mode prints a yellow `!`
|
||||
line, adds a `SUSPECT` entry to the run log, and counts it in the summary's
|
||||
`suspect` total (which is additive to `unpacked`).
|
||||
A clean report is not a proof of correctness, but a non-clean report is a reliable broken-output signal. Suspect PE files are still written and counted separately.
|
||||
|
||||
## Flags
|
||||
|
||||
| Flag | Behavior |
|
||||
| --- | --- |
|
||||
| `--out DIR` | Write outputs (and the log, unless `--no-log`) under `DIR`. |
|
||||
| `-v`, `--verbose` | Print detailed per-stage progress (and the destination path) for each file — `[N/9]` stages for PE targets, container/segment lines for Android libraries. In folder mode this replaces the progress bar. |
|
||||
| `-q`, `--quiet` | Once: hide progress bar and per-file lines; keep banner, summary, and duration. Twice (`-q -q`): suppress all stdio (exit code only). |
|
||||
| `--no-log` | Do not write `senbei-*.log`. Console output is unchanged by this flag alone. |
|
||||
| `--scan-all` | Probe every file in a folder, including ones the scan pre-filter skips (under 4128 bytes, or a bulk-asset extension like `.ab`/`.xml`/`.acb`). Much slower on large game trees; finds the same targets in practice. |
|
||||
| `--no-pause` | Skip the "Press Enter to exit" prompt (for scripted runs). |
|
||||
| `-V`, `--version` | Print `Senbei <version>` and exit. |
|
||||
| `--out DIR` | Write outputs and logs below `DIR`. |
|
||||
| `-v`, `--verbose` | Print per-stage progress. |
|
||||
| `-q`, `--quiet` | Hide progress and per-file lines; repeat to suppress all standard output. |
|
||||
| `--no-log` | Do not write a run log. |
|
||||
| `--scan-all` | Probe every selected target-name candidate, including files below the size floor. |
|
||||
| `--no-pause` | Disable the Explorer-friendly Windows exit prompt. |
|
||||
| `-V`, `--version` | Print the version and exit. |
|
||||
| `-h`, `--help` | Show usage. |
|
||||
|
||||
On Windows, when launched from Explorer (the process owns its console) senbei
|
||||
pauses for Enter before exiting so the window doesn't vanish. `--no-pause`
|
||||
disables this; it has no effect when stdout is piped or run from another
|
||||
process.
|
||||
|
||||
## Exit codes
|
||||
## Exit Codes
|
||||
|
||||
| Code | Meaning |
|
||||
| --- | --- |
|
||||
| `0` | Success (single file restored, or folder run with no errors). |
|
||||
| `1` | At least one file failed, a scan probe was unreadable, or a single-file unpack errored. |
|
||||
| `2` | Usage error: no path given, unknown option, missing `--out` value, or multiple input paths (help printed). |
|
||||
|
||||
A folder run also fails with `1` when parts of the tree could not be scanned
|
||||
(unreadable directory entries or files that failed the content probe) — those
|
||||
are potential missed targets, not clean skips. An il2cpp metadata blob whose
|
||||
format version senbei does not handle is *not* an error: it is reported, left
|
||||
untouched, and counted as skipped.
|
||||
| `0` | The requested restore completed without errors. |
|
||||
| `1` | A target failed, a scan probe was unreadable, or a single-file restore errored. |
|
||||
| `2` | The command line was invalid. |
|
||||
|
||||
Reference in New Issue
Block a user