Split web/ into senbei-wasm crate + static assets

The Rust bindings move from web/src to a top-level senbei-wasm crate (still
outside the workspace, own Cargo.lock), matching the other senbei-* crates.
web/ keeps only the static frontend; wasm-pack emits the JS/wasm package into
web/pkg/ via --out-dir. JS glue renamed senbei_web -> senbei_wasm with the
crate.
This commit is contained in:
2026-08-30 22:44:15 +08:00
parent f80749ffd6
commit 0c3f29f93a
13 changed files with 707 additions and 33 deletions
-463
View File
@@ -1,463 +0,0 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
version = 4
[[package]]
name = "anyhow"
version = "1.0.104"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "330a5ed07fa54e4702c9d6c4174f74427fc0ef6e214bbd677ae50a5099946470"
[[package]]
name = "bumpalo"
version = "3.20.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "72f5acc6cb2ba439de613abc23857ec3d78374d8ed5ac84e9d11336e87da8649"
[[package]]
name = "cfg-if"
version = "1.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801"
[[package]]
name = "console"
version = "0.16.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4fe5f465a4f6fee88fad41b85d990f84c835335e85b5d9e6e63e0d06d28cba7c"
dependencies = [
"encode_unicode",
"libc",
"unicode-width",
"windows-sys",
]
[[package]]
name = "console_error_panic_hook"
version = "0.1.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a06aeb73f470f66dcdbf7223caeebb85984942f22f1adb2a088cf9668146bbbc"
dependencies = [
"cfg-if",
"wasm-bindgen",
]
[[package]]
name = "encode_unicode"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "34aa73646ffb006b8f5147f3dc182bd4bcb190227ce861fc4a4844bf8e3cb2c0"
[[package]]
name = "futures-core"
version = "0.3.34"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "92d699e522242e69e3003b94ecc1f960f3a5e015aa7c5d7486e65ad01dd94f5e"
[[package]]
name = "futures-task"
version = "0.3.34"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cd417de3d1d015fc3bfd2b1ea46dfc7bab72ef86f1cc7cc9c78e728b34a6d1fd"
[[package]]
name = "futures-util"
version = "0.3.34"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0d50a92467f8ba5dd6e3ee5d4bd04d73ab2e4e1c44474a0674821dfce14b79bc"
dependencies = [
"futures-core",
"futures-task",
"pin-project-lite",
"slab",
]
[[package]]
name = "indicatif"
version = "0.18.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9433806cd6b4ec1aba79c021c7e4c58fb4c3b9977c085062e611ac929998fb0c"
dependencies = [
"console",
"portable-atomic",
"unicode-width",
"unit-prefix",
"web-time",
]
[[package]]
name = "js-sys"
version = "0.3.104"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0e0c1080212aad755ea003d18543e8768dd432c48819efd73a7bf1e39b7a5a3a"
dependencies = [
"cfg-if",
"futures-util",
"wasm-bindgen",
]
[[package]]
name = "libc"
version = "0.2.189"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3eaf3ede3fee6db1a4c2ee091bf8a8b4dccdc6d17f656fb07896ee72867612f2"
[[package]]
name = "once_cell"
version = "1.21.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9f7c3e4beb33f85d45ae3e3a1792185706c8e16d043238c593331cc7cd313b50"
[[package]]
name = "owo-colors"
version = "4.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "13c45bb4a6ae1280ec0803b1ef9d3455eb50f01efbbe1447ab020f1d54fba9d8"
[[package]]
name = "pin-project-lite"
version = "0.2.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a89322df9ebe1c1578d689c92318e070967d1042b512afbe49518723f4e6d5cd"
[[package]]
name = "portable-atomic"
version = "1.15.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "05c8b63e8d9609db387f0324918f81d68fe27748f084ef092fb35954d0539a85"
[[package]]
name = "proc-macro2"
version = "1.0.107"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "985e7ec9bb745e6ce6535b544d84d6cd6f7ad8bd711c398938ae983b91a766d9"
dependencies = [
"unicode-ident",
]
[[package]]
name = "quote"
version = "1.0.47"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1fbf4db142a473a8d80c26bbf18454ed458bf8d26c8219c331daecfdbd079001"
dependencies = [
"proc-macro2",
]
[[package]]
name = "rustversion"
version = "1.0.23"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cf54715a573b99ac80df0bc206da022bcd442c974952c7b9720069370852e21f"
[[package]]
name = "same-file"
version = "1.0.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502"
dependencies = [
"winapi-util",
]
[[package]]
name = "senbei-crypto"
version = "1.1.0"
dependencies = [
"thiserror",
]
[[package]]
name = "senbei-io"
version = "1.1.0"
dependencies = [
"anyhow",
"indicatif",
"libc",
"owo-colors",
"senbei-metadata",
"senbei-pe",
"walkdir",
"windows",
]
[[package]]
name = "senbei-metadata"
version = "1.1.0"
[[package]]
name = "senbei-pe"
version = "1.1.0"
dependencies = [
"senbei-crypto",
"thiserror",
]
[[package]]
name = "senbei-web"
version = "1.1.0"
dependencies = [
"console_error_panic_hook",
"senbei-io",
"senbei-metadata",
"senbei-pe",
"wasm-bindgen",
]
[[package]]
name = "slab"
version = "0.4.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0c790de23124f9ab44544d7ac05d60440adc586479ce501c1d6d7da3cd8c9cf5"
[[package]]
name = "syn"
version = "2.0.119"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "872831b642d1a07999a962a351ed35b955ea2cfc8f3862091e2a240a84f17297"
dependencies = [
"proc-macro2",
"quote",
"unicode-ident",
]
[[package]]
name = "syn"
version = "3.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e6275cddf4610d1775e6d1fe9469b2e77d0f39fd98fb7450901b821e0c53649f"
dependencies = [
"proc-macro2",
"quote",
"unicode-ident",
]
[[package]]
name = "thiserror"
version = "2.0.20"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ec86235f5fcc2a73650310756d2ac5b138a5780bbbdfae3eeccec992c435ba4f"
dependencies = [
"thiserror-impl",
]
[[package]]
name = "thiserror-impl"
version = "2.0.20"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bc04cd3e1236dd4a98afca4569f2deb3f120e5422a4023be2cb683f8486292af"
dependencies = [
"proc-macro2",
"quote",
"syn 3.0.4",
]
[[package]]
name = "unicode-ident"
version = "1.0.24"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75"
[[package]]
name = "unicode-width"
version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b4ac048d71ede7ee76d585517add45da530660ef4390e49b098733c6e897f254"
[[package]]
name = "unit-prefix"
version = "0.5.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "81e544489bf3d8ef66c953931f56617f423cd4b5494be343d9b9d3dda037b9a3"
[[package]]
name = "walkdir"
version = "2.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "29790946404f91d9c5d06f9874efddea1dc06c5efe94541a7d6863108e3a5e4b"
dependencies = [
"same-file",
"winapi-util",
]
[[package]]
name = "wasm-bindgen"
version = "0.2.127"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1b70935747edd64d89de3efa29d73789b806c15798f8e7dca4d8ac356b50ce70"
dependencies = [
"cfg-if",
"once_cell",
"rustversion",
"wasm-bindgen-macro",
"wasm-bindgen-shared",
]
[[package]]
name = "wasm-bindgen-macro"
version = "0.2.127"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "77775f8f3f7217702089053b94958f8f54061a3f663417df76e19cbdcca29bc1"
dependencies = [
"quote",
"wasm-bindgen-macro-support",
]
[[package]]
name = "wasm-bindgen-macro-support"
version = "0.2.127"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e11d33f857dc2fb11b8bc75aee111aa9cbeb12cd9f25efd3d4c2a3dd4e235284"
dependencies = [
"bumpalo",
"proc-macro2",
"quote",
"syn 2.0.119",
"wasm-bindgen-shared",
]
[[package]]
name = "wasm-bindgen-shared"
version = "0.2.127"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7ef64dbcc55df09c7e5a46182d181c2cfa3e925f3da937ea764728b4bbb9dcbf"
dependencies = [
"unicode-ident",
]
[[package]]
name = "web-time"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5a6580f308b1fad9207618087a65c04e7a10bc77e02c8e84e9b00dd4b12fa0bb"
dependencies = [
"js-sys",
"wasm-bindgen",
]
[[package]]
name = "winapi-util"
version = "0.1.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22"
dependencies = [
"windows-sys",
]
[[package]]
name = "windows"
version = "0.62.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "527fadee13e0c05939a6a05d5bd6eec6cd2e3dbd648b9f8e447c6518133d8580"
dependencies = [
"windows-collections",
"windows-core",
"windows-future",
"windows-numerics",
]
[[package]]
name = "windows-collections"
version = "0.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "23b2d95af1a8a14a3c7367e1ed4fc9c20e0a26e79551b1454d72583c97cc6610"
dependencies = [
"windows-core",
]
[[package]]
name = "windows-core"
version = "0.62.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b8e83a14d34d0623b51dce9581199302a221863196a1dde71a7663a4c2be9deb"
dependencies = [
"windows-implement",
"windows-interface",
"windows-link",
"windows-result",
"windows-strings",
]
[[package]]
name = "windows-future"
version = "0.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e1d6f90251fe18a279739e78025bd6ddc52a7e22f921070ccdc67dde84c605cb"
dependencies = [
"windows-core",
"windows-link",
"windows-threading",
]
[[package]]
name = "windows-implement"
version = "0.60.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "053e2e040ab57b9dc951b72c264860db7eb3b0200ba345b4e4c3b14f67855ddf"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.119",
]
[[package]]
name = "windows-interface"
version = "0.59.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3f316c4a2570ba26bbec722032c4099d8c8bc095efccdc15688708623367e358"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.119",
]
[[package]]
name = "windows-link"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5"
[[package]]
name = "windows-numerics"
version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6e2e40844ac143cdb44aead537bbf727de9b044e107a0f1220392177d15b0f26"
dependencies = [
"windows-core",
"windows-link",
]
[[package]]
name = "windows-result"
version = "0.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7781fa89eaf60850ac3d2da7af8e5242a5ea78d1a11c49bf2910bb5a73853eb5"
dependencies = [
"windows-link",
]
[[package]]
name = "windows-strings"
version = "0.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7837d08f69c77cf6b07689544538e017c1bfcf57e34b4c0ff58e6c2cd3b37091"
dependencies = [
"windows-link",
]
[[package]]
name = "windows-sys"
version = "0.61.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ae137229bcbd6cdf0f7b80a31df61766145077ddf49416a728b02cb3921ff3fc"
dependencies = [
"windows-link",
]
[[package]]
name = "windows-threading"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3949bd5b99cafdf1c7ca86b43ca564028dfe27d66958f2470940f73d86d75b37"
dependencies = [
"windows-link",
]
-21
View File
@@ -1,21 +0,0 @@
[package]
name = "senbei-web"
version = "1.1.0"
edition = "2024"
description = "WebAssembly browser frontend for senbei"
license = "AGPL-3.0-only"
[lib]
crate-type = ["cdylib"]
[dependencies]
senbei-io = { path = "../senbei-io" }
senbei-metadata = { path = "../senbei-metadata" }
senbei-pe = { path = "../senbei-pe" }
wasm-bindgen = "0.2"
console_error_panic_hook = "0.1"
[profile.release]
opt-level = "z"
lto = true
codegen-units = 1
+10 -9
View File
@@ -46,8 +46,8 @@ including the `wasm32-unknown-unknown` target) and
[wasm-pack](https://rustwasm.github.io/wasm-pack/installer/).
```cmd
cd web
wasm-pack build --target web --release
cd senbei-wasm
wasm-pack build --target web --release --out-dir ../web/pkg
```
This produces `web/pkg/` (git-ignored). Then serve the `web/` directory with
@@ -63,13 +63,14 @@ python -m http.server -d web 8000
## Layout
```
senbei-wasm/ the senbei-wasm cdylib crate (own Cargo.lock, outside the
workspace; depends on the senbei-pe/-io/-metadata crates)
└── src/lib.rs #[wasm_bindgen] bindings: detect / unpack_file /
unpack_file_force_exe / deobfuscate_metadata
web/
├── Cargo.toml senbei-web cdylib crate (depends on the senbei lib)
├── src/lib.rs #[wasm_bindgen] bindings: detect / unpack_file /
│ unpack_file_force_exe / deobfuscate_metadata
├── index.html the page
├── app.js dropzone, file list, worker orchestration, downloads
├── worker.js one-shot unpack worker (fresh wasm instance per file)
├── index.html the page
├── app.js dropzone, file list, worker orchestration, downloads
├── worker.js one-shot unpack worker (fresh wasm instance per file)
├── style.css
└── pkg/ wasm-pack output (git-ignored)
└── pkg/ wasm-pack output (git-ignored; build from senbei-wasm/)
```
+1 -1
View File
@@ -1,4 +1,4 @@
import init, { detect, deobfuscate_metadata } from './pkg/senbei_web.js';
import init, { detect, deobfuscate_metadata } from './pkg/senbei_wasm.js';
const dropzone = document.getElementById('dropzone');
const picker = document.getElementById('picker');
-183
View File
@@ -1,183 +0,0 @@
//! WebAssembly bindings for the senbei unpacker core.
//!
//! Everything here is I/O-free: the browser hands in file bytes and gets
//! unpacked file bytes back. No network, no filesystem, no uploads.
use wasm_bindgen::prelude::*;
/// Install a panic hook that forwards Rust panic messages to the browser
/// console (and to the JS error), instead of a bare `unreachable` trap.
#[wasm_bindgen(start)]
pub fn init_panic_hook() {
console_error_panic_hook::set_once();
}
/// Result of unpacking one protected module.
#[wasm_bindgen]
pub struct UnpackResult {
kind: String,
bytes: Vec<u8>,
suspect: bool,
issues: Vec<String>,
companion: bool,
}
#[wasm_bindgen]
impl UnpackResult {
/// Detected module kind: `"native-exe"`, `"managed-exe"`, `"native-dll"`,
/// or `"managed-dll"`.
#[wasm_bindgen(getter)]
pub fn kind(&self) -> String {
self.kind.clone()
}
/// The unpacked image bytes.
#[wasm_bindgen(getter)]
pub fn bytes(&self) -> Vec<u8> {
self.bytes.clone()
}
/// True when the static integrity check flagged the output as likely
/// broken at runtime. The bytes are still the best available.
#[wasm_bindgen(getter)]
pub fn suspect(&self) -> bool {
self.suspect
}
/// Human-readable integrity defects (empty when the check is clean).
#[wasm_bindgen(getter)]
pub fn issues(&self) -> Vec<String> {
self.issues.clone()
}
/// True when the input was spliced from an external-companion (`._`)
/// payload.
#[wasm_bindgen(getter)]
pub fn companion(&self) -> bool {
self.companion
}
}
/// Result of de-obfuscating an il2cpp `global-metadata.dat`.
#[wasm_bindgen]
pub struct MetadataResult {
bytes: Vec<u8>,
version: u32,
methods: usize,
remapped: usize,
modules: usize,
}
#[wasm_bindgen]
impl MetadataResult {
/// The (possibly rewritten) metadata bytes.
#[wasm_bindgen(getter)]
pub fn bytes(&self) -> Vec<u8> {
self.bytes.clone()
}
#[wasm_bindgen(getter)]
pub fn version(&self) -> u32 {
self.version
}
/// Total method-definition entries in the metadata.
#[wasm_bindgen(getter)]
pub fn methods(&self) -> usize {
self.methods
}
/// Method tokens actually rewritten (0 means the input was already
/// de-obfuscated and the bytes are unchanged).
#[wasm_bindgen(getter)]
pub fn remapped(&self) -> usize {
self.remapped
}
/// Modules (images) owning at least one method.
#[wasm_bindgen(getter)]
pub fn modules(&self) -> usize {
self.modules
}
}
fn kind_str(kind: senbei_pe::Kind) -> &'static str {
match kind {
senbei_pe::Kind::NativeExe => "native-exe",
senbei_pe::Kind::ManagedExe => "managed-exe",
senbei_pe::Kind::NativeDll => "native-dll",
senbei_pe::Kind::ManagedDll => "managed-dll",
}
}
/// Classify a file's bytes without unpacking.
///
/// Returns `"native-exe"`, `"managed-exe"`, `"native-dll"`, `"managed-dll"`,
/// `"metadata"` (an il2cpp `global-metadata.dat`), or `undefined` for
/// anything unrecognized.
#[wasm_bindgen]
pub fn detect(input: &[u8]) -> Option<String> {
if senbei_metadata::is_metadata(input) {
return Some("metadata".to_string());
}
senbei_pe::detect(input).map(|d| kind_str(d.kind).to_string())
}
/// Unpack a protected module.
///
/// `input` is the protected `.exe`/`.dll`; `companion` is the optional
/// `<input>._` external-companion payload (pass `null`/`undefined` when there
/// is none). Throws a string error when the input is not a supported
/// Crackproof file or is corrupt.
#[wasm_bindgen]
pub fn unpack_file(
input: &[u8],
companion: Option<Vec<u8>>,
) -> Result<UnpackResult, JsError> {
let r = senbei_io::job::unpack_bytes(input, companion.as_deref())
.map_err(|e| JsError::new(&e.to_string()))?;
Ok(UnpackResult {
kind: kind_str(r.kind).to_string(),
bytes: r.bytes,
suspect: !r.integrity.ok(),
issues: r.integrity.issues,
companion: r.companion,
})
}
/// De-obfuscate the method tokens of an il2cpp `global-metadata.dat`.
///
/// The transform is idempotent: an already-clean metadata comes back
/// byte-identical with `remapped == 0`. Throws a string error for non-metadata
/// input, an unsupported format version, or a malformed layout.
#[wasm_bindgen]
pub fn deobfuscate_metadata(data: &[u8]) -> Result<MetadataResult, JsError> {
let (bytes, report) =
senbei_metadata::deobfuscate(data).map_err(|e| JsError::new(&e.to_string()))?;
Ok(MetadataResult {
bytes,
version: report.version,
methods: report.methods,
remapped: report.remapped,
modules: report.modules,
})
}
/// Unpack a protected module, forcing the EXE pipeline (no DLL-pipeline
/// probe). See [`senbei_io::job::unpack_bytes_force_exe`] for why the web app
/// needs this recovery path.
#[wasm_bindgen]
pub fn unpack_file_force_exe(
input: &[u8],
companion: Option<Vec<u8>>,
) -> Result<UnpackResult, JsError> {
let r = senbei_io::job::unpack_bytes_force_exe(input, companion.as_deref())
.map_err(|e| JsError::new(&e.to_string()))?;
Ok(UnpackResult {
kind: kind_str(r.kind).to_string(),
bytes: r.bytes,
suspect: !r.integrity.ok(),
issues: r.integrity.issues,
companion: r.companion,
})
}
+1 -1
View File
@@ -10,7 +10,7 @@
// instance is never reused). That reproduces the CLI's
// dll-first/exe-fallback routing without a catchable panic.
import init, { unpack_file, unpack_file_force_exe } from './pkg/senbei_web.js';
import init, { unpack_file, unpack_file_force_exe } from './pkg/senbei_wasm.js';
let ready = null;