Update snet extension
Receive full snet payload from extension
This commit is contained in:
parent
a0b47f3ca3
commit
4bbd7989dd
@ -12,8 +12,8 @@ object Const {
|
||||
const val MAGISK_LOG = "/cache/magisk.log"
|
||||
|
||||
// Versions
|
||||
const val SNET_EXT_VER = 13
|
||||
const val SNET_REVISION = "a6c47f86f10b310358afa9dbe837037dd5d561df"
|
||||
const val SNET_EXT_VER = 14
|
||||
const val SNET_REVISION = "5e28617412bdad2396eab87fa786094d8242e568"
|
||||
const val BOOTCTL_REVISION = "a6c47f86f10b310358afa9dbe837037dd5d561df"
|
||||
|
||||
// Misc
|
||||
|
@ -1,5 +1,7 @@
|
||||
package com.topjohnwu.magisk.core.utils
|
||||
|
||||
import org.json.JSONObject
|
||||
|
||||
interface SafetyNetHelper {
|
||||
|
||||
val version: Int
|
||||
@ -7,15 +9,6 @@ interface SafetyNetHelper {
|
||||
fun attest()
|
||||
|
||||
interface Callback {
|
||||
fun onResponse(responseCode: Int)
|
||||
}
|
||||
|
||||
companion object {
|
||||
|
||||
const val RESPONSE_ERR = 0x01
|
||||
const val CONNECTION_FAIL = 0x02
|
||||
|
||||
const val BASIC_PASS = 0x10
|
||||
const val CTS_PASS = 0x20
|
||||
fun onResponse(response: JSONObject?)
|
||||
}
|
||||
}
|
||||
|
@ -2,10 +2,14 @@ package com.topjohnwu.magisk.model.events
|
||||
|
||||
import com.topjohnwu.magisk.core.model.MagiskPolicy
|
||||
import com.topjohnwu.magisk.utils.RxBus
|
||||
import org.json.JSONObject
|
||||
|
||||
sealed class PolicyUpdateEvent(val item: MagiskPolicy) : RxBus.Event {
|
||||
class Notification(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
|
||||
import com.topjohnwu.magisk.core.utils.SafetyNetHelper
|
||||
import com.topjohnwu.magisk.data.repository.MagiskRepository
|
||||
import com.topjohnwu.magisk.extensions.DynamicClassLoader
|
||||
import com.topjohnwu.magisk.extensions.OnErrorListener
|
||||
import com.topjohnwu.magisk.extensions.subscribeK
|
||||
import com.topjohnwu.magisk.extensions.writeTo
|
||||
import com.topjohnwu.magisk.utils.RxBus
|
||||
@ -19,8 +20,10 @@ import com.topjohnwu.superuser.Shell
|
||||
import dalvik.system.DexFile
|
||||
import io.reactivex.Completable
|
||||
import io.reactivex.subjects.PublishSubject
|
||||
import org.json.JSONObject
|
||||
import org.koin.core.KoinComponent
|
||||
import org.koin.core.inject
|
||||
import timber.log.Timber
|
||||
import java.io.File
|
||||
import java.lang.reflect.InvocationHandler
|
||||
|
||||
@ -40,18 +43,25 @@ class UpdateSafetyNetEvent : ViewEvent(), ContextExecutor, KoinComponent, Safety
|
||||
private val magiskRepo by inject<MagiskRepository>()
|
||||
private val rxBus by inject<RxBus>()
|
||||
|
||||
private lateinit var EXT_APK: File
|
||||
private lateinit var EXT_DEX: File
|
||||
private lateinit var apk: File
|
||||
private lateinit var dex: File
|
||||
|
||||
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")
|
||||
EXT_DEX = File(EXT_APK.parent, "snet.dex")
|
||||
attest(context) {
|
||||
// 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 {
|
||||
val loader = DynamicClassLoader(EXT_APK)
|
||||
val dex = DexFile.loadDex(EXT_APK.path, EXT_DEX.path, 0)
|
||||
val loader = DynamicClassLoader(apk)
|
||||
val dex = DexFile.loadDex(apk.path, dex.path, 0)
|
||||
|
||||
// Scan through the dex and find our helper class
|
||||
var helperClass: Class<*>? = null
|
||||
@ -66,32 +76,25 @@ class UpdateSafetyNetEvent : ViewEvent(), ContextExecutor, KoinComponent, Safety
|
||||
}
|
||||
helperClass ?: throw Exception()
|
||||
|
||||
val helper = helperClass.getMethod(
|
||||
"get",
|
||||
Class::class.java, Context::class.java, Any::class.java
|
||||
)
|
||||
val helper = helperClass
|
||||
.getMethod("get", Class::class.java, Context::class.java, Any::class.java)
|
||||
.invoke(null, SafetyNetHelper::class.java, context, this) as SafetyNetHelper
|
||||
|
||||
if (helper.version < Const.SNET_EXT_VER)
|
||||
throw Exception()
|
||||
|
||||
helper.attest()
|
||||
}.subscribeK(onError = {
|
||||
if (die) {
|
||||
rxBus.post(SafetyNetResult(-1))
|
||||
} else {
|
||||
Shell.sh("rm -rf " + EXT_APK.parent).exec()
|
||||
EXT_APK.parentFile?.mkdir()
|
||||
download(context, true)
|
||||
}
|
||||
})
|
||||
}.subscribeK(onError = onError)
|
||||
}
|
||||
|
||||
@Suppress("SameParameterValue")
|
||||
private fun download(context: Context, askUser: Boolean) {
|
||||
fun downloadInternal() = magiskRepo.fetchSafetynet()
|
||||
.map { it.byteStream().writeTo(EXT_APK) }
|
||||
.subscribeK { invoke(context) }
|
||||
.map { it.byteStream().writeTo(apk) }
|
||||
.subscribeK { attest(context) {
|
||||
Timber.e(it)
|
||||
rxBus.post(SafetyNetResult())
|
||||
} }
|
||||
|
||||
if (!askUser) {
|
||||
downloadInternal()
|
||||
@ -107,14 +110,14 @@ class UpdateSafetyNetEvent : ViewEvent(), ContextExecutor, KoinComponent, Safety
|
||||
onClick { downloadInternal() }
|
||||
}
|
||||
.applyButton(MagiskDialog.ButtonType.NEGATIVE) {
|
||||
titleRes = android.R.string.no
|
||||
onClick { rxBus.post(SafetyNetResult(-2)) }
|
||||
titleRes = android.R.string.cancel
|
||||
onClick { rxBus.post(SafetyNetResult(dismiss = true)) }
|
||||
}
|
||||
.reveal()
|
||||
}
|
||||
|
||||
override fun onResponse(responseCode: Int) {
|
||||
rxBus.post(SafetyNetResult(responseCode))
|
||||
override fun onResponse(response: JSONObject?) {
|
||||
rxBus.post(SafetyNetResult(response))
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -3,7 +3,6 @@ package com.topjohnwu.magisk.ui.safetynet
|
||||
import androidx.databinding.Bindable
|
||||
import com.topjohnwu.magisk.BR
|
||||
import com.topjohnwu.magisk.R
|
||||
import com.topjohnwu.magisk.core.utils.SafetyNetHelper
|
||||
import com.topjohnwu.magisk.extensions.subscribeK
|
||||
import com.topjohnwu.magisk.model.events.SafetyNetResult
|
||||
import com.topjohnwu.magisk.model.events.UpdateSafetyNetEvent
|
||||
@ -11,6 +10,7 @@ import com.topjohnwu.magisk.ui.base.BaseViewModel
|
||||
import com.topjohnwu.magisk.ui.safetynet.SafetyNetState.*
|
||||
import com.topjohnwu.magisk.utils.KObservableField
|
||||
import com.topjohnwu.magisk.utils.RxBus
|
||||
import org.json.JSONObject
|
||||
|
||||
enum class SafetyNetState {
|
||||
LOADING, PASS, FAILED, IDLE
|
||||
@ -35,14 +35,12 @@ class SafetynetViewModel(
|
||||
|
||||
init {
|
||||
rxBus.register<SafetyNetResult>()
|
||||
.subscribeK { resolveResponse(it.responseCode) }
|
||||
.subscribeK { resolveResponse(it) }
|
||||
.add()
|
||||
|
||||
if (safetyNetResult >= 0) {
|
||||
resolveResponse(safetyNetResult)
|
||||
} else {
|
||||
attest()
|
||||
}
|
||||
cachedResult?.also {
|
||||
resolveResponse(SafetyNetResult(it))
|
||||
} ?: attest()
|
||||
}
|
||||
|
||||
override fun notifyStateChanged() {
|
||||
@ -59,38 +57,40 @@ class SafetynetViewModel(
|
||||
|
||||
fun reset() = attest()
|
||||
|
||||
private fun resolveResponse(response: Int) = when {
|
||||
response and 0x0F == 0 -> {
|
||||
val hasCtsPassed = response and SafetyNetHelper.CTS_PASS != 0
|
||||
val hasBasicIntegrityPassed = response and SafetyNetHelper.BASIC_PASS != 0
|
||||
val result = hasCtsPassed && hasBasicIntegrityPassed
|
||||
safetyNetResult = response
|
||||
ctsState.value = hasCtsPassed
|
||||
basicIntegrityState.value = hasBasicIntegrityPassed
|
||||
currentState = if (result) PASS else FAILED
|
||||
safetyNetTitle.value =
|
||||
if (result) R.string.safetynet_attest_success
|
||||
else R.string.safetynet_attest_failure
|
||||
}
|
||||
response == -2 -> {
|
||||
currentState = FAILED
|
||||
ctsState.value = false
|
||||
basicIntegrityState.value = false
|
||||
private fun resolveResponse(response: SafetyNetResult) {
|
||||
if (response.dismiss) {
|
||||
back()
|
||||
return
|
||||
}
|
||||
else -> {
|
||||
|
||||
response.response?.apply {
|
||||
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
|
||||
safetyNetTitle.value =
|
||||
if (result) R.string.safetynet_attest_success
|
||||
else R.string.safetynet_attest_failure
|
||||
}.onFailure {
|
||||
currentState = FAILED
|
||||
ctsState.value = false
|
||||
basicIntegrityState.value = false
|
||||
safetyNetTitle.value = R.string.safetynet_res_invalid
|
||||
}
|
||||
} ?: {
|
||||
currentState = FAILED
|
||||
ctsState.value = false
|
||||
basicIntegrityState.value = false
|
||||
safetyNetTitle.value = when (response) {
|
||||
SafetyNetHelper.RESPONSE_ERR -> R.string.safetynet_res_invalid
|
||||
else -> R.string.safetynet_api_error
|
||||
}
|
||||
}
|
||||
safetyNetTitle.value = R.string.safetynet_api_error
|
||||
}()
|
||||
}
|
||||
|
||||
companion object {
|
||||
private var safetyNetResult = -1
|
||||
private var cachedResult: JSONObject? = null
|
||||
}
|
||||
|
||||
}
|
||||
|
16
build.py
16
build.py
@ -398,6 +398,22 @@ def build_stub(args):
|
||||
header('* Building Magisk Manager 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):
|
||||
header('* Packing Flashable Zip')
|
||||
|
Loading…
Reference in New Issue
Block a user