commit bf5d57758c330c4a05cf1b1e0bbb081e71d1289b
parent cbf02e1a4aba974e08fc965d891ec4c840bb40e9
Author: rhunk <101876869+rhunk@users.noreply.github.com>
Date: Sun, 22 Dec 2024 23:28:25 +0100
fix(core): restricted methods
Signed-off-by: rhunk <101876869+rhunk@users.noreply.github.com>
Diffstat:
3 files changed, 33 insertions(+), 18 deletions(-)
diff --git a/core/src/main/kotlin/me/rhunk/snapenhance/core/features/impl/messaging/Notifications.kt b/core/src/main/kotlin/me/rhunk/snapenhance/core/features/impl/messaging/Notifications.kt
@@ -12,7 +12,6 @@ import android.os.Build
import android.os.Bundle
import android.os.UserHandle
import de.robv.android.xposed.XposedBridge
-import de.robv.android.xposed.XposedHelpers
import kotlinx.coroutines.*
import me.rhunk.snapenhance.common.data.ContentType
import me.rhunk.snapenhance.common.data.FileType
@@ -30,6 +29,7 @@ import me.rhunk.snapenhance.core.features.impl.downloader.decoder.MessageDecoder
import me.rhunk.snapenhance.core.features.impl.experiments.BetterTranscript
import me.rhunk.snapenhance.core.features.impl.spying.StealthMode
import me.rhunk.snapenhance.core.util.hook.HookStage
+import me.rhunk.snapenhance.core.util.hook.findRestrictedConstructor
import me.rhunk.snapenhance.core.util.hook.findRestrictedMethod
import me.rhunk.snapenhance.core.util.hook.hook
import me.rhunk.snapenhance.core.util.ktx.setObjectField
@@ -69,13 +69,7 @@ class Notifications : Feature("Notifications") {
private val sentNotifications = mutableMapOf<Int, String>() // notificationId => conversationId
private val notifyAsUserMethod by lazy {
- XposedHelpers.findMethodExact(
- NotificationManager::class.java, "notifyAsUser",
- String::class.java,
- Int::class.javaPrimitiveType,
- Notification::class.java,
- UserHandle::class.java
- )
+ NotificationManager::class.java.findRestrictedMethod { it.name == "notifyAsUser" } ?: throw NoSuchMethodException("notifyAsUser")
}
private val notificationManager by lazy {
@@ -85,11 +79,9 @@ class Notifications : Feature("Notifications") {
private val translations by lazy { context.translation.getCategory("better_notifications") }
private val config by lazy { context.config.messaging.betterNotifications }
- private fun newNotificationBuilder(notification: Notification) = XposedHelpers.newInstance(
- Notification.Builder::class.java,
- context.androidContext,
- notification
- ) as Notification.Builder
+ private fun newNotificationBuilder(notification: Notification) = Notification.Builder::class.java.findRestrictedConstructor {
+ it.parameterTypes.size == 2 && it.parameterTypes[1] == Notification::class.java
+ }?.newInstance(context.androidContext, notification) as? Notification.Builder ?: throw NoSuchMethodException("Notification.Builder")
private fun setNotificationText(notification: Notification, text: String) {
notification.extras.putString("android.text", text)
diff --git a/core/src/main/kotlin/me/rhunk/snapenhance/core/messaging/ConversationExporter.kt b/core/src/main/kotlin/me/rhunk/snapenhance/core/messaging/ConversationExporter.kt
@@ -3,7 +3,6 @@ package me.rhunk.snapenhance.core.messaging
import android.util.Base64InputStream
import android.util.Base64OutputStream
import com.google.gson.stream.JsonWriter
-import de.robv.android.xposed.XposedHelpers
import me.rhunk.snapenhance.common.BuildConfig
import me.rhunk.snapenhance.common.data.ContentType
import me.rhunk.snapenhance.common.database.impl.FriendFeedEntry
@@ -11,6 +10,7 @@ import me.rhunk.snapenhance.common.database.impl.FriendInfo
import me.rhunk.snapenhance.common.util.snap.MediaDownloaderHelper
import me.rhunk.snapenhance.core.ModContext
import me.rhunk.snapenhance.core.features.impl.downloader.decoder.MessageDecoder
+import me.rhunk.snapenhance.core.util.hook.findRestrictedConstructor
import me.rhunk.snapenhance.core.wrapper.impl.Message
import me.rhunk.snapenhance.core.wrapper.impl.SnapUUID
import java.io.BufferedInputStream
@@ -47,6 +47,24 @@ class ConversationExporter(
private val outputFileStream by lazy { outputFile.outputStream() }
private val participants = mutableMapOf<String, Int>()
+ private val newBase64OutputStream by lazy {
+ Base64OutputStream::class.java.findRestrictedConstructor {
+ it.parameterTypes.size == 3 &&
+ it.parameterTypes[0] == OutputStream::class.java &&
+ it.parameterTypes[1] == Int::class.javaPrimitiveType &&
+ it.parameterTypes[2] == Boolean::class.javaPrimitiveType
+ } ?: throw Throwable("Failed to find Base64OutputStream constructor")
+ }
+
+ private val newBase64InputStream by lazy {
+ Base64InputStream::class.java.findRestrictedConstructor {
+ it.parameterTypes.size == 3 &&
+ it.parameterTypes[0] == InputStream::class.java &&
+ it.parameterTypes[1] == Int::class.javaPrimitiveType &&
+ it.parameterTypes[2] == Boolean::class.javaPrimitiveType
+ } ?: throw Throwable("Failed to find Base64InputStream constructor")
+ }
+
fun init() {
when (exportParams.exportFormat) {
ExportFormat.TEXT -> {
@@ -129,8 +147,7 @@ class ConversationExporter(
outputFileStream.write("<div class=\"media-$mediaKey\"><!-- ".toByteArray())
mediaFile.inputStream().use {
val deflateInputStream = DeflaterInputStream(it, Deflater(Deflater.BEST_SPEED, true))
- (XposedHelpers.newInstance(
- Base64InputStream::class.java,
+ (newBase64InputStream.newInstance(
deflateInputStream,
android.util.Base64.DEFAULT or android.util.Base64.NO_WRAP,
true
@@ -252,8 +269,7 @@ class ConversationExporter(
//write the json file
outputFileStream.write("<script type=\"application/json\" class=\"exported_content\">".toByteArray())
- (XposedHelpers.newInstance(
- Base64OutputStream::class.java,
+ (newBase64OutputStream.newInstance(
outputFileStream,
android.util.Base64.DEFAULT or android.util.Base64.NO_WRAP,
true
diff --git a/core/src/main/kotlin/me/rhunk/snapenhance/core/util/hook/Hooker.kt b/core/src/main/kotlin/me/rhunk/snapenhance/core/util/hook/Hooker.kt
@@ -4,6 +4,7 @@ import de.robv.android.xposed.XC_MethodHook
import de.robv.android.xposed.XposedBridge
import me.rhunk.snapenhance.common.logger.AbstractLogger
import org.lsposed.hiddenapibypass.HiddenApiBypass
+import java.lang.reflect.Constructor
import java.lang.reflect.Member
import java.lang.reflect.Method
import java.lang.reflect.Modifier
@@ -190,3 +191,9 @@ fun Class<*>.findRestrictedMethod(
): Method? {
return declaredMethods.find(predicate) ?: HiddenApiBypass.getDeclaredMethods(this).filterIsInstance<Method>().find(predicate)
}
+
+fun Class<*>.findRestrictedConstructor(
+ predicate: (Constructor<*>) -> Boolean
+): Constructor<*>? {
+ return declaredConstructors.find(predicate) ?: HiddenApiBypass.getDeclaredMethods(this).filterIsInstance<Constructor<*>>().find(predicate)
+}