Skip to main content

2 posts tagged with "风控"

View All Tags

· 10 min read
老陈

前言

当手机安装magisk后, 全局的挂载空间会受到变更, magisk给我们挂载上了一个su二进制, 这就是我们能够访问到su命令的原因 无论是Magisk hide还是Denylist, 我们都可以将它们的工作分成两个部分, 第一个部分是如何监控安卓进程的启动, 第二部分是在安卓进程启动(fork)之后, 尽快移除已经“污染”的挂载空间

ROOT隐藏关乎到magisk的核心原理mount 我们可以从linux内核获取到mount的相关信息

全局挂载空间

/proc/mounts /proc/mountinfo /proc/mountstats

进程的挂载空间

/proc/pid/mounts

如果我们成功刷入magisk, 以下命令将会有大量的挂载信息输出

cat /proc/mounts |grep magisk

在linux系统中, mount有好多种 我们注意关注的是“mount --bind” mount --bind命令来将两个目录连接起来, mount --bind命令是将前一个目录挂载到后一个目录上, 所有对后一个目录的访问其实都是对前一个目录的访问 就是说, 我们可以给文件目录戴上一个“面具”, 应用程序访问文件, 首先访问到的是“面具层”, 而不是真正的文件。

程序由数据和代码组成, 但是, 我们也可以说, 数据和代码都是基于“文件”, 因此对文件的改动, 就能改变程序的运行~ 所以我们可以面具实现许许多多的黑科技

在root下的程序可以切换到任意进程的命名空间, 进行mount(umount)操作

如果我们使用momo检测, 我们可以发现magisk这套检测并非运行得很完美

  1. magisk hide 的ptrace容易被检测到
  2. magisk hide 无法对isolated_zygote进程处理, 因为isolated_zygote与zygote进程共享挂载空间, 如果对isolated_zygote进程进行处理, 那么后面所有打开的app无法访问到su, 即无法获取到root权限。因此, 出现一个名为riru_unshare的插件可以使用unshare函数指定独立进程不与zygote共享命名空间。
  3. 还有一些可以绕过早期magisk hide(18)的方法, 就是自定义安卓的进程名后面加两个.., 如微信的“:hotpot..”进程会被magisk_hide忽略为无效的隐藏进程而被magisk hide忽视。

最常见检测安卓设备是否已ROOT的方法

一种最常见检测设备是否已root的方法
public static boolean isDeviceRooted() {
String[] paths = {
"/system/app/Superuser.apk",
"/sbin/su",
"/system/bin/su",
"/system/xbin/su",
"/data/local/xbin/su",
"/data/local/bin/su",
"/system/sd/xbin/su",
"/system/bin/failsafe/su",
"/data/local/su",
"/su/bin/su"
};

for (String path : paths) {
if (new File(path).exists()) {
return true;
}
}

return false;
}

上述方法在magisk时代下, 几乎毫无作用的, 因为我们可以使用面具随机地让某个指定程序看到哪些文件, 看不到哪些文件。

magisk hide/denylist 的源码分析

在magisk 23版本之前, 使用magisk hide进行隐藏, magisk hide使用ptrace监控进程启动, 如果是zygote进程且非isolated_zygote进程, 则暂停进程, 进行umount操作, 再继续运行进程

Part1 监控安卓进程的启动
Magisk/native/jni/magiskhide/proc_monitor.cpp
void proc_monitor() {
monitor_thread = pthread_self();

// *******
setup_inotify();

// First try find existing zygotes
check_zygote();
if (!is_zygote_done()) {
// Periodic scan every 250ms
timeval val { .tv_sec = 0, .tv_usec = 250000 };
itimerval interval { .it_interval = val, .it_value = val };
setitimer(ITIMER_REAL, &interval, nullptr);
}

for (int status;;) {
pthread_sigmask(SIG_UNBLOCK, &unblock_set, nullptr);

const int pid = waitpid(-1, &status, __WALL | __WNOTHREAD);
if (pid < 0) {
if (errno == ECHILD) {
// Nothing to wait yet, sleep and wait till signal interruption
LOGD("proc_monitor: nothing to monitor, wait for signal\n");
struct timespec ts = {
.tv_sec = INT_MAX,
.tv_nsec = 0
};
nanosleep(&ts, nullptr);
}
continue;
}

pthread_sigmask(SIG_SETMASK, &orig_mask, nullptr);

if (!WIFSTOPPED(status) /* Ignore if not ptrace-stop */)
DETACH_AND_CONT;

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 (event) {
case PTRACE_EVENT_FORK:
case PTRACE_EVENT_VFORK:
PTRACE_LOG("zygote forked: [%lu]\n", msg);
attaches[msg] = true;
break;
case PTRACE_EVENT_EXIT:
PTRACE_LOG("zygote exited with status: [%lu]\n", msg);
[[fallthrough]];
default:
zygote_map.erase(pid);
DETACH_AND_CONT;
}
} else {
switch (event) {
case PTRACE_EVENT_CLONE:
PTRACE_LOG("create new threads: [%lu]\n", msg);
if (attaches[pid] && check_pid(pid))
continue;
break;
case PTRACE_EVENT_EXEC:
case PTRACE_EVENT_EXIT:
PTRACE_LOG("exit or execve\n");
[[fallthrough]];
default:
DETACH_AND_CONT;
}
}
xptrace(PTRACE_CONT, pid);
} else if (signal == SIGSTOP) {
if (!attaches[pid]) {
// Double check if this is actually a process
attaches[pid] = is_process(pid);
}
if (attaches[pid]) {
// This is a process, continue monitoring
PTRACE_LOG("SIGSTOP from child\n");
xptrace(PTRACE_SETOPTIONS, pid, nullptr,
PTRACE_O_TRACECLONE | PTRACE_O_TRACEEXEC | PTRACE_O_TRACEEXIT);
xptrace(PTRACE_CONT, pid);
} else {
// This is a thread, do NOT monitor
PTRACE_LOG("SIGSTOP from thread\n");
DETACH_AND_CONT;
}
} else {
// Not caused by us, resend signal
xptrace(PTRACE_CONT, pid, nullptr, signal);
PTRACE_LOG("signal [%d]\n", signal);
}
}
Part2 移除已经污染对挂载空间
Magisk/native/jni/magiskhide/proc_monitor.cpp
static bool check_pid(int pid) {
char path[128];
char cmdline[1024];
struct stat st;

sprintf(path, "/proc/%d", pid);
if (stat(path, &st)) {
// Process died unexpectedly, ignore
detach_pid(pid);
return true;
}

int uid = st.st_uid;

// UID hasn't changed
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 died unexpectedly, ignore
detach_pid(pid);
return true;
}

if (cmdline == "zygote"sv || cmdline == "zygote32"sv || cmdline == "zygote64"sv ||
cmdline == "usap32"sv || cmdline == "usap64"sv)
return false;

if (!is_hide_target(uid, cmdline, 95))
goto not_target;

// Ensure ns is separated
read_ns(pid, &st);
for (auto &zit : zygote_map) {
if (zit.second.st_ino == st.st_ino &&
zit.second.st_dev == st.st_dev) {
// ns not separated, abort
LOGW("proc_monitor: skip [%s] PID=[%d] UID=[%d]\n", cmdline, pid, uid);
goto not_target;
}
}

// 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); // 对指定pid进程进行隐藏
return true;

not_target:
PTRACE_LOG("[%s] is not our target\n", cmdline);
detach_pid(pid);
return true;
}

在magisk 25版本之后, 使用denylist, 如果你要使用denylist, 那需要先需要开启zygote, 因为denylist通过hook zygote的fork函数获知进程的启动。 denylist与magisk hide也是一样, 都是对污染的命名空间进行卸载, 但有一点不同, denylist是在自身的进程执行卸载代码, 而magisk_hide在magiskd进程进行操作, 需要先使用switch_to_ns切换到目标进程的命名空间。

/Users/caz/AndroidProject/Magisk25.2/Magisk/native/src/zygisk/hook.cpp
// Unmount stuffs in the process's private mount namespace
DCL_HOOK_FUNC(int, unshare, int flags) {
int res = old_unshare(flags);
if (g_ctx && (flags & CLONE_NEWNS) != 0 && res == 0 &&
// For some unknown reason, unmounting app_process in SysUI can break.
// This is reproducible on the official AVD running API 26 and 27.
// Simply avoid doing any unmounts for SysUI to avoid potential issues.
g_ctx->process && g_ctx->process != "com.android.systemui"sv) {
if (g_ctx->flags[DO_REVERT_UNMOUNT]) {
revert_unmount();
} else {
umount2("/system/bin/app_process64", MNT_DETACH);
umount2("/system/bin/app_process32", MNT_DETACH);
}
// Restore errno back to 0
errno = 0;
}
return res;
}

Yyds.Msu 的设计

Yyds.Msu与magisk hide反其道而行之。大道至简, 尽可能让方案更加简单才能更稳定。

  1. Yyds.Msu 是先卸载, 默认应用是没有污染命名空间的, 也就是说, 直接对zygote以及zygote64进程进行命名空间卸载。
  2. Yyds.Msu 在不使用zygote hook与ptrace的情况下, 是无法监听其它app进程的启动的, 但可以想办法监听到yyds.msu管理apk的启动, 那就是inotify, 我们只需要让yyds.msu管理apk在Application时候创建一个文件, 即可让我们魔改过的magiskd知晓到并进行挂载。
  3. 缺点是我们无法让其它app打开即进行挂载获得到root权限, 在不进行任何hook的情况下除非我们死循环地地扫描/proc目录。
  4. 在Yyds.Msu中我们手动使用超级运行对指定进程直接挂载root权限以及相关magisk bin. 对于多进程的app, 我们不能只给其中一条进程进行挂载, 为了兼容性, 我们直接对zygote进程挂载root权限并在app启动成功后恢复zygote的正常挂载, 这个就是超级运行的兼容模式。

· 6 min read
老陈

Android系统中有众多的系统服务, 其中有三大核心服务:ActivityManagerService、WindowManagerService、PackageManagerService。

系统服务一览表:

1.ActivityManagerService

Android framework框架核心服务, 管理整个框架中任务、进程管理, Intent解析等的核心实现, 管理四大组建的生命周期。

2.WindowManagerService

Android framework框架核心服务, 窗口管理服务。

3.PackageManagerService

Android framework框架核心服务, 用于APK的解析、权限验证、安装等。

4.AccountManagerService

Android账户服务, 提供了对账户、密码、授权的集中管理。

5.AccessibilityManagerService

辅助管理程序截获所有的用户输入, 并根据这些输入给用户一些额外的反馈, 起到辅助的效果, View的点击、焦点等事件分发管理服。

6.AlarmManagerService

提供闹铃和定时器等功能。

7.AppWidgetService

Android中提供Widget的管理和相关服务

8.AssetAtlasService

负责将预加载的bitmap组装成纹理贴图, 生成的纹理贴图可以被用来跨进程使用, 以减少内存。

9.AudioService

AudioFlinger的上层管理封装, 主要是音量、音效、声道及铃声等的管理。

10.BackupManagerService

备份服务。

11.BatteryService

负责监控电池的充电状态、电池电量、电压、温度等信息, 当电池信息发生变化时, 发生广播通知其他关系电池信息的进程和服务。

12.BluetoothManagerService

负责蓝牙后台管理和服务。

13.ClipboardService

剪贴板服务。

14.CommonTimeManagementService

管理本地常见的时间服务的配置, 在网络配置变化时重新配置本地服务。

15.ConnectivityService

网络连接状态服务。

16.ContentService

内容服务, 主要是数据库等提供解决方法的服务。

17.ConsumerIrService

远程控制, 通过红外等控制周围的设备(例如电视等)

18.CountryDetectorService

检测用户国家

19.DevicePolicyManagerService

提供一些系统级别的设置及属性

20.DiskStatsService

磁盘统计服务, 供dumpsys使用

21.DisplayManagerService

用于管理全局显示生命周期, 决定在已连接的物理设备如何配置逻辑显示, 并且通知系统和应用状态的改变。

22.DreamManagerService

屏幕保护。

23.DropBoxManagerService

用于系统运行时日志的存储于管理。

24.IdleMaintenanceService

用于观察设备状态, 在设备空闲时执行维护任务。将一些比较耗时的代价比较高的任务放到设备空闲时执行, 这样保证用户的体验。

25.InputManagerService

以前在WindowManagerService中, 现在独立了出来, 用户处理事件分发。

26.InputMethodManagerService

输入法服务, 打开和关闭输入法。

27.LightsService

光感应传感器服务。

28.LocationManagerService

位置服务, GPS、定位等。

29.LockSettingsService

和锁屏界面中的输入密码, 手势等安全功能有关。可以保存每个user的相关锁屏信息。

30.WallpaperManagerService

壁纸管理服务。

31.MountService

磁盘加载服务程序, 一般要和一个linux daemon程序如vold/mountd等合作起作用, 主要负责监听并广播device的mount/unmount/badremoval等等事件。

32.NetworkManagementService

网络管理服务。ANDROID 系统网络连接和管理服务由四个系统服务ConnectivityService、NetworkPolicyManagerService、NetworkManagementService、NetworkStatsService共同配合完成网络连接和管理功能。ConnectivityService、NetworkPolicyManagerService、NetworkStatsService三个服务都通过INetworkManagementService接口跨进程访问NetworkManagementService服务, 实现与网络接口的交互及信息读取。

33.NetworkPolicyManagerService

维护网络使用策略。

34.NetworkStatsService

网络统计相关。

35.NetworkTimeUpdateService

监视网络时间, 当网络时间变化时更新本地时间。

36.NotificationManagerService

通知服务。

37.NsdService

网络服务搜索

38.PrintManagerService

打印服务。

39.PowerManagerService

电源管理服务。

40.RecognitionManagerService

身份识别相关。

41.SamplingProfilerService

用于耗时统计等。

42.SearchManagerService

搜索服务。

43.SchedulingPolicyService

调度策略。

44.SerialService

对串口的设备进行操作

45.StatusBarManagerService

状态栏。

46.TelephonyRegistry

提供电话注册、管理服务, 可以获取电话的链接状态、信号强度等等。

47.TextServicesManagerService

文本服务, 例如文本检查等。

48.TwilightService

指出用户当前所在位置是否为晚上, 被UiModeManager等用来调整夜间模式。

49.UiModeManagerService

管理当前Android设备的夜间模式和行车模式.。

50.UsbService

USB Host和device管理服务。

51.VibratorService

振动器服务。

52.WifiP2pService

Wifi Direct服务。

53.WifiService

Wifi服务。

54.WiredAccessoryManager

监视手机和底座上的耳机