Phase 2 — JS deobfuscator (rquickjs + ress)
Port NewPipeExtractor's JS pipeline: player.js fetch + cache, sig and
nsig function extraction, deobfuscation, sticky-error caching.
src/youtube/js/
* runtime.rs — rquickjs wrapper (mirrors utils/JavaScript.java)
compile_or_throw + run(snippet, name, parameter)
* lexer.rs — match_to_closing_brace via the `ress` JS scanner
(NPE's lexer is derived from the same crate
upstream)
* extractor.rs — iframe_api → embed page fallback for player.js
URL, regex-driven hash extraction, clean-and-fetch
* signature.rs — 6 sig fn name regexes (front-most-recent),
deobf-function-body via lexer w/ regex fallback,
helper-object + global-string-array extraction,
signatureTimestamp, snippet assembler
* nsig.rs — 8 nsig fn name regexes (incl. array-indirection),
body via lexer w/ regex fallback, fixupFunction
early-return strip
* player_manager.rs — orchestrator + sticky-error cache mirroring
YoutubeJavaScriptPlayerManager
PORT DEVIATIONS from NPE (each flagged in code):
* dropped the 6th sig fn name regex (used Java backref \2; Rust's
`regex` crate is backtracking-free, so we substitute a loose form
that NPE itself half-broke per audit Track B §2.1)
* dropped the Java atomic group `(?>...)` from helper-object regex —
Rust's NFA is already linear-time
* nsig fixup substitutes `(?:"undefined"|'undefined')` for the
\1 backref; harmless loosening
* sig and nsig assembled snippets prepend `var` — QuickJS rejects
bare-assignment to undeclared identifiers; NPE relied on Rhino's
non-strict mode
Tests:
* 43 lib unit tests (up from 7 in Phase 1)
* 7 Phase 2 offline integration tests against a hand-crafted
minified synthetic player.js — exercises the full sig pipeline
(build_deobfuscator → runtime::run) and nsig fixup_function
* 7 Phase 1 live smoke tests still green
57/57 total green.
This commit is contained in:
parent
2a2367a3d0
commit
6ad0c52aaf
12 changed files with 1536 additions and 0 deletions
62
src/youtube/js/mod.rs
Normal file
62
src/youtube/js/mod.rs
Normal file
|
|
@ -0,0 +1,62 @@
|
|||
// JS deobfuscator subsystem — mirrors NPE's player.js / sig / nsig pipeline.
|
||||
//
|
||||
// Public surface is the `player_manager` module (mirrors NPE's
|
||||
// YoutubeJavaScriptPlayerManager — the sole public class in the subsystem):
|
||||
// * signature_timestamp(video_id)
|
||||
// * deobfuscate_signature(video_id, obfuscated)
|
||||
// * url_with_throttling_parameter_deobfuscated(video_id, url)
|
||||
// * throttling_parameter_cache_size()
|
||||
// * clear_all_caches()
|
||||
// * clear_throttling_parameters_cache()
|
||||
//
|
||||
// Everything else (runtime / lexer / extractor / signature / nsig) is
|
||||
// crate-private plumbing.
|
||||
|
||||
pub mod extractor;
|
||||
pub mod lexer;
|
||||
pub mod nsig;
|
||||
pub mod player_manager;
|
||||
pub mod runtime;
|
||||
pub mod signature;
|
||||
|
||||
use thiserror::Error;
|
||||
|
||||
#[derive(Debug, Error)]
|
||||
pub enum DeobfError {
|
||||
#[error("could not fetch iframe_api: {0}")]
|
||||
FetchIframe(String),
|
||||
#[error("could not fetch embed page: {0}")]
|
||||
FetchEmbed(String),
|
||||
#[error("could not extract player.js URL")]
|
||||
PlayerUrlMissing,
|
||||
#[error("could not fetch player.js: {0}")]
|
||||
FetchPlayerCode(String),
|
||||
#[error("invalid player.js URL: {0}")]
|
||||
InvalidPlayerUrl(String),
|
||||
#[error("could not find sig deobf function via any pattern")]
|
||||
SigFuncNotFound,
|
||||
#[error("could not parse sig deobf function body: {0}")]
|
||||
SigBodyParseFailed(String),
|
||||
#[error("could not find sig helper object")]
|
||||
SigHelperMissing,
|
||||
#[error("could not find sig global array")]
|
||||
SigGlobalArrayMissing,
|
||||
#[error("could not extract signature timestamp")]
|
||||
SigTimestampMissing,
|
||||
#[error("could not find nsig deobf function via any pattern")]
|
||||
NsigFuncNotFound,
|
||||
#[error("could not parse nsig deobf function body: {0}")]
|
||||
NsigBodyParseFailed(String),
|
||||
#[error("nsig array indirection failed: {0}")]
|
||||
NsigArrayLookupFailed(String),
|
||||
#[error("js compile failed: {0}")]
|
||||
JsCompileFailed(String),
|
||||
#[error("js runtime failed: {0}")]
|
||||
JsRuntimeFailed(String),
|
||||
#[error("nsig output was empty (function neutered?)")]
|
||||
NsigEmpty,
|
||||
#[error("downloader not initialized")]
|
||||
DownloaderMissing,
|
||||
}
|
||||
|
||||
pub use player_manager::PlayerManager;
|
||||
Loading…
Add table
Add a link
Reference in a new issue