From 3f11e09a006d378b3de2af8161182be828bebe29 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=CE=9ET=CE=9ERNAL?= <137927782+particle-box@users.noreply.github.com> Date: Fri, 16 Jan 2026 12:31:17 +0530 Subject: [PATCH] ci: update debug signing secrets --- .github/workflows/debug.yml | 16 +- .../purrfectsnap/common/bridge/BridgeFiles.kt | 3 - .../impl/experiments/ComposerHooks.kt | 228 -------------- .../wrapper/impl/composer/ComposerContext.kt | 29 -- .../impl/composer/ComposerMarshaller.kt | 13 - .../wrapper/impl/composer/ComposerViewNode.kt | 62 ---- gradle.properties | 1 - native/rust/src/modules/composer_hook.rs | 287 ------------------ .../rust/src/modules/util/composer_utils.rs | 141 --------- 9 files changed, 8 insertions(+), 772 deletions(-) delete mode 100644 core/src/main/kotlin/me/eternal/purrfectsnap/core/features/impl/experiments/ComposerHooks.kt delete mode 100644 core/src/main/kotlin/me/eternal/purrfectsnap/core/wrapper/impl/composer/ComposerContext.kt delete mode 100644 core/src/main/kotlin/me/eternal/purrfectsnap/core/wrapper/impl/composer/ComposerMarshaller.kt delete mode 100644 core/src/main/kotlin/me/eternal/purrfectsnap/core/wrapper/impl/composer/ComposerViewNode.kt delete mode 100644 native/rust/src/modules/composer_hook.rs delete mode 100644 native/rust/src/modules/util/composer_utils.rs diff --git a/.github/workflows/debug.yml b/.github/workflows/debug.yml index 549c17ea..cb5ef4e7 100644 --- a/.github/workflows/debug.yml +++ b/.github/workflows/debug.yml @@ -38,10 +38,10 @@ jobs: uses: SnapEnhance/sign-android-release@master with: releaseDirectory: app/build/outputs/apk/armv8/debug/ - signingKeyBase64: ${{ secrets.JAVA_KEYSTORE_DATA }} - alias: ${{ secrets.KEY_ALIAS }} - keyStorePassword: ${{ secrets.KEYSTORE_PASSWORD }} - keyPassword: ${{ secrets.KEY_PASSWORD }} + signingKeyBase64: ${{ secrets.PS_BASE_64 }} + alias: ${{ secrets.PS_RELEASE_KEY_ALIAS }} + keyStorePassword: ${{ secrets.PS_RELEASE_STORE_PASSWORD }} + keyPassword: ${{ secrets.PS_RELEASE_KEY_PASSWORD }} env: BUILD_TOOLS_VERSION: ${{ env.BUILD_TOOL_VERSION }} - name: Get current build version @@ -90,10 +90,10 @@ jobs: uses: SnapEnhance/sign-android-release@master with: releaseDirectory: app/build/outputs/apk/armv7/debug/ - signingKeyBase64: ${{ secrets.JAVA_KEYSTORE_DATA }} - alias: ${{ secrets.KEY_ALIAS }} - keyStorePassword: ${{ secrets.KEYSTORE_PASSWORD }} - keyPassword: ${{ secrets.KEY_PASSWORD }} + signingKeyBase64: ${{ secrets.PS_BASE_64 }} + alias: ${{ secrets.PS_RELEASE_KEY_ALIAS }} + keyStorePassword: ${{ secrets.PS_RELEASE_STORE_PASSWORD }} + keyPassword: ${{ secrets.PS_RELEASE_KEY_PASSWORD }} env: BUILD_TOOLS_VERSION: ${{ env.BUILD_TOOL_VERSION }} - name: Get current build version diff --git a/common/src/main/kotlin/me/eternal/purrfectsnap/common/bridge/BridgeFiles.kt b/common/src/main/kotlin/me/eternal/purrfectsnap/common/bridge/BridgeFiles.kt index 6994d34f..8c9233fe 100644 --- a/common/src/main/kotlin/me/eternal/purrfectsnap/common/bridge/BridgeFiles.kt +++ b/common/src/main/kotlin/me/eternal/purrfectsnap/common/bridge/BridgeFiles.kt @@ -5,8 +5,6 @@ import android.os.ParcelFileDescriptor import android.os.ParcelFileDescriptor.AutoCloseInputStream import android.os.ParcelFileDescriptor.AutoCloseOutputStream import me.eternal.purrfectsnap.bridge.storage.FileHandle -import me.eternal.purrfectsnap.common.bridge.FileHandleScope.entries -import me.eternal.purrfectsnap.common.bridge.InternalFileHandleType.entries import me.eternal.purrfectsnap.common.util.LazyBridgeValue import me.eternal.purrfectsnap.common.util.lazyBridge import java.io.File @@ -96,4 +94,3 @@ open class FileHandleWrapper( } - diff --git a/core/src/main/kotlin/me/eternal/purrfectsnap/core/features/impl/experiments/ComposerHooks.kt b/core/src/main/kotlin/me/eternal/purrfectsnap/core/features/impl/experiments/ComposerHooks.kt deleted file mode 100644 index ac70580a..00000000 --- a/core/src/main/kotlin/me/eternal/purrfectsnap/core/features/impl/experiments/ComposerHooks.kt +++ /dev/null @@ -1,228 +0,0 @@ -package me.eternal.purrfectsnap.core.features.impl.experiments - -import androidx.compose.foundation.layout.* -import androidx.compose.foundation.rememberScrollState -import androidx.compose.foundation.verticalScroll -import androidx.compose.material.icons.Icons -import androidx.compose.material.icons.filled.BugReport -import androidx.compose.material3.* -import androidx.compose.runtime.getValue -import androidx.compose.runtime.mutableStateOf -import androidx.compose.runtime.remember -import androidx.compose.runtime.setValue -import androidx.compose.ui.Alignment -import androidx.compose.ui.Modifier -import androidx.compose.ui.text.TextStyle -import androidx.compose.ui.text.font.FontWeight -import androidx.compose.ui.unit.dp -import androidx.compose.ui.unit.sp -import kotlinx.coroutines.launch -import me.eternal.purrfectsnap.common.bridge.FileHandleScope -import me.eternal.purrfectsnap.common.bridge.toWrapper -import me.eternal.purrfectsnap.common.ui.createComposeAlertDialog -import me.eternal.purrfectsnap.core.PurrfectSnap -import me.eternal.purrfectsnap.core.features.Feature -import me.eternal.purrfectsnap.core.features.impl.downloader.MediaDownloader -import me.eternal.purrfectsnap.core.util.hook.HookStage -import me.eternal.purrfectsnap.core.util.hook.Hooker -import me.eternal.purrfectsnap.core.util.hook.hook -import me.eternal.purrfectsnap.core.wrapper.impl.composer.ComposerMarshaller -import me.eternal.purrfectsnap.nativelib.NativeLib -import java.lang.reflect.Proxy -import kotlin.math.absoluteValue -import kotlin.random.Random - -class ComposerHooks: Feature("ComposerHooks") { - private val config by lazy { context.config.experimental.nativeHooks.composerHooks } - private val getImportsFunctionName = Random.nextLong().absoluteValue.toString(16) - - private val composerConsole by lazy { - createComposeAlertDialog(context.mainActivity!!) { - Column( - modifier = Modifier - .fillMaxSize() - .padding(16.dp), - verticalArrangement = Arrangement.spacedBy(16.dp) - ) { - var result by remember { mutableStateOf("") } - var codeContent by remember { mutableStateOf("return 1 + 2") } - - Text("Composer Console", fontSize = 18.sp, fontWeight = FontWeight.Bold) - - TextField( - modifier = Modifier.fillMaxWidth(), - textStyle = TextStyle.Default.copy(fontSize = 12.sp), - value = codeContent, - placeholder = { Text("Enter your JS code here:") }, - onValueChange = { - codeContent = it - } - ) - Button( - modifier = Modifier.fillMaxWidth(), - onClick = { - context.log.verbose("input: $codeContent", "ComposerConsole") - result = "Running..." - context.coroutineScope.launch { - result = (context.native.composerEval(""" - (() => { - try { - const retVal = (() => { $codeContent })(); - if (typeof retVal === 'undefined') { - return 'undefined'; - } - if (retVal === null) { - return 'null'; - } - try { - return JSON.stringify(retVal, null, 2); - } catch { - return retVal.toString(); - } - } catch (e) { - return e.toString() - } - })() - """.trimIndent()) ?: "(no result)").toString().also { - context.log.verbose("result: $it", "ComposerConsole") - } - } - } - ) { - Text("Run") - } - - Column( - modifier = Modifier.verticalScroll(rememberScrollState()) - ) { - Text(result) - } - } - } - } - - private fun newComposerFunction(block: ComposerMarshaller.() -> Boolean): Any? { - val composerFunctionClass = findClass("com.snap.composer.callable.ComposerFunction") - return Proxy.newProxyInstance( - composerFunctionClass.classLoader, - arrayOf(composerFunctionClass) - ) { _, method, args -> - if (method.name != "perform") return@newProxyInstance null - block(ComposerMarshaller(args?.get(0) ?: return@newProxyInstance false)) - } - } - - @Suppress("UNCHECKED_CAST") - override fun init() { - if (config.globalState != true) return - - val importedFunctions = mutableMapOf() - - fun composerFunction(name: String, block: ComposerMarshaller.() -> Unit) { - importedFunctions[name] = newComposerFunction { - block(this) - true - } - } - - composerFunction("getConfig") { - pushUntyped(mapOf( - "operaDownloadButton" to context.config.downloader.operaDownloadButton.get(), - "bypassCameraRollLimit" to config.bypassCameraRollLimit.get(), - "showFirstCreatedUsername" to config.showFirstCreatedUsername.get(), - "composerLogs" to config.composerLogs.get(), - "customSelfDestructSnapDelay" to config.customSelfDestructSnapDelay.get(), - )) - } - - composerFunction("showToast") { - if (getSize() < 1) return@composerFunction - context.shortToast(getUntyped(0) as? String ?: return@composerFunction) - } - - composerFunction("downloadLastOperaMedia") { - context.feature(MediaDownloader::class).downloadLastOperaMediaAsync(getUntyped(0) == true) - } - - composerFunction("getFriendOriginalUsername") { - if (getSize() < 1) return@composerFunction - val username = getUntyped(0) as? String ?: return@composerFunction - - runCatching { - pushUntyped(context.database.getFriendOriginalUsername(username)) - }.onFailure { - pushUntyped(null) - } - } - - composerFunction("log") { - if (getSize() < 2) return@composerFunction - val logLevel = getUntyped(0) as? String ?: return@composerFunction - val message = getUntyped(1) as? String ?: return@composerFunction - - val tag = "ComposerLogs" - - when (logLevel) { - "log" -> context.log.verbose(message, tag) - "debug" -> context.log.debug(message, tag) - "info" -> context.log.info(message, tag) - "warn" -> context.log.warn(message, tag) - "error" -> context.log.error(message, tag) - } - } - - fun loadHooks() { - if (!NativeLib.initialized) { - context.log.error("ComposerHooks cannot be loaded without NativeLib") - return - } - val loaderScript = runCatching { - context.fileHandlerManager.getFileHandle(FileHandleScope.COMPOSER.key, "loader.js").toWrapper().readBytes().toString(Charsets.UTF_8) - }.onFailure { - context.log.error("Failed to load composer loader script", it) - }.getOrNull() ?: return - context.native.setComposerLoader(""" - const i = setInterval(() => { - try { - const _runtimeName = "${if (PurrfectSnap.classCache.nativeBridge.name == "com.snapchat.client.valdi.NativeBridge") "valdi" else "composer"}"; - require(_runtimeName + '_core/DeviceBridge').getDisplayWidth(); - clearInterval(i); - (() => { const _getImportsFunctionName = "$getImportsFunctionName"; $loaderScript })(); - } catch (e) {} - }, 200) - """.trimIndent().trim()) - } - - loadHooks() - - if (config.composerConsole.get()) { - context.inAppOverlay.addCustomComposable { - FilledIconButton( - onClick = { - composerConsole.show() - }, - modifier = Modifier.align(Alignment.TopEnd).padding(top = 100.dp, end = 16.dp) - ) { - Icon(Icons.Default.BugReport, contentDescription = "Debug Console") - } - } - } - - PurrfectSnap.classCache.nativeBridge.hook("registerNativeModuleFactory", HookStage.BEFORE) { param -> - val moduleFactory = param.argNullable(1) ?: return@hook - - Hooker.ephemeralHookObjectMethod(moduleFactory.javaClass, moduleFactory, "loadModule", HookStage.AFTER) { methodParam -> - val result = methodParam.getResult() as? MutableMap ?: return@ephemeralHookObjectMethod - - val looksLikeDeviceBridge = result.containsKey("getDisplayWidth") || result.containsKey("getDisplayHeight") - if (!looksLikeDeviceBridge) return@ephemeralHookObjectMethod - if (result.containsKey(getImportsFunctionName)) return@ephemeralHookObjectMethod - - result[getImportsFunctionName] = newComposerFunction { - pushUntyped(importedFunctions) - true - } - } - } - } -} \ No newline at end of file diff --git a/core/src/main/kotlin/me/eternal/purrfectsnap/core/wrapper/impl/composer/ComposerContext.kt b/core/src/main/kotlin/me/eternal/purrfectsnap/core/wrapper/impl/composer/ComposerContext.kt deleted file mode 100644 index c02d131e..00000000 --- a/core/src/main/kotlin/me/eternal/purrfectsnap/core/wrapper/impl/composer/ComposerContext.kt +++ /dev/null @@ -1,29 +0,0 @@ -package me.eternal.purrfectsnap.core.wrapper.impl.composer - -import de.robv.android.xposed.XposedHelpers -import me.eternal.purrfectsnap.core.wrapper.AbstractWrapper -import java.lang.ref.WeakReference -import java.lang.reflect.Proxy - -class ComposerContext(obj: Any): AbstractWrapper(obj) { - val componentPath by field("componentPath") - val moduleName by field("moduleName") - val componentContext by field>("componentContext") - - val viewModel: Any? - get() = runCatching { XposedHelpers.getObjectField(instanceNonNull(), "innerViewModel") }.getOrNull() - ?: runCatching { XposedHelpers.getObjectField(instanceNonNull(), "viewModel") }.getOrNull() - ?: instanceNonNull()::class.java.methods.firstOrNull { it.name == "getViewModel" && it.parameterTypes.isEmpty() }?.invoke(instanceNonNull()) - - fun enqueueNextRenderCallback(callback: () -> Unit) { - val method = instanceNonNull()::class.java.methods.firstOrNull { - it.name == "onNextLayout" - } - method?.invoke(instanceNonNull(), Proxy.newProxyInstance( - instanceNonNull()::class.java.classLoader, - arrayOf(method.parameterTypes[0]) - ) { _, _, _ -> - callback() - }) - } -} diff --git a/core/src/main/kotlin/me/eternal/purrfectsnap/core/wrapper/impl/composer/ComposerMarshaller.kt b/core/src/main/kotlin/me/eternal/purrfectsnap/core/wrapper/impl/composer/ComposerMarshaller.kt deleted file mode 100644 index 29249317..00000000 --- a/core/src/main/kotlin/me/eternal/purrfectsnap/core/wrapper/impl/composer/ComposerMarshaller.kt +++ /dev/null @@ -1,13 +0,0 @@ -package me.eternal.purrfectsnap.core.wrapper.impl.composer - -import me.eternal.purrfectsnap.core.wrapper.AbstractWrapper - -class ComposerMarshaller(obj: Any): AbstractWrapper(obj) { - private val getUntypedMethod by lazy { instanceNonNull().javaClass.methods.first { it.name == "getUntyped" } } - private val getSizeMethod by lazy { instanceNonNull().javaClass.methods.first { it.name == "getSize" } } - private val pushUntypedMethod by lazy { instanceNonNull().javaClass.methods.first { it.name == "pushUntyped" } } - - fun getUntyped(index: Int): Any? = getUntypedMethod.invoke(instanceNonNull(), index) - fun getSize() = getSizeMethod.invoke(instanceNonNull()) as Int - fun pushUntyped(value: Any?): Any? = pushUntypedMethod.invoke(instanceNonNull(), value) -} \ No newline at end of file diff --git a/core/src/main/kotlin/me/eternal/purrfectsnap/core/wrapper/impl/composer/ComposerViewNode.kt b/core/src/main/kotlin/me/eternal/purrfectsnap/core/wrapper/impl/composer/ComposerViewNode.kt deleted file mode 100644 index e5f336b1..00000000 --- a/core/src/main/kotlin/me/eternal/purrfectsnap/core/wrapper/impl/composer/ComposerViewNode.kt +++ /dev/null @@ -1,62 +0,0 @@ -package me.eternal.purrfectsnap.core.wrapper.impl.composer - -import me.eternal.purrfectsnap.core.PurrfectSnap -import me.eternal.purrfectsnap.core.wrapper.AbstractWrapper -import java.lang.reflect.Proxy - -fun createComposerFunction(block: (args: Array<*>) -> Any?): Any { - val adapter = PurrfectSnap.classCache.composerFunctionActionAdapter - ?: throw IllegalStateException("composerFunctionActionAdapter not available") - val action = PurrfectSnap.classCache.composerAction - ?: throw IllegalStateException("composerAction not available") - - return adapter.constructors.first().newInstance( - Proxy.newProxyInstance( - action.classLoader, - arrayOf(action), - ) { _, _, args -> - block(args?.get(0) as Array<*>) - } - ) -} - -class ComposerViewNode(obj: Long) : AbstractWrapper(obj) { - companion object { - fun fromNode(composerViewNode: Any?): ComposerViewNode? { - return (composerViewNode?.javaClass?.methods?.firstOrNull { - it.name == "getNativeHandle" - }?.invoke(composerViewNode) as? Long)?.let { ComposerViewNode(it) } ?: return null - } - } - - fun getAttribute(name: String): Any? { - return PurrfectSnap.classCache.nativeBridge?.methods?.firstOrNull { - it.name == "getValueForAttribute" - }?.invoke(null, instanceNonNull(), name) - } - - fun setAttribute(name: String, value: Any) { - PurrfectSnap.classCache.nativeBridge?.methods?.firstOrNull { - it.name == "setValueForAttribute" - }?.invoke(null, instanceNonNull(), name, value, false) - } - - fun getChildren(): List { - val children = PurrfectSnap.classCache.nativeBridge?.methods?.firstOrNull { - it.name == "getRetainedViewNodeChildren" - }?.invoke(null, instanceNonNull(), 1) as? LongArray ?: return emptyList() - return children.map { ComposerViewNode(it) } - } - - fun getClassName(): String { - return PurrfectSnap.classCache.nativeBridge?.methods?.firstOrNull { - it.name == "getViewClassName" - }?.invoke(null, instanceNonNull())?.toString() ?: "" - } - - override fun toString(): String { - return PurrfectSnap.classCache.nativeBridge?.methods?.firstOrNull { - it.name == "getViewNodeDebugDescription" - }?.invoke(null, instanceNonNull())?.toString() ?: "" - } -} diff --git a/gradle.properties b/gradle.properties index 60165411..d1c848bb 100644 --- a/gradle.properties +++ b/gradle.properties @@ -6,7 +6,6 @@ kotlin.code.style=official android.nonTransitiveRClass=true org.gradle.configuration-cache=true org.gradle.configuration-cache.problems=warn -org.gradle.java.home=C\:\\Program\ Files\\Java\\jdk-21 nativeAbis=arm64-v8a APP_VERSION_NAME=1.1.0 diff --git a/native/rust/src/modules/composer_hook.rs b/native/rust/src/modules/composer_hook.rs deleted file mode 100644 index 785387ca..00000000 --- a/native/rust/src/modules/composer_hook.rs +++ /dev/null @@ -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>>> = Lazy::new(|| Mutex::new(HashMap::new())); -static COMPOSER_LOADER_DATA: Mutex> = 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 = 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::().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 = AtomicPtr::new(std::ptr::null_mut()); -#[cfg(target_arch = "aarch64")] -static GLOBAL_CTX: AtomicPtr = AtomicPtr::new(std::ptr::null_mut()); - -#[cfg(target_arch = "aarch64")] -static JS_EVAL_ORIGINAL2: Lazy 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, - "\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"); - } - } -} diff --git a/native/rust/src/modules/util/composer_utils.rs b/native/rust/src/modules/util/composer_utils.rs deleted file mode 100644 index c1573449..00000000 --- a/native/rust/src/modules/util/composer_utils.rs +++ /dev/null @@ -1,141 +0,0 @@ -use std::{io::Error, string::FromUtf8Error}; - -#[derive(Debug, Clone)] -pub struct ModuleTag { - tag_type: u8, - buffer: Vec, -} - -impl ModuleTag { - pub fn new(module_type: u8, buffer: Vec) -> ModuleTag { - ModuleTag { - tag_type: module_type, - buffer, - } - } - - pub fn to_string(&self) -> Result { - 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 { - &self.buffer - } - - pub fn set_buffer(&mut self, buffer: Vec) { - self.buffer = buffer; - } -} - -#[derive(Debug, Clone)] -pub struct ComposerModule { - tags: Vec<(ModuleTag, ModuleTag)>, // file name => file content -} - -impl ComposerModule { - pub fn parse(buffer: Vec) -> Result { - 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, offset: &mut usize) -> Result { - 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 { - let mut tag_buffer = Vec::new(); - - fn write_u24(buffer: &mut Vec, 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, 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; - } -} -