Tapjacking protection

This commit is contained in:
vvb2060 2020-10-22 02:40:47 -07:00 committed by topjohnwu
parent 8ac1181e9a
commit 533cb8eb58
9 changed files with 63 additions and 6 deletions

View File

@ -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

View File

@ -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)

View File

@ -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)
}
}

View File

@ -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

View File

@ -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) {

View File

@ -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() {

View File

@ -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
}
}

View File

@ -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"

View File

@ -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 cant 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>