fix(e2ee): bind view foreground

- fix MessagingRuleType npe
- fix ProtoReader exception
This commit is contained in:
rhunk
2023-09-30 01:32:20 +02:00
parent 2749b734e4
commit 476de8dc38
12 changed files with 115 additions and 54 deletions

View File

@ -159,7 +159,7 @@ class ModDatabase(
val rules = mutableListOf<MessagingRuleType>() val rules = mutableListOf<MessagingRuleType>()
while (cursor.moveToNext()) { while (cursor.moveToNext()) {
runCatching { runCatching {
rules.add(MessagingRuleType.getByName(cursor.getStringOrNull("type")!!)) rules.add(MessagingRuleType.getByName(cursor.getStringOrNull("type")!!) ?: return@runCatching)
}.onFailure { }.onFailure {
context.log.error("Failed to parse rule", it) context.log.error("Failed to parse rule", it)
} }

View File

@ -520,13 +520,19 @@
"name": "No Friend Score Delay", "name": "No Friend Score Delay",
"description": "Removes the delay when viewing a Friends Score" "description": "Removes the delay when viewing a Friends Score"
}, },
"e2e_encryption": { "e2ee": {
"name": "End-To-End Encryption", "name": "End-To-End Encryption",
"description": "Encrypts your messages with AES using a shared secret key\nMake sure to save your key somewhere safe!" "description": "Encrypts your messages with AES using a shared secret key\nMake sure to save your key somewhere safe!",
}, "properties": {
"encrypted_message_indicator": { "encrypted_message_indicator": {
"name": "Encrypted Message Indicator", "name": "Encrypted Message Indicator",
"description": "Adds a \uD83D\uDD12 emoji next to encrypted messages" "description": "Adds a \uD83D\uDD12 emoji next to encrypted messages"
},
"force_message_encryption": {
"name": "Force Message Encryption",
"description": "Prevents sending encrypted messages to people who don't have E2E Encryption enabled only when multiple conversations are selected"
}
}
}, },
"add_friend_source_spoof": { "add_friend_source_spoof": {
"name": "Add Friend Source Spoof", "name": "Add Friend Source Spoof",

View File

@ -134,7 +134,7 @@ class BridgeClient(
fun passGroupsAndFriends(groups: List<String>, friends: List<String>) = service.passGroupsAndFriends(groups, friends) fun passGroupsAndFriends(groups: List<String>, friends: List<String>) = service.passGroupsAndFriends(groups, friends)
fun getRules(targetUuid: String): List<MessagingRuleType> { fun getRules(targetUuid: String): List<MessagingRuleType> {
return service.getRules(targetUuid).map { MessagingRuleType.getByName(it) } return service.getRules(targetUuid).mapNotNull { MessagingRuleType.getByName(it) }
} }
fun getRuleIds(ruleType: MessagingRuleType): List<String> { fun getRuleIds(ruleType: MessagingRuleType): List<String> {

View File

@ -0,0 +1,8 @@
package me.rhunk.snapenhance.core.config.impl
import me.rhunk.snapenhance.core.config.ConfigContainer
class E2EEConfig : ConfigContainer(hasGlobalState = true) {
val encryptedMessageIndicator = boolean("encrypted_message_indicator")
val forceMessageEncryption = boolean("force_message_encryption")
}

View File

@ -12,8 +12,7 @@ class Experimental : ConfigContainer() {
val meoPasscodeBypass = boolean("meo_passcode_bypass") val meoPasscodeBypass = boolean("meo_passcode_bypass")
val unlimitedMultiSnap = boolean("unlimited_multi_snap") { addNotices(FeatureNotice.BAN_RISK)} val unlimitedMultiSnap = boolean("unlimited_multi_snap") { addNotices(FeatureNotice.BAN_RISK)}
val noFriendScoreDelay = boolean("no_friend_score_delay") val noFriendScoreDelay = boolean("no_friend_score_delay")
val useE2EEncryption = boolean("e2e_encryption") val e2eEncryption = container("e2ee", E2EEConfig())
val encryptedMessageIndicator = boolean("encrypted_message_indicator") { addNotices(FeatureNotice.UNSTABLE) }
val hiddenSnapchatPlusFeatures = boolean("hidden_snapchat_plus_features") { addNotices(FeatureNotice.BAN_RISK, FeatureNotice.UNSTABLE) } val hiddenSnapchatPlusFeatures = boolean("hidden_snapchat_plus_features") { addNotices(FeatureNotice.BAN_RISK, FeatureNotice.UNSTABLE) }
val addFriendSourceSpoof = unique("add_friend_source_spoof", val addFriendSourceSpoof = unique("add_friend_source_spoof",
"added_by_username", "added_by_username",

View File

@ -76,7 +76,9 @@ class EventDispatcher(
interactionType = interactionType, interactionType = interactionType,
conversationId = conversationId, conversationId = conversationId,
messageId = messageId messageId = messageId
) ).apply {
adapter = param
}
) { ) {
postHookEvent() postHookEvent()
} }

View File

@ -23,7 +23,6 @@ abstract class AbstractHookEvent : Event() {
} }
fun invokeOriginal() { fun invokeOriginal() {
canceled = true
invokeLater() invokeLater()
adapter.invokeOriginal() adapter.invokeOriginal()
} }

View File

@ -43,7 +43,7 @@ enum class MessagingRuleType(
} }
companion object { companion object {
fun getByName(name: String) = values().first { it.key == name } fun getByName(name: String) = values().firstOrNull { it.key == name }
} }
} }

View File

@ -32,10 +32,10 @@ class ProtoReader(private val buffer: ByteArray) {
private fun read() { private fun read() {
while (offset < buffer.size) { while (offset < buffer.size) {
val tag = readVarInt().toInt()
val id = tag ushr 3
val type = WireType.fromValue(tag and 0x7) ?: break
try { try {
val tag = readVarInt().toInt()
val id = tag ushr 3
val type = WireType.fromValue(tag and 0x7) ?: break
val value = when (type) { val value = when (type) {
WireType.VARINT -> readVarInt() WireType.VARINT -> readVarInt()
WireType.FIXED64 -> { WireType.FIXED64 -> {

View File

@ -1,13 +1,16 @@
package me.rhunk.snapenhance.features.impl.experiments package me.rhunk.snapenhance.features.impl.experiments
import android.annotation.SuppressLint import android.annotation.SuppressLint
import android.graphics.Canvas
import android.graphics.Paint
import android.graphics.Rect
import android.graphics.drawable.ShapeDrawable
import android.graphics.drawable.shapes.Shape
import android.view.View import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
import android.view.ViewGroup.LayoutParams import android.view.ViewGroup.LayoutParams
import android.view.ViewGroup.MarginLayoutParams import android.view.ViewGroup.MarginLayoutParams
import android.widget.Button import android.widget.Button
import android.widget.LinearLayout
import android.widget.RelativeLayout
import android.widget.TextView import android.widget.TextView
import me.rhunk.snapenhance.core.event.events.impl.AddViewEvent import me.rhunk.snapenhance.core.event.events.impl.AddViewEvent
import me.rhunk.snapenhance.core.event.events.impl.BindViewEvent import me.rhunk.snapenhance.core.event.events.impl.BindViewEvent
@ -29,6 +32,8 @@ import me.rhunk.snapenhance.features.impl.Messaging
import me.rhunk.snapenhance.hook.HookStage import me.rhunk.snapenhance.hook.HookStage
import me.rhunk.snapenhance.hook.hookConstructor import me.rhunk.snapenhance.hook.hookConstructor
import me.rhunk.snapenhance.ui.ViewAppearanceHelper import me.rhunk.snapenhance.ui.ViewAppearanceHelper
import me.rhunk.snapenhance.ui.addForegroundDrawable
import me.rhunk.snapenhance.ui.removeForegroundDrawable
import java.security.MessageDigest import java.security.MessageDigest
import kotlin.random.Random import kotlin.random.Random
@ -37,7 +42,7 @@ class EndToEndEncryption : MessagingRuleFeature(
MessagingRuleType.E2E_ENCRYPTION, MessagingRuleType.E2E_ENCRYPTION,
loadParams = FeatureLoadParams.ACTIVITY_CREATE_SYNC or FeatureLoadParams.INIT_SYNC or FeatureLoadParams.INIT_ASYNC loadParams = FeatureLoadParams.ACTIVITY_CREATE_SYNC or FeatureLoadParams.INIT_SYNC or FeatureLoadParams.INIT_ASYNC
) { ) {
private val isEnabled get() = context.config.experimental.useE2EEncryption.get() private val isEnabled get() = context.config.experimental.e2eEncryption.globalState == true
private val e2eeInterface by lazy { context.bridgeClient.getE2eeInterface() } private val e2eeInterface by lazy { context.bridgeClient.getE2eeInterface() }
companion object { companion object {
@ -185,13 +190,11 @@ class EndToEndEncryption : MessagingRuleFeature(
} }
} }
val encryptedMessageIndicator by context.config.experimental.encryptedMessageIndicator val encryptedMessageIndicator by context.config.experimental.e2eEncryption.encryptedMessageIndicator
val chatMessageContentContainerId = context.resources.getIdentifier("chat_message_content_container", "id", context.androidContext.packageName)
// hook view binder to add special buttons // hook view binder to add special buttons
val receivePublicKeyTag = Random.nextLong().toString(16) val receivePublicKeyTag = Random.nextLong().toString(16)
val receiveSecretTag = Random.nextLong().toString(16) val receiveSecretTag = Random.nextLong().toString(16)
val encryptedMessageTag = Random.nextLong().toString(16)
context.event.subscribe(BindViewEvent::class) { event -> context.event.subscribe(BindViewEvent::class) { event ->
event.chatMessage { conversationId, messageId -> event.chatMessage { conversationId, messageId ->
@ -206,28 +209,15 @@ class EndToEndEncryption : MessagingRuleFeature(
} }
if (encryptedMessageIndicator) { if (encryptedMessageIndicator) {
viewGroup.findViewWithTag<ViewGroup>(encryptedMessageTag)?.also { viewGroup.removeForegroundDrawable("encryptedMessage")
val chatMessageContentContainer = viewGroup.findViewById<View>(chatMessageContentContainerId) as? LinearLayout ?: return@chatMessage
it.removeView(chatMessageContentContainer)
viewGroup.removeView(it)
viewGroup.addView(chatMessageContentContainer, 0)
}
if (encryptedMessages.contains(messageId.toLong())) { if (encryptedMessages.contains(messageId.toLong())) {
val chatMessageContentContainer = viewGroup.findViewById<View>(chatMessageContentContainerId) as? LinearLayout ?: return@chatMessage viewGroup.addForegroundDrawable("encryptedMessage", ShapeDrawable(object: Shape() {
viewGroup.removeView(chatMessageContentContainer) override fun draw(canvas: Canvas, paint: Paint) {
paint.textSize = 20f
viewGroup.addView(RelativeLayout(viewGroup.context).apply { canvas.drawText("\uD83D\uDD12", 0f, canvas.height / 2f, paint)
tag = encryptedMessageTag }
layoutParams = LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT) }))
addView(chatMessageContentContainer)
addView(TextView(viewGroup.context).apply {
text = "\uD83D\uDD12"
textAlignment = View.TEXT_ALIGNMENT_TEXT_END
layoutParams = LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT)
setPadding(20, 0, 20, 0)
})
}, 0)
} }
} }
@ -355,13 +345,25 @@ class EndToEndEncryption : MessagingRuleFeature(
override fun asyncInit() { override fun asyncInit() {
if (!isEnabled) return if (!isEnabled) return
// trick to disable fidelius encryption val forceMessageEncryption by context.config.experimental.e2eEncryption.forceMessageEncryption
context.event.subscribe(SendMessageWithContentEvent::class) { param ->
val messageContent = param.messageContent
val destinations = param.destinations
if (destinations.conversations.none { getState(it.toString()) }) return@subscribe
param.addInvokeLater { // trick to disable fidelius encryption
context.event.subscribe(SendMessageWithContentEvent::class) { event ->
val messageContent = event.messageContent
val destinations = event.destinations
val e2eeConversations = destinations.conversations.filter { getState(it.toString()) }
if (e2eeConversations.isEmpty()) return@subscribe
if (e2eeConversations.size != destinations.conversations.size) {
if (!forceMessageEncryption) return@subscribe
context.longToast("You can't send encrypted content to both encrypted and unencrypted conversations!")
event.canceled = true
return@subscribe
}
event.addInvokeLater {
if (messageContent.contentType == ContentType.SNAP) { if (messageContent.contentType == ContentType.SNAP) {
messageContent.contentType = ContentType.EXTERNAL_MEDIA messageContent.contentType = ContentType.EXTERNAL_MEDIA
} }

View File

@ -1,6 +1,9 @@
package me.rhunk.snapenhance.features.impl.spying package me.rhunk.snapenhance.features.impl.spying
import android.graphics.drawable.ColorDrawable import android.graphics.Canvas
import android.graphics.Paint
import android.graphics.drawable.ShapeDrawable
import android.graphics.drawable.shapes.Shape
import android.os.DeadObjectException import android.os.DeadObjectException
import com.google.gson.JsonObject import com.google.gson.JsonObject
import com.google.gson.JsonParser import com.google.gson.JsonParser
@ -12,6 +15,8 @@ import me.rhunk.snapenhance.features.Feature
import me.rhunk.snapenhance.features.FeatureLoadParams import me.rhunk.snapenhance.features.FeatureLoadParams
import me.rhunk.snapenhance.hook.HookStage import me.rhunk.snapenhance.hook.HookStage
import me.rhunk.snapenhance.hook.Hooker import me.rhunk.snapenhance.hook.Hooker
import me.rhunk.snapenhance.ui.addForegroundDrawable
import me.rhunk.snapenhance.ui.removeForegroundDrawable
import java.util.concurrent.Executors import java.util.concurrent.Executors
import kotlin.time.ExperimentalTime import kotlin.time.ExperimentalTime
import kotlin.time.measureTime import kotlin.time.measureTime
@ -155,14 +160,18 @@ class MessageLogger : Feature("MessageLogger",
context.event.subscribe(BindViewEvent::class) { event -> context.event.subscribe(BindViewEvent::class) { event ->
event.chatMessage { conversationId, messageId -> event.chatMessage { conversationId, messageId ->
val foreground = event.view.foreground event.view.removeForegroundDrawable("deletedMessage")
if (foreground is ColorDrawable && foreground.color == DELETED_MESSAGE_COLOR) {
event.view.foreground = null
}
getServerMessageIdentifier(conversationId, messageId.toLong())?.let { serverMessageId -> getServerMessageIdentifier(conversationId, messageId.toLong())?.let { serverMessageId ->
if (!deletedMessageCache.contains(serverMessageId)) return@chatMessage if (!deletedMessageCache.contains(serverMessageId)) return@chatMessage
} ?: return@chatMessage } ?: return@chatMessage
event.view.foreground = ColorDrawable(DELETED_MESSAGE_COLOR) // red with alpha
event.view.addForegroundDrawable("deletedMessage", ShapeDrawable(object: Shape() {
override fun draw(canvas: Canvas, paint: Paint) {
canvas.drawRect(0f, 0f, canvas.width.toFloat(), canvas.height.toFloat(), Paint().apply {
color = DELETED_MESSAGE_COLOR
})
}
}))
} }
} }
} }

View File

@ -4,21 +4,57 @@ import android.annotation.SuppressLint
import android.app.AlertDialog import android.app.AlertDialog
import android.content.Context import android.content.Context
import android.content.res.ColorStateList import android.content.res.ColorStateList
import android.graphics.Canvas
import android.graphics.Color import android.graphics.Color
import android.graphics.Paint
import android.graphics.drawable.ColorDrawable import android.graphics.drawable.ColorDrawable
import android.graphics.drawable.Drawable import android.graphics.drawable.Drawable
import android.graphics.drawable.ShapeDrawable import android.graphics.drawable.ShapeDrawable
import android.graphics.drawable.StateListDrawable import android.graphics.drawable.StateListDrawable
import android.graphics.drawable.shapes.Shape
import android.view.Gravity import android.view.Gravity
import android.view.View import android.view.View
import android.widget.Switch import android.widget.Switch
import android.widget.TextView import android.widget.TextView
import me.rhunk.snapenhance.Constants import me.rhunk.snapenhance.Constants
import kotlin.random.Random
fun View.applyTheme(componentWidth: Int? = null, hasRadius: Boolean = false, isAmoled: Boolean = true) { fun View.applyTheme(componentWidth: Int? = null, hasRadius: Boolean = false, isAmoled: Boolean = true) {
ViewAppearanceHelper.applyTheme(this, componentWidth, hasRadius, isAmoled) ViewAppearanceHelper.applyTheme(this, componentWidth, hasRadius, isAmoled)
} }
private val foregroundDrawableListTag = Random.nextInt(0x7000000, 0x7FFFFFFF)
@Suppress("UNCHECKED_CAST")
private fun View.getForegroundDrawables(): MutableMap<String, Drawable> {
return getTag(foregroundDrawableListTag) as? MutableMap<String, Drawable>
?: mutableMapOf<String, Drawable>().also {
setTag(foregroundDrawableListTag, it)
}
}
private fun View.updateForegroundDrawable() {
foreground = ShapeDrawable(object: Shape() {
override fun draw(canvas: Canvas, paint: Paint) {
getForegroundDrawables().forEach { (_, drawable) ->
drawable.draw(canvas)
}
}
})
}
fun View.removeForegroundDrawable(tag: String) {
getForegroundDrawables().remove(tag)?.let {
updateForegroundDrawable()
}
}
fun View.addForegroundDrawable(tag: String, drawable: Drawable) {
getForegroundDrawables()[tag] = drawable
updateForegroundDrawable()
}
object ViewAppearanceHelper { object ViewAppearanceHelper {
@SuppressLint("UseSwitchCompatOrMaterialCode", "RtlHardcoded", "DiscouragedApi", @SuppressLint("UseSwitchCompatOrMaterialCode", "RtlHardcoded", "DiscouragedApi",
"ClickableViewAccessibility" "ClickableViewAccessibility"