ci: update debug signing secrets
This commit is contained in:
@@ -1,287 +0,0 @@
|
||||
#![allow(dead_code, unused_imports)]
|
||||
|
||||
use super::util::composer_utils::{ComposerModule, ModuleTag};
|
||||
use std::{collections::HashMap, ffi::{c_void, CStr}, sync::{atomic::{AtomicPtr, Ordering}, Mutex}};
|
||||
use jni::{objects::JString, sys::jobject, JNIEnv};
|
||||
use once_cell::sync::Lazy;
|
||||
use crate::{common, config, def_hook, dobby_hook, dobby_hook_sym, sig, util::get_jni_string};
|
||||
|
||||
const JS_TAG_BIG_DECIMAL: i64 = -11;
|
||||
const JS_TAG_BIG_INT: i64 = -10;
|
||||
const JS_TAG_BIG_FLOAT: i64 = -9;
|
||||
const JS_TAG_SYMBOL: i64 = -8;
|
||||
const JS_TAG_STRING: i64 = -7;
|
||||
const JS_TAG_MODULE: i64 = -3;
|
||||
const JS_TAG_FUNCTION_BYTECODE: i64 = -2;
|
||||
const JS_TAG_OBJECT: i64 = -1;
|
||||
const JS_TAG_INT: i64 = 0;
|
||||
const JS_TAG_BOOL: i64 = 1;
|
||||
const JS_TAG_NULL: i64 = 2;
|
||||
const JS_TAG_UNDEFINED: i64 = 3;
|
||||
const JS_TAG_UNINITIALIZED: i64 = 4;
|
||||
const JS_TAG_CATCH_OFFSET: i64 = 5;
|
||||
const JS_TAG_EXCEPTION: i64 = 6;
|
||||
const JS_TAG_FLOAT64: i64 = 7;
|
||||
|
||||
#[repr(C)]
|
||||
struct JsString {
|
||||
/*
|
||||
original structure :
|
||||
struct JSString {
|
||||
struct JSRefCountHeader {
|
||||
int ref_count;
|
||||
};
|
||||
uint32_t len : 31;
|
||||
uint8_t is_wide_char : 1;
|
||||
uint32_t hash : 30;
|
||||
uint8_t atom_type : 2;
|
||||
uint32_t hash_next;
|
||||
|
||||
union {
|
||||
uint8_t str8[0];
|
||||
uint16_t str16[0];
|
||||
} u;
|
||||
};
|
||||
*/
|
||||
pad: [u32; 4],
|
||||
str8: [u8; 0],
|
||||
str16: [u16; 0],
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Copy, Clone)]
|
||||
union JsValueUnion {
|
||||
int32: i32,
|
||||
float64: f64,
|
||||
ptr: *mut c_void,
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Copy, Clone)]
|
||||
struct JsValue {
|
||||
u: JsValueUnion,
|
||||
tag: i64,
|
||||
}
|
||||
|
||||
static AASSET_MAP: Lazy<Mutex<HashMap<usize, Vec<u8>>>> = Lazy::new(|| Mutex::new(HashMap::new()));
|
||||
static COMPOSER_LOADER_DATA: Mutex<Option<String>> = Mutex::new(None);
|
||||
|
||||
def_hook!(
|
||||
aasset_get_length,
|
||||
i32,
|
||||
|arg0: *mut c_void| {
|
||||
if let Some(buffer) = AASSET_MAP.lock().unwrap().get(&(arg0 as usize)) {
|
||||
return buffer.len() as i32;
|
||||
}
|
||||
aasset_get_length_original.unwrap()(arg0)
|
||||
}
|
||||
);
|
||||
|
||||
def_hook!(
|
||||
aasset_get_buffer,
|
||||
*const c_void,
|
||||
|arg0: *mut c_void| {
|
||||
if let Some(buffer) = AASSET_MAP.lock().unwrap().get(&(arg0 as usize)) {
|
||||
return buffer.as_ptr() as *const c_void;
|
||||
}
|
||||
aasset_get_buffer_original.unwrap()(arg0)
|
||||
}
|
||||
);
|
||||
|
||||
def_hook!(
|
||||
aasset_manager_open,
|
||||
*mut c_void,
|
||||
|arg0: *mut c_void, arg1: *const u8, arg2: i32| {
|
||||
let handle = aasset_manager_open_original.unwrap()(arg0, arg1, arg2);
|
||||
|
||||
let path = Lazy::new(|| CStr::from_ptr(arg1).to_str().unwrap());
|
||||
if !handle.is_null() && path.starts_with("bridge_observables") {
|
||||
let asset_buffer = aasset_get_buffer_original.unwrap()(handle);
|
||||
let asset_length = aasset_get_length_original.unwrap()(handle);
|
||||
debug!("asset buffer: {:p}, length: {}", asset_buffer, asset_length);
|
||||
|
||||
let composer_loader = COMPOSER_LOADER_DATA.lock().unwrap().clone().expect("No composer loader data");
|
||||
|
||||
let archive_buffer: Vec<u8> = std::slice::from_raw_parts(asset_buffer as *const u8, asset_length as usize).to_vec();
|
||||
let decompressed = zstd::stream::decode_all(&archive_buffer[..]).expect("Failed to decompress composer archive");
|
||||
let mut composer_module = ComposerModule::parse(decompressed).expect("Failed to parse composer module");
|
||||
|
||||
let mut tags = composer_module.get_tags();
|
||||
let mut new_tags = Vec::new();
|
||||
|
||||
for (tag1, _) in tags.iter_mut() {
|
||||
let name = tag1.to_string().unwrap();
|
||||
if !name.ends_with("src/utils/converter.js") {
|
||||
continue;
|
||||
}
|
||||
|
||||
let old_file_name = name.split_once(".").unwrap().0.to_owned() + rand::random::<u32>().to_string().as_str();
|
||||
tag1.set_buffer((old_file_name.to_owned() + ".js").as_bytes().to_vec());
|
||||
let original_module_path = path.split_once(".").unwrap().0.to_owned() + "/" + &old_file_name;
|
||||
|
||||
let hooked_module = format!("{};module.exports = require(\"{}\");", composer_loader, original_module_path);
|
||||
|
||||
new_tags.push(
|
||||
(
|
||||
ModuleTag::new(128, name.as_bytes().to_vec()),
|
||||
ModuleTag::new(128, hooked_module.as_bytes().to_vec())
|
||||
)
|
||||
);
|
||||
|
||||
debug!("composer loader injected in {}", name);
|
||||
break;
|
||||
}
|
||||
|
||||
tags.extend(new_tags);
|
||||
composer_module.set_tags(tags);
|
||||
|
||||
let compressed = composer_module.to_bytes();
|
||||
let compressed = zstd::stream::encode_all(&compressed[..], 3).expect("Failed to compress");
|
||||
|
||||
AASSET_MAP.lock().unwrap().insert(handle as usize, compressed);
|
||||
}
|
||||
handle
|
||||
}
|
||||
);
|
||||
|
||||
def_hook!(
|
||||
aasset_close,
|
||||
c_void,
|
||||
|handle: *mut c_void| {
|
||||
AASSET_MAP.lock().unwrap().remove(&(handle as usize));
|
||||
aasset_close_original.unwrap()(handle)
|
||||
}
|
||||
);
|
||||
|
||||
#[cfg(target_arch = "aarch64")]
|
||||
static GLOBAL_INSTANCE: AtomicPtr<c_void> = AtomicPtr::new(std::ptr::null_mut());
|
||||
#[cfg(target_arch = "aarch64")]
|
||||
static GLOBAL_CTX: AtomicPtr<c_void> = AtomicPtr::new(std::ptr::null_mut());
|
||||
|
||||
#[cfg(target_arch = "aarch64")]
|
||||
static JS_EVAL_ORIGINAL2: Lazy<Mutex<Option<unsafe extern "C" fn(*mut c_void, *mut c_void, *mut c_void, *mut u8, usize, *const u8, u32) -> JsValue>>> = Lazy::new(|| Mutex::new(None));
|
||||
|
||||
def_hook!(
|
||||
js_eval,
|
||||
*mut c_void,
|
||||
|arg0: *mut c_void, arg1: *mut c_void, arg2: *mut c_void, arg3: *const u8, arg4: *const u8, arg5: *const u8, arg6: *mut c_void, arg7: u32| {
|
||||
#[cfg(target_arch = "aarch64")]
|
||||
{
|
||||
GLOBAL_INSTANCE.store(arg0, Ordering::Relaxed);
|
||||
GLOBAL_CTX.store(arg1, Ordering::Relaxed);
|
||||
}
|
||||
js_eval_original.unwrap()(arg0, arg1, arg2, arg3, arg4, arg5, arg6, arg7)
|
||||
}
|
||||
);
|
||||
|
||||
pub fn set_composer_loader(mut env: JNIEnv, _: *mut c_void, code: JString) {
|
||||
let new_code = get_jni_string(&mut env, code).expect("Failed to get composer loader code");
|
||||
COMPOSER_LOADER_DATA.lock().unwrap().replace(new_code);
|
||||
}
|
||||
|
||||
#[allow(unreachable_code, unused_variables)]
|
||||
pub unsafe fn composer_eval(env: JNIEnv, _: *mut c_void, script: JString) -> jobject {
|
||||
#[cfg(target_arch = "aarch64")]
|
||||
{
|
||||
let mut env = env;
|
||||
|
||||
let script_str = match get_jni_string(&mut env, script) {
|
||||
Ok(s) => s,
|
||||
Err(_) => return std::ptr::null_mut(),
|
||||
};
|
||||
|
||||
if JS_EVAL_ORIGINAL2.lock().unwrap().is_none() || GLOBAL_INSTANCE.load(Ordering::Relaxed).is_null() || GLOBAL_CTX.load(Ordering::Relaxed).is_null() {
|
||||
if let Ok(s) = env.new_string("Composer hook not initialized") {
|
||||
return s.into_raw();
|
||||
}
|
||||
return std::ptr::null_mut();
|
||||
}
|
||||
|
||||
let script_length = script_str.len();
|
||||
|
||||
let js_value = {
|
||||
let js_eval_fn = JS_EVAL_ORIGINAL2.lock().unwrap();
|
||||
let global_instance = GLOBAL_INSTANCE.load(Ordering::Relaxed);
|
||||
let global_ctx = GLOBAL_CTX.load(Ordering::Relaxed);
|
||||
|
||||
js_eval_fn.unwrap()(
|
||||
global_instance,
|
||||
global_ctx,
|
||||
std::ptr::null_mut(),
|
||||
(script_str + "\0").as_ptr() as *mut u8,
|
||||
script_length,
|
||||
"<eval>\0".as_ptr(),
|
||||
0
|
||||
)
|
||||
};
|
||||
|
||||
let result: String = if js_value.tag == JS_TAG_STRING {
|
||||
let string = js_value.u.ptr as *mut JsString;
|
||||
match CStr::from_ptr((*string).str8.as_ptr() as *const u8).to_str() {
|
||||
Ok(s) => s.to_string(),
|
||||
Err(_) => "[invalid string]".to_string(),
|
||||
}
|
||||
} else if js_value.tag == JS_TAG_INT {
|
||||
js_value.u.int32.to_string()
|
||||
} else if js_value.tag == JS_TAG_BOOL {
|
||||
if js_value.u.int32 == 1 { "true" } else { "false" }.to_string()
|
||||
} else if js_value.tag == JS_TAG_NULL {
|
||||
"null".to_string()
|
||||
} else if js_value.tag == JS_TAG_UNDEFINED {
|
||||
"undefined".to_string()
|
||||
} else if js_value.tag == JS_TAG_OBJECT {
|
||||
"[object]".to_string()
|
||||
} else if js_value.tag == JS_TAG_FLOAT64 {
|
||||
js_value.u.float64.to_string()
|
||||
} else if js_value.tag == JS_TAG_EXCEPTION {
|
||||
"Failed to evaluate script".to_string()
|
||||
} else {
|
||||
format!("[unknown tag {}]", js_value.tag)
|
||||
};
|
||||
|
||||
if let Ok(s) = env.new_string(result) {
|
||||
return s.into_raw();
|
||||
}
|
||||
}
|
||||
|
||||
std::ptr::null_mut()
|
||||
}
|
||||
|
||||
pub fn init() {
|
||||
if !config::native_config().composer_hooks {
|
||||
return
|
||||
}
|
||||
|
||||
dobby_hook_sym!("libandroid.so", "AAsset_getBuffer", aasset_get_buffer);
|
||||
dobby_hook_sym!("libandroid.so", "AAsset_getLength", aasset_get_length);
|
||||
dobby_hook_sym!("libandroid.so", "AAsset_close", aasset_close);
|
||||
dobby_hook_sym!("libandroid.so", "AAssetManager_open", aasset_manager_open);
|
||||
|
||||
#[cfg(target_arch = "aarch64")]
|
||||
{
|
||||
let signature = sig::find_signature(
|
||||
&common::CLIENT_MODULE,
|
||||
"00 E4 00 6F 29 00 80 52 76 00 04 8B", -0x28,
|
||||
"A1 B0 07 92 81 46", -0x7
|
||||
).or_else(|| {
|
||||
warn!("primary js_eval signature failed, trying fallback");
|
||||
sig::find_signature(
|
||||
&common::CLIENT_MODULE,
|
||||
"00 E4 00 6F ?? ?? ?? ?? ?? ?? ?? ??", -0x28,
|
||||
"?? ?? ?? ?? ?? ??", -0x7
|
||||
)
|
||||
});
|
||||
|
||||
if let Some(signature) = signature {
|
||||
dobby_hook!(signature as *mut c_void, js_eval);
|
||||
|
||||
unsafe {
|
||||
*JS_EVAL_ORIGINAL2.lock().unwrap() = Some(std::mem::transmute(js_eval_original.unwrap()));
|
||||
}
|
||||
|
||||
debug!("js_eval {:#x}", signature);
|
||||
} else {
|
||||
warn!("Unable to find js_eval signature");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,141 +0,0 @@
|
||||
use std::{io::Error, string::FromUtf8Error};
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct ModuleTag {
|
||||
tag_type: u8,
|
||||
buffer: Vec<u8>,
|
||||
}
|
||||
|
||||
impl ModuleTag {
|
||||
pub fn new(module_type: u8, buffer: Vec<u8>) -> ModuleTag {
|
||||
ModuleTag {
|
||||
tag_type: module_type,
|
||||
buffer,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn to_string(&self) -> Result<String, FromUtf8Error> {
|
||||
Ok(String::from_utf8(self.buffer.clone())?)
|
||||
}
|
||||
|
||||
pub fn get_tag_type(&self) -> u8 {
|
||||
self.tag_type
|
||||
}
|
||||
|
||||
pub fn get_size(&self) -> usize {
|
||||
self.buffer.len()
|
||||
}
|
||||
|
||||
pub fn get_buffer(&self) -> &Vec<u8> {
|
||||
&self.buffer
|
||||
}
|
||||
|
||||
pub fn set_buffer(&mut self, buffer: Vec<u8>) {
|
||||
self.buffer = buffer;
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct ComposerModule {
|
||||
tags: Vec<(ModuleTag, ModuleTag)>, // file name => file content
|
||||
}
|
||||
|
||||
impl ComposerModule {
|
||||
pub fn parse(buffer: Vec<u8>) -> Result<ComposerModule, Error> {
|
||||
let mut offset = 0;
|
||||
let magic = u32::from_be_bytes([buffer[offset], buffer[offset + 1], buffer[offset + 2], buffer[offset + 3]]);
|
||||
|
||||
offset += 4;
|
||||
|
||||
if magic != 0x33c60001 {
|
||||
return Err(Error::new(std::io::ErrorKind::InvalidData, "Invalid magic"));
|
||||
}
|
||||
|
||||
// skip content length
|
||||
offset += 4;
|
||||
|
||||
let mut tags = Vec::new();
|
||||
|
||||
loop {
|
||||
if offset >= buffer.len() {
|
||||
break;
|
||||
}
|
||||
|
||||
fn read_u24(buffer: &Vec<u8>, offset: &mut usize) -> Result<u32, Error> {
|
||||
let b1 = buffer[*offset] as u32;
|
||||
let b2 = buffer[*offset + 1] as u32;
|
||||
let b3 = buffer[*offset + 2] as u32;
|
||||
*offset += 3;
|
||||
Ok(b1 | (b2 << 8) | (b3 << 16))
|
||||
}
|
||||
|
||||
let tag_size = read_u24(&buffer, &mut offset)?;
|
||||
let tag_type = buffer[offset];
|
||||
offset += 1;
|
||||
let tag_buffer = buffer[offset..offset + tag_size as usize].to_vec();
|
||||
offset += tag_size as usize;
|
||||
|
||||
let padding = 4 - (tag_size % 4);
|
||||
|
||||
if padding != 4 {
|
||||
offset += padding as usize;
|
||||
}
|
||||
|
||||
tags.push(ModuleTag::new(tag_type, tag_buffer));
|
||||
}
|
||||
|
||||
let tags = tags.chunks(2).map(|chunk| {
|
||||
(chunk[0].clone(), chunk[1].clone())
|
||||
}).collect();
|
||||
|
||||
Ok(ComposerModule {
|
||||
tags,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn to_bytes(&self) -> Vec<u8> {
|
||||
let mut tag_buffer = Vec::new();
|
||||
|
||||
fn write_u24(buffer: &mut Vec<u8>, value: u32) {
|
||||
buffer.push((value & 0xff) as u8);
|
||||
buffer.push(((value >> 8) & 0xff) as u8);
|
||||
buffer.push(((value >> 16) & 0xff) as u8);
|
||||
}
|
||||
|
||||
fn write_tag(buffer: &mut Vec<u8>, tag: ModuleTag) {
|
||||
write_u24(buffer, tag.get_size() as u32);
|
||||
buffer.push(tag.get_tag_type());
|
||||
buffer.extend(tag.get_buffer());
|
||||
|
||||
let padding = 4 - (tag.get_size() % 4);
|
||||
|
||||
if padding != 4 {
|
||||
for _ in 0..padding {
|
||||
buffer.push(0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (tag1, tag2) in &self.tags {
|
||||
write_tag(&mut tag_buffer, tag1.clone());
|
||||
write_tag(&mut tag_buffer, tag2.clone());
|
||||
}
|
||||
|
||||
let mut buffer = Vec::new();
|
||||
|
||||
buffer.extend_from_slice(&[0x33, 0xc6, 0, 1]);
|
||||
buffer.extend_from_slice(&(tag_buffer.len() as u32).to_le_bytes());
|
||||
buffer.extend(tag_buffer);
|
||||
|
||||
buffer
|
||||
}
|
||||
|
||||
pub fn get_tags(&self) -> Vec<(ModuleTag, ModuleTag)> {
|
||||
self.tags.clone()
|
||||
}
|
||||
|
||||
pub fn set_tags(&mut self, tags: Vec<(ModuleTag, ModuleTag)>) {
|
||||
self.tags = tags;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user