Commit 4bbd7989 authored by topjohnwu's avatar topjohnwu

Update snet extension

Receive full snet payload from extension
parent a0b47f3c
...@@ -12,8 +12,8 @@ object Const { ...@@ -12,8 +12,8 @@ object Const {
const val MAGISK_LOG = "/cache/magisk.log" const val MAGISK_LOG = "/cache/magisk.log"
// Versions // Versions
const val SNET_EXT_VER = 13 const val SNET_EXT_VER = 14
const val SNET_REVISION = "a6c47f86f10b310358afa9dbe837037dd5d561df" const val SNET_REVISION = "5e28617412bdad2396eab87fa786094d8242e568"
const val BOOTCTL_REVISION = "a6c47f86f10b310358afa9dbe837037dd5d561df" const val BOOTCTL_REVISION = "a6c47f86f10b310358afa9dbe837037dd5d561df"
// Misc // Misc
......
package com.topjohnwu.magisk.core.utils package com.topjohnwu.magisk.core.utils
import org.json.JSONObject
interface SafetyNetHelper { interface SafetyNetHelper {
val version: Int val version: Int
...@@ -7,15 +9,6 @@ interface SafetyNetHelper { ...@@ -7,15 +9,6 @@ interface SafetyNetHelper {
fun attest() fun attest()
interface Callback { interface Callback {
fun onResponse(responseCode: Int) fun onResponse(response: JSONObject?)
}
companion object {
const val RESPONSE_ERR = 0x01
const val CONNECTION_FAIL = 0x02
const val BASIC_PASS = 0x10
const val CTS_PASS = 0x20
} }
} }
...@@ -2,10 +2,14 @@ package com.topjohnwu.magisk.model.events ...@@ -2,10 +2,14 @@ package com.topjohnwu.magisk.model.events
import com.topjohnwu.magisk.core.model.MagiskPolicy import com.topjohnwu.magisk.core.model.MagiskPolicy
import com.topjohnwu.magisk.utils.RxBus import com.topjohnwu.magisk.utils.RxBus
import org.json.JSONObject
sealed class PolicyUpdateEvent(val item: MagiskPolicy) : RxBus.Event { sealed class PolicyUpdateEvent(val item: MagiskPolicy) : RxBus.Event {
class Notification(item: MagiskPolicy) : PolicyUpdateEvent(item) class Notification(item: MagiskPolicy) : PolicyUpdateEvent(item)
class Log(item: MagiskPolicy) : PolicyUpdateEvent(item) class Log(item: MagiskPolicy) : PolicyUpdateEvent(item)
} }
data class SafetyNetResult(val responseCode: Int) : RxBus.Event data class SafetyNetResult(
val response: JSONObject? = null,
val dismiss: Boolean = false
) : RxBus.Event
...@@ -10,6 +10,7 @@ import com.topjohnwu.magisk.core.model.module.Repo ...@@ -10,6 +10,7 @@ import com.topjohnwu.magisk.core.model.module.Repo
import com.topjohnwu.magisk.core.utils.SafetyNetHelper import com.topjohnwu.magisk.core.utils.SafetyNetHelper
import com.topjohnwu.magisk.data.repository.MagiskRepository import com.topjohnwu.magisk.data.repository.MagiskRepository
import com.topjohnwu.magisk.extensions.DynamicClassLoader import com.topjohnwu.magisk.extensions.DynamicClassLoader
import com.topjohnwu.magisk.extensions.OnErrorListener
import com.topjohnwu.magisk.extensions.subscribeK import com.topjohnwu.magisk.extensions.subscribeK
import com.topjohnwu.magisk.extensions.writeTo import com.topjohnwu.magisk.extensions.writeTo
import com.topjohnwu.magisk.utils.RxBus import com.topjohnwu.magisk.utils.RxBus
...@@ -19,8 +20,10 @@ import com.topjohnwu.superuser.Shell ...@@ -19,8 +20,10 @@ import com.topjohnwu.superuser.Shell
import dalvik.system.DexFile import dalvik.system.DexFile
import io.reactivex.Completable import io.reactivex.Completable
import io.reactivex.subjects.PublishSubject import io.reactivex.subjects.PublishSubject
import org.json.JSONObject
import org.koin.core.KoinComponent import org.koin.core.KoinComponent
import org.koin.core.inject import org.koin.core.inject
import timber.log.Timber
import java.io.File import java.io.File
import java.lang.reflect.InvocationHandler import java.lang.reflect.InvocationHandler
...@@ -40,18 +43,25 @@ class UpdateSafetyNetEvent : ViewEvent(), ContextExecutor, KoinComponent, Safety ...@@ -40,18 +43,25 @@ class UpdateSafetyNetEvent : ViewEvent(), ContextExecutor, KoinComponent, Safety
private val magiskRepo by inject<MagiskRepository>() private val magiskRepo by inject<MagiskRepository>()
private val rxBus by inject<RxBus>() private val rxBus by inject<RxBus>()
private lateinit var EXT_APK: File private lateinit var apk: File
private lateinit var EXT_DEX: File private lateinit var dex: File
override fun invoke(context: Context) { override fun invoke(context: Context) {
val die = ::EXT_APK.isInitialized apk = File("${context.filesDir.parent}/snet", "snet.jar")
dex = File(apk.parent, "snet.dex")
EXT_APK = File("${context.filesDir.parent}/snet", "snet.jar") attest(context) {
EXT_DEX = File(EXT_APK.parent, "snet.dex") // Download and retry
Shell.sh("rm -rf " + apk.parent).exec()
apk.parentFile?.mkdir()
download(context, true)
}
}
private fun attest(context: Context, onError: OnErrorListener) {
Completable.fromAction { Completable.fromAction {
val loader = DynamicClassLoader(EXT_APK) val loader = DynamicClassLoader(apk)
val dex = DexFile.loadDex(EXT_APK.path, EXT_DEX.path, 0) val dex = DexFile.loadDex(apk.path, dex.path, 0)
// Scan through the dex and find our helper class // Scan through the dex and find our helper class
var helperClass: Class<*>? = null var helperClass: Class<*>? = null
...@@ -66,32 +76,25 @@ class UpdateSafetyNetEvent : ViewEvent(), ContextExecutor, KoinComponent, Safety ...@@ -66,32 +76,25 @@ class UpdateSafetyNetEvent : ViewEvent(), ContextExecutor, KoinComponent, Safety
} }
helperClass ?: throw Exception() helperClass ?: throw Exception()
val helper = helperClass.getMethod( val helper = helperClass
"get", .getMethod("get", Class::class.java, Context::class.java, Any::class.java)
Class::class.java, Context::class.java, Any::class.java
)
.invoke(null, SafetyNetHelper::class.java, context, this) as SafetyNetHelper .invoke(null, SafetyNetHelper::class.java, context, this) as SafetyNetHelper
if (helper.version < Const.SNET_EXT_VER) if (helper.version < Const.SNET_EXT_VER)
throw Exception() throw Exception()
helper.attest() helper.attest()
}.subscribeK(onError = { }.subscribeK(onError = onError)
if (die) {
rxBus.post(SafetyNetResult(-1))
} else {
Shell.sh("rm -rf " + EXT_APK.parent).exec()
EXT_APK.parentFile?.mkdir()
download(context, true)
}
})
} }
@Suppress("SameParameterValue") @Suppress("SameParameterValue")
private fun download(context: Context, askUser: Boolean) { private fun download(context: Context, askUser: Boolean) {
fun downloadInternal() = magiskRepo.fetchSafetynet() fun downloadInternal() = magiskRepo.fetchSafetynet()
.map { it.byteStream().writeTo(EXT_APK) } .map { it.byteStream().writeTo(apk) }
.subscribeK { invoke(context) } .subscribeK { attest(context) {
Timber.e(it)
rxBus.post(SafetyNetResult())
} }
if (!askUser) { if (!askUser) {
downloadInternal() downloadInternal()
...@@ -107,14 +110,14 @@ class UpdateSafetyNetEvent : ViewEvent(), ContextExecutor, KoinComponent, Safety ...@@ -107,14 +110,14 @@ class UpdateSafetyNetEvent : ViewEvent(), ContextExecutor, KoinComponent, Safety
onClick { downloadInternal() } onClick { downloadInternal() }
} }
.applyButton(MagiskDialog.ButtonType.NEGATIVE) { .applyButton(MagiskDialog.ButtonType.NEGATIVE) {
titleRes = android.R.string.no titleRes = android.R.string.cancel
onClick { rxBus.post(SafetyNetResult(-2)) } onClick { rxBus.post(SafetyNetResult(dismiss = true)) }
} }
.reveal() .reveal()
} }
override fun onResponse(responseCode: Int) { override fun onResponse(response: JSONObject?) {
rxBus.post(SafetyNetResult(responseCode)) rxBus.post(SafetyNetResult(response))
} }
} }
......
...@@ -3,7 +3,6 @@ package com.topjohnwu.magisk.ui.safetynet ...@@ -3,7 +3,6 @@ package com.topjohnwu.magisk.ui.safetynet
import androidx.databinding.Bindable import androidx.databinding.Bindable
import com.topjohnwu.magisk.BR import com.topjohnwu.magisk.BR
import com.topjohnwu.magisk.R import com.topjohnwu.magisk.R
import com.topjohnwu.magisk.core.utils.SafetyNetHelper
import com.topjohnwu.magisk.extensions.subscribeK import com.topjohnwu.magisk.extensions.subscribeK
import com.topjohnwu.magisk.model.events.SafetyNetResult import com.topjohnwu.magisk.model.events.SafetyNetResult
import com.topjohnwu.magisk.model.events.UpdateSafetyNetEvent import com.topjohnwu.magisk.model.events.UpdateSafetyNetEvent
...@@ -11,6 +10,7 @@ import com.topjohnwu.magisk.ui.base.BaseViewModel ...@@ -11,6 +10,7 @@ import com.topjohnwu.magisk.ui.base.BaseViewModel
import com.topjohnwu.magisk.ui.safetynet.SafetyNetState.* import com.topjohnwu.magisk.ui.safetynet.SafetyNetState.*
import com.topjohnwu.magisk.utils.KObservableField import com.topjohnwu.magisk.utils.KObservableField
import com.topjohnwu.magisk.utils.RxBus import com.topjohnwu.magisk.utils.RxBus
import org.json.JSONObject
enum class SafetyNetState { enum class SafetyNetState {
LOADING, PASS, FAILED, IDLE LOADING, PASS, FAILED, IDLE
...@@ -35,14 +35,12 @@ class SafetynetViewModel( ...@@ -35,14 +35,12 @@ class SafetynetViewModel(
init { init {
rxBus.register<SafetyNetResult>() rxBus.register<SafetyNetResult>()
.subscribeK { resolveResponse(it.responseCode) } .subscribeK { resolveResponse(it) }
.add() .add()
if (safetyNetResult >= 0) { cachedResult?.also {
resolveResponse(safetyNetResult) resolveResponse(SafetyNetResult(it))
} else { } ?: attest()
attest()
}
} }
override fun notifyStateChanged() { override fun notifyStateChanged() {
...@@ -59,38 +57,40 @@ class SafetynetViewModel( ...@@ -59,38 +57,40 @@ class SafetynetViewModel(
fun reset() = attest() fun reset() = attest()
private fun resolveResponse(response: Int) = when { private fun resolveResponse(response: SafetyNetResult) {
response and 0x0F == 0 -> { if (response.dismiss) {
val hasCtsPassed = response and SafetyNetHelper.CTS_PASS != 0 back()
val hasBasicIntegrityPassed = response and SafetyNetHelper.BASIC_PASS != 0 return
val result = hasCtsPassed && hasBasicIntegrityPassed }
safetyNetResult = response
ctsState.value = hasCtsPassed response.response?.apply {
basicIntegrityState.value = hasBasicIntegrityPassed runCatching {
val cts = getBoolean("ctsProfileMatch")
val basic = getBoolean("basicIntegrity")
val result = cts && basic
cachedResult = this
ctsState.value = cts
basicIntegrityState.value = basic
currentState = if (result) PASS else FAILED currentState = if (result) PASS else FAILED
safetyNetTitle.value = safetyNetTitle.value =
if (result) R.string.safetynet_attest_success if (result) R.string.safetynet_attest_success
else R.string.safetynet_attest_failure else R.string.safetynet_attest_failure
} }.onFailure {
response == -2 -> {
currentState = FAILED currentState = FAILED
ctsState.value = false ctsState.value = false
basicIntegrityState.value = false basicIntegrityState.value = false
back() safetyNetTitle.value = R.string.safetynet_res_invalid
} }
else -> { } ?: {
currentState = FAILED currentState = FAILED
ctsState.value = false ctsState.value = false
basicIntegrityState.value = false basicIntegrityState.value = false
safetyNetTitle.value = when (response) { safetyNetTitle.value = R.string.safetynet_api_error
SafetyNetHelper.RESPONSE_ERR -> R.string.safetynet_res_invalid }()
else -> R.string.safetynet_api_error
}
}
} }
companion object { companion object {
private var safetyNetResult = -1 private var cachedResult: JSONObject? = null
} }
} }
...@@ -398,6 +398,22 @@ def build_stub(args): ...@@ -398,6 +398,22 @@ def build_stub(args):
header('* Building Magisk Manager stub') header('* Building Magisk Manager stub')
build_apk(args, 'stub') build_apk(args, 'stub')
# Bind mount snet package on top of the stub folder
def build_snet(args):
header('* Building snet extension')
proc = execv([gradlew, 'stub:assembleRelease'])
if proc.returncode != 0:
error('Build snet extention failed!')
source = op.join('stub', 'build', 'outputs', 'apk',
'release', 'stub-release.apk')
target = op.join(config['outdir'], 'snet.jar')
# Extract classes.dex
with zipfile.ZipFile(target, 'w', compression=zipfile.ZIP_DEFLATED, allowZip64=False) as zout:
with zipfile.ZipFile(source) as zin:
zout.writestr('classes.dex', zin.read('classes.dex'))
rm(source)
header('Output: ' + target)
def zip_main(args): def zip_main(args):
header('* Packing Flashable Zip') header('* Packing Flashable Zip')
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment