topjohnwu / Magisk

The Magic Mask for Android
GNU General Public License v3.0
47.6k stars 12.08k forks source link

RootBeerFresh detects Magisk 18.2 #1226

Closed Ingan121 closed 5 years ago

Ingan121 commented 5 years ago

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

Ingan121 commented 5 years ago

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;

}```
DenyDarko commented 5 years ago

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. 🤔

topjohnwu commented 5 years ago

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....

djechelon commented 5 years ago

Rootbeer's solution is naive though effective.

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)

Ingan121 commented 5 years ago

@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.)

DenyDarko commented 5 years ago

https://github.com/topjohnwu/Magisk/pull/1359