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:
Mcore/src/main/kotlin/me/rhunk/snapenhance/core/features/impl/messaging/Notifications.kt | 18+++++-------------
Mcore/src/main/kotlin/me/rhunk/snapenhance/core/messaging/ConversationExporter.kt | 26+++++++++++++++++++++-----
Mcore/src/main/kotlin/me/rhunk/snapenhance/core/util/hook/Hooker.kt | 7+++++++
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) +}