Compare commits

...

11 Commits

Author SHA1 Message Date
topjohnwu
895b5f6cbf Release new canary build 2024-12-04 01:28:31 -08:00
SonyaMedved
cb3d4ea514 strings.xml
The strings have been translated into Ukrainian.
2024-12-04 01:26:39 -08:00
topjohnwu
0d89a2a97d Update AGP 2024-12-04 01:25:44 -08:00
nedokaka
3ca5913055 Update Russian Translation 2024-12-03 19:52:53 -08:00
topjohnwu
df6b808f49 Cleanup DesugarClassVisitorFactory 2024-12-03 19:52:39 -08:00
topjohnwu
09c7ac754b Simplify MagiskD Rust/C++ FFI 2024-12-03 15:51:17 -08:00
topjohnwu
805da67c23 Update cxx-rs 2024-12-03 14:16:14 -08:00
topjohnwu
3c6889505b Stop using polymorphism in magiskinit 2024-12-03 02:18:22 -08:00
topjohnwu
c8e9ce7627 Cleanup mount code in magiskinit 2024-12-03 02:18:22 -08:00
topjohnwu
837c679a31 Update avd_test API versions 2024-12-03 02:18:22 -08:00
LoveSy
06616659b8 Only desugar ZipEntry's methods 2024-12-02 19:55:28 -08:00
26 changed files with 339 additions and 362 deletions

View File

@@ -83,10 +83,10 @@ jobs:
strategy:
fail-fast: false
matrix:
version: [23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34]
version: [23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35]
type: [""]
include:
- version: 35
- version: "Baklava"
type: "google_apis"
steps:

1
.gitignore vendored
View File

@@ -14,6 +14,7 @@ native/out
*.iml
.gradle
.idea
.kotlin
/local.properties
/build
/captures

View File

@@ -22,7 +22,7 @@ Click the icon below to download Magisk apk.
[![](https://img.shields.io/badge/Magisk-v27.0-blue)](https://github.com/topjohnwu/Magisk/releases/tag/v27.0)
[![](https://img.shields.io/badge/Magisk%20Beta-v28.0-blue)](https://github.com/topjohnwu/Magisk/releases/tag/v28.0)
[![](https://img.shields.io/badge/Magisk-Canary-red)](https://github.com/topjohnwu/Magisk/releases/tag/canary-28002)
[![](https://img.shields.io/badge/Magisk-Canary-red)](https://github.com/topjohnwu/Magisk/releases/tag/canary-28003)
## Useful Links

View File

@@ -87,6 +87,9 @@
<string name="logs_cleared">Логи успешно очищены</string>
<string name="pid">PID: %1$d</string>
<string name="target_uid">Целевой UID: %1$d</string>
<string name="target_pid">Целевой PID пространства имён: %s</string>
<string name="selinux_context">Контекст SELinux: %s</string>
<string name="supp_group">Дополнительная группа: %s</string>
<!--SafetyNet-->
@@ -164,11 +167,16 @@
<string name="settings_su_reauth_title">Повторная аутентификация</string>
<string name="settings_su_reauth_summary">Повторный запрос прав суперпользователя после обновления приложений</string>
<string name="settings_su_tapjack_title">Защита от перехвата нажатий</string>
<string name="settings_su_auth_title">Аутентификация пользователя</string>
<string name="settings_su_auth_summary">Требовать аутентификацию пользователя при запросах Superuser</string>
<string name="settings_su_auth_insecure">На устройстве не настроен метод аутентификации</string>
<string name="settings_su_tapjack_summary">Окно запроса прав суперпользователя будет неактивно пока активированы наложения экрана</string>
<string name="settings_customization">Персонализация</string>
<string name="setting_add_shortcut_summary">Добавить ярлык на рабочий стол для удобного восприятия приложения после его скрытия</string>
<string name="settings_doh_title">DNS поверх HTTPS</string>
<string name="settings_doh_description">Активировать DoH (используйте при проблемах с подключением к сети)</string>
<string name="settings_random_name_title">Случайное имя образа</string>
<string name="settings_random_name_description">Генерировать случайные имена для патченных образов и tar-файлов для предотвращения обнаружения</string>
<string name="multiuser_mode">Многопользовательский режим</string>
<string name="settings_owner_only">Только администратор</string>

View File

@@ -130,8 +130,8 @@
<string name="settings_update_custom">Власний</string>
<string name="settings_update_custom_msg">Вставте власний URL</string>
<string name="settings_zygisk_summary">Запускати частини Magisk в сервісі zygote</string>
<string name="settings_denylist_title">Enforce DenyList</string>
<string name="settings_denylist_summary">Processes on the denylist will have all Magisk modifications reverted</string>
<string name="settings_denylist_title">Увімкнути DenyList</string>
<string name="settings_denylist_summary">Всі зміни, внесені Magisk, будуть приховані від процесів, позначених у DenyList</string>
<string name="settings_denylist_config_title">Налаштувати DenyList</string>
<string name="settings_denylist_config_summary">Вибрати процеси, які будуть додані до denylist</string>
<string name="settings_hosts_title">Позасистемні хости</string>
@@ -156,7 +156,7 @@
<string name="superuser_notification">Сповіщення суперкористувача</string>
<string name="settings_su_reauth_title">Повторна автентифікація</string>
<string name="settings_su_reauth_summary">Перевидача прав суперкористувача після оновлення застосунку</string>
<string name="settings_su_tapjack_title">Увімкнути захист від Tapjack</string>
<string name="settings_su_tapjack_title">Увімкнути захист від підміни натискань</string>
<string name="settings_su_tapjack_summary">Діалогове вікно суперкористувача не буде отримувати ввід від користувача, коли вікно перекрито іншим застосунком чи вікном</string>
<string name="settings_customization">Оформлення</string>
<string name="setting_add_shortcut_summary">Додати ярлик на домашній екран для зручного сприйняття застосунку після його приховування</string>

View File

@@ -7,21 +7,26 @@ import org.objectweb.asm.MethodVisitor
import org.objectweb.asm.Opcodes
import org.objectweb.asm.Opcodes.ASM9
private const val ZIP_ENTRY_CLASS_NAME = "java.util.zip.ZipEntry"
private const val DESUGAR_CLASS_NAME = "com.topjohnwu.magisk.core.utils.Desugar"
private const val ZIP_ENTRY_GET_TIME_DESC = "()Ljava/nio/file/attribute/FileTime;"
private const val DESUGAR_GET_TIME_DESC = "(Ljava/util/zip/ZipEntry;)Ljava/nio/file/attribute/FileTime;"
private const val DESUGAR_GET_TIME_DESC =
"(Ljava/util/zip/ZipEntry;)Ljava/nio/file/attribute/FileTime;"
private fun ClassData.isTypeOf(name: String) = className == name || superClasses.contains(name)
abstract class DesugarClassVisitorFactory : AsmClassVisitorFactory<InstrumentationParameters.None> {
override fun createClassVisitor(
classContext: ClassContext,
nextClassVisitor: ClassVisitor
): ClassVisitor {
return DesugarClassVisitor(nextClassVisitor)
return DesugarClassVisitor(classContext, nextClassVisitor)
}
override fun isInstrumentable(classData: ClassData) = classData.className != DESUGAR_CLASS_NAME
class DesugarClassVisitor(cv: ClassVisitor) : ClassVisitor(ASM9, cv) {
class DesugarClassVisitor(private val classContext: ClassContext, cv: ClassVisitor) :
ClassVisitor(ASM9, cv) {
override fun visitMethod(
access: Int,
name: String?,
@@ -29,39 +34,45 @@ abstract class DesugarClassVisitorFactory : AsmClassVisitorFactory<Instrumentati
signature: String?,
exceptions: Array<out String>?
): MethodVisitor {
return DesugarMethodVisitor(super.visitMethod(access, name, descriptor, signature, exceptions))
}
}
class DesugarMethodVisitor(mv: MethodVisitor?) : MethodVisitor(ASM9, mv) {
override fun visitMethodInsn(
opcode: Int,
owner: String,
name: String,
descriptor: String,
isInterface: Boolean
) {
if (!process(name, descriptor)) {
super.visitMethodInsn(opcode, owner, name, descriptor, isInterface)
}
return DesugarMethodVisitor(
super.visitMethod(access, name, descriptor, signature, exceptions)
)
}
private fun process(name: String, descriptor: String): Boolean {
if (descriptor != ZIP_ENTRY_GET_TIME_DESC)
return false
when (name) {
"getLastModifiedTime", "getLastAccessTime", "getCreationTime" -> {
mv.visitMethodInsn(
Opcodes.INVOKESTATIC,
DESUGAR_CLASS_NAME.replace('.', '/'),
name,
DESUGAR_GET_TIME_DESC,
false
)
inner class DesugarMethodVisitor(mv: MethodVisitor?) :
MethodVisitor(ASM9, mv) {
override fun visitMethodInsn(
opcode: Int,
owner: String,
name: String,
descriptor: String,
isInterface: Boolean
) {
if (!process(owner, name, descriptor)) {
super.visitMethodInsn(opcode, owner, name, descriptor, isInterface)
}
}
private fun process(owner: String, name: String, descriptor: String): Boolean {
val classData = classContext.loadClassData(owner.replace("/", ".")) ?: return false
if (!classData.isTypeOf(ZIP_ENTRY_CLASS_NAME))
return false
if (descriptor != ZIP_ENTRY_GET_TIME_DESC)
return false
return when (name) {
"getLastModifiedTime", "getLastAccessTime", "getCreationTime" -> {
mv.visitMethodInsn(
Opcodes.INVOKESTATIC,
DESUGAR_CLASS_NAME.replace('.', '/'),
name,
DESUGAR_GET_TIME_DESC,
false
)
true
}
else -> false
}
else -> return false
}
return true
}
}
}

View File

@@ -30,5 +30,5 @@ android.nonFinalResIds=false
# Magisk
magisk.stubVersion=40
magisk.versionCode=28002
magisk.versionCode=28003
magisk.ondkVersion=r27.4

View File

@@ -1,6 +1,6 @@
[versions]
kotlin = "2.0.21"
android = "8.7.2"
android = "8.7.3"
ksp = "2.0.21-1.0.28"
rikka = "1.3.0"
navigation = "2.8.4"

View File

@@ -1,6 +1,6 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-8.11-bin.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-8.11.1-bin.zip
networkTimeout=10000
validateDistributionUrl=true
zipStoreBase=GRADLE_USER_HOME

72
native/src/Cargo.lock generated
View File

@@ -2,6 +2,12 @@
# It is not intended for manual editing.
version = 3
[[package]]
name = "anstyle"
version = "1.0.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "55cc3b69f167a1ef2e161439aa98aed94e6028e5f9a59be9a6ffb47aef1651f9"
[[package]]
name = "argh"
version = "0.1.12"
@@ -111,6 +117,32 @@ version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
[[package]]
name = "clap"
version = "4.5.22"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "69371e34337c4c984bbe322360c2547210bf632eb2814bbe78a6e87a2935bd2b"
dependencies = [
"clap_builder",
]
[[package]]
name = "clap_builder"
version = "4.5.22"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6e24c1b4099818523236a8ca881d2b45db98dadfb4625cf6608c12069fcbbde1"
dependencies = [
"anstyle",
"clap_lex",
"strsim",
]
[[package]]
name = "clap_lex"
version = "0.7.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "afb84c814227b90d6895e01398aee0d8033c00e7466aca416fb6a8e0eb19d8a7"
[[package]]
name = "codespan-reporting"
version = "0.11.1"
@@ -182,16 +214,18 @@ dependencies = [
[[package]]
name = "cxx"
version = "1.0.124"
version = "1.0.133"
dependencies = [
"cc",
"cxxbridge-cmd",
"cxxbridge-flags",
"cxxbridge-macro",
"foldhash",
]
[[package]]
name = "cxx-gen"
version = "0.7.124"
version = "0.7.133"
dependencies = [
"codespan-reporting",
"proc-macro2",
@@ -199,16 +233,28 @@ dependencies = [
"syn",
]
[[package]]
name = "cxxbridge-cmd"
version = "1.0.133"
dependencies = [
"clap",
"codespan-reporting",
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "cxxbridge-flags"
version = "1.0.124"
version = "1.0.133"
[[package]]
name = "cxxbridge-macro"
version = "1.0.124"
version = "1.0.133"
dependencies = [
"proc-macro2",
"quote",
"rustversion",
"syn",
]
@@ -305,6 +351,12 @@ version = "0.4.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b3ea1ec5f8307826a5b71094dd91fc04d4ae75d5709b20ad351c7fb4815c86ec"
[[package]]
name = "foldhash"
version = "0.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f81ec6369c545a7d40e4589b5597581fa1c441fe1cce96dd1de43159910a36a2"
[[package]]
name = "getrandom"
version = "0.2.15"
@@ -709,6 +761,12 @@ dependencies = [
"zeroize",
]
[[package]]
name = "rustversion"
version = "1.0.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0e819f2bc632f285be6d7cd36e25940d45b2391dd6d9b939e79de557f7014248"
[[package]]
name = "sec1"
version = "0.8.0-rc.0"
@@ -803,6 +861,12 @@ dependencies = [
"der",
]
[[package]]
name = "strsim"
version = "0.11.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f"
[[package]]
name = "subtle"
version = "2.6.1"

View File

@@ -49,6 +49,7 @@ pub mod ffi {
}
#[namespace = "rust"]
#[allow(unused_unsafe)]
extern "Rust" {
unsafe fn extract_boot_from_payload(
partition: *const c_char,

View File

@@ -148,8 +148,8 @@ static bool check_safe_mode() {
* Boot Stage Handlers *
***********************/
bool MagiskD::post_fs_data() const {
as_rust().setup_logfile();
bool MagiskD::post_fs_data() const noexcept {
setup_logfile();
LOGI("** post-fs-data mode running\n");
@@ -194,8 +194,8 @@ bool MagiskD::post_fs_data() const {
return safe_mode;
}
void MagiskD::late_start() const {
as_rust().setup_logfile();
void MagiskD::late_start() const noexcept {
setup_logfile();
LOGI("** late_start service mode running\n");
@@ -203,8 +203,8 @@ void MagiskD::late_start() const {
exec_module_scripts("service");
}
void MagiskD::boot_complete() const {
as_rust().setup_logfile();
void MagiskD::boot_complete() const noexcept {
setup_logfile();
LOGI("** boot-complete triggered\n");

View File

@@ -128,20 +128,8 @@ static void poll_ctrl_handler(pollfd *pfd) {
}
}
const MagiskD &MagiskD::get() {
return *reinterpret_cast<const MagiskD*>(&rust::get_magiskd());
}
const rust::MagiskD *MagiskD::operator->() const {
return reinterpret_cast<const rust::MagiskD*>(this);
}
const rust::MagiskD &MagiskD::as_rust() const {
return *operator->();
}
void MagiskD::reboot() const {
if (as_rust().is_recovery())
void MagiskD::reboot() const noexcept {
if (is_recovery())
exec_command_sync("/system/bin/reboot", "recovery");
else
exec_command_sync("/system/bin/reboot");
@@ -171,7 +159,7 @@ static void handle_request_async(int client, int code, const sock_cred &cred) {
write_int(client, 0);
close(client);
if (do_reboot) {
MagiskD::get().reboot();
MagiskD().reboot();
}
break;
}
@@ -196,7 +184,7 @@ static void handle_request_sync(int client, int code) {
write_int(client, MAGISK_VER_CODE);
break;
case +RequestCode::START_DAEMON:
MagiskD::get()->setup_logfile();
MagiskD().setup_logfile();
break;
case +RequestCode::STOP_DAEMON: {
// Unmount all overlays
@@ -298,7 +286,7 @@ static void handle_request(pollfd *pfd) {
exec_task([=, fd = client.release()] { handle_request_async(fd, code, cred); });
} else {
exec_task([=, fd = client.release()] {
MagiskD::get()->boot_stage_handler(fd, code);
MagiskD().boot_stage_handler(fd, code);
});
}
}

View File

@@ -1,7 +1,7 @@
use std::fs::File;
use std::io;
use std::io::BufReader;
use std::sync::{Mutex, OnceLock};
use std::{io, mem};
use base::libc::{O_CLOEXEC, O_RDONLY};
use base::{
@@ -10,7 +10,7 @@ use base::{
};
use crate::consts::MAIN_CONFIG;
use crate::ffi::{get_magisk_tmp, CxxMagiskD, RequestCode};
use crate::ffi::{get_magisk_tmp, RequestCode};
use crate::get_prop;
use crate::logging::magisk_logging;
@@ -64,7 +64,7 @@ impl MagiskD {
match code {
RequestCode::POST_FS_DATA => {
if check_data() && !state.contains(BootState::PostFsDataDone) {
if self.as_cxx().post_fs_data() {
if self.post_fs_data() {
state.set(BootState::SafeMode);
}
state.set(BootState::PostFsDataDone);
@@ -75,7 +75,7 @@ impl MagiskD {
unsafe { libc::close(client) };
if state.contains(BootState::PostFsDataDone) && !state.contains(BootState::SafeMode)
{
self.as_cxx().late_start();
self.late_start();
state.set(BootState::LateStartDone);
}
}
@@ -83,7 +83,7 @@ impl MagiskD {
unsafe { libc::close(client) };
if state.contains(BootState::PostFsDataDone) {
state.set(BootState::BootComplete);
self.as_cxx().boot_complete()
self.boot_complete()
}
}
_ => {
@@ -91,11 +91,6 @@ impl MagiskD {
}
}
}
#[inline(always)]
fn as_cxx(&self) -> &CxxMagiskD {
unsafe { mem::transmute(self) }
}
}
pub fn daemon_entry() {

View File

@@ -108,7 +108,7 @@ db_settings::db_settings() {
data[SU_MULTIUSER_MODE] = MULTIUSER_MODE_OWNER_ONLY;
data[SU_MNT_NS] = NAMESPACE_MODE_REQUESTER;
data[DENYLIST_CONFIG] = false;
data[ZYGISK_CONFIG] = MagiskD::get()->is_emulator();
data[ZYGISK_CONFIG] = MagiskD().is_emulator();
data[BOOTLOOP_COUNT] = 0;
}

View File

@@ -2,29 +2,6 @@
#include <base.hpp>
namespace rust {
struct MagiskD;
}
struct MagiskD {
// Make sure only references can exist
~MagiskD() = delete;
// Binding to Rust
static const MagiskD &get();
// C++ implementation
void reboot() const;
bool post_fs_data() const;
void late_start() const;
void boot_complete() const;
const rust::MagiskD *operator->() const;
private:
const rust::MagiskD &as_rust() const;
};
const char *get_magisk_tmp();
// Rust bindings

View File

@@ -74,12 +74,6 @@ pub mod ffi {
fn resolve_preinit_dir(base_dir: Utf8CStrRef) -> String;
fn switch_mnt_ns(pid: i32) -> i32;
#[cxx_name = "MagiskD"]
type CxxMagiskD;
fn post_fs_data(self: &CxxMagiskD) -> bool;
fn late_start(self: &CxxMagiskD);
fn boot_complete(self: &CxxMagiskD);
}
extern "Rust" {
@@ -99,18 +93,28 @@ pub mod ffi {
unsafe fn persist_get_props(prop_cb: Pin<&mut PropCb>);
unsafe fn persist_delete_prop(name: Utf8CStrRef) -> bool;
unsafe fn persist_set_prop(name: Utf8CStrRef, value: Utf8CStrRef) -> bool;
#[namespace = "rust"]
fn daemon_entry();
}
#[namespace = "rust"]
// FFI for MagiskD
extern "Rust" {
fn daemon_entry();
type MagiskD;
fn get_magiskd() -> &'static MagiskD;
fn setup_logfile(self: &MagiskD);
fn is_emulator(self: &MagiskD) -> bool;
fn is_recovery(self: &MagiskD) -> bool;
fn boot_stage_handler(self: &MagiskD, client: i32, code: i32);
#[cxx_name = "MagiskD"]
fn get_magiskd() -> &'static MagiskD;
}
unsafe extern "C++" {
#[allow(dead_code)]
fn reboot(self: &MagiskD);
fn post_fs_data(self: &MagiskD) -> bool;
fn late_start(self: &MagiskD);
fn boot_complete(self: &MagiskD);
}
}

View File

@@ -11,8 +11,6 @@
using namespace std;
vector<string> mount_list;
template<char... cs> using chars = integer_sequence<char, cs...>;
// If quoted, parsing ends when we find char in [breaks]
@@ -148,6 +146,12 @@ void BootConfig::set(const kv_pairs &kv) {
strscpy(fstab_suffix, value.data(), sizeof(fstab_suffix));
} else if (key == "qemu") {
emulator = true;
} else if (key == "androidboot.partition_map") {
// androidboot.partition_map allows mapping a partition name to a raw block device.
// For example, "androidboot.partition_map=vdb,metadata;vdc,userdata" maps
// "vdb" to "metadata", and "vdc" to "userdata".
// https://android.googlesource.com/platform/system/core/+/refs/heads/android13-release/init/devices.cpp#191
partition_map = parse_partition_map(value);
}
}
}
@@ -165,41 +169,29 @@ void BootConfig::print() {
}
#define read_dt(name, key) \
ssprintf(file_name, sizeof(file_name), "%s/" name, config->dt_dir); \
ssprintf(file_name, sizeof(file_name), "%s/" name, dt_dir); \
if (access(file_name, R_OK) == 0) { \
string data = full_read(file_name); \
if (!data.empty()) { \
data.pop_back(); \
strscpy(config->key, data.data(), sizeof(config->key)); \
strscpy(key, data.data(), sizeof(key)); \
} \
}
void load_kernel_info(BootConfig *config) {
// Get kernel data using procfs and sysfs
xmkdir("/proc", 0755);
xmount("proc", "/proc", "proc", 0, nullptr);
xmkdir("/sys", 0755);
xmount("sysfs", "/sys", "sysfs", 0, nullptr);
void BootConfig::init() {
set(parse_cmdline(full_read("/proc/cmdline")));
set(parse_bootconfig(full_read("/proc/bootconfig")));
mount_list.emplace_back("/proc");
mount_list.emplace_back("/sys");
// Log to kernel
rust::setup_klog();
config->set(parse_cmdline(full_read("/proc/cmdline")));
config->set(parse_bootconfig(full_read("/proc/bootconfig")));
parse_prop_file("/.backup/.magisk", [=](auto key, auto value) -> bool {
parse_prop_file("/.backup/.magisk", [&](auto key, auto value) -> bool {
if (key == "RECOVERYMODE" && value == "true") {
config->skip_initramfs = config->emulator || !check_key_combo();
skip_initramfs = emulator || !check_key_combo();
return false;
}
return true;
});
if (config->dt_dir[0] == '\0')
strscpy(config->dt_dir, DEFAULT_DT_DIR, sizeof(config->dt_dir));
if (dt_dir[0] == '\0')
strscpy(dt_dir, DEFAULT_DT_DIR, sizeof(dt_dir));
char file_name[128];
read_dt("fstab_suffix", fstab_suffix)
@@ -207,26 +199,7 @@ void load_kernel_info(BootConfig *config) {
read_dt("hardware.platform", hardware_plat)
LOGD("Device config:\n");
config->print();
}
// `androidboot.partition_map` allows associating a partition name for a raw block device
// through a comma separated and semicolon deliminated list. For example,
// `androidboot.partition_map=vdb,metadata;vdc,userdata` maps `vdb` to `metadata` and `vdc` to
// `userdata`.
// https://android.googlesource.com/platform/system/core/+/refs/heads/android13-release/init/devices.cpp#191
kv_pairs load_partition_map() {
const string_view kPartitionMapKey = "androidboot.partition_map";
for (const auto &[key, value] : parse_cmdline(full_read("/proc/cmdline"))) {
if (key == kPartitionMapKey)
return parse_partition_map(value);
}
for (const auto &[key, value] : parse_bootconfig(full_read("/proc/bootconfig"))) {
if (key == kPartitionMapKey)
return parse_partition_map(value);
}
return {};
print();
}
bool check_two_stage() {
@@ -244,7 +217,7 @@ bool check_two_stage() {
return init.contains("selinux_setup");
}
void unxz_init(const char *init_xz, const char *init) {
static void unxz_init(const char *init_xz, const char *init) {
LOGD("unxz %s -> %s\n", init_xz, init);
int fd = xopen(init, O_WRONLY | O_CREAT, 0777);
fd_stream ch(fd);

View File

@@ -60,16 +60,67 @@ void restore_ramdisk_init() {
}
}
class RecoveryInit : public BaseInit {
public:
using BaseInit::BaseInit;
void start() override {
LOGD("Ramdisk is recovery, abort\n");
restore_ramdisk_init();
rm_rf("/.backup");
exec_init();
MagiskInit::MagiskInit(char **argv) : argv(argv), config{} {
// Get kernel data using procfs and sysfs
if (access("/proc/cmdline", F_OK) != 0) {
xmkdir("/proc", 0755);
xmount("proc", "/proc", "proc", 0, nullptr);
mount_list.emplace_back("/proc");
}
};
if (access("/sys/block", F_OK) != 0) {
xmkdir("/sys", 0755);
xmount("sysfs", "/sys", "sysfs", 0, nullptr);
mount_list.emplace_back("/sys");
}
// Log to kernel
rust::setup_klog();
// Load kernel configs
config.init();
}
static void recovery() {
LOGI("Ramdisk is recovery, abort\n");
restore_ramdisk_init();
rm_rf("/.backup");
}
void MagiskInit::legacy_system_as_root() {
LOGI("Legacy SAR Init\n");
prepare_data();
bool is_two_stage = mount_system_root();
if (is_two_stage)
redirect_second_stage();
else
patch_ro_root();
}
void MagiskInit::rootfs() {
LOGI("RootFS Init\n");
prepare_data();
LOGD("Restoring /init\n");
rename(backup_init(), "/init");
patch_rw_root();
}
void MagiskInit::start() {
if (argv[1] != nullptr && argv[1] == "selinux_setup"sv)
second_stage();
else if (config.skip_initramfs)
legacy_system_as_root();
else if (config.force_normal_boot)
first_stage();
else if (access("/sbin/recovery", F_OK) == 0 || access("/system/bin/recovery", F_OK) == 0)
recovery();
else if (check_two_stage())
first_stage();
else
rootfs();
// Finally execute the original init
exec_init();
}
int main(int argc, char *argv[]) {
umask(0);
@@ -81,29 +132,6 @@ int main(int argc, char *argv[]) {
if (getpid() != 1)
return 1;
BaseInit *init;
BootConfig config{};
if (argc > 1 && argv[1] == "selinux_setup"sv) {
rust::setup_klog();
init = new SecondStageInit(argv);
} else {
// This will also mount /sys and /proc
load_kernel_info(&config);
if (config.skip_initramfs)
init = new LegacySARInit(argv, &config);
else if (config.force_normal_boot)
init = new FirstStageInit(argv, &config);
else if (access("/sbin/recovery", F_OK) == 0 || access("/system/bin/recovery", F_OK) == 0)
init = new RecoveryInit(argv, &config);
else if (check_two_stage())
init = new FirstStageInit(argv, &config);
else
init = new RootFSInit(argv, &config);
}
// Run the main routine
init->start();
exit(1);
MagiskInit init(argv);
init.start();
}

View File

@@ -15,7 +15,10 @@ struct BootConfig {
char fstab_suffix[32];
char hardware[32];
char hardware_plat[32];
kv_pairs partition_map;
void init();
private:
void set(const kv_pairs &);
void print();
};
@@ -24,120 +27,45 @@ struct BootConfig {
#define INIT_PATH "/system/bin/init"
#define REDIR_PATH "/data/magiskinit"
extern std::vector<std::string> mount_list;
int magisk_proxy_main(int argc, char *argv[]);
bool unxz(out_stream &strm, rust::Slice<const uint8_t> bytes);
void load_kernel_info(BootConfig *config);
kv_pairs load_partition_map();
bool check_two_stage();
const char *backup_init();
void restore_ramdisk_init();
/***************
* Base classes
***************/
class BaseInit {
protected:
BootConfig *config = nullptr;
char **argv = nullptr;
[[noreturn]] void exec_init();
void prepare_data();
public:
BaseInit(char *argv[], BootConfig *config = nullptr) : config(config), argv(argv) {}
virtual ~BaseInit() = default;
virtual void start() = 0;
};
class MagiskInit : public BaseInit {
class MagiskInit {
private:
std::string preinit_dev;
std::vector<std::string> mount_list;
char **argv;
BootConfig config;
void parse_config_file();
void patch_sepolicy(const char *in, const char *out);
bool hijack_sepolicy();
// Setup mounts and environment
void setup_tmp(const char *path);
protected:
void collect_devices();
void mount_preinit_dir();
void prepare_data();
dev_t find_block(const char *partname);
bool mount_system_root();
// Setup and patch root directory
void parse_config_file();
void patch_rw_root();
void patch_ro_root();
// Two stage init
void redirect_second_stage();
void first_stage();
void second_stage();
// SELinux
void patch_sepolicy(const char *in, const char *out);
bool hijack_sepolicy();
[[noreturn]] void exec_init();
void legacy_system_as_root();
void rootfs();
public:
using BaseInit::BaseInit;
};
/***************
* 2 Stage Init
***************/
class FirstStageInit : public BaseInit {
private:
void prepare();
public:
FirstStageInit(char *argv[], BootConfig *config) : BaseInit(argv, config) {
LOGD("%s\n", __FUNCTION__);
};
void start() override {
prepare();
exec_init();
}
};
class SecondStageInit : public MagiskInit {
private:
bool prepare();
public:
SecondStageInit(char *argv[]) : MagiskInit(argv) {
LOGD("%s\n", __FUNCTION__);
};
void start() override {
bool is_rootfs = prepare();
if (is_rootfs)
patch_rw_root();
else
patch_ro_root();
exec_init();
}
};
/*************
* Legacy SAR
*************/
class LegacySARInit : public MagiskInit {
private:
bool mount_system_root();
void first_stage_prep();
public:
LegacySARInit(char *argv[], BootConfig *config) : MagiskInit(argv, config) {
LOGD("%s\n", __FUNCTION__);
};
void start() override {
prepare_data();
bool is_two_stage = mount_system_root();
if (is_two_stage)
first_stage_prep();
else
patch_ro_root();
exec_init();
}
};
/************
* Initramfs
************/
class RootFSInit : public MagiskInit {
private:
void prepare();
public:
RootFSInit(char *argv[], BootConfig *config) : MagiskInit(argv, config) {
LOGD("%s\n", __FUNCTION__);
}
void start() override {
prepare();
patch_rw_root();
exec_init();
}
explicit MagiskInit(char *argv[]);
void start();
};

View File

@@ -45,7 +45,7 @@ static void parse_device(devinfo *dev, const char *uevent) {
});
}
static void collect_devices(const auto &partition_map) {
void MagiskInit::collect_devices() {
char path[PATH_MAX];
devinfo dev{};
if (auto dir = xopen_dir("/sys/dev/block"); dir) {
@@ -59,9 +59,9 @@ static void collect_devices(const auto &partition_map) {
auto name = rtrim(full_read(path));
strscpy(dev.dmname, name.data(), sizeof(dev.dmname));
}
if (auto it = std::ranges::find_if(partition_map, [&](const auto &i) {
if (auto it = std::ranges::find_if(config.partition_map, [&](const auto &i) {
return i.first == dev.devname;
}); dev.partname[0] == '\0' && it != partition_map.end()) {
}); dev.partname[0] == '\0' && it != config.partition_map.end()) {
// use androidboot.partition_map as partname fallback.
strscpy(dev.partname, it->second.data(), sizeof(dev.partname));
}
@@ -72,52 +72,45 @@ static void collect_devices(const auto &partition_map) {
}
}
static struct {
char partname[32];
char block_dev[64];
} blk_info;
static dev_t setup_block() {
static const auto partition_map = load_partition_map();
dev_t MagiskInit::find_block(const char *partname) {
if (dev_list.empty())
collect_devices(partition_map);
collect_devices();
for (int tries = 0; tries < 3; ++tries) {
for (auto &dev : dev_list) {
if (strcasecmp(dev.partname, blk_info.partname) == 0)
LOGD("Setup %s: [%s] (%d, %d)\n", dev.partname, dev.devname, dev.major, dev.minor);
else if (strcasecmp(dev.dmname, blk_info.partname) == 0)
LOGD("Setup %s: [%s] (%d, %d)\n", dev.dmname, dev.devname, dev.major, dev.minor);
else if (strcasecmp(dev.devname, blk_info.partname) == 0)
LOGD("Setup %s: [%s] (%d, %d)\n", dev.devname, dev.devname, dev.major, dev.minor);
else if (std::string_view(dev.devpath).ends_with("/"s + blk_info.partname))
LOGD("Setup %s: [%s] (%d, %d)\n", dev.devpath, dev.devname, dev.major, dev.minor);
const char *name;
if (strcasecmp(dev.partname, partname) == 0)
name = dev.partname;
else if (strcasecmp(dev.dmname, partname) == 0)
name = dev.dmname;
else if (strcasecmp(dev.devname, partname) == 0)
name = dev.devname;
else if (std::string_view(dev.devpath).ends_with("/"s + partname))
name = dev.devpath;
else
continue;
dev_t rdev = makedev(dev.major, dev.minor);
xmknod(blk_info.block_dev, S_IFBLK | 0600, rdev);
return rdev;
LOGD("Found %s: [%s] (%d, %d)\n", name, dev.devname, dev.major, dev.minor);
return makedev(dev.major, dev.minor);
}
// Wait 10ms and try again
usleep(10000);
dev_list.clear();
collect_devices(partition_map);
collect_devices();
}
// The requested partname does not exist
return 0;
}
static void mount_preinit_dir(string preinit_dev) {
void MagiskInit::mount_preinit_dir() {
if (preinit_dev.empty()) return;
strcpy(blk_info.partname, preinit_dev.data());
strcpy(blk_info.block_dev, PREINITDEV);
auto dev = setup_block();
auto dev = find_block(preinit_dev.data());
if (dev == 0) {
LOGE("Cannot find preinit %s, abort!\n", preinit_dev.data());
return;
}
xmknod(PREINITDEV, S_IFBLK | 0600, dev);
xmkdir(MIRRDIR, 0);
bool mounted = false;
// First, find if it is already mounted
@@ -150,40 +143,40 @@ static void mount_preinit_dir(string preinit_dev) {
}
}
bool LegacySARInit::mount_system_root() {
bool MagiskInit::mount_system_root() {
LOGD("Mounting system_root\n");
// there's no /dev in stub cpio
xmkdir("/dev", 0777);
strcpy(blk_info.block_dev, "/dev/root");
dev_t dev;
do {
// Try legacy SAR dm-verity
strcpy(blk_info.partname, "vroot");
auto dev = setup_block();
dev = find_block("vroot");
if (dev > 0)
goto mount_root;
// Try NVIDIA naming scheme
strcpy(blk_info.partname, "APP");
dev = setup_block();
dev = find_block("APP");
if (dev > 0)
goto mount_root;
sprintf(blk_info.partname, "system%s", config->slot);
dev = setup_block();
// Try normal partname
char sys_part[32];
sprintf(sys_part, "system%s", config.slot);
dev = find_block(sys_part);
if (dev > 0)
goto mount_root;
// Poll forever if rootwait was given in cmdline
} while (config->rootwait);
} while (config.rootwait);
// We don't really know what to do at this point...
LOGE("Cannot find root partition, abort\n");
exit(1);
mount_root:
xmknod("/dev/root", S_IFBLK | 0600, dev);
xmkdir("/system_root", 0755);
if (xmount("/dev/root", "/system_root", "ext4", MS_RDONLY, nullptr)) {
@@ -205,20 +198,19 @@ mount_root:
// For API 28 AVD, it uses legacy SAR setup that requires
// special hacks in magiskinit to work properly.
if (!is_two_stage && config->emulator) {
if (!is_two_stage && config.emulator) {
avd_hack = true;
// These values are hardcoded for API 28 AVD
auto vendor_dev = find_block("vendor");
xmkdir("/dev/block", 0755);
strcpy(blk_info.block_dev, "/dev/block/vde1");
strcpy(blk_info.partname, "vendor");
setup_block();
xmount(blk_info.block_dev, "/vendor", "ext4", MS_RDONLY, nullptr);
xmknod("/dev/block/vde1", S_IFBLK | 0600, vendor_dev);
xmount("/dev/block/vde1", "/vendor", "ext4", MS_RDONLY, nullptr);
}
return is_two_stage;
}
void BaseInit::exec_init() {
void MagiskInit::exec_init() {
// Unmount in reverse order
for (auto &p : reversed(mount_list)) {
if (xumount2(p.data(), MNT_DETACH) == 0)
@@ -228,12 +220,12 @@ void BaseInit::exec_init() {
exit(1);
}
void BaseInit::prepare_data() {
void MagiskInit::prepare_data() {
LOGD("Setup data tmp\n");
xmkdir("/data", 0755);
xmount("magisk", "/data", "tmpfs", 0, "mode=755");
cp_afc("/init", "/data/magiskinit");
cp_afc("/init", REDIR_PATH);
cp_afc("/.backup", "/data/.backup");
cp_afc("/overlay.d", "/data/overlay.d");
}
@@ -246,7 +238,7 @@ void MagiskInit::setup_tmp(const char *path) {
xmkdir(DEVICEDIR, 0711);
xmkdir(WORKERDIR, 0);
mount_preinit_dir(preinit_dev);
mount_preinit_dir();
cp_afc(".backup/.magisk", MAIN_CONFIG);
rm_rf(".backup");

View File

@@ -347,12 +347,6 @@ void MagiskInit::patch_ro_root() {
chdir("/");
}
void RootFSInit::prepare() {
prepare_data();
LOGD("Restoring /init\n");
rename(backup_init(), "/init");
}
#define PRE_TMPSRC "/magisk"
#define PRE_TMPDIR PRE_TMPSRC "/tmp"

View File

@@ -64,7 +64,7 @@ bool MagiskInit::hijack_sepolicy() {
// This only happens on Android 8.0 - 9.0
char buf[4096];
ssprintf(buf, sizeof(buf), "%s/fstab/compatible", config->dt_dir);
ssprintf(buf, sizeof(buf), "%s/fstab/compatible", config.dt_dir);
dt_compat = full_read(buf);
if (dt_compat.empty()) {
// Device does not do early mount and uses monolithic policy
@@ -106,7 +106,7 @@ bool MagiskInit::hijack_sepolicy() {
int fd = xopen(MOCK_COMPAT, O_WRONLY);
char buf[4096];
ssprintf(buf, sizeof(buf), "%s/fstab/compatible", config->dt_dir);
ssprintf(buf, sizeof(buf), "%s/fstab/compatible", config.dt_dir);
xumount2(buf, MNT_DETACH);
hijack();

View File

@@ -8,12 +8,13 @@
using namespace std;
void FirstStageInit::prepare() {
void MagiskInit::first_stage() {
LOGI("First Stage Init\n");
prepare_data();
if (struct stat st{}; fstatat(-1, "/sdcard", &st, AT_SYMLINK_NOFOLLOW) != 0 &&
fstatat(-1, "/first_stage_ramdisk/sdcard", &st, AT_SYMLINK_NOFOLLOW) != 0) {
if (config->force_normal_boot) {
if (config.force_normal_boot) {
xmkdirs("/first_stage_ramdisk/storage/self", 0755);
xsymlink("/system/system/bin/init", "/first_stage_ramdisk/storage/self/primary");
LOGD("Symlink /first_stage_ramdisk/storage/self/primary -> /system/system/bin/init\n");
@@ -29,8 +30,8 @@ void FirstStageInit::prepare() {
LOGD("Bind mount /sdcard -> /sdcard\n");
} else {
// rootfs before 3.12
xmount("/data/magiskinit", "/sdcard", nullptr, MS_BIND, nullptr);
LOGD("Bind mount /sdcard -> /data/magiskinit\n");
xmount(REDIR_PATH, "/sdcard", nullptr, MS_BIND, nullptr);
LOGD("Bind mount " REDIR_PATH " -> /sdcard\n");
}
restore_ramdisk_init();
} else {
@@ -44,7 +45,7 @@ void FirstStageInit::prepare() {
}
}
void LegacySARInit::first_stage_prep() {
void MagiskInit::redirect_second_stage() {
// Patch init binary
int src = xopen("/init", O_RDONLY);
int dest = xopen("/data/init", O_CREAT | O_WRONLY, 0);
@@ -61,7 +62,8 @@ void LegacySARInit::first_stage_prep() {
xmount("/data/init", "/init", nullptr, MS_BIND, nullptr);
}
bool SecondStageInit::prepare() {
void MagiskInit::second_stage() {
LOGI("Second Stage Init\n");
umount2("/init", MNT_DETACH);
umount2(INIT_PATH, MNT_DETACH); // just in case
unlink("/data/init");
@@ -76,7 +78,8 @@ bool SecondStageInit::prepare() {
// We are still on rootfs, so make sure we will execute the init of the 2nd stage
unlink("/init");
xsymlink(INIT_PATH, "/init");
return true;
patch_rw_root();
} else {
patch_ro_root();
}
return false;
}

View File

@@ -8,7 +8,7 @@ lsposed_url='https://github.com/LSPosed/LSPosed/releases/download/v1.9.2/LSPosed
emu_pid=
atd_min_api=30
atd_max_api=34
atd_max_api=35
lsposed_min_api=27
lsposed_max_api=34
huge_ram_min_api=26
@@ -25,6 +25,7 @@ cleanup() {
}
wait_for_bootanim() {
set -e
adb wait-for-device
while true; do
local result="$(adb exec-out getprop init.svc.bootanim)"
@@ -38,6 +39,7 @@ wait_for_bootanim() {
}
wait_for_boot() {
set -e
adb wait-for-device
while true; do
local result="$(adb exec-out getprop sys.boot_completed)"
@@ -79,8 +81,15 @@ test_emu() {
test_setup $variant
# Install LSPosed
local lsposed
if [ $api -ge $lsposed_min_api -a $api -le $lsposed_max_api ]; then
lsposed=true
else
lsposed=false
fi
# Install LSPosed
if $lsposed; then
adb push out/lsposed.zip /data/local/tmp/lsposed.zip
echo 'PATH=$PATH:/debug_ramdisk magisk --install-module /data/local/tmp/lsposed.zip' | adb shell /system/xbin/su
fi
@@ -91,7 +100,7 @@ test_emu() {
test_app
# Try to launch LSPosed
if [ $api -ge $lsposed_min_api -a $api -le $atd_max_api ]; then
if $lsposed; then
adb shell rm -f /data/local/tmp/window_dump.xml
adb shell am start -c org.lsposed.manager.LAUNCH_MANAGER com.android.shell/.BugreportWarningActivity
while adb shell '[ ! -f /data/local/tmp/window_dump.xml ]'; do
@@ -113,6 +122,7 @@ run_test() {
TiramisuPrivacySandbox) api=33 ;;
UpsideDownCakePrivacySandbox) api=34 ;;
VanillaIceCream) api=35 ;;
Baklava) api=36 ;;
*)
print_error "! Unknown system image version '$ver'"
exit 1
@@ -211,11 +221,11 @@ curl -L $lsposed_url -o out/lsposed.zip
if [ -n "$1" ]; then
run_test $1 $2
else
for api in $(seq 23 34); do
for api in $(seq 23 35); do
run_test $api
done
# Android 15 Beta
run_test 35 google_apis
# Android 16 Beta
run_test Baklava google_apis
# Run 16k page tests
run_test 35 google_apis_ps16k
fi