Fate/Grand Order

I’ve been fighting with all sorts of weird device integrity detection methods over the years, but nothing is near the level of FGO. For some reason, the developers at Aniplex are extremely obsessed with detecting Magisk, as if it is an ultimate mission they are destined to accomplish. Other than the standard and common sensitive binaries, mount point, and environment variables checks, they went out the way to ban USB debugging, blacklist the package name of Magisk Manager ( com.topjohnwu.magisk ), and also parse through all system services and blacklisted Magisk related services. All of them are mitigated in Magisk with various different techniques that I will spare you from the lengthy details.

OnePlus 6 became my main development phone since I switched to Pixel 3 XL (I don’t usually use my daily driver for development), so all initial tests are done on that device. FGO has become a “benchmark” for the effectiveness of MagiskHide for quite a while, and one day I found out that the app no longer launches with the standard MagiskHide + Magisk Manager repackaging treatment. For the record, all tests are done with Fate/Grand Order US version ( com.aniplex.fategrandorder.en ), version 1.23.0.

Experiments

To do any analysis, USB debugging (ADB) is pretty much required. The Aniplex devs are smart and sneaky: the app simply refuses to run with ADB enabled. I’ve got some tricks up my sleeves so it is possible for me to enable ADB without being detected. However, I decide not to disclose the trick publicly since I really don’t want to make my life more difficult in the future. Just keep in mind that all following steps are done in a USB debuggable environment.

The first step is to experiment using stripped down Magisk versions to isolate the issue. The process is pretty straightforward: start from nearly pure stock, and gradually add more features until we hit a wall. Let’s try nano-Magisk: root daemon is launched, but no binaries are exposed to any unprivileged process. FGO works fine, great! Now let’s try micro-Magisk: /sbin tmpfs overlay is enabled. This does not include MagiskHide daemon, so the hiding procedures are done manually, which when applied the effect is both globally and permanently (meaning no process can ever gain root after applying the first hiding). FGO works fine, great! Next let’s try mini-Magisk: logcat monitor with MagiskHide is enabled, and no manual hiding is done. Finally FGO refuses to launch!

Analysis

MagiskHide exploits the fact that Android apps’ processes are mount_namespace isolated. Magisk modifications are only reverted and hidden in specific target processes, which is the reason why non-target processes can still use root graciously. Through experiments, the issue can theoretically be narrowed down to:

The effectiveness of process monitoring (FGO spawns processes that are not handled by Magisk) MagiskHide is simply just too slow (FGO detects root before MagiskHide have a chance to hijack the process)

The 2nd reason is extremely unlikely, so let’s assume the first scenario. To monitor all new processes spawned from Zygote( app_process )(we don’t care child process forked from target processes), we can go through am_proc_start logs (accessed via events buffer: logcat -b events ) and check if there are any weird process launched in the background. Hmm… doesn’t seem to be the case, something is definitely fishy here. It’s about time to pull out the big guns.

Digging Deeper

We utilize the tool strace to analyze what’s going on with FGO without taking apart the APK and do reverse engineering. If you want the full strace output, here you go (the target process PID is 9184 ). BTW here is an interesting info within the traces, the blacklisted packages (and some files):

com.cih.game_cih

com.hexview.android.memspector

cn.mm.gk

pl.Nyki.Dax

catch_.me_.if_.you_.can_

com.sbgamehacker

jp.kbc.ma34.devicefaker

com.saurik.substrate

de.robv.android.xposed.installer

com.felixheller.sharedprefseditor

cn.mc.sq

cn.mc1.sq

com.cih.game_cih

pl.aqua.gameguardian

org.sbtools.gamehack

com.hexview.android.memspector

mr.big.stuff

cat.dcat.roothide

de.robv.android.xposed.installer

com.saurik.substrate

com.topjohnwu.magisk

com.loserskater.suhidegui

eu.chainfire.suhide

eu.chainfire.supersu

eu.chainfire.supersu.pro

com.noshufou.android.su

com.koushikdutta.superuser

me.phh.superuser /system/app/superuser.apk

/system/app/Superuser.apk

/system/app/SuperUser.apk

/system/app/SUPERUSER.apk

/su/suhide

It also attempts to read current mount points in /proc/self/mount , environment variables via /proc/self/environ , tries to open /root , the path where Magisk store overlayed sbin binaries (access denied of course, who will let them read it 💩). But to my surprise, they are traversing the whole /proc folder and reading mount info from other processes! Thank god it seems all access are denied… oh wait a minute. WTF. It successfully read mount info of a random process (PID = 4053 ), and detected Magisk through it!

A snippet of the strace output

I then tried running FGO on a Pixel XL, and without surprise the app actually do work with a proper MagiskHide setup. What is wrong here?