refactor: security features
- remove remote shared library manager - prevent plugin from loading in newer versions Signed-off-by: rhunk <101876869+rhunk@users.noreply.github.com>
This commit is contained in:
@@ -75,7 +75,7 @@ class ModContext(
|
||||
val isDeveloper by lazy { config.scripting.developerMode.get() }
|
||||
|
||||
var isMainActivityPaused = true
|
||||
var isSafeMode = false
|
||||
var disablePlugin = false
|
||||
|
||||
fun <T : Feature> feature(featureClass: KClass<T>): T {
|
||||
return features.get(featureClass)!!
|
||||
|
||||
@@ -18,12 +18,13 @@ import androidx.compose.ui.text.style.TextAlign
|
||||
import androidx.compose.ui.unit.dp
|
||||
import kotlinx.coroutines.delay
|
||||
import me.rhunk.snapenhance.common.bridge.FileHandleScope
|
||||
import me.rhunk.snapenhance.common.bridge.InternalFileHandleType
|
||||
import me.rhunk.snapenhance.common.bridge.toWrapper
|
||||
import me.rhunk.snapenhance.common.config.MOD_DETECTION_VERSION_CHECK
|
||||
import me.rhunk.snapenhance.common.config.VersionRequirement
|
||||
import me.rhunk.snapenhance.common.ui.createComposeView
|
||||
import me.rhunk.snapenhance.core.event.events.impl.UnaryCallEvent
|
||||
import me.rhunk.snapenhance.core.ui.CustomComposable
|
||||
import me.rhunk.snapenhance.core.util.ktx.getObjectField
|
||||
|
||||
class SecurityFeatures(
|
||||
private val context: ModContext
|
||||
@@ -38,67 +39,55 @@ class SecurityFeatures(
|
||||
transact(this, 0)?.toString(2)?.padStart(32, '0')?.count { it == '1' }
|
||||
}
|
||||
|
||||
private fun isSafeMode(): Boolean {
|
||||
fun init() {
|
||||
val snapchatVersionCode = context.androidContext.packageManager?.getPackageInfo(context.androidContext.packageName, 0)?.longVersionCode ?: throw IllegalStateException("Failed to get version code")
|
||||
val shouldUseSafeMode = MOD_DETECTION_VERSION_CHECK.checkVersion(snapchatVersionCode)?.second == VersionRequirement.OLDER_REQUIRED
|
||||
var shouldDisablePlugin = MOD_DETECTION_VERSION_CHECK.checkVersion(snapchatVersionCode)?.second == VersionRequirement.OLDER_REQUIRED
|
||||
|
||||
// load user shared library
|
||||
context.config.experimental.nativeHooks.customSharedLibrary.get().takeIf { it.isNotEmpty() }?.let {
|
||||
runCatching {
|
||||
context.native.loadSharedLibrary(
|
||||
context.fileHandlerManager.getFileHandle(FileHandleScope.USER_IMPORT.key, it).toWrapper().readBytes()
|
||||
)
|
||||
context.log.verbose("loaded custom shared library")
|
||||
shouldDisablePlugin = false
|
||||
|
||||
lateinit var composable: CustomComposable
|
||||
composable = {
|
||||
Row(
|
||||
modifier = Modifier
|
||||
.padding(16.dp)
|
||||
.align(Alignment.TopCenter),
|
||||
) {
|
||||
Icon(Icons.Filled.Check, contentDescription = null, tint = Color(0xFF85A947))
|
||||
}
|
||||
|
||||
LaunchedEffect(Unit) {
|
||||
delay(2500)
|
||||
context.inAppOverlay.removeCustomComposable(composable)
|
||||
}
|
||||
}
|
||||
|
||||
context.inAppOverlay.addCustomComposable(composable)
|
||||
}.onFailure {
|
||||
context.log.error("Failed to load custom shared library", it)
|
||||
return true
|
||||
}
|
||||
} ?: context.bridgeClient.getDebugProp("enable_security_features", "false").takeIf { it == "true" }?.runCatching {
|
||||
context.native.loadSharedLibrary(
|
||||
context.fileHandlerManager.getFileHandle(FileHandleScope.INTERNAL.key, InternalFileHandleType.SIF.key)
|
||||
.toWrapper()
|
||||
.readBytes()
|
||||
.takeIf {
|
||||
it.isNotEmpty()
|
||||
} ?: throw IllegalStateException("Binary is empty")
|
||||
)
|
||||
context.log.verbose("loaded sif")
|
||||
}?.onFailure {
|
||||
context.log.error("Failed to load sif: " + it.message)
|
||||
return shouldUseSafeMode
|
||||
} ?: context.log.warn("Security features are disabled")
|
||||
|
||||
token // pre init token
|
||||
|
||||
val status = getStatus()
|
||||
val safeMode = shouldUseSafeMode && (status == null || status < 2)
|
||||
|
||||
if (status != null && status >= 2) {
|
||||
context.log.verbose("status=$status")
|
||||
lateinit var composable: CustomComposable
|
||||
composable = {
|
||||
Row(
|
||||
modifier = Modifier
|
||||
.padding(16.dp)
|
||||
.align(Alignment.TopCenter),
|
||||
) {
|
||||
Icon(Icons.Filled.Check, contentDescription = null, tint = Color(0xFF85A947))
|
||||
}
|
||||
|
||||
LaunchedEffect(Unit) {
|
||||
delay(2500)
|
||||
context.inAppOverlay.removeCustomComposable(composable)
|
||||
}
|
||||
}
|
||||
context.inAppOverlay.addCustomComposable(composable)
|
||||
}
|
||||
|
||||
return safeMode
|
||||
}
|
||||
if (context.bridgeClient.getDebugProp("test_mode", "false") == "true") {
|
||||
shouldDisablePlugin = false
|
||||
}
|
||||
|
||||
fun init() {
|
||||
context.isSafeMode = isSafeMode()
|
||||
context.log.verbose("isSafeMode=${context.isSafeMode}")
|
||||
if (!context.isSafeMode) return
|
||||
context.disablePlugin = shouldDisablePlugin
|
||||
context.log.verbose("disablePlugin=${context.disablePlugin}")
|
||||
if (!context.disablePlugin) return
|
||||
|
||||
context.event.subscribe(UnaryCallEvent::class) { event ->
|
||||
val callOptions = event.adapter.arg<Any>(2).let { it.javaClass.getMethod("build").invoke(it) } ?: return@subscribe
|
||||
if (callOptions.getObjectField("mAttestation") != null) {
|
||||
event.canceled = true
|
||||
}
|
||||
}
|
||||
|
||||
context.features.addActivityCreateListener { activity ->
|
||||
if (!activity.javaClass.name.endsWith("LoginSignupActivity")) return@addActivityCreateListener
|
||||
|
||||
@@ -18,7 +18,6 @@ import me.rhunk.snapenhance.common.action.EnumAction
|
||||
import me.rhunk.snapenhance.common.bridge.FileHandleScope
|
||||
import me.rhunk.snapenhance.common.bridge.InternalFileHandleType
|
||||
import me.rhunk.snapenhance.common.bridge.toWrapper
|
||||
import me.rhunk.snapenhance.common.config.MOD_DETECTION_VERSION_CHECK
|
||||
import me.rhunk.snapenhance.common.data.FriendStreaks
|
||||
import me.rhunk.snapenhance.common.data.MessagingFriendInfo
|
||||
import me.rhunk.snapenhance.common.data.MessagingGroupInfo
|
||||
@@ -33,6 +32,7 @@ import me.rhunk.snapenhance.core.util.hook.HookAdapter
|
||||
import me.rhunk.snapenhance.core.util.hook.HookStage
|
||||
import me.rhunk.snapenhance.core.util.hook.findRestrictedMethod
|
||||
import me.rhunk.snapenhance.core.util.hook.hook
|
||||
import me.rhunk.snapenhance.mapper.impl.PlatformClientAttestationMapper
|
||||
import kotlin.reflect.KClass
|
||||
import kotlin.system.exitProcess
|
||||
import kotlin.system.measureTimeMillis
|
||||
@@ -231,24 +231,22 @@ class SnapEnhance {
|
||||
else arrayOf(ClassLoader::class.java, String::class.java)
|
||||
)
|
||||
}!!.apply {
|
||||
if (appContext.isSafeMode) {
|
||||
if (appContext.disablePlugin) {
|
||||
hook(HookStage.BEFORE) { param ->
|
||||
if (param.arg<String>(1) != "scplugin") return@hook
|
||||
param.setResult(null)
|
||||
appContext.runOnUiThread {
|
||||
appContext.inAppOverlay.showStatusToast(
|
||||
Icons.Outlined.Cancel,
|
||||
"SnapEnhance is not compatible with this version of Snapchat and will result in a ban.\nUse Snapchat ${MOD_DETECTION_VERSION_CHECK.maxVersion?.first ?: "0.0.0"} or older to avoid detections.",
|
||||
durationMs = 7000,
|
||||
maxLines = 6
|
||||
)
|
||||
appContext.log.verbose("skipped scplugin load")
|
||||
|
||||
appContext.mappings.useMapper(PlatformClientAttestationMapper::class) {
|
||||
pluginNativeClass.getAsClass()?.methods?.filter {
|
||||
it.declaringClass == pluginNativeClass.getAsClass()
|
||||
}?.forEach { method ->
|
||||
method.hook(HookStage.BEFORE) {
|
||||
appContext.log.verbose("called $method")
|
||||
it.setResult(null)
|
||||
}
|
||||
} ?: error("Failed to get pluginNativeClass class")
|
||||
}
|
||||
runCatching {
|
||||
Thread.sleep(Long.MAX_VALUE)
|
||||
}.onFailure {
|
||||
appContext.log.error(it)
|
||||
}
|
||||
exitProcess(1)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user