| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674 |
- package com.example.modifier
- import android.annotation.SuppressLint
- import android.content.Context
- import android.content.Intent
- import android.os.Build
- import android.util.Log
- import androidx.core.content.ContextCompat
- import com.example.modifier.data.BackupItem
- import com.example.modifier.data.BackupItemDao
- import com.example.modifier.http.KtorClient
- import com.example.modifier.model.Backup
- import com.example.modifier.model.TelephonyConfig
- import com.example.modifier.service.ModifierService
- import com.example.modifier.ui.shellRun
- import com.example.modifier.utils.RcsHackTool
- import com.google.gson.Gson
- import io.ktor.client.request.head
- import kotlinx.coroutines.Dispatchers
- import kotlinx.coroutines.coroutineScope
- import kotlinx.coroutines.delay
- import kotlinx.coroutines.launch
- import kotlinx.coroutines.withContext
- import org.apache.commons.io.FileUtils
- import org.apache.commons.io.IOUtils
- import org.apache.commons.lang3.RandomStringUtils
- import org.apache.commons.lang3.StringUtils
- import java.io.File
- import java.io.FileWriter
- import java.nio.file.Files
- import java.time.ZoneId
- import java.time.ZonedDateTime
- import java.time.format.DateTimeFormatter
- import java.util.Base64
- import java.util.Date
- import java.util.Locale
- object Global {
- @JvmField
- var serverUrl: String = ""
- @JvmField
- var name: String? = ""
- @JvmField
- var telephonyConfig: TelephonyConfig = TelephonyConfig("", "", "", "", "", "", "", "")
- private const val TAG = "Modifier"
- @JvmStatic
- fun load() {
- val context = Utils.getContext()
- val prefs = context.getSharedPreferences(BuildConfig.APPLICATION_ID, Context.MODE_PRIVATE)
- serverUrl = prefs.getString("server", "http://47.98.225.28") ?: ""
- name = prefs.getString("name", Build.DEVICE)
- try {
- val file = File(ContextCompat.getDataDir(context), "config.json")
- if (file.exists()) {
- val gson = Gson()
- val json = FileUtils.readFileToString(file, "UTF-8")
- telephonyConfig = gson.fromJson(json, TelephonyConfig::class.java)
- }
- } catch (e: Exception) {
- e.printStackTrace()
- }
- }
- @JvmStatic
- val servers: MutableSet<String>
- get() {
- val context = Utils.getContext()
- val defServers: MutableSet<String> = HashSet()
- defServers.add("http://192.168.6.215:3000")
- defServers.add("http://192.168.50.135:3000")
- defServers.add("http://47.98.225.28")
- val prefs =
- context.getSharedPreferences(BuildConfig.APPLICATION_ID, Context.MODE_PRIVATE)
- return HashSet(prefs.getStringSet("servers", defServers))
- }
- @JvmStatic
- fun saveServer(server: String, name: String?) {
- serverUrl = server
- Global.name = name
- val context = Utils.getContext()
- val prefs = context.getSharedPreferences(BuildConfig.APPLICATION_ID, Context.MODE_PRIVATE)
- val servers = servers
- servers.add(server)
- prefs.edit().putStringSet("servers", servers)
- .putString("server", server)
- .putString("name", name)
- .apply()
- }
- @JvmStatic
- fun save(telephonyConfig: TelephonyConfig, suspend: Boolean? = true) {
- val context = Utils.getContext()
- Global.telephonyConfig.mcc = telephonyConfig.mcc
- Global.telephonyConfig.mnc = telephonyConfig.mnc
- Global.telephonyConfig.number = telephonyConfig.number
- Global.telephonyConfig.country = telephonyConfig.country
- Global.telephonyConfig.areaCode = telephonyConfig.areaCode
- Global.telephonyConfig.iccid = telephonyConfig.iccid
- Global.telephonyConfig.imei = telephonyConfig.imei
- Global.telephonyConfig.imsi = telephonyConfig.imsi
- try {
- if (suspend == true) {
- suspend(gms = true, sms = true)
- }
- val file = File(ContextCompat.getDataDir(context), "config.json")
- val gson = Gson()
- val json = gson.toJson(telephonyConfig)
- try {
- val writer = FileWriter(file)
- writer.write(json)
- writer.close()
- } catch (e: Exception) {
- e.printStackTrace()
- }
- Utils.runAsRoot(
- "setprop persist.spoof.mcc ${telephonyConfig.mcc}",
- "setprop persist.spoof.mnc ${telephonyConfig.mnc}",
- "setprop persist.spoof.number ${telephonyConfig.number}",
- "setprop persist.spoof.country ${telephonyConfig.country}",
- "setprop persist.spoof.iccid ${telephonyConfig.iccid}",
- "setprop persist.spoof.imei ${telephonyConfig.imei}",
- "setprop persist.spoof.imsi ${telephonyConfig.imsi}",
- "cp " + file.path + " /data/data/com.android.phone/rcsConfig.json",
- "echo 'copied to phone'",
- "chmod 777 /data/data/com.android.phone/rcsConfig.json",
- "sleep 1"
- )
- if (suspend == true) {
- unsuspend(gms = true, sms = true)
- }
- } catch (e: Exception) {
- e.printStackTrace()
- }
- }
- fun saveMock() {
- val content = Utils.getContext().assets.open("us_numbers.txt").bufferedReader().use {
- it.readText()
- }
- // get random number
- content.split("\n")
- .filter { it.isNotBlank() }
- .shuffled()
- .firstOrNull()
- ?.let {
- save(
- TelephonyConfig(
- number = it,
- mcc = "310",
- mnc = "240",
- iccid = genICCID("310", "1"),
- "310240" + RandomStringUtils.randomNumeric(9),
- Utils.generateIMEI(),
- "us",
- "1"
- )
- )
- }
- }
- @JvmStatic
- fun clear(gsf: Boolean, gms: Boolean, sms: Boolean) {
- try {
- suspend(gsf, gms, sms)
- val cmds: MutableList<String> = ArrayList()
- // suspend
- if (gsf) {
- cmds.add("pm suspend com.google.android.gsf")
- cmds.add("am force-stop com.google.android.gsf")
- cmds.add("echo 'gsf suspended'")
- }
- if (gms) {
- cmds.add("pm suspend com.google.android.gms")
- cmds.add("am force-stop com.google.android.gms")
- cmds.add("echo 'gms suspended'")
- }
- if (sms) {
- cmds.add("pm suspend com.google.android.apps.messaging")
- cmds.add("am force-stop com.google.android.apps.messaging")
- cmds.add("echo 'sms suspended'")
- }
- cmds.add("sleep 1")
- // clear
- if (gsf) {
- cmds.add("pm clear com.google.android.gsf")
- cmds.add("echo 'cleared gsf'")
- }
- if (gms) {
- cmds.add("pm clear com.google.android.gms")
- cmds.add("echo 'cleared gms'")
- }
- if (sms) {
- cmds.add("pm clear com.google.android.apps.messaging")
- cmds.add("echo 'cleared sms'")
- }
- cmds.add("sleep 1")
- // unsuspend
- if (gsf) {
- cmds.add("pm unsuspend com.google.android.gsf")
- cmds.add("echo 'gsf unsuspend'")
- }
- if (gms) {
- cmds.add("pm unsuspend com.google.android.gms")
- cmds.add("echo 'gms unsuspend'")
- }
- if (sms) {
- cmds.add("pm unsuspend com.google.android.apps.messaging")
- cmds.add("echo 'sms unsuspend'")
- }
- cmds.add("sleep 1")
- Utils.runAsRoot(*cmds.toTypedArray<String>())
- } catch (e: Exception) {
- e.printStackTrace()
- }
- }
- // @JvmStatic
- // fun stop(gsf: Boolean? = false, gms: Boolean? = false, sms: Boolean? = false) {
- // try {
- // val cmds: MutableList<String> = ArrayList()
- // if (gsf == true) {
- // cmds.add("am force-stop com.google.android.gsf")
- // cmds.add("echo 'stopped gsf'")
- // }
- // if (gms == true) {
- // cmds.add("am force-stop com.google.android.gms")
- // cmds.add("echo 'stopped gms'")
- // Thread.sleep(1000)
- // }
- // if (sms == true) {
- // cmds.add("am force-stop com.google.android.apps.messaging")
- // cmds.add("echo 'stopped sms'")
- // }
- // Utils.runAsRoot(*cmds.toTypedArray<String>())
- // } catch (e: Exception) {
- // e.printStackTrace()
- // }
- // }
- @JvmStatic
- fun suspend(gsf: Boolean? = false, gms: Boolean? = false, sms: Boolean? = false) {
- try {
- val cmds: MutableList<String> = ArrayList()
- if (gsf == true) {
- cmds.add("pm suspend com.google.android.gsf")
- cmds.add("am force-stop com.google.android.gsf")
- cmds.add("echo 'gsf suspended'")
- }
- if (gms == true) {
- cmds.add("pm suspend com.google.android.gms")
- cmds.add("am force-stop com.google.android.gms")
- cmds.add("echo 'gms suspended'")
- }
- if (sms == true) {
- cmds.add("pm suspend com.google.android.apps.messaging")
- cmds.add("am force-stop com.google.android.apps.messaging")
- cmds.add("echo 'sms suspended'")
- }
- cmds.add("sleep 1")
- Utils.runAsRoot(*cmds.toTypedArray<String>())
- } catch (e: Exception) {
- e.printStackTrace()
- }
- }
- @JvmStatic
- fun unsuspend(gsf: Boolean? = false, gms: Boolean? = false, sms: Boolean? = false) {
- try {
- val cmds: MutableList<String> = ArrayList()
- if (gsf == true) {
- cmds.add("pm unsuspend com.google.android.gsf")
- cmds.add("echo 'gsf unsuspend'")
- }
- if (gms == true) {
- cmds.add("pm unsuspend com.google.android.gms")
- cmds.add("echo 'gms unsuspend'")
- }
- if (sms == true) {
- cmds.add("pm unsuspend com.google.android.apps.messaging")
- cmds.add("echo 'sms unsuspend'")
- }
- cmds.add("sleep 1")
- Utils.runAsRoot(*cmds.toTypedArray<String>())
- } catch (e: Exception) {
- e.printStackTrace()
- }
- }
- @JvmStatic
- fun clearConv() {
- val context = Utils.getContext()
- if (context.getSharedPreferences("settings", Context.MODE_PRIVATE)
- .getBoolean("do_not_clean", false)
- ) {
- return
- }
- try {
- val dataDir = ContextCompat.getDataDir(context)
- val binDir = File(dataDir, "bin")
- val dbDir = File(dataDir, "providerDB")
- if (!binDir.exists()) {
- Utils.copyAssetFolder(context.assets, "bin", binDir.path)
- Utils.copyAssetFolder(context.assets, "providerDB", dbDir.path)
- }
- Log.i("Modifier", "arch: " + Build.SUPPORTED_ABIS.joinToString(", "))
- var arch: String? = null
- for (supportedAbi in Build.SUPPORTED_ABIS) {
- if ("x86" == supportedAbi) {
- arch = "x86"
- } else if ("x86_64" == supportedAbi) {
- arch = "x64"
- } else if ("arm64-v8a" == supportedAbi) {
- arch = "arm64"
- } else if ("armeabi-v7a" == supportedAbi) {
- arch = "arm"
- }
- if (StringUtils.isNoneBlank(arch)) {
- suspend(sms = true)
- val binPath = File(dataDir, "bin/sqlite3.$arch").path
- val providerDBPath = File(dataDir, "providerDB/mmssms.db").path
- Log.i("Modifier", "sqlite3 binPath: $binPath")
- Utils.runAsRoot(
- "chmod +x $binPath",
- "$binPath /data/data/com.google.android.apps.messaging/databases/bugle_db \"DELETE FROM conversations;\"",
- "$binPath /data/data/com.google.android.apps.messaging/databases/bugle_db \"DELETE FROM messages;\"",
- "cp $providerDBPath /data/data/com.android.providers.telephony/databases/mmssms.db",
- "chmod 660 /data/data/com.android.providers.telephony/databases/mmssms.db",
- "echo ok"
- )
- unsuspend(sms = true)
- break
- }
- }
- } catch (e: Exception) {
- e.printStackTrace()
- }
- }
- @JvmStatic
- suspend fun resetAll() {
- val context = Utils.getContext()
- try {
- val dataDir = ContextCompat.getDataDir(context)
- Utils.copyAssetFolder(context.assets, "providerDB", File(dataDir, "providerDB").path)
- val providerDBPath = File(dataDir, "providerDB/mmssms.db").path
- shellRun(
- "cp $providerDBPath /data/data/com.android.providers.telephony/databases/mmssms.db",
- "chmod 660 /data/data/com.android.providers.telephony/databases/mmssms.db",
- CMD_SUSPEND_MESSAGING_APP,
- CMD_KILL_MESSAGING_APP,
- CMD_CLEAR_MESSAGING_APP,
- CMD_CLEAR_GSF,
- CMD_CLEAR_GMS,
- "sleep 1",
- CMD_START_PLAY_STORE,
- "sleep 1",
- CMD_HOME,
- "sleep 10",
- CMD_CLEAR_GMS,
- CMD_RESUME_MESSAGING_APP,
- CMD_MESSAGING_APP
- )
- } catch (e: Exception) {
- e.printStackTrace()
- }
- }
- @JvmStatic
- suspend fun killPhoneProcess(force: Boolean = false): Boolean {
- try {
- if (!force) {
- if (shellRun("getprop phonekilled")["output"]!!.contains("yes")) {
- return true
- }
- }
- run kill@{
- repeat(3) {
- val pid = shellRun("pidof com.android.phone")["output"]!!.trim()
- if (!Regex("[0-9]+").matches(pid)) {
- Log.e(TAG, "killPhoneProcess: pid not found")
- return true
- }
- Log.i(TAG, "killPhoneProcess: pid=$pid")
- shellRun("kill -9 $pid")
- delay(1000)
- val pidNew = shellRun("pidof com.android.phone")["output"]!!.trim()
- if (pidNew == pid) {
- Log.e(TAG, "killPhoneProcess: failed to kill phone process")
- } else {
- Log.i(TAG, "killPhoneProcess: success, new pid: $pidNew")
- shellRun("kill -9 $pid", "setprop phonekilled yes")
- return true
- }
- }
- }
- } catch (e: Exception) {
- Log.e(TAG, "Error Kill Phone", e)
- }
- return false
- }
- @JvmStatic
- suspend fun sendSmsFrida(sender: String, msg: String) {
- val context = Utils.getContext()
- try {
- val dataDir = ContextCompat.getDataDir(context)
- Utils.copyAssetFolder(context.assets, "bin", File(dataDir, "bin").path)
- val binPath = File(dataDir, "bin/frida-inject-16.3.3-android-arm64").path
- val pduBase64 =
- String(Base64.getEncoder().encode(RcsHackTool.createFakeSms(sender, msg)))
- Log.i("Modifier", "pduBase64: $pduBase64")
- val script = IOUtils.toString(context.assets.open("scripts/sms.js"), "UTF-8")
- .replace("{pduBase64}", pduBase64)
- val tmpFile: File
- withContext(Dispatchers.IO) {
- tmpFile = File.createTempFile("script", ".js")
- FileUtils.writeStringToFile(tmpFile, script, "UTF-8")
- }
- val pid = Utils.runAsRoot("pidof com.android.phone").trim()
- if (!Regex("[0-9]+").matches(pid)) {
- return
- }
- Log.i("Modifier", "sendSms: $binPath -p $pid -s $tmpFile")
- val p = withContext(Dispatchers.IO) {
- Runtime.getRuntime().exec("su -M")
- }
- p.outputStream.bufferedWriter().use {
- it.write("chmod +x $binPath")
- it.newLine()
- it.flush()
- it.write("$binPath -p $pid -s $tmpFile")
- it.newLine()
- it.flush()
- }
- coroutineScope {
- launch {
- p.errorStream.bufferedReader().useLines { lines ->
- lines.forEach {
- Log.e("Modifier", it)
- }
- }
- }
- launch {
- p.inputStream
- .bufferedReader()
- .useLines { lines ->
- lines.forEach {
- Log.i("Modifier", it)
- if (it == "OK") {
- p.inputStream.close()
- p.errorStream.close()
- p.destroy()
- }
- }
- }
- }
- }
- withContext(Dispatchers.IO) {
- p.waitFor()
- }
- } catch (e: Exception) {
- e.printStackTrace()
- }
- }
- @JvmStatic
- fun sendSmsIntent(sender: String, msg: String) {
- val intent = Intent()
- intent.setAction("com.example.modifier.sms")
- intent.putExtra("sender", sender)
- intent.putExtra(
- "message",
- msg
- )
- val context = Utils.getContext()
- context.sendBroadcast(intent)
- }
- @JvmStatic
- suspend fun syncTime() {
- try {
- Log.i("Modifier", "syncTime: start")
- val response = KtorClient.head("http://www.baidu.com")
- val dateHeader = response.headers["Date"]
- val date = ZonedDateTime.parse(
- dateHeader,
- DateTimeFormatter.ofPattern("EEE, dd MMM yyyy HH:mm:ss z", Locale.ENGLISH)
- )
- // convert to Asia/Shanghai
- val dateInZone = date.withZoneSameInstant(ZoneId.of("Asia/Shanghai"))
- Log.i(
- TAG,
- "CurrentTime from Baidu: ${dateInZone.format(DateTimeFormatter.ISO_DATE_TIME)}"
- )
- shellRun(
- "settings put system time_12_24 24",
- "settings put global auto_time 0",
- "settings put global auto_time_zone 0",
- "setprop persist.sys.timezone Asia/Shanghai",
- "date \"${dateInZone.format(DateTimeFormatter.ofPattern("MMddHHmmyyyy.ss"))}\""
- )
- } catch (e: Exception) {
- Log.e(TAG, "Error SyncTime", e)
- }
- }
- @JvmStatic
- suspend fun hasRoot(): Boolean {
- val hasRoot = run checkRoot@{
- repeat(5) {
- if (Utils.hasRootAccess()) {
- return@checkRoot true
- }
- delay(500)
- }
- return@checkRoot false
- }
- return hasRoot
- }
- @SuppressLint("DefaultLocale")
- @JvmStatic
- fun genICCID(mnc: String, areaCode: String): String {
- val prefix = String.format("89%02d%s", areaCode.toInt(), mnc)
- return prefix + RandomStringUtils.randomNumeric(20 - prefix.length)
- }
- @JvmStatic
- suspend fun rebooted(): Boolean {
- if (shellRun("getprop rebooted").contains("yes")) {
- return false
- }
- shellRun("setprop rebooted yes")
- return true
- }
- @SuppressLint("SdCardPath")
- @JvmStatic
- suspend fun backup(backupItemDao: BackupItemDao, type: String, sendCount: Int): BackupItem {
- clearConv()
- // ModifierService.instance!!.toggleRcsSwitch(false)
- val context = Utils.getContext()
- val dest = File(
- ContextCompat.getExternalFilesDirs(context, "backup")[0],
- System.currentTimeMillis().toString()
- )
- dest.mkdirs()
- val file = File(ContextCompat.getDataDir(context), "config.json")
- if (!file.exists()) {
- throw Exception("Config file not found")
- }
- withContext(Dispatchers.IO) {
- IOUtils.copy(
- Files.newInputStream(file.toPath()),
- Files.newOutputStream(File(dest, "config.json").toPath())
- )
- }
- val dataDir = File(dest, "data")
- dataDir.mkdirs()
- val packages = mutableListOf(
- "com.google.android.apps.messaging",
- "com.google.android.gms",
- "com.google.android.gsf",
- )
- packages.forEach {
- File(dataDir, it).mkdirs()
- }
- val cmds = mutableListOf<String>()
- cmds.addAll(packages.flatMap { mutableListOf("pm suspend $it", "am force-stop $it") })
- for (pkg in packages) {
- if (!shellRun("ls /data/data/$pkg")["error"]!!.contains("No such file or directory")) {
- cmds.add("cp -r /data/data/$pkg $dataDir/$pkg/data")
- }
- if (!shellRun("ls /data/user_de/0/$pkg")["error"]!!.contains("No such file or directory")) {
- cmds.add("cp -r /data/user_de/0/$pkg $dataDir/$pkg/user_de")
- }
- if (!shellRun("ls /sdcard/Android/data/$pkg")["error"]!!.contains("No such file or directory")) {
- cmds.add("cp -r /sdcard/Android/data/$pkg $dataDir/$pkg/external")
- }
- }
- cmds.addAll(packages.reversed().map { "pm unsuspend $it" })
- shellRun(*cmds.toTypedArray())
- val backup = BackupItem(
- createdAt = Date().time,
- number = telephonyConfig.number,
- code = if (telephonyConfig.areaCode == null) "" else telephonyConfig.areaCode,
- country = telephonyConfig.country,
- mcc = telephonyConfig.mcc,
- mnc = telephonyConfig.mnc,
- imei = telephonyConfig.imei,
- imsi = telephonyConfig.imsi,
- iccid = telephonyConfig.iccid,
- path = dest.path,
- sendCount = sendCount,
- lastUse = Date().time,
- type = type
- )
- backupItemDao.findBackup(telephonyConfig.country, telephonyConfig.number)?.let {
- File(it.path).deleteRecursively()
- backupItemDao.delete(it)
- backup.sendCount += it.sendCount
- }
- backup.id = backupItemDao.insert(backup).toInt()
- return backup
- }
- @JvmStatic
- suspend fun restore(backup: BackupItem) {
- save(
- TelephonyConfig(
- number = backup.number,
- mcc = backup.mcc,
- mnc = backup.mnc,
- country = backup.country,
- areaCode = backup.code,
- iccid = backup.iccid,
- imei = backup.imei,
- imsi = backup.imsi,
- ), false
- )
- val packages = mutableListOf(
- "com.google.android.apps.messaging",
- "com.google.android.gms",
- "com.google.android.gsf",
- )
- val cmds = mutableListOf<String>()
- cmds.addAll(packages.flatMap { mutableListOf("pm suspend $it", "am force-stop $it") })
- val packageManager = Utils.getContext().packageManager
- for (pkg in packages) {
- val uid = packageManager.getApplicationInfo(pkg, 0).uid
- if (File("${backup.path}/data/$pkg/data").exists()) {
- cmds.add("rm -rf /data/data/$pkg/*")
- cmds.add("cp -r ${backup.path}/data/$pkg/data/* /data/data/$pkg")
- cmds.add("chown -R $uid:$uid /data/data/$pkg")
- }
- if (File("${backup.path}/data/$pkg/user_de").exists()) {
- cmds.add("rm -rf /data/user_de/0/$pkg/*")
- cmds.add("cp -r ${backup.path}/data/$pkg/user_de/* /data/user_de/0/$pkg")
- cmds.add("chown -R $uid:$uid /data/user_de/0/$pkg")
- }
- if (File("${backup.path}/data/$pkg/external").exists()) {
- cmds.add("rm -rf /sdcard/Android/data/$pkg/*")
- cmds.add("cp -r ${backup.path}/data/$pkg/external/* /sdcard/Android/data/$pkg")
- cmds.add("chown -R $uid:$uid /sdcard/Android/data/$pkg")
- }
- }
- cmds.addAll(packages.reversed().map { "pm unsuspend $it" })
- shellRun(*cmds.toTypedArray())
- }
- }
|