mirror of
https://github.com/topjohnwu/Magisk.git
synced 2025-08-14 15:27:25 +00:00
Compare commits
59 Commits
manager-v8
...
manager-v8
Author | SHA1 | Date | |
---|---|---|---|
![]() |
7f8257152f | ||
![]() |
0cd80f2556 | ||
![]() |
1717387876 | ||
![]() |
109363ebf6 | ||
![]() |
716c4fa386 | ||
![]() |
9a09b4eb20 | ||
![]() |
95a5b57265 | ||
![]() |
13fbf397d1 | ||
![]() |
20be99ec8a | ||
![]() |
04c53c3578 | ||
![]() |
51bc27a869 | ||
![]() |
71b083794c | ||
![]() |
b100d0c503 | ||
![]() |
76061296c9 | ||
![]() |
bb303d2da1 | ||
![]() |
c91c070343 | ||
![]() |
aec06a6f61 | ||
![]() |
e8ba671fc2 | ||
![]() |
1860e5d133 | ||
![]() |
f2cb3c38fe | ||
![]() |
9a28dd4f6e | ||
![]() |
d2acd59ea8 | ||
![]() |
79dfdb29e7 | ||
![]() |
eb21c8b42e | ||
![]() |
541bb53553 | ||
![]() |
fe8997efae | ||
![]() |
23455c722c | ||
![]() |
5ce29c30d2 | ||
![]() |
70d67728fd | ||
![]() |
e546884b08 | ||
![]() |
b36e6d987d | ||
![]() |
53c3dd5e8b | ||
![]() |
da723b207a | ||
![]() |
e050f77198 | ||
![]() |
540b4b7ea9 | ||
![]() |
bbef22daf7 | ||
![]() |
9ed110c91b | ||
![]() |
a30d510eb1 | ||
![]() |
ef98eaed8f | ||
![]() |
2a257f327c | ||
![]() |
4060c2107c | ||
![]() |
cd23d27048 | ||
![]() |
18b86e4fd2 | ||
![]() |
5f2e22a259 | ||
![]() |
4e97b18977 | ||
![]() |
f9bde347bc | ||
![]() |
947a7d6a2f | ||
![]() |
872ab2e99b | ||
![]() |
90b8813bb7 | ||
![]() |
88d0f63294 | ||
![]() |
79fa0d3a90 | ||
![]() |
8e61080a4a | ||
![]() |
3f9a64417b | ||
![]() |
eb959379e8 | ||
![]() |
41a644afb9 | ||
![]() |
6b42db943d | ||
![]() |
1c325459eb | ||
![]() |
6d88d8ad95 | ||
![]() |
246997f273 |
28
.github/ISSUE_TEMPLATE/bug_report.md
vendored
Normal file
28
.github/ISSUE_TEMPLATE/bug_report.md
vendored
Normal file
@@ -0,0 +1,28 @@
|
||||
---
|
||||
name: Bug report
|
||||
about: Create a report to help us improve
|
||||
title: ''
|
||||
labels: ''
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
## READ BEFORE OPENING ISSUES
|
||||
|
||||
All bug reports require you to **USE CANARY BUILDS**. Please include the version name and version code in the bug report.
|
||||
|
||||
If you experience a bootloop, attach a `dmesg` (kernel logs) when the device refuse to boot. This may very likely require a custom kernel on some devices as `last_kmsg` or `pstore ramoops` are usually not enabled by default. In addition, please also upload the result of `cat /proc/mounts` when your device is working correctly **WITHOUT ROOT**.
|
||||
|
||||
If you experience issues during installation, in recovery, upload the recovery logs, or in Magisk Manager, upload the install logs. Please also upload the `boot.img` or `recovery.img` that you are using for patching.
|
||||
|
||||
If you experience a crash of Magisk Manager, dump the full `logcat` **when the crash happens**. **DO NOT** upload `magisk.log`.
|
||||
|
||||
If you experience other issues related to Magisk, upload `magisk.log`, and preferably also include a boot `logcat` (start dumping `logcat` when the device boots up)
|
||||
|
||||
**DO NOT** open issues regarding root detection.
|
||||
|
||||
**DO NOT** ask for instructions.
|
||||
|
||||
**DO NOT** report issues if you have any modules installed.
|
||||
|
||||
Without following the rules above, your issue will be closed without explanation.
|
9
.github/workflows/build.yml
vendored
9
.github/workflows/build.yml
vendored
@@ -3,6 +3,13 @@ name: Magisk Build
|
||||
on:
|
||||
push:
|
||||
branches: [ master ]
|
||||
paths:
|
||||
- 'app/**'
|
||||
- 'native/**'
|
||||
- 'stub/**'
|
||||
- 'buildSrc/**'
|
||||
- 'build.py'
|
||||
- 'gradle.properties'
|
||||
pull_request:
|
||||
branches: [ master ]
|
||||
workflow_dispatch:
|
||||
@@ -43,6 +50,7 @@ jobs:
|
||||
echo "ANDROID_SDK_ROOT=$sdk_root" >> $env:GITHUB_ENV
|
||||
echo "ANDROID_HOME=$sdk_root" >> $env:GITHUB_ENV
|
||||
echo "MAGISK_NDK_VERSION=$ndk_ver" >> $env:GITHUB_ENV
|
||||
echo "GRADLE_OPTS=-Dorg.gradle.daemon=false" >> $env:GITHUB_ENV
|
||||
|
||||
- name: Set up GitHub env (Unix)
|
||||
if: runner.os != 'Windows'
|
||||
@@ -57,7 +65,6 @@ jobs:
|
||||
path: |
|
||||
~/.gradle/caches
|
||||
~/.gradle/wrapper
|
||||
!~/.gradle/caches/**/*.lock
|
||||
key: ${{ runner.os }}-gradle-${{ hashFiles('**/*.gradle.kts') }}
|
||||
restore-keys: ${{ runner.os }}-gradle-
|
||||
|
||||
|
3
.gitmodules
vendored
3
.gitmodules
vendored
@@ -25,6 +25,9 @@
|
||||
[submodule "pcre"]
|
||||
path = native/jni/external/pcre
|
||||
url = https://android.googlesource.com/platform/external/pcre
|
||||
[submodule "xhook"]
|
||||
path = native/jni/external/xhook
|
||||
url = https://github.com/iqiyi/xHook.git
|
||||
[submodule "termux-elf-cleaner"]
|
||||
path = tools/termux-elf-cleaner
|
||||
url = https://github.com/termux/termux-elf-cleaner.git
|
||||
|
@@ -15,11 +15,11 @@ Here are some feature highlights:
|
||||
|
||||
## Downloads
|
||||
|
||||
[](https://github.com/topjohnwu/Magisk/releases/download/manager-v8.0.3/MagiskManager-v8.0.3.apk)
|
||||
[](https://github.com/topjohnwu/Magisk/releases/download/manager-v8.0.5/MagiskManager-v8.0.5.apk)
|
||||
[](https://raw.githubusercontent.com/topjohnwu/magisk_files/canary/app-debug.apk)
|
||||
<br>
|
||||
[](https://github.com/topjohnwu/Magisk/releases/tag/v20.4)
|
||||
[](https://github.com/topjohnwu/Magisk/releases/tag/v21.1)
|
||||
[](https://github.com/topjohnwu/Magisk/releases/tag/v21.2)
|
||||
[](https://github.com/topjohnwu/Magisk/releases/tag/v21.2)
|
||||
|
||||
## Useful Links
|
||||
|
||||
|
@@ -41,6 +41,11 @@ abstract class BaseUIFragment<VM : BaseViewModel, Binding : ViewDataBinding> :
|
||||
return binding.root
|
||||
}
|
||||
|
||||
override fun onStart() {
|
||||
super.onStart()
|
||||
activity.supportActionBar?.subtitle = null
|
||||
}
|
||||
|
||||
override fun onEventDispatched(event: ViewEvent) = when(event) {
|
||||
is ContextExecutor -> event(requireContext())
|
||||
is ActivityExecutor -> event(activity)
|
||||
|
@@ -25,6 +25,7 @@ object Const {
|
||||
fun atLeast_20_2() = Info.env.magiskVersionCode >= 20200 || isCanary()
|
||||
fun atLeast_20_4() = Info.env.magiskVersionCode >= 20400 || isCanary()
|
||||
fun atLeast_21_0() = Info.env.magiskVersionCode >= 21000 || isCanary()
|
||||
fun atLeast_21_2() = Info.env.magiskVersionCode >= 21200 || isCanary()
|
||||
fun isCanary() = Info.env.magiskVersionCode % 100 != 0
|
||||
}
|
||||
|
||||
@@ -36,7 +37,6 @@ object Const {
|
||||
// notifications
|
||||
const val MAGISK_UPDATE_NOTIFICATION_ID = 4
|
||||
const val APK_UPDATE_NOTIFICATION_ID = 5
|
||||
const val HIDE_MANAGER_NOTIFICATION_ID = 8
|
||||
const val UPDATE_NOTIFICATION_CHANNEL = "update"
|
||||
const val PROGRESS_NOTIFICATION_CHANNEL = "progress"
|
||||
const val CHECK_MAGISK_UPDATE_WORKER_ID = "magisk_update"
|
||||
|
@@ -27,13 +27,13 @@ class LocalModule(path: String) : Module() {
|
||||
val dir = "$PERSIST/$id"
|
||||
if (enable) {
|
||||
disableFile.delete()
|
||||
if (Const.Version.isCanary())
|
||||
if (Const.Version.atLeast_21_2())
|
||||
Shell.su("copy_sepolicy_rules").submit()
|
||||
else
|
||||
Shell.su("mkdir -p $dir", "cp -af $ruleFile $dir").submit()
|
||||
} else {
|
||||
!disableFile.createNewFile()
|
||||
if (Const.Version.isCanary())
|
||||
if (Const.Version.atLeast_21_2())
|
||||
Shell.su("copy_sepolicy_rules").submit()
|
||||
else
|
||||
Shell.su("rm -rf $dir").submit()
|
||||
@@ -45,13 +45,13 @@ class LocalModule(path: String) : Module() {
|
||||
set(remove) {
|
||||
if (remove) {
|
||||
removeFile.createNewFile()
|
||||
if (Const.Version.isCanary())
|
||||
if (Const.Version.atLeast_21_2())
|
||||
Shell.su("copy_sepolicy_rules").submit()
|
||||
else
|
||||
Shell.su("rm -rf $PERSIST/$id").submit()
|
||||
} else {
|
||||
!removeFile.delete()
|
||||
if (Const.Version.isCanary())
|
||||
if (Const.Version.atLeast_21_2())
|
||||
Shell.su("copy_sepolicy_rules").submit()
|
||||
else
|
||||
Shell.su("cp -af $ruleFile $PERSIST/$id").submit()
|
||||
|
@@ -191,9 +191,10 @@ abstract class MagiskInstallImpl : KoinComponent {
|
||||
if (rawData.size < 256)
|
||||
continue
|
||||
|
||||
// Patch flags to AVB_VBMETA_IMAGE_FLAGS_VERIFICATION_DISABLED
|
||||
// Patch flags to AVB_VBMETA_IMAGE_FLAGS_HASHTREE_DISABLED |
|
||||
// AVB_VBMETA_IMAGE_FLAGS_VERIFICATION_DISABLED
|
||||
console.add("-- Patching: vbmeta.img")
|
||||
ByteBuffer.wrap(rawData).putInt(120, 2)
|
||||
ByteBuffer.wrap(rawData).putInt(120, 3)
|
||||
tarOut.putNextEntry(newEntry("vbmeta.img", rawData.size.toLong()))
|
||||
tarOut.write(rawData)
|
||||
} else {
|
||||
@@ -204,7 +205,7 @@ abstract class MagiskInstallImpl : KoinComponent {
|
||||
}
|
||||
val boot = SuFile.open(installDir, "boot.img")
|
||||
val recovery = SuFile.open(installDir, "recovery.img")
|
||||
if (recovery.exists() && boot.exists()) {
|
||||
if (Config.recovery && recovery.exists() && boot.exists()) {
|
||||
// Install Magisk to recovery
|
||||
srcBoot = recovery.path
|
||||
// Repack boot image to prevent restore
|
||||
|
@@ -29,6 +29,6 @@ val viewModelModules = module {
|
||||
viewModel { MainViewModel() }
|
||||
|
||||
// Legacy
|
||||
viewModel { (args: FlashFragmentArgs) -> FlashViewModel(args, get()) }
|
||||
viewModel { (args: FlashFragmentArgs) -> FlashViewModel(args) }
|
||||
viewModel { SuRequestViewModel(get(), get(), get(SUTimeout), get()) }
|
||||
}
|
||||
|
@@ -7,9 +7,11 @@ import android.content.Context
|
||||
import android.content.ContextWrapper
|
||||
import android.content.Intent
|
||||
import android.content.pm.ApplicationInfo
|
||||
import android.content.pm.ComponentInfo
|
||||
import android.content.pm.PackageManager
|
||||
import android.content.pm.PackageManager.*
|
||||
import android.content.pm.ServiceInfo
|
||||
import android.content.pm.ServiceInfo.FLAG_ISOLATED_PROCESS
|
||||
import android.content.pm.ServiceInfo.FLAG_USE_APP_ZYGOTE
|
||||
import android.content.res.Configuration
|
||||
import android.content.res.Resources
|
||||
import android.database.Cursor
|
||||
@@ -57,32 +59,10 @@ import java.lang.reflect.Array as JArray
|
||||
|
||||
val packageName: String get() = get<Context>().packageName
|
||||
|
||||
val ApplicationInfo.processes: List<String> @SuppressLint("InlinedApi") get() {
|
||||
val pm = get<PackageManager>()
|
||||
val appProcessName = processName ?: packageName
|
||||
val baseFlag = MATCH_DISABLED_COMPONENTS or MATCH_UNINSTALLED_PACKAGES
|
||||
val packageInfo = try {
|
||||
val request = GET_ACTIVITIES or GET_SERVICES or GET_RECEIVERS or GET_PROVIDERS
|
||||
pm.getPackageInfo(packageName, baseFlag or request)
|
||||
} catch (e: NameNotFoundException) { // EdXposed hooked, issue#3276
|
||||
return listOf(appProcessName)
|
||||
} catch (e: Exception) {
|
||||
// Exceed binder data transfer limit, fetch each component type separately
|
||||
pm.getPackageInfo(packageName, baseFlag).apply {
|
||||
runCatching { activities = pm.getPackageInfo(packageName, GET_ACTIVITIES).activities }
|
||||
runCatching { services = pm.getPackageInfo(packageName, GET_SERVICES).services }
|
||||
runCatching { receivers = pm.getPackageInfo(packageName, GET_RECEIVERS).receivers }
|
||||
runCatching { providers = pm.getPackageInfo(packageName, GET_PROVIDERS).providers }
|
||||
}
|
||||
}
|
||||
fun Array<out ComponentInfo>.processNames() = map { it.processName ?: appProcessName }
|
||||
return with(packageInfo) {
|
||||
activities?.processNames().orEmpty() +
|
||||
services?.processNames().orEmpty() +
|
||||
receivers?.processNames().orEmpty() +
|
||||
providers?.processNames().orEmpty()
|
||||
}
|
||||
}
|
||||
val ServiceInfo.isIsolated get() = (flags and FLAG_ISOLATED_PROCESS) != 0
|
||||
|
||||
@get:SuppressLint("InlinedApi")
|
||||
val ServiceInfo.useAppZygote get() = (flags and FLAG_USE_APP_ZYGOTE) != 0
|
||||
|
||||
fun Context.rawResource(id: Int) = resources.openRawResource(id)
|
||||
|
||||
|
@@ -32,6 +32,10 @@ class FlashFragment : BaseUIFragment<FlashViewModel, FragmentFlashMd2Binding>()
|
||||
super.onStart()
|
||||
setHasOptionsMenu(true)
|
||||
activity.setTitle(R.string.flash_screen_title)
|
||||
|
||||
viewModel.subtitle.observe(this) {
|
||||
activity.supportActionBar?.setSubtitle(it)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) {
|
||||
|
@@ -1,9 +1,10 @@
|
||||
package com.topjohnwu.magisk.ui.flash
|
||||
|
||||
import android.content.res.Resources
|
||||
import android.net.Uri
|
||||
import android.view.MenuItem
|
||||
import androidx.databinding.Bindable
|
||||
import androidx.lifecycle.LiveData
|
||||
import androidx.lifecycle.MutableLiveData
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import com.topjohnwu.magisk.BR
|
||||
import com.topjohnwu.magisk.R
|
||||
@@ -26,17 +27,15 @@ import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
class FlashViewModel(
|
||||
args: FlashFragmentArgs,
|
||||
private val resources: Resources
|
||||
args: FlashFragmentArgs
|
||||
) : BaseViewModel() {
|
||||
|
||||
@get:Bindable
|
||||
var showReboot = Shell.rootAccess()
|
||||
set(value) = set(value, field, { field = it }, BR.showReboot)
|
||||
|
||||
@get:Bindable
|
||||
var behaviorText = resources.getString(R.string.flashing)
|
||||
set(value) = set(value, field, { field = it }, BR.behaviorText)
|
||||
private val _subtitle = MutableLiveData(R.string.flashing)
|
||||
val subtitle get() = _subtitle as LiveData<Int>
|
||||
|
||||
val adapter = RvBindingAdapter<ConsoleItem>()
|
||||
val items = diffListOf<ConsoleItem>()
|
||||
@@ -91,9 +90,9 @@ class FlashViewModel(
|
||||
|
||||
private fun onResult(success: Boolean) {
|
||||
state = if (success) State.LOADED else State.LOADING_FAILED
|
||||
behaviorText = when {
|
||||
success -> resources.getString(R.string.done)
|
||||
else -> resources.getString(R.string.failure)
|
||||
when {
|
||||
success -> _subtitle.postValue(R.string.done)
|
||||
else -> _subtitle.postValue(R.string.failure)
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -1,47 +0,0 @@
|
||||
package com.topjohnwu.magisk.ui.hide
|
||||
|
||||
import android.content.pm.ApplicationInfo
|
||||
import android.content.pm.PackageManager
|
||||
import android.graphics.drawable.Drawable
|
||||
import com.topjohnwu.magisk.core.utils.currentLocale
|
||||
import com.topjohnwu.magisk.ktx.getLabel
|
||||
|
||||
class HideTarget(line: String) {
|
||||
val packageName: String
|
||||
val process: String
|
||||
|
||||
init {
|
||||
val split = line.split(Regex("\\|"), 2)
|
||||
packageName = split[0]
|
||||
process = split.getOrElse(1) { packageName }
|
||||
}
|
||||
}
|
||||
|
||||
class HideAppInfo(info: ApplicationInfo, pm: PackageManager)
|
||||
: ApplicationInfo(info), Comparable<HideAppInfo> {
|
||||
|
||||
val label = info.getLabel(pm)
|
||||
val iconImage: Drawable = info.loadIcon(pm)
|
||||
|
||||
override fun compareTo(other: HideAppInfo) = comparator.compare(this, other)
|
||||
|
||||
companion object {
|
||||
private val comparator = compareBy<HideAppInfo>(
|
||||
{ it.label.toLowerCase(currentLocale) },
|
||||
{ it.packageName }
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
data class HideProcessInfo(
|
||||
val name: String,
|
||||
val packageName: String,
|
||||
val isHidden: Boolean
|
||||
)
|
||||
|
||||
class HideAppTarget(
|
||||
val info: HideAppInfo,
|
||||
val processes: List<HideProcessInfo>
|
||||
) : Comparable<HideAppTarget> {
|
||||
override fun compareTo(other: HideAppTarget) = compareValuesBy(this, other) { it.info }
|
||||
}
|
105
app/src/main/java/com/topjohnwu/magisk/ui/hide/HideInfo.kt
Normal file
105
app/src/main/java/com/topjohnwu/magisk/ui/hide/HideInfo.kt
Normal file
@@ -0,0 +1,105 @@
|
||||
package com.topjohnwu.magisk.ui.hide
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.content.pm.ApplicationInfo
|
||||
import android.content.pm.ComponentInfo
|
||||
import android.content.pm.PackageManager
|
||||
import android.content.pm.PackageManager.*
|
||||
import android.content.pm.ServiceInfo
|
||||
import android.graphics.drawable.Drawable
|
||||
import com.topjohnwu.magisk.core.utils.currentLocale
|
||||
import com.topjohnwu.magisk.ktx.getLabel
|
||||
import com.topjohnwu.magisk.ktx.isIsolated
|
||||
import com.topjohnwu.magisk.ktx.useAppZygote
|
||||
|
||||
class CmdlineHiddenItem(line: String) {
|
||||
val packageName: String
|
||||
val process: String
|
||||
|
||||
init {
|
||||
val split = line.split(Regex("\\|"), 2)
|
||||
packageName = split[0]
|
||||
process = split.getOrElse(1) { packageName }
|
||||
}
|
||||
}
|
||||
|
||||
const val ISOLATED_MAGIC = "isolated"
|
||||
|
||||
@SuppressLint("InlinedApi")
|
||||
class HideAppInfo(info: ApplicationInfo, pm: PackageManager, hideList: List<CmdlineHiddenItem>)
|
||||
: ApplicationInfo(info), Comparable<HideAppInfo> {
|
||||
|
||||
val label = info.getLabel(pm)
|
||||
val iconImage: Drawable = info.loadIcon(pm)
|
||||
val processes = fetchProcesses(pm, hideList)
|
||||
|
||||
override fun compareTo(other: HideAppInfo) = comparator.compare(this, other)
|
||||
|
||||
private fun fetchProcesses(
|
||||
pm: PackageManager,
|
||||
hideList: List<CmdlineHiddenItem>
|
||||
): List<HideProcessInfo> {
|
||||
// Fetch full PackageInfo
|
||||
val baseFlag = MATCH_DISABLED_COMPONENTS or MATCH_UNINSTALLED_PACKAGES
|
||||
val packageInfo = try {
|
||||
val request = GET_ACTIVITIES or GET_SERVICES or GET_RECEIVERS or GET_PROVIDERS
|
||||
pm.getPackageInfo(packageName, baseFlag or request)
|
||||
} catch (e: NameNotFoundException) {
|
||||
// EdXposed hooked, issue#3276
|
||||
return emptyList()
|
||||
} catch (e: Exception) {
|
||||
// Exceed binder data transfer limit, fetch each component type separately
|
||||
pm.getPackageInfo(packageName, baseFlag).apply {
|
||||
runCatching { activities = pm.getPackageInfo(packageName, baseFlag or GET_ACTIVITIES).activities }
|
||||
runCatching { services = pm.getPackageInfo(packageName, baseFlag or GET_SERVICES).services }
|
||||
runCatching { receivers = pm.getPackageInfo(packageName, baseFlag or GET_RECEIVERS).receivers }
|
||||
runCatching { providers = pm.getPackageInfo(packageName, baseFlag or GET_PROVIDERS).providers }
|
||||
}
|
||||
}
|
||||
|
||||
val hidden = hideList.filter { it.packageName == packageName || it.packageName == ISOLATED_MAGIC }
|
||||
fun createProcess(name: String, pkg: String = packageName): HideProcessInfo {
|
||||
return HideProcessInfo(name, pkg, hidden.any { it.process == name })
|
||||
}
|
||||
|
||||
var haveAppZygote = false
|
||||
fun Array<out ComponentInfo>.processes() = map { createProcess(it.processName) }
|
||||
fun Array<ServiceInfo>.processes() = map {
|
||||
if (it.isIsolated) {
|
||||
if (it.useAppZygote) {
|
||||
haveAppZygote = true
|
||||
// Using app zygote, don't need to track the process
|
||||
null
|
||||
} else {
|
||||
createProcess("${it.processName}:${it.name}", ISOLATED_MAGIC)
|
||||
}
|
||||
} else {
|
||||
createProcess(it.processName)
|
||||
}
|
||||
}
|
||||
|
||||
return with(packageInfo) {
|
||||
activities?.processes().orEmpty() +
|
||||
services?.processes().orEmpty() +
|
||||
receivers?.processes().orEmpty() +
|
||||
providers?.processes().orEmpty() +
|
||||
listOf(if (haveAppZygote) createProcess("${processName}_zygote") else null)
|
||||
}.filterNotNull().distinctBy { it.name }.sortedBy { it.name }
|
||||
}
|
||||
|
||||
companion object {
|
||||
private val comparator = compareBy<HideAppInfo>(
|
||||
{ it.label.toLowerCase(currentLocale) },
|
||||
{ it.packageName }
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
data class HideProcessInfo(
|
||||
val name: String,
|
||||
val packageName: String,
|
||||
var isHidden: Boolean
|
||||
) {
|
||||
val isIsolated get() = name == ISOLATED_MAGIC
|
||||
val isAppZygote get() = name.endsWith("_zygote")
|
||||
}
|
@@ -12,14 +12,13 @@ import com.topjohnwu.magisk.utils.set
|
||||
import com.topjohnwu.superuser.Shell
|
||||
import kotlin.math.roundToInt
|
||||
|
||||
class HideItem(
|
||||
app: HideAppTarget
|
||||
) : ObservableItem<HideItem>(), Comparable<HideItem> {
|
||||
class HideRvItem(
|
||||
val info: HideAppInfo
|
||||
) : ObservableItem<HideRvItem>(), Comparable<HideRvItem> {
|
||||
|
||||
override val layoutRes = R.layout.item_hide_md2
|
||||
override val layoutRes get() = R.layout.item_hide_md2
|
||||
|
||||
val info = app.info
|
||||
val processes = app.processes.map { HideProcessItem(it) }
|
||||
val processes = info.processes.map { HideProcessRvItem(it) }
|
||||
|
||||
@get:Bindable
|
||||
var isExpanded = false
|
||||
@@ -41,11 +40,10 @@ class HideItem(
|
||||
if (value == true) {
|
||||
processes
|
||||
.filterNot { it.isHidden }
|
||||
.filter { isExpanded || it.process.name == it.process.packageName }
|
||||
.filter { isExpanded || it.defaultSelection }
|
||||
} else {
|
||||
processes
|
||||
.filter { it.isHidden }
|
||||
.filter { isExpanded || it.process.name == it.process.packageName }
|
||||
}.forEach { it.toggle() }
|
||||
}
|
||||
|
||||
@@ -69,14 +67,19 @@ class HideItem(
|
||||
else -> null
|
||||
}
|
||||
} else {
|
||||
processes.find { it.isHidden && it.process.name == it.process.packageName } != null
|
||||
val defaultProcesses = processes.filter { it.defaultSelection }
|
||||
when (defaultProcesses.count { it.isHidden }) {
|
||||
0 -> false
|
||||
defaultProcesses.size -> true
|
||||
else -> null
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun compareTo(other: HideItem) = comparator.compare(this, other)
|
||||
override fun compareTo(other: HideRvItem) = comparator.compare(this, other)
|
||||
|
||||
companion object {
|
||||
private val comparator = compareBy<HideItem>(
|
||||
private val comparator = compareBy<HideRvItem>(
|
||||
{ it.itemsChecked == 0 },
|
||||
{ it.info }
|
||||
)
|
||||
@@ -84,16 +87,17 @@ class HideItem(
|
||||
|
||||
}
|
||||
|
||||
class HideProcessItem(
|
||||
class HideProcessRvItem(
|
||||
val process: HideProcessInfo
|
||||
) : ObservableItem<HideProcessItem>() {
|
||||
) : ObservableItem<HideProcessRvItem>() {
|
||||
|
||||
override val layoutRes = R.layout.item_hide_process_md2
|
||||
override val layoutRes get() = R.layout.item_hide_process_md2
|
||||
|
||||
@get:Bindable
|
||||
var isHidden = process.isHidden
|
||||
set(value) = set(value, field, { field = it }, BR.hidden) {
|
||||
val arg = if (isHidden) "add" else "rm"
|
||||
var isHidden
|
||||
get() = process.isHidden
|
||||
set(value) = set(value, process.isHidden, { process.isHidden = it }, BR.hidden) {
|
||||
val arg = if (it) "add" else "rm"
|
||||
val (name, pkg) = process
|
||||
Shell.su("magiskhide --$arg $pkg $name").submit()
|
||||
}
|
||||
@@ -102,7 +106,10 @@ class HideProcessItem(
|
||||
isHidden = !isHidden
|
||||
}
|
||||
|
||||
override fun contentSameAs(other: HideProcessItem) = process == other.process
|
||||
override fun itemSameAs(other: HideProcessItem) = process.name == other.process.name
|
||||
val defaultSelection get() =
|
||||
process.isIsolated || process.isAppZygote || process.name == process.packageName
|
||||
|
||||
override fun contentSameAs(other: HideProcessRvItem) = process == other.process
|
||||
override fun itemSameAs(other: HideProcessRvItem) = process.name == other.process.name
|
||||
|
||||
}
|
||||
|
@@ -3,6 +3,7 @@ package com.topjohnwu.magisk.ui.hide
|
||||
import android.annotation.SuppressLint
|
||||
import android.content.pm.ApplicationInfo
|
||||
import android.content.pm.PackageManager
|
||||
import android.content.pm.PackageManager.MATCH_UNINSTALLED_PACKAGES
|
||||
import android.os.Process
|
||||
import androidx.databinding.Bindable
|
||||
import androidx.lifecycle.viewModelScope
|
||||
@@ -14,7 +15,6 @@ import com.topjohnwu.magisk.arch.itemBindingOf
|
||||
import com.topjohnwu.magisk.core.Config
|
||||
import com.topjohnwu.magisk.ktx.get
|
||||
import com.topjohnwu.magisk.ktx.packageName
|
||||
import com.topjohnwu.magisk.ktx.processes
|
||||
import com.topjohnwu.magisk.utils.Utils
|
||||
import com.topjohnwu.magisk.utils.set
|
||||
import com.topjohnwu.superuser.Shell
|
||||
@@ -45,11 +45,11 @@ class HideViewModel : BaseViewModel(), Queryable {
|
||||
submitQuery()
|
||||
}
|
||||
|
||||
val items = filterableListOf<HideItem>()
|
||||
val itemBinding = itemBindingOf<HideItem> {
|
||||
val items = filterableListOf<HideRvItem>()
|
||||
val itemBinding = itemBindingOf<HideRvItem> {
|
||||
it.bindExtra(BR.viewModel, this)
|
||||
}
|
||||
val itemInternalBinding = itemBindingOf<HideProcessItem> {
|
||||
val itemInternalBinding = itemBindingOf<HideProcessRvItem> {
|
||||
it.bindExtra(BR.viewModel, this)
|
||||
}
|
||||
|
||||
@@ -62,14 +62,13 @@ class HideViewModel : BaseViewModel(), Queryable {
|
||||
state = State.LOADING
|
||||
val (apps, diff) = withContext(Dispatchers.Default) {
|
||||
val pm = get<PackageManager>()
|
||||
val hides = Shell.su("magiskhide --ls").exec().out.map { HideTarget(it) }
|
||||
val apps = pm.getInstalledApplications(PackageManager.MATCH_UNINSTALLED_PACKAGES)
|
||||
val hideList = Shell.su("magiskhide --ls").exec().out.map { CmdlineHiddenItem(it) }
|
||||
val apps = pm.getInstalledApplications(MATCH_UNINSTALLED_PACKAGES)
|
||||
.asSequence()
|
||||
.filter { it.enabled && !blacklist.contains(it.packageName) }
|
||||
.map { HideAppInfo(it, pm) }
|
||||
.map { createTarget(it, hides) }
|
||||
.map { HideAppInfo(it, pm, hideList) }
|
||||
.filter { it.processes.isNotEmpty() }
|
||||
.map { HideItem(it) }
|
||||
.map { HideRvItem(it) }
|
||||
.toList()
|
||||
.sorted()
|
||||
apps to items.calculateDiff(apps)
|
||||
@@ -80,18 +79,6 @@ class HideViewModel : BaseViewModel(), Queryable {
|
||||
|
||||
// ---
|
||||
|
||||
private fun createTarget(info: HideAppInfo, hideList: List<HideTarget>): HideAppTarget {
|
||||
val pkg = info.packageName
|
||||
val hidden = hideList.filter { it.packageName == pkg }
|
||||
val processNames = info.processes.distinct()
|
||||
val processes = processNames.map { name ->
|
||||
HideProcessInfo(name, pkg, hidden.any { name == it.process })
|
||||
}
|
||||
return HideAppTarget(info, processes)
|
||||
}
|
||||
|
||||
// ---
|
||||
|
||||
override fun query() {
|
||||
items.filter {
|
||||
fun showHidden() = it.itemsChecked != 0
|
||||
|
@@ -312,7 +312,7 @@ object RequestTimeout : BaseSettingsItem.Selector() {
|
||||
|
||||
override var value = selected
|
||||
set(value) = setV(value, field, { field = it }) {
|
||||
Config.suDefaultTimeout = it
|
||||
Config.suDefaultTimeout = entryValues[it].toInt()
|
||||
}
|
||||
|
||||
private val selected: Int
|
||||
|
@@ -56,29 +56,6 @@
|
||||
app:icon="@drawable/ic_restart"
|
||||
app:iconTint="?colorOnPrimary" />
|
||||
|
||||
<com.google.android.material.card.MaterialCardView
|
||||
style="@style/WidgetFoundation.Card.Elevated"
|
||||
goneUnless="@{viewModel.loading}"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center"
|
||||
app:contentPadding="@dimen/l1">
|
||||
|
||||
<TextView
|
||||
movieBehavior="@{viewModel.loading}"
|
||||
movieBehaviorText="@{viewModel.behaviorText}"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center"
|
||||
android:fontFamily="monospace"
|
||||
android:gravity="center"
|
||||
android:textAppearance="@style/AppearanceFoundation.Body"
|
||||
android:textColor="?colorOnSurface"
|
||||
android:textStyle="bold"
|
||||
tools:text="Flashing..." />
|
||||
|
||||
</com.google.android.material.card.MaterialCardView>
|
||||
|
||||
</androidx.coordinatorlayout.widget.CoordinatorLayout>
|
||||
|
||||
</layout>
|
||||
|
@@ -9,7 +9,7 @@
|
||||
|
||||
<variable
|
||||
name="item"
|
||||
type="com.topjohnwu.magisk.ui.hide.HideItem" />
|
||||
type="com.topjohnwu.magisk.ui.hide.HideRvItem" />
|
||||
|
||||
<variable
|
||||
name="viewModel"
|
||||
|
@@ -7,7 +7,7 @@
|
||||
|
||||
<variable
|
||||
name="item"
|
||||
type="com.topjohnwu.magisk.ui.hide.HideProcessItem" />
|
||||
type="com.topjohnwu.magisk.ui.hide.HideProcessRvItem" />
|
||||
|
||||
<variable
|
||||
name="viewModel"
|
||||
|
@@ -1,10 +1,8 @@
|
||||
## v8.0.4
|
||||
## v8.0.6
|
||||
|
||||
- A lot of stability changes and minor bug fixes
|
||||
- Collect device properties, app logcat, and Magisk logs when saving logs in the logs menu
|
||||
- Minor UI changes
|
||||
- Update internal scripts
|
||||
|
||||
## v8.0.3
|
||||
## v8.0.5
|
||||
|
||||
- Switch to the new Magisk Module Repo setup in preparation to allow 3rd party repos
|
||||
- Add tapjacking protection on Superuser request dialog
|
||||
- Stability changes and bug fixes
|
||||
- Fix sepolicy rule copying
|
||||
|
@@ -1,169 +1,247 @@
|
||||
<resources>
|
||||
|
||||
<!--Welcome Activity-->
|
||||
<string name="modules">Moduly</string>
|
||||
<string name="superuser">Superuser</string>
|
||||
<string name="logs">Log</string>
|
||||
<!--Sections-->
|
||||
<string name="modules">Správce modulů</string>
|
||||
<string name="superuser">SuperUser</string>
|
||||
<string name="logs">Protokol</string>
|
||||
<string name="settings">Nastavení</string>
|
||||
<string name="refresh">Obnovit lokální data</string>
|
||||
<string name="install">Instalovat</string>
|
||||
<string name="unsupport_magisk_title">Nepodporovaná verze Magisk</string>
|
||||
<string name="section_home">Domů</string>
|
||||
<string name="section_theme">Téma</string>
|
||||
<string name="safetynet">SafetyNet</string>
|
||||
|
||||
<!--Status Fragment-->
|
||||
<string name="invalid_update_channel">Neplatný kanál aktualizace</string>
|
||||
<string name="safetynet_api_error">Chyba SafetyNet API</string>
|
||||
<string name="safetynet_res_invalid">Odpověď je neplatná.</string>
|
||||
<string name="keep_force_encryption">Udržet "force encryption"</string>
|
||||
<string name="keep_dm_verity">Udržet "AVB 2.0/dm-verity"</string>
|
||||
<string name="uninstall_magisk_title">Odinstalovat Magisk</string>
|
||||
<string name="uninstall_magisk_msg">Všechny moduly budou zakázány/odstraněny. Root bude odstraněn a pokud jsou data dešifrována, můžou být zašifrována.</string>
|
||||
<string name="update">Aktualizovat</string>
|
||||
|
||||
<!--Module Fragment-->
|
||||
<string name="no_info_provided">(Žádné informace)</string>
|
||||
<string name="reboot_recovery">Restartovat do Recovery</string>
|
||||
<string name="reboot_bootloader">Restartovat do Bootloaderu</string>
|
||||
<string name="reboot_download">Restartovat do Download</string>
|
||||
|
||||
<!--Repo Fragment-->
|
||||
<string name="update_available">Dostupná aktualizace</string>
|
||||
<string name="home_installed_version">Nainstalováno</string>
|
||||
<string name="sorting_order">Pořadí řazení</string>
|
||||
|
||||
<!--Log Fragment-->
|
||||
<string name="menuSaveLog">Uložit log</string>
|
||||
<string name="menuClearLog">Smazat log</string>
|
||||
<string name="logs_cleared">Log byl smazán.</string>
|
||||
|
||||
<!--About Activity-->
|
||||
<!--Home-->
|
||||
<string name="no_connection">Žádné připojení</string>
|
||||
<string name="app_changelog">Seznam změn</string>
|
||||
<string name="manager">Správce</string>
|
||||
<string name="loading">Načítání…</string>
|
||||
<string name="update">Aktualizovat</string>
|
||||
<string name="not_available">N/A</string>
|
||||
<string name="hide">Skrýt</string>
|
||||
<string name="status">Stav</string>
|
||||
<string name="home_package">Balíček</string>
|
||||
|
||||
<!-- System Components, Notifications -->
|
||||
<string name="update_channel">Aktualizace Magisk</string>
|
||||
<string name="progress_channel">Oznámení o průběhu</string>
|
||||
<string name="download_complete">Stahování dokončeno</string>
|
||||
<string name="download_file_error">Chyba při stahování souboru</string>
|
||||
<string name="magisk_update_title">Aktualizace Magisk je dostupná!</string>
|
||||
<string name="manager_update_title">Aktualizace Magisk Manager je dostupná!</string>
|
||||
<string name="home_notice_content">Zkontrolujte, zda používáte Magisk Manager s otevřeným kódem. Správce z neznámého zdroje může provádět nebezpečné akce!</string>
|
||||
<string name="home_support_title">Podpořte nás</string>
|
||||
<string name="home_item_source">Zdroj</string>
|
||||
<string name="home_support_content">Magisk je a vždy bude svobodný s otevřeným kódem. Můžete nám však zaslat malý dar jako poděkování.</string>
|
||||
<string name="home_status_normal">Normální</string>
|
||||
<string name="home_status_stub">Testovací</string>
|
||||
<string name="home_installed_version">Nainstalovaná</string>
|
||||
<string name="home_latest_version">Poslední</string>
|
||||
<string name="invalid_update_channel">Neplatný kanál aktualizace</string>
|
||||
<string name="uninstall_magisk_title">Odinstalovat Magisk</string>
|
||||
<string name="uninstall_magisk_msg">Všechny moduly budou zakázány/odstraněny!\nROOT bude odstraněn!\nPokud jsou data dešifrována, můžou být zašifrována!</string>
|
||||
<string name="home_check_safetynet">Zkontrolovat SafetyNet</string>
|
||||
|
||||
<!-- Installation -->
|
||||
<string name="manager_download_install">Stiskněte pro stažení a instalaci.</string>
|
||||
<string name="download_zip_only">Stáhnout pouze zip</string>
|
||||
<!--Install-->
|
||||
<string name="keep_force_encryption">Ponechat Force Encryption</string>
|
||||
<string name="keep_dm_verity">Ponechat AVB 2.0/DM-Verity</string>
|
||||
<string name="recovery_mode">Režim Recovery</string>
|
||||
<string name="install_options_title">Možnosti</string>
|
||||
<string name="install_method_title">Způsob</string>
|
||||
<string name="install_next">Další</string>
|
||||
<string name="install_start">Spustit</string>
|
||||
<string name="manager_download_install">Stiskněte pro stažení a instalaci</string>
|
||||
<string name="download_zip_only">Stáhnout pouze soubor ZIP</string>
|
||||
<string name="direct_install">Přímá instalace (doporučeno)</string>
|
||||
<string name="install_inactive_slot">Instalace do druhého slotu (po OTA)</string>
|
||||
<string name="install_inactive_slot_msg">Vaše zařízení bude po restartu VYNUCENĚ spuštěno do aktuálního neaktivního slotu!\nTuto možnost použijte pouze po dokončení OTA.\nPokračovat?</string>
|
||||
<string name="install_inactive_slot_msg">Vaše zařízení bude po restartu VYNUCENĚ spuštěno do aktuálního neaktivního slotu!\nTuto možnost použijte pouze po dokončení OTA.\nChcete pokračovat?</string>
|
||||
<string name="setup_title">Další nastavení</string>
|
||||
<string name="select_patch_file">Vybrat a opravit soubor</string>
|
||||
<string name="patch_file_msg">Vyberte obraz raw (*.img) nebo soubor tar pro ODIN (*.tar)</string>
|
||||
<string name="patch_file_msg">Vyberte soubor RAW (*.img) nebo soubor TAR pro ODIN (*.tar)</string>
|
||||
<string name="reboot_delay_toast">Restartování za 5 sekund…</string>
|
||||
<string name="flash_screen_title">Instalace</string>
|
||||
|
||||
<!--Toasts, Dialogs-->
|
||||
<string name="repo_install_title">Instalovat %1$s</string>
|
||||
<string name="repo_install_msg">Chcete nyní nainstalovat %1$s?</string>
|
||||
<string name="download">Stáhnout</string>
|
||||
<string name="reboot">Restartovat</string>
|
||||
<string name="release_notes">Poznámky k vydání</string>
|
||||
<string name="repo_cache_cleared">Mezipaměť smazána</string>
|
||||
<!--Superuser-->
|
||||
<string name="su_request_title">Požadavek SuperUser</string>
|
||||
<string name="touch_filtered_warning">Protože aplikace zavírá požadavek SuperUser, Magisk nemůže ověřit Vaši odpověď</string>
|
||||
<string name="deny">Zakázat</string>
|
||||
<string name="prompt">Zeptat se</string>
|
||||
<string name="grant">Povolit</string>
|
||||
<string name="su_warning">Povolí plný přístup k Vašemu zařízení.\nZakažte, pokud si nejste jisti!</string>
|
||||
<string name="forever">Trvale</string>
|
||||
<string name="once">Jednou</string>
|
||||
<string name="tenmin">10 minut</string>
|
||||
<string name="twentymin">20 minut</string>
|
||||
<string name="thirtymin">30 minut</string>
|
||||
<string name="sixtymin">60 minut</string>
|
||||
<string name="su_allow_toast">Pro %1$s bylo oprávnění SuperUser povoleno</string>
|
||||
<string name="su_deny_toast">Pro %1$s bylo oprávnění SuperUser zakázáno</string>
|
||||
<string name="su_snack_grant">SuperUser oprávnění pro %1$s je povoleno</string>
|
||||
<string name="su_snack_deny">SuperUser oprávnění pro %1$s je zakázáno</string>
|
||||
<string name="su_snack_notif_on">Oznámení pro %1$s jsou povolena</string>
|
||||
<string name="su_snack_notif_off">Oznámení pro %1$s jsou zakázána</string>
|
||||
<string name="su_snack_log_on">Protokolování %1$s je povoleno</string>
|
||||
<string name="su_snack_log_off">Protokolování %1$s je zakázáno</string>
|
||||
<string name="su_revoke_title">Smazat?</string>
|
||||
<string name="su_revoke_msg">Chcete smazat protokol k oprávnění pro %1$s?</string>
|
||||
<string name="toast">Text</string>
|
||||
<string name="none">Žádné</string>
|
||||
|
||||
<string name="flashing">Instalování</string>
|
||||
<string name="hide_manager_title">Skrytí Magisk Manageru…</string>
|
||||
<string name="hide_manager_fail_toast">Skrytí Magisk Manageru selhalo.</string>
|
||||
<string name="open_link_failed_toast">Nebyla nalezena žádná aplikace pro otevření odkazu.</string>
|
||||
<string name="complete_uninstall">Dokončete odinstalaci</string>
|
||||
<string name="restore_img">Obnovit obrazy</string>
|
||||
<string name="restore_img_msg">Obnovování…</string>
|
||||
<string name="restore_done">Obnovení bylo provedeno!</string>
|
||||
<string name="restore_fail">Výchozí záloha neexistuje!</string>
|
||||
<string name="proprietary_title">Stáhnout proprietární kód</string>
|
||||
<string name="proprietary_notice">Magisk Manager je FOSS a neobsahuje proprietární kód SafetyNet API společnosti Google.\n\nChcete povolit aplikaci Magisk Manager stažení rozšíření (obsahuje GoogleApiClient) pro kontrolu SafetyNet?</string>
|
||||
<string name="setup_fail">Nastavení selhalo.</string>
|
||||
<string name="env_fix_title">Vyžaduje se další nastavení</string>
|
||||
<string name="env_fix_msg">Vaše zařízení potřebuje další nastavení, aby Magisk fungoval správně. Stáhne se instalační zip Magisk, chcete pokračovat?</string>
|
||||
<string name="setup_msg">Nastavení je spuštěno…</string>
|
||||
<string name="superuser_toggle_notification">Oznámení</string>
|
||||
<string name="superuser_toggle_revoke">Smazat</string>
|
||||
<string name="superuser_policy_none">Žádná aplikace nepožádala o oprávnění SuperUser.</string>
|
||||
|
||||
<!--Settings Activity -->
|
||||
<string name="settings_clear_cache_title">Smazat uchovanou mezipaměť</string>
|
||||
<string name="settings_clear_cache_summary">Smaže informace online použití v mezipaměti, donutí aplikaci obnovit informace online.</string>
|
||||
<!--Logs-->
|
||||
<string name="log_data_none">Žádné protokoly. Vyzkoušejte nějakou aplikaci vyžadující oprávnění SuperUser.</string>
|
||||
<string name="log_data_magisk_none">Protokoly Magisk jsou prázdné. To je zvláštní!</string>
|
||||
<string name="menuSaveLog">Uložit protokol</string>
|
||||
<string name="menuClearLog">Smazat protokol</string>
|
||||
<string name="logs_cleared">Protokol byl smazán</string>
|
||||
<string name="pid">PID:%1$d</string>
|
||||
<string name="target_uid">Cílové UID: %1$d</string>
|
||||
|
||||
<!--SafetyNet-->
|
||||
<string name="safetynet_api_error">Chyba SafetyNet API</string>
|
||||
<string name="safetynet_res_invalid">Odpověď je neplatná</string>
|
||||
<string name="safetynet_attest_success">Úspěšné!</string>
|
||||
<string name="safetynet_attest_failure">Chyba ověření!</string>
|
||||
<string name="safetynet_attest_loading">Čekejte…</string>
|
||||
<string name="safetynet_attest_restart">Zkusit znovu</string>
|
||||
|
||||
<!--MagiskHide-->
|
||||
<string name="show_system_app">Zobrazit systémové aplikace</string>
|
||||
<string name="show_os_app">Zobrazit OS aplikace</string>
|
||||
<string name="hide_filter_hint">Filtrovat podle názvu</string>
|
||||
<string name="hide_scroll_up">Posunout nahoru</string>
|
||||
<string name="hide_filters">Filtry</string>
|
||||
<string name="hide_search">Vyhledávání</string>
|
||||
|
||||
<!--Module-->
|
||||
<string name="no_info_provided">(žádné informace)</string>
|
||||
<string name="reboot_userspace">Restartovat do UserSpace</string>
|
||||
<string name="reboot_recovery">Restartovat do Recovery</string>
|
||||
<string name="reboot_bootloader">Restartovat do Bootloader</string>
|
||||
<string name="reboot_download">Restartovat do Download</string>
|
||||
<string name="reboot_edl">Restartovat do EDL</string>
|
||||
<string name="module_version_author">%1$s z %2$s</string>
|
||||
<string name="module_section_pending">Aktualizace</string>
|
||||
<string name="module_section_pending_action">Aktualizovat vše</string>
|
||||
<string name="module_state_remove">Odstranit</string>
|
||||
<string name="module_state_restore">Obnovit</string>
|
||||
<string name="module_action_install_external">Instalace z úložiště</string>
|
||||
<string name="update_available">Dostupná aktualizace</string>
|
||||
<string name="module_installed">@string/home_installed_version</string>
|
||||
<string name="module_section_online">Online</string>
|
||||
<string name="sorting_order">Seřadit</string>
|
||||
|
||||
<!--Settings-->
|
||||
<string name="settings_dark_mode_title">Režim motivu</string>
|
||||
<string name="settings_dark_mode_message">Vyberte režim, který nejlépe vyhovuje Vašemu stylu!</string>
|
||||
<string name="settings_dark_mode_light">Světlý</string>
|
||||
<string name="settings_dark_mode_system">Podle systému</string>
|
||||
<string name="settings_dark_mode_dark">Tmavý</string>
|
||||
<string name="settings_download_path_title">Složka pro stahování</string>
|
||||
<string name="settings_download_path_message">Soubory budou uloženy do %1$s.</string>
|
||||
<string name="settings_clear_cache_title">Smazat mezipaměť</string>
|
||||
<string name="settings_clear_cache_summary">Smažete online informace o použití z mezipaměti, a tím na aplikaci vynutíte obnovení online informací.</string>
|
||||
<string name="settings_hide_manager_title">Skrýt Magisk Manager</string>
|
||||
<string name="settings_hide_manager_summary">Nahradí Magisk Manager náhodným názvem balíčku.</string>
|
||||
<string name="settings_hide_manager_summary">Nahradíte Magisk Manager náhodným názvem balíčku.</string>
|
||||
<string name="settings_restore_manager_title">Obnovit Magisk Manager</string>
|
||||
<string name="settings_restore_manager_summary">Obnoví Magisk Manager na původní název balíčku.</string>
|
||||
<string name="settings_restore_manager_summary">Obnovíte Magisk Manager na původní název balíčku.</string>
|
||||
<string name="language">Jazyk</string>
|
||||
<string name="system_default">(Výchozí systémový)</string>
|
||||
<string name="settings_check_update_title">Zkontrolovat aktualizace</string>
|
||||
<string name="settings_check_update_summary">Pravidelně kontrolujte aktualizace na pozadí.</string>
|
||||
<string name="system_default">(výchozí podle systému)</string>
|
||||
<string name="settings_check_update_title">Automatické aktualizace</string>
|
||||
<string name="settings_check_update_summary">Povolíte pravidelnou kontrolu aktualizace na pozadí.</string>
|
||||
<string name="settings_update_channel_title">Kanál aktualizace</string>
|
||||
<string name="settings_update_stable">Stabilní</string>
|
||||
<string name="settings_update_beta">Beta</string>
|
||||
<string name="settings_update_custom">Vlastní</string>
|
||||
<string name="settings_update_custom_msg">Vložte vlastní URL</string>
|
||||
<string name="settings_magiskhide_summary">Skrýt Magisk z různých detekcí.</string>
|
||||
<string name="settings_hosts_title">Nesystémoví hostitelé</string>
|
||||
<string name="settings_hosts_summary">Podpora nesystémových hostitelů pro aplikace Adblock.</string>
|
||||
<string name="settings_magiskhide_summary">Skryjete Magisk před různými detekcemi.</string>
|
||||
<string name="settings_hosts_title">Nesystémový hostitel</string>
|
||||
<string name="settings_hosts_summary">Přidáte modul pro podporu nesystémových hostitelů v aplikaci AdBlock.</string>
|
||||
<string name="settings_hosts_toast">Přidán systémový modul hostitelů</string>
|
||||
|
||||
<string name="settings_app_name_hint">Nový název</string>
|
||||
<string name="settings_app_name_helper">Název aplikace bude nahrazen tímto názvem.</string>
|
||||
<string name="settings_app_name_error">Neplatný formát</string>
|
||||
<string name="settings_su_app_adb">Aplikace a ADB</string>
|
||||
<string name="settings_su_app">Pouze aplikace</string>
|
||||
<string name="settings_su_adb">Pouze ADB</string>
|
||||
<string name="settings_su_disable">Zakázáno</string>
|
||||
<string name="settings_su_disable">Zakázat</string>
|
||||
<string name="settings_su_request_10">10 sekund</string>
|
||||
<string name="settings_su_request_15">15 sekund</string>
|
||||
<string name="settings_su_request_20">20 sekund</string>
|
||||
<string name="settings_su_request_30">30 sekund</string>
|
||||
<string name="settings_su_request_45">45 sekund</string>
|
||||
<string name="settings_su_request_60">60 sekund</string>
|
||||
<string name="superuser_access">Přístup Superuser</string>
|
||||
<string name="superuser_access">Přístup SuperUser</string>
|
||||
<string name="auto_response">Automatická odezva</string>
|
||||
<string name="request_timeout">Časový limit požadavku</string>
|
||||
<string name="superuser_notification">Oznámení Superuser</string>
|
||||
<string name="superuser_notification">Oznámení SuperUser</string>
|
||||
<string name="settings_su_reauth_title">Opětovné ověření po aktualizaci</string>
|
||||
<string name="settings_su_reauth_summary">Opětovné ověření oprávnění Superuser po aktualizaci aplikace</string>
|
||||
<string name="settings_su_reauth_summary">Opětovné ověření oprávnění SuperUser po aktualizaci aplikace.</string>
|
||||
<string name="settings_su_tapjack_title">Povolit ochranu před TapJack</string>
|
||||
<string name="settings_su_tapjack_summary">Okno dialogu SuperUser nebude reagovat na kliknutí v případě, že je zavřené nebo překryté jiným oknem.</string>
|
||||
<string name="settings_su_biometric_title">Povolit biometrické ověření</string>
|
||||
<string name="settings_su_biometric_summary">Použijte biometrické ověření pro povolení požadavků SuperUser.</string>
|
||||
<string name="no_biometric">Nepodporované zařízení nebo není biometrické ověření povolené</string>
|
||||
<string name="settings_customization">Přizpůsobit</string>
|
||||
<string name="setting_add_shortcut_summary">Přidejte odkaz na domovskou obrazovku v případě, že se po skrytí aplikace její název a ikona těžko rozpoznávají.</string>
|
||||
<string name="settings_doh_title">DNS nebo HTTPS</string>
|
||||
<string name="settings_doh_description">Řešení pro opravy DNS v některých zemích.</string>
|
||||
|
||||
<string name="multiuser_mode">Režim více uživatelů</string>
|
||||
<string name="settings_owner_only">Pouze vlastník zařízení</string>
|
||||
<string name="settings_owner_manage">Spravováno vlastníkem zařízení</string>
|
||||
<string name="settings_user_independent">Nezávislý uživatel</string>
|
||||
<string name="owner_only_summary">Pouze vlastník má root přístup.</string>
|
||||
<string name="owner_manage_summary">Root přístup spravuje pouze vlastník a přijímá požadavky k přístupu.</string>
|
||||
<string name="user_indepenent_summary">Každý uživatel má svá vlastní pravidla root.</string>
|
||||
<string name="settings_owner_only">Vlastník zařízení</string>
|
||||
<string name="settings_owner_manage">Správce zařízení</string>
|
||||
<string name="settings_user_independent">Všichni uživatelé</string>
|
||||
<string name="owner_only_summary">Pouze vlastník má přístup ROOT.</string>
|
||||
<string name="owner_manage_summary">Přístup ROOT má správce zařízení, který přijímá požadavky k přístupu.</string>
|
||||
<string name="user_indepenent_summary">Každý uživatel má svá vlastní pravidla přístupu ROOT.</string>
|
||||
|
||||
<string name="mount_namespace_mode">Režim připojení jmenného prostoru</string>
|
||||
<string name="settings_ns_global">Globální jmenný prostor</string>
|
||||
<string name="settings_ns_requester">Zděděný jmenný prostor</string>
|
||||
<string name="settings_ns_requester">Odvozený jmenný prostor</string>
|
||||
<string name="settings_ns_isolate">Izolovaný jmenný prostor</string>
|
||||
<string name="global_summary">Všechny relace root používají globální připojení jmenného prostoru.</string>
|
||||
<string name="requester_summary">Kořenové relace dědí jmenný prostor žadatele.</string>
|
||||
<string name="isolate_summary">Každá relace root bude mít svůj vlastní izolovaný jmenný prostor.</string>
|
||||
<string name="global_summary">Všechny relace ROOT používají globální jmenný prostor.</string>
|
||||
<string name="requester_summary">Relace ROOT je odvozena od jmenného prostoru žadatele.</string>
|
||||
<string name="isolate_summary">Každá relace ROOT má svůj vlastní izolovaný jmenný prostor.</string>
|
||||
|
||||
<!--Superuser-->
|
||||
<string name="su_request_title">Požadavek Superuser</string>
|
||||
<string name="deny">Zamítnout</string>
|
||||
<string name="prompt">Dotaz</string>
|
||||
<string name="grant">Povolit</string>
|
||||
<string name="su_warning">Povolí plný přístup k vašemu zařízení.\nZamítněte, pokud si nejste jisti!</string>
|
||||
<string name="forever">Navždy</string>
|
||||
<string name="once">Jednou</string>
|
||||
<string name="tenmin">10 minut</string>
|
||||
<string name="twentymin">20 minut</string>
|
||||
<string name="thirtymin">30 minut</string>
|
||||
<string name="sixtymin">60 minut</string>
|
||||
<string name="su_allow_toast">Pro %1$s bylo oprávnění Superuser povoleno</string>
|
||||
<string name="su_deny_toast">Pro %1$s bylo oprávnění Superuser zamítnuto</string>
|
||||
<string name="su_snack_grant">Superuser oprávnění pro %1$s je povoleno</string>
|
||||
<string name="su_snack_deny">Superuser oprávnění pro %1$s je zamítnuto</string>
|
||||
<string name="su_snack_notif_on">Oznámení pro %1$s jsou povolena</string>
|
||||
<string name="su_snack_notif_off">Oznámení pro %1$s jsou zakázána</string>
|
||||
<string name="su_snack_log_on">Logování %1$s je povoleno</string>
|
||||
<string name="su_snack_log_off">Logování %1$s je zakázáno</string>
|
||||
<string name="su_revoke_title">Smazat?</string>
|
||||
<string name="su_revoke_msg">Smazat záznam ohledně oprávnění pro %1$s?</string>
|
||||
<string name="toast">Informační text</string>
|
||||
<string name="none">Žádný</string>
|
||||
<!--Notifications-->
|
||||
<string name="update_channel">Aktualizace Magisk</string>
|
||||
<string name="progress_channel">Oznámení o průběhu</string>
|
||||
<string name="download_complete">Stahování dokončeno</string>
|
||||
<string name="download_file_error">Chyba při stahování souboru</string>
|
||||
<string name="download_open_parent">Zobrazit výchozí složku</string>
|
||||
<string name="download_open_self">Zobrazit soubor</string>
|
||||
<string name="magisk_update_title">Aktualizace Magisk je dostupná!</string>
|
||||
<string name="manager_update_title">Aktualizace Magisk Manager je dostupná!</string>
|
||||
|
||||
<!--Superuser logs-->
|
||||
<string name="pid">PID: %1$d</string>
|
||||
<string name="target_uid">Cílové UID: %1$d</string>
|
||||
|
||||
<!-- MagiskHide -->
|
||||
<string name="show_system_app">Zobrazit systémové aplikace</string>
|
||||
<!--Toasts, Dialogs-->
|
||||
<string name="yes">ANO</string>
|
||||
<string name="no">NE</string>
|
||||
<string name="repo_install_title">Instalovat %1$s</string>
|
||||
<string name="repo_install_msg">Chcete nyní nainstalovat %1$s?</string>
|
||||
<string name="download">Stáhnout</string>
|
||||
<string name="reboot">Restartovat</string>
|
||||
<string name="release_notes">Poznámky k vydání</string>
|
||||
<string name="repo_cache_cleared">Mezipaměť smazána</string>
|
||||
<string name="flashing">Instalování…</string>
|
||||
<string name="done">Hotovo!</string>
|
||||
<string name="failure">Selhalo!</string>
|
||||
<string name="hide_manager_title">Skrývání Magisk Manager…</string>
|
||||
<string name="hide_manager_fail_toast">Skrývání Magisk Manager selhalo.</string>
|
||||
<string name="restore_manager_fail_toast">Obnovení Magisk Manager selhalo.</string>
|
||||
<string name="open_link_failed_toast">Nebyla nalezena žádná aplikace pro otevření odkazu.</string>
|
||||
<string name="complete_uninstall">Odinstalovat</string>
|
||||
<string name="restore_img">Obnovit obrazy</string>
|
||||
<string name="restore_img_msg">Obnovování…</string>
|
||||
<string name="restore_done">Obnovení bylo provedeno!</string>
|
||||
<string name="restore_fail">Výchozí záloha neexistuje!</string>
|
||||
<string name="proprietary_title">Stáhnout kód</string>
|
||||
<string name="proprietary_notice">Magisk Manager je FOSS a neobsahuje kód SafetyNet API společnosti Google.\n\nChcete povolit aplikaci Magisk Manager stažení rozšíření (obsahuje GoogleApiClient) pro kontrolu SafetyNet?</string>
|
||||
<string name="setup_fail">Nastavení selhalo</string>
|
||||
<string name="env_fix_title">Vyžaduje se další nastavení</string>
|
||||
<string name="env_fix_msg">Vaše zařízení potřebuje další nastavení, aby Magisk fungoval správně. Stáhne se instalační soubor (.zip) Magisk. Chcete pokračovat?</string>
|
||||
<string name="setup_msg">Nastavení je spuštěno…</string>
|
||||
<string name="authenticate">Ověřit</string>
|
||||
<string name="unsupport_magisk_title">Nepodporovaná verze Magisk</string>
|
||||
<string name="unsupport_magisk_msg">Tato verze Magisk Manager nepodporuje verzi Magisk nižší než %1$s.\n\nMůžete buď ručně aktualizovat Magisk, nebo nainstalovat starší verzi aplikace.</string>
|
||||
<string name="external_rw_permission_denied">Udělte oprávnění pro povolení této funkce.</string>
|
||||
<string name="add_shortcut_title">Přidat odkaz na domovskou obrazovku</string>
|
||||
<string name="add_shortcut_msg">Po skrytí Magisk Manager se může jeho název a ikona těžko rozpoznat. Chcete přidat odkaz na domovskou obrazovku?</string>
|
||||
<string name="app_not_found">Nelze nalézt žádnou aplikaci, která dokáže provést tuto akci.</string>
|
||||
|
||||
</resources>
|
||||
|
@@ -15,7 +15,7 @@
|
||||
<string name="no_connection">Keine Verbindung verfügbar</string>
|
||||
<string name="app_changelog">Änderungen</string>
|
||||
<string name="manager">Manager</string>
|
||||
<string name="loading">Lädt…</string>
|
||||
<string name="loading">Laden…</string>
|
||||
<string name="update">Update</string>
|
||||
<string name="not_available">N/A</string>
|
||||
<string name="hide">Verstecken</string>
|
||||
@@ -40,7 +40,7 @@
|
||||
<string name="recovery_mode">Recovery Modus</string>
|
||||
<string name="install_options_title">Optionen</string>
|
||||
<string name="install_method_title">Methode</string>
|
||||
<string name="install_next">Nächste</string>
|
||||
<string name="install_next">Nächster Schritt</string>
|
||||
<string name="install_start">Los geht\'s</string>
|
||||
<string name="manager_download_install">Tippen zum Herunterladen und Installieren</string>
|
||||
<string name="download_zip_only">Nur Zip herunterladen</string>
|
||||
|
@@ -44,7 +44,7 @@
|
||||
<string name="install_next">Ďalej</string>
|
||||
<string name="install_start">Poďme na to</string>
|
||||
<string name="manager_download_install">Stlačte pre stiahnutie a inštaláciu</string>
|
||||
<string name="download_zip_only">Len tiahnuť zip</string>
|
||||
<string name="download_zip_only">Len stiahnuť zip</string>
|
||||
<string name="direct_install">Priama inštalácia (Odporúča sa)</string>
|
||||
<string name="install_inactive_slot">Inštalovať na neaktívny slot (Po OTA)</string>
|
||||
<string name="install_inactive_slot_msg">Vaše zariadenie bude po reštarte PRINÚTENÉ nabootovať do aktuálne neaktívneho slotu!\nTúto voľbu použite iba po skončení OTA.\nPokračovať?</string>
|
||||
|
@@ -28,11 +28,11 @@
|
||||
<string name="home_support_content">Magisk është, dhe gjithmonë do të jetë, falas dhe me burim të hapur. Megjithatë mund të na tregoni se ju interesoni duke dërguar një donacion të vogël.</string>
|
||||
<string name="home_status_normal">Normale</string>
|
||||
<string name="home_status_stub">Stub</string>
|
||||
<string name="home_installed_version">Instaluar</string>
|
||||
<string name="home_latest_version">Më të fundit</string>
|
||||
<string name="home_installed_version">E instaluar</string>
|
||||
<string name="home_latest_version">Më e fundit</string>
|
||||
<string name="invalid_update_channel">Kanal i pavlefshëm i azhurnimit</string>
|
||||
<string name="uninstall_magisk_title">Çinstalo Magisk</string>
|
||||
<string name="uninstall_magisk_msg">Të gjitha modulet do të çaktivizohen/hiqen! \n Rrënja do të hiqet! \n Të dhënat tuaja potencialisht të koduara nëse jo tashmë!</string>
|
||||
<string name="uninstall_magisk_msg">Të gjitha modulet do të çaktivizohen/hiqen! \n Rrënja do të hiqet! \n\n Të dhënat tuaja potencialisht të koduara nëse jo tashmë!</string>
|
||||
<string name="home_check_safetynet">Kontrolloni Rrjetin e Sigurisë</string>
|
||||
|
||||
<!--Install-->
|
||||
@@ -60,7 +60,7 @@
|
||||
<string name="prompt">Pyet</string>
|
||||
<string name="grant">Lejo</string>
|
||||
<string name="su_warning">Jep akses të plotë në pajisjen tuaj. \n Moho nëse nuk je i sigurt!</string>
|
||||
<string name="forever">Përgjithmonë</string>
|
||||
<string name="forever">Gjithmonë</string>
|
||||
<string name="once">Një herë</string>
|
||||
<string name="tenmin">10 minuta</string>
|
||||
<string name="twentymin">20 minuta</string>
|
||||
@@ -77,6 +77,7 @@
|
||||
<string name="su_revoke_title">Anulohet?</string>
|
||||
<string name="su_revoke_msg">Konfirmo të heqësh të drejtat e %1$s?</string>
|
||||
<string name="toast">dolli</string>
|
||||
<string name="none">Asnjë</string>
|
||||
<string name="superuser_toggle_notification">Njoftimet</string>
|
||||
<string name="superuser_toggle_revoke">Anulo</string>
|
||||
<string name="superuser_policy_none">Asnjë aplikacion nuk ka kërkuar ende leje për superpërdoruesin.</string>
|
||||
@@ -95,7 +96,7 @@
|
||||
<string name="safetynet_res_invalid">Përgjigjja është e pavlefshme </string>
|
||||
<string name="safetynet_attest_success">Suksese!</string>
|
||||
<string name="safetynet_attest_failure">Vërtetimi dështoi!</string>
|
||||
<string name="safetynet_attest_loading">Thjesht një sekondë…</string>
|
||||
<string name="safetynet_attest_loading">Prit një sekondë…</string>
|
||||
<string name="safetynet_attest_restart">Provo përsëri</string>
|
||||
|
||||
<!--MagiskHide-->
|
||||
@@ -111,7 +112,7 @@
|
||||
<string name="reboot_userspace">Ristartim normal</string>
|
||||
<string name="reboot_recovery">Ristartoni në recovery</string>
|
||||
<string name="reboot_bootloader">Ristartoni në bootloader</string>
|
||||
<string name="reboot_download">Ristartoni për në download</string>
|
||||
<string name="reboot_download">Ristartoni në download</string>
|
||||
<string name="reboot_edl">Ristartoni në EDL</string>
|
||||
<string name="module_version_author">%1$s nga %2$s</string>
|
||||
<string name="module_state_remove">Hiq</string>
|
||||
@@ -156,6 +157,12 @@
|
||||
<string name="settings_su_app">Vetëm aplikacionet</string>
|
||||
<string name="settings_su_adb">Vetëm ADB</string>
|
||||
<string name="settings_su_disable">Çaktivizuar</string>
|
||||
<string name="settings_su_request_10">10 Sekonda</string>
|
||||
<string name="settings_su_request_15">15 Sekonda</string>
|
||||
<string name="settings_su_request_20">20 Sekonda</string>
|
||||
<string name="settings_su_request_30">30 Sekonda</string>
|
||||
<string name="settings_su_request_45">45 Sekonda</string>
|
||||
<string name="settings_su_request_60">60 Sekonda</string>
|
||||
<string name="superuser_access">Hyrja në superpërdorues</string>
|
||||
<string name="auto_response">Përgjigje Automatike</string>
|
||||
<string name="request_timeout">Koha e kërkesës</string>
|
||||
@@ -207,7 +214,7 @@
|
||||
<string name="failure">Dështoi!</string>
|
||||
<string name="hide_manager_title">Fshehja e Magisk manager…</string>
|
||||
<string name="hide_manager_fail_toast">Fsheh Menaxherin e Magisk dështoi</string>
|
||||
<string name="restore_manager_fail_toast">Rikthe menaxherin e Magisk dështoi</string>
|
||||
<string name="restore_manager_fail_toast">Rikthimi i menaxherin e Magisk dështoi</string>
|
||||
<string name="open_link_failed_toast">Asnjë aplikacion nuk u gjet për të hapur lidhjen</string>
|
||||
<string name="complete_uninstall">Çinstalimi i plotë</string>
|
||||
<string name="restore_img">Rikthe Imazhet</string>
|
||||
|
@@ -132,7 +132,7 @@
|
||||
<string name="settings_dark_mode_message">Stilinize en iyi uyan modu seçin!</string>
|
||||
<string name="settings_dark_mode_light">Her Zaman Aydınlık</string>
|
||||
<string name="settings_dark_mode_system">Sistemi Takip Et</string>
|
||||
<string name="settings_dark_mode_dark">Her Zmana Karanlık</string>
|
||||
<string name="settings_dark_mode_dark">Her Zaman Karanlık</string>
|
||||
<string name="settings_download_path_title">İndirme dizini</string>
|
||||
<string name="settings_download_path_message">Dosyalar %1$s konumuna kaydedilecek</string>
|
||||
<string name="settings_clear_cache_title">Depo Önbelleğini Temizle</string>
|
||||
|
@@ -1,5 +1,10 @@
|
||||
# Magisk Manager Changelog
|
||||
|
||||
### v8.0.5
|
||||
|
||||
- Minor UI changes
|
||||
- Update internal scripts
|
||||
|
||||
### v8.0.4
|
||||
|
||||
- A lot of stability changes and minor bug fixes
|
||||
|
@@ -1,5 +1,11 @@
|
||||
# Magisk Changelog
|
||||
|
||||
### v21.3
|
||||
|
||||
- [MagiskInit] Avoid mounting `f2fs` userdata as it may result in kernel crashes. This shall fix a lot of bootloops
|
||||
- [MagiskBoot] Fix a minor header checksum bug for `DHTB` header and ASUS `blob` image formats
|
||||
- [MagiskHide] Allowing hiding isolated processes if the mount namespace is separated
|
||||
|
||||
### v21.2
|
||||
|
||||
- [MagiskInit] Detect 2SI after mounting `system_root` on legacy SAR devices
|
||||
|
@@ -17,7 +17,7 @@ Download and install the latest Magisk Manager. We use the app to gather some in
|
||||
|
||||
<p align="center"><img src="images/device_info.png" width="500"/></p>
|
||||
|
||||
Pay special attention to the **Ramdisk** info. If the result is **Yes**, congratulations, your device is perfect for installing Magisk! However, if the result is **No**, this means your device's boot partition does **NOT** include ramdisk, and unfortunately you would have to go through some hoops to make Magisk work properly.
|
||||
Pay special attention to the **Ramdisk** info. If the result is **Yes**, congratulations, your device is perfect for installing Magisk! However, if the result is **No** this means your device's boot partition does **NOT** include ramdisk. This means you will have to go through some extra steps to make Magisk work properly.
|
||||
|
||||
> **If your device does not have boot ramdisk, read the [Magisk in Recovery](#magisk-in-recovery) section after installing. The information in that section is VERY important!**
|
||||
|
||||
@@ -27,15 +27,15 @@ If you are using a Huawei device and the **SAR** result is **Yes**, please check
|
||||
|
||||
Otherwise, continue to [Patching Images](#patching-images).
|
||||
|
||||
(P.S.1 If your device have boot ramdisk, you can also install with [Custom Recovery](#custom-recovery))<br>
|
||||
(P.S.1 If your device has boot ramdisk, you can also install with [Custom Recovery](#custom-recovery))<br>
|
||||
(P.S.2 If you are interested in how Android boots and how it affects Magisk, check out [this document](boot.md))
|
||||
|
||||
## Patching Images
|
||||
|
||||
If your device have boot ramdisk, you need a copy of the `boot.img`<br>
|
||||
If your device has boot ramdisk, you need a copy of the `boot.img`<br>
|
||||
If your device does **NOT** have boot ramdisk, you need a copy of the `recovery.img`
|
||||
|
||||
You should be able to extract either of them from official firmware packages, your custom ROM zip (if using one), or go to [XDA-Developers](https://forum.xda-developers.com/) and seek for resources, guides, discussions, or ask for help in your device's forum.
|
||||
You should be able to extract the file you need from official firmware packages or your custom ROM zip (if using one). If you are still having trouble, go to [XDA-Developers](https://forum.xda-developers.com/) and look for resources, guides, discussions, or ask for help in your device's forum.
|
||||
|
||||
- Copy the boot/recovery image to your device
|
||||
- Press the **Install** button in the Magisk card
|
||||
@@ -52,7 +52,7 @@ For most devices, reboot into fastboot mode and flash with command:<br>
|
||||
|
||||
## Custom Recovery
|
||||
|
||||
In some custom recoveries of modern devices, the installer scripts either cannot properly detect the correct device info, or the recovery environment does not meet its expectation, causing the installation to fail (or looks like success but actually bootloops). If you face any issues, use the [Patch Image](#patching-images) method as it is guaranteed to work 100% of the time. Due to this reason, we no longer recommend installing Magisk through custom recoveries on modern devices. The custom recovery installation method exists mostly for legacy support.
|
||||
In some custom recoveries the installation may fail (this may look like success but actually bootloops). This is because the installer scripts cannot properly detect the correct device info or the recovery environment does not meet its expectation. If you face any issues, use the [Patch Image](#patching-images) method as it is guaranteed to work 100% of the time. Due to this reason, we no longer recommend installing Magisk through custom recoveries on modern devices. The custom recovery installation method exists mostly for legacy support.
|
||||
|
||||
- Download the Magisk installer zip
|
||||
- Reboot to custom recovery
|
||||
@@ -65,7 +65,7 @@ If your device does not have ramdisk in boot images, Magisk has no choice but to
|
||||
|
||||
When Magisk is installed in your recovery, **you CANNOT use custom recoveries to install/upgrade Magisk!** The only way to install/upgrade Magisk is through Magisk Manager. The app will be aware of your device state and install to the correct partition and reboot into the correct mode.
|
||||
|
||||
Since Magisk now hijacks the recovery of the device, there is a mechanism to let you *actually* boot into recovery mode when needed: it is determined by **how long you press volume up**.
|
||||
Since Magisk now hijacks the recovery of the device, there is a mechanism to let you *actually* boot into recovery mode when needed: it is determined by **how long you press the recovery key combo**.
|
||||
|
||||
Each device has its own key combo to boot into recovery, as an example for Galaxy S10 it is (Power + Bixby + Volume Up). A quick Google search should easily get you this info of your device. As soon as you press the combo and the device vibrates with a splash screen, release all buttons to boot into Magisk. If you decide to boot into actual recovery mode, continue to press volume up until you see the recovery screen.
|
||||
|
||||
|
11
docs/releases/21300.md
Normal file
11
docs/releases/21300.md
Normal file
@@ -0,0 +1,11 @@
|
||||
## 2021.1.16 Magisk v21.3
|
||||
|
||||
Happy 2021! v21.3 adds a workaround for devices with buggy F2FS Linux kernel drivers. This F2FS bug may cause bootloops on many devices. Checkout the full [v21.0 release notes](https://topjohnwu.github.io/Magisk/releases/21000.html) if coming from older releases.
|
||||
|
||||
### v21.3
|
||||
|
||||
- [MagiskInit] Avoid mounting `f2fs` userdata as it may result in kernel crashes. This shall fix a lot of bootloops
|
||||
- [MagiskBoot] Fix a minor header checksum bug for `DHTB` header and ASUS `blob` image formats
|
||||
- [MagiskHide] Allowing hiding isolated processes if the mount namespace is separated
|
||||
|
||||
### Full Changelog: [here](https://topjohnwu.github.io/Magisk/changes.html)
|
@@ -1,5 +1,7 @@
|
||||
# Release Notes
|
||||
|
||||
- [v21.3](21300.md)
|
||||
- [v21.2](21200.md)
|
||||
- [v21.1](21100.md)
|
||||
- [v21.0](21000.md)
|
||||
- [v20.4](20400.md)
|
||||
|
@@ -27,6 +27,6 @@ android.injected.testOnly=false
|
||||
kapt.incremental.apt=true
|
||||
|
||||
# Magisk
|
||||
magisk.versionCode=21200
|
||||
magisk.versionCode=21202
|
||||
magisk.ndkVersion=21d
|
||||
magisk.fullNdkVersion=21.3.6528147
|
||||
|
@@ -4,6 +4,9 @@ LOCAL_PATH := $(call my-dir)
|
||||
# Binaries
|
||||
########################
|
||||
|
||||
# Global toggle for the WIP zygote injection features
|
||||
ENABLE_INJECT := 0
|
||||
|
||||
ifdef B_MAGISK
|
||||
|
||||
include $(CLEAR_VARS)
|
||||
@@ -22,7 +25,6 @@ LOCAL_SRC_FILES := \
|
||||
core/restorecon.cpp \
|
||||
core/module.cpp \
|
||||
magiskhide/magiskhide.cpp \
|
||||
magiskhide/proc_monitor.cpp \
|
||||
magiskhide/hide_utils.cpp \
|
||||
magiskhide/hide_policy.cpp \
|
||||
resetprop/persist_properties.cpp \
|
||||
@@ -33,6 +35,18 @@ LOCAL_SRC_FILES := \
|
||||
su/su_daemon.cpp
|
||||
|
||||
LOCAL_LDLIBS := -llog
|
||||
LOCAL_CPPFLAGS := -DENABLE_INJECT=$(ENABLE_INJECT)
|
||||
|
||||
ifeq ($(ENABLE_INJECT),1)
|
||||
LOCAL_STATIC_LIBRARIES += libxhook
|
||||
LOCAL_SRC_FILES += \
|
||||
inject/entry.cpp \
|
||||
inject/utils.cpp \
|
||||
inject/hook.cpp
|
||||
else
|
||||
LOCAL_SRC_FILES += magiskhide/proc_monitor.cpp
|
||||
endif
|
||||
|
||||
include $(BUILD_EXECUTABLE)
|
||||
|
||||
endif
|
||||
@@ -140,15 +154,16 @@ include $(BUILD_EXECUTABLE)
|
||||
endif
|
||||
|
||||
ifdef B_TEST
|
||||
ifneq (,$(wildcard jni/test.cpp))
|
||||
|
||||
include $(CLEAR_VARS)
|
||||
LOCAL_MODULE := test
|
||||
LOCAL_STATIC_LIBRARIES := libutils
|
||||
LOCAL_C_INCLUDES := jni/include
|
||||
LOCAL_SRC_FILES := test.cpp
|
||||
LOCAL_LDFLAGS := -static
|
||||
include $(BUILD_EXECUTABLE)
|
||||
|
||||
endif
|
||||
endif
|
||||
|
||||
ifdef B_BB
|
||||
@@ -161,5 +176,4 @@ endif
|
||||
# Libraries
|
||||
########################
|
||||
include jni/utils/Android.mk
|
||||
include jni/systemproperties/Android.mk
|
||||
include jni/external/Android.mk
|
||||
|
@@ -1,6 +1,4 @@
|
||||
#include <libgen.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
|
||||
@@ -8,21 +6,27 @@
|
||||
#include <selinux.hpp>
|
||||
#include <utils.hpp>
|
||||
|
||||
using namespace std::literals;
|
||||
using namespace std;
|
||||
|
||||
using main_fun = int (*)(int, char *[]);
|
||||
|
||||
static main_fun applet_main[] = { su_client_main, resetprop_main, magiskhide_main, nullptr };
|
||||
|
||||
[[noreturn]] static void call_applet(int argc, char **argv) {
|
||||
static int call_applet(int argc, char *argv[]) {
|
||||
// Applets
|
||||
string_view base = basename(argv[0]);
|
||||
for (int i = 0; applet_names[i]; ++i) {
|
||||
if (strcmp(basename(argv[0]), applet_names[i]) == 0) {
|
||||
exit((*applet_main[i])(argc, argv));
|
||||
if (base == applet_names[i]) {
|
||||
return (*applet_main[i])(argc, argv);
|
||||
}
|
||||
}
|
||||
fprintf(stderr, "%s: applet not found\n", basename(argv[0]));
|
||||
exit(1);
|
||||
#if ENABLE_INJECT
|
||||
if (str_starts(base, "app_process")) {
|
||||
return app_process_main(argc, argv);
|
||||
}
|
||||
#endif
|
||||
fprintf(stderr, "%s: applet not found\n", base.data());
|
||||
return 1;
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
@@ -41,6 +45,6 @@ int main(int argc, char *argv[]) {
|
||||
}
|
||||
}
|
||||
|
||||
call_applet(argc, argv);
|
||||
return call_applet(argc, argv);
|
||||
}
|
||||
|
||||
|
@@ -13,6 +13,8 @@
|
||||
#include <resetprop.hpp>
|
||||
#include <selinux.hpp>
|
||||
|
||||
#include "core.hpp"
|
||||
|
||||
using namespace std;
|
||||
|
||||
static bool safe_mode = false;
|
||||
@@ -320,7 +322,7 @@ void post_fs_data(int client) {
|
||||
stop_magiskhide();
|
||||
} else {
|
||||
exec_common_scripts("post-fs-data");
|
||||
auto_start_magiskhide();
|
||||
auto_start_magiskhide(false);
|
||||
handle_modules();
|
||||
}
|
||||
|
||||
@@ -369,7 +371,7 @@ void boot_complete(int client) {
|
||||
if (access(SECURE_DIR, F_OK) != 0)
|
||||
xmkdir(SECURE_DIR, 0700);
|
||||
|
||||
auto_start_magiskhide();
|
||||
auto_start_magiskhide(true);
|
||||
|
||||
if (!check_manager()) {
|
||||
if (access(MANAGERAPK, F_OK) == 0) {
|
||||
|
26
native/jni/core/core.hpp
Normal file
26
native/jni/core/core.hpp
Normal file
@@ -0,0 +1,26 @@
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
extern bool RECOVERY_MODE;
|
||||
extern int DAEMON_STATE;
|
||||
|
||||
void unlock_blocks();
|
||||
void reboot();
|
||||
void setup_logfile(bool reset);
|
||||
|
||||
// Module stuffs
|
||||
void handle_modules();
|
||||
void magic_mount();
|
||||
void disable_modules();
|
||||
void remove_modules();
|
||||
void exec_module_scripts(const char *stage);
|
||||
|
||||
// Scripting
|
||||
void exec_script(const char *script);
|
||||
void exec_common_scripts(const char *stage);
|
||||
void exec_module_scripts(const char *stage, const std::vector<std::string> &module_list);
|
||||
void install_apk(const char *apk);
|
||||
[[noreturn]] void install_module(const char *file);
|
||||
|
@@ -1,6 +1,6 @@
|
||||
#include <fcntl.h>
|
||||
#include <pthread.h>
|
||||
#include <signal.h>
|
||||
#include <csignal>
|
||||
#include <libgen.h>
|
||||
#include <sys/un.h>
|
||||
#include <sys/types.h>
|
||||
@@ -16,11 +16,14 @@
|
||||
#include <flags.hpp>
|
||||
#include <stream.hpp>
|
||||
|
||||
#include "core.hpp"
|
||||
|
||||
using namespace std;
|
||||
|
||||
int SDK_INT = -1;
|
||||
bool RECOVERY_MODE = false;
|
||||
string MAGISKTMP;
|
||||
|
||||
bool RECOVERY_MODE = false;
|
||||
int DAEMON_STATE = STATE_NONE;
|
||||
|
||||
static struct stat self_st;
|
||||
@@ -33,10 +36,20 @@ static bool verify_client(pid_t pid) {
|
||||
return !(stat(path, &st) || st.st_dev != self_st.st_dev || st.st_ino != self_st.st_ino);
|
||||
}
|
||||
|
||||
static bool check_zygote(pid_t pid) {
|
||||
char buf[32];
|
||||
sprintf(buf, "/proc/%d/attr/current", pid);
|
||||
auto fp = open_file(buf, "r");
|
||||
if (!fp)
|
||||
return false;
|
||||
fscanf(fp.get(), "%s", buf);
|
||||
return buf == "u:r:zygote:s0"sv;
|
||||
}
|
||||
|
||||
static void request_handler(int client, int req_code, ucred cred) {
|
||||
switch (req_code) {
|
||||
case MAGISKHIDE:
|
||||
magiskhide_handler(client);
|
||||
magiskhide_handler(client, &cred);
|
||||
break;
|
||||
case SUPERUSER:
|
||||
su_daemon_handler(client, &cred);
|
||||
@@ -71,7 +84,12 @@ static void handle_request(int client) {
|
||||
// Verify client credentials
|
||||
ucred cred;
|
||||
get_client_cred(client, &cred);
|
||||
if (cred.uid != 0 && !verify_client(cred.pid))
|
||||
|
||||
bool is_root = cred.uid == 0;
|
||||
bool is_zygote = check_zygote(cred.pid);
|
||||
bool is_client = verify_client(cred.pid);
|
||||
|
||||
if (!is_root && !is_zygote && !is_client)
|
||||
goto shortcut;
|
||||
|
||||
req_code = read_int(client);
|
||||
@@ -80,13 +98,12 @@ static void handle_request(int client) {
|
||||
|
||||
// Check client permissions
|
||||
switch (req_code) {
|
||||
case MAGISKHIDE:
|
||||
case POST_FS_DATA:
|
||||
case LATE_START:
|
||||
case BOOT_COMPLETE:
|
||||
case SQLITE_CMD:
|
||||
case GET_PATH:
|
||||
if (cred.uid != 0) {
|
||||
if (!is_root) {
|
||||
write_int(client, ROOT_REQUIRED);
|
||||
goto shortcut;
|
||||
}
|
||||
@@ -97,6 +114,12 @@ static void handle_request(int client) {
|
||||
goto shortcut;
|
||||
}
|
||||
break;
|
||||
case MAGISKHIDE: // accept hide request from zygote
|
||||
if (!is_root && !is_zygote) {
|
||||
write_int(client, ROOT_REQUIRED);
|
||||
goto shortcut;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
// Simple requests
|
||||
@@ -189,21 +212,22 @@ static int magisk_log(int prio, const char *fmt, va_list ap) {
|
||||
return vfprintf(local_log_file.get(), buf, args);
|
||||
}
|
||||
|
||||
static void android_logging() {
|
||||
#define mlog(prio) [](auto fmt, auto ap){ return magisk_log(ANDROID_LOG_##prio, fmt, ap); }
|
||||
static void magisk_logging() {
|
||||
auto in_mem_file = make_stream_fp<byte_stream>(log_buf, log_buf_len);
|
||||
log_file.reset(in_mem_file.release(), [](FILE *) {
|
||||
free(log_buf);
|
||||
log_buf = nullptr;
|
||||
});
|
||||
log_cb.d = [](auto fmt, auto ap){ return magisk_log(ANDROID_LOG_DEBUG, fmt, ap); };
|
||||
log_cb.i = [](auto fmt, auto ap){ return magisk_log(ANDROID_LOG_INFO, fmt, ap); };
|
||||
log_cb.w = [](auto fmt, auto ap){ return magisk_log(ANDROID_LOG_WARN, fmt, ap); };
|
||||
log_cb.e = [](auto fmt, auto ap){ return magisk_log(ANDROID_LOG_ERROR, fmt, ap); };
|
||||
log_cb.d = mlog(DEBUG);
|
||||
log_cb.i = mlog(INFO);
|
||||
log_cb.w = mlog(WARN);
|
||||
log_cb.e = mlog(ERROR);
|
||||
log_cb.ex = nop_ex;
|
||||
}
|
||||
|
||||
static void daemon_entry(int ppid) {
|
||||
android_logging();
|
||||
magisk_logging();
|
||||
|
||||
int fd = xopen("/dev/null", O_WRONLY);
|
||||
xdup2(fd, STDOUT_FILENO);
|
||||
|
@@ -1,14 +1,10 @@
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <time.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include <dlfcn.h>
|
||||
#include <sys/stat.h>
|
||||
|
||||
#include <magisk.hpp>
|
||||
#include <db.hpp>
|
||||
#include <daemon.hpp>
|
||||
#include <socket.hpp>
|
||||
#include <utils.hpp>
|
||||
|
||||
#define DB_VERSION 10
|
||||
@@ -373,8 +369,8 @@ bool validate_manager(string &pkg, int userid, struct stat *st) {
|
||||
|
||||
void exec_sql(int client) {
|
||||
run_finally f([=]{ close(client); });
|
||||
char *sql = read_string(client);
|
||||
char *err = db_exec(sql, [&](db_row &row) -> bool {
|
||||
string sql = read_string(client);
|
||||
char *err = db_exec(sql.data(), [client](db_row &row) -> bool {
|
||||
string out;
|
||||
bool first = true;
|
||||
for (auto it : row) {
|
||||
@@ -384,11 +380,9 @@ void exec_sql(int client) {
|
||||
out += '=';
|
||||
out += it.second;
|
||||
}
|
||||
write_int(client, out.length());
|
||||
xwrite(client, out.data(), out.length());
|
||||
write_string(client, out);
|
||||
return true;
|
||||
});
|
||||
free(sql);
|
||||
write_int(client, 0);
|
||||
db_err_cmd(err, return; );
|
||||
}
|
||||
|
@@ -7,6 +7,8 @@
|
||||
#include <selinux.hpp>
|
||||
#include <flags.hpp>
|
||||
|
||||
#include "core.hpp"
|
||||
|
||||
using namespace std;
|
||||
|
||||
[[noreturn]] static void usage() {
|
||||
@@ -54,9 +56,8 @@ int magisk_main(int argc, char *argv[]) {
|
||||
} else if (argv[1] == "-v"sv) {
|
||||
int fd = connect_daemon();
|
||||
write_int(fd, CHECK_VERSION);
|
||||
char *v = read_string(fd);
|
||||
printf("%s\n", v);
|
||||
free(v);
|
||||
string v = read_string(fd);
|
||||
printf("%s\n", v.data());
|
||||
return 0;
|
||||
} else if (argv[1] == "-V"sv) {
|
||||
int fd = connect_daemon();
|
||||
@@ -99,13 +100,12 @@ int magisk_main(int argc, char *argv[]) {
|
||||
int fd = connect_daemon();
|
||||
write_int(fd, SQLITE_CMD);
|
||||
write_string(fd, argv[2]);
|
||||
string res;
|
||||
for (;;) {
|
||||
char *res = read_string(fd);
|
||||
if (res[0] == '\0') {
|
||||
read_string(fd, res);
|
||||
if (res.empty())
|
||||
return 0;
|
||||
}
|
||||
printf("%s\n", res);
|
||||
free(res);
|
||||
printf("%s\n", res.data());
|
||||
}
|
||||
} else if (argv[1] == "--remove-modules"sv) {
|
||||
int fd = connect_daemon();
|
||||
@@ -114,8 +114,8 @@ int magisk_main(int argc, char *argv[]) {
|
||||
} else if (argv[1] == "--path"sv) {
|
||||
int fd = connect_daemon();
|
||||
write_int(fd, GET_PATH);
|
||||
char *path = read_string(fd);
|
||||
printf("%s\n", path);
|
||||
string path = read_string(fd);
|
||||
printf("%s\n", path.data());
|
||||
return 0;
|
||||
} else if (argc >= 3 && argv[1] == "--install-module"sv) {
|
||||
install_module(argv[2]);
|
||||
|
@@ -5,9 +5,10 @@
|
||||
#include <utils.hpp>
|
||||
#include <magisk.hpp>
|
||||
#include <selinux.hpp>
|
||||
#include <daemon.hpp>
|
||||
#include <resetprop.hpp>
|
||||
|
||||
#include "core.hpp"
|
||||
|
||||
using namespace std;
|
||||
|
||||
#define VLOGD(tag, from, to) LOGD("%-8s: %s <- %s\n", tag, to, from)
|
||||
|
@@ -1,7 +1,6 @@
|
||||
#include <string_view>
|
||||
|
||||
#include <magisk.hpp>
|
||||
#include <daemon.hpp>
|
||||
#include <selinux.hpp>
|
||||
#include <utils.hpp>
|
||||
|
||||
|
@@ -6,6 +6,8 @@
|
||||
#include <utils.hpp>
|
||||
#include <selinux.hpp>
|
||||
|
||||
#include "core.hpp"
|
||||
|
||||
using namespace std;
|
||||
|
||||
#define BBEXEC_CMD bbpath(), "sh"
|
||||
|
@@ -1,11 +1,11 @@
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <fcntl.h>
|
||||
#include <endian.h>
|
||||
|
||||
#include <socket.hpp>
|
||||
#include <utils.hpp>
|
||||
|
||||
using namespace std;
|
||||
|
||||
static size_t socket_len(sockaddr_un *sun) {
|
||||
if (sun->sun_path[0])
|
||||
return sizeof(sa_family_t) + strlen(sun->sun_path) + 1;
|
||||
@@ -162,43 +162,21 @@ void write_int_be(int fd, int val) {
|
||||
xwrite(fd, &nl, sizeof(nl));
|
||||
}
|
||||
|
||||
static char *rd_str(int fd, int len) {
|
||||
char *val = (char *) xmalloc(sizeof(char) * (len + 1));
|
||||
xxread(fd, val, len);
|
||||
val[len] = '\0';
|
||||
return val;
|
||||
}
|
||||
|
||||
char* read_string(int fd) {
|
||||
void read_string(int fd, std::string &str) {
|
||||
int len = read_int(fd);
|
||||
return rd_str(fd, len);
|
||||
str.clear();
|
||||
str.resize(len);
|
||||
xxread(fd, str.data(), len);
|
||||
}
|
||||
|
||||
char* read_string_be(int fd) {
|
||||
int len = read_int_be(fd);
|
||||
return rd_str(fd, len);
|
||||
string read_string(int fd) {
|
||||
string str;
|
||||
read_string(fd, str);
|
||||
return str;
|
||||
}
|
||||
|
||||
void write_string(int fd, const char *val) {
|
||||
void write_string(int fd, string_view str) {
|
||||
if (fd < 0) return;
|
||||
int len = strlen(val);
|
||||
write_int(fd, len);
|
||||
xwrite(fd, val, len);
|
||||
}
|
||||
|
||||
void write_string_be(int fd, const char *val) {
|
||||
int len = strlen(val);
|
||||
write_int_be(fd, len);
|
||||
xwrite(fd, val, len);
|
||||
}
|
||||
|
||||
void write_key_value(int fd, const char *key, const char *val) {
|
||||
write_string_be(fd, key);
|
||||
write_string_be(fd, val);
|
||||
}
|
||||
|
||||
void write_key_token(int fd, const char *key, int tok) {
|
||||
char val[16];
|
||||
sprintf(val, "%d", tok);
|
||||
write_key_value(fd, key, val);
|
||||
write_int(fd, str.size());
|
||||
xwrite(fd, str.data(), str.size());
|
||||
}
|
||||
|
21
native/jni/external/Android.mk
vendored
21
native/jni/external/Android.mk
vendored
@@ -353,4 +353,23 @@ LOCAL_SRC_FILES := \
|
||||
pcre/dist2/src/pcre2_xclass.c
|
||||
include $(BUILD_STATIC_LIBRARY)
|
||||
|
||||
include $(LOCAL_PATH)/mincrypt/Android.mk
|
||||
# libxhook.a
|
||||
include $(CLEAR_VARS)
|
||||
LOCAL_MODULE:= libxhook
|
||||
LOCAL_C_INCLUDES := $(LOCAL_PATH)/xhook/libxhook/jni
|
||||
LOCAL_EXPORT_C_INCLUDES := $(LOCAL_C_INCLUDES)
|
||||
LOCAL_CFLAGS := -Wall -Wextra -Werror -fvisibility=hidden
|
||||
LOCAL_CONLYFLAGS := -std=c11
|
||||
LOCAL_SRC_FILES := \
|
||||
xhook/libxhook/jni/xh_log.c \
|
||||
xhook/libxhook/jni/xh_version.c \
|
||||
xhook/libxhook/jni/xh_jni.c \
|
||||
xhook/libxhook/jni/xhook.c \
|
||||
xhook/libxhook/jni/xh_core.c \
|
||||
xhook/libxhook/jni/xh_util.c \
|
||||
xhook/libxhook/jni/xh_elf.c
|
||||
include $(BUILD_STATIC_LIBRARY)
|
||||
|
||||
CWD := $(LOCAL_PATH)
|
||||
include $(CWD)/systemproperties/Android.mk
|
||||
include $(CWD)/mincrypt/Android.mk
|
||||
|
1
native/jni/external/xhook
vendored
Submodule
1
native/jni/external/xhook
vendored
Submodule
Submodule native/jni/external/xhook added at 9180bd7409
@@ -2,7 +2,6 @@
|
||||
|
||||
#include <pthread.h>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include <socket.hpp>
|
||||
|
||||
@@ -39,38 +38,21 @@ enum {
|
||||
STATE_BOOT_COMPLETE
|
||||
};
|
||||
|
||||
extern int SDK_INT;
|
||||
extern bool RECOVERY_MODE;
|
||||
extern int DAEMON_STATE;
|
||||
#define APP_DATA_DIR (SDK_INT >= 24 ? "/data/user_de" : "/data/user")
|
||||
int connect_daemon(bool create = false);
|
||||
|
||||
// Daemon handlers
|
||||
void post_fs_data(int client);
|
||||
void late_start(int client);
|
||||
void boot_complete(int client);
|
||||
void magiskhide_handler(int client);
|
||||
void magiskhide_handler(int client, ucred *cred);
|
||||
void su_daemon_handler(int client, ucred *credential);
|
||||
|
||||
// Misc
|
||||
int connect_daemon(bool create = false);
|
||||
void unlock_blocks();
|
||||
void reboot();
|
||||
void setup_logfile(bool reset);
|
||||
|
||||
// Module stuffs
|
||||
void handle_modules();
|
||||
void magic_mount();
|
||||
void disable_modules();
|
||||
void remove_modules();
|
||||
void exec_module_scripts(const char *stage);
|
||||
|
||||
// MagiskHide
|
||||
void auto_start_magiskhide();
|
||||
void auto_start_magiskhide(bool late_props);
|
||||
int stop_magiskhide();
|
||||
|
||||
// Scripting
|
||||
void exec_script(const char *script);
|
||||
void exec_common_scripts(const char *stage);
|
||||
void exec_module_scripts(const char *stage, const std::vector<std::string> &module_list);
|
||||
void install_apk(const char *apk);
|
||||
[[noreturn]] void install_module(const char *file);
|
||||
#if ENABLE_INJECT
|
||||
// For injected process to access daemon
|
||||
int remote_check_hide(int uid, const char *process);
|
||||
void remote_request_hide();
|
||||
#endif
|
||||
|
@@ -30,9 +30,13 @@ constexpr const char *init_applet[] = { "magiskpolicy", "supolicy", nullptr };
|
||||
#define POST_FS_DATA_WAIT_TIME 40
|
||||
#define POST_FS_DATA_SCRIPT_MAX_TIME 35
|
||||
|
||||
extern int SDK_INT;
|
||||
#define APP_DATA_DIR (SDK_INT >= 24 ? "/data/user_de" : "/data/user")
|
||||
|
||||
// Multi-call entrypoints
|
||||
int magisk_main(int argc, char *argv[]);
|
||||
int magiskhide_main(int argc, char *argv[]);
|
||||
int magiskpolicy_main(int argc, char *argv[]);
|
||||
int su_client_main(int argc, char *argv[]);
|
||||
int resetprop_main(int argc, char *argv[]);
|
||||
int app_process_main(int argc, char *argv[]);
|
||||
|
@@ -2,6 +2,7 @@
|
||||
|
||||
#include <sys/un.h>
|
||||
#include <sys/socket.h>
|
||||
#include <string_view>
|
||||
|
||||
socklen_t setup_sockaddr(sockaddr_un *sun, const char *name);
|
||||
int socket_accept(int sockfd, int timeout);
|
||||
@@ -12,9 +13,6 @@ int read_int(int fd);
|
||||
int read_int_be(int fd);
|
||||
void write_int(int fd, int val);
|
||||
void write_int_be(int fd, int val);
|
||||
char *read_string(int fd);
|
||||
char *read_string_be(int fd);
|
||||
void write_string(int fd, const char *val);
|
||||
void write_string_be(int fd, const char *val);
|
||||
void write_key_value(int fd, const char *key, const char *val);
|
||||
void write_key_token(int fd, const char *key, int tok);
|
||||
std::string read_string(int fd);
|
||||
void read_string(int fd, std::string &str);
|
||||
void write_string(int fd, std::string_view str);
|
||||
|
@@ -11,6 +11,8 @@
|
||||
|
||||
using namespace std;
|
||||
|
||||
vector<string> mount_list;
|
||||
|
||||
template<typename Func>
|
||||
static void parse_cmdline(const Func &fn) {
|
||||
char cmdline[4096];
|
||||
@@ -147,10 +149,13 @@ void load_kernel_info(cmdline *cmd) {
|
||||
xmkdir("/sys", 0755);
|
||||
xmount("sysfs", "/sys", "sysfs", 0, nullptr);
|
||||
|
||||
mount_list.emplace_back("/proc");
|
||||
mount_list.emplace_back("/sys");
|
||||
|
||||
// Log to kernel
|
||||
setup_klog();
|
||||
|
||||
parse_cmdline([=](string_view key, const char *value) -> void {
|
||||
parse_cmdline([=](string_view key, const char *value) {
|
||||
if (key == "androidboot.slot_suffix") {
|
||||
strcpy(cmd->slot, value);
|
||||
} else if (key == "androidboot.slot") {
|
||||
@@ -160,6 +165,8 @@ void load_kernel_info(cmdline *cmd) {
|
||||
cmd->skip_initramfs = true;
|
||||
} else if (key == "androidboot.force_normal_boot") {
|
||||
cmd->force_normal_boot = value[0] == '1';
|
||||
} else if (key == "rootwait") {
|
||||
cmd->rootwait = true;
|
||||
} else if (key == "androidboot.android_dt_dir") {
|
||||
strcpy(cmd->dt_dir, value);
|
||||
} else if (key == "androidboot.hardware") {
|
||||
@@ -174,6 +181,7 @@ void load_kernel_info(cmdline *cmd) {
|
||||
LOGD("Kernel cmdline info:\n");
|
||||
LOGD("skip_initramfs=[%d]\n", cmd->skip_initramfs);
|
||||
LOGD("force_normal_boot=[%d]\n", cmd->force_normal_boot);
|
||||
LOGD("rootwait=[%d]\n", cmd->rootwait);
|
||||
LOGD("slot=[%s]\n", cmd->slot);
|
||||
LOGD("dt_dir=[%s]\n", cmd->dt_dir);
|
||||
LOGD("fstab_suffix=[%s]\n", cmd->fstab_suffix);
|
||||
@@ -210,6 +218,6 @@ bool check_two_stage() {
|
||||
if (access("/system/bin/init", F_OK) == 0)
|
||||
return true;
|
||||
// If we still have no indication, parse the original init and see what's up
|
||||
auto init = raw_data::mmap_ro("/.backup/init");
|
||||
auto init = mmap_data::ro("/.backup/init");
|
||||
return init.contains("selinux_setup");
|
||||
}
|
||||
|
@@ -5,6 +5,7 @@
|
||||
struct cmdline {
|
||||
bool skip_initramfs;
|
||||
bool force_normal_boot;
|
||||
bool rootwait;
|
||||
char slot[3];
|
||||
char dt_dir[64];
|
||||
char fstab_suffix[32];
|
||||
@@ -28,6 +29,8 @@ struct fstab_entry {
|
||||
#define INIT_SOCKET "MAGISKINIT"
|
||||
#define DEFAULT_DT_DIR "/proc/device-tree/firmware/android"
|
||||
|
||||
extern std::vector<std::string> mount_list;
|
||||
|
||||
void load_kernel_info(cmdline *cmd);
|
||||
bool check_two_stage();
|
||||
int dump_magisk(const char *path, mode_t mode);
|
||||
@@ -41,20 +44,19 @@ class BaseInit {
|
||||
protected:
|
||||
cmdline *cmd;
|
||||
char **argv;
|
||||
std::vector<std::string> mount_list;
|
||||
|
||||
[[noreturn]] void exec_init();
|
||||
void read_dt_fstab(std::vector<fstab_entry> &fstab);
|
||||
public:
|
||||
BaseInit(char *argv[], cmdline *cmd) : cmd(cmd), argv(argv), mount_list{"/sys", "/proc"} {}
|
||||
BaseInit(char *argv[], cmdline *cmd) : cmd(cmd), argv(argv) {}
|
||||
virtual ~BaseInit() = default;
|
||||
virtual void start() = 0;
|
||||
};
|
||||
|
||||
class MagiskInit : public BaseInit {
|
||||
protected:
|
||||
auto_data<HEAP> self;
|
||||
auto_data<HEAP> config;
|
||||
mmap_data self;
|
||||
mmap_data config;
|
||||
std::string custom_rules_dir;
|
||||
|
||||
void mount_with_dt();
|
||||
@@ -99,8 +101,6 @@ private:
|
||||
public:
|
||||
SecondStageInit(char *argv[]) : SARBase(argv, nullptr) {
|
||||
LOGD("%s\n", __FUNCTION__);
|
||||
// Do not unmount /sys and /proc
|
||||
mount_list.clear();
|
||||
};
|
||||
void start() override {
|
||||
prepare();
|
||||
|
@@ -228,9 +228,9 @@ void MagiskInit::mount_rules_dir(const char *dev_base, const char *mnt_base) {
|
||||
if (setup_block(false) < 0)
|
||||
goto cache;
|
||||
}
|
||||
// Try to mount with either ext4 or f2fs
|
||||
// Failure means either FDE or metadata encryption
|
||||
if (!do_mount("ext4") && !do_mount("f2fs"))
|
||||
// WARNING: DO NOT ATTEMPT TO MOUNT F2FS AS IT MAY CRASH THE KERNEL
|
||||
// Failure means either f2fs, FDE, or metadata encryption
|
||||
if (!do_mount("ext4"))
|
||||
goto cache;
|
||||
|
||||
strcpy(p, "/data/unencrypted");
|
||||
@@ -289,7 +289,7 @@ success:
|
||||
}
|
||||
|
||||
void RootFSInit::early_mount() {
|
||||
self = raw_data::read("/init");
|
||||
self = mmap_data::ro("/init");
|
||||
|
||||
LOGD("Restoring /init\n");
|
||||
rename("/.backup/init", "/init");
|
||||
@@ -301,14 +301,16 @@ void SARBase::backup_files() {
|
||||
if (access("/overlay.d", F_OK) == 0)
|
||||
backup_folder("/overlay.d", overlays);
|
||||
|
||||
self = raw_data::read("/proc/self/exe");
|
||||
self = mmap_data::ro("/proc/self/exe");
|
||||
if (access("/.backup/.magisk", R_OK) == 0)
|
||||
config = raw_data::read("/.backup/.magisk");
|
||||
config = mmap_data::ro("/.backup/.magisk");
|
||||
}
|
||||
|
||||
void SARBase::mount_system_root() {
|
||||
LOGD("Early mount system_root\n");
|
||||
strcpy(blk_info.block_dev, "/dev/root");
|
||||
|
||||
do {
|
||||
// Try legacy SAR dm-verity
|
||||
strcpy(blk_info.partname, "vroot");
|
||||
auto dev = setup_block(false);
|
||||
@@ -326,6 +328,9 @@ void SARBase::mount_system_root() {
|
||||
if (dev >= 0)
|
||||
goto mount_root;
|
||||
|
||||
// Poll forever if rootwait was given in cmdline
|
||||
} while (cmd->rootwait);
|
||||
|
||||
// We don't really know what to do at this point...
|
||||
LOGE("Cannot find root partition, abort\n");
|
||||
exit(1);
|
||||
@@ -340,10 +345,8 @@ void SARInit::early_mount() {
|
||||
mount_system_root();
|
||||
switch_root("/system_root");
|
||||
|
||||
{
|
||||
auto init = raw_data::mmap_ro("/init");
|
||||
is_two_stage = init.contains("selinux_setup");
|
||||
}
|
||||
// Use the apex folder to determine whether 2SI (Android 10+)
|
||||
is_two_stage = access("/apex", F_OK) == 0;
|
||||
LOGD("is_two_stage: [%d]\n", is_two_stage);
|
||||
|
||||
if (!is_two_stage) {
|
||||
@@ -378,7 +381,7 @@ void BaseInit::exec_init() {
|
||||
static void patch_socket_name(const char *path) {
|
||||
char rstr[16];
|
||||
gen_rand_str(rstr, sizeof(rstr));
|
||||
auto bin = raw_data::mmap_rw(path);
|
||||
auto bin = mmap_data::rw(path);
|
||||
bin.patch({ make_pair(MAIN_SOCKET, rstr) });
|
||||
}
|
||||
|
||||
|
@@ -2,7 +2,7 @@
|
||||
|
||||
using namespace std;
|
||||
|
||||
int data_holder::patch(str_pairs list) {
|
||||
int mmap_data::patch(str_pairs list) {
|
||||
if (buf == nullptr)
|
||||
return 0;
|
||||
int count = 0;
|
||||
@@ -20,7 +20,7 @@ int data_holder::patch(str_pairs list) {
|
||||
return count;
|
||||
}
|
||||
|
||||
bool data_holder::contains(string_view pattern) {
|
||||
bool mmap_data::contains(string_view pattern) {
|
||||
if (buf == nullptr)
|
||||
return false;
|
||||
for (uint8_t *p = buf, *eof = buf + sz; p < eof; ++p) {
|
||||
@@ -32,33 +32,21 @@ bool data_holder::contains(string_view pattern) {
|
||||
return false;
|
||||
}
|
||||
|
||||
void data_holder::consume(data_holder &other) {
|
||||
void mmap_data::consume(mmap_data &other) {
|
||||
buf = other.buf;
|
||||
sz = other.sz;
|
||||
other.buf = nullptr;
|
||||
other.sz = 0;
|
||||
}
|
||||
|
||||
auto_data<HEAP> raw_data::read(int fd) {
|
||||
auto_data<HEAP> data;
|
||||
fd_full_read(fd, data.buf, data.sz);
|
||||
mmap_data mmap_data::rw(const char *name) {
|
||||
mmap_data data;
|
||||
mmap_rw(name, data.buf, data.sz);
|
||||
return data;
|
||||
}
|
||||
|
||||
auto_data<HEAP> raw_data::read(const char *name) {
|
||||
auto_data<HEAP> data;
|
||||
full_read(name, data.buf, data.sz);
|
||||
return data;
|
||||
}
|
||||
|
||||
auto_data<MMAP> raw_data::mmap_rw(const char *name) {
|
||||
auto_data<MMAP> data;
|
||||
::mmap_rw(name, data.buf, data.sz);
|
||||
return data;
|
||||
}
|
||||
|
||||
auto_data<MMAP> raw_data::mmap_ro(const char *name) {
|
||||
auto_data<MMAP> data;
|
||||
::mmap_ro(name, data.buf, data.sz);
|
||||
mmap_data mmap_data::ro(const char *name) {
|
||||
mmap_data data;
|
||||
mmap_ro(name, data.buf, data.sz);
|
||||
return data;
|
||||
}
|
||||
|
@@ -2,31 +2,23 @@
|
||||
|
||||
#include <utils.hpp>
|
||||
|
||||
struct data_holder {
|
||||
struct mmap_data {
|
||||
uint8_t *buf = nullptr;
|
||||
size_t sz = 0;
|
||||
|
||||
mmap_data() = default;
|
||||
mmap_data(const mmap_data&) = delete;
|
||||
mmap_data(mmap_data &&other) { consume(other); }
|
||||
~mmap_data() { if (buf) munmap(buf, sz); }
|
||||
mmap_data& operator=(mmap_data &&other) { consume(other); return *this; }
|
||||
|
||||
using str_pairs = std::initializer_list<std::pair<std::string_view, std::string_view>>;
|
||||
int patch(str_pairs list);
|
||||
bool contains(std::string_view pattern);
|
||||
protected:
|
||||
void consume(data_holder &other);
|
||||
};
|
||||
|
||||
enum data_type { HEAP, MMAP };
|
||||
template <data_type T>
|
||||
struct auto_data : public data_holder {
|
||||
auto_data<T>() = default;
|
||||
auto_data<T>(const auto_data&) = delete;
|
||||
auto_data<T>(auto_data<T> &&other) { consume(other); }
|
||||
~auto_data<T>() {}
|
||||
auto_data<T>& operator=(auto_data<T> &&other) { consume(other); return *this; }
|
||||
};
|
||||
template <> inline auto_data<MMAP>::~auto_data<MMAP>() { if (buf) munmap(buf, sz); }
|
||||
template <> inline auto_data<HEAP>::~auto_data<HEAP>() { free(buf); }
|
||||
static mmap_data rw(const char *name);
|
||||
static mmap_data ro(const char *name);
|
||||
|
||||
namespace raw_data {
|
||||
auto_data<HEAP> read(const char *name);
|
||||
auto_data<HEAP> read(int fd);
|
||||
auto_data<MMAP> mmap_rw(const char *name);
|
||||
auto_data<MMAP> mmap_ro(const char *name);
|
||||
}
|
||||
private:
|
||||
void consume(mmap_data &other);
|
||||
};
|
||||
|
@@ -9,12 +9,6 @@
|
||||
#include "init.hpp"
|
||||
#include "magiskrc.inc"
|
||||
|
||||
#ifdef USE_64BIT
|
||||
#define LIBNAME "lib64"
|
||||
#else
|
||||
#define LIBNAME "lib"
|
||||
#endif
|
||||
|
||||
using namespace std;
|
||||
|
||||
static vector<string> rc_list;
|
||||
@@ -68,7 +62,7 @@ static void load_overlay_rc(const char *overlay) {
|
||||
// Do not allow overwrite init.rc
|
||||
unlinkat(dfd, "init.rc", 0);
|
||||
for (dirent *entry; (entry = xreaddir(dir.get()));) {
|
||||
if (strend(entry->d_name, ".rc") == 0) {
|
||||
if (str_ends(entry->d_name, ".rc")) {
|
||||
LOGD("Found rc script [%s]\n", entry->d_name);
|
||||
int rc = xopenat(dfd, entry->d_name, O_RDONLY | O_CLOEXEC);
|
||||
rc_list.push_back(fd_full_read(rc));
|
||||
@@ -182,7 +176,6 @@ static void magic_mount(const string &sdir, const string &ddir = "") {
|
||||
|
||||
#define ROOTMIR MIRRDIR "/system_root"
|
||||
#define MONOPOLICY "/sepolicy"
|
||||
#define LIBSELINUX "/system/" LIBNAME "/libselinux.so"
|
||||
#define NEW_INITRC "/system/etc/init/hw/init.rc"
|
||||
|
||||
void SARBase::patch_rootdir() {
|
||||
@@ -218,7 +211,7 @@ void SARBase::patch_rootdir() {
|
||||
int patch_count;
|
||||
{
|
||||
int src = xopen("/init", O_RDONLY | O_CLOEXEC);
|
||||
auto init = raw_data::read(src);
|
||||
auto init = mmap_data::ro("/init");
|
||||
patch_count = init.patch({
|
||||
make_pair(SPLIT_PLAT_CIL, "xxx"), /* Force loading monolithic sepolicy */
|
||||
make_pair(MONOPOLICY, sepol) /* Redirect /sepolicy to custom path */
|
||||
@@ -231,15 +224,25 @@ void SARBase::patch_rootdir() {
|
||||
close(dest);
|
||||
}
|
||||
|
||||
if (patch_count != 2 && access(LIBSELINUX, F_OK) == 0) {
|
||||
if (patch_count != 2) {
|
||||
// init is dynamically linked, need to patch libselinux
|
||||
auto lib = raw_data::read(LIBSELINUX);
|
||||
const char *path = "/system/lib64/libselinux.so";
|
||||
if (access(path, F_OK) != 0) {
|
||||
path = "/system/lib/libselinux.so";
|
||||
if (access(path, F_OK) != 0)
|
||||
path = nullptr;
|
||||
}
|
||||
if (path) {
|
||||
char ovl[128];
|
||||
sprintf(ovl, ROOTOVL "%s", path);
|
||||
auto lib = mmap_data::ro(path);
|
||||
lib.patch({make_pair(MONOPOLICY, sepol)});
|
||||
xmkdirs(dirname(ROOTOVL LIBSELINUX), 0755);
|
||||
int dest = xopen(ROOTOVL LIBSELINUX, O_CREAT | O_WRONLY | O_CLOEXEC, 0);
|
||||
xmkdirs(dirname(ovl), 0755);
|
||||
int dest = xopen(ovl, O_CREAT | O_WRONLY | O_CLOEXEC, 0);
|
||||
xwrite(dest, lib.buf, lib.sz);
|
||||
close(dest);
|
||||
clone_attr(LIBSELINUX, ROOTOVL LIBSELINUX);
|
||||
clone_attr(path, ovl);
|
||||
}
|
||||
}
|
||||
|
||||
// sepolicy
|
||||
@@ -251,10 +254,9 @@ void SARBase::patch_rootdir() {
|
||||
if (connect(sockfd, (struct sockaddr*) &sun, setup_sockaddr(&sun, INIT_SOCKET)) == 0) {
|
||||
LOGD("ACK init daemon to write backup files\n");
|
||||
// Let daemon know where tmp_dir is
|
||||
write_string(sockfd, tmp_dir.data());
|
||||
write_string(sockfd, tmp_dir);
|
||||
// Wait for daemon to finish restoring files
|
||||
int ack;
|
||||
read(sockfd, &ack, sizeof(ack));
|
||||
read_int(sockfd);
|
||||
} else {
|
||||
LOGD("Restore backup files locally\n");
|
||||
restore_folder(ROOTOVL, overlays);
|
||||
@@ -301,7 +303,7 @@ void RootFSInit::patch_rootfs() {
|
||||
}
|
||||
|
||||
if (patch_sepolicy("/sepolicy")) {
|
||||
auto init = raw_data::mmap_rw("/init");
|
||||
auto init = mmap_data::rw("/init");
|
||||
init.patch({ make_pair(SPLIT_PLAT_CIL, "xxx") });
|
||||
}
|
||||
|
||||
@@ -331,8 +333,8 @@ void MagiskProxy::start() {
|
||||
xmount(nullptr, "/", nullptr, MS_REMOUNT, nullptr);
|
||||
|
||||
// Backup stuffs before removing them
|
||||
self = raw_data::read("/sbin/magisk");
|
||||
config = raw_data::read("/.backup/.magisk");
|
||||
self = mmap_data::ro("/sbin/magisk");
|
||||
config = mmap_data::ro("/.backup/.magisk");
|
||||
char custom_rules_dir[64];
|
||||
custom_rules_dir[0] = '\0';
|
||||
xreadlink(TMP_RULESDIR, custom_rules_dir, sizeof(custom_rules_dir));
|
||||
|
@@ -38,35 +38,58 @@ void FirstStageInit::prepare() {
|
||||
rename("/.backup/init", "/init");
|
||||
}
|
||||
|
||||
// Try to load fstab from dt
|
||||
vector<fstab_entry> fstab;
|
||||
read_dt_fstab(fstab);
|
||||
|
||||
char fstab_file[128];
|
||||
fstab_file[0] = '\0';
|
||||
|
||||
// Find existing fstab file
|
||||
for (const char *hw : { cmd->fstab_suffix, cmd->hardware, cmd->hardware_plat }) {
|
||||
if (hw[0] == '\0')
|
||||
for (const char *suffix : { cmd->fstab_suffix, cmd->hardware, cmd->hardware_plat }) {
|
||||
if (suffix[0] == '\0')
|
||||
continue;
|
||||
sprintf(fstab_file, "fstab.%s", hw);
|
||||
for (const char *prefix: { "odm/etc/fstab", "vendor/etc/fstab", "fstab" }) {
|
||||
sprintf(fstab_file, "%s.%s", prefix, suffix);
|
||||
if (access(fstab_file, F_OK) != 0) {
|
||||
fstab_file[0] = '\0';
|
||||
continue;
|
||||
} else {
|
||||
LOGD("Found fstab file: %s\n", fstab_file);
|
||||
break;
|
||||
goto exit_loop;
|
||||
}
|
||||
}
|
||||
}
|
||||
exit_loop:
|
||||
|
||||
// Try to load dt fstab
|
||||
vector<fstab_entry> fstab;
|
||||
read_dt_fstab(fstab);
|
||||
|
||||
if (!fstab.empty()) {
|
||||
// Dump dt fstab to fstab file in rootfs and force init to use it instead
|
||||
|
||||
// All dt fstab entries should be first_stage_mount
|
||||
for (auto &entry : fstab) {
|
||||
if (!str_contains(entry.fsmgr_flags, "first_stage_mount")) {
|
||||
if (!entry.fsmgr_flags.empty())
|
||||
entry.fsmgr_flags += ',';
|
||||
entry.fsmgr_flags += "first_stage_mount";
|
||||
}
|
||||
}
|
||||
|
||||
if (fstab.empty()) {
|
||||
// fstab has to be somewhere in ramdisk
|
||||
if (fstab_file[0] == '\0') {
|
||||
LOGE("Cannot find fstab file in ramdisk!\n");
|
||||
const char *suffix =
|
||||
cmd->fstab_suffix[0] ? cmd->fstab_suffix :
|
||||
(cmd->hardware[0] ? cmd->hardware :
|
||||
(cmd->hardware_plat[0] ? cmd->hardware_plat : nullptr));
|
||||
if (suffix == nullptr) {
|
||||
LOGE("Cannot determine fstab suffix!\n");
|
||||
return;
|
||||
}
|
||||
sprintf(fstab_file, "fstab.%s", suffix);
|
||||
}
|
||||
|
||||
// Parse and load fstab file
|
||||
// Patch init to force IsDtFstabCompatible() return false
|
||||
auto init = mmap_data::rw("/init");
|
||||
init.patch({ make_pair("android,fstab", "xxx") });
|
||||
} else {
|
||||
// Parse and load the fstab file
|
||||
file_readline(fstab_file, [&](string_view l) -> bool {
|
||||
if (l[0] == '#' || l.length() == 1)
|
||||
return true;
|
||||
@@ -90,32 +113,6 @@ void FirstStageInit::prepare() {
|
||||
fstab.emplace_back(std::move(entry));
|
||||
return true;
|
||||
});
|
||||
} else {
|
||||
// All dt fstab entries should be first_stage_mount
|
||||
for (auto &entry : fstab) {
|
||||
if (!str_contains(entry.fsmgr_flags, "first_stage_mount")) {
|
||||
if (!entry.fsmgr_flags.empty())
|
||||
entry.fsmgr_flags += ',';
|
||||
entry.fsmgr_flags += "first_stage_mount";
|
||||
}
|
||||
}
|
||||
|
||||
// Dump dt fstab to fstab file in rootfs
|
||||
if (fstab_file[0] == '\0') {
|
||||
const char *suffix =
|
||||
cmd->fstab_suffix[0] ? cmd->fstab_suffix :
|
||||
(cmd->hardware[0] ? cmd->hardware :
|
||||
(cmd->hardware_plat[0] ? cmd->hardware_plat : nullptr));
|
||||
if (suffix == nullptr) {
|
||||
LOGE("Cannot determine fstab suffix!\n");
|
||||
return;
|
||||
}
|
||||
sprintf(fstab_file, "fstab.%s", suffix);
|
||||
}
|
||||
|
||||
// Patch init to force IsDtFstabCompatible() return false
|
||||
auto init = raw_data::mmap_rw("/init");
|
||||
init.patch({ make_pair("android,fstab", "xxx") });
|
||||
}
|
||||
|
||||
{
|
||||
@@ -142,15 +139,13 @@ void FirstStageInit::prepare() {
|
||||
#define REDIR_PATH "/system/bin/am"
|
||||
|
||||
void SARInit::first_stage_prep() {
|
||||
int pid = getpid();
|
||||
|
||||
xmount("tmpfs", "/dev", "tmpfs", 0, "mode=755");
|
||||
|
||||
// Patch init binary
|
||||
int src = xopen("/init", O_RDONLY);
|
||||
int dest = xopen("/dev/init", O_CREAT | O_WRONLY, 0);
|
||||
{
|
||||
auto init = raw_data::read(src);
|
||||
auto init = mmap_data::ro("/init");
|
||||
init.patch({ make_pair(INIT_PATH, REDIR_PATH) });
|
||||
write(dest, init.buf, init.sz);
|
||||
fclone_attr(src, dest);
|
||||
@@ -174,7 +169,7 @@ void SARInit::first_stage_prep() {
|
||||
sigaddset(&block, SIGUSR1);
|
||||
sigprocmask(SIG_BLOCK, &block, &old);
|
||||
|
||||
if (int child = xfork(); child) {
|
||||
if (int child = xfork()) {
|
||||
LOGD("init daemon [%d]\n", child);
|
||||
// Wait for children signal
|
||||
int sig;
|
||||
@@ -190,22 +185,21 @@ void SARInit::first_stage_prep() {
|
||||
xlisten(sockfd, 1);
|
||||
|
||||
// Resume parent
|
||||
kill(pid, SIGUSR1);
|
||||
kill(getppid(), SIGUSR1);
|
||||
|
||||
// Wait for second stage ack
|
||||
int client = xaccept4(sockfd, nullptr, nullptr, SOCK_CLOEXEC);
|
||||
|
||||
// Write backup files
|
||||
char *tmp_dir = read_string(client);
|
||||
chdir(tmp_dir);
|
||||
free(tmp_dir);
|
||||
string tmp_dir = read_string(client);
|
||||
chdir(tmp_dir.data());
|
||||
int cfg = xopen(INTLROOT "/config", O_WRONLY | O_CREAT, 0);
|
||||
xwrite(cfg, config.buf, config.sz);
|
||||
close(cfg);
|
||||
restore_folder(ROOTOVL, overlays);
|
||||
|
||||
// Ack and bail out!
|
||||
write(sockfd, &sockfd, sizeof(sockfd));
|
||||
write_int(sockfd, 0);
|
||||
close(client);
|
||||
close(sockfd);
|
||||
|
||||
|
151
native/jni/inject/entry.cpp
Normal file
151
native/jni/inject/entry.cpp
Normal file
@@ -0,0 +1,151 @@
|
||||
#include <libgen.h>
|
||||
#include <dlfcn.h>
|
||||
#include <sys/mount.h>
|
||||
#include <sys/sendfile.h>
|
||||
#include <sys/prctl.h>
|
||||
#include <android/log.h>
|
||||
#include <atomic>
|
||||
|
||||
#include <utils.hpp>
|
||||
|
||||
#include "inject.hpp"
|
||||
|
||||
using namespace std;
|
||||
|
||||
static void *self_handle = nullptr;
|
||||
static atomic<int> active_threads = -1;
|
||||
|
||||
#define alog(prio) [](auto fmt, auto ap){ \
|
||||
return __android_log_vprint(ANDROID_LOG_##prio, "Magisk", fmt, ap); }
|
||||
static void inject_logging() {
|
||||
log_cb.d = alog(DEBUG);
|
||||
log_cb.i = alog(INFO);
|
||||
log_cb.w = alog(WARN);
|
||||
log_cb.e = alog(ERROR);
|
||||
log_cb.ex = nop_ex;
|
||||
}
|
||||
|
||||
__attribute__((destructor))
|
||||
static void inject_cleanup() {
|
||||
if (active_threads < 0)
|
||||
return;
|
||||
|
||||
// Setup 1ms
|
||||
timespec ts = { .tv_sec = 0, .tv_nsec = 1000000L };
|
||||
|
||||
// Check flag in busy loop
|
||||
while (active_threads)
|
||||
nanosleep(&ts, nullptr);
|
||||
|
||||
// Wait another 1ms to make sure all threads left our code
|
||||
nanosleep(&ts, nullptr);
|
||||
}
|
||||
|
||||
void self_unload() {
|
||||
LOGD("hook: Request to self unload\n");
|
||||
// If unhook failed, do not unload or else it will cause SIGSEGV
|
||||
if (!unhook_functions())
|
||||
return;
|
||||
new_daemon_thread(reinterpret_cast<void *(*)(void *)>(&dlclose), self_handle);
|
||||
active_threads--;
|
||||
}
|
||||
|
||||
static void *unload_first_stage(void *) {
|
||||
// Setup 1ms
|
||||
timespec ts = { .tv_sec = 0, .tv_nsec = 1000000L };
|
||||
|
||||
while (getenv(INJECT_ENV_1))
|
||||
nanosleep(&ts, nullptr);
|
||||
|
||||
// Wait another 1ms to make sure all threads left our code
|
||||
nanosleep(&ts, nullptr);
|
||||
|
||||
unmap_all(INJECT_LIB_1);
|
||||
active_threads--;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// Make sure /proc/self/environ does not reveal our secrets
|
||||
// Copy all env to a contiguous memory and set the memory region as MM_ENV
|
||||
static void sanitize_environ() {
|
||||
static string env;
|
||||
|
||||
for (int i = 0; environ[i]; ++i) {
|
||||
if (str_starts(environ[i], INJECT_ENV_1 "="))
|
||||
continue;
|
||||
env += environ[i];
|
||||
env += '\0';
|
||||
}
|
||||
|
||||
for (int i = 0; i < 2; ++i) {
|
||||
bool success = true;
|
||||
success &= (0 <= prctl(PR_SET_MM, PR_SET_MM_ENV_START, env.data(), 0, 0));
|
||||
success &= (0 <= prctl(PR_SET_MM, PR_SET_MM_ENV_END, env.data() + env.size(), 0, 0));
|
||||
if (success)
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
__attribute__((constructor))
|
||||
static void inject_init() {
|
||||
inject_logging();
|
||||
|
||||
if (getenv(INJECT_ENV_2)) {
|
||||
LOGD("zygote: inject 2nd stage\n");
|
||||
active_threads = 1;
|
||||
unsetenv(INJECT_ENV_2);
|
||||
|
||||
// Get our own handle
|
||||
self_handle = dlopen(INJECT_LIB_2, RTLD_LAZY);
|
||||
dlclose(self_handle);
|
||||
|
||||
hook_functions();
|
||||
|
||||
// Some cleanup
|
||||
sanitize_environ();
|
||||
active_threads++;
|
||||
new_daemon_thread(&unload_first_stage);
|
||||
} else if (char *env = getenv(INJECT_ENV_1)) {
|
||||
LOGD("zygote: inject 1st stage\n");
|
||||
|
||||
if (env[0] == '1')
|
||||
unsetenv("LD_PRELOAD");
|
||||
else
|
||||
setenv("LD_PRELOAD", env, 1); // Restore original LD_PRELOAD
|
||||
|
||||
// Setup second stage
|
||||
setenv(INJECT_ENV_2, "1", 1);
|
||||
cp_afc(INJECT_LIB_1, INJECT_LIB_2);
|
||||
dlopen(INJECT_LIB_2, RTLD_LAZY);
|
||||
|
||||
unsetenv(INJECT_ENV_1);
|
||||
}
|
||||
}
|
||||
|
||||
int app_process_main(int argc, char *argv[]) {
|
||||
inject_logging();
|
||||
char buf[4096];
|
||||
if (realpath("/proc/self/exe", buf) == nullptr)
|
||||
return 1;
|
||||
|
||||
int in = xopen(buf, O_RDONLY);
|
||||
int out = xopen(INJECT_LIB_1, O_CREAT | O_WRONLY | O_TRUNC, 0777);
|
||||
sendfile(out, in, nullptr, INT_MAX);
|
||||
close(in);
|
||||
close(out);
|
||||
|
||||
if (char *ld = getenv("LD_PRELOAD")) {
|
||||
char env[128];
|
||||
sprintf(env, "%s:" INJECT_LIB_1, ld);
|
||||
setenv("LD_PRELOAD", env, 1);
|
||||
setenv(INJECT_ENV_1, ld, 1); // Backup original LD_PRELOAD
|
||||
} else {
|
||||
setenv("LD_PRELOAD", INJECT_LIB_1, 1);
|
||||
setenv(INJECT_ENV_1, "1", 1);
|
||||
}
|
||||
|
||||
// Execute real app_process
|
||||
xumount2(buf, MNT_DETACH);
|
||||
execve(buf, argv, environ);
|
||||
return 1;
|
||||
}
|
275
native/jni/inject/hook.cpp
Normal file
275
native/jni/inject/hook.cpp
Normal file
@@ -0,0 +1,275 @@
|
||||
#include <jni.h>
|
||||
|
||||
#include <xhook.h>
|
||||
#include <utils.hpp>
|
||||
#include <flags.hpp>
|
||||
#include <daemon.hpp>
|
||||
|
||||
#include "inject.hpp"
|
||||
|
||||
using namespace std;
|
||||
|
||||
#define DCL_HOOK_FUNC(ret, func, ...) \
|
||||
static ret (*old_##func)(__VA_ARGS__); \
|
||||
static ret new_##func(__VA_ARGS__)
|
||||
|
||||
#define DCL_JNI_FUNC(name) \
|
||||
static const JNINativeMethod *name##_orig = nullptr; \
|
||||
extern const JNINativeMethod name##_methods[]; \
|
||||
extern const int name##_methods_num;
|
||||
|
||||
namespace {
|
||||
|
||||
struct HookContext {
|
||||
int pid;
|
||||
bool do_hide;
|
||||
};
|
||||
|
||||
// JNI method declarations
|
||||
DCL_JNI_FUNC(nativeForkAndSpecialize)
|
||||
DCL_JNI_FUNC(nativeSpecializeAppProcess)
|
||||
DCL_JNI_FUNC(nativeForkSystemServer)
|
||||
|
||||
}
|
||||
|
||||
// For some reason static vectors won't work, use pointers instead
|
||||
static vector<tuple<const char *, const char *, void **>> *xhook_list;
|
||||
static vector<JNINativeMethod> *jni_list;
|
||||
|
||||
static JavaVM *g_jvm;
|
||||
static int prev_fork_pid = -1;
|
||||
static HookContext *current_ctx;
|
||||
|
||||
#define HOOK_JNI(method) \
|
||||
if (newMethods[i].name == #method##sv) { \
|
||||
auto orig = new JNINativeMethod(); \
|
||||
memcpy(orig, &newMethods[i], sizeof(JNINativeMethod)); \
|
||||
method##_orig = orig; \
|
||||
jni_list->push_back(newMethods[i]); \
|
||||
for (int j = 0; j < method##_methods_num; ++j) { \
|
||||
if (strcmp(newMethods[i].signature, method##_methods[j].signature) == 0) { \
|
||||
newMethods[i] = method##_methods[j]; \
|
||||
LOGI("hook: replaced #" #method "\n"); \
|
||||
++hooked; \
|
||||
break; \
|
||||
} \
|
||||
} \
|
||||
continue; \
|
||||
}
|
||||
|
||||
DCL_HOOK_FUNC(int, jniRegisterNativeMethods,
|
||||
JNIEnv *env, const char *className, const JNINativeMethod *methods, int numMethods) {
|
||||
LOGD("hook: jniRegisterNativeMethods %s", className);
|
||||
|
||||
unique_ptr<JNINativeMethod[]> newMethods;
|
||||
int hooked = 0;
|
||||
|
||||
if (g_jvm == nullptr) {
|
||||
// Save for later unhooking
|
||||
env->GetJavaVM(&g_jvm);
|
||||
}
|
||||
|
||||
if (className == "com/android/internal/os/Zygote"sv) {
|
||||
newMethods = make_unique<JNINativeMethod[]>(numMethods);
|
||||
memcpy(newMethods.get(), methods, sizeof(JNINativeMethod) * numMethods);
|
||||
for (int i = 0; i < numMethods && hooked < 3; ++i) {
|
||||
HOOK_JNI(nativeForkAndSpecialize);
|
||||
HOOK_JNI(nativeSpecializeAppProcess);
|
||||
HOOK_JNI(nativeForkSystemServer);
|
||||
}
|
||||
}
|
||||
|
||||
return old_jniRegisterNativeMethods(env, className, newMethods.get() ?: methods, numMethods);
|
||||
}
|
||||
|
||||
DCL_HOOK_FUNC(int, fork) {
|
||||
if (prev_fork_pid < 0)
|
||||
return old_fork();
|
||||
|
||||
// Skip an actual fork and return the previous fork result
|
||||
int pid = prev_fork_pid;
|
||||
prev_fork_pid = -1;
|
||||
return pid;
|
||||
}
|
||||
|
||||
DCL_HOOK_FUNC(int, selinux_android_setcontext,
|
||||
uid_t uid, int isSystemServer, const char *seinfo, const char *pkgname) {
|
||||
if (current_ctx && current_ctx->do_hide) {
|
||||
// Ask magiskd to hide ourselves before switching context
|
||||
// because magiskd socket is not accessible on Android 8.0+
|
||||
remote_request_hide();
|
||||
LOGD("hook: process successfully hidden\n");
|
||||
}
|
||||
return old_selinux_android_setcontext(uid, isSystemServer, seinfo, pkgname);
|
||||
}
|
||||
|
||||
static int sigmask(int how, int signum) {
|
||||
sigset_t set;
|
||||
sigemptyset(&set);
|
||||
sigaddset(&set, signum);
|
||||
return sigprocmask(how, &set, nullptr);
|
||||
}
|
||||
|
||||
static int pre_specialize_fork() {
|
||||
// First block SIGCHLD, unblock after original fork is done
|
||||
sigmask(SIG_BLOCK, SIGCHLD);
|
||||
prev_fork_pid = old_fork();
|
||||
return prev_fork_pid;
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------
|
||||
|
||||
static void nativeSpecializeAppProcess_pre(HookContext *ctx,
|
||||
JNIEnv *env, jclass clazz, jint &uid, jint &gid, jintArray &gids, jint &runtime_flags,
|
||||
jobjectArray &rlimits, jint &mount_external, jstring &se_info, jstring &nice_name,
|
||||
jboolean &is_child_zygote, jstring &instruction_set, jstring &app_data_dir,
|
||||
jboolean &is_top_app, jobjectArray &pkg_data_info_list,
|
||||
jobjectArray &whitelisted_data_info_list, jboolean &mount_data_dirs,
|
||||
jboolean &mount_storage_dirs) {
|
||||
|
||||
current_ctx = ctx;
|
||||
|
||||
const char *process = env->GetStringUTFChars(nice_name, nullptr);
|
||||
LOGD("hook: %s %s\n", __FUNCTION__, process);
|
||||
|
||||
if (mount_external != 0 /* TODO: Handle MOUNT_EXTERNAL_NONE cases */
|
||||
&& remote_check_hide(uid, process)) {
|
||||
ctx->do_hide = true;
|
||||
LOGI("hook: [%s] should be hidden\n", process);
|
||||
}
|
||||
|
||||
env->ReleaseStringUTFChars(nice_name, process);
|
||||
}
|
||||
|
||||
static void nativeSpecializeAppProcess_post(HookContext *ctx, JNIEnv *env, jclass clazz) {
|
||||
LOGD("hook: %s\n", __FUNCTION__);
|
||||
|
||||
if (ctx->do_hide)
|
||||
self_unload();
|
||||
|
||||
current_ctx = nullptr;
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------
|
||||
|
||||
static void nativeForkAndSpecialize_pre(HookContext *ctx,
|
||||
JNIEnv *env, jclass clazz, jint &uid, jint &gid, jintArray &gids, jint &runtime_flags,
|
||||
jobjectArray &rlimits, jint &mount_external, jstring &se_info, jstring &nice_name,
|
||||
jintArray fds_to_close, jintArray fds_to_ignore, /* These 2 arguments are unique to fork */
|
||||
jboolean &is_child_zygote, jstring &instruction_set, jstring &app_data_dir,
|
||||
jboolean &is_top_app, jobjectArray &pkg_data_info_list,
|
||||
jobjectArray &whitelisted_data_info_list, jboolean &mount_data_dirs,
|
||||
jboolean &mount_storage_dirs) {
|
||||
|
||||
// Do our own fork before loading any 3rd party code
|
||||
ctx->pid = pre_specialize_fork();
|
||||
if (ctx->pid != 0)
|
||||
return;
|
||||
|
||||
nativeSpecializeAppProcess_pre(
|
||||
ctx, env, clazz, uid, gid, gids, runtime_flags, rlimits, mount_external, se_info,
|
||||
nice_name, is_child_zygote, instruction_set, app_data_dir, is_top_app,
|
||||
pkg_data_info_list, whitelisted_data_info_list, mount_data_dirs, mount_storage_dirs);
|
||||
}
|
||||
|
||||
static void nativeForkAndSpecialize_post(HookContext *ctx, JNIEnv *env, jclass clazz) {
|
||||
// Unblock SIGCHLD in case the original method didn't
|
||||
sigmask(SIG_UNBLOCK, SIGCHLD);
|
||||
if (ctx->pid != 0)
|
||||
return;
|
||||
|
||||
nativeSpecializeAppProcess_post(ctx, env, clazz);
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------
|
||||
|
||||
static void nativeForkSystemServer_pre(HookContext *ctx,
|
||||
JNIEnv *env, jclass clazz, uid_t &uid, gid_t &gid, jintArray &gids, jint &runtime_flags,
|
||||
jobjectArray &rlimits, jlong &permitted_capabilities, jlong &effective_capabilities) {
|
||||
|
||||
// Do our own fork before loading any 3rd party code
|
||||
ctx->pid = pre_specialize_fork();
|
||||
if (ctx->pid != 0)
|
||||
return;
|
||||
|
||||
current_ctx = ctx;
|
||||
LOGD("hook: %s\n", __FUNCTION__);
|
||||
}
|
||||
|
||||
static void nativeForkSystemServer_post(HookContext *ctx, JNIEnv *env, jclass clazz) {
|
||||
// Unblock SIGCHLD in case the original method didn't
|
||||
sigmask(SIG_UNBLOCK, SIGCHLD);
|
||||
|
||||
if (ctx->pid != 0)
|
||||
return;
|
||||
|
||||
LOGD("hook: %s\n", __FUNCTION__);
|
||||
current_ctx = nullptr;
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------
|
||||
|
||||
static bool hook_refresh() {
|
||||
if (xhook_refresh(0) == 0) {
|
||||
xhook_clear();
|
||||
LOGI("hook: xhook success\n");
|
||||
return true;
|
||||
} else {
|
||||
LOGE("hook: xhook failed\n");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
static int hook_register(const char *path, const char *symbol, void *new_func, void **old_func) {
|
||||
int ret = xhook_register(path, symbol, new_func, old_func);
|
||||
if (ret != 0) {
|
||||
LOGE("hook: Failed to register hook \"%s\"\n", symbol);
|
||||
return ret;
|
||||
}
|
||||
xhook_list->emplace_back(path, symbol, old_func);
|
||||
return 0;
|
||||
}
|
||||
|
||||
#define XHOOK_REGISTER(PATH_REGEX, NAME) \
|
||||
hook_register(PATH_REGEX, #NAME, (void*) new_##NAME, (void **) &old_##NAME)
|
||||
|
||||
void hook_functions() {
|
||||
#ifdef MAGISK_DEBUG
|
||||
xhook_enable_debug(1);
|
||||
xhook_enable_sigsegv_protection(0);
|
||||
#endif
|
||||
xhook_list = new remove_pointer_t<decltype(xhook_list)>();
|
||||
jni_list = new remove_pointer_t<decltype(jni_list)>();
|
||||
|
||||
XHOOK_REGISTER(".*\\libandroid_runtime.so$", jniRegisterNativeMethods);
|
||||
XHOOK_REGISTER(".*\\libandroid_runtime.so$", fork);
|
||||
XHOOK_REGISTER(".*\\libandroid_runtime.so$", selinux_android_setcontext);
|
||||
hook_refresh();
|
||||
}
|
||||
|
||||
bool unhook_functions() {
|
||||
JNIEnv* env;
|
||||
if (g_jvm->GetEnv(reinterpret_cast<void**>(&env), JNI_VERSION_1_6) != JNI_OK)
|
||||
return false;
|
||||
|
||||
// Unhook JNI methods
|
||||
if (!jni_list->empty() && old_jniRegisterNativeMethods(env,
|
||||
"com/android/internal/os/Zygote",
|
||||
jni_list->data(), jni_list->size()) != 0) {
|
||||
LOGE("hook: Failed to register JNI hook\n");
|
||||
return false;
|
||||
}
|
||||
delete jni_list;
|
||||
|
||||
// Unhook xhook
|
||||
for (auto &[path, sym, old_func] : *xhook_list) {
|
||||
if (xhook_register(path, sym, *old_func, nullptr) != 0) {
|
||||
LOGE("hook: Failed to register hook \"%s\"\n", sym);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
delete xhook_list;
|
||||
return hook_refresh();
|
||||
}
|
||||
|
||||
#include "jni_hooks.hpp"
|
22
native/jni/inject/inject.hpp
Normal file
22
native/jni/inject/inject.hpp
Normal file
@@ -0,0 +1,22 @@
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
#include <jni.h>
|
||||
|
||||
#define INJECT_LIB_1 "/dev/tmp/magisk.1.so"
|
||||
#define INJECT_LIB_2 "/dev/tmp/magisk.2.so"
|
||||
#define INJECT_ENV_1 "MAGISK_INJ_1"
|
||||
#define INJECT_ENV_2 "MAGISK_INJ_2"
|
||||
|
||||
// Unmap all pages matching the name
|
||||
void unmap_all(const char *name);
|
||||
|
||||
// Get library name and base address that contains the function
|
||||
uintptr_t get_function_lib(uintptr_t addr, char *lib);
|
||||
|
||||
// Get library base address with name
|
||||
uintptr_t get_remote_lib(int pid, const char *lib);
|
||||
|
||||
void self_unload();
|
||||
void hook_functions();
|
||||
bool unhook_functions();
|
366
native/jni/inject/jni_hooks.hpp
Normal file
366
native/jni/inject/jni_hooks.hpp
Normal file
@@ -0,0 +1,366 @@
|
||||
/*
|
||||
* Original code: https://github.com/RikkaApps/Riru/blob/master/riru/src/main/cpp/jni_native_method.cpp
|
||||
* The code is modified and sublicensed to GPLv3 for incorporating into Magisk.
|
||||
*
|
||||
* Copyright (c) 2018-2021, RikkaW
|
||||
* Copyright (c) 2021, John 'topjohnwu' Wu
|
||||
*/
|
||||
|
||||
#define ENABLE_LEGACY_DP 0 // Nobody should use outdated developer preview...
|
||||
|
||||
// All possible missing arguments
|
||||
static union {
|
||||
struct {
|
||||
jintArray fds_to_ignore;
|
||||
jboolean is_child_zygote;
|
||||
jboolean is_top_app;
|
||||
jobjectArray pkg_data_info_list;
|
||||
jobjectArray whitelisted_data_info_list;
|
||||
jboolean mount_data_dirs;
|
||||
jboolean mount_storage_dirs;
|
||||
};
|
||||
size_t args_buf[8]; // Easy access to wipe all variables at once
|
||||
};
|
||||
|
||||
#define DCL_JNI(ret, name, sig, ...) \
|
||||
const static char name##_sig[] = sig; \
|
||||
static ret name(__VA_ARGS__)
|
||||
|
||||
// -----------------------------------------------------------------
|
||||
|
||||
#define pre_fork() \
|
||||
HookContext ctx{}; \
|
||||
memset(args_buf, 0, sizeof(args_buf)); \
|
||||
nativeForkAndSpecialize_pre(&ctx, env, clazz, uid, gid, gids, runtime_flags, \
|
||||
rlimits, mount_external, se_info, nice_name, fds_to_close, fds_to_ignore, is_child_zygote, \
|
||||
instruction_set, app_data_dir, is_top_app, pkg_data_info_list, whitelisted_data_info_list, \
|
||||
mount_data_dirs, mount_storage_dirs)
|
||||
|
||||
#define orig_fork(ver, ...) \
|
||||
reinterpret_cast<decltype(&nativeForkAndSpecialize_##ver)> \
|
||||
(nativeForkAndSpecialize_orig->fnPtr)(__VA_ARGS__)
|
||||
|
||||
#define post_fork() \
|
||||
nativeForkAndSpecialize_post(&ctx, env, clazz); \
|
||||
return ctx.pid
|
||||
|
||||
#define DCL_FORK_AND_SPECIALIZE(ver, sig, ...) \
|
||||
DCL_JNI(jint, nativeForkAndSpecialize_##ver, sig, __VA_ARGS__)
|
||||
|
||||
DCL_FORK_AND_SPECIALIZE(m,
|
||||
"(II[II[[IILjava/lang/String;Ljava/lang/String;[ILjava/lang/String;Ljava/lang/String;)I",
|
||||
JNIEnv *env, jclass clazz, jint uid, jint gid, jintArray gids, jint runtime_flags,
|
||||
jobjectArray rlimits, jint mount_external, jstring se_info, jstring nice_name,
|
||||
jintArray fds_to_close, jstring instruction_set, jstring app_data_dir) {
|
||||
pre_fork();
|
||||
orig_fork(m, env, clazz, uid, gid, gids, runtime_flags, rlimits, mount_external,
|
||||
se_info, nice_name, fds_to_close, instruction_set, app_data_dir);
|
||||
post_fork();
|
||||
}
|
||||
|
||||
DCL_FORK_AND_SPECIALIZE(o,
|
||||
"(II[II[[IILjava/lang/String;Ljava/lang/String;[I[ILjava/lang/String;Ljava/lang/String;)I",
|
||||
JNIEnv *env, jclass clazz, jint uid, jint gid, jintArray gids, jint runtime_flags,
|
||||
jobjectArray rlimits, jint mount_external, jstring se_info, jstring nice_name,
|
||||
jintArray fds_to_close, jintArray fds_to_ignore, jstring instruction_set, jstring app_data_dir) {
|
||||
pre_fork();
|
||||
orig_fork(o, env, clazz, uid, gid, gids, runtime_flags, rlimits, mount_external,
|
||||
se_info, nice_name, fds_to_close, fds_to_ignore, instruction_set, app_data_dir);
|
||||
post_fork();
|
||||
}
|
||||
|
||||
DCL_FORK_AND_SPECIALIZE(p,
|
||||
"(II[II[[IILjava/lang/String;Ljava/lang/String;[I[IZLjava/lang/String;Ljava/lang/String;)I",
|
||||
JNIEnv *env, jclass clazz, jint uid, jint gid, jintArray gids, jint runtime_flags,
|
||||
jobjectArray rlimits, jint mount_external, jstring se_info, jstring nice_name,
|
||||
jintArray fds_to_close, jintArray fds_to_ignore, jboolean is_child_zygote,
|
||||
jstring instruction_set, jstring app_data_dir) {
|
||||
pre_fork();
|
||||
orig_fork(p, env, clazz, uid, gid, gids, runtime_flags, rlimits, mount_external, se_info,
|
||||
nice_name, fds_to_close, fds_to_ignore, is_child_zygote, instruction_set, app_data_dir);
|
||||
post_fork();
|
||||
}
|
||||
|
||||
DCL_FORK_AND_SPECIALIZE(q_alt,
|
||||
"(II[II[[IILjava/lang/String;Ljava/lang/String;[I[IZLjava/lang/String;Ljava/lang/String;Z)I",
|
||||
JNIEnv *env, jclass clazz, jint uid, jint gid, jintArray gids, jint runtime_flags,
|
||||
jobjectArray rlimits, jint mount_external, jstring se_info, jstring nice_name,
|
||||
jintArray fds_to_close, jintArray fds_to_ignore, jboolean is_child_zygote,
|
||||
jstring instruction_set, jstring app_data_dir, jboolean is_top_app) {
|
||||
pre_fork();
|
||||
orig_fork(q_alt, env, clazz, uid, gid, gids, runtime_flags, rlimits, mount_external, se_info,
|
||||
nice_name, fds_to_close, fds_to_ignore, is_child_zygote, instruction_set, app_data_dir, is_top_app);
|
||||
post_fork();
|
||||
}
|
||||
|
||||
#if ENABLE_LEGACY_DP
|
||||
DCL_FORK_AND_SPECIALIZE(r_dp2,
|
||||
"(II[II[[IILjava/lang/String;Ljava/lang/String;[I[IZLjava/lang/String;Ljava/lang/String;Z[Ljava/lang/String;)I",
|
||||
JNIEnv *env, jclass clazz, jint uid, jint gid, jintArray gids, jint runtime_flags,
|
||||
jobjectArray rlimits, jint mount_external, jstring se_info, jstring nice_name,
|
||||
jintArray fds_to_close, jintArray fds_to_ignore, jboolean is_child_zygote,
|
||||
jstring instruction_set, jstring app_data_dir, jboolean is_top_app, jobjectArray pkg_data_info_list) {
|
||||
pre_fork();
|
||||
orig_fork(r_dp2, env, clazz, uid, gid, gids, runtime_flags, rlimits, mount_external, se_info,
|
||||
nice_name, fds_to_close, fds_to_ignore, is_child_zygote, instruction_set, app_data_dir,
|
||||
is_top_app, pkg_data_info_list);
|
||||
post_fork();
|
||||
}
|
||||
|
||||
DCL_FORK_AND_SPECIALIZE(r_dp3,
|
||||
"(II[II[[IILjava/lang/String;Ljava/lang/String;[I[IZLjava/lang/String;Ljava/lang/String;Z[Ljava/lang/String;Z)I",
|
||||
JNIEnv *env, jclass clazz, jint uid, jint gid, jintArray gids, jint runtime_flags,
|
||||
jobjectArray rlimits, jint mount_external, jstring se_info, jstring nice_name,
|
||||
jintArray fds_to_close, jintArray fds_to_ignore, jboolean is_child_zygote,
|
||||
jstring instruction_set, jstring app_data_dir, jboolean is_top_app, jobjectArray pkg_data_info_list,
|
||||
jboolean mount_storage_dirs) {
|
||||
pre_fork();
|
||||
orig_fork(r_dp3, env, clazz, uid, gid, gids, runtime_flags, rlimits, mount_external,
|
||||
se_info, nice_name, fds_to_close, fds_to_ignore, is_child_zygote, instruction_set,
|
||||
app_data_dir, is_top_app, pkg_data_info_list, mount_storage_dirs);
|
||||
post_fork();
|
||||
}
|
||||
#endif // ENABLE_LEGACY_DP
|
||||
|
||||
DCL_FORK_AND_SPECIALIZE(r,
|
||||
"(II[II[[IILjava/lang/String;Ljava/lang/String;[I[IZLjava/lang/String;Ljava/lang/String;Z[Ljava/lang/String;[Ljava/lang/String;ZZ)I",
|
||||
JNIEnv *env, jclass clazz, jint uid, jint gid, jintArray gids, jint runtime_flags,
|
||||
jobjectArray rlimits, jint mount_external, jstring se_info, jstring nice_name,
|
||||
jintArray fds_to_close, jintArray fds_to_ignore, jboolean is_child_zygote,
|
||||
jstring instruction_set, jstring app_data_dir, jboolean is_top_app, jobjectArray pkg_data_info_list,
|
||||
jobjectArray whitelisted_data_info_list, jboolean mount_data_dirs, jboolean mount_storage_dirs) {
|
||||
pre_fork();
|
||||
orig_fork(r, env, clazz, uid, gid, gids, runtime_flags, rlimits, mount_external, se_info,
|
||||
nice_name, fds_to_close, fds_to_ignore, is_child_zygote, instruction_set, app_data_dir, is_top_app,
|
||||
pkg_data_info_list, whitelisted_data_info_list, mount_data_dirs, mount_storage_dirs);
|
||||
post_fork();
|
||||
}
|
||||
|
||||
DCL_FORK_AND_SPECIALIZE(samsung_m,
|
||||
"(II[II[[IILjava/lang/String;IILjava/lang/String;[ILjava/lang/String;Ljava/lang/String;)I",
|
||||
JNIEnv *env, jclass clazz, jint uid, jint gid, jintArray gids, jint runtime_flags,
|
||||
jobjectArray rlimits, jint mount_external, jstring se_info, jint category, jint accessInfo,
|
||||
jstring nice_name, jintArray fds_to_close, jstring instruction_set, jstring app_data_dir) {
|
||||
pre_fork();
|
||||
orig_fork(samsung_m, env, clazz, uid, gid, gids, runtime_flags, rlimits, mount_external,
|
||||
se_info, category, accessInfo, nice_name, fds_to_close, instruction_set, app_data_dir);
|
||||
post_fork();
|
||||
}
|
||||
|
||||
DCL_FORK_AND_SPECIALIZE(samsung_n,
|
||||
"(II[II[[IILjava/lang/String;IILjava/lang/String;[ILjava/lang/String;Ljava/lang/String;I)I",
|
||||
JNIEnv *env, jclass clazz, jint uid, jint gid, jintArray gids, jint runtime_flags,
|
||||
jobjectArray rlimits, jint mount_external, jstring se_info, jint category, jint accessInfo,
|
||||
jstring nice_name, jintArray fds_to_close, jstring instruction_set, jstring app_data_dir, jint a1) {
|
||||
pre_fork();
|
||||
orig_fork(samsung_n, env, clazz, uid, gid, gids, runtime_flags, rlimits, mount_external,
|
||||
se_info, category, accessInfo, nice_name, fds_to_close, instruction_set, app_data_dir, a1);
|
||||
post_fork();
|
||||
}
|
||||
|
||||
DCL_FORK_AND_SPECIALIZE(samsung_o,
|
||||
"(II[II[[IILjava/lang/String;IILjava/lang/String;[I[ILjava/lang/String;Ljava/lang/String;)I",
|
||||
JNIEnv *env, jclass clazz, jint uid, jint gid, jintArray gids, jint runtime_flags,
|
||||
jobjectArray rlimits, jint mount_external, jstring se_info, jint category, jint accessInfo,
|
||||
jstring nice_name, jintArray fds_to_close, jintArray fds_to_ignore, jstring instruction_set,
|
||||
jstring app_data_dir) {
|
||||
pre_fork();
|
||||
orig_fork(samsung_o, env, clazz, uid, gid, gids, runtime_flags, rlimits, mount_external,
|
||||
se_info, category, accessInfo, nice_name, fds_to_close, fds_to_ignore,
|
||||
instruction_set, app_data_dir);
|
||||
post_fork();
|
||||
}
|
||||
|
||||
DCL_FORK_AND_SPECIALIZE(samsung_p,
|
||||
"(II[II[[IILjava/lang/String;IILjava/lang/String;[I[IZLjava/lang/String;Ljava/lang/String;)I",
|
||||
JNIEnv *env, jclass clazz, jint uid, jint gid, jintArray gids, jint runtime_flags,
|
||||
jobjectArray rlimits, jint mount_external, jstring se_info, jint category, jint accessInfo,
|
||||
jstring nice_name, jintArray fds_to_close, jintArray fds_to_ignore, jboolean is_child_zygote,
|
||||
jstring instruction_set, jstring app_data_dir) {
|
||||
pre_fork();
|
||||
orig_fork(samsung_p, env, clazz, uid, gid, gids, runtime_flags, rlimits, mount_external,
|
||||
se_info, category, accessInfo, nice_name, fds_to_close, fds_to_ignore, is_child_zygote,
|
||||
instruction_set, app_data_dir);
|
||||
post_fork();
|
||||
}
|
||||
|
||||
#define DEF_FORK(ver) { \
|
||||
"nativeForkAndSpecialize", \
|
||||
nativeForkAndSpecialize_##ver##_sig, \
|
||||
(void *) &nativeForkAndSpecialize_##ver \
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------
|
||||
|
||||
#define pre_spec() \
|
||||
HookContext ctx{}; \
|
||||
memset(args_buf, 0, sizeof(args_buf)); \
|
||||
nativeSpecializeAppProcess_pre(&ctx, \
|
||||
env, clazz, uid, gid, gids, runtime_flags, rlimits, mount_external, se_info, nice_name, \
|
||||
is_child_zygote, instruction_set, app_data_dir, is_top_app, pkg_data_info_list, \
|
||||
whitelisted_data_info_list, mount_data_dirs, mount_storage_dirs)
|
||||
|
||||
#define orig_spec(ver, ...) \
|
||||
reinterpret_cast<decltype(&nativeSpecializeAppProcess_##ver)> \
|
||||
(nativeSpecializeAppProcess_orig->fnPtr)(__VA_ARGS__)
|
||||
|
||||
#define post_spec() \
|
||||
nativeSpecializeAppProcess_post(&ctx, env, clazz)
|
||||
|
||||
#define DCL_SPECIALIZE_APP(ver, sig, ...) \
|
||||
DCL_JNI(void, nativeSpecializeAppProcess_##ver, sig, __VA_ARGS__)
|
||||
|
||||
DCL_SPECIALIZE_APP(q,
|
||||
"(II[II[[IILjava/lang/String;Ljava/lang/String;ZLjava/lang/String;Ljava/lang/String;)V",
|
||||
JNIEnv *env, jclass clazz, jint uid, jint gid, jintArray gids, jint runtime_flags,
|
||||
jobjectArray rlimits, jint mount_external, jstring se_info, jstring nice_name,
|
||||
jboolean is_child_zygote, jstring instruction_set, jstring app_data_dir) {
|
||||
pre_spec();
|
||||
orig_spec(q, env, clazz, uid, gid, gids, runtime_flags, rlimits, mount_external, se_info,
|
||||
nice_name, is_child_zygote, instruction_set, app_data_dir);
|
||||
post_spec();
|
||||
}
|
||||
|
||||
DCL_SPECIALIZE_APP(q_alt,
|
||||
"(II[II[[IILjava/lang/String;Ljava/lang/String;ZLjava/lang/String;Ljava/lang/String;Z)V",
|
||||
JNIEnv *env, jclass clazz, jint uid, jint gid, jintArray gids, jint runtime_flags,
|
||||
jobjectArray rlimits, jint mount_external, jstring se_info, jstring nice_name,
|
||||
jboolean is_child_zygote, jstring instruction_set, jstring app_data_dir,
|
||||
jboolean is_top_app) {
|
||||
pre_spec();
|
||||
orig_spec(q_alt, env, clazz, uid, gid, gids, runtime_flags, rlimits, mount_external, se_info,
|
||||
nice_name, is_child_zygote, instruction_set, app_data_dir, is_top_app);
|
||||
post_spec();
|
||||
}
|
||||
|
||||
#if ENABLE_LEGACY_DP
|
||||
DCL_SPECIALIZE_APP(r_dp2,
|
||||
"(II[II[[IILjava/lang/String;Ljava/lang/String;ZLjava/lang/String;Ljava/lang/String;Z[Ljava/lang/String;)V",
|
||||
JNIEnv *env, jclass clazz, jint uid, jint gid, jintArray gids, jint runtime_flags,
|
||||
jobjectArray rlimits, jint mount_external, jstring se_info, jstring nice_name,
|
||||
jboolean is_child_zygote, jstring instruction_set, jstring app_data_dir,
|
||||
jboolean is_top_app, jobjectArray pkg_data_info_list) {
|
||||
pre_spec();
|
||||
orig_spec(r_dp2, env, clazz, uid, gid, gids, runtime_flags, rlimits, mount_external, se_info,
|
||||
nice_name, is_child_zygote, instruction_set, app_data_dir, is_top_app, pkg_data_info_list);
|
||||
post_spec();
|
||||
}
|
||||
|
||||
DCL_SPECIALIZE_APP(r_dp3,
|
||||
"(II[II[[IILjava/lang/String;Ljava/lang/String;ZLjava/lang/String;Ljava/lang/String;Z[Ljava/lang/String;Z)V",
|
||||
JNIEnv *env, jclass clazz, jint uid, jint gid, jintArray gids, jint runtime_flags,
|
||||
jobjectArray rlimits, jint mount_external, jstring se_info, jstring nice_name,
|
||||
jboolean is_child_zygote, jstring instruction_set, jstring app_data_dir,
|
||||
jboolean is_top_app, jobjectArray pkg_data_info_list, jboolean mount_storage_dirs) {
|
||||
pre_spec();
|
||||
orig_spec(r_dp3, env, clazz, uid, gid, gids, runtime_flags, rlimits, mount_external, se_info,
|
||||
nice_name, is_child_zygote, instruction_set, app_data_dir, is_top_app, pkg_data_info_list,
|
||||
mount_storage_dirs);
|
||||
post_spec();
|
||||
}
|
||||
#endif // ENABLE_LEGACY_DP
|
||||
|
||||
DCL_SPECIALIZE_APP(r,
|
||||
"(II[II[[IILjava/lang/String;Ljava/lang/String;ZLjava/lang/String;Ljava/lang/String;Z[Ljava/lang/String;[Ljava/lang/String;ZZ)V",
|
||||
JNIEnv *env, jclass clazz, jint uid, jint gid, jintArray gids, jint runtime_flags,
|
||||
jobjectArray rlimits, jint mount_external, jstring se_info, jstring nice_name,
|
||||
jboolean is_child_zygote, jstring instruction_set, jstring app_data_dir,
|
||||
jboolean is_top_app, jobjectArray pkg_data_info_list, jobjectArray whitelisted_data_info_list,
|
||||
jboolean mount_data_dirs, jboolean mount_storage_dirs) {
|
||||
pre_spec();
|
||||
orig_spec(r, env, clazz, uid, gid, gids, runtime_flags, rlimits, mount_external, se_info, nice_name,
|
||||
is_child_zygote, instruction_set, app_data_dir, is_top_app, pkg_data_info_list,
|
||||
whitelisted_data_info_list, mount_data_dirs, mount_storage_dirs);
|
||||
post_spec();
|
||||
}
|
||||
|
||||
DCL_SPECIALIZE_APP(samsung_q,
|
||||
"(II[II[[IILjava/lang/String;IILjava/lang/String;ZLjava/lang/String;Ljava/lang/String;)V",
|
||||
JNIEnv *env, jclass clazz, jint uid, jint gid, jintArray gids, jint runtime_flags,
|
||||
jobjectArray rlimits, jint mount_external, jstring se_info, jint space, jint accessInfo,
|
||||
jstring nice_name, jboolean is_child_zygote, jstring instruction_set, jstring app_data_dir) {
|
||||
pre_spec();
|
||||
orig_spec(samsung_q, env, clazz, uid, gid, gids, runtime_flags, rlimits, mount_external,
|
||||
se_info, space, accessInfo, nice_name, is_child_zygote, instruction_set, app_data_dir);
|
||||
post_spec();
|
||||
}
|
||||
|
||||
#define DEF_SPEC(ver) { \
|
||||
"nativeSpecializeAppProcess", \
|
||||
nativeSpecializeAppProcess_##ver##_sig, \
|
||||
(void *) &nativeSpecializeAppProcess_##ver \
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------
|
||||
|
||||
#define pre_server() \
|
||||
HookContext ctx{}; \
|
||||
memset(args_buf, 0, sizeof(args_buf)); \
|
||||
nativeForkSystemServer_pre(&ctx, env, clazz, uid, gid, gids, runtime_flags, \
|
||||
rlimits, permitted_capabilities, effective_capabilities)
|
||||
|
||||
#define orig_server(ver, ...) \
|
||||
reinterpret_cast<decltype(&nativeForkSystemServer_##ver)> \
|
||||
(nativeForkSystemServer_orig->fnPtr)(__VA_ARGS__)
|
||||
|
||||
#define post_server() \
|
||||
nativeForkSystemServer_post(&ctx, env, clazz); \
|
||||
return ctx.pid
|
||||
|
||||
#define DCL_FORK_SERVER(ver, sig, ...) \
|
||||
DCL_JNI(jint, nativeForkSystemServer_##ver, sig, __VA_ARGS__)
|
||||
|
||||
DCL_FORK_SERVER(m, "(II[II[[IJJ)I",
|
||||
JNIEnv *env, jclass clazz, uid_t uid, gid_t gid, jintArray gids, jint runtime_flags,
|
||||
jobjectArray rlimits, jlong permitted_capabilities, jlong effective_capabilities) {
|
||||
pre_server();
|
||||
orig_server(m, env, clazz, uid, gid, gids, runtime_flags, rlimits, permitted_capabilities,
|
||||
effective_capabilities);
|
||||
post_server();
|
||||
}
|
||||
|
||||
DCL_FORK_SERVER(samsung_q, "(II[IIII[[IJJ)I",
|
||||
JNIEnv *env, jclass clazz, uid_t uid, gid_t gid, jintArray gids, jint runtime_flags,
|
||||
jint space, jint accessInfo, jobjectArray rlimits, jlong permitted_capabilities,
|
||||
jlong effective_capabilities) {
|
||||
pre_server();
|
||||
orig_server(samsung_q, env, clazz, uid, gid, gids, runtime_flags, space, accessInfo, rlimits,
|
||||
permitted_capabilities, effective_capabilities);
|
||||
post_server();
|
||||
}
|
||||
|
||||
#define DEF_SERVER(ver) { \
|
||||
"nativeForkSystemServer", \
|
||||
nativeForkSystemServer_##ver##_sig, \
|
||||
(void *) &nativeForkSystemServer_##ver \
|
||||
}
|
||||
|
||||
namespace {
|
||||
|
||||
const JNINativeMethod nativeForkAndSpecialize_methods[] = {
|
||||
DEF_FORK(m), DEF_FORK(o), DEF_FORK(p),
|
||||
DEF_FORK(q_alt), DEF_FORK(r),
|
||||
DEF_FORK(samsung_m), DEF_FORK(samsung_n),
|
||||
DEF_FORK(samsung_o), DEF_FORK(samsung_p),
|
||||
#if ENABLE_LEGACY_DP
|
||||
DEF_FORK(r_dp2), DEF_FORK(r_dp3)
|
||||
#endif
|
||||
};
|
||||
const int nativeForkAndSpecialize_methods_num = std::size(nativeForkAndSpecialize_methods);
|
||||
|
||||
const JNINativeMethod nativeSpecializeAppProcess_methods[] = {
|
||||
DEF_SPEC(q), DEF_SPEC(q_alt),
|
||||
DEF_SPEC(r), DEF_SPEC(samsung_q),
|
||||
#if ENABLE_LEGACY_DP
|
||||
DEF_SPEC(r_dp2), DEF_SPEC(r_dp3)
|
||||
#endif
|
||||
};
|
||||
const int nativeSpecializeAppProcess_methods_num = std::size(
|
||||
nativeSpecializeAppProcess_methods);
|
||||
|
||||
const JNINativeMethod nativeForkSystemServer_methods[] = {
|
||||
DEF_SERVER(m), DEF_SERVER(samsung_q)
|
||||
};
|
||||
const int nativeForkSystemServer_methods_num = std::size(nativeForkSystemServer_methods);
|
||||
|
||||
}
|
241
native/jni/inject/ptrace.cpp
Normal file
241
native/jni/inject/ptrace.cpp
Normal file
@@ -0,0 +1,241 @@
|
||||
/*
|
||||
* Original code: https://github.com/Chainfire/injectvm-binderjack/blob/master/app/src/main/jni/libinject/inject.cpp
|
||||
* The code is heavily modified and sublicensed to GPLv3 for incorporating into Magisk.
|
||||
*
|
||||
* Copyright (c) 2015, Simone 'evilsocket' Margaritelli
|
||||
* Copyright (c) 2015-2019, Jorrit 'Chainfire' Jongma
|
||||
* Copyright (c) 2021, John 'topjohnwu' Wu
|
||||
*
|
||||
* See original LICENSE file from the original project for additional details:
|
||||
* https://github.com/Chainfire/injectvm-binderjack/blob/master/LICENSE
|
||||
*/
|
||||
|
||||
/*
|
||||
* NOTE:
|
||||
* The code in this file was originally planned to be used for some features,
|
||||
* but it turned out to be unsuitable for the task. However, this shall remain
|
||||
* in our arsenal in case it may be used in the future.
|
||||
*/
|
||||
|
||||
#include <elf.h>
|
||||
#include <sys/mount.h>
|
||||
#include <sys/ptrace.h>
|
||||
#include <sys/wait.h>
|
||||
|
||||
#include <utils.hpp>
|
||||
|
||||
#include "inject.hpp"
|
||||
#include "ptrace.hpp"
|
||||
|
||||
using namespace std;
|
||||
|
||||
#if defined(__arm__)
|
||||
#define CPSR_T_MASK (1u << 5)
|
||||
#define PARAMS_IN_REGS 4
|
||||
#elif defined(__aarch64__)
|
||||
#define CPSR_T_MASK (1u << 5)
|
||||
#define PARAMS_IN_REGS 8
|
||||
#define pt_regs user_pt_regs
|
||||
#define uregs regs
|
||||
#define ARM_pc pc
|
||||
#define ARM_sp sp
|
||||
#define ARM_cpsr pstate
|
||||
#define ARM_lr regs[30]
|
||||
#define ARM_r0 regs[0]
|
||||
#endif
|
||||
|
||||
bool _remote_read(int pid, uintptr_t addr, void *buf, size_t len) {
|
||||
for (size_t i = 0; i < len; i += sizeof(long)) {
|
||||
long data = xptrace(PTRACE_PEEKTEXT, pid, reinterpret_cast<void*>(addr + i));
|
||||
if (data < 0)
|
||||
return false;
|
||||
memcpy(static_cast<uint8_t *>(buf) + i, &data, std::min(len - i, sizeof(data)));
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool _remote_write(int pid, uintptr_t addr, const void *buf, size_t len) {
|
||||
for (size_t i = 0; i < len; i += sizeof(long)) {
|
||||
long data = 0;
|
||||
memcpy(&data, static_cast<const uint8_t *>(buf) + i, std::min(len - i, sizeof(data)));
|
||||
if (xptrace(PTRACE_POKETEXT, pid, reinterpret_cast<void*>(addr + i), data) < 0)
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// Get remote registers
|
||||
#define remote_getregs(regs) _remote_getregs(pid, regs)
|
||||
static void _remote_getregs(int pid, pt_regs *regs) {
|
||||
#if defined(__LP64__)
|
||||
uintptr_t regset = NT_PRSTATUS;
|
||||
iovec iov{};
|
||||
iov.iov_base = regs;
|
||||
iov.iov_len = sizeof(*regs);
|
||||
xptrace(PTRACE_GETREGSET, pid, reinterpret_cast<void*>(regset), &iov);
|
||||
#else
|
||||
xptrace(PTRACE_GETREGS, pid, nullptr, regs);
|
||||
#endif
|
||||
}
|
||||
|
||||
// Set remote registers
|
||||
#define remote_setregs(regs) _remote_setregs(pid, regs)
|
||||
static void _remote_setregs(int pid, pt_regs *regs) {
|
||||
#if defined(__LP64__)
|
||||
uintptr_t regset = NT_PRSTATUS;
|
||||
iovec iov{};
|
||||
iov.iov_base = regs;
|
||||
iov.iov_len = sizeof(*regs);
|
||||
xptrace(PTRACE_SETREGSET, pid, reinterpret_cast<void*>(regset), &iov);
|
||||
#else
|
||||
xptrace(PTRACE_SETREGS, pid, nullptr, regs);
|
||||
#endif
|
||||
}
|
||||
|
||||
uintptr_t remote_call_abi(int pid, uintptr_t func_addr, int nargs, va_list va) {
|
||||
pt_regs regs, regs_bak;
|
||||
|
||||
// Get registers and save a backup
|
||||
remote_getregs(®s);
|
||||
memcpy(®s_bak, ®s, sizeof(regs));
|
||||
|
||||
// ABI dependent: Setup stack and registers to perform the call
|
||||
|
||||
#if defined(__arm__) || defined(__aarch64__)
|
||||
// Fill R0-Rx with the first 4 (32-bit) or 8 (64-bit) parameters
|
||||
for (int i = 0; (i < nargs) && (i < PARAMS_IN_REGS); ++i) {
|
||||
regs.uregs[i] = va_arg(va, uintptr_t);
|
||||
}
|
||||
|
||||
// Push remaining parameters onto stack
|
||||
if (nargs > PARAMS_IN_REGS) {
|
||||
regs.ARM_sp -= sizeof(uintptr_t) * (nargs - PARAMS_IN_REGS);
|
||||
uintptr_t stack = regs.ARM_sp;
|
||||
for (int i = PARAMS_IN_REGS; i < nargs; ++i) {
|
||||
uintptr_t arg = va_arg(va, uintptr_t);
|
||||
remote_write(stack, &arg, sizeof(uintptr_t));
|
||||
stack += sizeof(uintptr_t);
|
||||
}
|
||||
}
|
||||
|
||||
// Set return address
|
||||
regs.ARM_lr = 0;
|
||||
|
||||
// Set function address to call
|
||||
regs.ARM_pc = func_addr;
|
||||
|
||||
// Setup the current processor status register
|
||||
if (regs.ARM_pc & 1u) {
|
||||
// thumb
|
||||
regs.ARM_pc &= (~1u);
|
||||
regs.ARM_cpsr |= CPSR_T_MASK;
|
||||
} else {
|
||||
// arm
|
||||
regs.ARM_cpsr &= ~CPSR_T_MASK;
|
||||
}
|
||||
#elif defined(__i386__)
|
||||
// Push all params onto stack
|
||||
regs.esp -= sizeof(uintptr_t) * nargs;
|
||||
uintptr_t stack = regs.esp;
|
||||
for (int i = 0; i < nargs; ++i) {
|
||||
uintptr_t arg = va_arg(va, uintptr_t);
|
||||
remote_write(stack, &arg, sizeof(uintptr_t));
|
||||
stack += sizeof(uintptr_t);
|
||||
}
|
||||
|
||||
// Push return address onto stack
|
||||
uintptr_t ret_addr = 0;
|
||||
regs.esp -= sizeof(uintptr_t);
|
||||
remote_write(regs.esp, &ret_addr, sizeof(uintptr_t));
|
||||
|
||||
// Set function address to call
|
||||
regs.eip = func_addr;
|
||||
#elif defined(__x86_64__)
|
||||
// Align, rsp - 8 must be a multiple of 16 at function entry point
|
||||
uintptr_t space = sizeof(uintptr_t);
|
||||
if (nargs > 6)
|
||||
space += sizeof(uintptr_t) * (nargs - 6);
|
||||
while (((regs.rsp - space - 8) & 0xF) != 0)
|
||||
regs.rsp--;
|
||||
|
||||
// Fill [RDI, RSI, RDX, RCX, R8, R9] with the first 6 parameters
|
||||
for (int i = 0; (i < nargs) && (i < 6); ++i) {
|
||||
uintptr_t arg = va_arg(va, uintptr_t);
|
||||
switch (i) {
|
||||
case 0: regs.rdi = arg; break;
|
||||
case 1: regs.rsi = arg; break;
|
||||
case 2: regs.rdx = arg; break;
|
||||
case 3: regs.rcx = arg; break;
|
||||
case 4: regs.r8 = arg; break;
|
||||
case 5: regs.r9 = arg; break;
|
||||
}
|
||||
}
|
||||
|
||||
// Push remaining parameters onto stack
|
||||
if (nargs > 6) {
|
||||
regs.rsp -= sizeof(uintptr_t) * (nargs - 6);
|
||||
uintptr_t stack = regs.rsp;
|
||||
for(int i = 6; i < nargs; ++i) {
|
||||
uintptr_t arg = va_arg(va, uintptr_t);
|
||||
remote_write(stack, &arg, sizeof(uintptr_t));
|
||||
stack += sizeof(uintptr_t);
|
||||
}
|
||||
}
|
||||
|
||||
// Push return address onto stack
|
||||
uintptr_t ret_addr = 0;
|
||||
regs.rsp -= sizeof(uintptr_t);
|
||||
remote_write(regs.rsp, &ret_addr, sizeof(uintptr_t));
|
||||
|
||||
// Set function address to call
|
||||
regs.rip = func_addr;
|
||||
|
||||
// may be needed
|
||||
regs.rax = 0;
|
||||
regs.orig_rax = 0;
|
||||
#else
|
||||
#error Unsupported ABI
|
||||
#endif
|
||||
|
||||
// Resume process to do the call
|
||||
remote_setregs(®s);
|
||||
xptrace(PTRACE_CONT, pid);
|
||||
|
||||
// Catch SIGSEGV caused by the 0 return address
|
||||
int status;
|
||||
while (waitpid(pid, &status, __WALL | __WNOTHREAD) == pid) {
|
||||
if (WIFSTOPPED(status) && (WSTOPSIG(status) == SIGSEGV))
|
||||
break;
|
||||
xptrace(PTRACE_CONT, pid);
|
||||
}
|
||||
|
||||
// Get registers again for return value
|
||||
remote_getregs(®s);
|
||||
|
||||
// Restore registers
|
||||
remote_setregs(®s_bak);
|
||||
|
||||
#if defined(__arm__) || defined(__aarch64__)
|
||||
return regs.ARM_r0;
|
||||
#elif defined(__i386__)
|
||||
return regs.eax;
|
||||
#elif defined(__x86_64__)
|
||||
return regs.rax;
|
||||
#endif
|
||||
}
|
||||
|
||||
uintptr_t remote_call_vararg(int pid, uintptr_t addr, int nargs, ...) {
|
||||
char lib_name[4096];
|
||||
auto local = get_function_lib(addr, lib_name);
|
||||
if (local == 0)
|
||||
return 0;
|
||||
auto remote = get_remote_lib(pid, lib_name);
|
||||
if (remote == 0)
|
||||
return 0;
|
||||
addr = addr - local + remote;
|
||||
va_list va;
|
||||
va_start(va, nargs);
|
||||
auto result = remote_call_abi(pid, addr, nargs, va);
|
||||
va_end(va);
|
||||
return result;
|
||||
}
|
27
native/jni/inject/ptrace.hpp
Normal file
27
native/jni/inject/ptrace.hpp
Normal file
@@ -0,0 +1,27 @@
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
// Write bytes to the remote process at addr
|
||||
bool _remote_write(int pid, uintptr_t addr, const void *buf, size_t len);
|
||||
#define remote_write(...) _remote_write(pid, __VA_ARGS__)
|
||||
|
||||
// Read bytes from the remote process at addr
|
||||
bool _remote_read(int pid, uintptr_t addr, void *buf, size_t len);
|
||||
#define remote_read(...) _remote_read(pid, __VA_ARGS__)
|
||||
|
||||
// Call a remote function
|
||||
// Arguments are expected to be only integer-like or pointer types
|
||||
// as other more complex C ABIs are not implemented.
|
||||
uintptr_t remote_call_abi(int pid, uintptr_t func_addr, int nargs, va_list va);
|
||||
|
||||
// Find remote offset and invoke function
|
||||
uintptr_t remote_call_vararg(int pid, uintptr_t addr, int nargs, ...);
|
||||
|
||||
// C++ wrapper for auto argument counting and casting function pointers
|
||||
template<class FuncPtr, class ...Args>
|
||||
static uintptr_t _remote_call(int pid, FuncPtr sym, Args && ...args) {
|
||||
auto addr = reinterpret_cast<uintptr_t>(sym);
|
||||
return remote_call_vararg(pid, addr, sizeof...(args), std::forward<Args>(args)...);
|
||||
}
|
||||
#define remote_call(...) _remote_call(pid, __VA_ARGS__)
|
101
native/jni/inject/utils.cpp
Normal file
101
native/jni/inject/utils.cpp
Normal file
@@ -0,0 +1,101 @@
|
||||
#include <utils.hpp>
|
||||
|
||||
#include "inject.hpp"
|
||||
|
||||
using namespace std;
|
||||
|
||||
namespace {
|
||||
|
||||
struct map_info {
|
||||
uintptr_t start;
|
||||
uintptr_t end;
|
||||
int perms;
|
||||
char *path;
|
||||
|
||||
map_info() : start(0), end(0), perms(0), path(nullptr) {}
|
||||
|
||||
enum {
|
||||
EXEC = (1 << 0),
|
||||
WRITE = (1 << 1),
|
||||
READ = (1 << 2),
|
||||
};
|
||||
};
|
||||
|
||||
} // namespace
|
||||
|
||||
template<typename Func>
|
||||
static void parse_maps(int pid, Func fn) {
|
||||
char file[32];
|
||||
|
||||
// format: start-end perms offset dev inode path
|
||||
sprintf(file, "/proc/%d/maps", pid);
|
||||
file_readline(true, file, [=](string_view l) -> bool {
|
||||
char *pos = (char *) l.data();
|
||||
map_info info;
|
||||
|
||||
// Parse address hex strings
|
||||
info.start = strtoul(pos, &pos, 16);
|
||||
info.end = strtoul(++pos, &pos, 16);
|
||||
|
||||
// Parse permissions
|
||||
if (*(++pos) != '-')
|
||||
info.perms |= map_info::READ;
|
||||
if (*(++pos) != '-')
|
||||
info.perms |= map_info::WRITE;
|
||||
if (*(++pos) != '-')
|
||||
info.perms |= map_info::EXEC;
|
||||
pos += 3;
|
||||
|
||||
// Skip everything except path
|
||||
int path_off;
|
||||
sscanf(pos, "%*s %*s %*s %n%*s", &path_off);
|
||||
pos += path_off;
|
||||
info.path = pos;
|
||||
|
||||
return fn(info);
|
||||
});
|
||||
}
|
||||
|
||||
void unmap_all(const char *name) {
|
||||
vector<map_info> unmaps;
|
||||
parse_maps(getpid(), [=, &unmaps](map_info &info) -> bool {
|
||||
if (strcmp(info.path, name) == 0)
|
||||
unmaps.emplace_back(info);
|
||||
return true;
|
||||
});
|
||||
for (map_info &info : unmaps) {
|
||||
void *addr = reinterpret_cast<void *>(info.start);
|
||||
size_t size = info.end - info.start;
|
||||
munmap(addr, size);
|
||||
if (info.perms & map_info::READ) {
|
||||
// Make sure readable pages are still readable
|
||||
xmmap(addr, size, PROT_READ, MAP_ANONYMOUS | MAP_PRIVATE, 0, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
uintptr_t get_function_lib(uintptr_t addr, char *lib) {
|
||||
uintptr_t base = 0;
|
||||
parse_maps(getpid(), [=, &base](map_info &info) -> bool {
|
||||
if (addr >= info.start && addr < info.end) {
|
||||
if (lib)
|
||||
strcpy(lib, info.path);
|
||||
base = info.start;
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
});
|
||||
return base;
|
||||
}
|
||||
|
||||
uintptr_t get_remote_lib(int pid, const char *lib) {
|
||||
uintptr_t base = 0;
|
||||
parse_maps(pid, [=, &base](map_info &info) -> bool {
|
||||
if (strcmp(info.path, lib) == 0 && (info.perms & map_info::EXEC)) {
|
||||
base = info.start;
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
});
|
||||
return base;
|
||||
}
|
@@ -443,6 +443,7 @@ void repack(const char* src_img, const char* out_img, bool skip_comp) {
|
||||
uint32_t second;
|
||||
uint32_t extra;
|
||||
uint32_t dtb;
|
||||
uint32_t total;
|
||||
} off;
|
||||
|
||||
fprintf(stderr, "Repack to boot image: [%s]\n", out_img);
|
||||
@@ -565,11 +566,12 @@ void repack(const char* src_img, const char* out_img, bool skip_comp) {
|
||||
restore_buf(fd, LG_BUMP_MAGIC, 16);
|
||||
}
|
||||
|
||||
off.total = lseek(fd, 0, SEEK_CUR);
|
||||
|
||||
// Pad image to at least original size if not chromeos (as it requires post processing)
|
||||
if (!is_flag(CHROMEOS_FLAG)) {
|
||||
auto current_sz = lseek(fd, 0, SEEK_CUR);
|
||||
if (current_sz < boot.map_size) {
|
||||
int padding = boot.map_size - current_sz;
|
||||
if (off.total < boot.map_size) {
|
||||
int padding = boot.map_size - off.total;
|
||||
write_zero(fd, padding);
|
||||
}
|
||||
}
|
||||
@@ -642,11 +644,11 @@ void repack(const char* src_img, const char* out_img, bool skip_comp) {
|
||||
// DHTB header
|
||||
auto hdr = reinterpret_cast<dhtb_hdr *>(boot.map_addr);
|
||||
memcpy(hdr, DHTB_MAGIC, 8);
|
||||
hdr->size = boot.map_size - sizeof(dhtb_hdr);
|
||||
hdr->size = off.total - sizeof(dhtb_hdr);
|
||||
SHA256_hash(boot.map_addr + sizeof(dhtb_hdr), hdr->size, hdr->checksum);
|
||||
} else if (is_flag(BLOB_FLAG)) {
|
||||
// Blob header
|
||||
auto hdr = reinterpret_cast<blob_hdr *>(boot.map_addr);
|
||||
hdr->size = boot.map_size - sizeof(blob_hdr);
|
||||
hdr->size = off.total - sizeof(blob_hdr);
|
||||
}
|
||||
}
|
||||
|
@@ -124,36 +124,35 @@ static void dtb_print(const char *file, bool fstab) {
|
||||
munmap(dtb, size);
|
||||
}
|
||||
|
||||
[[maybe_unused]]
|
||||
static bool dtb_patch_rebuild(uint8_t *dtb, size_t dtb_sz, const char *file);
|
||||
|
||||
static bool dtb_patch(const char *file) {
|
||||
bool keepverity = check_env("KEEPVERITY");
|
||||
bool patched = false;
|
||||
bool keep_verity = check_env("KEEPVERITY");
|
||||
|
||||
size_t size;
|
||||
uint8_t *dtb;
|
||||
fprintf(stderr, "Loading dtbs from [%s]\n", file);
|
||||
mmap_rw(file, dtb, size);
|
||||
// Loop through all the dtbs
|
||||
int dtb_num = 0;
|
||||
|
||||
bool patched = false;
|
||||
for (int i = 0; i < size; ++i) {
|
||||
if (memcmp(dtb + i, FDT_MAGIC_STR, 4) == 0) {
|
||||
auto fdt = dtb + i;
|
||||
fprintf(stderr, "Loading dtb.%04d\n", dtb_num);
|
||||
if (int fstab = find_fstab(fdt); fstab >= 0) {
|
||||
int node;
|
||||
fdt_for_each_subnode(node, fdt, fstab) {
|
||||
const char *name = fdt_get_name(fdt, node, nullptr);
|
||||
fprintf(stderr, "Found fstab entry [%s]\n", name);
|
||||
if (!keepverity) {
|
||||
if (!keep_verity) {
|
||||
int len;
|
||||
auto value = fdt_getprop(fdt, node, "fsmgr_flags", &len);
|
||||
patched |= patch_verity(const_cast<void *>(value), len) != len;
|
||||
char *value = (char *) fdt_getprop(fdt, node, "fsmgr_flags", &len);
|
||||
patched |= patch_verity(value, len) != len;
|
||||
}
|
||||
}
|
||||
}
|
||||
++dtb_num;
|
||||
i += fdt_totalsize(fdt) - 1;
|
||||
}
|
||||
}
|
||||
fprintf(stderr, "\n");
|
||||
|
||||
munmap(dtb, size);
|
||||
return patched;
|
||||
}
|
||||
@@ -176,7 +175,6 @@ int dtb_commands(int argc, char *argv[]) {
|
||||
}
|
||||
|
||||
namespace {
|
||||
// Unused, but keep these precious code as they took TONs of effort to write
|
||||
|
||||
struct fdt_blob {
|
||||
void *fdt;
|
||||
@@ -184,62 +182,29 @@ namespace {
|
||||
uint32_t len;
|
||||
};
|
||||
|
||||
template <class Iter>
|
||||
class fdt_map_iter {
|
||||
public:
|
||||
typedef decltype(std::declval<typename Iter::value_type::second_type>().fdt) value_type;
|
||||
typedef value_type* pointer;
|
||||
typedef value_type& reference;
|
||||
}
|
||||
|
||||
explicit fdt_map_iter(Iter j) : i(j) {}
|
||||
fdt_map_iter& operator++() { ++i; return *this; }
|
||||
fdt_map_iter operator++(int) { auto tmp = *this; ++(*this); return tmp; }
|
||||
fdt_map_iter& operator--() { --i; return *this; }
|
||||
fdt_map_iter operator--(int) { auto tmp = *this; --(*this); return tmp; }
|
||||
bool operator==(fdt_map_iter j) const { return i == j.i; }
|
||||
bool operator!=(fdt_map_iter j) const { return !(*this == j); }
|
||||
reference operator*() { return i->second.fdt; }
|
||||
pointer operator->() { return &i->second.fdt; }
|
||||
private:
|
||||
Iter i;
|
||||
};
|
||||
|
||||
template<class Iter>
|
||||
inline fdt_map_iter<Iter> make_iter(Iter j) { return fdt_map_iter<Iter>(j); }
|
||||
|
||||
template <typename Iter>
|
||||
static bool fdt_patch(Iter first, Iter last) {
|
||||
bool keepverity = check_env("KEEPVERITY");
|
||||
bool redirect = check_env("TWOSTAGEINIT");
|
||||
bool modified = false;
|
||||
|
||||
int idx = 0;
|
||||
for (auto it = first; it != last; ++it) {
|
||||
++idx;
|
||||
auto fdt = *it;
|
||||
static bool fdt_patch(void *fdt) {
|
||||
int fstab = find_fstab(fdt);
|
||||
if (fstab < 0)
|
||||
continue;
|
||||
fprintf(stderr, "Found fstab in dtb.%04d\n", idx - 1);
|
||||
int block;
|
||||
fdt_for_each_subnode(block, fdt, fstab) {
|
||||
const char *name = fdt_get_name(fdt, block, nullptr);
|
||||
fprintf(stderr, "Found entry [%s] in fstab\n", name);
|
||||
if (!keepverity) {
|
||||
int size;
|
||||
auto value = fdt_getprop(fdt, block, "fsmgr_flags", &size);
|
||||
char *copy = static_cast<char *>(memcpy(malloc(size), value, size));
|
||||
if (patch_verity(copy, size) != size) {
|
||||
return false;
|
||||
bool modified = false;
|
||||
int node;
|
||||
fdt_for_each_subnode(node, fdt, fstab) {
|
||||
const char *name = fdt_get_name(fdt, node, nullptr);
|
||||
// Force remove AVB for 2SI since it may bootloop some devices
|
||||
int len;
|
||||
auto value = (const char *) fdt_getprop(fdt, node, "fsmgr_flags", &len);
|
||||
string copy(value, len);
|
||||
uint32_t new_len = patch_verity(copy.data(), len);
|
||||
if (new_len != len) {
|
||||
modified = true;
|
||||
fdt_setprop_string(fdt, block, "fsmgr_flags", copy);
|
||||
fdt_setprop(fdt, node, "fsmgr_flags", copy.data(), new_len);
|
||||
}
|
||||
free(copy);
|
||||
}
|
||||
if (redirect && name == "system"sv) {
|
||||
if (name == "system"sv) {
|
||||
fprintf(stderr, "Setting [mnt_point] to [/system_root]\n");
|
||||
fdt_setprop_string(fdt, node, "mnt_point", "/system_root");
|
||||
modified = true;
|
||||
fprintf(stderr, "Changing mnt_point to /system_root\n");
|
||||
fdt_setprop_string(fdt, block, "mnt_point", "/system_root");
|
||||
}
|
||||
}
|
||||
}
|
||||
return modified;
|
||||
@@ -248,21 +213,22 @@ namespace {
|
||||
#define MAX_FDT_GROWTH 256
|
||||
|
||||
template <class Table, class Header>
|
||||
static int dt_table_patch(const Header *hdr, const char *out) {
|
||||
static bool dt_table_patch(const Header *hdr, const char *out) {
|
||||
map<uint32_t, fdt_blob> dtb_map;
|
||||
auto buf = reinterpret_cast<const uint8_t *>(hdr);
|
||||
auto tables = reinterpret_cast<const Table *>(hdr + 1);
|
||||
auto tables = reinterpret_cast<const Table *>(buf + sizeof(Header));
|
||||
|
||||
constexpr bool is_dt_table = std::is_same_v<Header, dt_table_header>;
|
||||
constexpr bool is_aosp = std::is_same_v<Header, dt_table_header>;
|
||||
|
||||
// AOSP DTB store ints in big endian
|
||||
using endian_conv = uint32_t (*)(uint32_t);
|
||||
endian_conv be_to_le;
|
||||
endian_conv le_to_be;
|
||||
if constexpr (is_dt_table) {
|
||||
if constexpr (is_aosp) {
|
||||
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; };
|
||||
be_to_le = le_to_be = [](uint32_t x) { return x; };
|
||||
}
|
||||
|
||||
// Collect all dtbs
|
||||
@@ -279,15 +245,19 @@ namespace {
|
||||
}
|
||||
}
|
||||
if (dtb_map.empty())
|
||||
return 1;
|
||||
return false;
|
||||
|
||||
// Patch fdt
|
||||
if (!fdt_patch(make_iter(dtb_map.begin()), make_iter(dtb_map.end())))
|
||||
return 1;
|
||||
bool modified = false;
|
||||
for (auto &[_, blob] : dtb_map)
|
||||
modified |= fdt_patch(blob.fdt);
|
||||
if (!modified)
|
||||
return false;
|
||||
|
||||
unlink(out);
|
||||
int fd = xopen(out, O_RDWR | O_CREAT | O_CLOEXEC, 0644);
|
||||
|
||||
// This value is only used if AOSP DTB
|
||||
uint32_t total_size = 0;
|
||||
|
||||
// Copy headers and tables
|
||||
@@ -299,7 +269,7 @@ namespace {
|
||||
|
||||
// Guess alignment using gcd
|
||||
uint32_t align = 1;
|
||||
if constexpr (!is_dt_table) {
|
||||
if constexpr (!is_aosp) {
|
||||
auto it = dtb_map.begin();
|
||||
align = (it++)->first;
|
||||
for (; it != dtb_map.end(); ++it)
|
||||
@@ -313,14 +283,15 @@ namespace {
|
||||
fdt_pack(fdt);
|
||||
auto size = fdt_totalsize(fdt);
|
||||
total_size += xwrite(fd, fdt, size);
|
||||
if constexpr (!is_aosp) {
|
||||
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 headers
|
||||
if constexpr (is_dt_table) {
|
||||
if constexpr (is_aosp) {
|
||||
auto hdr_rw = reinterpret_cast<Header *>(addr);
|
||||
hdr_rw->total_size = le_to_be(total_size);
|
||||
}
|
||||
@@ -334,12 +305,13 @@ namespace {
|
||||
munmap(addr, mmap_sz);
|
||||
close(fd);
|
||||
|
||||
return 0;
|
||||
return true;
|
||||
}
|
||||
|
||||
static int blob_patch(uint8_t *dtb, size_t dtb_sz, const char *out) {
|
||||
static bool blob_patch(uint8_t *dtb, size_t dtb_sz, const char *out) {
|
||||
vector<uint8_t *> fdt_list;
|
||||
vector<uint32_t> padding_list;
|
||||
|
||||
for (int i = 0; i < dtb_sz; ++i) {
|
||||
if (memcmp(dtb + i, FDT_MAGIC_STR, 4) == 0) {
|
||||
auto len = fdt_totalsize(dtb + i);
|
||||
@@ -354,8 +326,11 @@ namespace {
|
||||
}
|
||||
}
|
||||
|
||||
if (!fdt_patch(fdt_list.begin(), fdt_list.end()))
|
||||
return 1;
|
||||
bool modified = false;
|
||||
for (auto fdt : fdt_list)
|
||||
modified |= fdt_patch(fdt);
|
||||
if (!modified)
|
||||
return false;
|
||||
|
||||
unlink(out);
|
||||
int fd = xopen(out, O_WRONLY | O_CREAT | O_CLOEXEC, 0644);
|
||||
@@ -373,82 +348,73 @@ namespace {
|
||||
}
|
||||
close(fd);
|
||||
|
||||
return 0;
|
||||
return true;
|
||||
}
|
||||
|
||||
#define MATCH(s) (memcmp(dtb, s, sizeof(s) - 1) == 0)
|
||||
|
||||
[[maybe_unused]] static int dtb_patch(const char *in, const char *out) {
|
||||
if (!out)
|
||||
out = in;
|
||||
size_t dtb_sz ;
|
||||
uint8_t *dtb;
|
||||
fprintf(stderr, "Loading dtbs from [%s]\n", in);
|
||||
mmap_ro(in, dtb, dtb_sz);
|
||||
run_finally f([&]{ munmap(dtb, dtb_sz); });
|
||||
|
||||
static bool dtb_patch_rebuild(uint8_t *dtb, size_t dtb_sz, const char *file) {
|
||||
if (MATCH(QCDT_MAGIC)) {
|
||||
auto hdr = reinterpret_cast<qcdt_hdr*>(dtb);
|
||||
switch (hdr->version) {
|
||||
case 1:
|
||||
fprintf(stderr, "QCDT v1\n");
|
||||
return dt_table_patch<qctable_v1>(hdr, out);
|
||||
return dt_table_patch<qctable_v1>(hdr, file);
|
||||
case 2:
|
||||
fprintf(stderr, "QCDT v2\n");
|
||||
return dt_table_patch<qctable_v2>(hdr, out);
|
||||
return dt_table_patch<qctable_v2>(hdr, file);
|
||||
case 3:
|
||||
fprintf(stderr, "QCDT v3\n");
|
||||
return dt_table_patch<qctable_v3>(hdr, out);
|
||||
return dt_table_patch<qctable_v3>(hdr, file);
|
||||
default:
|
||||
return 1;
|
||||
return false;
|
||||
}
|
||||
} else if (MATCH(DTBH_MAGIC)) {
|
||||
auto hdr = reinterpret_cast<dtbh_hdr *>(dtb);
|
||||
switch (hdr->version) {
|
||||
case 2:
|
||||
fprintf(stderr, "DTBH v2\n");
|
||||
return dt_table_patch<bhtable_v2>(hdr, out);
|
||||
return dt_table_patch<bhtable_v2>(hdr, file);
|
||||
default:
|
||||
return 1;
|
||||
return false;
|
||||
}
|
||||
} else if (MATCH(PXADT_MAGIC)) {
|
||||
auto hdr = reinterpret_cast<pxadt_hdr *>(dtb);
|
||||
switch (hdr->version) {
|
||||
case 1:
|
||||
fprintf(stderr, "PXA-DT v1\n");
|
||||
return dt_table_patch<pxatable_v1>(hdr, out);
|
||||
return dt_table_patch<pxatable_v1>(hdr, file);
|
||||
default:
|
||||
return 1;
|
||||
return false;
|
||||
}
|
||||
} else if (MATCH(PXA19xx_MAGIC)) {
|
||||
auto hdr = reinterpret_cast<pxa19xx_hdr *>(dtb);
|
||||
switch (hdr->version) {
|
||||
case 1:
|
||||
fprintf(stderr, "PXA-19xx v1\n");
|
||||
return dt_table_patch<pxatable_v1>(hdr, out);
|
||||
return dt_table_patch<pxatable_v1>(hdr, file);
|
||||
default:
|
||||
return 1;
|
||||
return false;
|
||||
}
|
||||
} else if (MATCH(SPRD_MAGIC)) {
|
||||
auto hdr = reinterpret_cast<sprd_hdr *>(dtb);
|
||||
switch (hdr->version) {
|
||||
case 1:
|
||||
fprintf(stderr, "SPRD v1\n");
|
||||
return dt_table_patch<sprdtable_v1>(hdr, out);
|
||||
return dt_table_patch<sprdtable_v1>(hdr, file);
|
||||
default:
|
||||
return 1;
|
||||
return false;
|
||||
}
|
||||
} 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 dt_table_patch<dt_table_entry>(hdr, out);
|
||||
return dt_table_patch<dt_table_entry>(hdr, file);
|
||||
default:
|
||||
return 1;
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
return blob_patch(dtb, dtb_sz, out);
|
||||
}
|
||||
return blob_patch(dtb, dtb_sz, file);
|
||||
}
|
||||
}
|
||||
|
@@ -73,7 +73,6 @@ void magisk_cpio::patch() {
|
||||
#define MAGISK_PATCHED (1 << 0)
|
||||
#define UNSUPPORTED_CPIO (1 << 1)
|
||||
#define COMPRESSED_CPIO (1 << 2)
|
||||
#define TWO_STAGE_INIT (1 << 3)
|
||||
|
||||
int magisk_cpio::test() {
|
||||
for (auto file : UNSUPPORT_LIST)
|
||||
@@ -87,9 +86,6 @@ int magisk_cpio::test() {
|
||||
decompress();
|
||||
}
|
||||
|
||||
if (exists("apex") || exists("first_stage_ramdisk"))
|
||||
flags |= TWO_STAGE_INIT;
|
||||
|
||||
for (auto file : MAGISK_LIST) {
|
||||
if (exists(file)) {
|
||||
flags |= MAGISK_PATCHED;
|
||||
|
@@ -30,7 +30,7 @@ static const char *late_prop_val[] =
|
||||
{ "green", nullptr };
|
||||
|
||||
void hide_sensitive_props() {
|
||||
LOGI("hide_policy: Hiding sensitive props\n");
|
||||
LOGI("hide: Hiding sensitive props\n");
|
||||
|
||||
for (int i = 0; prop_key[i]; ++i) {
|
||||
auto value = getprop(prop_key[i]);
|
||||
@@ -63,7 +63,7 @@ void hide_sensitive_props() {
|
||||
}
|
||||
|
||||
void hide_late_sensitive_props() {
|
||||
LOGI("hide_policy: Hiding sensitive props (late)\n");
|
||||
LOGI("hide: Hiding sensitive props (late)\n");
|
||||
|
||||
for (int i = 0; late_prop_key[i]; ++i) {
|
||||
auto value = getprop(late_prop_key[i]);
|
||||
@@ -74,7 +74,7 @@ void hide_late_sensitive_props() {
|
||||
|
||||
static void lazy_unmount(const char* mountpoint) {
|
||||
if (umount2(mountpoint, MNT_DETACH) != -1)
|
||||
LOGD("hide_policy: Unmounted (%s)\n", mountpoint);
|
||||
LOGD("hide: Unmounted (%s)\n", mountpoint);
|
||||
}
|
||||
|
||||
void hide_daemon(int pid) {
|
||||
@@ -90,10 +90,10 @@ void hide_daemon(int pid) {
|
||||
strncmp(mentry->mnt_dir, "/" #dir, sizeof("/" #dir) - 1) == 0)
|
||||
|
||||
void hide_unmount(int pid) {
|
||||
if (switch_mnt_ns(pid))
|
||||
if (pid > 0 && switch_mnt_ns(pid))
|
||||
return;
|
||||
|
||||
LOGD("hide_policy: handling PID=[%d]\n", pid);
|
||||
LOGD("hide: handling PID=[%d]\n", pid);
|
||||
|
||||
char val;
|
||||
int fd = xopen(SELINUX_ENFORCE, O_RDONLY);
|
||||
|
@@ -1,11 +1,9 @@
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <fcntl.h>
|
||||
#include <dirent.h>
|
||||
#include <string.h>
|
||||
#include <set>
|
||||
|
||||
#include <magisk.hpp>
|
||||
#include <utils.hpp>
|
||||
@@ -15,11 +13,46 @@
|
||||
|
||||
using namespace std;
|
||||
|
||||
static pthread_t proc_monitor_thread;
|
||||
static bool hide_state = false;
|
||||
static set<pair<string, string>> hide_set; /* set of <pkg, process> pair */
|
||||
map<int, vector<string_view>> uid_proc_map; /* uid -> list of process */
|
||||
|
||||
// This locks the 2 variables above
|
||||
static pthread_mutex_t hide_state_lock = PTHREAD_MUTEX_INITIALIZER;
|
||||
// Locks the variables above
|
||||
pthread_mutex_t hide_state_lock = PTHREAD_MUTEX_INITIALIZER;
|
||||
|
||||
#if !ENABLE_INJECT
|
||||
static pthread_t monitor_thread;
|
||||
#endif
|
||||
|
||||
void update_uid_map() {
|
||||
mutex_guard lock(hide_state_lock);
|
||||
uid_proc_map.clear();
|
||||
string data_path(APP_DATA_DIR);
|
||||
size_t len = data_path.length();
|
||||
auto dir = open_dir(APP_DATA_DIR);
|
||||
bool first_iter = true;
|
||||
for (dirent *entry; (entry = xreaddir(dir.get()));) {
|
||||
data_path.resize(len);
|
||||
data_path += '/';
|
||||
data_path += entry->d_name; // multiuser user id
|
||||
data_path += '/';
|
||||
size_t user_len = data_path.length();
|
||||
struct stat st;
|
||||
for (auto &hide : hide_set) {
|
||||
if (hide.first == ISOLATED_MAGIC) {
|
||||
if (!first_iter) continue;
|
||||
// Setup isolated processes
|
||||
uid_proc_map[-1].emplace_back(hide.second);
|
||||
}
|
||||
data_path.resize(user_len);
|
||||
data_path += hide.first;
|
||||
if (stat(data_path.data(), &st))
|
||||
continue;
|
||||
uid_proc_map[st.st_uid].emplace_back(hide.second);
|
||||
}
|
||||
first_iter = false;
|
||||
}
|
||||
}
|
||||
|
||||
// Leave /proc fd opened as we're going to read from it repeatedly
|
||||
static DIR *procfp;
|
||||
@@ -43,28 +76,27 @@ bool hide_enabled() {
|
||||
return hide_state;
|
||||
}
|
||||
|
||||
void set_hide_state(bool state) {
|
||||
mutex_guard g(hide_state_lock);
|
||||
hide_state = state;
|
||||
}
|
||||
|
||||
template <bool str_op(string_view, string_view)>
|
||||
static bool proc_name_match(int pid, const char *name) {
|
||||
char buf[4019];
|
||||
sprintf(buf, "/proc/%d/cmdline", pid);
|
||||
if (FILE *f = fopen(buf, "re")) {
|
||||
fgets(buf, sizeof(buf), f);
|
||||
fclose(f);
|
||||
if (strcmp(buf, name) == 0)
|
||||
if (auto fp = open_file(buf, "re")) {
|
||||
fgets(buf, sizeof(buf), fp.get());
|
||||
if (str_op(buf, name)) {
|
||||
LOGD("hide: kill PID=[%d] (%s)\n", pid, buf);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static void kill_process(const char *name, bool multi = false) {
|
||||
static inline bool str_eql(string_view s, string_view ss) { return s == ss; }
|
||||
|
||||
static void kill_process(const char *name, bool multi = false,
|
||||
bool (*filter)(int, const char *) = proc_name_match<&str_eql>) {
|
||||
crawl_procfs([=](int pid) -> bool {
|
||||
if (proc_name_match(pid, name)) {
|
||||
if (kill(pid, SIGTERM) == 0)
|
||||
LOGD("hide_utils: killed PID=[%d] (%s)\n", pid, name);
|
||||
if (filter(pid, name)) {
|
||||
kill(pid, SIGTERM);
|
||||
return multi;
|
||||
}
|
||||
return true;
|
||||
@@ -72,6 +104,8 @@ static void kill_process(const char *name, bool multi = false) {
|
||||
}
|
||||
|
||||
static bool validate(const char *s) {
|
||||
if (strcmp(s, ISOLATED_MAGIC) == 0)
|
||||
return true;
|
||||
bool dot = false;
|
||||
for (char c; (c = *s); ++s) {
|
||||
if ((c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z') ||
|
||||
@@ -87,7 +121,18 @@ static bool validate(const char *s) {
|
||||
return dot;
|
||||
}
|
||||
|
||||
static int add_list(const char *pkg, const char *proc = "") {
|
||||
static void add_hide_set(const char *pkg, const char *proc) {
|
||||
LOGI("hide_list add: [%s/%s]\n", pkg, proc);
|
||||
hide_set.emplace(pkg, proc);
|
||||
if (strcmp(pkg, ISOLATED_MAGIC) == 0) {
|
||||
// Kill all matching isolated processes
|
||||
kill_process(proc, true, proc_name_match<&str_starts>);
|
||||
} else {
|
||||
kill_process(proc);
|
||||
}
|
||||
}
|
||||
|
||||
static int add_list(const char *pkg, const char *proc) {
|
||||
if (proc[0] == '\0')
|
||||
proc = pkg;
|
||||
|
||||
@@ -105,45 +150,41 @@ static int add_list(const char *pkg, const char *proc = "") {
|
||||
char *err = db_exec(sql);
|
||||
db_err_cmd(err, return DAEMON_ERROR);
|
||||
|
||||
LOGI("hide_list add: [%s/%s]\n", pkg, proc);
|
||||
|
||||
// Critical region
|
||||
{
|
||||
mutex_guard lock(monitor_lock);
|
||||
hide_set.emplace(pkg, proc);
|
||||
// Critical region
|
||||
mutex_guard lock(hide_state_lock);
|
||||
add_hide_set(pkg, proc);
|
||||
}
|
||||
|
||||
kill_process(proc);
|
||||
return DAEMON_SUCCESS;
|
||||
}
|
||||
|
||||
int add_list(int client) {
|
||||
char *pkg = read_string(client);
|
||||
char *proc = read_string(client);
|
||||
int ret = add_list(pkg, proc);
|
||||
free(pkg);
|
||||
free(proc);
|
||||
string pkg = read_string(client);
|
||||
string proc = read_string(client);
|
||||
int ret = add_list(pkg.data(), proc.data());
|
||||
if (ret == DAEMON_SUCCESS)
|
||||
update_uid_map();
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int rm_list(const char *pkg, const char *proc = "") {
|
||||
static int rm_list(const char *pkg, const char *proc) {
|
||||
bool remove = false;
|
||||
{
|
||||
// Critical region
|
||||
mutex_guard lock(monitor_lock);
|
||||
bool remove = false;
|
||||
mutex_guard lock(hide_state_lock);
|
||||
for (auto it = hide_set.begin(); it != hide_set.end();) {
|
||||
if (it->first == pkg && (proc[0] == '\0' || it->second == proc)) {
|
||||
remove = true;
|
||||
LOGI("hide_list rm: [%s]\n", it->second.data());
|
||||
LOGI("hide_list rm: [%s/%s]\n", it->first.data(), it->second.data());
|
||||
it = hide_set.erase(it);
|
||||
} else {
|
||||
++it;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!remove)
|
||||
return HIDE_ITEM_NOT_EXIST;
|
||||
}
|
||||
|
||||
char sql[4096];
|
||||
if (proc[0] == '\0')
|
||||
@@ -157,20 +198,19 @@ static int rm_list(const char *pkg, const char *proc = "") {
|
||||
}
|
||||
|
||||
int rm_list(int client) {
|
||||
char *pkg = read_string(client);
|
||||
char *proc = read_string(client);
|
||||
int ret = rm_list(pkg, proc);
|
||||
free(pkg);
|
||||
free(proc);
|
||||
string pkg = read_string(client);
|
||||
string proc = read_string(client);
|
||||
int ret = rm_list(pkg.data(), proc.data());
|
||||
if (ret == DAEMON_SUCCESS)
|
||||
update_uid_map();
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void init_list(const char *pkg, const char *proc) {
|
||||
LOGI("hide_list init: [%s/%s]\n", pkg, proc);
|
||||
hide_set.emplace(pkg, proc);
|
||||
kill_process(proc);
|
||||
static bool str_ends_safe(string_view s, string_view ss) {
|
||||
// Never kill webview zygote
|
||||
if (s == "webview_zygote")
|
||||
return false;
|
||||
return str_ends(s, ss);
|
||||
}
|
||||
|
||||
#define SNET_PROC "com.google.android.gms.unstable"
|
||||
@@ -178,39 +218,42 @@ static void init_list(const char *pkg, const char *proc) {
|
||||
#define MICROG_PKG "org.microg.gms.droidguard"
|
||||
|
||||
static bool init_list() {
|
||||
LOGD("hide_list: initialize\n");
|
||||
LOGD("hide: initialize\n");
|
||||
|
||||
char *err = db_exec("SELECT * FROM hidelist", [](db_row &row) -> bool {
|
||||
init_list(row["package_name"].data(), row["process"].data());
|
||||
add_hide_set(row["package_name"].data(), row["process"].data());
|
||||
return true;
|
||||
});
|
||||
db_err_cmd(err, return false);
|
||||
|
||||
// If Android Q+, also kill blastula pool
|
||||
// If Android Q+, also kill blastula pool and all app zygotes
|
||||
if (SDK_INT >= 29) {
|
||||
kill_process("usap32", true);
|
||||
kill_process("usap64", true);
|
||||
kill_process("_zygote", true, proc_name_match<&str_ends_safe>);
|
||||
}
|
||||
|
||||
// Add SafetyNet by default
|
||||
init_list(GMS_PKG, SNET_PROC);
|
||||
init_list(MICROG_PKG, SNET_PROC);
|
||||
add_hide_set(GMS_PKG, SNET_PROC);
|
||||
add_hide_set(MICROG_PKG, SNET_PROC);
|
||||
|
||||
// We also need to hide the default GMS process if MAGISKTMP != /sbin
|
||||
// The snet process communicates with the main process and get additional info
|
||||
if (MAGISKTMP != "/sbin")
|
||||
init_list(GMS_PKG, GMS_PKG);
|
||||
add_hide_set(GMS_PKG, GMS_PKG);
|
||||
|
||||
update_uid_map();
|
||||
return true;
|
||||
}
|
||||
|
||||
void ls_list(int client) {
|
||||
FILE *out = fdopen(recv_fd(client), "a");
|
||||
for (auto &hide : hide_set)
|
||||
fprintf(out, "%s|%s\n", hide.first.data(), hide.second.data());
|
||||
fclose(out);
|
||||
write_int(client, DAEMON_SUCCESS);
|
||||
for (auto &hide : hide_set) {
|
||||
write_int(client, hide.first.size() + hide.second.size() + 1);
|
||||
xwrite(client, hide.first.data(), hide.first.size());
|
||||
xwrite(client, "|", 1);
|
||||
xwrite(client, hide.second.data(), hide.second.size());
|
||||
}
|
||||
write_int(client, 0);
|
||||
close(client);
|
||||
}
|
||||
|
||||
@@ -222,8 +265,8 @@ static void update_hide_config() {
|
||||
db_err(err);
|
||||
}
|
||||
|
||||
int launch_magiskhide() {
|
||||
mutex_guard g(hide_state_lock);
|
||||
int launch_magiskhide(bool late_props) {
|
||||
mutex_guard lock(hide_state_lock);
|
||||
|
||||
if (SDK_INT < 19)
|
||||
return DAEMON_ERROR;
|
||||
@@ -231,32 +274,35 @@ int launch_magiskhide() {
|
||||
if (hide_state)
|
||||
return HIDE_IS_ENABLED;
|
||||
|
||||
if (access("/proc/1/ns/mnt", F_OK) != 0)
|
||||
if (access("/proc/self/ns/mnt", F_OK) != 0)
|
||||
return HIDE_NO_NS;
|
||||
|
||||
if (procfp == nullptr && (procfp = opendir("/proc")) == nullptr)
|
||||
return DAEMON_ERROR;
|
||||
|
||||
LOGI("* Starting MagiskHide\n");
|
||||
|
||||
// Initialize the mutex lock
|
||||
pthread_mutex_init(&monitor_lock, nullptr);
|
||||
LOGI("* Enable MagiskHide\n");
|
||||
|
||||
// Initialize the hide list
|
||||
if (!init_list())
|
||||
return DAEMON_ERROR;
|
||||
|
||||
hide_sensitive_props();
|
||||
if (DAEMON_STATE >= STATE_BOOT_COMPLETE || DAEMON_STATE == STATE_NONE)
|
||||
if (late_props)
|
||||
hide_late_sensitive_props();
|
||||
|
||||
#if !ENABLE_INJECT
|
||||
// Start monitoring
|
||||
void *(*start)(void*) = [](void*) -> void* { proc_monitor(); return nullptr; };
|
||||
if (xpthread_create(&proc_monitor_thread, nullptr, start, nullptr))
|
||||
if (new_daemon_thread(&proc_monitor))
|
||||
return DAEMON_ERROR;
|
||||
#endif
|
||||
|
||||
hide_state = true;
|
||||
update_hide_config();
|
||||
|
||||
// Unlock here or else we'll be stuck in deadlock
|
||||
lock.unlock();
|
||||
|
||||
update_uid_map();
|
||||
return DAEMON_SUCCESS;
|
||||
}
|
||||
|
||||
@@ -264,8 +310,12 @@ int stop_magiskhide() {
|
||||
mutex_guard g(hide_state_lock);
|
||||
|
||||
if (hide_state) {
|
||||
LOGI("* Stopping MagiskHide\n");
|
||||
pthread_kill(proc_monitor_thread, SIGTERMTHRD);
|
||||
LOGI("* Disable MagiskHide\n");
|
||||
uid_proc_map.clear();
|
||||
hide_set.clear();
|
||||
#if !ENABLE_INJECT
|
||||
pthread_kill(monitor_thread, SIGTERMTHRD);
|
||||
#endif
|
||||
}
|
||||
|
||||
hide_state = false;
|
||||
@@ -273,20 +323,61 @@ int stop_magiskhide() {
|
||||
return DAEMON_SUCCESS;
|
||||
}
|
||||
|
||||
void auto_start_magiskhide() {
|
||||
void auto_start_magiskhide(bool late_props) {
|
||||
if (hide_enabled()) {
|
||||
pthread_kill(proc_monitor_thread, SIGALRM);
|
||||
#if !ENABLE_INJECT
|
||||
pthread_kill(monitor_thread, SIGALRM);
|
||||
#endif
|
||||
hide_late_sensitive_props();
|
||||
} else if (SDK_INT >= 19) {
|
||||
db_settings dbs;
|
||||
get_db_settings(dbs, HIDE_CONFIG);
|
||||
if (dbs[HIDE_CONFIG])
|
||||
launch_magiskhide();
|
||||
launch_magiskhide(late_props);
|
||||
}
|
||||
}
|
||||
|
||||
bool is_hide_target(int uid, string_view process) {
|
||||
mutex_guard lock(hide_state_lock);
|
||||
|
||||
if (uid % 100000 >= 90000) {
|
||||
// Isolated processes
|
||||
auto it = uid_proc_map.find(-1);
|
||||
if (it == uid_proc_map.end())
|
||||
return false;
|
||||
|
||||
for (auto &s : it->second) {
|
||||
if (str_starts(process, s))
|
||||
return true;
|
||||
}
|
||||
} else {
|
||||
auto it = uid_proc_map.find(uid);
|
||||
if (it == uid_proc_map.end())
|
||||
return false;
|
||||
|
||||
for (auto &s : it->second) {
|
||||
if (s == process)
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
#if !ENABLE_INJECT
|
||||
void test_proc_monitor() {
|
||||
if (procfp == nullptr && (procfp = opendir("/proc")) == nullptr)
|
||||
exit(1);
|
||||
proc_monitor();
|
||||
}
|
||||
#endif
|
||||
|
||||
#if ENABLE_INJECT
|
||||
int check_uid_map(int client) {
|
||||
if (!hide_enabled())
|
||||
return 0;
|
||||
|
||||
int uid = read_int(client);
|
||||
string process = read_string(client);
|
||||
return is_hide_target(uid, process) ? 1 : 0;
|
||||
}
|
||||
#endif
|
||||
|
@@ -1,19 +1,11 @@
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <pthread.h>
|
||||
#include <signal.h>
|
||||
#include <string.h>
|
||||
#include <sys/wait.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/mount.h>
|
||||
|
||||
#include <daemon.hpp>
|
||||
#include <utils.hpp>
|
||||
|
||||
#include "magiskhide.hpp"
|
||||
|
||||
using namespace std::literals;
|
||||
using namespace std;
|
||||
|
||||
[[noreturn]] static void usage(char *arg0) {
|
||||
fprintf(stderr,
|
||||
@@ -35,7 +27,7 @@ using namespace std::literals;
|
||||
exit(1);
|
||||
}
|
||||
|
||||
void magiskhide_handler(int client) {
|
||||
void magiskhide_handler(int client, ucred *cred) {
|
||||
int req = read_int(client);
|
||||
int res = DAEMON_ERROR;
|
||||
|
||||
@@ -53,7 +45,7 @@ void magiskhide_handler(int client) {
|
||||
|
||||
switch (req) {
|
||||
case LAUNCH_MAGISKHIDE:
|
||||
res = launch_magiskhide();
|
||||
res = launch_magiskhide(true);
|
||||
break;
|
||||
case STOP_MAGISKHIDE:
|
||||
res = stop_magiskhide();
|
||||
@@ -70,6 +62,17 @@ void magiskhide_handler(int client) {
|
||||
case HIDE_STATUS:
|
||||
res = hide_enabled() ? HIDE_IS_ENABLED : HIDE_NOT_ENABLED;
|
||||
break;
|
||||
#if ENABLE_INJECT
|
||||
case REMOTE_CHECK_HIDE:
|
||||
res = check_uid_map(client);
|
||||
break;
|
||||
case REMOTE_DO_HIDE:
|
||||
kill(cred->pid, SIGSTOP);
|
||||
write_int(client, 0);
|
||||
hide_daemon(cred->pid);
|
||||
close(client);
|
||||
return;
|
||||
#endif
|
||||
}
|
||||
|
||||
write_int(client, res);
|
||||
@@ -105,7 +108,7 @@ int magiskhide_main(int argc, char *argv[]) {
|
||||
execvp(argv[2], argv + 2);
|
||||
exit(1);
|
||||
}
|
||||
#if 0
|
||||
#if 0 && !ENABLE_INJECT
|
||||
else if (opt == "test"sv)
|
||||
test_proc_monitor();
|
||||
#endif
|
||||
@@ -120,8 +123,6 @@ int magiskhide_main(int argc, char *argv[]) {
|
||||
write_string(fd, argv[2]);
|
||||
write_string(fd, argv[3] ? argv[3] : "");
|
||||
}
|
||||
if (req == LS_HIDELIST)
|
||||
send_fd(fd, STDOUT_FILENO);
|
||||
|
||||
// Get response
|
||||
int code = read_int(fd);
|
||||
@@ -130,30 +131,66 @@ int magiskhide_main(int argc, char *argv[]) {
|
||||
break;
|
||||
case HIDE_NOT_ENABLED:
|
||||
fprintf(stderr, "MagiskHide is not enabled\n");
|
||||
break;
|
||||
goto return_code;
|
||||
case HIDE_IS_ENABLED:
|
||||
fprintf(stderr, "MagiskHide is enabled\n");
|
||||
break;
|
||||
goto return_code;
|
||||
case HIDE_ITEM_EXIST:
|
||||
fprintf(stderr, "Target already exists in hide list\n");
|
||||
break;
|
||||
goto return_code;
|
||||
case HIDE_ITEM_NOT_EXIST:
|
||||
fprintf(stderr, "Target does not exist in hide list\n");
|
||||
break;
|
||||
goto return_code;
|
||||
case HIDE_NO_NS:
|
||||
fprintf(stderr, "Your kernel doesn't support mount namespace\n");
|
||||
break;
|
||||
goto return_code;
|
||||
case HIDE_INVALID_PKG:
|
||||
fprintf(stderr, "Invalid package / process name\n");
|
||||
break;
|
||||
goto return_code;
|
||||
case ROOT_REQUIRED:
|
||||
fprintf(stderr, "Root is required for this operation\n");
|
||||
break;
|
||||
goto return_code;
|
||||
case DAEMON_ERROR:
|
||||
default:
|
||||
fprintf(stderr, "Daemon error\n");
|
||||
return DAEMON_ERROR;
|
||||
}
|
||||
|
||||
if (req == LS_HIDELIST) {
|
||||
string res;
|
||||
for (;;) {
|
||||
read_string(fd, res);
|
||||
if (res.empty())
|
||||
break;
|
||||
printf("%s\n", res.data());
|
||||
}
|
||||
}
|
||||
|
||||
return_code:
|
||||
return req == HIDE_STATUS ? (code == HIDE_IS_ENABLED ? 0 : 1) : code != DAEMON_SUCCESS;
|
||||
}
|
||||
|
||||
#if ENABLE_INJECT
|
||||
int remote_check_hide(int uid, const char *process) {
|
||||
int fd = connect_daemon();
|
||||
write_int(fd, MAGISKHIDE);
|
||||
write_int(fd, REMOTE_CHECK_HIDE);
|
||||
write_int(fd, uid);
|
||||
write_string(fd, process);
|
||||
int res = read_int(fd);
|
||||
close(fd);
|
||||
return res;
|
||||
}
|
||||
|
||||
void remote_request_hide() {
|
||||
int fd = connect_daemon();
|
||||
write_int(fd, MAGISKHIDE);
|
||||
write_int(fd, REMOTE_DO_HIDE);
|
||||
|
||||
// Should receive SIGSTOP before reading anything
|
||||
// During process stop, magiskd will cleanup our mount ns
|
||||
read_int(fd);
|
||||
|
||||
close(fd);
|
||||
}
|
||||
#endif
|
||||
|
@@ -5,41 +5,46 @@
|
||||
#include <pthread.h>
|
||||
#include <unistd.h>
|
||||
#include <dirent.h>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#include <functional>
|
||||
#include <map>
|
||||
#include <set>
|
||||
|
||||
#include <daemon.hpp>
|
||||
|
||||
#define SIGTERMTHRD SIGUSR1
|
||||
#define ISOLATED_MAGIC "isolated"
|
||||
|
||||
// CLI entries
|
||||
int launch_magiskhide();
|
||||
int launch_magiskhide(bool late_props);
|
||||
int stop_magiskhide();
|
||||
int add_list(int client);
|
||||
int rm_list(int client);
|
||||
void ls_list(int client);
|
||||
[[noreturn]] void test_proc_monitor();
|
||||
|
||||
#if !ENABLE_INJECT
|
||||
// Process monitoring
|
||||
[[noreturn]] void proc_monitor();
|
||||
void update_uid_map();
|
||||
[[noreturn]] void test_proc_monitor();
|
||||
#else
|
||||
// Response whether target process should be hidden
|
||||
int check_uid_map(int client);
|
||||
#endif
|
||||
|
||||
// Utility functions
|
||||
void crawl_procfs(const std::function<bool (int)> &fn);
|
||||
void crawl_procfs(DIR *dir, const std::function<bool (int)> &fn);
|
||||
bool hide_enabled();
|
||||
void set_hide_state(bool state);
|
||||
void update_uid_map();
|
||||
bool is_hide_target(int uid, std::string_view process);
|
||||
|
||||
// Hide policies
|
||||
void hide_daemon(int pid);
|
||||
void hide_unmount(int pid = getpid());
|
||||
void hide_unmount(int pid = -1);
|
||||
void hide_sensitive_props();
|
||||
void hide_late_sensitive_props();
|
||||
|
||||
extern pthread_mutex_t monitor_lock;
|
||||
extern std::set<std::pair<std::string, std::string>> hide_set;
|
||||
extern pthread_mutex_t hide_state_lock;
|
||||
extern std::map<int, std::vector<std::string_view>> uid_proc_map;
|
||||
|
||||
enum {
|
||||
LAUNCH_MAGISKHIDE,
|
||||
@@ -48,6 +53,8 @@ enum {
|
||||
RM_HIDELIST,
|
||||
LS_HIDELIST,
|
||||
HIDE_STATUS,
|
||||
REMOTE_CHECK_HIDE,
|
||||
REMOTE_DO_HIDE
|
||||
};
|
||||
|
||||
enum {
|
||||
|
@@ -1,6 +1,3 @@
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include <fcntl.h>
|
||||
#include <signal.h>
|
||||
@@ -21,18 +18,11 @@ using namespace std;
|
||||
|
||||
static int inotify_fd = -1;
|
||||
|
||||
static void term_thread(int sig = SIGTERMTHRD);
|
||||
static void new_zygote(int pid);
|
||||
|
||||
/**********************
|
||||
* All data structures
|
||||
**********************/
|
||||
|
||||
set<pair<string, string>> hide_set; /* set of <pkg, process> pair */
|
||||
static map<int, struct stat> zygote_map; /* zygote pid -> mnt ns */
|
||||
static map<int, vector<string_view>> uid_proc_map; /* uid -> list of process */
|
||||
|
||||
pthread_mutex_t monitor_lock;
|
||||
/******************
|
||||
* Data structures
|
||||
******************/
|
||||
|
||||
#define PID_MAX 32768
|
||||
struct pid_set {
|
||||
@@ -44,7 +34,10 @@ private:
|
||||
};
|
||||
|
||||
// true if pid is monitored
|
||||
pid_set attaches;
|
||||
static pid_set attaches;
|
||||
|
||||
// zygote pid -> mnt ns
|
||||
static map<int, struct stat> zygote_map;
|
||||
|
||||
/********
|
||||
* Utils
|
||||
@@ -61,51 +54,17 @@ static int parse_ppid(int pid) {
|
||||
int ppid;
|
||||
|
||||
sprintf(path, "/proc/%d/stat", pid);
|
||||
FILE *stat = fopen(path, "re");
|
||||
if (stat == nullptr)
|
||||
|
||||
auto stat = open_file(path, "re");
|
||||
if (!stat)
|
||||
return -1;
|
||||
|
||||
// PID COMM STATE PPID .....
|
||||
fscanf(stat, "%*d %*s %*c %d", &ppid);
|
||||
fclose(stat);
|
||||
fscanf(stat.get(), "%*d %*s %*c %d", &ppid);
|
||||
|
||||
return ppid;
|
||||
}
|
||||
|
||||
static inline long xptrace(int request, pid_t pid, void *addr, void *data) {
|
||||
long ret = ptrace(request, pid, addr, data);
|
||||
if (ret < 0)
|
||||
PLOGE("ptrace %d", pid);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static inline long xptrace(int request, pid_t pid, void *addr = nullptr, intptr_t data = 0) {
|
||||
return xptrace(request, pid, addr, reinterpret_cast<void *>(data));
|
||||
}
|
||||
|
||||
void update_uid_map() {
|
||||
mutex_guard lock(monitor_lock);
|
||||
uid_proc_map.clear();
|
||||
string data_path(APP_DATA_DIR);
|
||||
size_t len = data_path.length();
|
||||
auto dir = open_dir(APP_DATA_DIR);
|
||||
for (dirent *entry; (entry = xreaddir(dir.get()));) {
|
||||
data_path.resize(len);
|
||||
data_path += '/';
|
||||
data_path += entry->d_name;
|
||||
data_path += '/';
|
||||
size_t user_len = data_path.length();
|
||||
struct stat st;
|
||||
for (auto &hide : hide_set) {
|
||||
data_path.resize(user_len);
|
||||
data_path += hide.first;
|
||||
if (stat(data_path.data(), &st))
|
||||
continue;
|
||||
uid_proc_map[st.st_uid].emplace_back(hide.second);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static bool is_zygote_done() {
|
||||
#ifdef __LP64__
|
||||
return zygote_map.size() >= 2;
|
||||
@@ -139,7 +98,7 @@ static void check_zygote() {
|
||||
static void setup_inotify() {
|
||||
inotify_fd = xinotify_init1(IN_CLOEXEC);
|
||||
if (inotify_fd < 0)
|
||||
term_thread();
|
||||
return;
|
||||
|
||||
// Setup inotify asynchronous I/O
|
||||
fcntl(inotify_fd, F_SETFL, O_ASYNC);
|
||||
@@ -167,8 +126,8 @@ static void setup_inotify() {
|
||||
************************/
|
||||
|
||||
static void inotify_event(int) {
|
||||
/* Make sure we can actually read stuffs
|
||||
* or else the whole thread will be blocked.*/
|
||||
// Make sure we can actually read stuffs
|
||||
// or else the whole thread will be blocked.
|
||||
struct pollfd pfd = {
|
||||
.fd = inotify_fd,
|
||||
.events = POLLIN,
|
||||
@@ -187,13 +146,8 @@ static void inotify_event(int) {
|
||||
// Workaround for the lack of pthread_cancel
|
||||
static void term_thread(int) {
|
||||
LOGD("proc_monitor: cleaning up\n");
|
||||
uid_proc_map.clear();
|
||||
zygote_map.clear();
|
||||
hide_set.clear();
|
||||
attaches.reset();
|
||||
// Misc
|
||||
set_hide_state(false);
|
||||
pthread_mutex_destroy(&monitor_lock);
|
||||
close(inotify_fd);
|
||||
inotify_fd = -1;
|
||||
LOGD("proc_monitor: terminate\n");
|
||||
@@ -224,20 +178,22 @@ static bool check_pid(int pid) {
|
||||
|
||||
sprintf(path, "/proc/%d", pid);
|
||||
if (stat(path, &st)) {
|
||||
// Process killed unexpectedly, ignore
|
||||
// Process died unexpectedly, ignore
|
||||
detach_pid(pid);
|
||||
return true;
|
||||
}
|
||||
|
||||
int uid = st.st_uid;
|
||||
|
||||
// UID hasn't changed
|
||||
if (st.st_uid == 0)
|
||||
if (uid == 0)
|
||||
return false;
|
||||
|
||||
sprintf(path, "/proc/%d/cmdline", pid);
|
||||
if (auto f = open_file(path, "re")) {
|
||||
fgets(cmdline, sizeof(cmdline), f.get());
|
||||
} else {
|
||||
// Process killed unexpectedly, ignore
|
||||
// Process died unexpectedly, ignore
|
||||
detach_pid(pid);
|
||||
return true;
|
||||
}
|
||||
@@ -246,37 +202,28 @@ static bool check_pid(int pid) {
|
||||
cmdline == "usap32"sv || cmdline == "usap64"sv)
|
||||
return false;
|
||||
|
||||
int uid = st.st_uid;
|
||||
auto it = uid_proc_map.find(uid);
|
||||
if (it != uid_proc_map.end()) {
|
||||
for (auto &s : it->second) {
|
||||
if (s == cmdline) {
|
||||
// Double check whether ns is separated
|
||||
if (!is_hide_target(uid, cmdline))
|
||||
goto not_target;
|
||||
|
||||
// Ensure ns is separated
|
||||
read_ns(pid, &st);
|
||||
bool mnt_ns = true;
|
||||
for (auto &zit : zygote_map) {
|
||||
if (zit.second.st_ino == st.st_ino &&
|
||||
zit.second.st_dev == st.st_dev) {
|
||||
mnt_ns = false;
|
||||
break;
|
||||
// ns not separated, abort
|
||||
goto not_target;
|
||||
}
|
||||
}
|
||||
// For some reason ns is not separated, abort
|
||||
if (!mnt_ns)
|
||||
break;
|
||||
|
||||
// Finally this is our target!
|
||||
// Detach from ptrace but should still remain stopped.
|
||||
// The hide daemon will resume the process.
|
||||
PTRACE_LOG("target found\n");
|
||||
// Detach but the process should still remain stopped
|
||||
// The hide daemon will resume the process after hiding it
|
||||
LOGI("proc_monitor: [%s] PID=[%d] UID=[%d]\n", cmdline, pid, uid);
|
||||
detach_pid(pid, SIGSTOP);
|
||||
hide_daemon(pid);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
PTRACE_LOG("[%s] not our target\n", cmdline);
|
||||
|
||||
not_target:
|
||||
PTRACE_LOG("[%s] is not our target\n", cmdline);
|
||||
detach_pid(pid);
|
||||
return true;
|
||||
}
|
||||
@@ -323,8 +270,7 @@ static void new_zygote(int pid) {
|
||||
xptrace(PTRACE_CONT, pid);
|
||||
}
|
||||
|
||||
#define WEVENT(s) (((s) & 0xffff0000) >> 16)
|
||||
#define DETACH_AND_CONT { detach = true; continue; }
|
||||
#define DETACH_AND_CONT { detach_pid(pid); continue; }
|
||||
|
||||
void proc_monitor() {
|
||||
// Unblock some signals
|
||||
@@ -354,9 +300,7 @@ void proc_monitor() {
|
||||
setitimer(ITIMER_REAL, &interval, nullptr);
|
||||
}
|
||||
|
||||
int status;
|
||||
|
||||
for (;;) {
|
||||
for (int status;;) {
|
||||
const int pid = waitpid(-1, &status, __WALL | __WNOTHREAD);
|
||||
if (pid < 0) {
|
||||
if (errno == ECHILD) {
|
||||
@@ -370,38 +314,35 @@ void proc_monitor() {
|
||||
}
|
||||
continue;
|
||||
}
|
||||
bool detach = false;
|
||||
run_finally f([&] {
|
||||
if (detach)
|
||||
// Non of our business now
|
||||
detach_pid(pid);
|
||||
});
|
||||
|
||||
if (!WIFSTOPPED(status) /* Ignore if not ptrace-stop */)
|
||||
DETACH_AND_CONT;
|
||||
|
||||
if (WSTOPSIG(status) == SIGTRAP && WEVENT(status)) {
|
||||
int event = WEVENT(status);
|
||||
int signal = WSTOPSIG(status);
|
||||
|
||||
if (signal == SIGTRAP && event) {
|
||||
unsigned long msg;
|
||||
xptrace(PTRACE_GETEVENTMSG, pid, nullptr, &msg);
|
||||
if (zygote_map.count(pid)) {
|
||||
// Zygote event
|
||||
switch (WEVENT(status)) {
|
||||
switch (event) {
|
||||
case PTRACE_EVENT_FORK:
|
||||
case PTRACE_EVENT_VFORK:
|
||||
PTRACE_LOG("zygote forked: [%d]\n", msg);
|
||||
PTRACE_LOG("zygote forked: [%lu]\n", msg);
|
||||
attaches[msg] = true;
|
||||
break;
|
||||
case PTRACE_EVENT_EXIT:
|
||||
PTRACE_LOG("zygote exited with status: [%d]\n", msg);
|
||||
PTRACE_LOG("zygote exited with status: [%lu]\n", msg);
|
||||
[[fallthrough]];
|
||||
default:
|
||||
zygote_map.erase(pid);
|
||||
DETACH_AND_CONT;
|
||||
}
|
||||
} else {
|
||||
switch (WEVENT(status)) {
|
||||
switch (event) {
|
||||
case PTRACE_EVENT_CLONE:
|
||||
PTRACE_LOG("create new threads: [%d]\n", msg);
|
||||
PTRACE_LOG("create new threads: [%lu]\n", msg);
|
||||
if (attaches[pid] && check_pid(pid))
|
||||
continue;
|
||||
break;
|
||||
@@ -414,7 +355,7 @@ void proc_monitor() {
|
||||
}
|
||||
}
|
||||
xptrace(PTRACE_CONT, pid);
|
||||
} else if (WSTOPSIG(status) == SIGSTOP) {
|
||||
} else if (signal == SIGSTOP) {
|
||||
if (!attaches[pid]) {
|
||||
// Double check if this is actually a process
|
||||
attaches[pid] = is_process(pid);
|
||||
@@ -432,8 +373,8 @@ void proc_monitor() {
|
||||
}
|
||||
} else {
|
||||
// Not caused by us, resend signal
|
||||
xptrace(PTRACE_CONT, pid, nullptr, WSTOPSIG(status));
|
||||
PTRACE_LOG("signal [%d]\n", WSTOPSIG(status));
|
||||
xptrace(PTRACE_CONT, pid, nullptr, signal);
|
||||
PTRACE_LOG("signal [%d]\n", signal);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -106,13 +106,14 @@ void sepolicy::magisk_rules() {
|
||||
// Don't allow pesky processes to monitor audit deny logs when poking magisk daemon socket
|
||||
dontaudit(ALL, SEPOL_PROC_DOMAIN, "unix_stream_socket", ALL);
|
||||
|
||||
// Only allow client processes to connect to magisk daemon socket
|
||||
// Only allow client processes and zygote to connect to magisk daemon socket
|
||||
allow(SEPOL_CLIENT_DOMAIN, SEPOL_PROC_DOMAIN, "unix_stream_socket", ALL);
|
||||
allow("zygote", SEPOL_PROC_DOMAIN, "unix_stream_socket", ALL);
|
||||
} else {
|
||||
// Fallback to poking holes in sandbox as Android 4.3 to 7.1 set PR_SET_NO_NEW_PRIVS
|
||||
|
||||
// Allow these processes to access MagiskSU
|
||||
const char *clients[] { "init", "shell", "appdomain" };
|
||||
const char *clients[] { "init", "shell", "appdomain", "zygote" };
|
||||
for (auto type : clients) {
|
||||
if (!exists(type))
|
||||
continue;
|
||||
|
@@ -1,7 +1,6 @@
|
||||
#include <sys/types.h>
|
||||
#include <sys/wait.h>
|
||||
|
||||
#include <daemon.hpp>
|
||||
#include <utils.hpp>
|
||||
#include <selinux.hpp>
|
||||
|
||||
@@ -31,7 +30,7 @@ enum {
|
||||
? info->uid / 100000 : 0)
|
||||
|
||||
#define get_cmd(to) \
|
||||
(to.command[0] ? to.command : to.shell[0] ? to.shell : DEFAULT_SHELL)
|
||||
(to.command.empty() ? (to.shell.empty() ? DEFAULT_SHELL : to.shell.data()) : to.command.data())
|
||||
|
||||
class Extra {
|
||||
const char *key;
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user