composer_hook.rs (8777B) - raw


      1 #![allow(dead_code, unused_imports)]
      2 
      3 use super::util::composer_utils::{ComposerModule, ModuleTag};
      4 use std::{collections::HashMap, ffi::{c_void, CStr}, sync::Mutex};
      5 use jni::{objects::JString, sys::jobject, JNIEnv};
      6 use once_cell::sync::Lazy;
      7 use crate::{common, config, def_hook, dobby_hook, dobby_hook_sym, sig, util::get_jni_string};
      8 
      9 const JS_TAG_BIG_DECIMAL: i64 = -11;
     10 const JS_TAG_BIG_INT: i64 = -10;
     11 const JS_TAG_BIG_FLOAT: i64 = -9;
     12 const JS_TAG_SYMBOL: i64 = -8;
     13 const JS_TAG_STRING: i64 = -7;
     14 const JS_TAG_MODULE: i64 = -3;
     15 const JS_TAG_FUNCTION_BYTECODE: i64 = -2;
     16 const JS_TAG_OBJECT: i64 = -1;
     17 const JS_TAG_INT: i64 = 0;
     18 const JS_TAG_BOOL: i64 = 1;
     19 const JS_TAG_NULL: i64 = 2;
     20 const JS_TAG_UNDEFINED: i64 = 3;
     21 const JS_TAG_UNINITIALIZED: i64 = 4;
     22 const JS_TAG_CATCH_OFFSET: i64 = 5;
     23 const JS_TAG_EXCEPTION: i64 = 6;
     24 const JS_TAG_FLOAT64: i64 = 7;
     25 
     26 #[repr(C)]
     27 struct JsString {
     28     /*
     29     original structure : 
     30     struct JSString {
     31         struct JSRefCountHeader {
     32             int ref_count;
     33         };
     34         uint32_t len : 31;
     35         uint8_t is_wide_char : 1;
     36         uint32_t hash : 30;
     37         uint8_t atom_type : 2;
     38         uint32_t hash_next;
     39 
     40         union {
     41             uint8_t str8[0];
     42             uint16_t str16[0];
     43         } u;
     44     };
     45     */
     46     pad: [u32; 4],
     47     str8: [u8; 0],
     48     str16: [u16; 0],
     49 }
     50 
     51 #[repr(C)]
     52 #[derive(Copy, Clone)]
     53 union JsValueUnion {
     54     int32: i32,
     55     float64: f64,
     56     ptr: *mut c_void,
     57 }
     58 
     59 #[repr(C)]
     60 #[derive(Copy, Clone)]
     61 struct JsValue {
     62     u: JsValueUnion,
     63     tag: i64,
     64 }
     65 
     66 static AASSET_MAP: Lazy<Mutex<HashMap<usize, Vec<u8>>>> = Lazy::new(|| Mutex::new(HashMap::new()));
     67 static COMPOSER_LOADER_DATA: Mutex<Option<String>> = Mutex::new(None);
     68 
     69 def_hook!(
     70     aasset_get_length,
     71     i32,
     72     |arg0: *mut c_void| {
     73         if let Some(buffer) = AASSET_MAP.lock().unwrap().get(&(arg0 as usize)) {
     74             return buffer.len() as i32;
     75         }
     76         aasset_get_length_original.unwrap()(arg0)
     77     }
     78 );
     79 
     80 def_hook!(
     81     aasset_get_buffer,
     82     *const c_void,
     83     |arg0: *mut c_void| {
     84         if let Some(buffer) = AASSET_MAP.lock().unwrap().get(&(arg0 as usize)) {
     85             return buffer.as_ptr() as *const c_void;
     86         }
     87         aasset_get_buffer_original.unwrap()(arg0)
     88     }
     89 );
     90 
     91 def_hook!(
     92     aasset_manager_open,
     93     *mut c_void,
     94     |arg0: *mut c_void, arg1: *const u8, arg2: i32| {
     95         let handle = aasset_manager_open_original.unwrap()(arg0, arg1, arg2);
     96 
     97         let path = Lazy::new(|| CStr::from_ptr(arg1).to_str().unwrap());
     98         if !handle.is_null() && path.starts_with("bridge_observables") {
     99             let asset_buffer = aasset_get_buffer_original.unwrap()(handle);
    100             let asset_length = aasset_get_length_original.unwrap()(handle);
    101             debug!("asset buffer: {:p}, length: {}", asset_buffer, asset_length);
    102 
    103             let composer_loader = COMPOSER_LOADER_DATA.lock().unwrap().clone().expect("No composer loader data");
    104 
    105             let archive_buffer: Vec<u8> = std::slice::from_raw_parts(asset_buffer as *const u8, asset_length as usize).to_vec();
    106             let decompressed = zstd::stream::decode_all(&archive_buffer[..]).expect("Failed to decompress composer archive");
    107             let mut composer_module = ComposerModule::parse(decompressed).expect("Failed to parse composer module");
    108 
    109             let mut tags = composer_module.get_tags();
    110             let mut new_tags = Vec::new();
    111 
    112             for (tag1, _) in tags.iter_mut() {
    113                 let name = tag1.to_string().unwrap();
    114                 if !name.ends_with("src/utils/converter.js") {
    115                     continue;
    116                 }
    117 
    118                 let old_file_name = name.split_once(".").unwrap().0.to_owned() + rand::random::<u32>().to_string().as_str();
    119                 tag1.set_buffer((old_file_name.to_owned() + ".js").as_bytes().to_vec());
    120                 let original_module_path = path.split_once(".").unwrap().0.to_owned() + "/" + &old_file_name;
    121 
    122                 let hooked_module = format!("{};module.exports = require(\"{}\");", composer_loader, original_module_path);
    123 
    124                 new_tags.push(
    125                     (
    126                         ModuleTag::new(128, name.as_bytes().to_vec()),
    127                         ModuleTag::new(128, hooked_module.as_bytes().to_vec())
    128                     )
    129                 );
    130 
    131                 debug!("composer loader injected in {}", name);
    132                 break;
    133             }
    134 
    135             tags.extend(new_tags);
    136             composer_module.set_tags(tags);
    137 
    138             let compressed = composer_module.to_bytes();
    139             let compressed = zstd::stream::encode_all(&compressed[..], 3).expect("Failed to compress");
    140 
    141             AASSET_MAP.lock().unwrap().insert(handle as usize, compressed);
    142         }
    143         handle
    144     }
    145 );
    146 
    147 def_hook!(
    148     aasset_close,
    149     c_void,
    150     |handle: *mut c_void| {
    151         AASSET_MAP.lock().unwrap().remove(&(handle as usize));
    152         aasset_close_original.unwrap()(handle)
    153     }
    154 );
    155     
    156 #[cfg(target_arch = "aarch64")]
    157 static mut GLOBAL_INSTANCE: Option<*mut c_void> = None;
    158 #[cfg(target_arch = "aarch64")]
    159 static mut GLOBAL_CTX: Option<*mut c_void> = None;
    160 
    161 #[cfg(target_arch = "aarch64")]
    162 static mut JS_EVAL_ORIGINAL2: Option<unsafe extern "C" fn(*mut c_void, *mut c_void, *mut c_void, *mut u8, usize, *const u8, u32) -> JsValue> = None;
    163 
    164 def_hook!(
    165     js_eval,
    166     *mut c_void,
    167     |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| {
    168         #[cfg(target_arch = "aarch64")]
    169         {
    170             GLOBAL_INSTANCE = Some(arg0);
    171             GLOBAL_CTX = Some(arg1);
    172         }
    173         js_eval_original.unwrap()(arg0, arg1, arg2, arg3, arg4, arg5, arg6, arg7)
    174     }
    175 );
    176 
    177 pub fn set_composer_loader(mut env: JNIEnv, _: *mut c_void, code: JString) {
    178     let new_code = get_jni_string(&mut env, code).expect("Failed to get composer loader code");
    179     COMPOSER_LOADER_DATA.lock().unwrap().replace(new_code);
    180 }
    181 
    182 #[allow(unreachable_code, unused_variables)]
    183 pub unsafe fn composer_eval(env: JNIEnv, _: *mut c_void, script: JString) -> jobject {
    184     #[cfg(target_arch = "aarch64")]
    185     {
    186         let mut env = env;
    187 
    188         let script_str = get_jni_string(&mut env, script).expect("Failed to get script");
    189         let script_length = script_str.len();
    190     
    191         let js_value = JS_EVAL_ORIGINAL2.expect("No js eval found")(
    192             GLOBAL_INSTANCE.expect("No global instance found"), 
    193             GLOBAL_CTX.expect("No global context found"),
    194             std::ptr::null_mut(),
    195             (script_str + "\0").as_ptr() as *mut u8, 
    196             script_length, 
    197             "<eval>\0".as_ptr(), 
    198             0
    199         );
    200     
    201         let result: String =  if js_value.tag == JS_TAG_STRING {
    202             let string = js_value.u.ptr as *mut JsString;
    203             CStr::from_ptr((*string).str8.as_ptr() as *const u8).to_str().unwrap().into()
    204         } else if js_value.tag == JS_TAG_INT {
    205             js_value.u.int32.to_string()
    206         } else if js_value.tag == JS_TAG_BOOL {
    207             if js_value.u.int32 == 1 { "true" } else { "false" }.into()
    208         } else if js_value.tag == JS_TAG_NULL {
    209             "null".into()
    210         } else if js_value.tag == JS_TAG_UNDEFINED {
    211             "undefined".into()
    212         } else if js_value.tag == JS_TAG_OBJECT {
    213             "[object]".into()
    214         } else if js_value.tag == JS_TAG_FLOAT64 {
    215             js_value.u.float64.to_string()
    216         } else if js_value.tag == JS_TAG_EXCEPTION {
    217             "Failed to evaluate script".into()
    218         } else {
    219             "[unknown tag ".to_owned() + &js_value.tag.to_string() + "]".into()
    220         };
    221         
    222         return env.new_string(result).unwrap().into_raw()
    223     }
    224 
    225     return env.new_string("Architecture not supported").unwrap().into_raw();
    226 }
    227 
    228 pub fn init() {
    229     if !config::native_config().composer_hooks {
    230         return
    231     }
    232 
    233     dobby_hook_sym!("libandroid.so", "AAsset_getBuffer", aasset_get_buffer);
    234     dobby_hook_sym!("libandroid.so", "AAsset_getLength", aasset_get_length);
    235     dobby_hook_sym!("libandroid.so", "AAsset_close", aasset_close);
    236     dobby_hook_sym!("libandroid.so", "AAssetManager_open", aasset_manager_open);
    237     
    238     #[cfg(target_arch = "aarch64")]
    239     {
    240         if let Some(signature) = sig::find_signature(
    241             &common::CLIENT_MODULE,
    242             "00 E4 00 6F 29 00 80 52 76 00 04 8B", -0x28,
    243             "A1 B0 07 92 81 46", -0x7
    244         ) {
    245             dobby_hook!(signature as *mut c_void, js_eval);
    246             
    247             unsafe { 
    248                 JS_EVAL_ORIGINAL2 = Some(std::mem::transmute(js_eval_original.unwrap()));
    249             }
    250     
    251             debug!("js_eval {:#x}", signature);
    252         } else {
    253             warn!("Unable to find js_eval signature");
    254         }
    255     }
    256 }
    257