commit a339e1190db48478e3bc3d06e9c9d634564b9f73
parent b06c87c4643a15ecfa1a2e7526a5f2c8b5347a21
Author: rhunk <101876869+rhunk@users.noreply.github.com>
Date: Fri, 15 Mar 2024 21:29:24 +0100
fix(core): add friend source spoof
Diffstat:
6 files changed, 101 insertions(+), 48 deletions(-)
diff --git a/common/src/main/assets/lang/en_US.json b/common/src/main/assets/lang/en_US.json
@@ -881,7 +881,6 @@
"spotlight": "Spotlight"
},
"add_friend_source_spoof": {
- "added_by_quick_add": "By Quick Add",
"added_by_username": "By Username",
"added_by_mention": "By Mention",
"added_by_group_chat": "By Group Chat",
diff --git a/common/src/main/kotlin/me/rhunk/snapenhance/common/config/impl/Experimental.kt b/common/src/main/kotlin/me/rhunk/snapenhance/common/config/impl/Experimental.kt
@@ -43,7 +43,6 @@ class Experimental : ConfigContainer() {
requireRestart()
}
val addFriendSourceSpoof = unique("add_friend_source_spoof",
- "added_by_quick_add",
"added_by_username",
"added_by_mention",
"added_by_group_chat",
diff --git a/common/src/main/kotlin/me/rhunk/snapenhance/common/data/SnapEnums.kt b/common/src/main/kotlin/me/rhunk/snapenhance/common/data/SnapEnums.kt
@@ -173,4 +173,48 @@ enum class QuotedMessageContentStatus {
JOINEDAFTERORIGINALMESSAGESENT,
UNAVAILABLE,
STORYMEDIADELETEDBYPOSTER
+}
+
+enum class FriendAddSource(
+ val id: Int
+) {
+ UNKNOWN(0),
+ PHONE(1),
+ USERNAME(2),
+ QR_CODE(3),
+ ADDED_ME_BACK(4),
+ NEARBY(5),
+ SUGGESTED(6),
+ OFFICIAL_STORY_SEARCH(7),
+ DEEP_LINK(8),
+ INVITE(9),
+ STORY_CHROME(10),
+ SHARED_USERNAME(11),
+ SHARED_STORY(12),
+ GROUP_CHAT(13),
+ SHAZAM(14),
+ MOB(15),
+ FEATURED_OFFICIAL_STORY(16),
+ OUR_STORY(17),
+ INFLUENCER_RECOMMENDATION(18),
+ DISPLAY_NAME(198),
+ TEST(20),
+ MENTION(21),
+ SUBSCRIPTION(22),
+ MENTION_STICKER(23),
+ SNAPCODE_STICKER(24),
+ SPOTLIGHT(25),
+ PUBLIC_PROFILE(26),
+ LENS(27),
+ CHAT(28),
+ SNAP_ANYONE(29),
+ COMMUNITY(30),
+ NEARBY_FRIENDS(31),
+ SEARCH(32);
+
+ companion object {
+ fun fromId(id: Int): FriendAddSource {
+ return entries.firstOrNull { it.id == id } ?: UNKNOWN
+ }
+ }
}
\ No newline at end of file
diff --git a/core/src/main/kotlin/me/rhunk/snapenhance/core/action/impl/BulkMessagingAction.kt b/core/src/main/kotlin/me/rhunk/snapenhance/core/action/impl/BulkMessagingAction.kt
@@ -445,7 +445,7 @@ class BulkMessagingAction : AbstractAction() {
private fun removeFriend(userId: String) {
context.mappings.useMapper(FriendRelationshipChangerMapper::class) {
val friendRelationshipChangerInstance = context.feature(AddFriendSourceSpoof::class).friendRelationshipChangerInstance!!
- val removeMethod = removeFriendClass.getAsClass()?.methods?.first {
+ val removeMethod = friendshipRelationshipChangerKtx.getAsClass()?.methods?.first {
it.name == removeFriendMethod.getAsString()
} ?: throw Exception("Failed to find removeFriend method")
diff --git a/core/src/main/kotlin/me/rhunk/snapenhance/core/features/impl/experiments/AddFriendSourceSpoof.kt b/core/src/main/kotlin/me/rhunk/snapenhance/core/features/impl/experiments/AddFriendSourceSpoof.kt
@@ -1,9 +1,11 @@
package me.rhunk.snapenhance.core.features.impl.experiments
+import me.rhunk.snapenhance.common.data.FriendAddSource
+import me.rhunk.snapenhance.common.util.protobuf.ProtoEditor
+import me.rhunk.snapenhance.core.event.events.impl.UnaryCallEvent
import me.rhunk.snapenhance.core.features.Feature
import me.rhunk.snapenhance.core.features.FeatureLoadParams
import me.rhunk.snapenhance.core.util.hook.HookStage
-import me.rhunk.snapenhance.core.util.hook.hook
import me.rhunk.snapenhance.core.util.hook.hookConstructor
import me.rhunk.snapenhance.mapper.impl.FriendRelationshipChangerMapper
@@ -16,51 +18,50 @@ class AddFriendSourceSpoof : Feature("AddFriendSourceSpoof", loadParams = Featur
classReference.get()?.hookConstructor(HookStage.AFTER) { param ->
friendRelationshipChangerInstance = param.thisObject()
}
+ }
- classReference.get()?.hook(addFriendMethod.get()!!, HookStage.BEFORE) { param ->
- val spoofedSource = context.config.experimental.addFriendSourceSpoof.getNullable() ?: return@hook
-
- fun setEnum(index: Int, value: String) {
- val enumData = param.arg<Any>(index)
- enumData::class.java.enumConstants.first { it.toString() == value }.let {
- param.setArg(index, it)
+ context.event.subscribe(UnaryCallEvent::class) { event ->
+ if (event.uri != "/snapchat.friending.server.FriendAction/AddFriends") return@subscribe
+ val spoofedSource = context.config.experimental.addFriendSourceSpoof.getNullable() ?: return@subscribe
+ event.buffer = ProtoEditor(event.buffer).apply {
+ edit {
+ fun setPage(value: String) {
+ remove(1)
+ addString(1, value)
}
- }
- when (spoofedSource) {
- "added_by_quick_add" -> {
- setEnum(1, "PROFILE")
- setEnum(2, "ADD_FRIENDS_BUTTON_ON_TOP_BAR_ON_FRIENDS_FEED")
- setEnum(3, "ADDED_BY_SUGGESTED")
- }
- "added_by_group_chat" -> {
- setEnum(1, "PROFILE")
- setEnum(2, "GROUP_PROFILE")
- setEnum(3, "ADDED_BY_GROUP_CHAT")
- }
- "added_by_username" -> {
- setEnum(1, "SEARCH")
- setEnum(2, "SEARCH")
- setEnum(3, "ADDED_BY_USERNAME")
- }
- "added_by_qr_code" -> {
- setEnum(1, "PROFILE")
- setEnum(2, "PROFILE")
- setEnum(3, "ADDED_BY_QR_CODE")
- }
- "added_by_mention" -> {
- setEnum(1, "CONTEXT_CARDS")
- setEnum(2, "CONTEXT_CARD")
- setEnum(3, "ADDED_BY_MENTION")
- }
- "added_by_community" -> {
- setEnum(1, "PROFILE")
- setEnum(2, "PROFILE")
- setEnum(3, "ADDED_BY_COMMUNITY")
+ editEach(2) {
+ remove(3) // remove suggestion token
+ fun setSource(source: FriendAddSource) {
+ remove(2)
+ addVarInt(2, source.id)
+ }
+
+ when (spoofedSource) {
+ "added_by_group_chat" -> {
+ setPage("group_profile")
+ setSource(FriendAddSource.GROUP_CHAT)
+ }
+ "added_by_username" -> {
+ setPage("search")
+ setSource(FriendAddSource.USERNAME)
+ }
+ "added_by_qr_code" -> {
+ setPage("scan_snapcode")
+ setSource(FriendAddSource.QR_CODE)
+ }
+ "added_by_mention" -> {
+ setPage("context_card")
+ setSource(FriendAddSource.MENTION)
+ }
+ "added_by_community" -> {
+ setPage("profile")
+ setSource(FriendAddSource.COMMUNITY)
+ }
+ }
}
- else -> return@hook
}
- }
+ }.toByteArray()
}
}
}
\ No newline at end of file
diff --git a/mapper/src/main/kotlin/me/rhunk/snapenhance/mapper/impl/FriendRelationshipChangerMapper.kt b/mapper/src/main/kotlin/me/rhunk/snapenhance/mapper/impl/FriendRelationshipChangerMapper.kt
@@ -9,9 +9,9 @@ import java.lang.reflect.Modifier
class FriendRelationshipChangerMapper : AbstractClassMapper("FriendRelationshipChanger") {
val classReference = classReference("class")
- val addFriendMethod = string("addFriendMethod")
- val removeFriendClass = classReference("removeFriendClass")
+ val friendshipRelationshipChangerKtx = classReference("removeFriendClass")
+ val addFriendMethod = string("addFriendMethod")
val removeFriendMethod = string("removeFriendMethod")
init {
@@ -28,7 +28,6 @@ class FriendRelationshipChangerMapper : AbstractClassMapper("FriendRelationshipC
this@FriendRelationshipChangerMapper.apply {
classReference.set(classDef.getClassName())
- addFriendMethod.set(addFriendDexMethod.name)
}
return@mapper
@@ -45,8 +44,19 @@ class FriendRelationshipChangerMapper : AbstractClassMapper("FriendRelationshipC
getClass(it.parameterTypes[3])?.getClassName()?.endsWith("InteractionPlacementInfo") == true
} ?: continue
- removeFriendClass.set(classDef.getClassName())
+ friendshipRelationshipChangerKtx.set(classDef.getClassName())
removeFriendMethod.set(removeFriendDexMethod.name)
+
+ val addFriendDexMethod = classDef.methods.firstOrNull {
+ Modifier.isStatic(it.accessFlags) &&
+ it.parameterTypes.size == 5 &&
+ it.parameterTypes[1] == "Ljava/lang/String;" &&
+ getClass(it.parameterTypes[2])?.isEnum() == true &&
+ getClass(it.parameterTypes[4])?.isEnum() == true &&
+ it.parameterTypes[5] == "I"
+ } ?: return@mapper
+
+ addFriendMethod.set(addFriendDexMethod.name)
return@mapper
}
}