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

@ -520,13 +520,19 @@
"name": "No Friend Score Delay",
"description": "Removes the delay when viewing a Friends Score"
},
"e2e_encryption": {
"e2ee": {
"name": "End-To-End Encryption",
"description": "Encrypts your messages with AES using a shared secret key\nMake sure to save your key somewhere safe!"
},
"encrypted_message_indicator": {
"name": "Encrypted Message Indicator",
"description": "Adds a \uD83D\uDD12 emoji next to encrypted messages"
"description": "Encrypts your messages with AES using a shared secret key\nMake sure to save your key somewhere safe!",
"properties": {
"encrypted_message_indicator": {
"name": "Encrypted Message Indicator",
"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": {
"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 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> {

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 unlimitedMultiSnap = boolean("unlimited_multi_snap") { addNotices(FeatureNotice.BAN_RISK)}
val noFriendScoreDelay = boolean("no_friend_score_delay")
val useE2EEncryption = boolean("e2e_encryption")
val encryptedMessageIndicator = boolean("encrypted_message_indicator") { addNotices(FeatureNotice.UNSTABLE) }
val e2eEncryption = container("e2ee", E2EEConfig())
val hiddenSnapchatPlusFeatures = boolean("hidden_snapchat_plus_features") { addNotices(FeatureNotice.BAN_RISK, FeatureNotice.UNSTABLE) }
val addFriendSourceSpoof = unique("add_friend_source_spoof",
"added_by_username",

View File

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

View File

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

View File

@ -43,7 +43,7 @@ enum class MessagingRuleType(
}
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() {
while (offset < buffer.size) {
val tag = readVarInt().toInt()
val id = tag ushr 3
val type = WireType.fromValue(tag and 0x7) ?: break
try {
val tag = readVarInt().toInt()
val id = tag ushr 3
val type = WireType.fromValue(tag and 0x7) ?: break
val value = when (type) {
WireType.VARINT -> readVarInt()
WireType.FIXED64 -> {

View File

@ -1,13 +1,16 @@
package me.rhunk.snapenhance.features.impl.experiments
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.ViewGroup
import android.view.ViewGroup.LayoutParams
import android.view.ViewGroup.MarginLayoutParams
import android.widget.Button
import android.widget.LinearLayout
import android.widget.RelativeLayout
import android.widget.TextView
import me.rhunk.snapenhance.core.event.events.impl.AddViewEvent
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.hookConstructor
import me.rhunk.snapenhance.ui.ViewAppearanceHelper
import me.rhunk.snapenhance.ui.addForegroundDrawable
import me.rhunk.snapenhance.ui.removeForegroundDrawable
import java.security.MessageDigest
import kotlin.random.Random
@ -37,7 +42,7 @@ class EndToEndEncryption : MessagingRuleFeature(
MessagingRuleType.E2E_ENCRYPTION,
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() }
companion object {
@ -185,13 +190,11 @@ class EndToEndEncryption : MessagingRuleFeature(
}
}
val encryptedMessageIndicator by context.config.experimental.encryptedMessageIndicator
val chatMessageContentContainerId = context.resources.getIdentifier("chat_message_content_container", "id", context.androidContext.packageName)
val encryptedMessageIndicator by context.config.experimental.e2eEncryption.encryptedMessageIndicator
// hook view binder to add special buttons
val receivePublicKeyTag = Random.nextLong().toString(16)
val receiveSecretTag = Random.nextLong().toString(16)
val encryptedMessageTag = Random.nextLong().toString(16)
context.event.subscribe(BindViewEvent::class) { event ->
event.chatMessage { conversationId, messageId ->
@ -206,28 +209,15 @@ class EndToEndEncryption : MessagingRuleFeature(
}
if (encryptedMessageIndicator) {
viewGroup.findViewWithTag<ViewGroup>(encryptedMessageTag)?.also {
val chatMessageContentContainer = viewGroup.findViewById<View>(chatMessageContentContainerId) as? LinearLayout ?: return@chatMessage
it.removeView(chatMessageContentContainer)
viewGroup.removeView(it)
viewGroup.addView(chatMessageContentContainer, 0)
}
viewGroup.removeForegroundDrawable("encryptedMessage")
if (encryptedMessages.contains(messageId.toLong())) {
val chatMessageContentContainer = viewGroup.findViewById<View>(chatMessageContentContainerId) as? LinearLayout ?: return@chatMessage
viewGroup.removeView(chatMessageContentContainer)
viewGroup.addView(RelativeLayout(viewGroup.context).apply {
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)
viewGroup.addForegroundDrawable("encryptedMessage", ShapeDrawable(object: Shape() {
override fun draw(canvas: Canvas, paint: Paint) {
paint.textSize = 20f
canvas.drawText("\uD83D\uDD12", 0f, canvas.height / 2f, paint)
}
}))
}
}
@ -355,13 +345,25 @@ class EndToEndEncryption : MessagingRuleFeature(
override fun asyncInit() {
if (!isEnabled) return
// trick to disable fidelius encryption
context.event.subscribe(SendMessageWithContentEvent::class) { param ->
val messageContent = param.messageContent
val destinations = param.destinations
if (destinations.conversations.none { getState(it.toString()) }) return@subscribe
val forceMessageEncryption by context.config.experimental.e2eEncryption.forceMessageEncryption
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) {
messageContent.contentType = ContentType.EXTERNAL_MEDIA
}

View File

@ -1,6 +1,9 @@
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 com.google.gson.JsonObject
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.hook.HookStage
import me.rhunk.snapenhance.hook.Hooker
import me.rhunk.snapenhance.ui.addForegroundDrawable
import me.rhunk.snapenhance.ui.removeForegroundDrawable
import java.util.concurrent.Executors
import kotlin.time.ExperimentalTime
import kotlin.time.measureTime
@ -155,14 +160,18 @@ class MessageLogger : Feature("MessageLogger",
context.event.subscribe(BindViewEvent::class) { event ->
event.chatMessage { conversationId, messageId ->
val foreground = event.view.foreground
if (foreground is ColorDrawable && foreground.color == DELETED_MESSAGE_COLOR) {
event.view.foreground = null
}
event.view.removeForegroundDrawable("deletedMessage")
getServerMessageIdentifier(conversationId, messageId.toLong())?.let { serverMessageId ->
if (!deletedMessageCache.contains(serverMessageId)) 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.content.Context
import android.content.res.ColorStateList
import android.graphics.Canvas
import android.graphics.Color
import android.graphics.Paint
import android.graphics.drawable.ColorDrawable
import android.graphics.drawable.Drawable
import android.graphics.drawable.ShapeDrawable
import android.graphics.drawable.StateListDrawable
import android.graphics.drawable.shapes.Shape
import android.view.Gravity
import android.view.View
import android.widget.Switch
import android.widget.TextView
import me.rhunk.snapenhance.Constants
import kotlin.random.Random
fun View.applyTheme(componentWidth: Int? = null, hasRadius: Boolean = false, isAmoled: Boolean = true) {
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 {
@SuppressLint("UseSwitchCompatOrMaterialCode", "RtlHardcoded", "DiscouragedApi",
"ClickableViewAccessibility"