feat: security features

Signed-off-by: rhunk <101876869+rhunk@users.noreply.github.com>
This commit is contained in:
rhunk
2024-07-06 16:47:45 +02:00
parent f9974b0e84
commit 8d17d55c83
19 changed files with 322 additions and 118 deletions

View File

@@ -153,13 +153,11 @@ class ModContext(
}
fun reloadNativeConfig() {
if (config.experimental.nativeHooks.globalState != true) return
native.loadNativeConfig(
NativeConfig(
disableBitmoji = config.experimental.nativeHooks.disableBitmoji.get(),
disableMetrics = config.global.disableMetrics.get(),
composerHooks = config.experimental.nativeHooks.composerHooks.globalState == true,
remapExecutable = config.experimental.nativeHooks.remapExecutable.get(),
customEmojiFontPath = getCustomEmojiFontPath(this)
)
)

View File

@@ -4,7 +4,8 @@ import android.app.Activity
import android.content.Context
import android.content.res.Resources
import android.os.Build
import dalvik.system.BaseDexClassLoader
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.outlined.Cancel
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
@@ -14,6 +15,9 @@ import me.rhunk.snapenhance.bridge.SyncCallback
import me.rhunk.snapenhance.common.Constants
import me.rhunk.snapenhance.common.ReceiversConfig
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.data.FriendStreaks
import me.rhunk.snapenhance.common.data.MessagingFriendInfo
import me.rhunk.snapenhance.common.data.MessagingGroupInfo
@@ -27,7 +31,6 @@ import me.rhunk.snapenhance.core.util.LSPatchUpdater
import me.rhunk.snapenhance.core.util.hook.HookAdapter
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 kotlin.reflect.KClass
import kotlin.system.exitProcess
import kotlin.system.measureTimeMillis
@@ -166,6 +169,8 @@ class SnapEnhance {
}
}
private var safeMode = false
private fun onActivityCreate(activity: Activity) {
measureTimeMillis {
with(appContext) {
@@ -173,6 +178,10 @@ class SnapEnhance {
inAppOverlay.onActivityCreate(activity)
scriptRuntime.eachModule { callFunction("module.onSnapMainActivityCreate", activity) }
actionManager.onActivityCreate()
if (safeMode) {
appContext.inAppOverlay.showStatusToast(Icons.Outlined.Cancel, "Failed to load security features! Snapchat may not work properly.", durationMs = 5000)
}
}
}.also { time ->
appContext.log.verbose("onActivityCreate took $time")
@@ -180,36 +189,58 @@ class SnapEnhance {
}
private fun initNative() {
// don't initialize native when not logged in
if (
!appContext.isLoggedIn() &&
appContext.bridgeClient.getDebugProp("force_native_load", null) != "true"
) return
if (appContext.config.experimental.nativeHooks.globalState != true) return
val lateInit = appContext.native.initOnce {
nativeUnaryCallCallback = { request ->
appContext.event.post(NativeUnaryCallEvent(request.uri, request.buffer)) {
request.buffer = buffer
request.canceled = canceled
}
}
appContext.reloadNativeConfig()
}
if (appContext.bridgeClient.getDebugProp("disable_sif", "false") != "true") {
runCatching {
appContext.native.loadSharedLibrary(
appContext.fileHandlerManager.getFileHandle(FileHandleScope.INTERNAL.key, InternalFileHandleType.SIF.key)
.toWrapper()
.readBytes()
.takeIf {
it.isNotEmpty()
} ?: throw IllegalStateException("buffer is empty")
)
appContext.log.verbose("loaded sif")
}.onFailure {
safeMode = true
appContext.log.error("Failed to load sif", it)
}
} else {
appContext.log.warn("sif is disabled")
}
lateinit var unhook: () -> Unit
Runtime::class.java.declaredMethods.first {
it.name == "loadLibrary0" && it.parameterTypes.contentEquals(
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) arrayOf(Class::class.java, String::class.java)
else arrayOf(ClassLoader::class.java, String::class.java)
)
}.hook(HookStage.AFTER) { param ->
val libName = param.arg<String>(1)
if (libName != "client") return@hook
unhook()
appContext.native.initOnce {
nativeUnaryCallCallback = { request ->
appContext.event.post(NativeUnaryCallEvent(request.uri, request.buffer)) {
request.buffer = buffer
request.canceled = canceled
}
}.apply {
if (safeMode) {
hook(HookStage.BEFORE) { param ->
if (param.arg<String>(1) != "scplugin") return@hook
appContext.log.warn("Can't load scplugin in safe mode")
Thread.sleep(Long.MAX_VALUE)
}
appContext.reloadNativeConfig()
}
BaseDexClassLoader::class.java.hookConstructor(HookStage.AFTER) {
appContext.native.hideAnonymousDexFiles()
}
}.also { unhook = { it.unhook() } }
lateinit var unhook: () -> Unit
hook(HookStage.AFTER) { param ->
val libName = param.arg<String>(1)
if (libName != "client") return@hook
unhook()
appContext.log.verbose("libclient lateInit")
lateInit()
}.also { unhook = { it.unhook() } }
}
}
private fun initConfigListener() {

View File

@@ -11,7 +11,7 @@ import me.rhunk.snapenhance.core.util.LSPatchUpdater
import me.rhunk.snapenhance.core.util.hook.HookStage
import me.rhunk.snapenhance.core.util.hook.hook
class DeviceSpooferHook: Feature("device_spoofer") {
class DeviceSpooferHook: Feature("Device Spoofer") {
private fun hookInstallerPackageName() {
context.androidContext.packageManager::class.java.hook("getInstallerPackageName", HookStage.BEFORE) { param ->
param.setResult("com.android.vending")