fix(core): device spoofer

- add lspatch install package name hook
This commit is contained in:
rhunk
2023-12-15 23:19:04 +01:00
parent abfbe86a91
commit cadcbc958e
6 changed files with 103 additions and 113 deletions

View File

@ -567,35 +567,29 @@
"name": "Spoof", "name": "Spoof",
"description": "Spoof various information about you", "description": "Spoof various information about you",
"properties": { "properties": {
"device": { "play_store_installer_package_name": {
"name": "Device", "name": "Play Store Installer Package Name",
"description": "Spoof your device information", "description": "Overrides the installer package name to com.android.vending"
"properties": { },
"fingerprint": { "fingerprint": {
"name": "Device Fingerprint", "name": "Device Fingerprint",
"description": "Spoofs your device Fingerprint" "description": "Spoofs your device Fingerprint"
}, },
"android_id": { "android_id": {
"name": "Android ID", "name": "Android ID",
"description": "Spoofs your Android ID to the specified value" "description": "Spoofs your Android ID to the specified value"
}, },
"installer_package_name": { "remove_vpn_transport_flag": {
"name": "Installer Package name", "name": "Remove VPN Transport Flag",
"description": "Spoofs the installers Package name" "description": "Prevents Snapchat from detecting VPNs"
}, },
"debug_flag": { "remove_mock_location_flag": {
"name": "Debug Flag", "name": "Remove Mock Location Flag",
"description": "Makes Snapchat debuggable" "description": "Prevents Snapchat from detecting Mock location"
}, },
"mock_location": { "randomize_persistent_device_token": {
"name": "Mock location", "name": "Randomize Persistent Device Token",
"description": "Spoofs the Mock Location device state" "description": "Generates a random device token after each login"
},
"split_classloader": {
"name": "Split Classloader",
"description": "Spoofs splitClassloader\nRequested by org.chromium.base.JNIUtils"
}
}
} }
} }
}, },

View File

@ -9,7 +9,7 @@ class Experimental : ConfigContainer() {
} }
val nativeHooks = container("native_hooks", NativeHooks()) { icon = "Memory"; requireRestart() } val nativeHooks = container("native_hooks", NativeHooks()) { icon = "Memory"; requireRestart() }
val spoof = container("spoof", Spoof()) { icon = "Fingerprint" } val spoof = container("spoof", Spoof()) { icon = "Fingerprint" ; addNotices(FeatureNotice.BAN_RISK); requireRestart() }
val convertMessageLocally = boolean("convert_message_locally") { requireRestart() } val convertMessageLocally = boolean("convert_message_locally") { requireRestart() }
val appPasscode = string("app_passcode") val appPasscode = string("app_passcode")
val appLockOnResume = boolean("app_lock_on_resume") val appLockOnResume = boolean("app_lock_on_resume")

View File

@ -1,18 +1,12 @@
package me.rhunk.snapenhance.common.config.impl package me.rhunk.snapenhance.common.config.impl
import me.rhunk.snapenhance.common.config.ConfigContainer import me.rhunk.snapenhance.common.config.ConfigContainer
import me.rhunk.snapenhance.common.config.FeatureNotice
class Spoof : ConfigContainer() { class Spoof : ConfigContainer(hasGlobalState = true) {
inner class Device : ConfigContainer(hasGlobalState = true) { val overridePlayStoreInstallerPackageName = boolean("play_store_installer_package_name")
val fingerprint = string("fingerprint") val fingerprint = string("fingerprint")
val androidId = string("android_id") val androidId = string("android_id")
val getInstallerPackageName = string("installer_package_name") val removeVpnTransportFlag = boolean("remove_vpn_transport_flag")
val debugFlag = boolean("debug_flag") val removeMockLocationFlag = boolean("remove_mock_location_flag")
val mockLocationState = boolean("mock_location") val randomizePersistentDeviceToken = boolean("randomize_persistent_device_token")
val splitClassLoader = string("split_classloader")
val isLowEndDevice = string("low_end_device")
val getDataDirectory = string("get_data_directory")
}
val device = container("device", Device()) { addNotices(FeatureNotice.BAN_RISK) }
} }

View File

@ -17,9 +17,6 @@ class SnapClassCache (
val feedEntry by lazy { findClass("com.snapchat.client.messaging.FeedEntry") } val feedEntry by lazy { findClass("com.snapchat.client.messaging.FeedEntry") }
val conversation by lazy { findClass("com.snapchat.client.messaging.Conversation") } val conversation by lazy { findClass("com.snapchat.client.messaging.Conversation") }
val feedManager by lazy { findClass("com.snapchat.client.messaging.FeedManager\$CppProxy") } val feedManager by lazy { findClass("com.snapchat.client.messaging.FeedManager\$CppProxy") }
val chromiumJNIUtils by lazy { findClass("org.chromium.base.JNIUtils")}
val chromiumBuildInfo by lazy { findClass("org.chromium.base.BuildInfo")}
val chromiumPathUtils by lazy { findClass("org.chromium.base.PathUtils")}
private fun findClass(className: String): Class<*> { private fun findClass(className: String): Class<*> {
return try { return try {

View File

@ -1,93 +1,94 @@
package me.rhunk.snapenhance.core.features.impl.experiments package me.rhunk.snapenhance.core.features.impl.experiments
import android.annotation.SuppressLint
import android.location.Location
import android.net.ConnectivityManager
import android.net.Network
import android.net.NetworkCapabilities
import android.os.Build
import me.rhunk.snapenhance.core.features.Feature import me.rhunk.snapenhance.core.features.Feature
import me.rhunk.snapenhance.core.features.FeatureLoadParams import me.rhunk.snapenhance.core.features.FeatureLoadParams
import me.rhunk.snapenhance.core.util.LSPatchUpdater
import me.rhunk.snapenhance.core.util.hook.HookStage import me.rhunk.snapenhance.core.util.hook.HookStage
import me.rhunk.snapenhance.core.util.hook.Hooker import me.rhunk.snapenhance.core.util.hook.hook
class DeviceSpooferHook: Feature("device_spoofer", loadParams = FeatureLoadParams.INIT_SYNC) { class DeviceSpooferHook: Feature("device_spoofer", loadParams = FeatureLoadParams.INIT_SYNC) {
private fun hookInstallerPackageName() {
context.androidContext.packageManager::class.java.hook("getInstallerPackageName", HookStage.BEFORE) { param ->
param.setResult("com.android.vending")
}
}
@SuppressLint("MissingPermission")
override fun init() { override fun init() {
if (context.config.experimental.spoof.globalState != true) return // force installer package name for lspatch users
if (LSPatchUpdater.HAS_LSPATCH) {
val fingerprint by context.config.experimental.spoof.device.fingerprint hookInstallerPackageName()
val androidId by context.config.experimental.spoof.device.androidId
val getInstallerPackageName by context.config.experimental.spoof.device.getInstallerPackageName
val debugFlag by context.config.experimental.spoof.device.debugFlag
val mockLocationState by context.config.experimental.spoof.device.mockLocationState
val splitClassLoader by context.config.experimental.spoof.device.splitClassLoader
val isLowEndDevice by context.config.experimental.spoof.device.isLowEndDevice
val getDataDirectory by context.config.experimental.spoof.device.getDataDirectory
val settingsSecureClass = android.provider.Settings.Secure::class.java
val fingerprintClass = android.os.Build::class.java
val packageManagerClass = android.content.pm.PackageManager::class.java
val applicationInfoClass = android.content.pm.ApplicationInfo::class.java
//FINGERPRINT
if (fingerprint.isNotEmpty()) {
Hooker.hook(fingerprintClass, "FINGERPRINT", HookStage.BEFORE) { hookAdapter ->
hookAdapter.setResult(fingerprint)
context.log.verbose("Fingerprint spoofed to $fingerprint")
}
Hooker.hook(fingerprintClass, "deriveFingerprint", HookStage.BEFORE) { hookAdapter ->
hookAdapter.setResult(fingerprint)
context.log.verbose("Fingerprint spoofed to $fingerprint")
}
} }
//ANDROID ID if (context.config.experimental.spoof.globalState != true) return
if (androidId.isNotEmpty()) {
Hooker.hook(settingsSecureClass, "getString", HookStage.BEFORE) { hookAdapter -> val fingerprint by context.config.experimental.spoof.fingerprint
if(hookAdapter.args()[1] == "android_id") { val androidId by context.config.experimental.spoof.androidId
hookAdapter.setResult(androidId) val removeMockLocationFlag by context.config.experimental.spoof.removeMockLocationFlag
context.log.verbose("Android ID spoofed to $androidId") val overridePlayStoreInstallerPackageName by context.config.experimental.spoof.overridePlayStoreInstallerPackageName
val removeVpnTransportFlag by context.config.experimental.spoof.removeVpnTransportFlag
val randomizePersistentDeviceToken by context.config.experimental.spoof.randomizePersistentDeviceToken
//Installer package name
if(overridePlayStoreInstallerPackageName) {
hookInstallerPackageName()
}
findClass("android.provider.Settings\$NameValueCache").apply {
hook("getStringForUser", HookStage.BEFORE) { hookAdapter ->
val key = hookAdapter.argNullable<String>(1) ?: return@hook
when (key) {
"android_id" -> {
if (androidId.isNotEmpty()) {
hookAdapter.setResult(androidId)
}
}
"ALLOW_MOCK_LOCATION" -> {
if (removeMockLocationFlag) {
hookAdapter.setResult("0")
}
}
} }
} }
} }
//TODO: org.chromium.base.BuildInfo, org.chromium.base.PathUtils getDataDirectory, MushroomDeviceTokenManager(?), TRANSPORT_VPN FLAG, isFromMockProvider, nativeLibraryDir, sourceDir, network capabilities, query all jvm properties if (removeMockLocationFlag) {
Location::class.java.hook("isMock", HookStage.BEFORE) { param ->
//INSTALLER PACKAGE NAME param.setResult(false)
if(getInstallerPackageName.isNotEmpty()) {
Hooker.hook(packageManagerClass, "getInstallerPackageName", HookStage.BEFORE) { hookAdapter ->
hookAdapter.setResult(getInstallerPackageName)
} }
} }
//DEBUG FLAG if (randomizePersistentDeviceToken) {
Hooker.hook(applicationInfoClass, "FLAG_DEBUGGABLE", HookStage.BEFORE) { hookAdapter -> context.androidContext.filesDir.resolve("Snapchat").listFiles()?.firstOrNull {
hookAdapter.setResult(debugFlag) it.name.startsWith("device_token")
}?.delete()
} }
//MOCK LOCATION if (removeVpnTransportFlag) {
Hooker.hook(settingsSecureClass, "getString", HookStage.BEFORE) { hookAdapter -> ConnectivityManager::class.java.hook("getAllNetworks", HookStage.AFTER) { param ->
if(hookAdapter.args()[1] == "ALLOW_MOCK_LOCATION") { val instance = param.thisObject() as? ConnectivityManager ?: return@hook
hookAdapter.setResult(mockLocationState) val networks = param.getResult() as? Array<*> ?: return@hook
param.setResult(networks.filterIsInstance<Network>().filter { network ->
val capabilities = instance.getNetworkCapabilities(network) ?: return@filter false
!capabilities.hasTransport(NetworkCapabilities.TRANSPORT_VPN)
}.toTypedArray())
} }
} }
//GET SPLIT CLASSLOADER if (fingerprint.isNotEmpty()) {
if(splitClassLoader.isNotEmpty()) { Build.FINGERPRINT // init fingerprint field
Hooker.hook(context.classCache.chromiumJNIUtils, "getSplitClassLoader", HookStage.BEFORE) { hookAdapter -> Build::class.java.getField("FINGERPRINT").apply {
hookAdapter.setResult(splitClassLoader) isAccessible = true
set(null, fingerprint)
isAccessible = false
} }
} }
//ISLOWENDDEVICE
if(isLowEndDevice.isNotEmpty()) {
Hooker.hook(context.classCache.chromiumBuildInfo, "getAll", HookStage.BEFORE) { hookAdapter ->
hookAdapter.setResult(isLowEndDevice)
}
}
//GETDATADIRECTORY
if(getDataDirectory.isNotEmpty()) {
Hooker.hook(context.classCache.chromiumPathUtils, "getDataDirectory", HookStage.BEFORE) { hookAdapter ->
hookAdapter.setResult(getDataDirectory)
}
}
//accessibility_enabled
} }
} }

View File

@ -9,6 +9,9 @@ import java.util.zip.ZipFile
object LSPatchUpdater { object LSPatchUpdater {
private const val TAG = "LSPatchUpdater" private const val TAG = "LSPatchUpdater"
var HAS_LSPATCH = false
private set
private fun getModuleUniqueHash(module: ZipFile): String { private fun getModuleUniqueHash(module: ZipFile): String {
return module.entries().asSequence() return module.entries().asSequence()
.filter { !it.isDirectory } .filter { !it.isDirectory }
@ -38,6 +41,7 @@ object LSPatchUpdater {
} ?: return } ?: return
} ?: return } ?: return
HAS_LSPATCH = true
context.log.verbose("Found embedded SE at ${embeddedModule.absolutePath}", TAG) context.log.verbose("Found embedded SE at ${embeddedModule.absolutePath}", TAG)
val seAppApk = File(bridgeClient.getApplicationApkPath()).also { val seAppApk = File(bridgeClient.getApplicationApkPath()).also {