Magisk: RootBeerFresh detects Magisk 18.2

Created on 19 Mar 2019  路  6Comments  路  Source: topjohnwu/Magisk

It's "MAGISK UDS AND STAT" process detects Magisk even if MagiskHide is being used.
Its source is available here: https://github.com/kimchangyoun/rootbeerFresh
Also, sample app is available here: https://play.google.com/store/apps/details?id=com.kimchangyoun.rootbeerFresh.sample
Tested on Samsung Galaxy S7 (SM-G930L) with 8.0 Oreo
Screenshot_20190320-000445_RootbeerFresh

Most helpful comment

UDS is Unix Domain Socket, and it detects magisk by checking if there is a random string of 32 (or more) digits starting with @ in /proc/net/unix (It may have some more ways to detect Magisk.)

A screenshot of non-root terminal that shows why it works:
Screenshot_20190320-003608_Terminal Emulator
(It doesn't matter whether MagiskHide is enabled for terminal app.)

NDK C++ Source of UDS detecting part (Full):
```/*************************

  • Description: Check the Unix Domain Socket used by Magisk

*

  • Parameters: none

*

  • Return value: 0 - non-existant / not visible, 1 or more - exists

*

*************************/

int Java_com_kimchangyoun_rootbeerFresh_RootBeerNative_checkForMagiskUDS( JNIEnv* env, jobject thiz )

{

聽聽聽聽int uds_detect_count = 0;

聽聽聽聽int magisk_file_detect_count = 0;

聽聽聽聽int result = 0;

聽聽聽聽// Magisk UDS(Unix Domain Socket) Detection Method.

聽聽聽聽// The unix domain socket is typically used for local communications, ie IPC.

聽聽聽聽// At least Android 8.0 can look up unix domain sockets.

聽聽聽聽// You need to be sure that you can query the unix domain socket on Android 9.0 or later.

聽聽聽聽FILE *fh = fopen("/proc/net/unix", "r");

聽聽聽聽if (fh) {

聽聽聽聽聽聽聽聽for (;;) {

聽聽聽聽聽聽聽聽聽聽聽聽char filename[BUFSIZE] = {0};

聽聽聽聽聽聽聽聽聽聽聽聽uint32_t a, b, c, d, e, f, g;

聽聽聽聽聽聽聽聽聽聽聽聽int count = fscanf(fh, "%x: %u %u %u %u %u %u ",

聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽&a, &b, &c, &d, &e, &f, &g);

聽聽聽聽聽聽聽聽聽聽聽聽if (count == 0) {

聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽if (!fgets(filename, BUFSIZE, fh)) {

聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽break;

聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽}

聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽continue;

聽聽聽聽聽聽聽聽聽聽聽聽} else if (count == -1) {

聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽break;

聽聽聽聽聽聽聽聽聽聽聽聽} else if (!fgets(filename, BUFSIZE, fh)) {

聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽break;

聽聽聽聽聽聽聽聽聽聽聽聽}

聽聽聽聽聽聽聽聽聽聽聽聽LOGD("%s", filename);

聽聽聽聽聽聽聽聽聽聽聽聽magisk_file_detect_count += checkFileStat("/dev/.magisk.unblock");

聽聽聽聽聽聽聽聽聽聽聽聽magisk_file_detect_count += checkFileStat("/sbin/magiskinit");

聽聽聽聽聽聽聽聽聽聽聽聽magisk_file_detect_count += checkFileStat("/sbin/magisk");

聽聽聽聽聽聽聽聽聽聽聽聽magisk_file_detect_count += checkFileStat("/sbin/.magisk");

聽聽聽聽聽聽聽聽聽聽聽聽magisk_file_detect_count += checkFileStat("/data/adb/magisk.img");

聽聽聽聽聽聽聽聽聽聽聽聽magisk_file_detect_count += checkFileStat("/data/adb/magisk.db");

聽聽聽聽聽聽聽聽聽聽聽聽magisk_file_detect_count += checkFileStat("/data/adb/.boot_count");

聽聽聽聽聽聽聽聽聽聽聽聽magisk_file_detect_count += checkFileStat("/data/adb/magisk_simple");

聽聽聽聽聽聽聽聽聽聽聽聽magisk_file_detect_count += checkFileStat("/data/adb/magisk");

聽聽聽聽聽聽聽聽聽聽聽聽magisk_file_detect_count += checkFileStat("/cache/.disable_magisk");

聽聽聽聽聽聽聽聽聽聽聽聽magisk_file_detect_count += checkFileStat("/cache/magisk.log");

聽聽聽聽聽聽聽聽聽聽聽聽magisk_file_detect_count += checkFileStat("/init.magisk.rc");

聽聽聽聽聽聽聽聽聽聽聽聽/*

/overlay/sbin/magisk

/data/adb/magisk/magisk.apk

/data/adb/magisk_debug.log

/data/adb/magisk_merge.img

/dev/.magisk.patch.done

/data/data/com.topjohnwu.magisk/install

/data/user_de/0/com.topjohnwu.magisk/install

*/

聽聽聽聽聽聽聽聽聽聽聽聽// The name of the unix domain socket created by the daemon is prefixed with an @ symbol.

聽聽聽聽聽聽聽聽聽聽聽聽char *ptr = strtok(filename, "@");

聽聽聽聽聽聽聽聽聽聽聽聽if(ptr) {

聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽// On Android, the / character, space, and dot characters are the names of the normal unix domain sockets.

聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽if(strstr(ptr, "/")) {

聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽;

聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽} else if(strstr(ptr, " ")) {

聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽;

聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽} else if(strstr(ptr, ".")) {

聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽;

聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽} else { // Magisk replaces the name of the unix domain socket with a random string of 32 digits.

聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽int len = strlen(ptr);

聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽if (len >= 32) {

聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽// Magisk was detected.

聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽LOGD("[Detect Magisk UnixDomainSocket] %s", ptr);

聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽uds_detect_count++;

聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽}

聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽}

聽聽聽聽聽聽聽聽聽聽聽聽}

聽聽聽聽聽聽聽聽}

聽聽聽聽聽聽聽聽fclose(fh);

聽聽聽聽}

聽聽聽聽if(uds_detect_count == 0 || magisk_file_detect_count == 0) {

聽聽聽聽聽聽聽聽result = 0;

聽聽聽聽} else {

聽聽聽聽聽聽聽聽result = 1;

聽聽聽聽}

聽聽聽聽return result;

}```

All 6 comments

UDS is Unix Domain Socket, and it detects magisk by checking if there is a random string of 32 (or more) digits starting with @ in /proc/net/unix (It may have some more ways to detect Magisk.)

A screenshot of non-root terminal that shows why it works:
Screenshot_20190320-003608_Terminal Emulator
(It doesn't matter whether MagiskHide is enabled for terminal app.)

NDK C++ Source of UDS detecting part (Full):
```/*************************

  • Description: Check the Unix Domain Socket used by Magisk

*

  • Parameters: none

*

  • Return value: 0 - non-existant / not visible, 1 or more - exists

*

*************************/

int Java_com_kimchangyoun_rootbeerFresh_RootBeerNative_checkForMagiskUDS( JNIEnv* env, jobject thiz )

{

聽聽聽聽int uds_detect_count = 0;

聽聽聽聽int magisk_file_detect_count = 0;

聽聽聽聽int result = 0;

聽聽聽聽// Magisk UDS(Unix Domain Socket) Detection Method.

聽聽聽聽// The unix domain socket is typically used for local communications, ie IPC.

聽聽聽聽// At least Android 8.0 can look up unix domain sockets.

聽聽聽聽// You need to be sure that you can query the unix domain socket on Android 9.0 or later.

聽聽聽聽FILE *fh = fopen("/proc/net/unix", "r");

聽聽聽聽if (fh) {

聽聽聽聽聽聽聽聽for (;;) {

聽聽聽聽聽聽聽聽聽聽聽聽char filename[BUFSIZE] = {0};

聽聽聽聽聽聽聽聽聽聽聽聽uint32_t a, b, c, d, e, f, g;

聽聽聽聽聽聽聽聽聽聽聽聽int count = fscanf(fh, "%x: %u %u %u %u %u %u ",

聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽&a, &b, &c, &d, &e, &f, &g);

聽聽聽聽聽聽聽聽聽聽聽聽if (count == 0) {

聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽if (!fgets(filename, BUFSIZE, fh)) {

聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽break;

聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽}

聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽continue;

聽聽聽聽聽聽聽聽聽聽聽聽} else if (count == -1) {

聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽break;

聽聽聽聽聽聽聽聽聽聽聽聽} else if (!fgets(filename, BUFSIZE, fh)) {

聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽break;

聽聽聽聽聽聽聽聽聽聽聽聽}

聽聽聽聽聽聽聽聽聽聽聽聽LOGD("%s", filename);

聽聽聽聽聽聽聽聽聽聽聽聽magisk_file_detect_count += checkFileStat("/dev/.magisk.unblock");

聽聽聽聽聽聽聽聽聽聽聽聽magisk_file_detect_count += checkFileStat("/sbin/magiskinit");

聽聽聽聽聽聽聽聽聽聽聽聽magisk_file_detect_count += checkFileStat("/sbin/magisk");

聽聽聽聽聽聽聽聽聽聽聽聽magisk_file_detect_count += checkFileStat("/sbin/.magisk");

聽聽聽聽聽聽聽聽聽聽聽聽magisk_file_detect_count += checkFileStat("/data/adb/magisk.img");

聽聽聽聽聽聽聽聽聽聽聽聽magisk_file_detect_count += checkFileStat("/data/adb/magisk.db");

聽聽聽聽聽聽聽聽聽聽聽聽magisk_file_detect_count += checkFileStat("/data/adb/.boot_count");

聽聽聽聽聽聽聽聽聽聽聽聽magisk_file_detect_count += checkFileStat("/data/adb/magisk_simple");

聽聽聽聽聽聽聽聽聽聽聽聽magisk_file_detect_count += checkFileStat("/data/adb/magisk");

聽聽聽聽聽聽聽聽聽聽聽聽magisk_file_detect_count += checkFileStat("/cache/.disable_magisk");

聽聽聽聽聽聽聽聽聽聽聽聽magisk_file_detect_count += checkFileStat("/cache/magisk.log");

聽聽聽聽聽聽聽聽聽聽聽聽magisk_file_detect_count += checkFileStat("/init.magisk.rc");

聽聽聽聽聽聽聽聽聽聽聽聽/*

/overlay/sbin/magisk

/data/adb/magisk/magisk.apk

/data/adb/magisk_debug.log

/data/adb/magisk_merge.img

/dev/.magisk.patch.done

/data/data/com.topjohnwu.magisk/install

/data/user_de/0/com.topjohnwu.magisk/install

*/

聽聽聽聽聽聽聽聽聽聽聽聽// The name of the unix domain socket created by the daemon is prefixed with an @ symbol.

聽聽聽聽聽聽聽聽聽聽聽聽char *ptr = strtok(filename, "@");

聽聽聽聽聽聽聽聽聽聽聽聽if(ptr) {

聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽// On Android, the / character, space, and dot characters are the names of the normal unix domain sockets.

聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽if(strstr(ptr, "/")) {

聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽;

聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽} else if(strstr(ptr, " ")) {

聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽;

聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽} else if(strstr(ptr, ".")) {

聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽;

聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽} else { // Magisk replaces the name of the unix domain socket with a random string of 32 digits.

聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽int len = strlen(ptr);

聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽if (len >= 32) {

聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽// Magisk was detected.

聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽LOGD("[Detect Magisk UnixDomainSocket] %s", ptr);

聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽uds_detect_count++;

聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽}

聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽}

聽聽聽聽聽聽聽聽聽聽聽聽}

聽聽聽聽聽聽聽聽}

聽聽聽聽聽聽聽聽fclose(fh);

聽聽聽聽}

聽聽聽聽if(uds_detect_count == 0 || magisk_file_detect_count == 0) {

聽聽聽聽聽聽聽聽result = 0;

聽聽聽聽} else {

聽聽聽聽聽聽聽聽result = 1;

聽聽聽聽}

聽聽聽聽return result;

}```

Screenshot_20190319-213235

It's a really fuzzy (still smart!) way to detect Magisk though if I got this correctly.
I mean just because Magisk uses abstract namespace doesn't mean that only magisk does it. 馃

Well this is not a good solution, I can rename it to any length and maybe some other weird strings.
People should come up with better ideas to overcome this....

Rootbeer's solution is naive though effective.

  • If Magisk starts to use / in socket name, rootbeer can update accordingly
  • If Magisk starts to use . in socket name, rootbeer can update accordingly
  • If Magisk starts to use space in socket name, rootbeer can update accordingly
  • If Magisk starts to use socket names shorter than 32 characters, rootbeer will have to update accordingly

Also, once another _legitimate_ app/service creates a 32 bytes long socket name, it will create a false positive. Shortening the socket name to 7 or 10 characters will likely force rootbeer to update its detection, thus increase the false positive rate.

Personally I would not chase rootbeer's detection algorithm but rather focus on Play Services.

(beucase Play Services prevents us from NFC payments)

@djechelon Yeah, it is really a dumb way to detect, but the problem is that some anti-root solutions started to implement this method. I think no one will try to detect 10-20 digit socket names since it's very common. (And , it is almost impossible to detect its "randomity" itself.)

Was this page helpful?
0 / 5 - 0 ratings