programing

루트 장치에서 실행 중인지 확인

telecom 2023. 7. 29. 08:10
반응형

루트 장치에서 실행 중인지 확인

내 앱에는 루트를 사용할 수 있는 장치에서만 작동하는 특정 기능이 있습니다.이 기능을 사용할 때 실패하게 하는 대신(사용자에게 적절한 오류 메시지를 표시하는 경우) 루트를 먼저 사용할 수 있는지 자동으로 확인하고, 사용할 수 없는 경우 각 옵션을 먼저 숨길 수 있는 기능을 선호합니다.

이것을 할 수 있는 방법이 있습니까?

여기 세 가지 방법 중 하나인 루트를 확인할 클래스가 있습니다.

/** @author Kevin Kowalewski */
public class RootUtil {
    public static boolean isDeviceRooted() {
        return checkRootMethod1() || checkRootMethod2() || checkRootMethod3();
    }

    private static boolean checkRootMethod1() {
        String buildTags = android.os.Build.TAGS;
        return buildTags != null && buildTags.contains("test-keys");
    }

    private static boolean checkRootMethod2() {
        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;
    }

    private static boolean checkRootMethod3() {
        Process process = null;
        try {
            process = Runtime.getRuntime().exec(new String[] { "/system/xbin/which", "su" });
            BufferedReader in = new BufferedReader(new InputStreamReader(process.getInputStream()));
            if (in.readLine() != null) return true;
            return false;
        } catch (Throwable t) {
            return false;
        } finally {
            if (process != null) process.destroy();
        }
    }
}

패브릭/Firebase Crashlytics를 이미 사용 중인 경우 전화로 문의할 수 있습니다.

CommonUtils.isRooted(context)

이 방법은 현재 구현되어 있습니다.

public static boolean isRooted(Context context) {
    boolean isEmulator = isEmulator(context);
    String buildTags = Build.TAGS;
    if (!isEmulator && buildTags != null && buildTags.contains("test-keys")) {
        return true;
    } else {
        File file = new File("/system/app/Superuser.apk");
        if (file.exists()) {
            return true;
        } else {
            file = new File("/system/xbin/su");
            return !isEmulator && file.exists();
        }
    }
}

public static boolean isEmulator(Context context) {
    String androidId = Secure.getString(context.getContentResolver(), "android_id");
    return "sdk".equals(Build.PRODUCT) || "google_sdk".equals(Build.PRODUCT) || androidId == null;
}

루트 도구 라이브러리는 루트를 확인하는 간단한 방법을 제공합니다.

RootTools.isRootAvailable()

언급

애플리케이션에서 "su" 명령을 실행하여 장치가 루트인지 여부를 확인하고 있습니다.하지만 오늘 제 코드의 이 부분을 제거했습니다. 왜죠?

내 애플리케이션이 기억을 죽이는 사람이 되었기 때문입니다. 어떻게요?제 이야기를 들려드리겠습니다.

제 애플리케이션이 장치 속도를 늦추고 있다는 불만이 있었습니다(물론 그럴 리가 없다고 생각했습니다).그 이유를 알아내려고 노력했습니다.그래서 저는 MAT를 사용하여 더미 덤프를 수집하고 분석했습니다. 모든 것이 완벽해 보였습니다.그러나 여러 번 앱을 다시 실행한 후 장치가 정말 느려지고 프로그램을 중지해도 장치를 다시 시작하지 않는 한 속도가 빨라지지 않는다는 것을 깨달았습니다.장치가 매우 느린 상태에서 덤프 파일을 다시 분석했습니다.하지만 덤프 파일에 대한 모든 것은 여전히 완벽했습니다.그리고 처음에 해야 할 일을 했습니다.프로세스를 나열했습니다.

$ adb shell ps

놀랍게도, (내 애플리케이션의 프로세스 태그가 매니페스트에 있는) 내 애플리케이션에는 많은 프로세스가 있었습니다.그들 중 일부는 좀비였고 일부는 그렇지 않았습니다.

단일 활동이 있고 "su" 명령만 실행하는 샘플 응용 프로그램을 사용하여 응용 프로그램을 시작할 때마다 좀비 프로세스가 생성된다는 것을 깨달았습니다.처음에 이 좀비들은 0KB를 할당하지만, 어떤 일이 일어나고 좀비 프로세스는 내 애플리케이션의 주요 프로세스와 거의 동일한 KB를 보유하고 있으며 이는 스탠다드아트 프로세스가 되었습니다.

버그에 대한 동일한 이슈에 대한 버그 보고서가 있습니다.sun.com : http://bugs.sun.com/view_bug.do?bug_id=6474073 이것은 명령을 찾을 수 없는 경우 좀비가 exec(실행) 메서드로 생성된다는 것을 설명합니다.하지만 저는 여전히 그들이 왜 그리고 어떻게 스탠다드 아트 프로세스가 되고 상당한 KB를 보유할 수 있는지 이해할 수 없습니다. (이런 일이 항상 일어나는 것은 아닙니다.)

아래 코드 샘플을 사용하여 시도할 수 있습니다.

String commandToExecute = "su";
executeShellCommand(commandToExecute);

간단한 명령 실행 방법

private boolean executeShellCommand(String command){
    Process process = null;            
    try{
        process = Runtime.getRuntime().exec(command);
        return true;
    } catch (Exception e) {
        return false;
    } finally{
        if(process != null){
            try{
                process.destroy();
            }catch (Exception e) {
            }
        }
    }
}

요약하자면, 장치가 루트인지 여부를 결정하는 것에 대한 조언은 없습니다.하지만 내가 당신이라면 Runtime.getRuntime().exec()을 사용하지 않을 것입니다.

참고로 RootTools.isRootAvailable()은 동일한 문제를 발생시킵니다.

여기에 나열된 많은 답변에는 다음과 같은 고유한 문제가 있습니다.

  • 테스트 키를 확인하는 것은 루트 액세스와 상관 관계가 있지만 반드시 루트 액세스를 보장하는 것은 아닙니다.
  • "PATH" 디렉터리는 하드 코딩되는 대신 실제 "PATH" 환경 변수에서 파생되어야 합니다.
  • "su" 실행 파일이 있다고 해서 반드시 장치가 루팅된 것은 아닙니다.
  • "어떤" 실행 파일이 설치될 수도 있고 설치되지 않을 수도 있으며, 가능하면 시스템이 경로를 해결하도록 해야 합니다.
  • 장치에 SuperUser 앱이 설치되어 있다고 해서 장치가 아직 루트 액세스 권한을 가지고 있는 것은 아닙니다.

Stericson의 RootTools 라이브러리가 루트를 보다 합법적으로 확인하는 것 같습니다.여분의 도구와 유틸리티도 많이 구비되어 있어서 적극 추천합니다.그러나 루트를 구체적으로 확인하는 방법에 대한 설명은 없으며, 대부분의 앱이 실제로 필요로 하는 것보다 약간 무거울 수 있습니다.

루트툴 라이브러리를 기반으로 한 유틸리티 메소드를 몇 가지 만들었습니다.단순히 "su" 실행 파일이 장치에 있는지 확인하려는 경우 다음 방법을 사용할 수 있습니다.

public static boolean isRootAvailable(){
    for(String pathDir : System.getenv("PATH").split(":")){
        if(new File(pathDir, "su").exists()) {
            return true;
        }
    }
    return false;
}

이 방법은 단순히 "PATH" 환경 변수에 나열된 디렉터리를 순환하고 그 중 하나에 "su" 파일이 있는지 확인합니다.

루트 액세스를 제대로 확인하려면 "su" 명령을 실제로 실행해야 합니다.SuperUser와 같은 앱이 설치되어 있으면 이 시점에서 루트 액세스를 요청하거나 이미 허용/거부된 경우 액세스가 허용/거부되었는지 여부를 나타내는 토스트가 표시될 수 있습니다.사용자 ID가 실제로 0(루트)인지 확인할 수 있도록 실행하기 좋은 명령은 "id"입니다.

루트 액세스가 허용되었는지 여부를 확인하는 예제 방법은 다음과 같습니다.

public static boolean isRootGiven(){
    if (isRootAvailable()) {
        Process process = null;
        try {
            process = Runtime.getRuntime().exec(new String[]{"su", "-c", "id"});
            BufferedReader in = new BufferedReader(new InputStreamReader(process.getInputStream()));
            String output = in.readLine();
            if (output != null && output.toLowerCase().contains("uid=0"))
                return true;
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            if (process != null)
                process.destroy();
        }
    }

    return false;
}

일부 에뮬레이터에는 "su" 실행 파일이 미리 설치되어 있지만 특정 사용자만 adb 셸처럼 액세스할 수 있기 때문에 실제로 "su" 명령 실행을 테스트하는 것이 중요합니다.

또한 "su" 실행 파일을 실행하기 전에 "su" 실행 파일이 있는지 확인하는 것이 중요합니다. 왜냐하면 안드로이드는 누락된 명령을 실행하려는 프로세스를 제대로 처리하지 않기 때문입니다.이러한 고스트 프로세스는 시간이 지남에 따라 메모리 소비를 증가시킬 수 있습니다.

Java 수준의 루트 검사는 안전한 솔루션이 아닙니다.응용 프로그램이 루트 장치에서 실행해야 하는 보안 문제가 있는 경우 이 솔루션을 사용하십시오.

케빈의 대답은 전화기에도 루트클락 같은 앱이 없는 한 작동합니다.이러한 앱은 일단 전화기가 루팅되면 Handle over Java APIs를 가지고 있으며, 전화기가 루팅되지 않은 상태에서 이러한 API를 조롱합니다.

케빈의 답변을 토대로 네이티브 레벨 코드를 작성하였습니다. 루트클락에서도 작동합니다! 또한 메모리 누수 문제가 발생하지 않습니다.

#include <string.h>
#include <jni.h>
#include <time.h>
#include <sys/stat.h>
#include <stdio.h>
#include "android_log.h"
#include <errno.h>
#include <unistd.h>
#include <sys/system_properties.h>

JNIEXPORT int JNICALL Java_com_test_RootUtils_checkRootAccessMethod1(
        JNIEnv* env, jobject thiz) {


    //Access function checks whether a particular file can be accessed
    int result = access("/system/app/Superuser.apk",F_OK);

    ANDROID_LOGV( "File Access Result %d\n", result);

    int len;
    char build_tags[PROP_VALUE_MAX]; // PROP_VALUE_MAX from <sys/system_properties.h>.
    len = __system_property_get(ANDROID_OS_BUILD_TAGS, build_tags); // On return, len will equal (int)strlen(model_id).
    if(strcmp(build_tags,"test-keys") == 0){
        ANDROID_LOGV( "Device has test keys\n", build_tags);
        result = 0;
    }
    ANDROID_LOGV( "File Access Result %s\n", build_tags);
    return result;

}

JNIEXPORT int JNICALL Java_com_test_RootUtils_checkRootAccessMethod2(
        JNIEnv* env, jobject thiz) {
    //which command is enabled only after Busy box is installed on a rooted device
    //Outpput of which command is the path to su file. On a non rooted device , we will get a null/ empty path
    //char* cmd = const_cast<char *>"which su";
    FILE* pipe = popen("which su", "r");
    if (!pipe) return -1;
    char buffer[128];
    std::string resultCmd = "";
    while(!feof(pipe)) {
        if(fgets(buffer, 128, pipe) != NULL)
            resultCmd += buffer;
    }
    pclose(pipe);

    const char *cstr = resultCmd.c_str();
    int result = -1;
    if(cstr == NULL || (strlen(cstr) == 0)){
        ANDROID_LOGV( "Result of Which command is Null");
    }else{
        result = 0;
        ANDROID_LOGV( "Result of Which command %s\n", cstr);
        }
    return result;

}

JNIEXPORT int JNICALL Java_com_test_RootUtils_checkRootAccessMethod3(
        JNIEnv* env, jobject thiz) {


    int len;
    char build_tags[PROP_VALUE_MAX]; // PROP_VALUE_MAX from <sys/system_properties.h>.
    int result = -1;
    len = __system_property_get(ANDROID_OS_BUILD_TAGS, build_tags); // On return, len will equal (int)strlen(model_id).
    if(len >0 && strstr(build_tags,"test-keys") != NULL){
        ANDROID_LOGV( "Device has test keys\n", build_tags);
        result = 0;
    }

    return result;

}

Java 코드에서 네이티브 호출을 하려면 래퍼 클래스 RootUtils를 만들어야 합니다.

    public boolean checkRooted() {

       if( rootUtils.checkRootAccessMethod3()  == 0 || rootUtils.checkRootAccessMethod1()  == 0 || rootUtils.checkRootAccessMethod2()  == 0 )
           return true;
      return false;
     }

http://code.google.com/p/roottools/

jar 파일을 사용하지 않으려면 다음 코드를 사용합니다.

public static boolean findBinary(String binaryName) {
        boolean found = false;
        if (!found) {
            String[] places = { "/sbin/", "/system/bin/", "/system/xbin/",
                    "/data/local/xbin/", "/data/local/bin/",
                    "/system/sd/xbin/", "/system/bin/failsafe/", "/data/local/" };
            for (String where : places) {
                if (new File(where + binaryName).exists()) {
                    found = true;

                    break;
                }
            }
        }
        return found;
    }

프로그램에서 su 폴더 찾기를 시도합니다.

private static boolean isRooted() {
        return findBinary("su");
    }

예:

if (isRooted()) {
   textView.setText("Device Rooted");

} else {
   textView.setText("Device Unrooted");
}

루트비어는 스콧과 매튜가 만든 루트 체크 안드로이드 라이브러리입니다.다양한 검사를 사용하여 장치가 루트인지 여부를 나타냅니다.

Java 검사

  • 루트 관리 앱 확인

  • 잠재적으로 위험한 앱 확인

  • 루트 클로킹 앱 확인

  • 테스트 키 확인

  • 위험한 제안 확인

  • 비지박스 바이너리 확인

  • Subbinary 검사

  • checkSu 존재

  • 확인 대상RW 시스템

네이티브 체크

기본 루트 검사기를 호출하여 자체 검사를 수행합니다.기본 검사는 일반적으로 추적하기 어렵기 때문에 일부 루트 망토 앱은 특정 키워드가 포함된 기본 라이브러리의 로드를 차단합니다.

  • Subbinary 검사

isRootAvailable()을 사용하는 대신 isAccessGiven()을 사용할 수 있습니다.루트 도구 위키에서 직접:

if (RootTools.isAccessGiven()) {
    // your app has been granted root access
}

RootTools.isAccessGiven()은 장치가 루트인지 확인할 뿐만 아니라 앱에 대한 su를 호출하고 권한을 요청하며 앱에 루트 권한이 성공적으로 부여되면 true를 반환합니다.필요할 때 액세스 권한이 부여되는지 확인하기 위해 앱의 첫 번째 검사로 사용할 수 있습니다.

언급

시스템 속성을 설정하는 데 사용되는 일부 수정된 빌드 ro.modversion이 진행된 것 . 몇 달 전 Dude의 제 과 같습니다.일이 진행된 것 같습니다. 몇 달 전에 The Dude에서 나온 제 빌드는 다음과 같습니다.

cmb@apollo:~$ adb -d shell getprop |grep build
[ro.build.id]: [CUPCAKE]
[ro.build.display.id]: [htc_dream-eng 1.5 CUPCAKE eng.TheDudeAbides.20090427.235325 test-keys]
[ro.build.version.incremental]: [eng.TheDude.2009027.235325]
[ro.build.version.sdk]: [3]
[ro.build.version.release]: [1.5]
[ro.build.date]: [Mon Apr 20 01:42:32 CDT 2009]
[ro.build.date.utc]: [1240209752]
[ro.build.type]: [eng]
[ro.build.user]: [TheDude]
[ro.build.host]: [ender]
[ro.build.tags]: [test-keys]
[ro.build.product]: [dream]
[ro.build.description]: [kila-user 1.1 PLAT-RC33 126986 ota-rel-keys,release-keys]
[ro.build.fingerprint]: [tmobile/kila/dream/trout:1.1/PLAT-RC33/126986:user/ota-rel-keys,release-keys]
[ro.build.changelist]: [17615# end build properties]

반면에 1.5 SDK의 에뮬레이터는 1.5 이미지를 실행하며 루트를 가지고 있으며 아마도 Android Dev Phone 1과 비슷하며 다음과 같은 기능을 가지고 있습니다.

cmb@apollo:~$ adb -e shell getprop |grep build
[ro.build.id]: [CUPCAKE]
[ro.build.display.id]: [sdk-eng 1.5 CUPCAKE 148875 test-keys]
[ro.build.version.incremental]: [148875]
[ro.build.version.sdk]: [3]
[ro.build.version.release]: [1.5]
[ro.build.date]: [Thu May 14 18:09:10 PDT 2009]
[ro.build.date.utc]: [1242349750]
[ro.build.type]: [eng]
[ro.build.user]: [android-build]
[ro.build.host]: [undroid16.mtv.corp.google.com]
[ro.build.tags]: [test-keys]
[ro.build.product]: [generic]
[ro.build.description]: [sdk-eng 1.5 CUPCAKE 148875 test-keys]
[ro.build.fingerprint]: [generic/sdk/generic/:1.5/CUPCAKE/148875:eng/test-keys]

빌드에 는 손에 소매건에관는해서, 손것없만지이다, 래의다검있습니이색양한아물을의 한 검색이 있습니다.site:xda-developers.com유익합니다.여기 네덜란드의 G1이 있습니다. 당신은 이것을 볼 수 있습니다.ro.build.tags을 가지고 있지 않음test-keys그게 아마 가장 믿을 만한 재산일 거예요

루트 탐지에 네이티브 코드를 사용하는 것이 좋습니다.다음은 완전한 작동 예입니다.

enter image description here

JAVA 래퍼:

package com.kozhevin.rootchecks.util;


import android.support.annotation.NonNull;

import com.kozhevin.rootchecks.BuildConfig;

public class MeatGrinder {
    private final static String LIB_NAME = "native-lib";
    private static boolean isLoaded;
    private static boolean isUnderTest = false;

    private MeatGrinder() {

    }

    public boolean isLibraryLoaded() {
        if (isLoaded) {
            return true;
        }
        try {
            if(isUnderTest) {
                throw new UnsatisfiedLinkError("under test");
            }
            System.loadLibrary(LIB_NAME);
            isLoaded = true;
        } catch (UnsatisfiedLinkError e) {
            if (BuildConfig.DEBUG) {
                e.printStackTrace();
            }
        }
        return isLoaded;
    }

    public native boolean isDetectedDevKeys();

    public native boolean isDetectedTestKeys();

    public native boolean isNotFoundReleaseKeys();

    public native boolean isFoundDangerousProps();

    public native boolean isPermissiveSelinux();

    public native boolean isSuExists();

    public native boolean isAccessedSuperuserApk();

    public native boolean isFoundSuBinary();

    public native boolean isFoundBusyboxBinary();

    public native boolean isFoundXposed();

    public native boolean isFoundResetprop();

    public native boolean isFoundWrongPathPermission();

    public native boolean isFoundHooks();

    @NonNull
    public static MeatGrinder getInstance() {
        return InstanceHolder.INSTANCE;
    }

    private static class InstanceHolder {
        private static final MeatGrinder INSTANCE = new MeatGrinder();
    }
}

JNI 래퍼(네이티브-lib.c):

JNIEXPORT jboolean JNICALL
Java_com_kozhevin_rootchecks_util_MeatGrinder_isDetectedTestKeys(
        JNIEnv *env,
        jobject  this ) {

    return (jboolean) isDetectedTestKeys();
}

JNIEXPORT jboolean JNICALL
Java_com_kozhevin_rootchecks_util_MeatGrinder_isDetectedDevKeys(
        JNIEnv *env,
        jobject  this ) {

    return (jboolean) isDetectedDevKeys();
}

JNIEXPORT jboolean JNICALL
Java_com_kozhevin_rootchecks_util_MeatGrinder_isNotFoundReleaseKeys(
        JNIEnv *env,
        jobject  this ) {

    return (jboolean) isNotFoundReleaseKeys();
}

JNIEXPORT jboolean JNICALL
Java_com_kozhevin_rootchecks_util_MeatGrinder_isFoundDangerousProps(
        JNIEnv *env,
        jobject  this ) {

    return (jboolean) isFoundDangerousProps();
}

JNIEXPORT jboolean JNICALL
Java_com_kozhevin_rootchecks_util_MeatGrinder_isPermissiveSelinux(
        JNIEnv *env,
        jobject  this ) {

    return (jboolean) isPermissiveSelinux();
}

JNIEXPORT jboolean JNICALL
Java_com_kozhevin_rootchecks_util_MeatGrinder_isSuExists(
        JNIEnv *env,
        jobject  this ) {

    return (jboolean) isSuExists();
}

JNIEXPORT jboolean JNICALL
Java_com_kozhevin_rootchecks_util_MeatGrinder_isAccessedSuperuserApk(
        JNIEnv *env,
        jobject  this ) {

    return (jboolean) isAccessedSuperuserApk();
}

JNIEXPORT jboolean JNICALL
Java_com_kozhevin_rootchecks_util_MeatGrinder_isFoundSuBinary(
        JNIEnv *env,
        jobject  this ) {

    return (jboolean) isFoundSuBinary();
}

JNIEXPORT jboolean JNICALL
Java_com_kozhevin_rootchecks_util_MeatGrinder_isFoundBusyboxBinary(
        JNIEnv *env,
        jobject  this ) {

    return (jboolean) isFoundBusyboxBinary();
}


JNIEXPORT jboolean JNICALL
Java_com_kozhevin_rootchecks_util_MeatGrinder_isFoundXposed(
        JNIEnv *env,
        jobject  this ) {

    return (jboolean) isFoundXposed();
}

JNIEXPORT jboolean JNICALL
Java_com_kozhevin_rootchecks_util_MeatGrinder_isFoundResetprop(
        JNIEnv *env,
        jobject  this ) {

    return (jboolean) isFoundResetprop();
}

JNIEXPORT jboolean JNICALL
Java_com_kozhevin_rootchecks_util_MeatGrinder_isFoundWrongPathPermission(
        JNIEnv *env,
        jobject  this ) {

    return (jboolean) isFoundWrongPathPermission();
}

JNIEXPORT jboolean JNICALL
Java_com_kozhevin_rootchecks_util_MeatGrinder_isFoundHooks(
        JNIEnv *env,
        jobject  this ) {

    return (jboolean) isFoundHooks();
}

상수:

// Comma-separated tags describing the build, like= "unsigned,debug".
const char *const ANDROID_OS_BUILD_TAGS = "ro.build.tags";

// A string that uniquely identifies this build. 'BRAND/PRODUCT/DEVICE:RELEASE/ID/VERSION.INCREMENTAL:TYPE/TAGS'.
const char *const ANDROID_OS_BUILD_FINGERPRINT = "ro.build.fingerprint";

const char *const ANDROID_OS_SECURE = "ro.secure";

const char *const ANDROID_OS_DEBUGGABLE = "ro.debuggable";
const char *const ANDROID_OS_SYS_INITD = "sys.initd";
const char *const ANDROID_OS_BUILD_SELINUX = "ro.build.selinux";
//see https://android.googlesource.com/platform/system/core/+/master/adb/services.cpp#86
const char *const SERVICE_ADB_ROOT = "service.adb.root";

const char * const MG_SU_PATH[] = {
        "/data/local/",
        "/data/local/bin/",
        "/data/local/xbin/",
        "/sbin/",
        "/system/bin/",
        "/system/bin/.ext/",
        "/system/bin/failsafe/",
        "/system/sd/xbin/",
        "/su/xbin/",
        "/su/bin/",
        "/magisk/.core/bin/",
        "/system/usr/we-need-root/",
        "/system/xbin/",
        0
};

const char * const MG_EXPOSED_FILES[] = {
        "/system/lib/libxposed_art.so",
        "/system/lib64/libxposed_art.so",
        "/system/xposed.prop",
        "/cache/recovery/xposed.zip",
        "/system/framework/XposedBridge.jar",
        "/system/bin/app_process64_xposed",
        "/system/bin/app_process32_xposed",
        "/magisk/xposed/system/lib/libsigchain.so",
        "/magisk/xposed/system/lib/libart.so",
        "/magisk/xposed/system/lib/libart-disassembler.so",
        "/magisk/xposed/system/lib/libart-compiler.so",
        "/system/bin/app_process32_orig",
        "/system/bin/app_process64_orig",
        0
};

const char * const MG_READ_ONLY_PATH[] = {
        "/system",
        "/system/bin",
        "/system/sbin",
        "/system/xbin",
        "/vendor/bin",
        "/sbin",
        "/etc",
        0
};

네이티브 코드의 루트 탐지:

struct mntent *getMntent(FILE *fp, struct mntent *e, char *buf, int buf_len) {

    while (fgets(buf, buf_len, fp) != NULL) {
        // Entries look like "/dev/block/vda /system ext4 ro,seclabel,relatime,data=ordered 0 0".
        // That is: mnt_fsname mnt_dir mnt_type mnt_opts mnt_freq mnt_passno.
        int fsname0, fsname1, dir0, dir1, type0, type1, opts0, opts1;
        if (sscanf(buf, " %n%*s%n %n%*s%n %n%*s%n %n%*s%n %d %d",
                   &fsname0, &fsname1, &dir0, &dir1, &type0, &type1, &opts0, &opts1,
                   &e->mnt_freq, &e->mnt_passno) == 2) {
            e->mnt_fsname = &buf[fsname0];
            buf[fsname1] = '\0';
            e->mnt_dir = &buf[dir0];
            buf[dir1] = '\0';
            e->mnt_type = &buf[type0];
            buf[type1] = '\0';
            e->mnt_opts = &buf[opts0];
            buf[opts1] = '\0';
            return e;
        }
    }
    return NULL;
}


bool isPresentMntOpt(const struct mntent *pMnt, const char *pOpt) {
    char *token = pMnt->mnt_opts;
    const char *end = pMnt->mnt_opts + strlen(pMnt->mnt_opts);
    const size_t optLen = strlen(pOpt);
    while (token != NULL) {
        const char *tokenEnd = token + optLen;
        if (tokenEnd > end) break;
        if (memcmp(token, pOpt, optLen) == 0 &&
            (*tokenEnd == '\0' || *tokenEnd == ',' || *tokenEnd == '=')) {
            return true;
        }
        token = strchr(token, ',');
        if (token != NULL) {
            token++;
        }
    }
    return false;
}

static char *concat2str(const char *pString1, const char *pString2) {
    char *result;
    size_t lengthBuffer = 0;

    lengthBuffer = strlen(pString1) +
                   strlen(pString2) + 1;
    result = malloc(lengthBuffer);
    if (result == NULL) {
        GR_LOGW("malloc failed\n");
        return NULL;
    }
    memset(result, 0, lengthBuffer);
    strcpy(result, pString1);
    strcat(result, pString2);
    return result;
}

static bool
isBadPropertyState(const char *key, const char *badValue, bool isObligatoryProperty, bool isExact) {
    if (badValue == NULL) {
        GR_LOGE("badValue may not be NULL");
        return false;
    }
    if (key == NULL) {
        GR_LOGE("key may not be NULL");
        return false;
    }
    char value[PROP_VALUE_MAX + 1];
    int length = __system_property_get(key, value);
    bool result = false;
    /* A length 0 value indicates that the property is not defined */
    if (length > 0) {
        GR_LOGI("property:[%s]==[%s]", key, value);
        if (isExact) {
            if (strcmp(value, badValue) == 0) {
                GR_LOGW("bad value[%s] equals to [%s] in the property [%s]", value, badValue, key);
                result = true;
            }
        } else {
            if (strlen(value) >= strlen(badValue) && strstr(value, badValue) != NULL) {
                GR_LOGW("bad value[%s] found in [%s] in the property [%s]", value, badValue, key);
                result = true;
            }
        }
    } else {
        GR_LOGI("[%s] property not found", key);
        if (isObligatoryProperty) {
            result = true;
        }
    }
    return result;
}

bool isDetectedTestKeys() {
    const char *TEST_KEYS_VALUE = "test-keys";
    return isBadPropertyState(ANDROID_OS_BUILD_TAGS, TEST_KEYS_VALUE, true, false);
}

bool isDetectedDevKeys() {
    const char *DEV_KEYS_VALUE = "dev-keys";
    return isBadPropertyState(ANDROID_OS_BUILD_TAGS, DEV_KEYS_VALUE, true, false);
}

bool isNotFoundReleaseKeys() {
    const char *RELEASE_KEYS_VALUE = "release-keys";
    return !isBadPropertyState(ANDROID_OS_BUILD_TAGS, RELEASE_KEYS_VALUE, false, true);
}

bool isFoundWrongPathPermission() {

    bool result = false;
    FILE *file = fopen("/proc/mounts", "r");
    char mntent_strings[BUFSIZ];
    if (file == NULL) {
        GR_LOGE("setmntent");
        return result;
    }

    struct mntent ent = {0};
    while (NULL != getMntent(file, &ent, mntent_strings, sizeof(mntent_strings))) {
        for (size_t i = 0; MG_READ_ONLY_PATH[i]; i++) {
            if (strcmp((&ent)->mnt_dir, MG_READ_ONLY_PATH[i]) == 0 &&
                isPresentMntOpt(&ent, "rw")) {
                GR_LOGI("%s %s %s %s\n", (&ent)->mnt_fsname, (&ent)->mnt_dir, (&ent)->mnt_opts,
                        (&ent)->mnt_type);
                result = true;
                break;
            }
        }
        memset(&ent, 0, sizeof(ent));
    }
    fclose(file);
    return result;
}


bool isFoundDangerousProps() {
    const char *BAD_DEBUGGABLE_VALUE = "1";
    const char *BAD_SECURE_VALUE = "0";
    const char *BAD_SYS_INITD_VALUE = "1";
    const char *BAD_SERVICE_ADB_ROOT_VALUE = "1";

    bool result = isBadPropertyState(ANDROID_OS_DEBUGGABLE, BAD_DEBUGGABLE_VALUE, true, true) ||
                  isBadPropertyState(SERVICE_ADB_ROOT, BAD_SERVICE_ADB_ROOT_VALUE, false, true) ||
                  isBadPropertyState(ANDROID_OS_SECURE, BAD_SECURE_VALUE, true, true) ||
                  isBadPropertyState(ANDROID_OS_SYS_INITD, BAD_SYS_INITD_VALUE, false, true);

    return result;
}

bool isPermissiveSelinux() {
    const char *BAD_VALUE = "0";
    return isBadPropertyState(ANDROID_OS_BUILD_SELINUX, BAD_VALUE, false, false);
}

bool isSuExists() {
    char buf[BUFSIZ];
    char *str = NULL;
    char *temp = NULL;
    size_t size = 1;  // start with size of 1 to make room for null terminator
    size_t strlength;

    FILE *pipe = popen("which su", "r");
    if (pipe == NULL) {
        GR_LOGI("pipe is null");
        return false;
    }

    while (fgets(buf, sizeof(buf), pipe) != NULL) {
        strlength = strlen(buf);
        temp = realloc(str, size + strlength);  // allocate room for the buf that gets appended
        if (temp == NULL) {
            // allocation error
            GR_LOGE("Error (re)allocating memory");
            pclose(pipe);
            if (str != NULL) {
                free(str);
            }
            return false;
        } else {
            str = temp;
        }
        strcpy(str + size - 1, buf);
        size += strlength;
    }
    pclose(pipe);
    GR_LOGW("A size of the result from pipe is [%zu], result:\n [%s] ", size, str);
    if (str != NULL) {
        free(str);
    }
    return size > 1 ? true : false;
}

static bool isAccessedFile(const char *path) {
    int result = access(path, F_OK);
    GR_LOGV("[%s] has been accessed with result: [%d]", path, result);
    return result == 0 ? true : false;
}

static bool isFoundBinaryFromArray(const char *const *array, const char *binary) {
    for (size_t i = 0; array[i]; ++i) {
        char *checkedPath = concat2str(array[i], binary);
        if (checkedPath == NULL) { // malloc failed
            return false;
        }
        bool result = isAccessedFile(checkedPath);
        free(checkedPath);
        if (result) {
            return result;
        }
    }
    return false;
}

bool isAccessedSuperuserApk() {
    return isAccessedFile("/system/app/Superuser.apk");
}

bool isFoundResetprop() {
    return isAccessedFile("/data/magisk/resetprop");
}

bool isFoundSuBinary() {
    return isFoundBinaryFromArray(MG_SU_PATH, "su");
}

bool isFoundBusyboxBinary() {
    return isFoundBinaryFromArray(MG_SU_PATH, "busybox");
}

bool isFoundXposed() {
    for (size_t i = 0; MG_EXPOSED_FILES[i]; ++i) {
        bool result = isAccessedFile(MG_EXPOSED_FILES[i]);
        if (result) {
            return result;
        }
    }
    return false;
}

bool isFoundHooks() {
    bool result = false;
    pid_t pid = getpid();
    char maps_file_name[512];
    sprintf(maps_file_name, "/proc/%d/maps", pid);
    GR_LOGI("try to open [%s]", maps_file_name);
    const size_t line_size = BUFSIZ;
    char *line = malloc(line_size);
    if (line == NULL) {
        return result;
    }
    FILE *fp = fopen(maps_file_name, "r");
    if (fp == NULL) {
        free(line);
        return result;
    }
    memset(line, 0, line_size);
    const char *substrate = "com.saurik.substrate";
    const char *xposed = "XposedBridge.jar";
    while (fgets(line, line_size, fp) != NULL) {
        const size_t real_line_size = strlen(line);
        if ((real_line_size >= strlen(substrate) && strstr(line, substrate) != NULL) ||
            (real_line_size >= strlen(xposed) && strstr(line, xposed) != NULL)) {
            GR_LOGI("found in [%s]: [%s]", maps_file_name, line);
            result = true;
            break;
        }
    }
    free(line);
    fclose(fp);
    return result;
}

다음은 몇 가지 답변을 기반으로 한 코드입니다.

 /**
   * Checks if the phone is rooted.
   * 
   * @return <code>true</code> if the phone is rooted, <code>false</code>
   * otherwise.
   */
  public static boolean isPhoneRooted() {

    // get from build info
    String buildTags = android.os.Build.TAGS;
    if (buildTags != null && buildTags.contains("test-keys")) {
      return true;
    }

    // check if /system/app/Superuser.apk is present
    try {
      File file = new File("/system/app/Superuser.apk");
      if (file.exists()) {
        return true;
      }
    } catch (Throwable e1) {
      // ignore
    }

    return false;
  }

@Kevins 답변을 덧붙이자면, 저는 최근 그의 시스템을 사용하는 동안 Nexus 7.1이 반환되고 있다는 것을 발견했습니다.false세 가지 방법 모두에 대해 - 아니요which, " ", " "test-keys그리고.SuperSU 않습니다./system/app.

추가한 내용:

public static boolean checkRootMethod4(Context context) {
    return isPackageInstalled("eu.chainfire.supersu", context);     
}

private static boolean isPackageInstalled(String packagename, Context context) {
    PackageManager pm = context.getPackageManager();
    try {
        pm.getPackageInfo(packagename, PackageManager.GET_ACTIVITIES);
        return true;
    } catch (NameNotFoundException e) {
        return false;
    }
}

일부 상황(루트 액세스가 보장되어야 하는 경우)에서는 Super에서 완전히 가능하기 때문에 유용하지 않습니다.SU 액세스 권한이 없는 장치에 SU를 설치해야 합니다.

하지만 슈퍼가 가능하기 때문에.SU가 설치되어 작동하고 있지만 작동하고 있지 않습니다./system/app디렉터리, 이 추가 사례는 이러한 사례를 뿌리뽑습니다(제거).

    public static boolean isRootAvailable(){
            Process p = null;
            try{
               p = Runtime.getRuntime().exec(new String[] {"su"});
               writeCommandToConsole(p,"exit 0");
               int result = p.waitFor();
               if(result != 0)
                   throw new Exception("Root check result with exit command " + result);
               return true;
            } catch (IOException e) {
                Log.e(LOG_TAG, "Su executable is not available ", e);
            } catch (Exception e) {
                Log.e(LOG_TAG, "Root is unavailable ", e);
            }finally {
                if(p != null)
                    p.destroy();
            }
            return false;
        }
 private static String writeCommandToConsole(Process proc, String command, boolean ignoreError) throws Exception{
            byte[] tmpArray = new byte[1024];
            proc.getOutputStream().write((command + "\n").getBytes());
            proc.getOutputStream().flush();
            int bytesRead = 0;
            if(proc.getErrorStream().available() > 0){
                if((bytesRead = proc.getErrorStream().read(tmpArray)) > 1){
                    Log.e(LOG_TAG,new String(tmpArray,0,bytesRead));
                    if(!ignoreError)
                        throw new Exception(new String(tmpArray,0,bytesRead));
                }
            }
            if(proc.getInputStream().available() > 0){
                bytesRead = proc.getInputStream().read(tmpArray);
                Log.i(LOG_TAG, new String(tmpArray,0,bytesRead));
            }
            return new String(tmpArray);
        }

앱에서 장치가 루트를 지원하는지 확인하려면 두 가지 추가 아이디어가 있습니다.

  1. 'su' 바이너리가 존재하는지 확인합니다. "which su"를 실행합니다.Runtime.getRuntime().exec()
  2. SuperUser.apkin에서 ./system/app/Superuser.apk위치

사용자가 루트클락과 같은 루트를 숨기는 애플리케이션을 사용하는 경우에도 ndk와 함께 C++를 사용하는 것이 루트를 탐지하는 가장 좋은 방법입니다.나는 루트클락으로 이 코드를 테스트했고 사용자가 숨기려고 해도 루트를 감지할 수 있었습니다.cpp 파일은 다음과 같습니다.

#include <jni.h>
#include <string>


/**
 *
 * function that checks for the su binary files and operates even if 
 * root cloak is installed
 * @return integer 1: device is rooted, 0: device is not 
 *rooted
*/
extern "C"
JNIEXPORT int JNICALL


Java_com_example_user_root_1native_rootFunction(JNIEnv *env,jobject thiz){
const char *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"};

int counter =0;
while (counter<9){
    if(FILE *file = fopen(paths[counter],"r")){
        fclose(file);
        return 1;
    }
    counter++;
}
return 0;
}

그리고 당신은 당신의 자바 코드에서 다음과 같이 함수를 호출할 것입니다.

public class Root_detect {



   /**
    *
    * function that calls a native function to check if the device is 
    *rooted or not
    * @return boolean: true if the device is rooted, false if the 
    *device is not rooted
   */
   public boolean check_rooted(){

        int checker = rootFunction();

        if(checker==1){
           return true;
        }else {
           return false;
        }
   }
   static {
    System.loadLibrary("cpp-root-lib");//name of your cpp file
   }

   public native int rootFunction();
}

여기에 있는 몇 가지 답변을 바탕으로, 저는 그것들을 병합하고 알려진 루트 관리자 앱이 설치되어 있는지 확인하기도 했습니다.

fun isProbablyRooted(context: Context, alsoIncludeCheckingRootManagerApp: Boolean = false): Boolean {
    return hasRootManagerSystemApp(context) || (alsoIncludeCheckingRootManagerApp && hasRootManagerSystemApp(context))
}

fun hasRootManagerSystemApp(context: Context): Boolean {
    val rootAppsPackageNames = arrayOf("com.topjohnwu.magisk", "eu.chainfire.supersu", "com.koushikdutta.superuser", "com.noshufou.android.su", "me.phh.superuser")
    rootAppsPackageNames.forEach { rootAppPackageName ->
        try {
            context.packageManager.getApplicationInfo(rootAppPackageName, 0)
            return true
        } catch (e: Exception) {
        }
    }
    return false
}

fun hasSuBinary(): Boolean {
    return try {
        findBinary("su")
    } catch (e: Exception) {
        e.printStackTrace()
        false
    }
}

private fun findBinary(binaryName: String): Boolean {
    val paths = System.getenv("PATH")
    if (!paths.isNullOrBlank()) {
        val systemPlaces: List<String> = paths.split(":")
        return systemPlaces.firstOrNull { File(it, binaryName).exists() } != null
    }
    val places = arrayOf("/sbin/", "/system/bin/", "/system/xbin/", "/data/local/xbin/", "/data/local/bin/",
            "/system/sd/xbin/", "/system/bin/failsafe/", "/data/local/")
    return places.firstOrNull { File(it, binaryName).exists() } != null
}

매니페스트:

<queries>
    <package android:name="com.topjohnwu.magisk" />
    <package android:name="eu.chainfire.supersu" />
    <package android:name="com.koushikdutta.superuser" />
    <package android:name="com.noshufou.android.su" />
    <package android:name="me.phh.superuser" />
</queries>

물론, 이것은 다른 모든 해결책들과 마찬가지로 여전히 추측입니다.예를 들어 장치를 루팅하지 않고도 Magisk를 설치할 수 있습니다.

2021년 마지막 분기인 오늘 @Himanshi Takur님의 답변에 대해 SafetyNet을 사용하려고 했습니다.하지만 저는 문제가 생겨서 여기서 질문을 열었습니다.여전히 답이 없습니다.

그래서 루트비어를 쓰기로 했습니다.완벽하게 작동하지만 Magisk가 뿌리를 숨기면 작동하지 않습니다.

이 경우를 신경 쓰지 않는 경우(많은 은행 앱에서도 이 문제를 해결할 수 없음) 다음 단계를 사용할 수 있습니다.

  1. Gradle에 추가:
implementation 'com.scottyab:rootbeer-lib:0.1.0'
  1. 다음 행을 사용합니다.
RootBeer rootBeer = new RootBeer(context);
if (rootBeer.isRooted()) {
    //we found indication of root
} else {
    //we didn't find indication of root
}
if [[ "`adb shell which su | grep -io "permission denied"`" != "permission denied" ]]; then
   echo "Yes. Rooted device."
 else
   echo "No. Device not rooted. Only limited tasks can be performed. Done."
    zenity --warning --title="Device Not Rooted" --text="The connected Android Device is <b>NOT ROOTED</b>. Only limited tasks can be performed." --no-wrap
fi

루트 앱과 하위 바이너리를 탐지하는 것은 모두 잊어버립니다.루트 데몬 프로세스를 확인합니다.이 작업은 단말기에서 수행할 수 있으며 앱 내에서 터미널 명령을 실행할 수 있습니다.이거 원라이너로 해보세요.

if [ ! "$(/system/bin/ps -A | grep -v grep | grep -c daemonsu)" = "0" ]; then echo "device is rooted"; else echo "device is not rooted"; fi

이 작업을 수행하려면 루트 권한도 필요하지 않습니다.

편집: 이제 더 나은 탐지를 위해 이 방법을 사용합니다!

if [ $(ps -A | grep -e ^shell -e ^root | grep -v "\[" | tr -s ' ' | cut -d ' ' -f 9 | grep -c su) ] || [ $(which su) ]; then echo 'rooted'; else echo 'not rooted'; fi

Google SafetyNet 증명 API를 사용하면 장치가 루트인지 쉽게 확인할 수 있습니다.

  1. build.gradle(:app)에 종속성 추가

    구현 'com.google.gms:play-services-safetynet:17.0.0'

  2. 링크를 사용하여 API 키를 가져오고 Android Device Verification API 사용

  3. public static void sendSafetyNetRequest(활동 컨텍스트) {

     if(GoogleApiAvailability.getInstance().isGooglePlayServicesAvailable(context, 13000000) == ConnectionResult.SUCCESS) {
         Log.e(TAG, "The SafetyNet Attestation API is available");
    
         // TODO(developer): Change the nonce generation to include your own, used once value,
         // ideally from your remote server.
    
         String nonceData = "Safety Net Sample: " + System.currentTimeMillis();
         ByteArrayOutputStream byteStream = new ByteArrayOutputStream();
         Random mRandom = new SecureRandom();
    
         byte[] bytes = new byte[24];
         mRandom.nextBytes(bytes);
         try {
             byteStream.write(bytes);
             byteStream.write(nonceData.getBytes());
         } catch (IOException e) {
             e.printStackTrace();
         }
    
         byte[] nonce = byteStream.toByteArray();
    
         SafetyNetClient client = SafetyNet.getClient(context);
         Task<SafetyNetApi.AttestationResponse> task = client.attest(nonce, API_KEY_FROM_STEP_2_LINK);
    
         task.addOnSuccessListener(context, attestationResponse -> {
    
    
           /*
                  TODO(developer): Forward this result to your server together with
                  the nonce for verification.
                  You can also parse the JwsResult locally to confirm that the API
                  returned a response by checking for an 'error' field first and before
                  retrying the request with an exponential backoff.
                  NOTE: Do NOT rely on a local, client-side only check for security, you
                  must verify the response on a remote server!
                 */
    
             String jwsResult = attestationResponse.getJwsResult();
    
             Log.e(TAG, "Success! SafetyNet result:\n" + jwsResult + "\n");
    
             if (jwsResult == null) {
                 Log.e(TAG, "jwsResult Null");
    
             }
             final String[] jwtParts = jwsResult.split("\\.");
    
             if (jwtParts.length == 3) {
                 String decodedPayload = new String(Base64.decode(jwtParts[1], Base64.DEFAULT));
                 Log.e(TAG, "decodedPayload :     " + decodedPayload);
             }
    
    
         });
    
         task.addOnFailureListener(context, e -> {
             // An error occurred while communicating with the service.
             String mResult = null;
    
             if (e instanceof ApiException) {
                 // An error with the Google Play Services API contains some additional details.
                 ApiException apiException = (ApiException) e;
    
                 Util.showLog(TAG, "Error: " +
                         CommonStatusCodes.getStatusCodeString(apiException.getStatusCode()) + ": " +
                         apiException.getStatusMessage());
             } else {
                 // A different, unknown type of error occurred.
                Log.e(TAG, "ERROR! " + e.getMessage());
             }
    
         });
    
     } else {
      Log.e(TAG, "Prompt user to update Google Play services.";
    
     }
    

    } `

  4. 로그에서 디코딩된 Payload iftsProfileMatch 및 basic을 확인합니다.무결성 둘 다 거짓입니다. 이는 장치가 루트임을 의미합니다.증명 API는 다음과 같은 JWS 응답을 반환합니다.

{ "nonce": "6pLrr9zWyl6TNzj+kpbR4LZcfPY3U2FmZXR5IE5ldCBTYW1wbGU6IDE2MTQ2NzkwMTIzNjc=", "timestampMs": 9860437986543, "apkPackageName": " your package name will be displayed here", "ctsProfileMatch": true, "apkDigestSha256": [ "base64 encoded, SHA-256 hash of the certificate used to sign requesting app" ], "basicIntegrity": true, "evaluationType": "BASIC" }

status of ctsProfileMatch and basicIntegrity 자세한 내용은 이 링크를 참조하십시오.

Google Play 서비스Safety Net 증명 API를 사용하여 장치를 평가하고 장치가 루트/조작되었는지 여부를 확인할 수 있습니다.

근본적인 장치를 처리하려면 제 답변을 검토해 주십시오.
https://stackoverflow.com/a/58304556/3908895

타사 라이브러리나 임의 솔루션을 사용하지 않으려면 Google lib를 사용하여 검색하십시오.

Android 장치 확인

응답:

{
  "timestampMs": 9860437986543,
  "nonce": "R2Rra24fVm5xa2Mg",
  "apkPackageName": "com.package.name.of.requesting.app",
  "apkCertificateDigestSha256": ["base64 encoded, SHA-256 hash of the
                                  certificate used to sign requesting app"],
  "ctsProfileMatch": true,
  "basicIntegrity": true,
}

ctsProfileMatch 장치가 루트인 경우 false를 제공합니다.

참조 링크: [1]: https://developer.android.com/training/safetynet/attestation

다음 코드를 사용하여 이 작업을 수행할 수 있습니다.

public boolean getRootInfo() {
    if (checkRootFiles() || checkTags()) {
        return true;
    }
    return false;
}

private boolean checkRootFiles() {
    boolean root = false;
    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) {
        root = new File(path).exists();
        if (root)
            break;
    }
    return root;
}

private boolean checkTags() {
    String tag = Build.TAGS;
    return tag != null && tag.trim().contains("test-keys");
}

RootBeer 라이브러리도 확인할 수 있습니다.

2021년(오늘) 현재, 특히 Magisk와 같은 강력한 은닉 도구가 있는 경우 루트를 탐지할 수 있는 신뢰할 수 있는 방법이나 방법이 없는 것으로 보입니다.숨기기를 사용할 수 있습니다.여기서 대부분의 답변은 더 이상 관련이 없으므로 프로덕션에서 사용하지 마십시오.SafetyNet과 같은 검증된 검사에 의존하고 루트를 탐지하기 위해 추가 마일을 가는 대신 디버거/인스톨레이션을 방지하고 난독화를 사용하는 것과 같은 런타임 모두에서 앱을 보호할 것을 제안합니다.

사실 그것은 흥미로운 질문이고 지금까지 아무도 상을 받을 자격이 없습니다.다음 코드를 사용합니다.

  boolean isRooted() {
      try {
                ServerSocket ss = new ServerSocket(81);
                ss.close();
                                    return true;
            } catch (Exception e) {
                // not sure
            }
    return false;
  }

코드는 확실히 방탄이 아닙니다. 네트워크를 사용할 수 없기 때문에 예외가 발생할 수 있습니다.이 방법이 true를 반환하면 99%이고, 그렇지 않으면 50%만 true를 반환하면 됩니다.네트워킹 권한도 솔루션을 손상시킬 수 있습니다.

루트박스에 있는 내 라이브러리를 사용하면 꽤 쉽습니다.아래의 필수 코드를 확인하십시오.

    //Pass true to <Shell>.start(...) call to run as superuser
    Shell shell = null;
    try {
            shell = Shell.start(true);
    } catch (IOException exception) {
            exception.printStackTrace();
    }
    if (shell == null)
            // We failed to execute su binary
            return;
    if (shell.isRoot()) {
            // Verified running as uid 0 (root), can continue with commands
            ...
    } else
            throw Exception("Unable to gain root access. Make sure you pressed Allow/Grant in superuser prompt.");

언급URL : https://stackoverflow.com/questions/1101380/determine-if-running-on-a-rooted-device

반응형