From 5d32c35ad7e7bc3a12843c2e0528c0f01abec5d1 Mon Sep 17 00:00:00 2001 From: techmetx11 Date: Tue, 30 Apr 2024 15:51:50 +0100 Subject: [PATCH] Done implementing everything --- src/jobs.rs | 207 +++++++++++++++++++++++++++++++++++++++++++++++----- src/main.rs | 32 ++++++-- 2 files changed, 215 insertions(+), 24 deletions(-) diff --git a/src/jobs.rs b/src/jobs.rs index 1fb2474..c280d63 100644 --- a/src/jobs.rs +++ b/src/jobs.rs @@ -1,16 +1,13 @@ use regex::Regex; -use rquickjs::{async_with, AsyncContext, AsyncRuntime, Exception, FromJs, IntoJs}; -use std::{num::NonZeroUsize, pin::Pin, sync::Arc, thread::available_parallelism}; -use tokio::{ - io::{AsyncWrite, AsyncWriteExt, BufWriter}, - net::{unix::WriteHalf, UnixStream}, - runtime::Handle, - sync::Mutex, - task::block_in_place, -}; +use rquickjs::{async_with, AsyncContext, AsyncRuntime}; +use std::{num::NonZeroUsize, sync::Arc, thread::available_parallelism}; +use tokio::{io::AsyncWriteExt, runtime::Handle, sync::Mutex, task::block_in_place}; use tub::Pool; -use crate::consts::{NSIG_FUNCTION_ARRAY, NSIG_FUNCTION_NAME, REGEX_PLAYER_ID, TEST_YOUTUBE_VIDEO}; +use crate::consts::{ + NSIG_FUNCTION_ARRAY, NSIG_FUNCTION_NAME, REGEX_HELPER_OBJ_NAME, REGEX_PLAYER_ID, + REGEX_SIGNATURE_FUNCTION, REGEX_SIGNATURE_TIMESTAMP, TEST_YOUTUBE_VIDEO, +}; pub enum JobOpcode { ForceUpdate, @@ -45,13 +42,18 @@ impl From for JobOpcode { pub struct PlayerInfo { nsig_function_code: String, + sig_function_code: String, + sig_function_name: String, + signature_timestamp: u64, player_id: u32, } pub struct JavascriptInterpreter { js_runtime: AsyncRuntime, + sig_context: AsyncContext, nsig_context: AsyncContext, - player_id: Mutex, + sig_player_id: Mutex, + nsig_player_id: Mutex, } impl JavascriptInterpreter { @@ -63,10 +65,17 @@ impl JavascriptInterpreter { .block_on(AsyncContext::full(&js_runtime)) .unwrap() }); + let sig_context = block_in_place(|| { + Handle::current() + .block_on(AsyncContext::full(&js_runtime)) + .unwrap() + }); JavascriptInterpreter { - js_runtime: js_runtime, - nsig_context: nsig_context, - player_id: Mutex::new(0), + js_runtime, + sig_context, + nsig_context, + sig_player_id: Mutex::new(0), + nsig_player_id: Mutex::new(0), } } } @@ -91,7 +100,10 @@ impl GlobalState { GlobalState { player_info: Mutex::new(PlayerInfo { nsig_function_code: Default::default(), + sig_function_code: Default::default(), + sig_function_name: Default::default(), player_id: Default::default(), + signature_timestamp: Default::default(), }), js_runtime_pool: runtime_pool, } @@ -113,13 +125,14 @@ pub async fn process_fetch_update( W: tokio::io::AsyncWrite + Unpin + Send, { let cloned_writer = stream.clone(); - let mut writer = cloned_writer.lock().await; + let mut writer; let global_state = state.clone(); let response = match reqwest::get(TEST_YOUTUBE_VIDEO).await { Ok(req) => req.text().await.unwrap(), Err(x) => { println!("Could not fetch the test video: {}", x); + writer = cloned_writer.lock().await; write_failure!(writer, request_id); return; } @@ -128,6 +141,7 @@ pub async fn process_fetch_update( let player_id_str = match REGEX_PLAYER_ID.captures(&response).unwrap().get(1) { Some(result) => result.as_str(), None => { + writer = cloned_writer.lock().await; write_failure!(writer, request_id); return; } @@ -142,6 +156,7 @@ pub async fn process_fetch_update( if player_id == current_player_id { // Player is already up to date + writer = cloned_writer.lock().await; writer.write_u32(request_id).await; writer.write_u16(0xFFFF).await; return; @@ -156,6 +171,7 @@ pub async fn process_fetch_update( Ok(req) => req.text().await.unwrap(), Err(x) => { println!("Could not fetch the player JS: {}", x); + writer = cloned_writer.lock().await; write_failure!(writer, request_id); return; } @@ -179,6 +195,7 @@ pub async fn process_fetch_update( Ok(x) => x, Err(x) => { println!("Error: nsig regex compilation failed: {}", x); + writer = cloned_writer.lock().await; write_failure!(writer, request_id); return; } @@ -190,7 +207,7 @@ pub async fn process_fetch_update( .get(1) .unwrap() .as_str() - .split(","); + .split(','); let array_values: Vec<&str> = array_content.collect(); @@ -215,11 +232,69 @@ pub async fn process_fetch_update( .as_str(); // Extract signature function name + let sig_function_name = REGEX_SIGNATURE_FUNCTION + .captures(&player_javascript) + .unwrap() + .get(1) + .unwrap() + .as_str(); + + let mut sig_function_body_regex_str: String = String::new(); + sig_function_body_regex_str += sig_function_name; + sig_function_body_regex_str += "=function\\([a-zA-Z0-9_]+\\)\\{.+?\\}"; + + let sig_function_body_regex = Regex::new(&sig_function_body_regex_str).unwrap(); + + let sig_function_body = sig_function_body_regex + .captures(&player_javascript) + .unwrap() + .get(0) + .unwrap() + .as_str(); + + // Get the helper object + let helper_object_name = REGEX_HELPER_OBJ_NAME + .captures(sig_function_body) + .unwrap() + .get(1) + .unwrap() + .as_str(); + + let mut helper_object_body_regex_str = String::new(); + helper_object_body_regex_str += "var "; + helper_object_body_regex_str += helper_object_name; + helper_object_body_regex_str += "=\\{(?>.|\\n)+?\\}\\};"; + + let helper_object_body_regex = Regex::new(&helper_object_body_regex_str).unwrap(); + let helper_object_body = helper_object_body_regex + .captures(&player_javascript) + .unwrap() + .get(0) + .unwrap() + .as_str(); + + let mut sig_code = String::new(); + sig_code += helper_object_body; + sig_code += sig_function_body; + + // Get signature timestamp + let signature_timestamp: u64 = REGEX_SIGNATURE_TIMESTAMP + .captures(&player_javascript) + .unwrap() + .get(1) + .unwrap() + .as_str() + .parse() + .unwrap(); current_player_info = global_state.player_info.lock().await; current_player_info.player_id = player_id; current_player_info.nsig_function_code = nsig_function_code; + current_player_info.sig_function_code = sig_code; + current_player_info.sig_function_name = sig_function_name.to_string(); + current_player_info.signature_timestamp = signature_timestamp; + writer = cloned_writer.lock().await; writer.write_u32(request_id).await; // sync code to tell the client the player had updated writer.write_u16(0xF44F).await; @@ -238,7 +313,6 @@ pub async fn process_decrypt_n_signature( W: tokio::io::AsyncWrite + Unpin + Send, { let cloned_writer = stream.clone(); - let mut writer = cloned_writer.lock().await; let global_state = state.clone(); println!("Signature to be decrypted: {}", sig); @@ -246,7 +320,8 @@ pub async fn process_decrypt_n_signature( let cloned_interp = interp.clone(); async_with!(cloned_interp.nsig_context => |ctx|{ - let mut current_player_id = interp.player_id.lock().await; + let mut writer; + let mut current_player_id = interp.nsig_player_id.lock().await; let player_info = global_state.player_info.lock().await; if player_info.player_id != *current_player_id { @@ -258,6 +333,7 @@ pub async fn process_decrypt_n_signature( } else { println!("JavaScript interpreter error (nsig code): {}", n); } + writer = cloned_writer.lock().await; write_failure!(writer, request_id); return; } @@ -280,18 +356,111 @@ pub async fn process_decrypt_n_signature( } else { println!("JavaScript interpreter error (nsig code): {}", n); } + writer = cloned_writer.lock().await; write_failure!(writer, request_id); return; } }; + writer = cloned_writer.lock().await; + writer.write_u32(request_id).await; writer.write_u16(u16::try_from(decrypted_string.len()).unwrap()).await; writer.write_all(decrypted_string.as_bytes()).await; - writer.flush().await; println!("Decrypted signature: {}", decrypted_string); }) .await; } + +pub async fn process_decrypt_signature( + state: Arc, + sig: String, + stream: Arc>, + request_id: u32, +) where + W: tokio::io::AsyncWrite + Unpin + Send, +{ + let cloned_writer = stream.clone(); + let global_state = state.clone(); + + let interp = global_state.js_runtime_pool.acquire().await; + let cloned_interp = interp.clone(); + + async_with!(cloned_interp.sig_context => |ctx|{ + let mut writer; + let mut current_player_id = interp.sig_player_id.lock().await; + let player_info = global_state.player_info.lock().await; + + if player_info.player_id != *current_player_id { + match ctx.eval::<(),String>(player_info.sig_function_code.clone()) { + Ok(x) => x, + Err(n) => { + if n.is_exception() { + println!("JavaScript interpreter error (sig code): {:?}", ctx.catch().as_exception()); + } else { + println!("JavaScript interpreter error (sig code): {}", n); + } + writer = cloned_writer.lock().await; + write_failure!(writer, request_id); + return; + } + } + *current_player_id = player_info.player_id; + } + + let sig_function_name = &player_info.sig_function_name; + + let mut call_string: String = String::new(); + call_string += sig_function_name; + call_string += "(\""; + call_string += &sig; + call_string += "\")"; + + drop(player_info); + + let decrypted_string = match ctx.eval::(call_string) { + Ok(x) => x, + Err(n) => { + if n.is_exception() { + println!("JavaScript interpreter error (sig code): {:?}", ctx.catch().as_exception()); + } else { + println!("JavaScript interpreter error (sig code): {}", n); + } + writer = cloned_writer.lock().await; + write_failure!(writer, request_id); + return; + } + }; + + writer = cloned_writer.lock().await; + + writer.write_u32(request_id).await; + writer.write_u16(u16::try_from(decrypted_string.len()).unwrap()).await; + writer.write_all(decrypted_string.as_bytes()).await; + + println!("Decrypted signature: {}", decrypted_string); + + }) + .await; +} + +pub async fn process_get_signature_timestamp( + state: Arc, + stream: Arc>, + request_id: u32, +) where + W: tokio::io::AsyncWrite + Unpin + Send, +{ + let cloned_writer = stream.clone(); + let global_state = state.clone(); + + let player_info = global_state.player_info.lock().await; + let timestamp = player_info.signature_timestamp; + + let mut writer = cloned_writer.lock().await; + + writer.write_u32(request_id).await; + writer.write_u64(timestamp).await; +} diff --git a/src/main.rs b/src/main.rs index f519e40..a3bf7e4 100644 --- a/src/main.rs +++ b/src/main.rs @@ -5,14 +5,13 @@ use consts::DEFAULT_SOCK_PATH; use jobs::{process_decrypt_n_signature, process_fetch_update, GlobalState, JobOpcode}; use std::{env::args, io::Error, sync::Arc}; use tokio::{ - io::{self, AsyncReadExt, BufReader, BufStream, BufWriter}, - net::{ - unix::{ReadHalf, WriteHalf}, - UnixListener, UnixStream, - }, + io::{self, AsyncReadExt, BufReader, BufWriter}, + net::{UnixListener, UnixStream}, sync::Mutex, }; +use crate::jobs::{process_decrypt_signature, process_get_signature_timestamp}; + macro_rules! break_fail { ($res:expr) => { match $res { @@ -107,6 +106,29 @@ async fn process_socket(state: Arc, socket: UnixStream) -> Result<( process_decrypt_n_signature(cloned_state, str, cloned_stream, request_id).await; }); } + JobOpcode::DecryptSignature => { + let sig_size: usize = usize::from(eof_fail!( + inside_readstream.read_u16().await, + inside_readstream + )); + let mut buf = vec![0u8; sig_size]; + + break_fail!(inside_readstream.read_exact(&mut buf).await); + + let str = break_fail!(String::from_utf8(buf)); + let cloned_state = state.clone(); + let cloned_stream = cloned_writestream.clone(); + tokio::spawn(async move { + process_decrypt_signature(cloned_state, str, cloned_stream, request_id).await; + }); + } + JobOpcode::GetSignatureTimestamp => { + let cloned_state = state.clone(); + let cloned_stream = cloned_writestream.clone(); + tokio::spawn(async move { + process_get_signature_timestamp(cloned_state, cloned_stream, request_id).await; + }); + } _ => {} } }