UtilsFragment.kt 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389
  1. package com.example.modifier.ui.utils
  2. import android.os.Bundle
  3. import android.util.Log
  4. import android.view.LayoutInflater
  5. import android.view.View
  6. import android.view.ViewGroup
  7. import android.widget.Toast
  8. import androidx.appcompat.app.AlertDialog
  9. import androidx.fragment.app.Fragment
  10. import androidx.lifecycle.lifecycleScope
  11. import com.example.modifier.Frida
  12. import com.example.modifier.R
  13. import com.example.modifier.Utils
  14. import com.example.modifier.baseTag
  15. import com.example.modifier.constants.PACKAGE_GMS
  16. import com.example.modifier.constants.PACKAGE_GSF
  17. import com.example.modifier.constants.PACKAGE_MESSAGING
  18. import com.example.modifier.data.AppPreferences
  19. import com.example.modifier.databinding.DialogUpdateBinding
  20. import com.example.modifier.databinding.FragmentUtilsBinding
  21. import com.example.modifier.extension.kill
  22. import com.example.modifier.http.ktorClient
  23. import com.example.modifier.http.api.SysConfigApi
  24. import com.example.modifier.http.response.SysConfigResponse
  25. import com.example.modifier.utils.clear
  26. import com.example.modifier.utils.clearConv
  27. import com.example.modifier.utils.killPhoneProcess
  28. import com.example.modifier.utils.resetAll
  29. import com.example.modifier.utils.resumePackage
  30. import com.example.modifier.utils.shellRun
  31. import com.example.modifier.utils.injectOTP
  32. import com.example.modifier.utils.suspendPackage
  33. import com.example.modifier.utils.syncTime
  34. import com.google.android.material.dialog.MaterialAlertDialogBuilder
  35. import io.ktor.client.call.body
  36. import io.ktor.client.plugins.resources.get
  37. import io.ktor.client.request.prepareGet
  38. import io.ktor.http.contentLength
  39. import io.ktor.utils.io.ByteReadChannel
  40. import io.ktor.utils.io.core.isEmpty
  41. import io.ktor.utils.io.core.readBytes
  42. import kotlinx.coroutines.CoroutineScope
  43. import kotlinx.coroutines.Dispatchers
  44. import kotlinx.coroutines.Job
  45. import kotlinx.coroutines.delay
  46. import kotlinx.coroutines.flow.StateFlow
  47. import kotlinx.coroutines.launch
  48. import kotlinx.coroutines.withContext
  49. import java.io.File
  50. class UtilsFragment : Fragment() {
  51. private val tag = "$baseTag/UtilsFragment"
  52. private lateinit var binding: FragmentUtilsBinding
  53. private lateinit var appPreferences: StateFlow<AppPreferences>
  54. override fun onCreate(savedInstanceState: Bundle?) {
  55. super.onCreate(savedInstanceState)
  56. }
  57. override fun onCreateView(
  58. inflater: LayoutInflater, container: ViewGroup?,
  59. savedInstanceState: Bundle?
  60. ): View? {
  61. if (this::binding.isInitialized) {
  62. return binding.root
  63. }
  64. binding = FragmentUtilsBinding.inflate(inflater, container, false)
  65. binding.btnClear.setOnClickListener {
  66. onClear()
  67. }
  68. binding.btnStop.setOnClickListener {
  69. onStopClick()
  70. }
  71. binding.btnSend.setOnClickListener {
  72. Utils.makeLoadingButton(context, binding.btnSend)
  73. lifecycleScope.launch {
  74. delay(1000L)
  75. val otp = binding.etOtp.text.toString()
  76. withContext(Dispatchers.IO) {
  77. injectOTP(otp)
  78. }
  79. binding.btnSend.setIconResource(R.drawable.ic_done)
  80. binding.btnSend.text = "OK"
  81. delay(1500L)
  82. binding.btnSend.isEnabled = true
  83. binding.btnSend.icon = null
  84. binding.btnSend.text = "Send"
  85. }
  86. }
  87. binding.btnClearConv.setOnClickListener {
  88. binding.btnClearConv.isEnabled = false
  89. Utils.makeLoadingButton(context, binding.btnClearConv)
  90. lifecycleScope.launch {
  91. clearConv()
  92. withContext(Dispatchers.Main) {
  93. binding.btnClearConv.setIconResource(R.drawable.ic_done)
  94. binding.btnClearConv.text = "OK"
  95. delay(1500L)
  96. binding.btnClearConv.isEnabled = true
  97. binding.btnClearConv.icon = null
  98. binding.btnClearConv.text = "Clear Msg"
  99. }
  100. }
  101. }
  102. binding.btnSuspend.setOnClickListener {
  103. binding.btnSuspend.isEnabled = false
  104. Utils.makeLoadingButton(context, binding.btnSuspend)
  105. lifecycleScope.launch {
  106. val packages = mutableListOf<String>()
  107. if (binding.cbGsf.isChecked) {
  108. packages.add(PACKAGE_GSF)
  109. }
  110. if (binding.cbGms.isChecked) {
  111. packages.add(PACKAGE_GMS)
  112. }
  113. if (binding.cbSms.isChecked) {
  114. packages.add(PACKAGE_MESSAGING)
  115. }
  116. withContext(Dispatchers.IO) {
  117. suspendPackage(*packages.toTypedArray())
  118. }
  119. binding.btnSuspend.setIconResource(R.drawable.ic_done)
  120. binding.btnSuspend.text = "OK"
  121. delay(1500)
  122. binding.btnSuspend.isEnabled = true
  123. binding.btnSuspend.icon = null
  124. binding.btnSuspend.text = "Suspend"
  125. }
  126. }
  127. binding.btnUnsuspend.setOnClickListener {
  128. binding.btnUnsuspend.isEnabled = false
  129. Utils.makeLoadingButton(context, binding.btnUnsuspend)
  130. lifecycleScope.launch {
  131. val packages = mutableListOf<String>()
  132. if (binding.cbGsf.isChecked) {
  133. packages.add(PACKAGE_GSF)
  134. }
  135. if (binding.cbGms.isChecked) {
  136. packages.add(PACKAGE_GMS)
  137. }
  138. if (binding.cbSms.isChecked) {
  139. packages.add(PACKAGE_MESSAGING)
  140. }
  141. withContext(Dispatchers.IO) {
  142. resumePackage(*packages.toTypedArray())
  143. }
  144. binding.btnUnsuspend.setIconResource(R.drawable.ic_done)
  145. binding.btnUnsuspend.text = "OK"
  146. delay(1500)
  147. binding.btnUnsuspend.isEnabled = true
  148. binding.btnUnsuspend.icon = null
  149. binding.btnUnsuspend.text = "Unsuspend"
  150. }
  151. }
  152. binding.btnStartFrida.setOnClickListener {
  153. lifecycleScope.launch {
  154. withContext(Dispatchers.IO) {
  155. Frida.start()
  156. }
  157. }
  158. Toast.makeText(context, "Frida started", Toast.LENGTH_SHORT).show()
  159. }
  160. binding.btnStopFrida.setOnClickListener {
  161. lifecycleScope.launch {
  162. withContext(Dispatchers.IO) {
  163. Frida.stop()
  164. }
  165. }
  166. Toast.makeText(context, "Frida stopped", Toast.LENGTH_SHORT).show()
  167. }
  168. binding.btnKillPhone.setOnClickListener {
  169. binding.btnKillPhone.isEnabled = false
  170. Utils.makeLoadingButton(context, binding.btnKillPhone)
  171. lifecycleScope.launch {
  172. val success = withContext(Dispatchers.IO) {
  173. killPhoneProcess(force = true)
  174. }
  175. if (success) {
  176. binding.btnKillPhone.setIconResource(R.drawable.ic_done)
  177. binding.btnKillPhone.text = "OK"
  178. } else {
  179. binding.btnKillPhone.setIconResource(R.drawable.ic_error)
  180. binding.btnKillPhone.text = "Fail"
  181. }
  182. delay(1500L)
  183. binding.btnKillPhone.isEnabled = true
  184. binding.btnKillPhone.icon = null
  185. binding.btnKillPhone.text = "Kill Phone"
  186. }
  187. }
  188. binding.btnSyncTime.setOnClickListener {
  189. binding.btnSyncTime.isEnabled = false
  190. Utils.makeLoadingButton(context, binding.btnSyncTime)
  191. lifecycleScope.launch {
  192. withContext(Dispatchers.IO) {
  193. syncTime()
  194. }
  195. binding.btnSyncTime.setIconResource(R.drawable.ic_done)
  196. binding.btnSyncTime.text = "OK"
  197. delay(1500L)
  198. binding.btnSyncTime.isEnabled = true
  199. binding.btnSyncTime.icon = null
  200. binding.btnSyncTime.text = "Sync Time"
  201. }
  202. }
  203. binding.btnUpdateModifier.setOnClickListener {
  204. lifecycleScope.launch {
  205. try {
  206. val config = ktorClient.get(
  207. SysConfigApi.Id(
  208. SysConfigApi(),
  209. "modifier_apk"
  210. )
  211. )
  212. .body<SysConfigResponse>()
  213. installApk(config.value)
  214. } catch (e: Exception) {
  215. Log.e(tag, "Failed to get message apk", e)
  216. }
  217. }
  218. }
  219. binding.btnUpdateMessage.setOnClickListener {
  220. lifecycleScope.launch {
  221. try {
  222. val config = ktorClient.get(
  223. SysConfigApi.Id(
  224. SysConfigApi(),
  225. "message_apk"
  226. )
  227. )
  228. .body<SysConfigResponse>()
  229. installApk(config.value)
  230. } catch (e: Exception) {
  231. Log.e(tag, "Failed to get message apk", e)
  232. }
  233. }
  234. }
  235. binding.btnUpdateGms.setOnClickListener {
  236. lifecycleScope.launch {
  237. try {
  238. val config = ktorClient.get(
  239. SysConfigApi.Id(
  240. SysConfigApi(),
  241. "gms_apk"
  242. )
  243. )
  244. .body<SysConfigResponse>()
  245. installApk(config.value)
  246. } catch (e: Exception) {
  247. Log.e(tag, "Failed to get gms apk", e)
  248. }
  249. }
  250. }
  251. return binding.root
  252. }
  253. private fun onClear() {
  254. binding.btnClear.isEnabled = false
  255. Utils.makeLoadingButton(context, binding.btnClear)
  256. val all = binding.cbAll.isChecked
  257. val gsf = binding.cbGsf.isChecked
  258. val gms = binding.cbGms.isChecked
  259. val sms = binding.cbSms.isChecked
  260. lifecycleScope.launch {
  261. withContext(Dispatchers.IO) {
  262. if (all) {
  263. resetAll()
  264. } else {
  265. clear(gsf, gms, sms)
  266. }
  267. }
  268. binding.btnClear.setIconResource(R.drawable.ic_done)
  269. binding.btnClear.text = "OK"
  270. delay(1500)
  271. binding.btnClear.isEnabled = true
  272. binding.btnClear.icon = null
  273. binding.btnClear.text = "Clear"
  274. }
  275. }
  276. private fun onStopClick() {
  277. binding.btnStop.isEnabled = false
  278. Utils.makeLoadingButton(context, binding.btnStop)
  279. lifecycleScope.launch {
  280. withContext(Dispatchers.IO) {
  281. val packages = mutableListOf<String>()
  282. if (binding.cbGsf.isChecked) {
  283. packages.add(PACKAGE_GSF)
  284. }
  285. if (binding.cbGms.isChecked) {
  286. packages.add(PACKAGE_GMS)
  287. }
  288. if (binding.cbSms.isChecked) {
  289. packages.add(PACKAGE_MESSAGING)
  290. }
  291. packages.forEach {
  292. shellRun(it.kill())
  293. }
  294. }
  295. binding.btnStop.setIconResource(R.drawable.ic_done)
  296. binding.btnStop.text = "OK"
  297. delay(1500)
  298. binding.btnStop.isEnabled = true
  299. binding.btnStop.icon = null
  300. binding.btnStop.text = "Stop"
  301. }
  302. }
  303. private suspend fun installApk(url: String) {
  304. if (url.isEmpty()) {
  305. Toast.makeText(context, "URL is empty", Toast.LENGTH_SHORT).show()
  306. return
  307. }
  308. if (!url.endsWith(".apk")) {
  309. Toast.makeText(context, "URL is not an APK", Toast.LENGTH_SHORT).show()
  310. return
  311. }
  312. if (!url.startsWith("http")) {
  313. Toast.makeText(context, "URL is not valid", Toast.LENGTH_SHORT).show()
  314. return
  315. }
  316. var job: Job? = null
  317. lateinit var dialogBinding: DialogUpdateBinding
  318. lateinit var dialog: AlertDialog
  319. withContext(Dispatchers.Main) {
  320. dialogBinding = DialogUpdateBinding.inflate(layoutInflater)
  321. dialog = MaterialAlertDialogBuilder(requireContext())
  322. .setTitle("Installing...")
  323. .setView(dialogBinding.root)
  324. .setCancelable(false)
  325. .setNegativeButton("Cancel") { _, _ ->
  326. job?.cancel()
  327. }
  328. .create()
  329. dialog.show()
  330. }
  331. job = CoroutineScope(Dispatchers.IO).launch {
  332. try {
  333. val file = File.createTempFile("files", ".apk")
  334. ktorClient.prepareGet(url)
  335. .execute { httpResponse ->
  336. val channel: ByteReadChannel = httpResponse.body()
  337. while (!channel.isClosedForRead) {
  338. val packet = channel.readRemaining(DEFAULT_BUFFER_SIZE.toLong())
  339. while (!packet.isEmpty) {
  340. val bytes = packet.readBytes()
  341. file.appendBytes(bytes)
  342. withContext(
  343. Dispatchers.Main
  344. ) {
  345. dialogBinding.progressBar.isIndeterminate = false
  346. dialogBinding.progressBar.progress =
  347. (file.length() * 100 / httpResponse.contentLength()!!).toInt()
  348. }
  349. }
  350. }
  351. }
  352. Log.i(tag, "A file saved to ${file.path}")
  353. shellRun("pm install -d -r ${file.path}")
  354. } catch (e: Exception) {
  355. Log.e(tag, "Failed to download apk", e)
  356. }
  357. withContext(Dispatchers.Main) {
  358. dialog.dismiss()
  359. }
  360. }
  361. }
  362. }