Files
Senbei/docs/usage.md
T
Momoko-Ayase d3dd1a8ff8 Merge Android (AArch64) shared-library restoration, bump to 1.2.0
Adds the Android protection-scheme pipeline: hollowed ELF64/AArch64
libraries are restored statically (stage-1/stage-2 module extraction,
container decode, dynamic-linker table rebuild), with app-package
(.apk/.apks/.xapk) container handling, cross-source content dedup, and
il2cpp metadata support for the Android variants (seeded RID permutation;
embedded XOR-wrapped blob extraction).

The single senbei CLI now routes single .so files, packages, and folders
by content; outputs follow the existing .unpack-infix naming under
<root>/unpack or --out. PE behavior is unchanged (35/35 goldens).
2026-09-02 03:09:21 +08:00

7.7 KiB

Usage

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

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:

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.

Android targets

Senbei also restores Android (AArch64) protected shared libraries and app packages:

  • .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>/....

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).

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

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:

senbei "C:\Games\MyGame"
:: -> C:\Games\MyGame\unpack\...
:: -> C:\Games\MyGame\unpack\senbei-YYYYMMDD-HHMMSS.log

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.)

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).

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.
-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

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.