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

@ -17,9 +17,6 @@ class SnapClassCache (
val feedEntry by lazy { findClass("com.snapchat.client.messaging.FeedEntry") }
val conversation by lazy { findClass("com.snapchat.client.messaging.Conversation") }
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<*> {
return try {

View File

@ -1,93 +1,94 @@
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.FeatureLoadParams
import me.rhunk.snapenhance.core.util.LSPatchUpdater
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) {
private fun hookInstallerPackageName() {
context.androidContext.packageManager::class.java.hook("getInstallerPackageName", HookStage.BEFORE) { param ->
param.setResult("com.android.vending")
}
}
@SuppressLint("MissingPermission")
override fun init() {
if (context.config.experimental.spoof.globalState != true) return
val fingerprint by context.config.experimental.spoof.device.fingerprint
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")
}
// force installer package name for lspatch users
if (LSPatchUpdater.HAS_LSPATCH) {
hookInstallerPackageName()
}
//ANDROID ID
if (androidId.isNotEmpty()) {
Hooker.hook(settingsSecureClass, "getString", HookStage.BEFORE) { hookAdapter ->
if(hookAdapter.args()[1] == "android_id") {
hookAdapter.setResult(androidId)
context.log.verbose("Android ID spoofed to $androidId")
if (context.config.experimental.spoof.globalState != true) return
val fingerprint by context.config.experimental.spoof.fingerprint
val androidId by context.config.experimental.spoof.androidId
val removeMockLocationFlag by context.config.experimental.spoof.removeMockLocationFlag
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
//INSTALLER PACKAGE NAME
if(getInstallerPackageName.isNotEmpty()) {
Hooker.hook(packageManagerClass, "getInstallerPackageName", HookStage.BEFORE) { hookAdapter ->
hookAdapter.setResult(getInstallerPackageName)
if (removeMockLocationFlag) {
Location::class.java.hook("isMock", HookStage.BEFORE) { param ->
param.setResult(false)
}
}
//DEBUG FLAG
Hooker.hook(applicationInfoClass, "FLAG_DEBUGGABLE", HookStage.BEFORE) { hookAdapter ->
hookAdapter.setResult(debugFlag)
if (randomizePersistentDeviceToken) {
context.androidContext.filesDir.resolve("Snapchat").listFiles()?.firstOrNull {
it.name.startsWith("device_token")
}?.delete()
}
//MOCK LOCATION
Hooker.hook(settingsSecureClass, "getString", HookStage.BEFORE) { hookAdapter ->
if(hookAdapter.args()[1] == "ALLOW_MOCK_LOCATION") {
hookAdapter.setResult(mockLocationState)
if (removeVpnTransportFlag) {
ConnectivityManager::class.java.hook("getAllNetworks", HookStage.AFTER) { param ->
val instance = param.thisObject() as? ConnectivityManager ?: return@hook
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(splitClassLoader.isNotEmpty()) {
Hooker.hook(context.classCache.chromiumJNIUtils, "getSplitClassLoader", HookStage.BEFORE) { hookAdapter ->
hookAdapter.setResult(splitClassLoader)
if (fingerprint.isNotEmpty()) {
Build.FINGERPRINT // init fingerprint field
Build::class.java.getField("FINGERPRINT").apply {
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 {
private const val TAG = "LSPatchUpdater"
var HAS_LSPATCH = false
private set
private fun getModuleUniqueHash(module: ZipFile): String {
return module.entries().asSequence()
.filter { !it.isDirectory }
@ -38,6 +41,7 @@ object LSPatchUpdater {
} ?: return
} ?: return
HAS_LSPATCH = true
context.log.verbose("Found embedded SE at ${embeddedModule.absolutePath}", TAG)
val seAppApk = File(bridgeClient.getApplicationApkPath()).also {