Merge branch 'master' into feature/redesign

This commit is contained in:
topjohnwu 2020-01-02 14:52:50 +08:00
commit 40c64d50d5
82 changed files with 1960 additions and 1642 deletions

View File

@ -74,7 +74,7 @@ dependencies {
implementation "${bindingAdapter}:${vBAdapt}"
implementation "${bindingAdapter}-recyclerview:${vBAdapt}"
def vMarkwon = '4.1.2'
def vMarkwon = '4.2.0'
implementation "io.noties.markwon:core:${vMarkwon}"
implementation "io.noties.markwon:html:${vMarkwon}"
implementation "io.noties.markwon:image:${vMarkwon}"
@ -99,7 +99,7 @@ dependencies {
implementation "com.squareup.okhttp3:okhttp:${vOkHttp}"
implementation "com.squareup.okhttp3:logging-interceptor:${vOkHttp}"
def vMoshi = '1.9.1'
def vMoshi = '1.9.2'
implementation "com.squareup.moshi:moshi:${vMoshi}"
def vKotshi = '2.0.2'
@ -111,7 +111,7 @@ dependencies {
replacedBy('com.github.topjohnwu:room-runtime')
}
}
def vRoom = '2.2.1'
def vRoom = '2.2.2'
implementation "com.github.topjohnwu:room-runtime:${vRoom}"
implementation "androidx.room:room-rxjava2:${vRoom}"
kapt "androidx.room:room-compiler:${vRoom}"
@ -125,13 +125,12 @@ dependencies {
implementation 'androidx.swiperefreshlayout:swiperefreshlayout:1.1.0-alpha03'
implementation 'androidx.browser:browser:1.0.0'
implementation 'androidx.preference:preference:1.1.0'
implementation 'androidx.recyclerview:recyclerview:1.1.0-rc01'
implementation 'androidx.fragment:fragment-ktx:1.2.0-rc02'
implementation 'androidx.recyclerview:recyclerview:1.1.0'
implementation 'androidx.fragment:fragment-ktx:1.2.0-rc03'
implementation 'androidx.work:work-runtime:2.2.0'
implementation 'androidx.transition:transition:1.2.0'
implementation 'androidx.transition:transition:1.3.0-rc02'
implementation 'androidx.multidex:multidex:2.0.1'
implementation 'androidx.core:core-ktx:1.1.0'
implementation 'androidx.biometric:biometric:1.0.0'
implementation 'com.google.android.material:material:1.1.0-beta01'
implementation 'com.google.android.material:material:1.2.0-alpha01'
implementation 'com.karumi:dexter:6.0.0'
}

View File

@ -54,6 +54,7 @@ object Config : PreferenceModel, DBConfig {
const val REDESIGN = "redesign"
const val SAFETY = "safety_notice"
const val THEME_ORDINAL = "theme_ordinal"
const val BOOT_ID = "boot_id"
// system state
const val MAGISKHIDE = "magiskhide"
@ -111,6 +112,8 @@ object Config : PreferenceModel, DBConfig {
}
else Value.DEFAULT_CHANNEL
var bootId by preference(Key.BOOT_ID, "")
var downloadPath by preference(Key.DOWNLOAD_PATH, Environment.DIRECTORY_DOWNLOADS)
var repoOrder by preference(Key.REPO_ORDER, Value.ORDER_DATE)

View File

@ -24,9 +24,6 @@ import com.topjohnwu.magisk.ui.flash.FlashActivity
import com.topjohnwu.magisk.ui.surequest.SuRequestActivity
import com.topjohnwu.magisk.utils.refreshLocale
import com.topjohnwu.magisk.utils.updateConfig
import com.topjohnwu.magisk.utils.currentLocale
import com.topjohnwu.magisk.utils.defaultLocale
import java.util.*
import com.topjohnwu.magisk.redesign.MainActivity as RedesignActivity
fun AssetManager.addAssetPath(path: String) {

View File

@ -8,6 +8,8 @@ import com.topjohnwu.magisk.utils.CachedValue
import com.topjohnwu.magisk.utils.KObservableField
import com.topjohnwu.superuser.Shell
import com.topjohnwu.superuser.ShellUtils
import java.io.FileInputStream
import java.io.IOException
val isRunningAsStub get() = Info.stub != null
@ -36,6 +38,22 @@ object Info {
}
}
val isNewReboot by lazy {
try {
FileInputStream("/proc/sys/kernel/random/boot_id").bufferedReader().use {
val id = it.readLine()
if (id != Config.bootId) {
Config.bootId = id
true
} else {
false
}
}
} catch (e: IOException) {
false
}
}
private fun loadState() = runCatching {
val str = ShellUtils.fastCmd("magisk -v").split(":".toRegex())[0]
val code = ShellUtils.fastCmd("magisk -V").toInt()

View File

@ -3,8 +3,8 @@ package com.topjohnwu.magisk.data.database
import android.content.Context
import android.content.pm.PackageManager
import com.topjohnwu.magisk.Const
import com.topjohnwu.magisk.data.database.magiskdb.Delete
import com.topjohnwu.magisk.data.database.magiskdb.BaseDao
import com.topjohnwu.magisk.data.database.magiskdb.Delete
import com.topjohnwu.magisk.data.database.magiskdb.Replace
import com.topjohnwu.magisk.data.database.magiskdb.Select
import com.topjohnwu.magisk.extensions.now

View File

@ -1,7 +1,7 @@
package com.topjohnwu.magisk.data.database
import com.topjohnwu.magisk.data.database.magiskdb.Delete
import com.topjohnwu.magisk.data.database.magiskdb.BaseDao
import com.topjohnwu.magisk.data.database.magiskdb.Delete
import com.topjohnwu.magisk.data.database.magiskdb.Replace
import com.topjohnwu.magisk.data.database.magiskdb.Select

View File

@ -1,7 +1,7 @@
package com.topjohnwu.magisk.data.database
import com.topjohnwu.magisk.data.database.magiskdb.Delete
import com.topjohnwu.magisk.data.database.magiskdb.BaseDao
import com.topjohnwu.magisk.data.database.magiskdb.Delete
import com.topjohnwu.magisk.data.database.magiskdb.Replace
import com.topjohnwu.magisk.data.database.magiskdb.Select

View File

@ -33,8 +33,8 @@ import com.topjohnwu.magisk.FileProvider
import com.topjohnwu.magisk.utils.DynamicClassLoader
import com.topjohnwu.magisk.utils.Utils
import com.topjohnwu.magisk.utils.currentLocale
import com.topjohnwu.superuser.ShellUtils
import com.topjohnwu.superuser.Shell
import com.topjohnwu.superuser.ShellUtils
import java.io.File
import java.io.FileNotFoundException
import java.text.SimpleDateFormat
@ -344,4 +344,4 @@ val isSAR
val isAB
get() = ShellUtils
.fastCmd("grep_prop ro.build.ab_update")
.let { it.isNotEmpty() && it.toBoolean() }
.let { it.isNotEmpty() && it.toBoolean() }

View File

@ -1,7 +1,6 @@
package com.topjohnwu.magisk.extensions
import android.os.Build
import androidx.core.net.toFile
import timber.log.Timber
import java.io.File
import java.io.InputStream

View File

@ -3,7 +3,6 @@ package com.topjohnwu.magisk.model.download
import android.app.Activity
import android.app.Notification
import android.content.Intent
import androidx.core.app.NotificationCompat
import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
import com.topjohnwu.magisk.R

View File

@ -13,17 +13,21 @@ class Module(path: String) : BaseModule() {
override var versionCode: Int = -1
override var description: String = ""
private val removeFile: SuFile = SuFile(path, "remove")
private val disableFile: SuFile = SuFile(path, "disable")
private val updateFile: SuFile = SuFile(path, "update")
private val removeFile = SuFile(path, "remove")
private val disableFile = SuFile(path, "disable")
private val updateFile = SuFile(path, "update")
private val ruleFile = SuFile(path, "sepolicy.rule")
val updated: Boolean = updateFile.exists()
var enable: Boolean = !disableFile.exists()
set(enable) {
val dir = "$PERSIST/$id"
field = if (enable) {
Shell.su("mkdir -p $dir", "cp -af $ruleFile $dir").submit()
disableFile.delete()
} else {
Shell.su("rm -rf $dir").submit()
!disableFile.createNewFile()
}
}
@ -31,8 +35,10 @@ class Module(path: String) : BaseModule() {
var remove: Boolean = removeFile.exists()
set(remove) {
field = if (remove) {
Shell.su("rm -rf $PERSIST/$id").submit()
removeFile.createNewFile()
} else {
Shell.su("cp -af $ruleFile $PERSIST/$id").submit()
!removeFile.delete()
}
}
@ -54,6 +60,8 @@ class Module(path: String) : BaseModule() {
companion object {
private const val PERSIST = "/sbin/.magisk/mirror/persist/magisk"
@WorkerThread
fun loadModules(): List<Module> {
val moduleList = mutableListOf<Module>()

View File

@ -287,8 +287,10 @@ abstract class MagiskInstaller {
protected fun flashBoot(): Boolean {
if (!"direct_install $installDir $srcBoot".sh().isSuccess)
return false
if (!Info.keepVerity)
"patch_dtbo_image".sh()
arrayOf(
"(KEEPVERITY=${Info.keepVerity} patch_dtb_partitions)",
"run_migrations"
).sh()
return true
}

View File

@ -3,18 +3,16 @@ package com.topjohnwu.magisk.ui
import android.app.Activity
import android.content.Context
import android.os.Bundle
import android.text.TextUtils
import androidx.appcompat.app.AlertDialog
import com.topjohnwu.magisk.*
import com.topjohnwu.magisk.model.navigation.Navigation
import com.topjohnwu.magisk.BuildConfig
import com.topjohnwu.magisk.Config
import com.topjohnwu.magisk.intent
import com.topjohnwu.magisk.Info
import com.topjohnwu.magisk.model.navigation.Navigation
import com.topjohnwu.magisk.utils.Utils
import com.topjohnwu.magisk.view.Notifications
import com.topjohnwu.magisk.view.Shortcuts
import com.topjohnwu.magisk.wrap
import com.topjohnwu.superuser.Shell
import com.topjohnwu.superuser.ShellUtils
open class SplashActivity : Activity() {
@ -41,6 +39,10 @@ open class SplashActivity : Activity() {
}
}
Info.keepVerity = ShellUtils.fastCmd("echo \$KEEPVERITY").toBoolean()
Info.keepEnc = ShellUtils.fastCmd("echo \$KEEPFORCEENCRYPT").toBoolean()
Info.recovery = ShellUtils.fastCmd("echo \$RECOVERYMODE").toBoolean()
// Set default configs
Config.initialize()
@ -53,9 +55,13 @@ open class SplashActivity : Activity() {
// Setup shortcuts
Shortcuts.setup(this)
Shell.su("mm_patch_dtbo").submit {
if (it.isSuccess)
Notifications.dtboPatched(this)
if (Info.isNewReboot) {
val shell = Shell.newInstance()
shell.newJob().add("mm_patch_dtb").submit {
if (it.isSuccess)
Notifications.dtboPatched(this)
shell.close()
}
}
DONE = true

View File

@ -2,12 +2,10 @@ package com.topjohnwu.magisk.utils
import android.content.Context
import com.topjohnwu.magisk.Const
import com.topjohnwu.magisk.Info
import com.topjohnwu.magisk.R
import com.topjohnwu.magisk.extensions.rawResource
import com.topjohnwu.magisk.wrap
import com.topjohnwu.superuser.Shell
import com.topjohnwu.superuser.ShellUtils
import com.topjohnwu.superuser.io.SuFile
class RootInit : Shell.Initializer() {
@ -17,9 +15,6 @@ class RootInit : Shell.Initializer() {
}
fun init(context: Context, shell: Shell): Boolean {
// Invalidate env state if shell is recreated
Info.envRef.invalidate()
val job = shell.newJob()
if (shell.isRoot) {
job.add(context.rawResource(R.raw.util_functions))
@ -29,15 +24,12 @@ class RootInit : Shell.Initializer() {
job.add(context.rawResource(R.raw.nonroot_utils))
}
job.add("mount_partitions",
"get_flags",
"run_migrations",
"export BOOTMODE=true")
.exec()
Info.keepVerity = ShellUtils.fastCmd("echo \$KEEPVERITY").toBoolean()
Info.keepEnc = ShellUtils.fastCmd("echo \$KEEPFORCEENCRYPT").toBoolean()
Info.recovery = ShellUtils.fastCmd("echo \$RECOVERYMODE").toBoolean()
job.add(
"mount_partitions",
"get_flags",
"run_migrations",
"export BOOTMODE=true"
).exec()
return true
}

View File

@ -59,10 +59,12 @@
android:layout_gravity="bottom|center_horizontal"
android:layout_margin="@dimen/fab_padding"
android:onClick="@{() -> viewModel.fabPressed()}"
android:focusable="true"
android:clickable="true"
app:fabSize="normal"
app:layout_behavior="com.google.android.material.floatingactionbutton.FloatingActionButton$Behavior"
app:srcCompat="@drawable/ic_add" />
</androidx.coordinatorlayout.widget.CoordinatorLayout>
</layout>
</layout>

View File

@ -16,6 +16,7 @@
</data>
<TextView
android:focusable="true"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:fontFamily="monospace"

View File

@ -94,6 +94,8 @@
<androidx.appcompat.widget.AppCompatImageView
android:id="@+id/hide_app_checkbox"
android:focusable="true"
android:clickable="true"
style="@style/Widget.Icon"
isChecked="@{item.isHiddenState}"
android:onClick="@{() -> item.toggle()}"

View File

@ -38,6 +38,8 @@
<androidx.appcompat.widget.AppCompatImageView
android:id="@+id/hide_process_icon"
android:focusable="true"
android:clickable="true"
style="@style/Widget.Icon"
isChecked="@{item.isHidden}"
app:layout_constraintBottom_toBottomOf="parent"

View File

@ -106,6 +106,8 @@
<androidx.appcompat.widget.AppCompatImageView
android:id="@+id/checkbox"
android:focusable="true"
android:clickable="true"
style="@style/Widget.Icon"
isChecked="@{item.isChecked}"
android:layout_marginEnd="@dimen/margin_generic"
@ -127,6 +129,8 @@
<androidx.appcompat.widget.AppCompatImageView
android:id="@+id/delete"
android:focusable="true"
android:clickable="true"
style="@style/Widget.Icon"
srcCompat="@{item.isDeletable ? R.drawable.ic_undelete : R.drawable.ic_delete}"
android:onClick="@{() -> item.toggleDelete()}"

View File

@ -29,6 +29,7 @@
items="@{item.items}"
scrollPosition="@={viewModel.scrollPosition}"
scrollPositionSmooth="@{true}"
android:focusable="true"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:orientation="vertical"
@ -39,6 +40,8 @@
<com.google.android.material.floatingactionbutton.FloatingActionButton
hide="@{viewModel.scrollPosition == item.items.size - 1 || item.items.size == 0}"
android:focusable="true"
android:clickable="true"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="bottom|end"
@ -50,4 +53,4 @@
</FrameLayout>
</layout>
</layout>

View File

@ -105,6 +105,8 @@
<androidx.appcompat.widget.AppCompatImageView
android:id="@+id/download"
android:focusable="true"
android:clickable="true"
style="@style/Widget.Icon"
isEnabled="@{viewModel.isConnected}"
android:alpha="@{viewModel.isConnected ? 1f : .2f}"

View File

@ -1,5 +1,4 @@
# v7.4.0
- Hide Magisk Manager with stub APKs on Android 9.0+. Not all devices will be supported, please refer to Magisk v20.1 release notes.
- Allow customizing app name when hiding Magisk Manager
- Generate random keys to sign the hidden Magisk Manager to prevent signature detections
- Fix fingerprint UI infinite loop
# v7.5.0
- Support new communication method (ContentProvider)
- Fix several issues with hidden stub APK
- Support using BiometricPrompt (face unlock)

View File

@ -1,6 +1,7 @@
mount_partitions() {
[ "`getprop ro.build.ab_update`" = "true" ] && SLOT=`getprop ro.boot.slot_suffix`
[ "`getprop ro.build.system_root_image`" = "true" ] && SYSTEM_ROOT=true || SYSTEM_ROOT=false
# Check whether non rootfs root dir exists
grep ' / ' /proc/mounts | grep -qv 'rootfs' && SYSTEM_ROOT=true || SYSTEM_ROOT=false
}
get_flags() {

View File

@ -17,16 +17,6 @@ fix_env() {
cd /
}
run_migrations() {
# Move the stock backups
if [ -f /data/magisk/stock_boot* ]; then
mv /data/magisk/stock_boot* /data 2>/dev/null
fi
if [ -f /data/adb/magisk/stock_boot* ]; then
mv /data/adb/magisk/stock_boot* /data 2>/dev/null
fi
}
direct_install() {
rm -rf $MAGISKBIN/* 2>/dev/null
mkdir -p $MAGISKBIN 2>/dev/null
@ -43,30 +33,43 @@ direct_install() {
return 0
}
mm_patch_dtbo() {
$KEEPVERITY && return 1 || patch_dtbo_image
mm_patch_dtb() {
local result=1
local PATCHED=$TMPDIR/dt.patched
for name in dtb dtbo; do
local IMAGE=`find_block $name$SLOT`
if [ ! -z $IMAGE ]; then
if $MAGISKBIN/magiskboot dtb $IMAGE patch $PATCHED; then
result=0
if [ ! -z $SHA1 ]; then
# Backup stuffs
mkdir /data/magisk_backup_${SHA1} 2>/dev/null
cat $IMAGE | gzip -9 > /data/magisk_backup_${SHA1}/${name}.img.gz
fi
cat $PATCHED /dev/zero > $IMAGE
rm -f $PATCHED
fi
fi
done
return $result
}
restore_imgs() {
local SHA1=`grep_prop SHA1 /sbin/.magisk/config`
[ -z $SHA1 ] && local SHA1=`cat /.backup/.sha1`
[ -z $SHA1 ] && return 1
local STOCKBOOT=/data/stock_boot_${SHA1}.img.gz
local STOCKDTBO=/data/stock_dtbo.img.gz
[ -f $STOCKBOOT ] || return 1
local BACKUPDIR=/data/magisk_backup_$SHA1
[ -d $BACKUPDIR ] || return 1
get_flags
find_boot_image
find_dtbo_image
if [ -f $STOCKDTBO -a -b "$DTBOIMAGE" ]; then
flash_image $STOCKDTBO $DTBOIMAGE
fi
if [ -f $STOCKBOOT -a -b "$BOOTIMAGE" ]; then
flash_image $STOCKBOOT $BOOTIMAGE
return 0
fi
return 1
for name in dtb dtbo; do
[ -f $BACKUPDIR/${name}.img.gz ] || continue
local IMAGE=`find_block $name$SLOT`
[ -z $IMAGE ] && continue
flash_image $BACKUPDIR/${name}.img.gz $IMAGE
done
[ -f $BACKUPDIR/boot.img.gz ] || return 1
flash_image $BACKUPDIR/boot.img.gz $BOOTIMAGE
}
post_ota() {
@ -119,3 +122,5 @@ force_pm_install() {
[ "$VERIFY" -eq 1 ] && settings put global package_verifier_enable 1
return $res
}
SHA1=`grep_prop SHA1 /sbin/.magisk/config`

View File

@ -80,10 +80,10 @@
<string name="direct_install">Instal·lació directa (Recomanat)</string>
<string name="install_inactive_slot">Instal·la a la ranura inactiva (Després d\'una OTA)</string>
<string name="install_inactive_slot_msg">El teu dispositiu serà FORÇAT a arrancar en l\'actual ranura inactiva després del reinici!\nUtilitza aquesta opció NOMÉS quan l\'OTA s\'hagi fet.\nContinuar?</string>
<string name="select_method">Sel·lecciona un mètode</string>
<string name="select_method">Selecciona un mètode</string>
<string name="setup_title">Instal·lació addicional</string>
<string name="select_patch_file">Sel·lecciona i arranja un arxiu</string>
<string name="patch_file_msg">Sel·lecciona una imatge crua (*.img) o un ODIN tarfile (*.tar)</string>
<string name="select_patch_file">Selecciona i arranja un arxiu</string>
<string name="patch_file_msg">Selecciona una imatge crua (*.img) o un ODIN tarfile (*.tar)</string>
<string name="reboot_delay_toast">Reinici en 5 segons…</string>
<!--Toasts, Dialogs-->
@ -94,7 +94,6 @@
<string name="settings_reboot_toast">Reinicia per aplicar els canvis</string>
<string name="release_notes">Notes de llançament</string>
<string name="repo_cache_cleared">Memòria cau del repositori netejada</string>
<string name="dtbo_patched_title">S\'ha arranjat DTBO</string>
<string name="dtbo_patched_reboot">Magisk Manager ha arranjat dtbo.img. Si us plau, reinicia el telèfon.</string>
<string name="flashing">Arranjament…</string>
@ -110,18 +109,19 @@
<string name="restore_done">Restauració feta!</string>
<string name="restore_fail">La còpia de seguretat de Stock no existeix!</string>
<string name="proprietary_title">Baixar codi propietari</string>
<string name="proprietary_notice">Magisk Manager és codi lliure i no conté codi de l\'API de SafetyNet, ja que és codi propietari de Google.\n\nPot permetre que Magisk Manager baixi una extensió que conté el GoogleApiClient per poder fer la comprobació de SafetyNet?</string>
<string name="proprietary_notice">Magisk Manager és codi lliure i no conté codi de l\'API de SafetyNet, ja que és codi propietari de Google.\n\nVot permetre que Magisk Manager baixi una extensió que conté el GoogleApiClient per poder fer la comprovació de SafetyNet?</string>
<string name="setup_fail">Instal·lació fallida.</string>
<string name="env_fix_title">Es requereix instal·lació addicional</string>
<string name="env_fix_msg">El teu dispositiu necessita instal·lació addicional per Magisk per funcionar correctament. Es baixarà el ZIP d\'instal·lació de Magisk , vol procedir a l\'instalació ara?</string>
<string name="env_fix_msg">El teu dispositiu necessita instal·lació addicional per Magisk per funcionar correctament. Es baixarà el ZIP d\'instal·lació de Magisk , vol procedir a l\'instal·lació ara?</string>
<string name="setup_msg">S\'està executant la configuració de l\'entorn…</string>
<string name="authenticate">Autenticar</string>
<!--Settings Activity -->
<string name="settings_general_category">General</string>
<string name="settings_dark_theme_title">Tema fosc</string>
<string name="settings_dark_theme_summary">Habilitar el tema fosc</string>
<string name="settings_download_path_title">Directori de baixades</string>
<string name="settings_download_path_message">Els arxius es desaràn a %1$s</string>
<string name="settings_download_path_message">Els arxius es desaran a %1$s</string>
<string name="settings_clear_cache_title">Netejar memòria cau del repositori</string>
<string name="settings_clear_cache_summary">Neteja l\'informació en memòria cau per als repositoris en línia, força a l\'aplicació a actualitzar-se en línia.</string>
<string name="settings_hide_manager_title">Amagar Magisk Manager</string>
@ -139,7 +139,7 @@
<string name="settings_update_custom">Personalitzat</string>
<string name="settings_update_custom_msg">Inserta una URL personalitzada</string>
<string name="settings_core_only_title">Mode nucli de Magisk</string>
<string name="settings_core_only_summary">Habilitar només les funcions principals, no es carregaran tots els mòduls. MagiskSU y MagiskHide seguirán habilitats</string>
<string name="settings_core_only_summary">Habilitar només les funcions principals, no es carregaran tots els mòduls. MagiskSU y MagiskHide seguiran habilitats</string>
<string name="settings_magiskhide_summary">Amagar Magisk de varies deteccions</string>
<string name="settings_hosts_title">Systemless Hosts</string>
<string name="settings_hosts_summary">Suport per aplicacions tipus Adblock fora de la partició del sistema</string>
@ -148,7 +148,7 @@
<string name="settings_app_name">Escriu el nom desitjat per l\'App</string>
<string name="settings_app_name_hint">Nou nom</string>
<string name="settings_app_name_helper">Es refarà l\'App amb aquest nom</string>
<string name="settings_app_name_error">Format invàl·lid</string>
<string name="settings_app_name_error">Format invàlid</string>
<string name="settings_su_app_adb">Aplicacions y ADB</string>
<string name="settings_su_app">Només aplicacions</string>
<string name="settings_su_adb">Només ADB</string>
@ -166,6 +166,9 @@
<string name="request_timeout_summary">%1$d segons</string>
<string name="settings_su_reauth_title">Demanar després d\'una actualització</string>
<string name="settings_su_reauth_summary">Demanar permisos de superusuari novament si una aplicació és actualitzada o reinstal·lada</string>
<string name="settings_su_biometric_title">Activar autenticació biomètrica</string>
<string name="settings_su_biometric_summary">Utilitza l\'autenticació biomètrica per permetre solicituds de superusuari</string>
<string name="no_biometric">El dispositiu no suporta o no té establerta configuració biomètrica</string>
<string name="multiuser_mode">Mode Multiusuari</string>
<string name="settings_owner_only">Només Administrador del Dispositiu</string>
@ -182,7 +185,7 @@
<string name="global_summary">Totes les sessions d\'arrel utilitzen el suport Namespace Global</string>
<string name="requester_summary">Les sessions d\'arrel heretaran les peticiones Namespace</string>
<string name="isolate_summary">Totes les sessions d\'arrel tindran la seva pròpia Namespace</string>
<string name="settings_download_path_error">Error al crear la carpeta. El directori ha de ser accesible desde el directori arrel i no pot ser un arxiu.</string>
<string name="settings_download_path_error">Error al crear la carpeta. El directori ha de ser accessible des de el directori arrel i no pot ser un arxiu.</string>
<!--Superuser-->
<string name="su_request_title">Petició de superusuari</string>
@ -216,6 +219,6 @@
<string name="command">Ordre: %1$s</string>
<!-- MagiskHide -->
<string name="show_system_app">Mostra apps del sistema</string>
<string name="show_system_app">Mostra Apps del sistema</string>
</resources>

View File

@ -8,13 +8,14 @@
<string name="settings">Einstellungen</string>
<string name="install">Installieren</string>
<string name="unsupport_magisk_title">Nicht unterstützte Magisk Version</string>
<string name="unsupport_magisk_msg">Diese Version von Magisk Manager unterstützt keine Magisk-Versionen unter %1$s.\n\nDie App verhält sich so, als ob kein Magisk installiert ist. Bitte Magisk so schnell wie möglich aktualisieren.</string>
<!--Status Fragment-->
<string name="magisk_version_error">Magisk ist nicht installiert</string>
<string name="checking_for_updates">Suche nach Aktualisierungen…</string>
<string name="invalid_update_channel">Ungültiger Aktualisierungskanal</string>
<string name="safetyNet_check_text">SafetyNet-Status abfragen</string>
<string name="checking_safetyNet_status">Prüfe SafetyNet-Status…</string>
<string name="checking_safetyNet_status">SafetyNet-Status prüfen</string>
<string name="safetyNet_check_success">SafetyNet-Test erfolgreich</string>
<string name="safetyNet_api_error">SafetyNet API Fehler</string>
<string name="safetyNet_res_invalid">Die Antwort ist ungültig</string>
@ -23,11 +24,12 @@
<string name="advanced_settings_title">Erweiterte Optionen</string>
<string name="keep_force_encryption">\"force encryption\" beibehalten</string>
<string name="keep_dm_verity">AVB 2.0/dm-verity beibehalten</string>
<string name="recovery_mode">Wiederherstellungsmodus</string>
<string name="current_installed">Installiert: %1$s</string>
<string name="latest_version">Neueste: %1$s</string>
<string name="uninstall">Deinstallieren</string>
<string name="uninstall_magisk_title">Magisk deinstallieren</string>
<string name="uninstall_magisk_msg">Alle Module werden deaktiviert/entfernt. Root wird entfernt, und Ihre Daten werden möglicherweise verschlüsselt, wenn nicht bereits der Fall.</string>
<string name="uninstall_magisk_msg">Alle Module werden deaktiviert/entfernt. Root wird entfernt, und deine Daten werden möglicherweise verschlüsselt, wenn es nicht bereits der Fall ist.</string>
<string name="update">aktualisieren</string>
<string name="core_only_enabled">(Core only Modus aktiviert)</string>
@ -43,6 +45,7 @@
<string name="reboot_recovery">Neustart in das Recovery</string>
<string name="reboot_bootloader">Neustart in den Bootloader</string>
<string name="reboot_download">Neustart in den Download-Modus</string>
<string name="reboot_edl">Neustart in den EDL-Modus</string>
<!--Repo Fragment-->
<string name="update_available">Aktualisierung verfügbar</string>
@ -67,6 +70,8 @@
<string name="progress_channel">Fortschrittsbenachrichtigungen</string>
<string name="download_complete">Download abgeschlossen</string>
<string name="download_file_error">Fehler beim Herunterladen der Datei</string>
<string name="download_open_parent">Im übergeordneten Ordner anzeigen</string>
<string name="download_open_self">Datei anzeigen</string>
<string name="magisk_update_title">Neues Magisk Update verfügbar!</string>
<string name="manager_update_title">Aktualisierung für Magisk Manager verfügbar!</string>
@ -74,71 +79,77 @@
<string name="manager_download_install">Herunterladen und installieren</string>
<string name="download_zip_only">Nur Zip-Datei herunterladen</string>
<string name="direct_install">Direkt installieren (empfohlen)</string>
<string name="install_inactive_slot">Installiere in inaktiven Slot (Nach OTA)</string>
<string name="install_inactive_slot">In inaktiven Slot installieren (Nach OTA)</string>
<string name="install_inactive_slot_msg">Dein Gerät wird GEZWUNGEN in den aktuell inaktiven Slot zu starten, nachdem ein Neustart durchgeführt wurde!\nBenutze diese Option nur, nachdem das OTA beendet wurde.\nFortsetzen?</string>
<string name="select_method">Methode auswählen</string>
<string name="setup_title">Zusätzliche Einrichtung</string>
<string name="select_patch_file">Auswählen und Patchen einer Datei</string>
<string name="patch_file_msg">Wählen Sie ein Rohabbild (*.img) oder eine ODIN-Tar-Datei (*.tar) aus.</string>
<string name="patch_file_msg">Ein Rohabbild (*.img) oder eine ODIN-Tar-Datei (*.tar) auswählen.</string>
<string name="reboot_delay_toast">Neustart in 5 Sekunden…</string>
<!--Toasts, Dialogs-->
<string name="repo_install_title">Installiere %1$s</string>
<string name="repo_install_title">%1$s installieren</string>
<string name="repo_install_msg">Möchtest du %1$s installieren?</string>
<string name="download">Herunterladen</string>
<string name="reboot">Neustart</string>
<string name="settings_reboot_toast">Neustarten, um die Änderungen zu übernehmen</string>
<string name="release_notes">Versionshinweise</string>
<string name="repo_cache_cleared">Repo-Cache geleert</string>
<string name="dtbo_patched_title">DTBO wurde gepatched!</string>
<string name="dtbo_patched_reboot">Magisk Manager hat dtbo.img gepatched, bitte neustarten</string>
<string name="flashing">Flashing</string>
<string name="done">Erledigt!</string>
<string name="failure">Fehler</string>
<string name="hide_manager_title">Verberge Magisk Manager…</string>
<string name="hide_manager_fail_toast">Verbergen von Magisk Manager fehlgeschlagen…</string>
<string name="open_link_failed_toast">Es wurde keine Anwendung gefunden, um diesen Link zu öffnen...</string>
<string name="hide_manager_title">Magisk Manager verbergen</string>
<string name="hide_manager_fail_toast">Magisk Manager verbergen ist fehlgeschlagen…</string>
<string name="open_link_failed_toast">Es wurde keine App gefunden, um diesen Link zu öffnen…</string>
<string name="warning">Warnung</string>
<string name="complete_uninstall">Komplette Deinstallation</string>
<string name="restore_img">Images wiederherstellen</string>
<string name="restore_img_msg">Wiederherstellen...</string>
<string name="restore_img_msg">Wiederherstellen</string>
<string name="restore_done">Wiederherstellung erfolgreich!</string>
<string name="restore_fail">Kein Original Backup vorhanden!</string>
<string name="proprietary_title">Lade proprietären Code herunter</string>
<string name="proprietary_notice">Magisk Manager ist FOSS und enthält keinen proprietären SafetyNet API Code von Google. Magisk Manager erlauben eine Erweiterung (enthält GoogleApiClient) für SafetyNet-Checks herunterzuladen?</string>
<string name="setup_fail">Einrichtung fehlgeschlagen</string>
<string name="env_fix_title">Zusätzliche Einrichtung erforderlich</string>
<string name="env_fix_msg">Ihr Gerät benötigt zusätzliche Einrichtung, damit Magisk ordnungsgemäß funktioniert. Es wird eine Magisk-Installations-Zip wird heruntergeladen, fortfahren?</string>
<string name="env_fix_msg">Dein Gerät benötigt eine zusätzliche Einrichtung, damit Magisk ordnungsgemäß funktioniert. Es wird eine Magisk-Installations-Zip heruntergeladen. Fortfahren?</string>
<string name="setup_msg">Umgebungseinrichtung läuft…</string>
<string name="authenticate">Authentifizieren</string>
<!--Settings Activity -->
<string name="settings_general_category">Allgemein</string>
<string name="settings_dark_theme_title">Dunkles Theme</string>
<string name="settings_dark_theme_summary">Dunkles Theme aktivieren</string>
<string name="settings_download_path_title">Download-Verzeichnis</string>
<string name="settings_download_path_message">Dateien werden in %1$s gespeichert</string>
<string name="settings_clear_cache_title">Repo-Cache leeren</string>
<string name="settings_clear_cache_summary">Löscht die zwischengespeicherten Informationen des Online-Repos. Erzwingt eine Aktualisierung</string>
<string name="settings_hide_manager_title">Magisk Manager verbergen</string>
<string name="settings_hide_manager_summary">Magisk Manager mit zufälligem Paketnamen neu packen</string>
<string name="settings_restore_manager_title">Magisk Manager wiederherstellen</string>
<string name="settings_restore_manager_summary">Stellt Magisk Manager mit ursprünglichem Paketnamen wieder her</string>
<string name="settings_restore_manager_summary">Magisk Manager mit ursprünglichem Paketnamen wiederherstellen</string>
<string name="language">Sprache</string>
<string name="system_default">(Systemstandard)</string>
<string name="settings_update">Aktualisierungs-Einstellungen</string>
<string name="settings_check_update_title">Prüfe nach Aktualisierungen</string>
<string name="settings_check_update_summary">Prüfe regelmäßig im Hintergrund nach Aktualisierungen</string>
<string name="settings_check_update_title">Auf Aktualisierungen prüfen</string>
<string name="settings_check_update_summary">Regelmäßig im Hintergrund auf Aktualisierungen prüfen</string>
<string name="settings_update_channel_title">Aktualisierungs-Kanal</string>
<string name="settings_update_stable">Stabil</string>
<string name="settings_update_beta">Beta</string>
<string name="settings_update_custom">Benutzerdefiniert</string>
<string name="settings_update_custom_msg">Gib eine benutzerdefinierte URL ein</string>
<string name="settings_update_custom_msg">Eine benutzerdefinierte URL eingeben</string>
<string name="settings_core_only_title">Nur Kernfunktionen</string>
<string name="settings_core_only_summary">Aktiviert lediglich die Kernfunktionen, Module werden nicht geladen. MagiskSU und Magisk Hide bleiben weiterhin aktiv</string>
<string name="settings_magiskhide_summary">Versteckt Magisk vor diversen Entdeckungsmethoden</string>
<string name="settings_hosts_title">Systemlose Hosts-Datei</string>
<string name="settings_hosts_summary">Systemlose Unterstützung für Werbeblocker</string>
<string name="settings_hosts_toast">systemless Hosts-Modul hinzugefügt</string>
<string name="settings_hosts_toast">Systemless Hosts-Modul hinzugefügt</string>
<string name="settings_app_name">Den gewünschten App-Namen eingeben</string>
<string name="settings_app_name_hint">Neuer Name</string>
<string name="settings_app_name_helper">Die App wird unter diesem Namen neu gepackt</string>
<string name="settings_app_name_error">Ungültiges Format</string>
<string name="settings_su_app_adb">Apps und ADB</string>
<string name="settings_su_app">Nur Apps</string>
<string name="settings_su_adb">Nur ADB</string>
@ -156,6 +167,9 @@
<string name="request_timeout_summary">%1$d Sekunden</string>
<string name="settings_su_reauth_title">Nach Aktualisierung erneut authentifizieren</string>
<string name="settings_su_reauth_summary">Superuser-Zugriff nach App-Aktualisierung erneut abfragen</string>
<string name="settings_su_biometric_title">Biometrische Authentifizierung aktivieren</string>
<string name="settings_su_biometric_summary">Biometrische Authentifizierung verwenden, um Superuser-Anfragen zu ermöglichen</string>
<string name="no_biometric">Nicht unterstütztes Gerät oder keine biometrischen Einstellungen aktiviert</string>
<string name="multiuser_mode">Mehrbenutzermodus</string>
<string name="settings_owner_only">Nur der Gerätebesitzer</string>
@ -172,6 +186,7 @@
<string name="global_summary">Alle Root-Sitzungen benutzen den global angelegten Namespace</string>
<string name="requester_summary">Root-Sitzungen erben den Namespace des Abfragenden</string>
<string name="isolate_summary">Jede Root-Sitzung hat ihren isolierten Namespace</string>
<string name="settings_download_path_error">Fehler beim Erstellen eines Ordners. Er muss im Stammverzeichnis des Speichers zugänglich und darf keine Datei sein.</string>
<!--Superuser-->
<string name="su_request_title">Superuser-Anfrage</string>
@ -187,7 +202,7 @@
<string name="sixtymin">60 Min.</string>
<string name="su_allow_toast">Superuser-Rechte für %1$s gewährt</string>
<string name="su_deny_toast">Superuser-Rechte für %1$s verweigert</string>
<string name="no_apps_found">Keine Applikationen gefunden</string>
<string name="no_apps_found">Keine Apps gefunden</string>
<string name="su_snack_grant">Superuser-Rechte werden für %1$s gewährt</string>
<string name="su_snack_deny">Superuser-Rechte werden für %1$s verweigert</string>
<string name="su_snack_notif_on">Benachrichtigungen für %1$s sind aktiviert</string>
@ -205,6 +220,6 @@
<string name="command">Befehl: %1$s</string>
<!-- MagiskHide -->
<string name="show_system_app">System Applikationen anzeigen</string>
<string name="show_system_app">System-Apps anzeigen</string>
</resources>

View File

@ -3,216 +3,221 @@
<!--Welcome Activity-->
<string name="modules">Modules</string>
<string name="downloads">Téléchargements</string>
<string name="superuser">Superutilisateur</string>
<string name="superuser">Superutilisateur</string>
<string name="log">Journal</string>
<string name="settings">Paramètres</string>
<string name="install">Installer</string>
<string name="unsupport_magisk_title">Version de Magisk non prise en charge</string>
<string name="unsupport_magisk_title">Version de Magisk non prise en charge</string>
<string name="unsupport_magisk_msg">Cette version de Magisk Manager ne prend pas en charge les versions de Magisk inférieures à %1$s.\n\nLapplication se comportera comme si Magisk nétait pas installé, veuillez mettre à jour Magisk dès que possible.</string>
<!--Status Fragment-->
<string name="magisk_version_error">Magisk nest pas installé.</string>
<string name="checking_for_updates">Vérification des mises à jour…</string>
<string name="invalid_update_channel">Canal de mise à jour invalide</string>
<string name="safetyNet_check_text">Appuyez pour lancer le contrôle SafetyNet.</string>
<string name="magisk_version_error">Magisk nest pas installé</string>
<string name="checking_for_updates">Vérification des mises à jour…</string>
<string name="invalid_update_channel">Canal de mise à jour invalide</string>
<string name="safetyNet_check_text">Appuyez pour lancer le contrôle SafetyNet</string>
<string name="checking_safetyNet_status">Vérification de létat de SafetyNet…</string>
<string name="safetyNet_check_success">Contrôle SafetyNet passé avec succès</string>
<string name="safetyNet_check_success">Contrôle SafetyNet réussi</string>
<string name="safetyNet_api_error">Erreur de lAPI SafetyNet</string>
<string name="safetyNet_res_invalid">La réponse est incorrecte.</string>
<string name="magisk_up_to_date">Magisk est à jour</string>
<string name="manager_up_to_date">Magisk Manager est à jour</string>
<string name="safetyNet_res_invalid">La réponse est incorrecte</string>
<string name="magisk_up_to_date">Magisk est à jour</string>
<string name="manager_up_to_date">Magisk Manager est à jour</string>
<string name="advanced_settings_title">Paramètres avancés</string>
<string name="keep_force_encryption">Conserver le chiffrement forcé</string>
<string name="keep_dm_verity">Conserver AVB 2.0/dm-verity</string>
<string name="recovery_mode">Mode Récupération</string>
<string name="current_installed">Version actuellement installée : %1$s</string>
<string name="latest_version">Dernière version disponible : %1$s</string>
<string name="recovery_mode">Mode récupération</string>
<string name="current_installed">Version actuellement installée : %1$s</string>
<string name="latest_version">Dernière version disponible : %1$s</string>
<string name="uninstall">Désinstaller</string>
<string name="uninstall_magisk_title">Désinstaller Magisk</string>
<string name="uninstall_magisk_msg">Tous les modules seront désactivés ou supprimés. Les permissions de superutilisateur seront perdues et vos données seront potentiellement chiffrées si elles ne le sont pas déjà.</string>
<string name="update">Mise à jour</string>
<string name="core_only_enabled">(Mode « sans modules » activé)</string>
<string name="uninstall_magisk_msg">Tous les modules seront désactivés ou supprimés!\nLaccès superutiliateur sera perdu!\nVos données seront potentiellement chiffrées si elles ne le sont pas déjà.</string>
<string name="update">Mise à jour</string>
<string name="core_only_enabled">(mode « sans modules » activé)</string>
<!--Module Fragment-->
<string name="no_info_provided">(aucune information transmise)</string>
<string name="no_modules_found">Aucun module trouvé.</string>
<string name="update_file_created">Le module sera mis à jour au prochain redémarrage.</string>
<string name="remove_file_created">Le module sera supprimé au prochain redémarrage.</string>
<string name="remove_file_deleted">Le module ne sera pas supprimé au prochain redémarrage.</string>
<string name="disable_file_created">Le module sera désactivé au prochain redémarrage.</string>
<string name="disable_file_removed">Le module sera activé au prochain redémarrage.</string>
<string name="no_modules_found">Aucun module trouvé</string>
<string name="update_file_created">Le module sera mis à jour au prochain redémarrage!</string>
<string name="remove_file_created">Le module sera supprimé au prochain redémarrage!</string>
<string name="remove_file_deleted">Le module ne sera pas supprimé au prochain redémarrage!</string>
<string name="disable_file_created">Le module sera désactivé au prochain redémarrage!</string>
<string name="disable_file_removed">Le module sera activé au prochain redémarrage!</string>
<string name="author">Créé par %1$s</string>
<string name="reboot_recovery">Redémarrer en mode récupération</string>
<string name="reboot_bootloader">Redémarrer en mode amorçage</string>
<string name="reboot_download">Redémarrer en mode téléchargement</string>
<string name="reboot_edl">Redémarrer en mode EDL</string>
<string name="reboot_edl">Redémarrer en mode secours (EDL)</string>
<!--Repo Fragment-->
<string name="update_available">Mise à jour disponible</string>
<string name="installed">Installé</string>
<string name="not_installed">Non installé</string>
<string name="updated_on">Mis à jour le : %1$s</string>
<string name="sorting_order">Mode de tri :</string>
<string name="sort_by_name">par nom</string>
<string name="sort_by_update">par date décroissante</string>
<string name="updated_on">Mis à jour le : %1$s</string>
<string name="sorting_order">Mode de tri</string>
<string name="sort_by_name">alphabétique</string>
<string name="sort_by_update">antichronologique</string>
<!--Log Fragment-->
<string name="menuSaveLog">Enregistrer le journal</string>
<string name="menuReload">Actualiser</string>
<string name="menuClearLog">Effacer le journal maintenant</string>
<string name="logs_cleared">Journal effacé avec succès.</string>
<string name="logs_cleared">Le journal a bien été effacé.</string>
<!--About Activity-->
<string name="app_changelog">Journal</string>
<string name="app_changelog">Journal des modifications</string>
<!-- System Components, Notifications -->
<string name="update_channel">Mises à jour de Magisk</string>
<string name="progress_channel">Progression des notifications</string>
<string name="update_channel">Mises à jour de Magisk</string>
<string name="progress_channel">Notifications de progression</string>
<string name="download_complete">Téléchargement terminé</string>
<string name="download_file_error">Erreur de téléchargement du fichier</string>
<string name="download_open_parent">Afficher dans le dossier parent</string>
<string name="download_open_self">Afficher fichier</string>
<string name="magisk_update_title">Une mise à jour de Magisk est disponible!</string>
<string name="manager_update_title">Une mise à jour de Magisk Manager est disponible!</string>
<string name="download_file_error">Erreur lors du téléchargement du fichier</string>
<string name="download_open_parent">Afficher le dossier parent</string>
<string name="download_open_self">Afficher le fichier</string>
<string name="magisk_update_title">Une mise à jour de Magisk est disponible!</string>
<string name="manager_update_title">Une mise à jour de Magisk Manager est disponible!</string>
<!-- Installation -->
<string name="manager_download_install">Appuyez pour le télécharger et linstaller.</string>
<string name="download_zip_only">Télécharger le ZIP uniquement</string>
<string name="download_zip_only">Télécharger le ZIP sans linstaller</string>
<string name="direct_install">Installation directe (recommandée)</string>
<string name="install_inactive_slot">Installer dans lespace inactif (après mise à jour OTA)</string>
<string name="install_inactive_slot_msg">Votre appareil sera réamorcé à partir de lespace actuellement inactif après un redémarrage!\nNutilisez cette option quuniquement après que la mise à jour OTA ait été effectuée.\nVoulez-vous continuer?</string>
<string name="install_inactive_slot">Installer dans lespace inactif (après mise à jour OTA)</string>
<string name="install_inactive_slot_msg">Votre appareil sera obligatoirement réamorcé à partir de lespace (slot) actuellement inactif après son redémarrage!\nNutilisez cette option uniquement après que la mise à jour OTA a été effectuée.\nVoulezvous continuer?</string>
<string name="select_method">Sélectionnez la méthode</string>
<string name="setup_title">Installation additionnelle</string>
<string name="select_patch_file">Sélectionner et Patcher un Fichier</string>
<string name="patch_file_msg">Sélectionnez une image brute (*.img) ou un fichier tar ODIN (*.tar)</string>
<string name="reboot_delay_toast">Redémarrage dans 5 secondes…</string>
<string name="setup_title">Configuration supplémentaire</string>
<string name="select_patch_file">Sélectionnez le fichier cible du correctif</string>
<string name="patch_file_msg">Sélectionnez une image brute (*.img) ou une archive TAR ODIN (*.tar)</string>
<string name="reboot_delay_toast">Redémarrage dans 5 secondes…</string>
<!--Toasts, Dialogs-->
<string name="repo_install_title">Installer %1$s</string>
<string name="repo_install_msg">Voulezvous installer %1$s maintenant?</string>
<string name="repo_install_msg">Voulezvous installer %1$s maintenant?</string>
<string name="download">Télécharger</string>
<string name="reboot">Redémarrer</string>
<string name="settings_reboot_toast">Redémarrer afin dappliquer les réglages.</string>
<string name="settings_reboot_toast">Redémarrer afin dappliquer les paramètres</string>
<string name="release_notes">Notes de version</string>
<string name="repo_cache_cleared">Cache du dépôt effacé</string>
<string name="dtbo_patched_title">DTBO a été modifié!</string>
<string name="dtbo_patched_reboot">Magisk Manager vient de modifier le fichier dtbo.img, veuillez redémarrer.</string>
<string name="flashing">Installation</string>
<string name="done">Terminé!</string>
<string name="failure">Échec</string>
<string name="hide_manager_title">Masquer Magisk Manager…</string>
<string name="hide_manager_fail_toast">Le masquage de Magisk Manager a échoué.</string>
<string name="open_link_failed_toast">Aucune application permettant douvrir le lien na été trouvée.</string>
<string name="dtbo_patched_title">Larborescence matérielle (DTBO) a été modifiée!</string>
<string name="dtbo_patched_reboot">Magisk Manager a modifié le fichier dtbo.img. Veuillez redémarrer.</string>
<string name="flashing">Écriture dans la mémoire Flash…</string>
<string name="done">Terminé!</string>
<string name="failure">Erreur</string>
<string name="hide_manager_title">Masquer Magisk Manager…</string>
<string name="hide_manager_fail_toast">Le masquage de Magisk Manager a échoué.</string>
<string name="open_link_failed_toast">Aucune application permettant douvrir le lien na été trouvée</string>
<string name="warning">Avertissement</string>
<string name="complete_uninstall">Désinstallation terminée</string>
<string name="complete_uninstall">Désinstallation complète</string>
<string name="restore_img">Restauration des images</string>
<string name="restore_img_msg">Restauration…</string>
<string name="restore_done">Restauration terminée!</string>
<string name="restore_fail">La sauvegarde par défaut nexiste pas!</string>
<string name="restore_fail">La sauvegarde par défaut nexiste pas!</string>
<string name="proprietary_title">Téléchargement de code propriétaire</string>
<string name="proprietary_notice">Magisk Manager est un logiciel libre et ne contient pas le code propriétaire de lAPI SafetyNet de Google.Voulezvous autoriser Magisk Manager à télécharger une extension (contenant GoogleApiClient) pour les contrôles SafetyNet?</string>
<string name="proprietary_notice">Magisk Manager est un logiciel libre et ne contient pas le code propriétaire de lAPI SafetyNet de Google. Autorisezvous Magisk Manager à télécharger une extension (contenant GoogleApiClient) pour les contrôles SafetyNet?</string>
<string name="setup_fail">Échec de linstallation</string>
<string name="env_fix_title">Installation additionnelle requise</string>
<string name="env_fix_msg">Votre appareil a besoin dune configuration supplémentaire pour que Magisk fonctionne correctement. Pour cela, il est nécessaire de télécharger un fichier ZIP dinstallation de Magisk. Voulezvous le faire maintenant?</string>
<string name="setup_msg">Démarrer linstallation de lenvironnement…</string>
<string name="env_fix_title">Installation supplémentaire requise</string>
<string name="env_fix_msg">Votre appareil a besoin dune installation supplémentaire afin que Magisk fonctionne correctement. Un fichier ZIP dinstallation pour Magisk devra être téléchargé. Voulezvous faire cette installation maintenant?</string>
<string name="setup_msg">Installation de lenvironnement en cours…</string>
<string name="authenticate">Authentification</string>
<!--Settings Activity -->
<string name="settings_general_category">Général</string>
<string name="settings_dark_theme_title">Thème sombre</string>
<string name="settings_dark_theme_summary">Activer le thème sombre.</string>
<string name="settings_download_path_title">Emplacement téléchargement</string>
<string name="settings_dark_theme_summary">Activer le thème sombre</string>
<string name="settings_download_path_title">Répertoire de téléchargement</string>
<string name="settings_download_path_message">Les fichiers seront sauvegardés dans %1$s</string>
<string name="settings_clear_cache_title">Effacer le cache du dépôt</string>
<string name="settings_clear_cache_summary">Effacer les informations de cache pour les dépôts en ligne. Cela force lapplication à récupérer ses informations en ligne.</string>
<string name="settings_hide_manager_title">Masquer Magisk Manager</string>
<string name="settings_hide_manager_summary">Réempaqueter Magisk Manager avec un nom de paquet aléatoire.</string>
<string name="settings_restore_manager_title">Restaurer Magisk Manager</string>
<string name="settings_restore_manager_summary">Restaurer Magisk Manager avec son nom de paquet dorigine</string>
<string name="settings_clear_cache_summary">Effacer les informations en cache concerant les dépôts en ligne. Ceci force lapplication à télécharger des informations à jour.</string>
<string name="settings_hide_manager_title">Masquer Magisk Manager</string>
<string name="settings_hide_manager_summary">Réempaqueter Magisk Manager avec des noms de paquet et dapplication aléatoires.</string>
<string name="settings_restore_manager_title">Restaurer Magisk Manager</string>
<string name="settings_restore_manager_summary">Restaurer Magisk Manager avec ses noms de paquet et dapplication dorigine</string>
<string name="language">Langue</string>
<string name="system_default">(système par défaut)</string>
<string name="settings_update">Mise à jour des réglages</string>
<string name="settings_check_update_title">Vérification des mises à jour</string>
<string name="settings_check_update_summary">Vérifier périodiquement en tâche de fond lexistence dune mise à jour.</string>
<string name="settings_update_channel_title">Canal de mise à jour</string>
<string name="system_default">(langue par défaut du système)</string>
<string name="settings_update">Paramètres de mise à jour</string>
<string name="settings_check_update_title">Vérifier les mises à jour</string>
<string name="settings_check_update_summary">Vérifier périodiquement en tâche de fond lexistence dune mise à jour</string>
<string name="settings_update_channel_title">Canal de mise à jour</string>
<string name="settings_update_stable">Stable</string>
<string name="settings_update_beta">Bêta</string>
<string name="settings_update_custom">Personnalisé</string>
<string name="settings_update_custom_msg">Insérez une URL personnalisée</string>
<string name="settings_core_only_title">Mode Magisk Core uniquement</string>
<string name="settings_core_only_summary">Active uniquement les fonctionnalités de base. MagiskSU et MagiskHide resteront activés, mais aucun module ne sera chargé. </string>
<string name="settings_update_custom_msg">Saisissez une URL personnalisée</string>
<string name="settings_core_only_title">Mode « sans modules » uniquement</string>
<string name="settings_core_only_summary">Activer uniquement les fonctionnalités de base. MagiskSU et MagiskHide resteront activés, mais aucun module ne sera chargé.</string>
<string name="settings_magiskhide_summary">Rendre Magisk invisible à diverses formes de détection.</string>
<string name="settings_hosts_title">Fichier hosts sans système</string>
<string name="settings_hosts_summary">Prise en charge du fichier hosts sans système pour les applications de type AdBlock.</string>
<string name="settings_hosts_toast">Ajout dun module hosts sans système</string>
<string name="settings_app_name">Taper le nom de l\'application désirée</string>
<string name="settings_hosts_title">Fichier hosts hors partition système</string>
<string name="settings_hosts_summary">Utilisation dun fichier hosts hors de la partition système pour les applications de blocage de publicité.</string>
<string name="settings_hosts_toast">Ajout dun module pour fichier hosts hors système</string>
<string name="settings_app_name">Saisissez le nom de lapplication désiré</string>
<string name="settings_app_name_hint">Nouveau nom</string>
<string name="settings_app_name_helper">L\'application sera réempacter sous ce nom</string>
<string name="settings_app_name_error">Format invalide</string>
<string name="settings_app_name_helper">Lapplication sera réempaquetée sous ce nom</string>
<string name="settings_app_name_error">Format incorrect</string>
<string name="settings_su_app_adb">Applications et ADB</string>
<string name="settings_su_app">Applications uniquement</string>
<string name="settings_su_adb">ADB uniquement</string>
<string name="settings_su_disable">Désactivé</string>
<string name="settings_su_request_10">10 secondes</string>
<string name="settings_su_request_15">15 secondes</string>
<string name="settings_su_request_20">20 secondes</string>
<string name="settings_su_request_30">30 secondes</string>
<string name="settings_su_request_45">45 secondes</string>
<string name="settings_su_request_60">60 secondes</string>
<string name="superuser_access">Accès superutilisateur</string>
<string name="settings_su_request_10">10 secondes</string>
<string name="settings_su_request_15">15 secondes</string>
<string name="settings_su_request_20">20 secondes</string>
<string name="settings_su_request_30">30 secondes</string>
<string name="settings_su_request_45">45 secondes</string>
<string name="settings_su_request_60">60 secondes</string>
<string name="superuser_access">Accès superutilisateur</string>
<string name="auto_response">Réponse automatique</string>
<string name="request_timeout">Délai dexpiration de la demande</string>
<string name="superuser_notification">Notification superutilisateur</string>
<string name="request_timeout_summary">%1$d secondes</string>
<string name="settings_su_reauth_title">Authentifier à nouveau après la mise à niveau</string>
<string name="settings_su_reauth_summary">Authentifier à nouveau les autorisations superutilisateur après une mise à jour de lapplication</string>
<string name="superuser_notification">Notification superutilisateur</string>
<string name="request_timeout_summary">%1$d secondes</string>
<string name="settings_su_reauth_title">Sauthentifier à nouveau après la mise à niveau</string>
<string name="settings_su_reauth_summary">Redemander une authentification pour autoriser laccès en superutilisateur après une mise à jour de lapplication</string>
<string name="settings_su_biometric_title">Activer lauthentification biométrique</string>
<string name="settings_su_biometric_summary">Utiliser lauthentification biométrique pour autoriser les accès en superutilisateur</string>
<string name="no_biometric">Lappareil nest pas pris en charge ou alors aucun paramètre biométrique nest activé</string>
<string name="multiuser_mode">Mode multiutilisateur</string>
<string name="multiuser_mode">Mode multiutilisateur</string>
<string name="settings_owner_only">Propriétaire de lappareil uniquement</string>
<string name="settings_owner_manage">Géré par le propriétaire de lappareil</string>
<string name="settings_user_independent">Indépendant de lutilisateur</string>
<string name="owner_only_summary">Seul le propriétaire a un accès superutilisateur.</string>
<string name="owner_manage_summary">Seul le propriétaire peut gérer laccès superutilisateur et recevoir les demandes de requêtes.</string>
<string name="user_indepenent_summary">Chaque utilisateur a ses propres règles superutilisateur séparées.</string>
<string name="owner_only_summary">Seul le propriétaire possède un accès superutilisateur.</string>
<string name="owner_manage_summary">Seul le propriétaire peut gérer laccès superutilisateur et recevoir les demandes daccès.</string>
<string name="user_indepenent_summary">Chaque utilisateur a ses propres règles daccès superutilisateur séparées.</string>
<string name="mount_namespace_mode">Mode despace de noms du montage</string>
<string name="mount_namespace_mode">Mode despace de noms du montage</string>
<string name="settings_ns_global">Espace de noms global</string>
<string name="settings_ns_requester">Hériter de lespace de noms</string>
<string name="settings_ns_requester">Hériter de lespace de noms</string>
<string name="settings_ns_isolate">Espace de noms isolé</string>
<string name="global_summary">Toutes les sessions superutilisateur utilisent lespace de noms global du montage.</string>
<string name="requester_summary">Les sessions superutilisateur hériteront de lespace de noms de leur demandeur.</string>
<string name="isolate_summary">Chaque session superutilisateur aura son propre espace de noms isolé.</string>
<string name="settings_download_path_error">Erreur lors de la création du dossier. Il doit être accessible depuis le répertoire racine du stockage et ne doit pas être un fichier.</string>
<string name="global_summary">Toutes les sessions superutilisateur utilisent lespace de noms global du montage.</string>
<string name="requester_summary">Les sessions superutilisateur hériteront de lespace de noms de leur demandeur.</string>
<string name="isolate_summary">Chaque session superutilisateur aura son propre espace de noms isolé.</string>
<string name="settings_download_path_error">Erreur lors de la création du dossier. Ce dernier doit être accessible depuis le répertoire racine du stockage et ne doit pas être un fichier.</string>
<!--Superuser-->
<string name="su_request_title">Requête superutilisateur</string>
<string name="su_request_title">Demande daccès superutilisateur</string>
<string name="deny">Refuser</string>
<string name="prompt">Demander</string>
<string name="grant">Accepter</string>
<string name="su_warning">Autoriser laccès complet à votre appareil.\nRefusez si vous nêtes pas sûr!</string>
<string name="su_warning">Autoriser laccès complet à votre appareil.\nRefusez si vous nêtes pas sûr(e)!</string>
<string name="forever">Toujours</string>
<string name="once">Une fois</string>
<string name="once">Une fois</string>
<string name="tenmin">10min</string>
<string name="twentymin">20min</string>
<string name="thirtymin">30min</string>
<string name="sixtymin">60min</string>
<string name="su_allow_toast">%1$s a obtenu les droits superutilisateur</string>
<string name="su_deny_toast">%1$s na pas obtenu les droits superutilisateur</string>
<string name="su_allow_toast">%1$s a obtenu les droits de superutilisateur</string>
<string name="su_deny_toast">%1$s sest vu refuser les droits de superutilisateur</string>
<string name="no_apps_found">Aucune application trouvée</string>
<string name="su_snack_grant">Les droits superutilisateur de %1$s sont accordés</string>
<string name="su_snack_deny">Les droits superutilisateur de %1$s sont refusés</string>
<string name="su_snack_notif_on">Les notifications pour %1$s sont activées</string>
<string name="su_snack_notif_off">Les notifications pour %1$s sont désactivées</string>
<string name="su_snack_log_on">La journalisation pour %1$s est activée</string>
<string name="su_snack_log_off">La journalisation pour %1$s est désactivée</string>
<string name="su_revoke_title">Annuler ?</string>
<string name="su_revoke_msg">Confirmezvous lannulation des droits pour %1$s?</string>
<string name="su_snack_grant">Les droits de superutilisateur sont accordés à %1$s</string>
<string name="su_snack_deny">Les droits de superutilisateur sont refusés à %1$s</string>
<string name="su_snack_notif_on">Les notifications sont activées pour %1$s</string>
<string name="su_snack_notif_off">Les notifications sont désactivées pour %1$s</string>
<string name="su_snack_log_on">La journalisation est activée pour %1$s</string>
<string name="su_snack_log_off">La journalisation est désactivée pour %1$s</string>
<string name="su_revoke_title">Révoquer?</string>
<string name="su_revoke_msg">Confirmezvous la révocation des droits accordés à %1$s?</string>
<string name="toast">Toast</string>
<string name="none">Aucun</string>
<!--Superuser logs-->
<string name="pid">PID : %1$d</string>
<string name="target_uid">UID cible : %1$d</string>
<string name="command">Commande : %1$s</string>
<string name="pid">PID : %1$d</string>
<string name="target_uid">UID cible : %1$d</string>
<string name="command">Commande : %1$s</string>
<!-- MagiskHide -->
<string name="show_system_app">Afficher les applications système</string>

View File

@ -8,6 +8,7 @@
<string name="settings">Ayarlar</string>
<string name="install">Yükle</string>
<string name="unsupport_magisk_title">Desteklenmeyen Magisk Sürümü</string>
<string name="unsupport_magisk_msg">Magisk Manager\'ın bu sürümü, %1$s daha düşük Magisk versiyonlarını desteklememektedir.\n\nUygulama hiçbir Magisk kurulu değil gibi davranacak, lütfen en kısa zamanda Magisk\'i yükseltin.</string>
<!--Status Fragment-->
<string name="magisk_version_error">Magisk yüklü değil</string>
@ -94,8 +95,7 @@
<string name="settings_reboot_toast">Ayarları uygulamak için yeniden başlatın</string>
<string name="release_notes">Sürüm notları</string>
<string name="repo_cache_cleared">Repo önbelleği temizlendi</string>
<string name="dtbo_patched_title">DTBO yamalandı!</string>
<string name="dtbo_patched_title">DTBO yamalandı!</string>
<string name="dtbo_patched_reboot">Magisk Manager dtbo.img\'yi yamaladı, lütfen yeniden başlatın</string>
<string name="flashing">Yükleniyor</string>
<string name="done">Tamamlandı!</string>
@ -115,6 +115,7 @@
<string name="env_fix_title">Ek Kurulum Gerekli</string>
<string name="env_fix_msg">Cihazınızın Magisk\'in düzgün çalışması için ek kuruluma ihtiyacı var. Bu Magisk kurulum zip dosyasını indirecektir, şimdi devam etmek istiyor musunuz?</string>
<string name="setup_msg">Ortam kurulumu çalışıyor…</string>
<string name="authenticate">Kimlik doğrulaması</string>
<!--Settings Activity -->
<string name="settings_general_category">Genel</string>
@ -166,6 +167,9 @@
<string name="request_timeout_summary">%1$d saniye</string>
<string name="settings_su_reauth_title">Yükseltmeden sonra yeniden kimlik doğrula</string>
<string name="settings_su_reauth_summary">Uygulama yükseltmeleri sonrasında yetkili kullanıcı izinlerini yeniden doğrula</string>
<string name="settings_su_biometric_title">Biyometrik Kimlik Doğrulamayı Etkinleştir</string>
<string name="settings_su_biometric_summary">Superuser isteklerine izin vermek için biyometrik kimlik doğrulamayı kullanın</string>
<string name="no_biometric">Desteklenmeyen cihaz veya biyometrik ayar etkinleştirilmemiş</string>
<string name="multiuser_mode">Çok Kullanıcılı Mod</string>
<string name="settings_owner_only">Yalnızca Cihaz Sahibi</string>

View File

@ -167,7 +167,7 @@
<string name="request_timeout_summary">%1$d 秒</string>
<string name="settings_su_reauth_title">更新後重新驗證</string>
<string name="settings_su_reauth_summary">應用程式更新後,重新驗證超級使用者的請求</string>
<string name="settings_su_biometric_title">啟用生物特徵驗證</string>
<string name="multiuser_mode">多重使用者模式</string>
<string name="settings_owner_only">僅限裝置擁有者</string>
<string name="settings_owner_manage">由裝置擁有者管理</string>

View File

@ -7,7 +7,7 @@ if (configPath.exists())
configPath.withInputStream { is -> props.load(is) }
buildscript {
ext.vKotlin = '1.3.60'
ext.vKotlin = '1.3.61'
repositories {
google()
@ -16,7 +16,7 @@ buildscript {
maven { url 'https://kotlin.bintray.com/kotlinx' }
}
dependencies {
classpath 'com.android.tools.build:gradle:3.5.2'
classpath 'com.android.tools.build:gradle:3.5.3'
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:${vKotlin}"

View File

@ -1,5 +1,5 @@
# Magisk Documentation
(Updated on 2019.9.19)
(Updated on 2020.1.2)
- [Installation](install.md)
- [Tutorials](tutorials.md)
@ -8,7 +8,7 @@
The following sections are for developers
- [Magisk Details](details.md)
- [Magisk Tools](tools.md)
- [Developer Guides](guides.md)
- [Magisk Tools](tools.md)
- [Internal Details](details.md)
- [Deployment](deploy.md)

View File

@ -1,4 +1,4 @@
# Magisk Details
# Internal Details
## File Structure
### Paths in "sbin tmpfs overlay"
One of Magisk's breakthrough designs is sbin tmpfs overlay. It is required to support system-as-root devices, and also is the key to hiding Magisk from detection. All Magisk binaries, applets, mirrors, and other trivial stuffs are all located in the `tmpfs` mounted on `/sbin`. MagiskHide can just simply unmount `/sbin` and the bind mounts to hide all modifications easily.

View File

@ -1,9 +1,7 @@
# Developer Guides
Please read through [Magisk Details](details.md) before reading the following guides. If you are developing a module, pay extra attention to the [Magic Mount](details.md#magic-mount) section.
## Magisk Modules
A Magisk module is a folder placed in `/data/adb/modules` with a structure below:
A Magisk module is a folder placed in `/data/adb/modules` with the structure below:
```
/data/adb/modules
@ -14,40 +12,38 @@ A Magisk module is a folder placed in `/data/adb/modules` with a structure below
│ │
│ │ *** Module Identity ***
│ │
│   ├── module.prop <--- This files stores the metadata of the module
│   ├── module.prop <--- This file stores the metadata of the module
│ │
│ │ *** Status files ***
│ │ *** Main Contents ***
│ │
│   ├── skip_mount <--- If exists, Magisk will NOT mount your files
│   ├── system <--- This folder will be mounted if skip_mount does not exists
│   │   ├── ...
│   │   ├── ...
│   │   └── ...
│ │
│ │ *** Status Flags ***
│ │
│   ├── skip_mount <--- If exists, Magisk will NOT mount your system folder
│   ├── disable <--- If exists, the module will be disabled
│   ├── remove <--- If exists, the module will be removed next reboot
│ │
│ │ *** Scripts ***
│ │ *** Optional Files ***
│ │
│   ├── post-fs-data.sh <--- This script will be executed in post-fs-data
│   ├── service.sh <--- This script will be executed in late_start service
| ├── uninstall.sh <--- This script will be executed when Magisk removes your module
│ │
│ │ *** Resetprop Files ***
│ │
│   ├── system.prop <--- This file will be loaded as system properties by resetprop
│ │
│ │ *** Module contents ***
│ │
│   ├── system <--- This folder will be Magic Mounted if skip_mount does not exists
│   │   ├── .
│   │   ├── .
│   │   └── .
│   ├── system.prop <--- Properties in this file will be loaded as system properties by resetprop
│   ├── sepolicy.rule <--- Additional custom sepolicy rules to be patched
│   │
│ │ *** Auto Generated by Magisk ***
│ │ *** Auto Generated, DO NOT MANUALLY CREATE OR MODIFY ***
│   │
│   ├── vendor <--- A symlink to $MODID/system/vendor
│   ├── product <--- A symlink to $MODID/system/product
│ │
│ │ *** Others ***
│ │ *** Any additional files / folders are allowed ***
│ │
│   ├── . <--- Any additional files / folders are allowed
│   └── .
│   ├── ...
│   └── ...
|
├── another_module
│   ├── .
@ -55,9 +51,11 @@ A Magisk module is a folder placed in `/data/adb/modules` with a structure below
├── .
├── .
```
As long as a folder follows the structure above, it will be recognized as a module.
Here is the **strict** format of `module.prop`:
#### module.prop
This is the **strict** format of `module.prop`
```
id=<string>
@ -71,27 +69,134 @@ description=<string>
ex: ✓ `a_module`, ✓ `a.module`, ✓ `module-101`, ✗ `a module`, ✗ `1_module`, ✗ `-a-module`<br>
This is the **unique identifier** of your module. You should not change it once published.
- `versionCode` has to be an **integer**. This is used to compare versions
- Others that isn't mentioned above can be any **single line** string.
- Others that weren't mentioned above can be any **single line** string.
#### Shell scripts (`*.sh`)
Please read the [Boot Scripts](#boot-scripts) section to understand the difference between `post-fs-data.sh` and `service.sh`. For most module developers, `service.sh` should be good enough if you just need to run a boot script.
In all scripts of your module, please use `MODDIR=${0%/*}` to get your module's base directory path; do **NOT** hardcode your module path in scripts.
#### system.prop
This file follows the same format as `build.prop`. Each line comprises of `[key]=[value]`.
#### sepolicy.rule
If your module requires some additional sepolicy patches, please add those rules into this file. The module installer script and Magisk's daemon will make sure this file is copied to somewhere `magiskinit` can read pre-init to ensure these rules are injected properly.
Each line in this file will be treated as a policy statement. For more details how a policy statement is formated, please check [magiskpolicy](tools.md#magiskpolicy)'s documentation.
#### The `system` folder
All files you want Magisk to replace/inject for you should be placed in this folder. Please read through the [Magic Mount](details.md#magic-mount) section to understand how Magisk mount your files.
## Magisk Module Installer
The official Magisk Module installer is hosted **[here](https://github.com/topjohnwu/magisk-module-installer)**.
That repo is a starting point for creating a Magisk module installer zip. Here are some details:
A Magisk Module Installer is a Magisk Module packaged in a zip file that can be flashed in Magisk Manager or custom recoveries such as TWRP. An installer have the same file structure as a Magisk module (please check the previous section for more info). The simplest Magisk Module Installer is just a Magisk Module packed in a zip file, with addition to the following files:
- You will found out that `META-INF/com/google/android/update-binary` is a dummy file. If you are creating a module locally for personal usage or testing, download the latest installer [`module_installer.sh`](https://github.com/topjohnwu/Magisk/blob/master/scripts/module_installer.sh) and replace `update-binary` with the script
- The next thing to do is to modify `module.prop` to provide information about your module
- Finally, modify `install.sh` to fit your need. The script is heavily documented so you should know what to do.
- (Optional) If you want to run custom uninstallation logic when your module is removed, add a new file `uninstall.sh` to the root of the installer
- **Windows users aware!!** The line endings of all text files should be in the **Unix format**. Please use advanced text editors like Sublime, Atom, Notepad++ etc. to edit **ALL** text files, **NEVER** use Windows Notepad.
- `update-binary`: Download the latest [module_installer.sh](https://github.com/topjohnwu/Magisk/blob/master/scripts/module_installer.sh) and rename/copy that script as `update-binary`
- `updater-script`: This file should only contain the string `#MAGISK`
By default, `update-binary` will check/setup the environment, load utility functions, extract the module installer zip to where your module will be installed, and finally do some trivial tasks and cleanups, which should cover most simple modules' needs.
```
module.zip
├── META-INF
│   └── com
│      └── google
│         └── android
│            ├── update-binary <--- The module_installer.sh you downloaded
│            └── updater-script <--- Should only contain the string "#MAGISK"
├── customize.sh <--- (Optional, more details later)
│ This script will be sourced by update-binary
├── ...
├── ... /* The rest of module's files */
|
```
#### Customization
If you need to customize the module installation process, optionally you can create a new script in the installer called `customize.sh`. This script will be *sourced* (not executed!) by `update-binary` after all files are extracted and default permissions and secontext are applied. This is very useful if your module includes different files based on ABI, or you need to set special permissions/secontext for some of your files (e.g. files in `/system/bin`).
If you need even more customization and prefer to do everything on your own, declare `SKIPUNZIP=1` in `customize.sh` to skip the extraction and applying default permissions/secontext steps. Be aware that by doing so, your `customize.sh` will then be responsible to install everything by itself.
#### `customize.sh` Environment
Magisk's internal busybox's path `$BBPATH` is added in the front of `PATH`. The following variables and shell functions are available for convenience:
##### Variables
- `MAGISK_VER` (string): the version string of current installed Magisk (e.g. `v20.0`)
- `MAGISK_VER_CODE` (int): the version code of current installed Magisk (e.g. `20000`)
- `BOOTMODE` (bool): `true` if the module is being installed in Magisk Manager
- `MODPATH` (path): the path where your module files should be installed
- `TMPDIR` (path): a place where you can temporarily store files
- `ZIPFILE` (path): your module's installation zip
- `ARCH` (string): the CPU architecture of the device. Value is either `arm`, `arm64`, `x86`, or `x64`
- `IS64BIT` (bool): `true` if `$ARCH` is either `arm64` or `x64`
- `API` (int): the API level (Android version) of the device (e.g. `21` for Android 5.0)
##### Functions
```
ui_print <msg>
print <msg> to console
Avoid using 'echo' as it will not display in custom recovery's console
abort <msg>
print error message <msg> to console and terminate installation
Avoid using 'exit' as it will skip the termination cleanup steps
set_perm <target> <owner> <group> <permission> [context]
if [context] is not set, the default is "u:object_r:system_file:s0"
this function is a shorthand for the following commands:
chown owner.group target
chmod permission target
chcon context target
set_perm_recursive <directory> <owner> <group> <dirpermission> <filepermission> [context]
if [context] is not set, the default is "u:object_r:system_file:s0"
for all files in <directory>, it will call:
set_perm file owner group filepermission context
for all directories in <directory> (including itself), it will call:
set_perm dir owner group dirpermission context
```
##### Easy Replace
You can declare a list of folders you want to directly replace in the variable name `REPLACE`. The module installer script will pickup this variable and create `.replace` files for you. An example declaration:
```
REPLACE="
/system/app/YouTube
/system/app/Bloatware
"
```
The list above will result in the following files being created: `$MODPATH/system/app/YouTube/.replace` and `$MODPATH/system/app/Bloatware/.replace`
#### Notes
- When your module is downloaded with Magisk Manager, `update-binary` will be **forcefully** replaced with the latest [`module_installer.sh`](https://github.com/topjohnwu/Magisk/blob/master/scripts/module_installer.sh) to ensure all installer uses up-to-date scripts. **DO NOT** try to add any custom logic in `update-binary` as it is pointless.
- Due to historical reasons, **DO NOT** add a file named `install.sh` in your module installer. That specific file was previously used and will be treated differently.
- **DO NOT** call `exit` at the end of `customize.sh`. The module installer would want to do finalizations.
## Submit Modules
You can submit a module to **Magisk-Module-Repo** so users can download your module directly in Magisk Manager. Before submitting, follow the instructions in the previous section to create a valid installer for your module. You can then create a request for submission via this link: [submission](https://github.com/Magisk-Modules-Repo/submission).
- When your module is downloaded with Magisk Manager, `META-INF/com/google/android/update-binary` will be **forcefully** replaced with the latest [`module_installer.sh`](https://github.com/topjohnwu/Magisk/blob/master/scripts/module_installer.sh) to make sure all installation uses the latest scripts.
- Since `update-binary` will be replaced, this means that all modules in the repo are expected to follow how the installation framework works: the installation framework will load your `install.sh` script and run the corresponding callbacks.
- This also means that you should NOT add custom logic in `update-binary` as they will simply be ignored.
You can submit a module to **Magisk-Module-Repo** so users can download your module directly in Magisk Manager.
- Follow the instructions in the previous section to create a valid installer for your module.
- Create `README.md` (filename should be exactly the same) containing all info for your module. If you are not familiar with the Markdown syntax, the [Markdown Cheat Sheet](https://github.com/adam-p/markdown-here/wiki/Markdown-Cheatsheet) will be handy.
- Create a repository with your personal GitHub account, and upload your module installer to the repo
- Create a request for submission via this link: [submission](https://github.com/Magisk-Modules-Repo/submission)
## Module Tricks
#### Remove Files
How to remove a file systemless-ly? To actually make the file *disappear* is complicated (possible, not worth the effort). Replacing it with a dummy file should be good enough! Create an empty file with the same name and place it in the same path within a module, it shall replace your target file with a dummy file.
#### Remove Folders
Same as mentioned above, actually making the folder *disappear* is not worth the effort. Replacing it with an empty folder should be good enough! A handy trick for module developers is to add the folder you want to remove into the `REPLACE` list within `customize.sh`. If your module doesn't provide a corresponding folder, it will create an empty folder, and automatically add `.replace` into the empty folder so the dummy folder will properly replace the one in `/system`.
## Boot Scripts
In Magisk, you can run boot scripts in 2 different modes: **post-fs-data** and **late_start service** mode.
- post-fs-data mode
@ -118,9 +223,10 @@ In Magisk, there are also 2 kinds of scripts: **general scripts** and **module s
- Will NOT be executed when **Core-Only** mode is enabled (all modules are disabled)
- Modules require boot scripts should **ONLY** use module scripts instead of general scripts
Magisk's internal busybox's path `$BBPATH` is always prepended in `PATH`. This means all commands you call in scripts are always using busybox unless the applet is not included. This makes sure that your script always run in a predictable environment and always have the full suite of commands regardless of which Android version it is running on.
Magisk's internal busybox's path `$BBPATH` is added in the front of `PATH`. This means all commands you call in scripts are always using busybox unless the applet is not included. This makes sure that your script always run in a predictable environment and always have the full suite of commands regardless of which Android version it is running on.
## Root Directory Overlay System
Since `/` is read-only in system-as-root devices, Magisk provides an overlay system, allowing developers to patch files / add new rc scripts. Additional files shall be placed in the `overlay.d` folder in the ramdisk, and they will have the following restrictions:
- All `*.rc` files in `overlay.d` will be read and concatenated *AFTER* `init.rc`
@ -130,9 +236,3 @@ e.g. you can replace `/res/random.png` by adding the file `overlay.d/res/random.
e.g. `overlay.d/new_file` will be ignored if `/new_file` does not exist
- Additional files in `overlay.d/sbin` is allowed as they will be copied into Magisk's sbin overlay.<br>
e.g. `overlay.d/sbin/libfoo.ko` will be copied to `/sbin/libfoo.ko`.
## Remove Files
How to remove a file systemless-ly? To actually make the file *disappear* is complicated (possible, not worth the effort). Replacing it with a dummy file should be good enough! Create an empty file with the same name and place it in the same path within a module, it shall replace your target file with a dummy file.
## Remove Folders
Same as mentioned above, actually making the folder *disappear* is not worth the effort. Replacing it with an empty folder should be good enough! A handy trick for module developers is to add the folder you want to remove into the `REPLACE` list within `install.sh`. If your module doesn't provide a corresponding folder, it will create an empty folder, and automatically add `.replace` into the empty folder so the dummy folder will properly replace the one in `/system`.

View File

@ -25,9 +25,11 @@ The concept of `magiskboot` is to make boot image modification simpler. For unpa
Usage: magiskboot <action> [args...]
Supported actions:
unpack [-h] <bootimg>
unpack [-n] [-h] <bootimg>
Unpack <bootimg> to, if available, kernel, kernel_dtb, ramdisk.cpio,
second, dtb, extra, and recovery_dtbo into current directory.
If '-n' is provided, it will not attempt to decompress kernel or
ramdisk.cpio from their original formats.
If '-h' is provided, it will dump header info to 'header',
which will be parsed when repacking.
Return values:
@ -45,7 +47,7 @@ Supported actions:
cpio <incpio> [commands...]
Do cpio commands to <incpio> (modifications are done directly)
Each command is a single argument, use quotes if necessary
Each command is a single argument, add quotes for each command
Supported commands:
exists ENTRY
Return 0 if ENTRY exists, else return 1
@ -65,8 +67,9 @@ Supported actions:
Test the current cpio's patch status
Return values:
0:stock 1:Magisk 2:unsupported (phh, SuperSU, Xposed)
patch KEEPVERITY KEEPFORCEENCRYPT
Ramdisk patches. KEEP**** are boolean values
patch
Apply ramdisk patches. Configure settings with env variables:
KEEPVERITY KEEPFORCEENCRYPT
backup ORIG
Create ramdisk backups from ORIG
restore
@ -74,17 +77,25 @@ Supported actions:
sha1
Print stock boot SHA1 if previously backed up in ramdisk
dtb-<cmd> <dtb>
Do dtb related cmds to <dtb> (modifications are done directly)
Supported commands:
dump
Dump all contents from dtb for debugging
test
Check if fstab has verity/avb flags
Return values:
0:flag exists 1:no flags
patch
dtb <input> <action> [args...]
Do dtb related actions to <input>
Supported actions:
print [-f]
Print all contents of dtb for debugging
Specify [-f] to only print fstab nodes
patch [OUT]
Search for fstab and remove verity/avb
If [OUT] is not specified, it will directly output to <input>
Configure with env variables: KEEPVERITY TWOSTAGEINIT
split <input>
Split image.*-dtb into kernel + kernel_dtb
sha1 <file>
Print the SHA1 checksum for <file>
cleanup
Cleanup the current working directory
compress[=method] <infile> [outfile]
Compress <infile> with [method] (default: gzip), optionally to [outfile]
@ -95,12 +106,6 @@ Supported actions:
Detect method and decompress <infile>, optionally to [outfile]
<infile>/[outfile] can be '-' to be STDIN/STDOUT
Supported methods: bzip2 gzip lz4 lz4_legacy lzma xz
sha1 <file>
Print the SHA1 checksum for <file>
cleanup
Cleanup the current working directory
```
### magiskinit
@ -119,20 +124,22 @@ Usage: magiskpolicy [--options...] [policy statements...]
Options:
--help show help message for policy statements
--load FILE load policies from FILE
--load-split load from preloaded sepolicy or compile
--load-split load from precompiled sepolicy or compile
split policies
--compile-split compile split cil policies
--save FILE save policies to FILE
--live directly apply sepolicy live
--magisk inject built-in rules for a minimal
Magisk selinux environment
--apply FILE apply rules from FILE, read and parsed
line by line as policy statements
If neither --load or --compile-split is specified, it will load
from current live policies (/sys/fs/selinux/policy)
One policy statement should be treated as one parameter;
this means a full policy statement should be enclosed in quotes;
multiple policy statements can be provided in a single command
this means a full policy statement should be enclosed in quotes.
Multiple policy statements can be provided in a single command.
The statements has a format of "<rule_name> [args...]"
Multiple types and permissions can be grouped into collections
@ -173,10 +180,10 @@ Notes:
Example: allow { s1 s2 } { t1 t2 } class *
Will be expanded to:
allow s1 t1 class { all permissions }
allow s1 t2 class { all permissions }
allow s2 t1 class { all permissions }
allow s2 t2 class { all permissions }
allow s1 t1 class { all-permissions }
allow s1 t2 class { all-permissions }
allow s2 t1 class { all-permissions }
allow s2 t2 class { all-permissions }
```
@ -202,7 +209,6 @@ Advanced Options (Internal APIs):
--clone-attr SRC DEST clone permission, owner, and selinux context
--clone SRC DEST clone SRC to DEST
--sqlite SQL exec SQL commands to Magisk database
--use-broadcast use broadcast for su logging and notify
Supported init triggers:
post-fs-data, service, boot-complete
@ -269,5 +275,4 @@ Actions:
ls Print the current hide list
exec CMDs... Execute commands in isolated mount
namespace and do all hide unmounts
test Run process monitor test
```

View File

@ -73,6 +73,7 @@ LOCAL_SRC_FILES := \
magiskpolicy/magiskpolicy.cpp \
magiskpolicy/rules.cpp \
magiskpolicy/policydb.cpp \
magiskpolicy/statement.cpp \
magiskpolicy/sepolicy.c
LOCAL_CFLAGS := -DAPPLET_STUB_MAIN=magiskpolicy_main
@ -97,7 +98,6 @@ ifdef BB_INIT
LOCAL_STATIC_LIBRARIES := libsepol libxz libutils
LOCAL_C_INCLUDES := \
jni/include \
jni/magiskpolicy \
$(EXT_PATH)/include \
out \
out/$(TARGET_ARCH_ABI) \
@ -106,13 +106,14 @@ LOCAL_C_INCLUDES := \
LOCAL_SRC_FILES := \
init/init.cpp \
init/early_mount.cpp \
init/mount.cpp \
init/rootdir.cpp \
init/getinfo.cpp \
magiskpolicy/api.cpp \
magiskpolicy/magiskpolicy.cpp \
magiskpolicy/rules.cpp \
magiskpolicy/policydb.cpp \
magiskpolicy/statement.cpp \
magiskpolicy/sepolicy.c
LOCAL_LDFLAGS := -static

View File

@ -131,7 +131,7 @@ void node_entry::create_module_tree(const char *module) {
auto full_path = get_path();
snprintf(buf, PATH_MAX, "%s/%s%s", MODULEROOT, module, full_path.c_str());
unique_ptr<DIR, decltype(&closedir)> dir(xopendir(buf), closedir);
auto dir = xopen_dir(buf);
if (!dir)
return;
@ -148,7 +148,7 @@ void node_entry::create_module_tree(const char *module) {
return;
}
for (struct dirent *entry; (entry = xreaddir(dir.get()));) {
for (dirent *entry; (entry = xreaddir(dir.get()));) {
if (entry->d_name == "."sv || entry->d_name == ".."sv)
continue;
// Create new node
@ -207,22 +207,18 @@ void node_entry::create_module_tree(const char *module) {
}
void node_entry::clone_skeleton() {
DIR *dir;
struct dirent *entry;
// Clone the structure
auto full_path = get_path();
snprintf(buf, PATH_MAX, "%s%s", MIRRDIR, full_path.c_str());
if (!(dir = xopendir(buf)))
return;
while ((entry = xreaddir(dir))) {
if (entry->d_name == "."sv || entry->d_name == ".."sv)
continue;
// Create dummy node
auto dummy = new node_entry(entry->d_name, entry->d_type, IS_DUMMY);
insert(dummy);
}
closedir(dir);
snprintf(buf, PATH_MAX, "%s%s", MIRRDIR, full_path.data());
if (auto dir = xopen_dir(buf); dir) {
for (dirent *entry; (entry = xreaddir(dir.get()));) {
if (entry->d_name == "."sv || entry->d_name == ".."sv)
continue;
// Create dummy node
auto dummy = new node_entry(entry->d_name, entry->d_type, IS_DUMMY);
insert(dummy);
}
} else { return; }
if (status & IS_SKEL) {
file_attr attr;
@ -321,13 +317,15 @@ static int bind_mount(const char *from, const char *to, bool log) {
static bool magisk_env() {
LOGI("* Initializing Magisk environment\n");
string pkg;
check_manager(&pkg);
char install_dir[128];
sprintf(install_dir, "%s/0/%s/install", APP_DATA_DIR, pkg.data());
// Alternative binaries paths
constexpr const char *alt_bin[] = {
"/cache/data_adb/magisk", "/data/magisk",
"/data/data/com.topjohnwu.magisk/install",
"/data/user_de/0/com.topjohnwu.magisk/install"
};
for (auto &alt : alt_bin) {
const char *alt_bin[] = { "/cache/data_adb/magisk", "/data/magisk", install_dir };
for (auto alt : alt_bin) {
struct stat st;
if (lstat(alt, &st) != -1) {
if (S_ISLNK(st.st_mode)) {
@ -415,15 +413,9 @@ static bool magisk_env() {
}
static void prepare_modules() {
const char *legacy_imgs[] = {SECURE_DIR "/magisk.img", SECURE_DIR "/magisk_merge.img"};
for (auto img : legacy_imgs) {
if (access(img, F_OK) == 0)
migrate_img(img);
}
DIR *dir;
struct dirent *entry;
if ((dir = opendir(MODULEUPGRADE))) {
while ((entry = xreaddir(dir))) {
// Upgrade modules
if (auto dir = open_dir(MODULEUPGRADE); dir) {
for (dirent *entry; (entry = xreaddir(dir.get()));) {
if (entry->d_type == DT_DIR) {
if (entry->d_name == "."sv || entry->d_name == ".."sv)
continue;
@ -436,7 +428,6 @@ static void prepare_modules() {
rename(buf2, buf);
}
}
closedir(dir);
rm_rf(MODULEUPGRADE);
}
bind_mount(MIRRDIR MODULEROOT, MODULEMNT, false);
@ -458,51 +449,48 @@ static void reboot() {
void remove_modules() {
LOGI("* Remove all modules and reboot");
chdir(MODULEROOT);
rm_rf("lost+found");
DIR *dir = xopendir(".");
struct dirent *entry;
while ((entry = xreaddir(dir))) {
auto dir = xopen_dir(MODULEROOT);
int dfd = dirfd(dir.get());
for (dirent *entry; (entry = xreaddir(dir.get()));) {
if (entry->d_type == DT_DIR) {
if (entry->d_name == "."sv || entry->d_name == ".."sv || entry->d_name == ".core"sv)
continue;
chdir(entry->d_name);
close(creat("remove", 0644));
chdir("..");
int modfd = xopenat(dfd, entry->d_name, O_RDONLY | O_CLOEXEC);
close(xopenat(modfd, "remove", O_RDONLY | O_CREAT | O_CLOEXEC));
close(modfd);
}
}
closedir(dir);
chdir("/");
reboot();
}
static void collect_modules() {
chdir(MODULEROOT);
rm_rf("lost+found");
DIR *dir = xopendir(".");
struct dirent *entry;
while ((entry = xreaddir(dir))) {
auto dir = xopen_dir(MODULEROOT);
int dfd = dirfd(dir.get());
for (dirent *entry; (entry = xreaddir(dir.get()));) {
if (entry->d_type == DT_DIR) {
if (entry->d_name == "."sv || entry->d_name == ".."sv || entry->d_name == ".core"sv)
continue;
chdir(entry->d_name);
if (access("remove", F_OK) == 0) {
chdir("..");
int modfd = xopenat(dfd, entry->d_name, O_RDONLY);
run_finally f([=]{ close(modfd); });
if (faccessat(modfd, "remove", F_OK, 0) == 0) {
LOGI("%s: remove\n", entry->d_name);
sprintf(buf, "%s/uninstall.sh", entry->d_name);
fd_pathat(modfd, "uninstall.sh", buf, sizeof(buf));
if (access(buf, F_OK) == 0)
exec_script(buf);
rm_rf(entry->d_name);
frm_rf(modfd);
unlinkat(dfd, entry->d_name, AT_REMOVEDIR);
continue;
}
unlink("update");
if (access("disable", F_OK))
unlinkat(modfd, "update", 0);
if (faccessat(modfd, "disable", F_OK, 0) != 0)
module_list.emplace_back(entry->d_name);
chdir("..");
}
}
closedir(dir);
chdir("/");
}
static bool load_modules(node_entry *root) {
@ -510,36 +498,47 @@ static bool load_modules(node_entry *root) {
bool has_modules = false;
for (const auto &m : module_list) {
const auto module = m.c_str();
const auto module = m.data();
char *name = buf + snprintf(buf, sizeof(buf), MODULEROOT "/%s/", module);
// Read props
snprintf(buf, PATH_MAX, "%s/%s/system.prop", MODULEROOT, module);
strcpy(name, "system.prop");
if (access(buf, F_OK) == 0) {
LOGI("%s: loading [system.prop]\n", module);
load_prop_file(buf, false);
}
// Copy sepolicy rules
strcpy(name, "sepolicy.rule");
if (access(MIRRDIR "/persist", F_OK) == 0 && access(buf, F_OK) == 0) {
char *p = buf2 + snprintf(buf2, sizeof(buf2), MIRRDIR "/persist/magisk/%s", module);
xmkdirs(buf2, 0755);
strcpy(p, "/sepolicy.rule");
cp_afc(buf, buf2);
}
// Check whether skip mounting
snprintf(buf, PATH_MAX, "%s/%s/skip_mount", MODULEROOT, module);
strcpy(name, "skip_mount");
if (access(buf, F_OK) == 0)
continue;
// Double check whether the system folder exists
snprintf(buf, PATH_MAX, "%s/%s/system", MODULEROOT, module);
if (access(buf, F_OK) == -1)
strcpy(name, "system");
if (access(buf, F_OK) != 0)
continue;
// Construct structure
has_modules = true;
LOGI("%s: constructing magic mount structure\n", module);
// If /system/vendor exists in module, create a link outside
snprintf(buf, PATH_MAX, "%s/%s/system/vendor", MODULEROOT, module);
strcpy(name, "system/vendor");
if (node_entry::vendor_root && access(buf, F_OK) == 0) {
snprintf(buf2, PATH_MAX, "%s/%s/vendor", MODULEROOT, module);
snprintf(buf2, sizeof(buf2), "%s/%s/vendor", MODULEROOT, module);
unlink(buf2);
xsymlink("./system/vendor", buf2);
}
// If /system/product exists in module, create a link outside
snprintf(buf, PATH_MAX, "%s/%s/system/product", MODULEROOT, module);
strcpy(name, "system/product");
if (node_entry::product_root && access(buf, F_OK) == 0) {
snprintf(buf2, PATH_MAX, "%s/%s/product", MODULEROOT, module);
snprintf(buf2, sizeof(buf2), "%s/%s/product", MODULEROOT, module);
unlink(buf2);
xsymlink("./system/product", buf2);
}
@ -575,15 +574,14 @@ static bool check_data() {
}
void unlock_blocks() {
DIR *dir;
struct dirent *entry;
int fd, dev, OFF = 0;
if (!(dir = xopendir("/dev/block")))
auto dir = xopen_dir("/dev/block");
if (!dir)
return;
dev = dirfd(dir);
dev = dirfd(dir.get());
while((entry = readdir(dir))) {
for (dirent *entry; (entry = readdir(dir.get()));) {
if (entry->d_type == DT_BLK) {
if ((fd = openat(dev, entry->d_name, O_RDONLY | O_CLOEXEC)) < 0)
continue;
@ -592,7 +590,6 @@ void unlock_blocks() {
close(fd);
}
}
closedir(dir);
}
static bool log_dump = false;
@ -664,22 +661,6 @@ void post_fs_data(int client) {
unblock_boot_process();
}
#if 0
// Increment boot count
int boot_count = 0;
FILE *cf = fopen(BOOTCOUNT, "r");
if (cf) {
fscanf(cf, "%d", &boot_count);
fclose(cf);
}
boot_count++;
if (boot_count > 2)
creat(DISABLEFILE, 0644);
cf = xfopen(BOOTCOUNT, "w");
fprintf(cf, "%d", boot_count);
fclose(cf);
#endif
if (!magisk_env()) {
LOGE("* Magisk environment setup incomplete, abort\n");
unblock_boot_process();
@ -760,7 +741,6 @@ void late_start(int client) {
auto_start_magiskhide();
// Run scripts after full patch, most reliable way to run scripts
LOGI("* Running service.d scripts\n");
exec_common_script("service");
@ -791,11 +771,9 @@ void boot_complete(int client) {
rename(MANAGERAPK, "/data/magisk.apk");
install_apk("/data/magisk.apk");
} else {
// Check whether we have a valid manager installed
db_strings str;
get_db_strings(str, SU_MANAGER);
if (validate_manager(str[SU_MANAGER], 0, nullptr)) {
// There is no manager installed, install the stub
// Check whether we have manager installed
if (!check_manager()) {
// Install stub
exec_command_sync("/sbin/magiskinit", "-x", "manager", "/data/magisk.apk");
install_apk("/data/magisk.apk");
}

View File

@ -118,10 +118,10 @@ static void main_daemon() {
// Unmount pre-init patches
if (access(ROOTMNT, F_OK) == 0) {
file_readline(ROOTMNT, [](auto line) -> bool {
file_readline(true, ROOTMNT, [](auto line) -> bool {
umount2(line.data(), MNT_DETACH);
return true;
}, true);
});
}
LOGI(SHOW_VER(Magisk) " daemon started\n");

View File

@ -246,28 +246,41 @@ int get_uid_policy(su_access &su, int uid) {
return 0;
}
int validate_manager(string &alt_pkg, int userid, struct stat *st) {
bool check_manager(string *pkg) {
db_strings str;
get_db_strings(str, SU_MANAGER);
bool ret = validate_manager(str[SU_MANAGER], 0, nullptr);
if (pkg) {
if (ret)
pkg->swap(str[SU_MANAGER]);
else
*pkg = "xxx"; /* Make sure the return pkg can never exist */
}
return ret;
}
bool validate_manager(string &pkg, int userid, struct stat *st) {
struct stat tmp_st;
if (st == nullptr)
st = &tmp_st;
// Prefer DE storage
char app_path[128];
sprintf(app_path, "%s/%d/%s", APP_DATA_DIR, userid, alt_pkg.empty() ? "xxx" : alt_pkg.data());
if (stat(app_path, st)) {
sprintf(app_path, "%s/%d/%s", APP_DATA_DIR, userid, pkg.data());
if (pkg.empty() || stat(app_path, st)) {
// Check the official package name
sprintf(app_path, "%s/%d/" JAVA_PACKAGE_NAME, APP_DATA_DIR, userid);
if (stat(app_path, st)) {
LOGE("su: cannot find manager");
memset(st, 0, sizeof(*st));
alt_pkg.clear();
return 1;
pkg.clear();
return false;
} else {
// Switch to official package if exists
alt_pkg = JAVA_PACKAGE_NAME;
pkg = JAVA_PACKAGE_NAME;
}
}
return 0;
return true;
}
void exec_sql(int client) {

View File

@ -26,37 +26,36 @@ void exec_script(const char *script) {
void exec_common_script(const char *stage) {
char path[4096];
DIR *dir;
struct dirent *entry;
sprintf(path, SECURE_DIR "/%s.d", stage);
if (!(dir = xopendir(path)))
char *name = path + sprintf(path, SECURE_DIR "/%s.d", stage);
auto dir = xopen_dir(path);
if (!dir)
return;
chdir(path);
bool pfs = strcmp(stage, "post-fs-data") == 0;
while ((entry = xreaddir(dir))) {
int dfd = dirfd(dir.get());
bool pfs = stage == "post-fs-data"sv;
*(name++) = '/';
for (dirent *entry; (entry = xreaddir(dir.get()));) {
if (entry->d_type == DT_REG) {
if (access(entry->d_name, X_OK) == -1)
if (faccessat(dfd, entry->d_name, X_OK, 0) != 0)
continue;
LOGI("%s.d: exec [%s]\n", stage, entry->d_name);
strcpy(name, entry->d_name);
exec_t exec {
.pre_exec = set_path,
.fork = pfs ? fork_no_zombie : fork_dont_care
};
if (pfs)
exec_command_sync(exec, "/system/bin/sh", entry->d_name);
exec_command_sync(exec, "/system/bin/sh", path);
else
exec_command(exec, "/system/bin/sh", entry->d_name);
exec_command(exec, "/system/bin/sh", path);
}
}
closedir(dir);
chdir("/");
}
void exec_module_script(const char *stage, const vector<string> &module_list) {
char path[4096];
bool pfs = strcmp(stage, "post-fs-data") == 0;
bool pfs = stage == "post-fs-data"sv;
for (auto &m : module_list) {
const char* module = m.c_str();
sprintf(path, MODULEROOT "/%s/%s.sh", module, stage);
@ -74,33 +73,6 @@ void exec_module_script(const char *stage, const vector<string> &module_list) {
}
}
constexpr char migrate_script[] =
"MODULEROOT=" MODULEROOT R"EOF(
IMG=%s
MNT=/dev/img_mnt
e2fsck -yf $IMG
mkdir -p $MNT
for num in 0 1 2 3 4 5 6 7; do
losetup /dev/block/loop${num} $IMG || continue
mount -t ext4 /dev/block/loop${num} $MNT
rm -rf $MNT/lost+found $MNT/.core
magisk --clone $MNT $MODULEROOT
umount $MNT
rm -rf $MNT
losetup -d /dev/block/loop${num}
break
done
rm -rf $IMG
)EOF";
void migrate_img(const char *img) {
LOGI("* Migrating %s\n", img);
exec_t exec { .pre_exec = set_path };
char cmds[sizeof(migrate_script) + 128];
sprintf(cmds, migrate_script, img);
exec_command_sync(exec, "/system/bin/sh", "-c", cmds);
}
constexpr char install_script[] = R"EOF(
APK=%s
log -t Magisk "apk_install: $APK"

View File

@ -68,7 +68,6 @@ void remove_modules();
void exec_script(const char *script);
void exec_common_script(const char *stage);
void exec_module_script(const char *stage, const std::vector<std::string> &module_list);
void migrate_img(const char *img);
void install_apk(const char *apk);
/**************

View File

@ -155,7 +155,8 @@ typedef std::function<bool(db_row&)> db_row_cb;
int get_db_settings(db_settings &cfg, int key = -1);
int get_db_strings(db_strings &str, int key = -1);
int get_uid_policy(su_access &su, int uid);
int validate_manager(std::string &alt_pkg, int userid, struct stat *st);
bool check_manager(std::string *pkg = nullptr);
bool validate_manager(std::string &pkg, int userid, struct stat *st);
void exec_sql(int client);
char *db_exec(const char *sql);
char *db_exec(const char *sql, const db_row_cb &fn);

View File

@ -1,6 +1,3 @@
/* magiskpolicy.h - Public API for policy patching
*/
#pragma once
#include <stdlib.h>
@ -35,3 +32,8 @@ int sepol_exists(const char *source);
// Built in rules
void sepol_magisk_rules();
// Statement parsing
void parse_statement(const char *statement);
void load_rule_file(const char *file);
void statement_help();

View File

@ -112,7 +112,7 @@ void load_kernel_info(cmdline *cmd) {
cmd->slot[0] = '_';
strcpy(cmd->slot + 1, value);
} else if (key == "skip_initramfs") {
cmd->system_as_root = true;
cmd->skip_initramfs = true;
} else if (key == "androidboot.force_normal_boot") {
cmd->force_normal_boot = value[0] == '1';
} else if (key == "androidboot.android_dt_dir") {
@ -143,13 +143,13 @@ void load_kernel_info(cmdline *cmd) {
if (recovery_mode) {
LOGD("Running in recovery mode, waiting for key...\n");
cmd->system_as_root = !check_key_combo();
cmd->skip_initramfs = !check_key_combo();
}
if (cmd->dt_dir[0] == '\0')
strcpy(cmd->dt_dir, DEFAULT_DT_DIR);
LOGD("system_as_root=[%d]\n", cmd->system_as_root);
LOGD("skip_initramfs=[%d]\n", cmd->skip_initramfs);
LOGD("force_normal_boot=[%d]\n", cmd->force_normal_boot);
LOGD("slot=[%s]\n", cmd->slot);
LOGD("dt_dir=[%s]\n", cmd->dt_dir);

View File

@ -75,12 +75,12 @@ static bool unxz(int fd, const uint8_t *buf, size_t size) {
xz_crc32_init();
struct xz_dec *dec = xz_dec_init(XZ_DYNALLOC, 1 << 26);
struct xz_buf b = {
.in = buf,
.in_pos = 0,
.in_size = size,
.out = out,
.out_pos = 0,
.out_size = sizeof(out)
.in = buf,
.in_pos = 0,
.in_size = size,
.out = out,
.out_pos = 0,
.out_size = sizeof(out)
};
enum xz_ret ret;
do {
@ -147,11 +147,11 @@ class TestInit : public BaseInit {
public:
TestInit(char *argv[], cmdline *cmd) : BaseInit(argv, cmd) {};
void start() override {
// Write init tests here
// Place init tests here
}
};
static void setup_test(const char *dir) {
[[maybe_unused]] static int test_main(int argc, char *argv[]) {
// Log to console
cmdline_logging();
log_cb.ex = nop_ex;
@ -167,13 +167,21 @@ static void setup_test(const char *dir) {
mounts.emplace_back(me->mnt_dir);
return true;
});
for (auto m = mounts.rbegin(); m != mounts.rend(); ++m)
xumount(m->data());
for (auto &m : reversed(mounts))
xumount(m.data());
// chroot jail
chdir(dir);
chdir(dirname(argv[0]));
chroot(".");
chdir("/");
cmdline cmd{};
load_kernel_info(&cmd);
auto init = make_unique<TestInit>(argv, &cmd);
init->start();
return 1;
}
int main(int argc, char *argv[]) {
@ -184,53 +192,44 @@ int main(int argc, char *argv[]) {
return (*init_applet_main[i])(argc, argv);
}
if (argc > 1 && strcmp(argv[1], "-x") == 0) {
if (strcmp(argv[2], "magisk") == 0)
#ifdef MAGISK_DEBUG
if (getenv("INIT_TEST") != nullptr)
return test_main(argc, argv);
#endif
if (argc > 1 && argv[1] == "-x"sv) {
if (argv[2] == "magisk"sv)
return dump_magisk(argv[3], 0755);
else if (strcmp(argv[2], "manager") == 0)
else if (argv[2] == "manager"sv)
return dump_manager(argv[3], 0644);
}
if (argc > 1 && argv[1] == "selinux_setup"sv) {
auto init = make_unique<SecondStageInit>(argv);
init->start();
}
#ifdef MAGISK_DEBUG
bool run_test = getenv("INIT_TEST") != nullptr;
#else
constexpr bool run_test = false;
#endif
if (run_test) {
setup_test(dirname(argv[0]));
} else {
if (getpid() != 1)
return 1;
setup_klog();
}
cmdline cmd{};
load_kernel_info(&cmd);
if (getpid() != 1)
return 1;
setup_klog();
unique_ptr<BaseInit> init;
if (run_test) {
init = make_unique<TestInit>(argv, &cmd);
} else if (cmd.force_normal_boot) {
init = make_unique<ABFirstStageInit>(argv, &cmd);
} else if (cmd.system_as_root) {
if (access("/overlay", F_OK) == 0) /* Compatible mode */
init = make_unique<SARCompatInit>(argv, &cmd);
else
init = make_unique<SARInit>(argv, &cmd);
cmdline cmd{};
if (argc > 1 && argv[1] == "selinux_setup"sv) {
init = make_unique<SecondStageInit>(argv);
} else {
decompress_ramdisk();
if (access("/sbin/recovery", F_OK) == 0 || access("/system/bin/recovery", F_OK) == 0)
init = make_unique<RecoveryInit>(argv, &cmd);
else if (access("/apex", F_OK) == 0)
init = make_unique<AFirstStageInit>(argv, &cmd);
else
init = make_unique<RootFSInit>(argv, &cmd);
// This will also mount /sys and /proc
load_kernel_info(&cmd);
if (cmd.force_normal_boot) {
init = make_unique<ABFirstStageInit>(argv, &cmd);
} else if (cmd.skip_initramfs) {
init = make_unique<SARInit>(argv, &cmd);
} else {
decompress_ramdisk();
if (access("/sbin/recovery", F_OK) == 0 || access("/system/bin/recovery", F_OK) == 0)
init = make_unique<RecoveryInit>(argv, &cmd);
else if (access("/apex", F_OK) == 0)
init = make_unique<AFirstStageInit>(argv, &cmd);
else
init = make_unique<RootFSInit>(argv, &cmd);
}
}
// Run the main routine

View File

@ -3,8 +3,10 @@
#include <stdlib.h>
#include <vector>
#include <magisk.h>
struct cmdline {
bool system_as_root;
bool skip_initramfs;
bool force_normal_boot;
char slot[3];
char dt_dir[128];
@ -45,7 +47,7 @@ protected:
virtual void cleanup();
public:
BaseInit(char *argv[], cmdline *cmd) :
cmd(cmd), argv(argv), mount_list{"/sys", "/proc", "/dev"} {}
cmd(cmd), argv(argv), mount_list{"/sys", "/proc"} {}
virtual ~BaseInit() = default;
virtual void start() = 0;
};
@ -53,28 +55,14 @@ public:
class MagiskInit : public BaseInit {
protected:
raw_data self;
const char *persist_dir;
virtual void early_mount() = 0;
bool read_dt_fstab(const char *name, char *partname, char *fstype);
bool patch_sepolicy(const char *file = "/sepolicy");
public:
MagiskInit(char *argv[], cmdline *cmd) : BaseInit(argv, cmd) {};
};
class RootFSBase : public MagiskInit {
protected:
int root = -1;
virtual void setup_rootfs();
public:
RootFSBase(char *argv[], cmdline *cmd) : MagiskInit(argv, cmd) {};
void start() override {
early_mount();
setup_rootfs();
exec_init();
}
};
class SARBase : public MagiskInit {
protected:
raw_data config;
@ -83,7 +71,9 @@ protected:
void backup_files();
void patch_rootdir();
public:
SARBase(char *argv[], cmdline *cmd) : MagiskInit(argv, cmd) {};
SARBase(char *argv[], cmdline *cmd) : MagiskInit(argv, cmd) {
persist_dir = MIRRDIR "/persist/magisk";
}
void start() override {
early_mount();
patch_rootdir();
@ -96,7 +86,7 @@ public:
* *************/
class ABFirstStageInit : public BaseInit {
protected:
private:
void prepare();
public:
ABFirstStageInit(char *argv[], cmdline *cmd) : BaseInit(argv, cmd) {};
@ -107,7 +97,7 @@ public:
};
class AFirstStageInit : public BaseInit {
protected:
private:
void prepare();
public:
AFirstStageInit(char *argv[], cmdline *cmd) : BaseInit(argv, cmd) {};
@ -140,26 +130,26 @@ public:
* Initramfs
* **********/
class RootFSInit : public RootFSBase {
class RootFSInit : public MagiskInit {
private:
int root = -1;
void setup_rootfs();
protected:
void early_mount() override;
public:
RootFSInit(char *argv[], cmdline *cmd) : RootFSBase(argv, cmd) {};
};
RootFSInit(char *argv[], cmdline *cmd) : MagiskInit(argv, cmd) {
persist_dir = "/dev/.magisk/mirror/persist/magisk";
}
/* ****************
* Compat-mode SAR
* ****************/
class SARCompatInit : public RootFSBase {
protected:
void early_mount() override;
void setup_rootfs() override;
public:
SARCompatInit(char *argv[], cmdline *cmd) : RootFSBase(argv, cmd) {};
void start() override {
early_mount();
setup_rootfs();
exec_init();
}
};
void load_kernel_info(cmdline *cmd);
int dump_magisk(const char *path, mode_t mode);
int magisk_proxy_main(int argc, char *argv[]);
void setup_klog();
void mount_sbin();

View File

@ -6,6 +6,7 @@
#include <utils.h>
#include <logging.h>
#include <selinux.h>
#include <magisk.h>
#include "init.h"
@ -20,13 +21,17 @@ struct devinfo {
static vector<devinfo> dev_list;
static char partname[32];
static char fstype[32];
static char block_dev[64];
static void parse_device(devinfo *dev, const char *uevent) {
dev->partname[0] = '\0';
parse_prop_file(uevent, [=](string_view key, string_view value) -> bool {
if (key == "MAJOR")
dev->major = atoi(value.data());
dev->major = parse_int(value.data());
else if (key == "MINOR")
dev->minor = atoi(value.data());
dev->minor = parse_int(value.data());
else if (key == "DEVNAME")
strcpy(dev->devname, value.data());
else if (key == "PARTNAME")
@ -38,35 +43,33 @@ static void parse_device(devinfo *dev, const char *uevent) {
static void collect_devices() {
char path[128];
struct dirent *entry;
devinfo dev;
DIR *dir = xopendir("/sys/dev/block");
if (dir == nullptr)
return;
while ((entry = readdir(dir))) {
if (entry->d_name == "."sv || entry->d_name == ".."sv)
continue;
sprintf(path, "/sys/dev/block/%s/uevent", entry->d_name);
parse_device(&dev, path);
dev_list.push_back(dev);
devinfo dev{};
if (auto dir = xopen_dir("/sys/dev/block"); dir) {
for (dirent *entry; (entry = readdir(dir.get()));) {
if (entry->d_name == "."sv || entry->d_name == ".."sv)
continue;
sprintf(path, "/sys/dev/block/%s/uevent", entry->d_name);
parse_device(&dev, path);
dev_list.push_back(dev);
}
}
closedir(dir);
}
static dev_t setup_block(const char *partname, char *block_dev = nullptr) {
static int64_t setup_block(bool write_block = true) {
if (dev_list.empty())
collect_devices();
for (;;) {
xmkdir("/dev", 0755);
xmkdir("/dev/block", 0755);
for (int tries = 0; tries < 3; ++tries) {
for (auto &dev : dev_list) {
if (strcasecmp(dev.partname, partname) == 0) {
xmkdir("/dev", 0755);
if (block_dev) {
if (write_block) {
sprintf(block_dev, "/dev/block/%s", dev.devname);
xmkdir("/dev/block", 0755);
}
LOGD("Found %s: [%s] (%d, %d)\n", dev.partname, dev.devname, dev.major, dev.minor);
dev_t rdev = makedev(dev.major, dev.minor);
mknod(block_dev ? block_dev : "/dev/root", S_IFBLK | 0600, rdev);
mknod(block_dev, S_IFBLK | 0600, rdev);
return rdev;
}
}
@ -75,6 +78,9 @@ static dev_t setup_block(const char *partname, char *block_dev = nullptr) {
dev_list.clear();
collect_devices();
}
// The requested partname does not exist
return -1;
}
static bool is_lnk(const char *name) {
@ -84,15 +90,7 @@ static bool is_lnk(const char *name) {
return S_ISLNK(st.st_mode);
}
void BaseInit::cleanup() {
// Unmount in reverse order
for (auto &p : reversed(mount_list)) {
LOGD("Unmount [%s]\n", p.data());
umount(p.data());
}
}
bool MagiskInit::read_dt_fstab(const char *name, char *partname, char *fstype) {
static bool read_dt_fstab(cmdline *cmd, const char *name) {
char path[128];
int fd;
sprintf(path, "%s/fstab/%s/dev", cmd->dt_dir, name);
@ -112,18 +110,10 @@ bool MagiskInit::read_dt_fstab(const char *name, char *partname, char *fstype) {
return false;
}
static char partname[32];
static char fstype[32];
static char block_dev[64];
#define link_root(name) \
if (is_lnk("/system_root" name)) \
cp_afc("/system_root" name, name)
#define mount_root(name) \
if (!is_lnk("/" #name) && read_dt_fstab(#name, partname, fstype)) { \
if (!is_lnk("/" #name) && read_dt_fstab(cmd, #name)) { \
LOGD("Early mount " #name "\n"); \
setup_block(partname, block_dev); \
setup_block(); \
xmkdir("/" #name, 0755); \
xmount(block_dev, "/" #name, fstype, MS_RDONLY, nullptr); \
mount_list.emplace_back("/" #name); \
@ -136,36 +126,19 @@ void RootFSInit::early_mount() {
root = xopen("/", O_RDONLY | O_CLOEXEC);
rename("/.backup/init", "/init");
// Mount sbin overlay for persist, but move it and add to cleanup list
mount_sbin();
xmount("/sbin", "/dev", nullptr, MS_MOVE, nullptr);
mount_list.emplace_back("/dev");
mount_list.emplace_back("/dev/.magisk/mirror/persist");
mount_list.emplace_back("/dev/.magisk/mirror/cache");
mount_root(system);
mount_root(vendor);
mount_root(product);
mount_root(odm);
}
void SARCompatInit::early_mount() {
full_read("/init", self.buf, self.sz);
LOGD("Cleaning rootfs\n");
root = xopen("/", O_RDONLY | O_CLOEXEC);
frm_rf(root, { ".backup", "overlay", "overlay.d", "proc", "sys" });
LOGD("Early mount system_root\n");
sprintf(partname, "system%s", cmd->slot);
setup_block(partname, block_dev);
xmkdir("/system_root", 0755);
if (xmount(block_dev, "/system_root", "ext4", MS_RDONLY, nullptr))
xmount(block_dev, "/system_root", "erofs", MS_RDONLY, nullptr);
xmkdir("/system", 0755);
xmount("/system_root/system", "/system", nullptr, MS_BIND, nullptr);
link_root("/vendor");
link_root("/product");
link_root("/odm");
mount_root(vendor);
mount_root(product);
mount_root(odm);
}
static void switch_root(const string &path) {
LOGD("Switch root to %s\n", path.data());
vector<string> mounts;
@ -203,6 +176,7 @@ void SARInit::early_mount() {
// Make dev writable
xmkdir("/dev", 0755);
xmount("tmpfs", "/dev", "tmpfs", 0, "mode=755");
mount_list.emplace_back("/dev");
backup_files();
@ -213,7 +187,19 @@ void SARInit::early_mount() {
LOGD("Early mount system_root\n");
sprintf(partname, "system%s", cmd->slot);
system_dev = setup_block(partname);
strcpy(block_dev, "/dev/root");
auto dev = setup_block(false);
if (dev < 0) {
// Try NVIDIA naming scheme
strcpy(partname, "APP");
dev = setup_block();
if (dev < 0) {
// We don't really know what to do at this point...
LOGE("Cannot find root partition, abort\n");
exit(1);
}
}
system_dev = dev;
xmkdir("/system_root", 0755);
if (xmount("/dev/root", "/system_root", "ext4", MS_RDONLY, nullptr))
xmount("/dev/root", "/system_root", "erofs", MS_RDONLY, nullptr);
@ -245,3 +231,42 @@ void SecondStageInit::early_mount() {
switch_root("/system_root");
}
void BaseInit::cleanup() {
// Unmount in reverse order
for (auto &p : reversed(mount_list)) {
if (xumount(p.data()) == 0)
LOGD("Unmount [%s]\n", p.data());
}
mount_list.clear();
mount_list.shrink_to_fit();
}
void mount_sbin() {
LOGD("Mount /sbin tmpfs overlay\n");
xmount("tmpfs", "/sbin", "tmpfs", 0, "mode=755");
xmkdir(MAGISKTMP, 0755);
xmkdir(MIRRDIR, 0);
xmkdir(BLOCKDIR, 0);
// Mount persist partition
strcpy(partname, "persist");
strcpy(block_dev, BLOCKDIR "/persist");
const char *mnt_point = MIRRDIR "/persist";
if (setup_block(false) < 0) {
// Fallback to cache
strcpy(partname, "cache");
strcpy(block_dev, BLOCKDIR "/cache");
if (setup_block(false) < 0) {
// Try NVIDIA's BS
strcpy(partname, "CAC");
if (setup_block(false) < 0)
return;
}
mnt_point = MIRRDIR "/cache";
xsymlink("./cache", MIRRDIR "/persist");
}
xmkdir(mnt_point, 0755);
xmount(block_dev, mnt_point, "ext4", 0, nullptr);
}

View File

@ -24,7 +24,7 @@ static void patch_socket_name(const char *path) {
mmap_rw(path, buf, size);
for (int i = 0; i < size; ++i) {
if (memcmp(buf + i, MAIN_SOCKET, sizeof(MAIN_SOCKET)) == 0) {
gen_rand_str(buf + i, sizeof(MAIN_SOCKET));
gen_rand_str(buf + i, 16);
i += sizeof(MAIN_SOCKET);
}
}
@ -85,7 +85,7 @@ static void load_overlay_rc(int dirfd) {
rewinddir(dir);
}
void RootFSBase::setup_rootfs() {
void RootFSInit::setup_rootfs() {
if (patch_sepolicy()) {
char *addr;
size_t size;
@ -101,17 +101,8 @@ void RootFSBase::setup_rootfs() {
munmap(addr, size);
}
// Handle legacy overlays
int fd = open("/overlay", O_RDONLY | O_CLOEXEC);
if (fd >= 0) {
LOGD("Merge overlay folder\n");
mv_dir(fd, root);
close(fd);
rmdir("/overlay");
}
// Handle overlays
fd = open("/overlay.d", O_RDONLY | O_CLOEXEC);
int fd = open("/overlay.d", O_RDONLY | O_CLOEXEC);
if (fd >= 0) {
LOGD("Merge overlay.d\n");
load_overlay_rc(fd);
@ -141,16 +132,6 @@ void RootFSBase::setup_rootfs() {
close(fd);
}
void SARCompatInit::setup_rootfs() {
// Clone rootfs
LOGD("Clone root dir from system to rootfs\n");
int system_root = xopen("/system_root", O_RDONLY | O_CLOEXEC);
clone_dir(system_root, root, false);
close(system_root);
RootFSBase::setup_rootfs();
}
bool MagiskInit::patch_sepolicy(const char *file) {
bool patch_init = false;
@ -174,7 +155,23 @@ bool MagiskInit::patch_sepolicy(const char *file) {
sepol_magisk_rules();
sepol_allow(SEPOL_PROC_DOMAIN, ALL, ALL, ALL);
// Custom rules
if (auto dir = xopen_dir(persist_dir); dir) {
char path[4096];
for (dirent *entry; (entry = xreaddir(dir.get()));) {
if (entry->d_name == "."sv || entry->d_name == ".."sv)
continue;
snprintf(path, sizeof(path), "%s/%s/sepolicy.rule", persist_dir, entry->d_name);
if (access(path, R_OK) == 0) {
LOGD("Loading custom sepolicy patch: %s\n", path);
load_rule_file(path);
}
}
}
dump_policydb(file);
destroy_policydb();
// Remove OnePlus stupid debug sepolicy and use our own
if (access("/sepolicy_debug", F_OK) == 0) {
@ -192,8 +189,7 @@ constexpr const char wrapper[] =
;
static void sbin_overlay(const raw_data &self, const raw_data &config) {
LOGD("Mount /sbin tmpfs overlay\n");
xmount("tmpfs", "/sbin", "tmpfs", 0, "mode=755");
mount_sbin();
// Dump binaries
xmkdir(MAGISKTMP, 0755);
@ -225,6 +221,48 @@ static void sbin_overlay(const raw_data &self, const raw_data &config) {
xsymlink("./magiskinit", "/sbin/supolicy");
}
static void recreate_sbin(const char *mirror) {
int src = xopen(mirror, O_RDONLY | O_CLOEXEC);
int dest = xopen("/sbin", O_RDONLY | O_CLOEXEC);
DIR *fp = fdopendir(src);
char buf[256];
bool use_bind_mount = true;
for (dirent *entry; (entry = xreaddir(fp));) {
if (entry->d_name == "."sv || entry->d_name == ".."sv)
continue;
struct stat st;
fstatat(src, entry->d_name, &st, AT_SYMLINK_NOFOLLOW);
if (S_ISLNK(st.st_mode)) {
xreadlinkat(src, entry->d_name, buf, sizeof(buf));
xsymlinkat(buf, dest, entry->d_name);
} else {
char sbin_path[256];
sprintf(buf, "%s/%s", mirror, entry->d_name);
sprintf(sbin_path, "/sbin/%s", entry->d_name);
if (use_bind_mount) {
// Create dummy
if (S_ISDIR(st.st_mode))
xmkdir(sbin_path, st.st_mode & 0777);
else
close(xopen(sbin_path, O_CREAT | O_WRONLY | O_CLOEXEC, st.st_mode & 0777));
if (xmount(buf, sbin_path, nullptr, MS_BIND, nullptr)) {
// Bind mount failed, fallback to symlink
remove(sbin_path);
use_bind_mount = false;
} else {
continue;
}
}
xsymlink(buf, sbin_path);
}
}
close(src);
close(dest);
}
#define ROOTMIR MIRRDIR "/system_root"
#define ROOTBLK BLOCKDIR "/system_root"
#define MONOPOLICY "/sepolicy"
@ -260,47 +298,19 @@ void SARBase::patch_rootdir() {
sbin_overlay(self, config);
// Mount system_root mirror
xmkdir(MIRRDIR, 0);
xmkdir(ROOTMIR, 0);
xmkdir(BLOCKDIR, 0);
xmkdir(ROOTMIR, 0755);
mknod(ROOTBLK, S_IFBLK | 0600, system_dev);
if (xmount(ROOTBLK, ROOTMIR, "ext4", MS_RDONLY, nullptr))
xmount(ROOTBLK, ROOTMIR, "erofs", MS_RDONLY, nullptr);
// Recreate original sbin structure
int src = xopen(ROOTMIR "/sbin", O_RDONLY | O_CLOEXEC);
int dest = xopen("/sbin", O_RDONLY | O_CLOEXEC);
DIR *fp = fdopendir(src);
struct dirent *entry;
struct stat st;
char buf[256];
while ((entry = xreaddir(fp))) {
if (entry->d_name == "."sv || entry->d_name == ".."sv)
continue;
fstatat(src, entry->d_name, &st, AT_SYMLINK_NOFOLLOW);
if (S_ISLNK(st.st_mode)) {
xreadlinkat(src, entry->d_name, buf, sizeof(buf));
xsymlinkat(buf, dest, entry->d_name);
} else {
char spath[256];
sprintf(buf, "/sbin/%s", entry->d_name);
sprintf(spath, ROOTMIR "/sbin/%s", entry->d_name);
// Create dummy
if (S_ISDIR(st.st_mode))
xmkdir(buf, st.st_mode & 0777);
else
close(xopen(buf, O_CREAT | O_WRONLY | O_CLOEXEC, st.st_mode & 0777));
xmount(spath, buf, nullptr, MS_BIND, nullptr);
}
}
close(src);
close(dest);
recreate_sbin(ROOTMIR "/sbin");
// Patch init
raw_data init;
file_attr attr;
bool redirect = false;
src = xopen("/init", O_RDONLY | O_CLOEXEC);
int src = xopen("/init", O_RDONLY | O_CLOEXEC);
fd_full_read(src, init.buf, init.sz);
fgetattr(src, &attr);
close(src);
@ -320,7 +330,7 @@ void SARBase::patch_rootdir() {
}
}
xmkdir(ROOTOVL, 0);
dest = xopen(ROOTOVL "/init", O_CREAT | O_WRONLY | O_CLOEXEC);
int dest = xopen(ROOTOVL "/init", O_CREAT | O_WRONLY | O_CLOEXEC);
xwrite(dest, init.buf, init.sz);
fsetattr(dest, &attr);
close(dest);
@ -423,17 +433,16 @@ static void patch_fstab(const string &fstab) {
#define FSR "/first_stage_ramdisk"
void ABFirstStageInit::prepare() {
DIR *dir = xopendir(FSR);
auto dir = xopen_dir(FSR);
if (!dir)
return;
string fstab(FSR "/");
for (dirent *de; (de = readdir(dir));) {
for (dirent *de; (de = xreaddir(dir.get()));) {
if (strstr(de->d_name, "fstab")) {
fstab += de->d_name;
break;
}
}
closedir(dir);
if (fstab.length() == sizeof(FSR))
return;
@ -450,14 +459,13 @@ void ABFirstStageInit::prepare() {
}
void AFirstStageInit::prepare() {
DIR *dir = xopendir("/");
for (dirent *de; (de = readdir(dir));) {
auto dir = xopen_dir("/");
for (dirent *de; (de = xreaddir(dir.get()));) {
if (strstr(de->d_name, "fstab")) {
patch_fstab(de->d_name);
break;
}
}
closedir(dir);
// Move stuffs for next stage
xmkdir("/system", 0755);
@ -483,18 +491,7 @@ int magisk_proxy_main(int argc, char *argv[]) {
sbin_overlay(self, config);
// Create symlinks pointing back to /root
char path[256];
int sbin = xopen("/sbin", O_RDONLY | O_CLOEXEC);
DIR *dir = xopendir("/root");
struct dirent *entry;
while((entry = xreaddir(dir))) {
if (entry->d_name == "."sv || entry->d_name == ".."sv)
continue;
sprintf(path, "/root/%s", entry->d_name);
xsymlinkat(path, sbin, entry->d_name);
}
close(sbin);
closedir(dir);
recreate_sbin("/root");
setenv("REMOUNT_ROOT", "1", 1);
execv("/sbin/magisk", argv);

View File

@ -22,14 +22,14 @@ uint32_t dyn_img_hdr::j32 = 0;
uint64_t dyn_img_hdr::j64 = 0;
static void decompress(format_t type, int fd, const void *in, size_t size) {
auto ptr = get_decoder(type, make_stream<fd_stream>(fd));
auto ptr = get_decoder(type, make_unique<fd_stream>(fd));
ptr->write(in, size);
}
static off_t compress(format_t type, int fd, const void *in, size_t size) {
auto prev = lseek(fd, 0, SEEK_CUR);
{
auto strm = get_encoder(type, make_stream<fd_stream>(fd));
auto strm = get_encoder(type, make_unique<fd_stream>(fd));
strm->write(in, size);
}
auto now = lseek(fd, 0, SEEK_CUR);
@ -282,33 +282,61 @@ void boot_img::parse_image(uint8_t *addr) {
fprintf(stderr, "RAMDISK_FMT [%s]\n", fmt2name[r_fmt]);
}
void boot_img::find_kernel_dtb() {
const int eof = static_cast<int>(hdr->kernel_size());
for (int i = 0; i < eof - (int) sizeof(fdt_header); ++i) {
auto fdt_hdr = reinterpret_cast<fdt_header *>(kernel + i);
static int find_dtb_offset(uint8_t *buf, int sz) {
for (int off = 0; off < sz - (int) sizeof(fdt_header); ++off) {
auto fdt_hdr = reinterpret_cast<fdt_header *>(buf + off);
if (fdt32_to_cpu(fdt_hdr->magic) != FDT_MAGIC)
continue;
// Check that fdt_header.totalsize does not overflow kernel image size
uint32_t totalsize = fdt32_to_cpu(fdt_hdr->totalsize);
if (totalsize + i > eof)
if (totalsize + off > sz)
continue;
// Check that fdt_header.off_dt_struct does not overflow kernel image size
uint32_t off_dt_struct = fdt32_to_cpu(fdt_hdr->off_dt_struct);
if (off_dt_struct + i > eof)
if (off_dt_struct + off > sz)
continue;
// Check that fdt_node_header.tag of first node is FDT_BEGIN_NODE
auto fdt_node_hdr = reinterpret_cast<fdt_node_header *>(kernel + i + off_dt_struct);
auto fdt_node_hdr = reinterpret_cast<fdt_node_header *>(buf + off + off_dt_struct);
if (fdt32_to_cpu(fdt_node_hdr->tag) != FDT_BEGIN_NODE)
continue;
kernel_dtb = kernel + i;
kernel_dt_size = eof - i;
hdr->kernel_size() = i;
return off;
}
return -1;
}
void boot_img::find_kernel_dtb() {
if (int off = find_dtb_offset(kernel, hdr->kernel_size()); off > 0) {
kernel_dtb = kernel + off;
kernel_dt_size = hdr->kernel_size() - off;
hdr->kernel_size() = off;
fprintf(stderr, "KERNEL_DTB [%u]\n", kernel_dt_size);
break;
}
}
int split_image_dtb(const char *filename) {
uint8_t *buf;
size_t sz;
mmap_ro(filename, buf, sz);
run_finally f([=]{ munmap(buf, sz); });
if (int off = find_dtb_offset(buf, sz); off > 0) {
format_t fmt = check_fmt(buf, sz);
if (COMPRESSED(fmt)) {
int fd = creat(KERNEL_FILE, 0644);
decompress(fmt, fd, buf, off);
close(fd);
} else {
dump(buf, off, KERNEL_FILE);
}
dump(buf + off, sz - off, KER_DTB_FILE);
return 0;
} else {
fprintf(stderr, "Cannot find DTB in %s\n", filename);
return 1;
}
}

View File

@ -57,7 +57,7 @@ protected:
ENCODE
} mode;
gz_strm(mode_t mode, sFILE &&fp) : cpr_stream(std::move(fp)), mode(mode) {
gz_strm(mode_t mode, stream_ptr &&base) : cpr_stream(std::move(base)), mode(mode) {
switch(mode) {
case DECODE:
inflateInit2(&strm, 15 | 16);
@ -99,12 +99,12 @@ private:
class gz_decoder : public gz_strm {
public:
explicit gz_decoder(sFILE &&fp) : gz_strm(DECODE, std::move(fp)) {};
explicit gz_decoder(stream_ptr &&base) : gz_strm(DECODE, std::move(base)) {};
};
class gz_encoder : public gz_strm {
public:
explicit gz_encoder(sFILE &&fp) : gz_strm(ENCODE, std::move(fp)) {};
explicit gz_encoder(stream_ptr &&base) : gz_strm(ENCODE, std::move(base)) {};
};
class bz_strm : public cpr_stream {
@ -131,7 +131,7 @@ protected:
ENCODE
} mode;
bz_strm(mode_t mode, sFILE &&fp) : cpr_stream(std::move(fp)), mode(mode) {
bz_strm(mode_t mode, stream_ptr &&base) : cpr_stream(std::move(base)), mode(mode) {
switch(mode) {
case DECODE:
BZ2_bzDecompressInit(&strm, 0, 0);
@ -173,12 +173,12 @@ private:
class bz_decoder : public bz_strm {
public:
explicit bz_decoder(sFILE &&fp) : bz_strm(DECODE, std::move(fp)) {};
explicit bz_decoder(stream_ptr &&base) : bz_strm(DECODE, std::move(base)) {};
};
class bz_encoder : public bz_strm {
public:
explicit bz_encoder(sFILE &&fp) : bz_strm(ENCODE, std::move(fp)) {};
explicit bz_encoder(stream_ptr &&base) : bz_strm(ENCODE, std::move(base)) {};
};
class lzma_strm : public cpr_stream {
@ -199,8 +199,8 @@ protected:
ENCODE_LZMA
} mode;
lzma_strm(mode_t mode, sFILE &&fp)
: cpr_stream(std::move(fp)), mode(mode), strm(LZMA_STREAM_INIT) {
lzma_strm(mode_t mode, stream_ptr &&base)
: cpr_stream(std::move(base)), mode(mode), strm(LZMA_STREAM_INIT) {
lzma_options_lzma opt;
// Initialize preset
@ -247,22 +247,22 @@ private:
class lzma_decoder : public lzma_strm {
public:
explicit lzma_decoder(sFILE &&fp) : lzma_strm(DECODE, std::move(fp)) {}
explicit lzma_decoder(stream_ptr &&base) : lzma_strm(DECODE, std::move(base)) {}
};
class xz_encoder : public lzma_strm {
public:
explicit xz_encoder(sFILE &&fp) : lzma_strm(ENCODE_XZ, std::move(fp)) {}
explicit xz_encoder(stream_ptr &&base) : lzma_strm(ENCODE_XZ, std::move(base)) {}
};
class lzma_encoder : public lzma_strm {
public:
explicit lzma_encoder(sFILE &&fp) : lzma_strm(ENCODE_LZMA, std::move(fp)) {}
explicit lzma_encoder(stream_ptr &&base) : lzma_strm(ENCODE_LZMA, std::move(base)) {}
};
class LZ4F_decoder : public cpr_stream {
public:
explicit LZ4F_decoder(sFILE &&fp) : cpr_stream(std::move(fp)), outbuf(nullptr) {
explicit LZ4F_decoder(stream_ptr &&base) : cpr_stream(std::move(base)), outbuf(nullptr) {
LZ4F_createDecompressionContext(&ctx, LZ4F_VERSION);
}
@ -317,8 +317,8 @@ private:
class LZ4F_encoder : public cpr_stream {
public:
explicit LZ4F_encoder(sFILE &&fp)
: cpr_stream(std::move(fp)), outbuf(nullptr), outCapacity(0) {
explicit LZ4F_encoder(stream_ptr &&base)
: cpr_stream(std::move(base)), outbuf(nullptr), outCapacity(0) {
LZ4F_createCompressionContext(&ctx, LZ4F_VERSION);
}
@ -378,8 +378,8 @@ private:
class LZ4_decoder : public cpr_stream {
public:
explicit LZ4_decoder(sFILE &&fp)
: cpr_stream(std::move(fp)), out_buf(new char[LZ4_UNCOMPRESSED]),
explicit LZ4_decoder(stream_ptr &&base)
: cpr_stream(std::move(base)), out_buf(new char[LZ4_UNCOMPRESSED]),
buffer(new char[LZ4_COMPRESSED]), init(false), block_sz(0), buf_off(0) {}
~LZ4_decoder() override {
@ -439,8 +439,8 @@ private:
class LZ4_encoder : public cpr_stream {
public:
explicit LZ4_encoder(sFILE &&fp)
: cpr_stream(std::move(fp)), outbuf(new char[LZ4_COMPRESSED]), buf(new char[LZ4_UNCOMPRESSED]),
explicit LZ4_encoder(stream_ptr &&base)
: cpr_stream(std::move(base)), outbuf(new char[LZ4_COMPRESSED]), buf(new char[LZ4_UNCOMPRESSED]),
init(false), buf_off(0), in_total(0) {}
int write(const void *in, size_t size) override {
@ -500,38 +500,38 @@ private:
unsigned in_total;
};
stream_ptr get_encoder(format_t type, sFILE &&fp) {
stream_ptr get_encoder(format_t type, stream_ptr &&base) {
switch (type) {
case XZ:
return make_unique<xz_encoder>(std::move(fp));
return make_unique<xz_encoder>(std::move(base));
case LZMA:
return make_unique<lzma_encoder>(std::move(fp));
return make_unique<lzma_encoder>(std::move(base));
case BZIP2:
return make_unique<bz_encoder>(std::move(fp));
return make_unique<bz_encoder>(std::move(base));
case LZ4:
return make_unique<LZ4F_encoder>(std::move(fp));
return make_unique<LZ4F_encoder>(std::move(base));
case LZ4_LEGACY:
return make_unique<LZ4_encoder>(std::move(fp));
return make_unique<LZ4_encoder>(std::move(base));
case GZIP:
default:
return make_unique<gz_encoder>(std::move(fp));
return make_unique<gz_encoder>(std::move(base));
}
}
stream_ptr get_decoder(format_t type, sFILE &&fp) {
stream_ptr get_decoder(format_t type, stream_ptr &&base) {
switch (type) {
case XZ:
case LZMA:
return make_unique<lzma_decoder>(std::move(fp));
return make_unique<lzma_decoder>(std::move(base));
case BZIP2:
return make_unique<bz_decoder>(std::move(fp));
return make_unique<bz_decoder>(std::move(base));
case LZ4:
return make_unique<LZ4F_decoder>(std::move(fp));
return make_unique<LZ4F_decoder>(std::move(base));
case LZ4_LEGACY:
return make_unique<LZ4_decoder>(std::move(fp));
return make_unique<LZ4_decoder>(std::move(base));
case GZIP:
default:
return make_unique<gz_decoder>(std::move(fp));
return make_unique<gz_decoder>(std::move(base));
}
}
@ -573,7 +573,7 @@ void decompress(char *infile, const char *outfile) {
}
FILE *out_fp = outfile == "-"sv ? stdout : xfopen(outfile, "we");
strm = get_decoder(type, make_sFILE(out_fp));
strm = get_decoder(type, make_unique<fp_stream>(out_fp));
if (ext) *ext = '.';
}
if (strm->write(buf, len) < 0)
@ -614,7 +614,7 @@ void compress(const char *method, const char *infile, const char *outfile) {
out_fp = outfile == "-"sv ? stdout : xfopen(outfile, "we");
}
auto strm = get_encoder(it->second, make_sFILE(out_fp));
auto strm = get_encoder(it->second, make_unique<fp_stream>(out_fp));
char buf[4096];
size_t len;

View File

@ -4,9 +4,9 @@
#include "format.h"
stream_ptr get_encoder(format_t type, sFILE &&fp);
stream_ptr get_encoder(format_t type, stream_ptr &&base);
stream_ptr get_decoder(format_t type, sFILE &&fp);
stream_ptr get_decoder(format_t type, stream_ptr &&base);
void compress(const char *method, const char *infile, const char *outfile);

View File

@ -1,93 +1,20 @@
#include <unistd.h>
#include <sys/mman.h>
#include <bitset>
#include <vector>
#include <map>
extern "C" {
#include <libfdt.h>
}
#include <utils.h>
#include <bitset>
#include <vector>
#include <map>
#include "magiskboot.h"
#include "dtb.h"
using namespace std;
#define DTB_MAGIC "\xd0\x0d\xfe\xed"
#define QCDT_MAGIC "QCDT"
#define DTBH_MAGIC "DTBH"
#define PXADT_MAGIC "PXA-DT"
#define PXA19xx_MAGIC "PXA-19xx"
#define SPRD_MAGIC "SPRD"
struct qcdt_hdr {
char magic[4]; /* "QCDT" */
uint32_t version; /* QCDT version */
uint32_t num_dtbs; /* Number of DTBs */
} __attribute__((packed));
struct qctable_v1 {
uint32_t cpu_info[3]; /* Some CPU info */
uint32_t offset; /* DTB offset in QCDT */
uint32_t len; /* DTB size */
} __attribute__((packed));
struct qctable_v2 {
uint32_t cpu_info[4]; /* Some CPU info */
uint32_t offset; /* DTB offset in QCDT */
uint32_t len; /* DTB size */
} __attribute__((packed));
struct qctable_v3 {
uint32_t cpu_info[8]; /* Some CPU info */
uint32_t offset; /* DTB offset in QCDT */
uint32_t len; /* DTB size */
} __attribute__((packed));
struct dtbh_hdr {
char magic[4]; /* "DTBH" */
uint32_t version; /* DTBH version */
uint32_t num_dtbs; /* Number of DTBs */
} __attribute__((packed));
struct bhtable_v2 {
uint32_t cpu_info[5]; /* Some CPU info */
uint32_t offset; /* DTB offset in DTBH */
uint32_t len; /* DTB size */
uint32_t space; /* 0x00000020 */
} __attribute__((packed));
struct pxadt_hdr {
char magic[6]; /* "PXA-DT" */
uint32_t version; /* PXA-* version */
uint32_t num_dtbs; /* Number of DTBs */
} __attribute__((packed));
struct pxa19xx_hdr {
char magic[8]; /* "PXA-19xx" */
uint32_t version; /* PXA-* version */
uint32_t num_dtbs; /* Number of DTBs */
} __attribute__((packed));
struct pxatable_v1 {
uint32_t cpu_info[2]; /* Some CPU info */
uint32_t offset; /* DTB offset in PXA-* */
uint32_t len; /* DTB size */
} __attribute__((packed));
struct sprd_hdr {
char magic[4]; /* "SPRD" */
uint32_t version; /* SPRD version */
uint32_t num_dtbs; /* Number of DTBs */
} __attribute__((packed));
struct sprdtable_v1 {
uint32_t cpu_info[3]; /* Some CPU info */
uint32_t offset; /* DTB offset in SPRD */
uint32_t len; /* DTB size */
} __attribute__((packed));
struct dtb_blob {
struct fdt_blob {
void *fdt;
uint32_t offset;
uint32_t len;
@ -206,7 +133,7 @@ static void dtb_print(const char *file, bool fstab) {
// Loop through all the dtbs
int dtb_num = 0;
for (int i = 0; i < size; ++i) {
if (memcmp(dtb + i, DTB_MAGIC, 4) == 0) {
if (memcmp(dtb + i, FDT_MAGIC_STR, 4) == 0) {
auto fdt = dtb + i;
if (fstab) {
int node = find_fstab(fdt);
@ -266,19 +193,33 @@ static bool fdt_patch(Iter first, Iter last) {
template <class Table, class Header>
static int dtb_patch(const Header *hdr, const char *in, const char *out) {
map<uint32_t, dtb_blob> dtb_map;
map<uint32_t, fdt_blob> dtb_map;
auto buf = reinterpret_cast<const uint8_t *>(hdr);
auto tables = reinterpret_cast<const Table *>(hdr + 1);
constexpr bool is_dt_table = std::is_same_v<Header, dt_table_header>;
using endian_conv = uint32_t (*)(uint32_t);
endian_conv be_to_le;
endian_conv le_to_be;
if constexpr (is_dt_table) {
be_to_le = fdt32_to_cpu;
le_to_be = cpu_to_fdt32;
} else {
be_to_le = le_to_be = [](uint32_t x) -> auto { return x; };
}
// Collect all dtbs
for (int i = 0; i < hdr->num_dtbs; ++i) {
if (dtb_map.find(tables[i].offset) == dtb_map.end()) {
auto blob = buf + tables[i].offset;
int size = fdt_totalsize(blob);
auto num_dtb = be_to_le(hdr->num_dtbs);
for (int i = 0; i < num_dtb; ++i) {
auto offset = be_to_le(tables[i].offset);
if (dtb_map.count(offset) == 0) {
auto blob = buf + offset;
uint32_t size = fdt_totalsize(blob);
auto fdt = xmalloc(size + 256);
memcpy(fdt, blob, size);
fdt_open_into(fdt, fdt, size + 256);
dtb_map[tables[i].offset] = { fdt, tables[i].offset };
dtb_map[offset] = { fdt, offset };
}
}
if (dtb_map.empty())
@ -292,18 +233,23 @@ static int dtb_patch(const Header *hdr, const char *in, const char *out) {
unlink(in);
int fd = xopen(out, O_RDWR | O_CREAT | O_TRUNC | O_CLOEXEC, 0644);
uint32_t total_size = 0;
// Copy headers and tables
xwrite(fd, buf, dtb_map.begin()->first);
total_size += xwrite(fd, buf, dtb_map.begin()->first);
// mmap rw to patch table values retroactively
auto mmap_sz = lseek(fd, 0, SEEK_CUR);
auto addr = (uint8_t *) xmmap(nullptr, mmap_sz, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
// Guess page size using gcd
auto it = dtb_map.begin();
uint32_t page_size = (it++)->first;
for (; it != dtb_map.end(); ++it)
page_size = binary_gcd(page_size, it->first);
// Guess alignment using gcd
uint32_t align = 1;
if constexpr (!is_dt_table) {
auto it = dtb_map.begin();
align = (it++)->first;
for (; it != dtb_map.end(); ++it)
align = binary_gcd(align, it->first);
}
// Write dtbs
for (auto &val : dtb_map) {
@ -311,18 +257,23 @@ static int dtb_patch(const Header *hdr, const char *in, const char *out) {
auto fdt = val.second.fdt;
fdt_pack(fdt);
int size = fdt_totalsize(fdt);
xwrite(fd, fdt, size);
val.second.len = do_align(size, page_size);
write_zero(fd, align_off(lseek(fd, 0, SEEK_CUR), page_size));
total_size += xwrite(fd, fdt, size);
val.second.len = do_align(size, align);
write_zero(fd, align_off(lseek(fd, 0, SEEK_CUR), align));
// total_size += align_off(lseek(fd, 0, SEEK_CUR), align); /* Not needed */
free(fdt);
}
// Patch tables
// Patch headers
if constexpr (is_dt_table) {
auto hdr_rw = reinterpret_cast<Header *>(addr);
hdr_rw->total_size = le_to_be(total_size);
}
auto tables_rw = reinterpret_cast<Table *>(addr + sizeof(Header));
for (int i = 0; i < hdr->num_dtbs; ++i) {
auto &blob = dtb_map[tables_rw[i].offset];
tables_rw[i].offset = blob.offset;
tables_rw[i].len = blob.len;
for (int i = 0; i < num_dtb; ++i) {
auto &blob = dtb_map[be_to_le(tables_rw[i].offset)];
tables_rw[i].offset = le_to_be(blob.offset);
tables_rw[i].len = le_to_be(blob.len);
}
munmap(addr, mmap_sz);
@ -393,10 +344,19 @@ static int dtb_patch(const char *in, const char *out) {
default:
return 1;
}
} else if (MATCH(DT_TABLE_MAGIC)) {
auto hdr = reinterpret_cast<dt_table_header *>(dtb);
switch (hdr->version) {
case 0:
fprintf(stderr, "DT_TABLE v0\n");
return dtb_patch<dt_table_entry>(hdr, in, out);
default:
return 1;
}
} else {
vector<uint8_t *> fdt_list;
for (int i = 0; i < dtb_sz; ++i) {
if (memcmp(dtb + i, DTB_MAGIC, 4) == 0) {
if (memcmp(dtb + i, FDT_MAGIC_STR, 4) == 0) {
int len = fdt_totalsize(dtb + i);
auto fdt = static_cast<uint8_t *>(xmalloc(len + 256));
memcpy(fdt, dtb + i, len);

104
native/jni/magiskboot/dtb.h Normal file
View File

@ -0,0 +1,104 @@
#pragma once
#include <stdint.h>
#define FDT_MAGIC_STR "\xd0\x0d\xfe\xed"
#define DT_TABLE_MAGIC "\xd7\xb7\xab\x1e"
#define QCDT_MAGIC "QCDT"
#define DTBH_MAGIC "DTBH"
#define PXADT_MAGIC "PXA-DT"
#define PXA19xx_MAGIC "PXA-19xx"
#define SPRD_MAGIC "SPRD"
struct qcdt_hdr {
char magic[4]; /* "QCDT" */
uint32_t version; /* QCDT version */
uint32_t num_dtbs; /* Number of DTBs */
} __attribute__((packed));
struct qctable_v1 {
uint32_t cpu_info[3]; /* Some CPU info */
uint32_t offset; /* DTB offset in QCDT */
uint32_t len; /* DTB size */
} __attribute__((packed));
struct qctable_v2 {
uint32_t cpu_info[4]; /* Some CPU info */
uint32_t offset; /* DTB offset in QCDT */
uint32_t len; /* DTB size */
} __attribute__((packed));
struct qctable_v3 {
uint32_t cpu_info[8]; /* Some CPU info */
uint32_t offset; /* DTB offset in QCDT */
uint32_t len; /* DTB size */
} __attribute__((packed));
struct dtbh_hdr {
char magic[4]; /* "DTBH" */
uint32_t version; /* DTBH version */
uint32_t num_dtbs; /* Number of DTBs */
} __attribute__((packed));
struct bhtable_v2 {
uint32_t cpu_info[5]; /* Some CPU info */
uint32_t offset; /* DTB offset in DTBH */
uint32_t len; /* DTB size */
uint32_t space; /* 0x00000020 */
} __attribute__((packed));
struct pxadt_hdr {
char magic[6]; /* "PXA-DT" */
uint32_t version; /* PXA-* version */
uint32_t num_dtbs; /* Number of DTBs */
} __attribute__((packed));
struct pxa19xx_hdr {
char magic[8]; /* "PXA-19xx" */
uint32_t version; /* PXA-* version */
uint32_t num_dtbs; /* Number of DTBs */
} __attribute__((packed));
struct pxatable_v1 {
uint32_t cpu_info[2]; /* Some CPU info */
uint32_t offset; /* DTB offset in PXA-* */
uint32_t len; /* DTB size */
} __attribute__((packed));
struct sprd_hdr {
char magic[4]; /* "SPRD" */
uint32_t version; /* SPRD version */
uint32_t num_dtbs; /* Number of DTBs */
} __attribute__((packed));
struct sprdtable_v1 {
uint32_t cpu_info[3]; /* Some CPU info */
uint32_t offset; /* DTB offset in SPRD */
uint32_t len; /* DTB size */
} __attribute__((packed));
/* AOSP DTB/DTBO partition layout */
struct dt_table_header {
uint32_t magic; /* DT_TABLE_MAGIC */
uint32_t total_size; /* includes dt_table_header + all dt_table_entry */
uint32_t header_size; /* sizeof(dt_table_header) */
uint32_t dt_entry_size; /* sizeof(dt_table_entry) */
uint32_t num_dtbs; /* number of dt_table_entry */
uint32_t dt_entries_offset; /* offset to the first dt_table_entry */
uint32_t page_size; /* flash page size we assume */
uint32_t version; /* DTBO image version */
} __attribute__((packed));
struct dt_table_entry {
uint32_t len; /* DTB size */
uint32_t offset;
uint32_t id;
uint32_t rev;
uint32_t flags;
uint32_t custom[3];
} __attribute__((packed));

View File

@ -14,6 +14,7 @@
int unpack(const char *image, bool nodecomp = false, bool hdr = false);
void repack(const char* src_img, const char* out_img, bool nocomp = false);
int split_image_dtb(const char *filename);
int hexpatch(const char *image, const char *from, const char *to);
int cpio_commands(int argc, char *argv[]);
int dtb_commands(int argc, char *argv[]);

View File

@ -16,7 +16,7 @@ using namespace std;
static void usage(char *arg0) {
fprintf(stderr,
FULL_VER(MagiskBoot) R"EOF( - Boot Image Modification Tool
FULL_VER(MagiskBoot) R"EOF( - Boot Image Modification Tool
Usage: %s <action> [args...]
@ -84,6 +84,15 @@ Supported actions:
If [OUT] is not specified, it will directly output to <input>
Configure with env variables: KEEPVERITY TWOSTAGEINIT
split <input>
Split image.*-dtb into kernel + kernel_dtb
sha1 <file>
Print the SHA1 checksum for <file>
cleanup
Cleanup the current working directory
compress[=method] <infile> [outfile]
Compress <infile> with [method] (default: gzip), optionally to [outfile]
<infile>/[outfile] can be '-' to be STDIN/STDOUT
@ -102,16 +111,7 @@ Supported actions:
for (auto &it : name2fmt)
fprintf(stderr, "%s ", it.first.data());
fprintf(stderr, R"EOF(
sha1 <file>
Print the SHA1 checksum for <file>
cleanup
Cleanup the current working directory
)EOF");
fprintf(stderr, "\n\n");
exit(1);
}
@ -147,6 +147,8 @@ int main(int argc, char *argv[]) {
printf("%02x", i);
printf("\n");
munmap(buf, size);
} else if (argc > 2 && action == "split") {
return split_image_dtb(argv[2]);
} else if (argc > 2 && action == "unpack") {
int idx = 2;
bool nodecomp = false;

View File

@ -5,54 +5,58 @@
#include "magiskboot.h"
#define MATCH(p) else if (strncmp(s + skip, p, sizeof(p) - 1) == 0) skip += (sizeof(p) - 1)
static int check_verity_pattern(const char *s) {
int skip = 0;
if (s[0] == ',') ++skip;
if (strncmp(s + skip, "verify", 6) == 0)
skip += 6;
else if (strncmp(s + skip, "avb", 3) == 0)
skip += 3;
else
return -1;
int skip = s[0] == ',';
if (0) {}
MATCH("verify");
MATCH("avb");
MATCH("support_scfs");
else return -1;
if (s[skip] == '=') {
while (s[skip] != '\0' && s[skip] != ' ' && s[skip] != '\n' && s[skip] != ',') ++skip;
while (s[skip] != '\0' && s[skip] != ' ' && s[skip] != '\n' && s[skip] != ',')
++skip;
}
return skip;
}
#undef MATCH
#define MATCH(p) else if (strncmp(s, p, sizeof(p) - 1) == 0) return (sizeof(p) - 1)
static int check_encryption_pattern(const char *s) {
static const char *encrypt_list[] = { "forceencrypt", "forcefdeorfbe" };
for (auto enc : encrypt_list) {
int len = strlen(enc);
if (strncmp(s, enc, len) == 0)
return len;
}
return -1;
if (0) {}
MATCH("forceencrypt");
MATCH("forcefdeorfbe");
MATCH("fileencryption");
else return -1;
}
char *patch_verity(const void *buf, uint32_t &size, bool inplace) {
auto src = static_cast<const char *>(buf);
auto dest = (char *)(inplace ? buf : xmalloc(size));
int src_size = size;
bool found = false;
auto patched = (char *)(inplace ? buf : xmalloc(size));
int write = 0;
for (int read = 0; read < src_size; ++read, ++write) {
for (int read = 0; read < src_size;) {
if (int skip; (skip = check_verity_pattern(src + read)) > 0) {
fprintf(stderr, "Found pattern [%.*s]\n", skip, src + read);
size -= skip;
read += skip;
found = true;
} else {
dest[write++] = src[read++];
}
patched[write] = src[read];
}
patched[write] = '\0';
dest[write] = '\0';
if (!found) {
if (!inplace)
free(patched);
free(dest);
return nullptr;
}
return patched;
return dest;
}
void patch_encryption(void *buf, uint32_t &size) {

View File

@ -47,8 +47,11 @@ void magisk_cpio::patch() {
for (auto it = entries.begin(); it != entries.end();) {
auto cur = it++;
bool fstab = (!keepverity || !keepforceencrypt) &&
!str_starts(cur->first, ".backup") &&
str_contains(cur->first, "fstab") && S_ISREG(cur->second->mode);
S_ISREG(cur->second->mode) &&
!str_starts(cur->first, ".backup") &&
!str_contains(cur->first, "twrp") &&
!str_contains(cur->first, "recovery") &&
str_contains(cur->first, "fstab");
if (!keepverity) {
if (fstab) {
fprintf(stderr, "Found fstab file [%s]\n", cur->first.data());
@ -244,7 +247,7 @@ void magisk_cpio::compress() {
uint8_t *data;
size_t len;
auto strm = make_stream(get_encoder(XZ, make_stream<byte_stream>(data, len)));
auto strm = make_stream_fp(get_encoder(XZ, make_unique<byte_stream>(data, len)));
dump(strm.release());
entries.clear();
@ -264,7 +267,7 @@ void magisk_cpio::decompress() {
char *data;
size_t len;
{
auto strm = get_decoder(XZ, make_stream<byte_stream>(data, len));
auto strm = get_decoder(XZ, make_unique<byte_stream>(data, len));
strm->write(it->second->data, it->second->filesize);
}

View File

@ -67,7 +67,7 @@ void hide_unmount(int pid) {
// Unmount dummy skeletons and /sbin links
parse_mnt("/proc/self/mounts", [&](mntent *mentry) {
if (TMPFS_MNT(system) || TMPFS_MNT(vendor) || TMPFS_MNT(sbin))
if (TMPFS_MNT(system) || TMPFS_MNT(vendor) || TMPFS_MNT(sbin) || TMPFS_MNT(product))
targets.emplace_back(mentry->mnt_dir);
return true;
});

View File

@ -183,7 +183,7 @@ bool init_list() {
// Migrate old hide list into database
if (access(LEGACY_LIST, R_OK) == 0) {
file_readline(LEGACY_LIST, [](string_view s) -> bool {
file_readline(true, LEGACY_LIST, [](string_view s) -> bool {
add_list(s.data());
return true;
});

View File

@ -1,4 +1,5 @@
#include "magiskpolicy.h"
#include <magiskpolicy.h>
#include "sepolicy.h"
//#define vprint(fmt, ...) printf(fmt, __VA_ARGS__)

View File

@ -1,459 +1,59 @@
/* magiskpolicy.cpp - Main function for policy patching
*
* Includes all the parsing logic for the policy statements
*/
#include <stdio.h>
#include <limits.h>
#include <vector>
#include <string>
#include <logging.h>
#include <utils.h>
#include <flags.h>
#include <magiskpolicy.h>
#include "sepolicy.h"
#include "magiskpolicy.h"
using namespace std;
static const char *type_msg_1 =
"Type 1:\n"
"\"<rule_name> source_type target_type class perm_set\"\n"
"Rules: allow, deny, auditallow, dontaudit\n";
static const char *type_msg_2 =
"Type 2:\n"
"\"<rule_name> source_type target_type class operation xperm_set\"\n"
"Rules: allowxperm, auditallowxperm, dontauditxperm\n"
"* The only supported operation is ioctl\n"
"* The only supported xperm_set format is range ([low-high])\n";
static const char *type_msg_3 =
"Type 3:\n"
"\"<rule_name> class\"\n"
"Rules: create, permissive, enforcing\n";
static const char *type_msg_4 =
"Type 4:\n"
"\"attradd class attribute\"\n";
static const char *type_msg_5 =
"Type 5:\n"
"\"<rule_name> source_type target_type class default_type\"\n"
"Rules: type_transition, type_change, type_member\n";
static const char *type_msg_6 =
"Type 6:\n"
"\"name_transition source_type target_type class default_type object_name\"\n";
[[noreturn]] static void statements() {
fprintf(stderr,
"One policy statement should be treated as one parameter;\n"
"this means a full policy statement should be enclosed in quotes;\n"
"multiple policy statements can be provided in a single command\n"
"\n"
"The statements has a format of \"<rule_name> [args...]\"\n"
"Multiple types and permissions can be grouped into collections\n"
"wrapped in curly brackets.\n"
"'*' represents a collection containing all valid matches.\n"
"\n"
"Supported policy statements:\n"
"\n"
"%s\n"
"%s\n"
"%s\n"
"%s\n"
"%s\n"
"%s\n"
"Notes:\n"
"* Type 4 - 6 does not support collections\n"
"* Object classes cannot be collections\n"
"* source_type and target_type can also be attributes\n"
"\n"
"Example: allow { s1 s2 } { t1 t2 } class *\n"
"Will be expanded to:\n"
"\n"
"allow s1 t1 class { all permissions }\n"
"allow s1 t2 class { all permissions }\n"
"allow s2 t1 class { all permissions }\n"
"allow s2 t2 class { all permissions }\n"
"\n",
type_msg_1, type_msg_2, type_msg_3, type_msg_4, type_msg_5, type_msg_6);
exit(0);
}
using namespace std::literals;
[[noreturn]] static void usage(char *arg0) {
fprintf(stderr,
FULL_VER(MagiskPolicy) "\n\n"
"Usage: %s [--options...] [policy statements...]\n"
"\n"
"Options:\n"
" --help show help message for policy statements\n"
" --load FILE load policies from FILE\n"
" --load-split load from preloaded sepolicy or compile\n"
" split policies\n"
" --compile-split compile split cil policies\n"
" --save FILE save policies to FILE\n"
" --live directly apply sepolicy live\n"
" --magisk inject built-in rules for a minimal\n"
" Magisk selinux environment\n"
"\n"
"If neither --load or --compile-split is specified, it will load\n"
"from current live policies (" SELINUX_POLICY ")\n"
"\n",
arg0);
FULL_VER(MagiskPolicy) R"EOF(
Usage: %s [--options...] [policy statements...]
Options:
--help show help message for policy statements
--load FILE load policies from FILE
--load-split load from precompiled sepolicy or compile
split policies
--compile-split compile split cil policies
--save FILE save policies to FILE
--live directly apply sepolicy live
--magisk inject built-in rules for a minimal
Magisk selinux environment
--apply FILE apply rules from FILE, read and parsed
line by line as policy statements
If neither --load or --compile-split is specified, it will load
from current live policies (/sys/fs/selinux/policy)
)EOF", arg0);
exit(1);
}
static int parse_bracket(char *tok, char *&stmt, vector<const char *> *vec) {
if (tok == nullptr || tok[0] != '{') {
// Not in a bracket
vec->push_back(tok);
} else {
if (stmt)
stmt[-1] = ' ';
tok = strchr(tok, '{') + 1;
char *end = strchr(tok, '}');
if (end == nullptr) // Bracket not closed
return 1;
*end = '\0';
char *cur;
while ((cur = strtok_r(nullptr, " ", &tok)) != nullptr)
vec->push_back(cur);
stmt = end + 1;
}
return 0;
}
// Pattern 1: action { source } { target } class { permission }
static int parse_pattern_1(int action, const char *action_str, char *stmt) {
int (*action_func)(const char*, const char*, const char*, const char*);
switch (action) {
case 0:
action_func = sepol_allow;
break;
case 1:
action_func = sepol_deny;
break;
case 2:
action_func = sepol_auditallow;
break;
case 3:
action_func = sepol_dontaudit;
break;
default:
return 1;
}
int state = 0;
char *cur, *cls;
vector<const char*> source, target, permission;
while ((cur = strtok_r(nullptr, " ", &stmt)) != nullptr) {
if (cur[0] == '*') cur = ALL;
vector<const char *> *vec;
switch (state) {
case 0:
vec = &source;
break;
case 1:
vec = &target;
break;
case 2:
vec = nullptr;
cls = cur;
break;
case 3:
vec = &permission;
break;
default:
return 1;
}
if (vec && parse_bracket(cur, stmt, vec))
return 1;
++state;
}
if (state != 4 || source.empty() || target.empty() || permission.empty())
return 1;
for (auto src : source)
for (auto tgt : target)
for (auto perm : permission)
if (action_func(src, tgt, cls, perm))
fprintf(stderr, "Error in: %s %s %s %s %s\n", action_str, src, tgt, cls, perm);
return 0;
}
// Pattern 2: action { source } { target } { class } ioctl range
static int parse_pattern_2(int action, const char *action_str, char *stmt) {
int (*action_func)(const char*, const char*, const char*, const char*);
switch (action) {
case 0:
action_func = sepol_allowxperm;
break;
case 1:
action_func = sepol_auditallowxperm;
break;
case 2:
action_func = sepol_dontauditxperm;
break;
default:
return 1;
}
int state = 0;
char *cur, *range;
vector<const char *> source, target, classes;
while ((cur = strtok_r(nullptr, " ", &stmt)) != nullptr) {
if (cur[0] == '*') cur = ALL;
vector<const char *> *vec;
switch (state) {
case 0:
vec = &source;
break;
case 1:
vec = &target;
break;
case 2:
vec = &classes;
break;
case 3:
// Currently only support ioctl
if (strcmp(cur, "ioctl") != 0)
return 1;
vec = nullptr;
break;
case 4:
vec = nullptr;
range = cur;
break;
default:
return 1;
}
if (vec && parse_bracket(cur, stmt, vec))
return 1;
++state;
}
if (state != 5 || source.empty() || target.empty() || classes.empty())
return 1;
for (auto src : source)
for (auto tgt : target)
for (auto cls : classes)
if (action_func(src, tgt, cls, range))
fprintf(stderr, "Error in: %s %s %s %s %s\n", action_str, src, tgt, cls, range);
return 0;
}
// Pattern 3: action { type }
static int parse_pattern_3(int action, const char *action_str, char* stmt) {
int (*action_func)(const char*);
switch (action) {
case 0:
action_func = sepol_create;
break;
case 1:
action_func = sepol_permissive;
break;
case 2:
action_func = sepol_enforce;
break;
default:
return 1;
}
char *cur;
vector<const char *> domains;
while ((cur = strtok_r(nullptr, " {}", &stmt)) != nullptr) {
if (cur[0] == '*') cur = ALL;
domains.push_back(cur);
}
if (domains.empty())
return 1;
for (auto dom : domains)
if (action_func(dom))
fprintf(stderr, "Error in: %s %s\n", action_str, dom);
return 0;
}
// Pattern 4: action { class } { attribute }
static int parse_pattern_4(int action, const char *action_str, char *stmt) {
int (*action_func)(const char*, const char*);
switch (action) {
case 0:
action_func = sepol_attradd;
break;
default:
return 1;
}
int state = 0;
char *cur;
vector<const char *> classes, attribute;
while ((cur = strtok_r(nullptr, " ", &stmt)) != nullptr) {
if (cur[0] == '*') cur = ALL;
vector<const char *> *vec;
switch (state) {
case 0:
vec = &classes;
break;
case 1:
vec = &attribute;
break;
default:
return 1;
}
if (parse_bracket(cur, stmt, vec))
return 1;
++state;
}
if (state != 2 || classes.empty() || attribute.empty())
return 1;
for (auto cls : classes)
for (auto attr : attribute)
if (action_func(cls, attr))
fprintf(stderr, "Error in: %s %s %s\n", action_str, cls, attr);
return 0;
}
// Pattern 5: action source target class default
static int parse_pattern_5(int action, const char *action_str, char *stmt) {
int (*action_func)(const char*, const char*, const char*, const char*);
switch (action) {
case 0:
action_func = sepol_typetrans;
break;
case 1:
action_func = sepol_typechange;
break;
case 2:
action_func = sepol_typemember;
break;
default:
return 1;
}
int state = 0;
char *cur;
char *source, *target, *cls, *def;
while ((cur = strtok_r(nullptr, " ", &stmt)) != nullptr) {
switch(state) {
case 0:
source = cur;
break;
case 1:
target = cur;
break;
case 2:
cls = cur;
break;
case 3:
def = cur;
break;
default:
return 1;
}
++state;
}
if (state < 4) return 1;
if (action_func(source, target, cls, def))
fprintf(stderr, "Error in: %s %s %s %s %s\n", action_str, source, target, cls, def);
return 0;
}
// Pattern 6: action source target class default filename
static int parse_pattern_6(int action, const char *action_str, char *stmt) {
int state = 0;
char *cur;
char *source, *target, *cls, *def, *filename;
while ((cur = strtok_r(nullptr, " ", &stmt)) != nullptr) {
switch(state) {
case 0:
source = cur;
break;
case 1:
target = cur;
break;
case 2:
cls = cur;
break;
case 3:
def = cur;
break;
case 4:
filename = cur;
break;
default:
return 1;
}
++state;
}
if (state < 4) return 1;
if (sepol_nametrans(source, target, cls, def, filename))
fprintf(stderr, "Error in: %s %s %s %s %s %s\n",
action_str, source, target, cls, def, filename);
return 0;
}
#define add_action(name, type, num) \
else if (strcmp(name, action) == 0) { \
if (parse_pattern_##type(num, name, remain)) \
fprintf(stderr, "Syntax error in '%s'\n\n%s\n", orig.c_str(), type_msg_##type); \
}
static void parse_statement(char *statement) {
char *action, *remain;
// strtok will modify the origin string, duplicate the statement for error messages
string orig(statement);
action = strtok_r(statement, " ", &remain);
if (remain == nullptr) remain = &action[strlen(action)];
if (0) {}
add_action("allow", 1, 0)
add_action("deny", 1, 1)
add_action("auditallow", 1, 2)
add_action("dontaudit", 1, 3)
add_action("allowxperm", 2, 0)
add_action("auditallowxperm", 2, 1)
add_action("dontauditxperm", 2, 2)
add_action("create", 3, 0)
add_action("permissive", 3, 1)
add_action("enforce", 3, 2)
add_action("attradd", 4, 0)
add_action("type_transition", 5, 0)
add_action("type_change", 5, 1)
add_action("type_member", 5, 2)
add_action("name_transition", 6, 0)
else { fprintf(stderr, "Unknown statement: '%s'\n\n", orig.c_str()); }
}
int magiskpolicy_main(int argc, char *argv[]) {
cmdline_logging();
const char *outfile = nullptr;
bool magisk = false, live = false;
const char *out_file = nullptr;
const char *rule_file = nullptr;
bool magisk = false;
bool live = false;
if (argc < 2) usage(argv[0]);
int i = 1;
for (; i < argc; ++i) {
// Parse options
if (argv[i][0] == '-' && argv[i][1] == '-') {
if (strcmp(argv[i] + 2, "live") == 0)
auto option = argv[i] + 2;
if (option == "live"sv)
live = true;
else if (strcmp(argv[i] + 2, "magisk") == 0)
else if (option == "magisk"sv)
magisk = true;
else if (strcmp(argv[i] + 2, "load") == 0) {
else if (option == "load"sv) {
if (argv[i + 1] == nullptr)
usage(argv[0]);
if (load_policydb(argv[i + 1])) {
@ -461,23 +61,28 @@ int magiskpolicy_main(int argc, char *argv[]) {
return 1;
}
++i;
} else if (strcmp(argv[i] + 2, "load-split") == 0) {
} else if (option == "load-split"sv) {
if (load_split_cil()) {
fprintf(stderr, "Cannot load split cil\n");
return 1;
}
} else if (strcmp(argv[i] + 2, "compile-split") == 0) {
} else if (option == "compile-split"sv) {
if (compile_split_cil()) {
fprintf(stderr, "Cannot compile split cil\n");
return 1;
}
} else if (strcmp(argv[i] + 2, "save") == 0) {
} else if (option == "save"sv) {
if (argv[i + 1] == nullptr)
usage(argv[0]);
outfile = argv[i + 1];
out_file = argv[i + 1];
++i;
} else if (strcmp(argv[i] + 2, "help") == 0) {
statements();
} else if (option == "apply"sv) {
if (argv[i + 1] == nullptr)
usage(argv[0]);
rule_file = argv[i + 1];
++i;
} else if (option == "help"sv) {
statement_help();
} else {
usage(argv[0]);
}
@ -495,6 +100,9 @@ int magiskpolicy_main(int argc, char *argv[]) {
if (magisk)
sepol_magisk_rules();
if (rule_file)
load_rule_file(rule_file);
for (; i < argc; ++i)
parse_statement(argv[i]);
@ -503,8 +111,8 @@ int magiskpolicy_main(int argc, char *argv[]) {
return 1;
}
if (outfile && dump_policydb(outfile)) {
fprintf(stderr, "Cannot dump policy to %s\n", outfile);
if (out_file && dump_policydb(out_file)) {
fprintf(stderr, "Cannot dump policy to %s\n", out_file);
return 1;
}

View File

@ -8,11 +8,12 @@
#include <utils.h>
#include <logging.h>
#include <stream.h>
#include <magiskpolicy.h>
#include "magiskpolicy.h"
#include "sepolicy.h"
int load_policydb(const char *file) {
LOGD("Load policy from: %s\n", file);
if (magisk_policydb)
destroy_policydb();
@ -101,7 +102,7 @@ static void load_cil(struct cil_db *db, const char *file) {
size_t size;
mmap_ro(file, addr, size);
cil_add_file(db, (char *) file, addr, size);
LOGD("cil_add[%s]\n", file);
LOGD("cil_add [%s]\n", file);
munmap(addr, size);
}
@ -178,7 +179,7 @@ int dump_policydb(const char *file) {
size_t len;
{
auto fp = make_stream<byte_stream>(data, len);
auto fp = make_stream_fp<byte_stream>(data, len);
struct policy_file pf;
policy_file_init(&pf);
pf.type = PF_USE_STDIO;

View File

@ -1,7 +1,7 @@
#include <logging.h>
#include <flags.h>
#include <magiskpolicy.h>
#include "magiskpolicy.h"
#include "sepolicy.h"
static void allowSuClient(const char *target) {

View File

@ -0,0 +1,425 @@
#include <cstring>
#include <vector>
#include <string>
#include <magiskpolicy.h>
#include <logging.h>
#include <utils.h>
using namespace std;
static const char *type_msg_1 =
R"EOF(Type 1:
"<rule_name> source_type target_type class perm_set"
Rules: allow, deny, auditallow, dontaudit
)EOF";
static const char *type_msg_2 =
R"EOF(Type 2:
"<rule_name> source_type target_type class operation xperm_set"
Rules: allowxperm, auditallowxperm, dontauditxperm
* The only supported operation is ioctl
* The only supported xperm_set format is range ([low-high])
)EOF";
static const char *type_msg_3 =
R"EOF(Type 3:
"<rule_name> class"
Rules: create, permissive, enforcing
)EOF";
static const char *type_msg_4 =
R"EOF(Type 4:
"attradd class attribute"
)EOF";
static const char *type_msg_5 =
R"EOF(Type 5:
"<rule_name> source_type target_type class default_type"
Rules: type_transition, type_change, type_member
)EOF";
static const char *type_msg_6 =
R"EOF(Type 6:
"name_transition source_type target_type class default_type object_name"
)EOF";
void statement_help() {
fprintf(stderr,
R"EOF(One policy statement should be treated as one parameter;
this means a full policy statement should be enclosed in quotes.
Multiple policy statements can be provided in a single command.
The statements has a format of "<rule_name> [args...]"
Multiple types and permissions can be grouped into collections
wrapped in curly brackets.
'*' represents a collection containing all valid matches.
Supported policy statements:
%s
%s
%s
%s
%s
%s
Notes:
* Type 4 - 6 does not support collections
* Object classes cannot be collections
* source_type and target_type can also be attributes
Example: allow { s1 s2 } { t1 t2 } class *
Will be expanded to:
allow s1 t1 class { all-permissions }
allow s1 t2 class { all-permissions }
allow s2 t1 class { all-permissions }
allow s2 t2 class { all-permissions }
)EOF", type_msg_1, type_msg_2, type_msg_3, type_msg_4, type_msg_5, type_msg_6);
exit(0);
}
static int parse_bracket(char *tok, char *&stmt, vector<const char *> &vec) {
if (tok == nullptr || tok[0] != '{') {
// Not in a bracket
vec.push_back(tok);
} else {
if (stmt)
stmt[-1] = ' ';
tok = strchr(tok, '{') + 1;
char *end = strchr(tok, '}');
if (end == nullptr) // Bracket not closed
return 1;
*end = '\0';
char *cur;
while ((cur = strtok_r(nullptr, " ", &tok)) != nullptr)
vec.push_back(cur);
stmt = end + 1;
}
return 0;
}
// Pattern 1: action { source } { target } class { permission }
static int parse_pattern_1(int action, const char *action_str, char *stmt) {
int (*action_func)(const char*, const char*, const char*, const char*);
switch (action) {
case 0:
action_func = sepol_allow;
break;
case 1:
action_func = sepol_deny;
break;
case 2:
action_func = sepol_auditallow;
break;
case 3:
action_func = sepol_dontaudit;
break;
default:
return 1;
}
int state = 0;
char *cur, *cls;
vector<const char*> source, target, permission;
while ((cur = strtok_r(nullptr, " ", &stmt)) != nullptr) {
if (cur[0] == '*') cur = ALL;
vector<const char *> *vec;
switch (state) {
case 0:
vec = &source;
break;
case 1:
vec = &target;
break;
case 2:
vec = nullptr;
cls = cur;
break;
case 3:
vec = &permission;
break;
default:
return 1;
}
if (vec && parse_bracket(cur, stmt, *vec))
return 1;
++state;
}
if (state != 4 || source.empty() || target.empty() || permission.empty())
return 1;
for (auto src : source)
for (auto tgt : target)
for (auto perm : permission)
if (action_func(src, tgt, cls, perm))
LOGW("Error in: %s %s %s %s %s\n", action_str, src, tgt, cls, perm);
return 0;
}
// Pattern 2: action { source } { target } { class } ioctl range
static int parse_pattern_2(int action, const char *action_str, char *stmt) {
int (*action_func)(const char*, const char*, const char*, const char*);
switch (action) {
case 0:
action_func = sepol_allowxperm;
break;
case 1:
action_func = sepol_auditallowxperm;
break;
case 2:
action_func = sepol_dontauditxperm;
break;
default:
return 1;
}
int state = 0;
char *cur, *range;
vector<const char *> source, target, classes;
while ((cur = strtok_r(nullptr, " ", &stmt)) != nullptr) {
if (cur[0] == '*') cur = ALL;
vector<const char *> *vec;
switch (state) {
case 0:
vec = &source;
break;
case 1:
vec = &target;
break;
case 2:
vec = &classes;
break;
case 3:
// Currently only support ioctl
if (strcmp(cur, "ioctl") != 0)
return 1;
vec = nullptr;
break;
case 4:
vec = nullptr;
range = cur;
break;
default:
return 1;
}
if (vec && parse_bracket(cur, stmt, *vec))
return 1;
++state;
}
if (state != 5 || source.empty() || target.empty() || classes.empty())
return 1;
for (auto src : source)
for (auto tgt : target)
for (auto cls : classes)
if (action_func(src, tgt, cls, range))
LOGW("Error in: %s %s %s %s %s\n", action_str, src, tgt, cls, range);
return 0;
}
// Pattern 3: action { type }
static int parse_pattern_3(int action, const char *action_str, char* stmt) {
int (*action_func)(const char*);
switch (action) {
case 0:
action_func = sepol_create;
break;
case 1:
action_func = sepol_permissive;
break;
case 2:
action_func = sepol_enforce;
break;
default:
return 1;
}
char *cur;
vector<const char *> domains;
while ((cur = strtok_r(nullptr, " {}", &stmt)) != nullptr) {
if (cur[0] == '*') cur = ALL;
domains.push_back(cur);
}
if (domains.empty())
return 1;
for (auto dom : domains)
if (action_func(dom))
LOGW("Error in: %s %s\n", action_str, dom);
return 0;
}
// Pattern 4: action { class } { attribute }
static int parse_pattern_4(int action, const char *action_str, char *stmt) {
int (*action_func)(const char*, const char*);
switch (action) {
case 0:
action_func = sepol_attradd;
break;
default:
return 1;
}
int state = 0;
char *cur;
vector<const char *> classes, attribute;
while ((cur = strtok_r(nullptr, " ", &stmt)) != nullptr) {
if (cur[0] == '*') cur = ALL;
vector<const char *> *vec;
switch (state) {
case 0:
vec = &classes;
break;
case 1:
vec = &attribute;
break;
default:
return 1;
}
if (parse_bracket(cur, stmt, *vec))
return 1;
++state;
}
if (state != 2 || classes.empty() || attribute.empty())
return 1;
for (auto cls : classes)
for (auto attr : attribute)
if (action_func(cls, attr))
LOGW("Error in: %s %s %s\n", action_str, cls, attr);
return 0;
}
// Pattern 5: action source target class default
static int parse_pattern_5(int action, const char *action_str, char *stmt) {
int (*action_func)(const char*, const char*, const char*, const char*);
switch (action) {
case 0:
action_func = sepol_typetrans;
break;
case 1:
action_func = sepol_typechange;
break;
case 2:
action_func = sepol_typemember;
break;
default:
return 1;
}
int state = 0;
char *cur;
char *source, *target, *cls, *def;
while ((cur = strtok_r(nullptr, " ", &stmt)) != nullptr) {
switch(state) {
case 0:
source = cur;
break;
case 1:
target = cur;
break;
case 2:
cls = cur;
break;
case 3:
def = cur;
break;
default:
return 1;
}
++state;
}
if (state < 4) return 1;
if (action_func(source, target, cls, def))
LOGW("Error in: %s %s %s %s %s\n", action_str, source, target, cls, def);
return 0;
}
// Pattern 6: action source target class default filename
static int parse_pattern_6(int action, const char *action_str, char *stmt) {
int state = 0;
char *cur;
char *source, *target, *cls, *def, *filename;
while ((cur = strtok_r(nullptr, " ", &stmt)) != nullptr) {
switch(state) {
case 0:
source = cur;
break;
case 1:
target = cur;
break;
case 2:
cls = cur;
break;
case 3:
def = cur;
break;
case 4:
filename = cur;
break;
default:
return 1;
}
++state;
}
if (state < 4) return 1;
if (sepol_nametrans(source, target, cls, def, filename))
LOGW("Error in: %s %s %s %s %s %s\n", action_str, source, target, cls, def, filename);
return 0;
}
#define add_action(name, type, num) \
else if (strcmp(name, action) == 0) { \
if (parse_pattern_##type(num, name, remain)) \
LOGW("Syntax error in '%s'\n\n%s\n", statement, type_msg_##type); \
}
void parse_statement(const char *statement) {
char *action, *remain;
// strtok will modify strings, duplicate the statement
string stmt(statement);
action = strtok_r(stmt.data(), " ", &remain);
if (remain == nullptr) {
LOGE("Syntax error in '%s'\n\n", statement);
return;
}
if (0) {}
add_action("allow", 1, 0)
add_action("deny", 1, 1)
add_action("auditallow", 1, 2)
add_action("dontaudit", 1, 3)
add_action("allowxperm", 2, 0)
add_action("auditallowxperm", 2, 1)
add_action("dontauditxperm", 2, 2)
add_action("create", 3, 0)
add_action("permissive", 3, 1)
add_action("enforce", 3, 2)
add_action("attradd", 4, 0)
add_action("type_transition", 5, 0)
add_action("type_change", 5, 1)
add_action("type_member", 5, 2)
add_action("name_transition", 6, 0)
else { LOGW("Unknown statement: '%s'\n\n", statement); }
}
void load_rule_file(const char *file) {
file_readline(true, file, [](string_view line) -> bool {
if (line.empty() || line[0] == '#')
return true;
parse_statement(line.data());
return true;
});
}

View File

@ -1,8 +1,4 @@
/* file.cpp - Contains all files related utilities
*/
#include <sys/sendfile.h>
#include <sys/mman.h>
#include <linux/fs.h>
#include <stdlib.h>
#include <fcntl.h>
@ -359,7 +355,7 @@ void write_zero(int fd, size_t size) {
}
}
void file_readline(const char *file, const function<bool (string_view)> &fn, bool trim) {
void file_readline(bool trim, const char *file, const std::function<bool(std::string_view)> &fn) {
FILE *fp = xfopen(file, "re");
if (fp == nullptr)
return;
@ -384,7 +380,7 @@ void file_readline(const char *file, const function<bool (string_view)> &fn, boo
}
void parse_prop_file(const char *file, const function<bool (string_view, string_view)> &fn) {
file_readline(file, [&](string_view line_view) -> bool {
file_readline(true, file, [&](string_view line_view) -> bool {
char *line = (char *) line_view.data();
if (line[0] == '#')
return true;
@ -393,7 +389,7 @@ void parse_prop_file(const char *file, const function<bool (string_view, string_
return true;
*eql = '\0';
return fn(line, eql + 1);
}, true);
});
}
void parse_mnt(const char *file, const function<bool (mntent*)> &fn) {

View File

@ -1,17 +1,16 @@
#pragma once
#include <sys/mman.h>
#include <sys/stat.h>
#include <mntent.h>
#include <functional>
#include <string_view>
#include "xwrap.h"
#define do_align(p, a) (((p) + (a) - 1) / (a) * (a))
#define align_off(p, a) (do_align(p, a) - (p))
using sFILE = std::unique_ptr<FILE, decltype(&fclose)>;
static inline sFILE make_sFILE(FILE *fp = nullptr) {
return sFILE(fp, fclose);
}
struct file_attr {
struct stat st;
char con[128];
@ -36,12 +35,15 @@ void clone_attr(const char *source, const char *target);
void fd_full_read(int fd, void **buf, size_t *size);
void full_read(const char *filename, void **buf, size_t *size);
void write_zero(int fd, size_t size);
void file_readline(const char *file, const std::function<bool (std::string_view)> &fn, bool trim = false);
void parse_prop_file(const char *file, const std::function
<bool(std::string_view, std::string_view)> &fn);
void file_readline(bool trim, const char *file, const std::function<bool(std::string_view)> &fn);
static inline void file_readline(const char *file,
const std::function<bool(std::string_view)> &fn) {
file_readline(false, file, fn);
}
void parse_prop_file(const char *file,
const std::function<bool(std::string_view, std::string_view)> &fn);
void *__mmap(const char *filename, size_t *size, bool rw);
void frm_rf(int dirfd, std::initializer_list<const char *> excl = std::initializer_list<const char *>());
void frm_rf(int dirfd, std::initializer_list<const char *> excl = {});
void clone_dir(int src, int dest, bool overwrite = true);
void parse_mnt(const char *file, const std::function<bool(mntent*)> &fn);
@ -80,3 +82,22 @@ void mmap_rw(const char *filename, B &buf, L &sz) {
buf = (B) __mmap(filename, &__sz, true);
sz = __sz;
}
using sFILE = std::unique_ptr<FILE, decltype(&fclose)>;
using sDIR = std::unique_ptr<DIR, decltype(&closedir)>;
static inline sDIR open_dir(const char *path) {
return sDIR(opendir(path), closedir);
}
static inline sDIR xopen_dir(const char *path) {
return sDIR(xopendir(path), closedir);
}
static inline sFILE open_file(const char *path, const char *mode) {
return sFILE(fopen(path, mode), fclose);
}
static inline sFILE xopen_file(const char *path, const char *mode) {
return sFILE(xfopen(path, mode), fclose);
}

View File

@ -5,17 +5,6 @@
#include "../files.h"
class stream;
using stream_ptr = std::unique_ptr<stream>;
sFILE make_stream(stream_ptr &&strm);
template <class T, class... Args>
sFILE make_stream(Args &&... args) {
return make_stream(stream_ptr(new T(std::forward<Args>(args)...)));
}
class stream {
public:
virtual int read(void *buf, size_t len);
@ -24,35 +13,22 @@ public:
virtual ~stream() = default;
};
// Delegates all operations to the base FILE pointer
using stream_ptr = std::unique_ptr<stream>;
// Delegates all operations to base stream
class filter_stream : public stream {
public:
filter_stream(sFILE &&fp = make_sFILE()) : fp(std::move(fp)) {}
filter_stream(stream_ptr &&base) : base(std::move(base)) {}
int read(void *buf, size_t len) override;
int write(const void *buf, size_t len) override;
void set_base(sFILE &&f);
template <class T, class... Args >
void set_base(Args&&... args) {
set_base(make_stream<T>(std::forward<Args>(args)...));
}
protected:
sFILE fp;
};
// Handy interface for classes that need custom seek logic
class seekable_stream : public stream {
protected:
size_t _pos = 0;
off_t seek_pos(off_t off, int whence);
virtual size_t end_pos() = 0;
stream_ptr base;
};
// Byte stream that dynamically allocates memory
class byte_stream : public seekable_stream {
class byte_stream : public stream {
public:
byte_stream(uint8_t *&buf, size_t &len);
template <class byte>
@ -64,10 +40,10 @@ public:
private:
uint8_t *&_buf;
size_t &_len;
size_t _pos = 0;
size_t _cap = 0;
void resize(size_t new_pos, bool zero = false);
size_t end_pos() final { return _len; }
};
// File stream but does not close the file descriptor at any time
@ -81,3 +57,28 @@ public:
private:
int fd;
};
/* ****************************************
* Bridge between stream class and C stdio
* ****************************************/
// sFILE -> stream_ptr
class fp_stream final : public stream {
public:
fp_stream(FILE *fp = nullptr) : fp(fp, fclose) {}
fp_stream(sFILE &&fp) : fp(std::move(fp)) {}
int read(void *buf, size_t len) override;
int write(const void *buf, size_t len) override;
off_t seek(off_t off, int whence) override;
private:
sFILE fp;
};
// stream_ptr -> sFILE
sFILE make_stream_fp(stream_ptr &&strm);
template <class T, class... Args>
sFILE make_stream_fp(Args &&... args) {
return make_stream_fp(stream_ptr(new T(std::forward<Args>(args)...)));
}

View File

@ -1,14 +1,5 @@
#pragma once
#include <sys/socket.h>
#include <sys/stat.h>
#include <sys/mman.h>
#include <stdio.h>
#include <dirent.h>
#include <pthread.h>
#include <poll.h>
#include <mntent.h>
#include "../missing.h"
#include "../xwrap.h"
#include "../files.h"

View File

@ -1,5 +1,6 @@
#pragma once
#include <pthread.h>
#include <string>
#include <functional>
#include <string_view>

View File

@ -55,61 +55,37 @@ static int __setcon(const char *ctx) {
static int __getfilecon(const char *path, char **ctx) {
char buf[1024];
int rc = syscall(__NR_getxattr, path, XATTR_NAME_SELINUX, buf, sizeof(buf) - 1);
if (rc < 0) {
errno = -rc;
return -1;
}
*ctx = strdup(buf);
if (rc >= 0)
*ctx = strdup(buf);
return rc;
}
static int __lgetfilecon(const char *path, char **ctx) {
char buf[1024];
int rc = syscall(__NR_lgetxattr, path, XATTR_NAME_SELINUX, buf, sizeof(buf) - 1);
if (rc < 0) {
errno = -rc;
return -1;
}
*ctx = strdup(buf);
if (rc >= 0)
*ctx = strdup(buf);
return rc;
}
static int __fgetfilecon(int fd, char **ctx) {
char buf[1024];
int rc = syscall(__NR_fgetxattr, fd, XATTR_NAME_SELINUX, buf, sizeof(buf) - 1);
if (rc < 0) {
errno = -rc;
return -1;
}
*ctx = strdup(buf);
if (rc >= 0)
*ctx = strdup(buf);
return rc;
}
static int __setfilecon(const char *path, const char *ctx) {
int rc = syscall(__NR_setxattr, path, XATTR_NAME_SELINUX, ctx, strlen(ctx) + 1, 0);
if (rc) {
errno = -rc;
return -1;
}
return 0;
return syscall(__NR_setxattr, path, XATTR_NAME_SELINUX, ctx, strlen(ctx) + 1, 0);
}
static int __lsetfilecon(const char *path, const char *ctx) {
int rc = syscall(__NR_lsetxattr, path, XATTR_NAME_SELINUX, ctx, strlen(ctx) + 1, 0);
if (rc) {
errno = -rc;
return -1;
}
return 0;
return syscall(__NR_lsetxattr, path, XATTR_NAME_SELINUX, ctx, strlen(ctx) + 1, 0);
}
static int __fsetfilecon(int fd, const char *ctx) {
int rc = syscall(__NR_fsetxattr, fd, XATTR_NAME_SELINUX, ctx, strlen(ctx) + 1, 0);
if (rc) {
errno = -rc;
return -1;
}
return 0;
return syscall(__NR_fsetxattr, fd, XATTR_NAME_SELINUX, ctx, strlen(ctx) + 1, 0);
}
// Function pointers
@ -231,11 +207,10 @@ void restore_rootcon() {
setfilecon(MIRRDIR, ROOT_CON);
setfilecon(BLOCKDIR, ROOT_CON);
struct dirent *entry;
DIR *dir = xopendir("/sbin");
int dfd = dirfd(dir);
auto dir = xopen_dir("/sbin");
int dfd = dirfd(dir.get());
while ((entry = xreaddir(dir))) {
for (dirent *entry; (entry = xreaddir(dir.get()));) {
if (entry->d_name == "."sv || entry->d_name == ".."sv)
continue;
setfilecon_at(dfd, entry->d_name, ROOT_CON);
@ -244,6 +219,4 @@ void restore_rootcon() {
setfilecon("/sbin/magisk.bin", MAGISK_CON);
setfilecon("/sbin/magisk", MAGISK_CON);
setfilecon("/sbin/magiskinit", MAGISK_CON);
closedir(dir);
}

View File

@ -23,7 +23,7 @@ static int strm_close(void *v) {
return 0;
}
sFILE make_stream(stream_ptr &&strm) {
sFILE make_stream_fp(stream_ptr &&strm) {
sFILE fp(funopen(strm.release(), strm_read, strm_write, strm_seek, strm_close), fclose);
setbuf(fp.get(), nullptr);
return fp;
@ -44,29 +44,24 @@ off_t stream::seek(off_t off, int whence) {
return -1;
}
int filter_stream::read(void *buf, size_t len) {
int fp_stream::read(void *buf, size_t len) {
return fread(buf, 1, len, fp.get());
}
int filter_stream::write(const void *buf, size_t len) {
int fp_stream::write(const void *buf, size_t len) {
return fwrite(buf, 1, len, fp.get());
}
void filter_stream::set_base(sFILE &&f) {
fp = std::move(f);
off_t fp_stream::seek(off_t off, int whence) {
return fseek(fp.get(), off, whence);
}
off_t seekable_stream::seek_pos(off_t off, int whence) {
switch (whence) {
case SEEK_CUR:
return _pos + off;
case SEEK_END:
return end_pos() + off;
case SEEK_SET:
return off;
default:
return -1;
}
int filter_stream::read(void *buf, size_t len) {
return base->read(buf, len);
}
int filter_stream::write(const void *buf, size_t len) {
return base->write(buf, len);
}
byte_stream::byte_stream(uint8_t *&buf, size_t &len) : _buf(buf), _len(len) {
@ -89,9 +84,20 @@ int byte_stream::write(const void *buf, size_t len) {
}
off_t byte_stream::seek(off_t off, int whence) {
off_t np = seek_pos(off, whence);
if (np < 0)
return -1;
off_t np;
switch (whence) {
case SEEK_CUR:
np = _pos + off;
break;
case SEEK_END:
np = _len + off;
break;
case SEEK_SET:
np = off;
break;
default:
return -1;
}
resize(np, true);
_pos = np;
return np;

View File

@ -1,17 +1,6 @@
/* xwrap.cpp - wrappers around existing library functions.
*
* Functions with the x prefix are wrappers that either succeed or log the
* error message. They usually have the same arguments and return value
* as the function they wrap.
*
*/
#include <sched.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <dirent.h>
#include <errno.h>
#include <pthread.h>
#include <sys/socket.h>

View File

@ -1,5 +1,10 @@
#pragma once
#include <dirent.h>
#include <stdio.h>
#include <poll.h>
#include <fcntl.h>
FILE *xfopen(const char *pathname, const char *mode);
FILE *xfdopen(int fd, const char *mode);
int xopen(const char *pathname, int flags);

View File

@ -70,9 +70,8 @@ main() {
remove_system_su
find_manager_apk
patch_boot_image
install_magisk
cd /
# Cleanups
$BOOTMODE || recovery_cleanup
rm -rf $TMPDIR

View File

@ -13,20 +13,15 @@
#
# File name Type Description
#
# boot_patch.sh script A script to patch boot. Expect path to boot image as parameter.
# boot_patch.sh script A script to patch boot image for Magisk.
# (this file) The script will use binaries and files in its same directory
# to complete the patching process
# util_functions.sh script A script which hosts all functions requires for this script
# util_functions.sh script A script which hosts all functions required for this script
# to work properly
# magiskinit binary The binary to replace /init, which has the magisk binary embedded
# magiskboot binary A tool to unpack boot image, decompress ramdisk, extract ramdisk,
# and patch the ramdisk for Magisk support
# chromeos folder This folder should store all the utilities and keys to sign
# (optional) a chromeos device. Used for Pixel C
#
# If the script is not running as root, then the input boot image should be a stock image
# or have a backup included in ramdisk internally, since we cannot access the stock boot
# image placed under /data we've created when previously installed
# magiskinit binary The binary to replace /init; magisk binary embedded
# magiskboot binary A tool to manipulate boot images
# chromeos folder This folder includes all the utilities and keys to sign
# (optional) chromeos boot images. Currently only used for Pixel C
#
##########################################################################################
##########################################################################################
@ -104,10 +99,8 @@ fi
case $((STATUS & 3)) in
0 ) # Stock boot
ui_print "- Stock boot image detected"
ui_print "- Backing up stock boot image"
SHA1=`./magiskboot sha1 "$BOOTIMAGE" 2>/dev/null`
STOCKDUMP=stock_boot_${SHA1}.img.gz
./magiskboot compress "$BOOTIMAGE" $STOCKDUMP
cat $BOOTIMAGE > stock_boot.img
cp -af ramdisk.cpio ramdisk.cpio.orig 2>/dev/null
;;
1 ) # Magisk patched
@ -158,7 +151,7 @@ rm -f ramdisk.cpio.orig config
##########################################################################################
for dt in dtb kernel_dtb extra recovery_dtbo; do
[ -f $dt ] && ./magiskboot dtb $dt patch && ui_print "- Patching fstab in $dt"
[ -f $dt ] && ./magiskboot dtb $dt patch && ui_print "- Patch fstab in $dt"
done
if [ -f kernel ]; then

View File

@ -53,7 +53,7 @@ ui_print "- Target image: $BOOTIMAGE"
# Detect version and architecture
api_level_arch_detect
[ $API -lt 17 ] && abort "! Magisk is only for Android 4.2 and above"
[ $API -lt 17 ] && abort "! Magisk only support Android 4.2 and above"
ui_print "- Device platform: $ARCH"
@ -108,9 +108,8 @@ $BOOTMODE || recovery_actions
# Boot/DTBO Patching
##########################################################################################
patch_boot_image
install_magisk
cd /
# Cleanups
$BOOTMODE || recovery_cleanup
rm -rf $TMPDIR

View File

@ -15,6 +15,7 @@ TMPDIR=/dev/tmp
INSTALLER=$TMPDIR/install
CHROMEDIR=$INSTALLER/chromeos
PERSISTDIR=/sbin/.magisk/mirror/persist
# Default permissions
umask 022
@ -50,6 +51,7 @@ chmod -R 755 $MAGISKBIN
check_data
$DATA_DE || abort "! Cannot access /data, please uninstall with Magisk Manager"
$BOOTMODE || recovery_actions
run_migrations
##########################################################################################
# Uninstall
@ -57,7 +59,6 @@ $BOOTMODE || recovery_actions
get_flags
find_boot_image
find_dtbo_image
[ -e $BOOTIMAGE ] || abort "! Unable to detect boot image"
ui_print "- Found target image: $BOOTIMAGE"
@ -96,16 +97,18 @@ case $((STATUS & 3)) in
1 ) # Magisk patched
ui_print "- Magisk patched image detected"
# Find SHA1 of stock boot image
[ -z $SHA1 ] && SHA1=`./magiskboot cpio ramdisk.cpio sha1 2>/dev/null`
STOCKBOOT=/data/stock_boot_${SHA1}.img.gz
STOCKDTBO=/data/stock_dtbo.img.gz
if [ -f $STOCKBOOT ]; then
SHA1=`./magiskboot cpio ramdisk.cpio sha1 2>/dev/null`
BACKUPDIR=/data/magisk_backup_$SHA1
if [ -d $BACKUPDIR ]; then
ui_print "- Restoring stock boot image"
flash_image $STOCKBOOT $BOOTIMAGE
if [ -f $STOCKDTBO -a -b "$DTBOIMAGE" ]; then
ui_print "- Restoring stock dtbo image"
flash_image $STOCKDTBO $DTBOIMAGE
fi
flash_image $BACKUPDIR/boot.img.gz $BOOTIMAGE
for name in dtb dtbo; do
[ -f $BACKUPDIR/${name}.img.gz ] || continue
IMAGE=`find_block $name$SLOT`
[ -z $IMAGE ] && continue
ui_print "- Restoring stock $name image"
flash_image $BACKUPDIR/${name}.img.gz $IMAGE
done
else
ui_print "! Boot image backup unavailable"
ui_print "- Restoring ramdisk with internal backup"
@ -128,9 +131,10 @@ case $((STATUS & 3)) in
esac
ui_print "- Removing Magisk files"
rm -rf /cache/*magisk* /cache/unblock /data/*magisk* /data/cache/*magisk* /data/property/*magisk* \
/data/Magisk.apk /data/busybox /data/custom_ramdisk_patch.sh /data/adb/*magisk* \
/data/adb/post-fs-data.d /data/adb/service.d /data/adb/modules* 2>/dev/null
rm -rf \
/cache/*magisk* /cache/unblock /data/*magisk* /data/cache/*magisk* /data/property/*magisk* \
/data/Magisk.apk /data/busybox /data/custom_ramdisk_patch.sh /data/adb/*magisk* \
/data/adb/post-fs-data.d /data/adb/service.d /data/adb/modules* $PERSISTDIR/magisk 2>/dev/null
if [ -f /system/addon.d/99-magisk.sh ]; then
mount -o rw,remount /system

View File

@ -1,12 +1,15 @@
#!/sbin/sh
TMPDIR=/dev/tmp
MOUNTPATH=/dev/magisk_img
#################
# Initialization
#################
# Default permissions
umask 022
# Initial cleanup
# Global vars
TMPDIR=/dev/tmp
PERSISTDIR=/sbin/.magisk/mirror/persist
rm -rf $TMPDIR 2>/dev/null
mkdir -p $TMPDIR
@ -14,20 +17,33 @@ mkdir -p $TMPDIR
ui_print() { echo "$1"; }
require_new_magisk() {
ui_print "***********************************"
ui_print " Please install the latest Magisk! "
ui_print "***********************************"
ui_print "*******************************"
ui_print " Please install Magisk v19.0+! "
ui_print "*******************************"
exit 1
}
imageless_magisk() {
[ $MAGISK_VER_CODE -gt 18100 ]
is_legacy_script() {
unzip -l "$ZIPFILE" install.sh | grep -q install.sh
return $?
}
##########################################################################################
print_modname() {
local len
len=`echo -n $MODNAME | wc -c`
len=$((len + 2))
local pounds=`printf "%${len}s" | tr ' ' '*'`
ui_print "$pounds"
ui_print " $MODNAME "
ui_print "$pounds"
ui_print "*******************"
ui_print " Powered by Magisk "
ui_print "*******************"
}
##############
# Environment
##########################################################################################
##############
OUTFD=$2
ZIPFILE=$3
@ -35,12 +51,9 @@ ZIPFILE=$3
mount /data 2>/dev/null
# Load utility functions
if [ -f /data/adb/magisk/util_functions.sh ]; then
. /data/adb/magisk/util_functions.sh
NVBASE=/data/adb
else
require_new_magisk
fi
[ -f /data/adb/magisk/util_functions.sh ] || require_new_magisk
. /data/adb/magisk/util_functions.sh
[ $MAGISK_VER_CODE -gt 18100 ] || require_new_magisk
# Preperation for flashable zips
setup_flashable
@ -54,98 +67,107 @@ api_level_arch_detect
# Setup busybox and binaries
$BOOTMODE && boot_actions || recovery_actions
##########################################################################################
##############
# Preparation
##########################################################################################
##############
# Extract common files
unzip -oj "$ZIPFILE" module.prop install.sh uninstall.sh 'common/*' -d $TMPDIR >&2
[ ! -f $TMPDIR/install.sh ] && abort "! Unable to extract zip file!"
# Load install script
. $TMPDIR/install.sh
if imageless_magisk; then
$BOOTMODE && MODDIRNAME=modules_update || MODDIRNAME=modules
MODULEROOT=$NVBASE/$MODDIRNAME
else
$BOOTMODE && IMGNAME=magisk_merge.img || IMGNAME=magisk.img
IMG=$NVBASE/$IMGNAME
request_zip_size_check "$ZIPFILE"
mount_magisk_img
MODULEROOT=$MOUNTPATH
fi
# Extract prop file
unzip -o "$ZIPFILE" module.prop -d $TMPDIR >&2
[ ! -f $TMPDIR/module.prop ] && abort "! Unable to extract zip file!"
$BOOTMODE && MODDIRNAME=modules_update || MODDIRNAME=modules
MODULEROOT=$NVBASE/$MODDIRNAME
MODID=`grep_prop id $TMPDIR/module.prop`
MODPATH=$MODULEROOT/$MODID
print_modname
ui_print "******************************"
ui_print "Powered by Magisk (@topjohnwu)"
ui_print "******************************"
##########################################################################################
# Install
##########################################################################################
MODNAME=`grep_prop name $TMPDIR/module.prop`
# Create mod paths
rm -rf $MODPATH 2>/dev/null
mkdir -p $MODPATH
on_install
##########
# Install
##########
# Remove placeholder
rm -f $MODPATH/system/placeholder 2>/dev/null
if is_legacy_script; then
unzip -oj "$ZIPFILE" module.prop install.sh uninstall.sh 'common/*' -d $TMPDIR >&2
# Custom uninstaller
[ -f $TMPDIR/uninstall.sh ] && cp -af $TMPDIR/uninstall.sh $MODPATH/uninstall.sh
# Load install script
. $TMPDIR/install.sh
# Auto Mount
if imageless_magisk; then
# Callbacks
print_modname
on_install
# Custom uninstaller
[ -f $TMPDIR/uninstall.sh ] && cp -af $TMPDIR/uninstall.sh $MODPATH/uninstall.sh
# Skip mount
$SKIPMOUNT && touch $MODPATH/skip_mount
# prop file
$PROPFILE && cp -af $TMPDIR/system.prop $MODPATH/system.prop
# Module info
cp -af $TMPDIR/module.prop $MODPATH/module.prop
# post-fs-data scripts
$POSTFSDATA && cp -af $TMPDIR/post-fs-data.sh $MODPATH/post-fs-data.sh
# service scripts
$LATESTARTSERVICE && cp -af $TMPDIR/service.sh $MODPATH/service.sh
ui_print "- Setting permissions"
set_permissions
else
$SKIPMOUNT || touch $MODPATH/auto_mount
fi
print_modname
# prop files
$PROPFILE && cp -af $TMPDIR/system.prop $MODPATH/system.prop
unzip -o "$ZIPFILE" customize.sh -d $MODPATH >&2
# Module info
cp -af $TMPDIR/module.prop $MODPATH/module.prop
if $BOOTMODE; then
# Update info for Magisk Manager
if imageless_magisk; then
mktouch $NVBASE/modules/$MODID/update
cp -af $TMPDIR/module.prop $NVBASE/modules/$MODID/module.prop
else
mktouch /sbin/.magisk/img/$MODID/update
cp -af $TMPDIR/module.prop /sbin/.magisk/img/$MODID/module.prop
if ! grep -q '^SKIPUNZIP=1$' $MODPATH/customize.sh 2>/dev/null; then
ui_print "- Extracting module files"
unzip -o "$ZIPFILE" -x 'META-INF/*' -d $MODPATH >&2
# Default permissions
set_perm_recursive $MODPATH 0 0 0755 0644
fi
# Load customization script
[ -f $MODPATH/customize.sh ] && . $MODPATH/customize.sh
fi
# post-fs-data mode scripts
$POSTFSDATA && cp -af $TMPDIR/post-fs-data.sh $MODPATH/post-fs-data.sh
# service mode scripts
$LATESTARTSERVICE && cp -af $TMPDIR/service.sh $MODPATH/service.sh
# Handle replace folders
for TARGET in $REPLACE; do
ui_print "- Replace target: $TARGET"
mktouch $MODPATH$TARGET/.replace
done
ui_print "- Setting permissions"
set_permissions
if $BOOTMODE; then
# Update info for Magisk Manager
mktouch $NVBASE/modules/$MODID/update
cp -af $MODPATH/module.prop $NVBASE/modules/$MODID/module.prop
fi
##########################################################################################
# Copy over custom sepolicy rules
if [ -f $MODPATH/sepolicy.rule -a -e $PERSISTDIR ]; then
ui_print "- Installing custom sepolicy patch"
PERSISTMOD=$PERSISTDIR/magisk/$MODID
mkdir -p $PERSISTMOD
cp -af $MODPATH/sepolicy.rule $PERSISTMOD/sepolicy.rule
fi
# Remove stuffs that don't belong to modules
rm -rf \
$MODPATH/system/placeholder $MODPATH/customize.sh \
$MODPATH/README.md $MODPATH/.git* 2>/dev/null
##############
# Finalizing
##########################################################################################
##############
cd /
imageless_magisk || unmount_magisk_img
$BOOTMODE || recovery_cleanup
rm -rf $TMPDIR $MOUNTPATH
rm -rf $TMPDIR
ui_print "- Done"
exit 0

View File

@ -5,27 +5,8 @@
#
#########################################
##########
# Presets
##########
#MAGISK_VERSION_STUB
# Detect whether in boot mode
[ -z $BOOTMODE ] && BOOTMODE=false
$BOOTMODE || ps | grep zygote | grep -qv grep && BOOTMODE=true
$BOOTMODE || ps -A 2>/dev/null | grep zygote | grep -qv grep && BOOTMODE=true
# Presets
MAGISKTMP=/sbin/.magisk
NVBASE=/data/adb
[ -z $TMPDIR ] && TMPDIR=/dev/tmp
# Bootsigner related stuff
BOOTSIGNERCLASS=a.a
BOOTSIGNER="/system/bin/dalvikvm -Xnodex2oat -Xnoimage-dex2oat -cp \$APK \$BOOTSIGNERCLASS"
BOOTSIGNED=false
###################
# Helper Functions
###################
@ -128,15 +109,15 @@ recovery_actions() {
}
recovery_cleanup() {
ui_print "- Unmounting partitions"
umount -l /system 2>/dev/null
umount -l /system_root 2>/dev/null
umount -l /vendor 2>/dev/null
umount -l /dev/random 2>/dev/null
export PATH=$OLD_PATH
[ -z $OLD_LD_LIB ] || export LD_LIBRARY_PATH=$OLD_LD_LIB
[ -z $OLD_LD_PRE ] || export LD_PRELOAD=$OLD_LD_PRE
[ -z $OLD_LD_CFG ] || export LD_CONFIG_FILE=$OLD_LD_CFG
ui_print "- Unmounting partitions"
umount -l /system_root 2>/dev/null
umount -l /system 2>/dev/null
umount -l /vendor 2>/dev/null
umount -l /dev/random 2>/dev/null
}
#######################
@ -165,19 +146,29 @@ find_block() {
return 1
}
mount_part() {
$BOOTMODE && return
# mount_name <partname> <mountpoint> <flag>
mount_name() {
local PART=$1
local POINT=/${PART}
local POINT=$2
local FLAG=$3
[ -L $POINT ] && rm -f $POINT
mkdir $POINT 2>/dev/null
mkdir -p $POINT 2>/dev/null
is_mounted $POINT && return
ui_print "- Mounting $PART"
mount -o ro $POINT 2>/dev/null
ui_print "- Mounting $POINT"
# First try mounting with fstab
mount $FLAG $POINT 2>/dev/null
if ! is_mounted $POINT; then
local BLOCK=`find_block $PART$SLOT`
mount -o ro $BLOCK $POINT
local BLOCK=`find_block $PART`
mount $FLAG $BLOCK $POINT
fi
}
mount_ro_ensure() {
# We handle ro partitions only in recovery
$BOOTMODE && return
local PART=$1$SLOT
local POINT=/$1
mount_name $PART $POINT '-o ro'
is_mounted $POINT || abort "! Cannot mount $POINT"
}
@ -190,7 +181,8 @@ mount_partitions() {
fi
[ -z $SLOT ] || ui_print "- Current boot slot: $SLOT"
mount_part system
# Mount ro partitions
mount_ro_ensure system
if [ -f /system/init.rc ]; then
SYSTEM_ROOT=true
[ -L /system_root ] && rm -f /system_root
@ -201,8 +193,20 @@ mount_partitions() {
grep ' / ' /proc/mounts | grep -qv 'rootfs' || grep -q ' /system_root ' /proc/mounts \
&& SYSTEM_ROOT=true || SYSTEM_ROOT=false
fi
[ -L /system/vendor ] && mount_part vendor
[ -L /system/vendor ] && mount_ro_ensure vendor
$SYSTEM_ROOT && ui_print "- Device is system-as-root"
# Mount persist partition in recovery
if ! $BOOTMODE && [ ! -z $PERSISTDIR ]; then
# Try to mount persist
PERSISTDIR=/persist
mount_name persist /persist
if ! is_mounted /persist; then
# Fallback to cache
mount_name cache /cache
is_mounted /cache && PERSISTDIR=/cache || PERSISTDIR=
fi
fi
}
get_flags() {
@ -272,30 +276,29 @@ flash_image() {
return 0
}
find_dtbo_image() {
DTBOIMAGE=`find_block dtbo$SLOT`
}
patch_dtbo_image() {
find_dtbo_image
if [ ! -z $DTBOIMAGE ]; then
ui_print "- DTBO image: $DTBOIMAGE"
local PATCHED=$TMPDIR/dtbo
if $MAGISKBIN/magiskboot dtb $DTBOIMAGE patch $PATCHED; then
ui_print "- Backing up stock DTBO image"
$MAGISKBIN/magiskboot compress $DTBOIMAGE $MAGISKBIN/stock_dtbo.img.gz
ui_print "- Patching DTBO to remove avb-verity"
cat $PATCHED /dev/zero > $DTBOIMAGE
rm -f $PATCHED
return 0
patch_dtb_partitions() {
local result=1
cd $MAGISKBIN
for name in dtb dtbo; do
local IMAGE=`find_block $name$SLOT`
if [ ! -z $IMAGE ]; then
ui_print "- $name image: $IMAGE"
if ./magiskboot dtb $IMAGE patch dt.patched; then
result=0
ui_print "- Backing up stock $name image"
cat $IMAGE > stock_${name}.img
ui_print "- Flashing patched $name"
cat dt.patched /dev/zero > $IMAGE
rm -f dt.patched
fi
fi
fi
return 1
done
cd /
return $result
}
patch_boot_image() {
# Common installation script for flash_script.sh (updater-script) and addon.d.sh
SOURCEDMODE=true
# Common installation script for flash_script.sh and addon.d.sh
install_magisk() {
cd $MAGISKBIN
eval $BOOTSIGNER -verify < $BOOTIMAGE && BOOTSIGNED=true
@ -304,6 +307,7 @@ patch_boot_image() {
$IS64BIT && mv -f magiskinit64 magiskinit 2>/dev/null || rm -f magiskinit64
# Source the boot patcher
SOURCEDMODE=true
. ./boot_patch.sh "$BOOTIMAGE"
ui_print "- Flashing new boot image"
@ -318,18 +322,8 @@ patch_boot_image() {
./magiskboot cleanup
rm -f new-boot.img
if [ -f stock_boot* ]; then
rm -f /data/stock_boot* 2>/dev/null
$DATA && mv stock_boot* /data
fi
# Patch DTBO together with boot image
$KEEPVERITY || patch_dtbo_image
if [ -f stock_dtbo* ]; then
rm -f /data/stock_dtbo* 2>/dev/null
$DATA && mv stock_dtbo* /data
fi
patch_dtb_partitions
run_migrations
}
sign_chromeos() {
@ -411,6 +405,41 @@ find_manager_apk() {
[ -f $APK ] || ui_print "! Unable to detect Magisk Manager APK for BootSigner"
}
run_migrations() {
local LOCSHA1
local TARGET
# Legacy app installation
local BACKUP=/data/adb/magisk/stock_boot*.gz
if [ -f $BACKUP ]; then
cp $BACKUP /data
rm -f $BACKUP
fi
# Legacy backup
for gz in /data/stock_boot*.gz; do
[ -f $gz ] || break
LOCSHA1=`basename $gz | sed -e 's/stock_boot_//' -e 's/.img.gz//'`
[ -z $LOCSHA1 ] && break
mkdir /data/magisk_backup_${LOCSHA1} 2>/dev/null
mv $gz /data/magisk_backup_${LOCSHA1}/boot.img.gz
done
# Stock backups
LOCSHA1=$SHA1
for name in boot dtb dtbo; do
BACKUP=/data/adb/magisk/stock_${name}.img
[ -f $BACKUP ] || continue
if [ $name = 'boot' ]; then
LOCSHA1=`$MAGISKBIN/magiskboot sha1 $BACKUP`
mkdir /data/magisk_backup_${LOCSHA1} 2>/dev/null
fi
TARGET=/data/magisk_backup_${LOCSHA1}/${name}.img
cp $BACKUP $TARGET
rm -f $BACKUP
gzip -9f $TARGET
done
}
#################
# Module Related
#################
@ -446,27 +475,24 @@ request_zip_size_check() {
reqSizeM=`unzip -l "$1" | tail -n 1 | awk '{ print int(($1 - 1) / 1048576 + 1) }'`
}
##################################
# Backwards Compatibile Functions
##################################
get_outfd() { setup_flashable; }
mount_magisk_img() {
$BOOTMODE && MODULE_BASE=modules_update || MODULE_BASE=modules
MODULEPATH=$NVBASE/$MODULE_BASE
mkdir -p $MODULEPATH 2>/dev/null
ln -s $MODULEPATH $MOUNTPATH
}
unmount_magisk_img() {
rm -f $MOUNTPATH 2>/dev/null
}
boot_actions() { return; }
########
# Setup
########
##########
# Presets
##########
# Detect whether in boot mode
[ -z $BOOTMODE ] && ps | grep zygote | grep -qv grep && BOOTMODE=true
[ -z $BOOTMODE ] && ps -A 2>/dev/null | grep zygote | grep -qv grep && BOOTMODE=true
[ -z $BOOTMODE ] && BOOTMODE=false
MAGISKTMP=/sbin/.magisk
NVBASE=/data/adb
[ -z $TMPDIR ] && TMPDIR=/dev/tmp
# Bootsigner related stuff
BOOTSIGNERCLASS=a.a
BOOTSIGNER="/system/bin/dalvikvm -Xnodex2oat -Xnoimage-dex2oat -cp \$APK \$BOOTSIGNERCLASS"
BOOTSIGNED=false
resolve_vars

View File

@ -34,7 +34,7 @@ repositories {
dependencies {
implementation fileTree(include: ['*.jar'], dir: 'libs')
def bcVer = '1.63'
def bcVer = '1.64'
api "org.bouncycastle:bcprov-jdk15on:${bcVer}"
api "org.bouncycastle:bcpkix-jdk15on:${bcVer}"
}

View File

@ -1,5 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="upgrade_msg">Mettre à jour vers la version complête de Magisk Manager pour finir l\'installation. Télécharger et installer?</string>
<string name="no_internet_msg">Veuillez vous connecter à Internet! Une mise à niveau complête vers le Gestionnaire Magisk est requise.</string>
<string name="upgrade_msg">Une mise à niveau de Magisk Manager en version complète est nécessaire afin de terminer linstallation. Souhaitezvous procéder à son téléchargement et son installation?</string>
<string name="no_internet_msg">Veuillez vous connecter à Internet! Une mise à niveau complète de Magisk Manager est requise.</string>
<string name="dling">Téléchargement en cours</string>
</resources>

View File

@ -1,4 +1,5 @@
<resources>
<string name="upgrade_msg">需要升級到完整版 Magisk Manager。是否下載並安裝</string>
<string name="no_internet_msg">請連上網路!升級到完整版 Magisk Manager 是必須的。</string>
<string name="dling">下載中</string>
</resources>