mirror of
https://github.com/topjohnwu/Magisk.git
synced 2024-12-24 19:57:43 +00:00
Tapjacking protection
This commit is contained in:
parent
8ac1181e9a
commit
533cb8eb58
@ -70,6 +70,10 @@ abstract class BaseUIActivity<VM : BaseViewModel, Binding : ViewDataBinding> :
|
||||
ensureInsets()
|
||||
}
|
||||
|
||||
fun setAccessibilityDelegate(delegate: View.AccessibilityDelegate?) {
|
||||
viewRoot.rootView.accessibilityDelegate = delegate
|
||||
}
|
||||
|
||||
override fun onResume() {
|
||||
super.onResume()
|
||||
viewModel.requestRefresh()
|
||||
@ -79,7 +83,7 @@ abstract class BaseUIActivity<VM : BaseViewModel, Binding : ViewDataBinding> :
|
||||
return currentFragment?.onKeyEvent(event) == true || super.dispatchKeyEvent(event)
|
||||
}
|
||||
|
||||
override fun onEventDispatched(event: ViewEvent) = when(event) {
|
||||
override fun onEventDispatched(event: ViewEvent) = when (event) {
|
||||
is ContextExecutor -> event(this)
|
||||
is ActivityExecutor -> event(this)
|
||||
else -> Unit
|
||||
|
@ -50,6 +50,7 @@ object Config : PreferenceModel, DBConfig {
|
||||
const val SU_AUTO_RESPONSE = "su_auto_response"
|
||||
const val SU_NOTIFICATION = "su_notification"
|
||||
const val SU_REAUTH = "su_reauth"
|
||||
const val SU_TAPJACK = "su_tapjack"
|
||||
const val CHECK_UPDATES = "check_update"
|
||||
const val UPDATE_CHANNEL = "update_channel"
|
||||
const val CUSTOM_CHANNEL = "custom_channel"
|
||||
@ -134,6 +135,7 @@ object Config : PreferenceModel, DBConfig {
|
||||
var darkTheme by preference(Key.DARK_THEME, AppCompatDelegate.MODE_NIGHT_FOLLOW_SYSTEM)
|
||||
var themeOrdinal by preference(Key.THEME_ORDINAL, Theme.Piplup.ordinal)
|
||||
var suReAuth by preference(Key.SU_REAUTH, false)
|
||||
var suTapjack by preference(Key.SU_TAPJACK, true)
|
||||
var checkUpdate by preference(Key.CHECK_UPDATES, true)
|
||||
var doh by preference(Key.DOH, false)
|
||||
var magiskHide by preference(Key.MAGISKHIDE, true)
|
||||
|
@ -4,6 +4,7 @@ import android.app.Activity
|
||||
import android.content.ActivityNotFoundException
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.view.View
|
||||
import android.widget.Toast
|
||||
import androidx.navigation.NavDirections
|
||||
import com.topjohnwu.magisk.MainDirections
|
||||
@ -58,9 +59,11 @@ class DieEvent : ViewEvent(), ActivityExecutor {
|
||||
}
|
||||
}
|
||||
|
||||
class ShowUIEvent : ViewEvent(), ActivityExecutor {
|
||||
class ShowUIEvent(private val delegate: View.AccessibilityDelegate?)
|
||||
: ViewEvent(), ActivityExecutor {
|
||||
override fun invoke(activity: BaseUIActivity<*, *>) {
|
||||
activity.setContentView()
|
||||
activity.setAccessibilityDelegate(delegate)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -195,6 +195,13 @@ object SystemlessHosts : BaseSettingsItem.Blank() {
|
||||
override val description = R.string.settings_hosts_summary.asTransitive()
|
||||
}
|
||||
|
||||
object Tapjack : BaseSettingsItem.Toggle() {
|
||||
override val title = R.string.settings_su_tapjack_title.asTransitive()
|
||||
override var description = R.string.settings_su_tapjack_summary.asTransitive()
|
||||
override var value = Config.suTapjack
|
||||
set(value) = setV(value, field, { field = it }) { Config.suTapjack = it }
|
||||
}
|
||||
|
||||
object Biometrics : BaseSettingsItem.Toggle() {
|
||||
override val title = R.string.settings_su_biometric_title.asTransitive()
|
||||
override var value = Config.suBiometric
|
||||
|
@ -79,7 +79,7 @@ class SettingsViewModel(
|
||||
if (Utils.showSuperUser()) {
|
||||
list.addAll(listOf(
|
||||
Superuser,
|
||||
Biometrics, AccessMode, MultiuserMode, MountNamespaceMode,
|
||||
Tapjack, Biometrics, AccessMode, MultiuserMode, MountNamespaceMode,
|
||||
AutomaticResponse, RequestTimeout, SUNotification
|
||||
))
|
||||
if (Build.VERSION.SDK_INT < 23) {
|
||||
|
@ -28,8 +28,8 @@ open class SuRequestActivity : BaseUIActivity<SuRequestViewModel, ActivityReques
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
supportRequestWindowFeature(Window.FEATURE_NO_TITLE)
|
||||
lockOrientation()
|
||||
window.setFlags(WindowManager.LayoutParams.FLAG_SECURE,
|
||||
WindowManager.LayoutParams.FLAG_SECURE)
|
||||
window.addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON)
|
||||
window.addFlags(WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_HIDDEN)
|
||||
super.onCreate(savedInstanceState)
|
||||
|
||||
fun showRequest() {
|
||||
|
@ -1,11 +1,20 @@
|
||||
package com.topjohnwu.magisk.ui.surequest
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.content.Intent
|
||||
import android.content.SharedPreferences
|
||||
import android.content.pm.PackageManager
|
||||
import android.content.res.Resources
|
||||
import android.graphics.drawable.Drawable
|
||||
import android.os.Bundle
|
||||
import android.os.CountDownTimer
|
||||
import android.view.MotionEvent
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.view.accessibility.AccessibilityEvent
|
||||
import android.view.accessibility.AccessibilityNodeInfo
|
||||
import android.view.accessibility.AccessibilityNodeProvider
|
||||
import android.widget.Toast
|
||||
import androidx.databinding.Bindable
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import com.topjohnwu.magisk.BR
|
||||
@ -22,6 +31,7 @@ import com.topjohnwu.magisk.events.DieEvent
|
||||
import com.topjohnwu.magisk.events.ShowUIEvent
|
||||
import com.topjohnwu.magisk.events.dialog.BiometricEvent
|
||||
import com.topjohnwu.magisk.ui.superuser.SpinnerRvItem
|
||||
import com.topjohnwu.magisk.utils.Utils
|
||||
import com.topjohnwu.magisk.utils.set
|
||||
import kotlinx.coroutines.launch
|
||||
import me.tatarka.bindingcollectionadapter2.BindingListViewAdapter
|
||||
@ -51,6 +61,19 @@ class SuRequestViewModel(
|
||||
var grantEnabled = false
|
||||
set(value) = set(value, field, { field = it }, BR.grantEnabled)
|
||||
|
||||
@SuppressLint("ClickableViewAccessibility")
|
||||
val grantTouchListener = View.OnTouchListener { _: View, event: MotionEvent ->
|
||||
// Filter obscured touches by consuming them.
|
||||
if (event.flags and MotionEvent.FLAG_WINDOW_IS_OBSCURED != 0
|
||||
|| event.flags and MotionEvent.FLAG_WINDOW_IS_PARTIALLY_OBSCURED != 0) {
|
||||
if (event.action == MotionEvent.ACTION_UP) {
|
||||
Utils.toast(R.string.touch_filtered_warning, Toast.LENGTH_SHORT)
|
||||
}
|
||||
return@OnTouchListener Config.suTapjack
|
||||
}
|
||||
false
|
||||
}
|
||||
|
||||
private val items = res.getStringArray(R.array.allow_timeout).map { SpinnerRvItem(it) }
|
||||
val adapter = BindingListViewAdapter<SpinnerRvItem>(1).apply {
|
||||
itemBinding = ItemBinding.of { binding, _, item ->
|
||||
@ -104,7 +127,7 @@ class SuRequestViewModel(
|
||||
timer = SuTimer(millis, 1000).apply { start() }
|
||||
|
||||
// Actually show the UI
|
||||
ShowUIEvent().publish()
|
||||
ShowUIEvent(if (Config.suTapjack) EmptyAccessibilityDelegate else null).publish()
|
||||
}
|
||||
|
||||
private fun respond(action: Int) {
|
||||
@ -141,4 +164,18 @@ class SuRequestViewModel(
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Invisible for accessibility services
|
||||
object EmptyAccessibilityDelegate : View.AccessibilityDelegate() {
|
||||
override fun sendAccessibilityEvent(host: View?, eventType: Int) {}
|
||||
override fun performAccessibilityAction(host: View?, action: Int, args: Bundle?) = true
|
||||
override fun sendAccessibilityEventUnchecked(host: View?, event: AccessibilityEvent?) {}
|
||||
override fun dispatchPopulateAccessibilityEvent(host: View?, event: AccessibilityEvent?) = true
|
||||
override fun onPopulateAccessibilityEvent(host: View?, event: AccessibilityEvent?) {}
|
||||
override fun onInitializeAccessibilityEvent(host: View?, event: AccessibilityEvent?) {}
|
||||
override fun onInitializeAccessibilityNodeInfo(host: View, info: AccessibilityNodeInfo) {}
|
||||
override fun addExtraDataToAccessibilityNodeInfo(host: View, info: AccessibilityNodeInfo, extraDataKey: String, arguments: Bundle?) {}
|
||||
override fun onRequestSendAccessibilityEvent(host: ViewGroup?, child: View?, event: AccessibilityEvent?): Boolean = false
|
||||
override fun getAccessibilityNodeProvider(host: View?): AccessibilityNodeProvider? = null
|
||||
}
|
||||
}
|
||||
|
@ -138,6 +138,7 @@
|
||||
<Button
|
||||
android:id="@+id/grant_btn"
|
||||
style="@style/WidgetFoundation.Button.Text"
|
||||
onTouch="@{viewModel.grantTouchListener}"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
|
@ -56,6 +56,7 @@
|
||||
|
||||
<!--Superuser-->
|
||||
<string name="su_request_title">Superuser Request</string>
|
||||
<string name="touch_filtered_warning">Because an app is obscuring a superuser request, Magisk can’t verify your response</string>
|
||||
<string name="deny">Deny</string>
|
||||
<string name="prompt">Prompt</string>
|
||||
<string name="grant">Grant</string>
|
||||
@ -172,6 +173,8 @@
|
||||
<string name="superuser_notification">Superuser Notification</string>
|
||||
<string name="settings_su_reauth_title">Reauthenticate after upgrade</string>
|
||||
<string name="settings_su_reauth_summary">Reauthenticate superuser permissions after an application upgrades</string>
|
||||
<string name="settings_su_tapjack_title">Enable Tapjacking Protection</string>
|
||||
<string name="settings_su_tapjack_summary">The superuser prompt dialog will not respond to input while obscured by any other window or overlay</string>
|
||||
<string name="settings_su_biometric_title">Enable Biometric Authentication</string>
|
||||
<string name="settings_su_biometric_summary">Use biometric authentication to allow superuser requests</string>
|
||||
<string name="no_biometric">Unsupported device or no biometric settings are enabled</string>
|
||||
|
Loading…
x
Reference in New Issue
Block a user