루팅 된 기기에서 실행 중인지 확인


292

내 응용 프로그램에는 루트를 사용할 수있는 장치에서만 작동하는 특정 기능이 있습니다. 이 기능을 사용할 때 실패한 다음 사용자에게 적절한 오류 메시지를 표시하는 대신 루트를 먼저 사용할 수 있는지 자동으로 확인하고 그렇지 않은 경우 먼저 해당 옵션을 숨기는 기능을 선호합니다 .

이 방법이 있습니까?


11
그렇게 할 수있는 확실한 방법은 없습니다. 아래의 답변은 일반적인 특성을 확인하지만 주어진 장치가 일반적인 방식으로 뿌리를 내리지 못할 수 있습니다. 루트 검사가 널리 퍼지면 루트 솔루션이 스스로 숨기려고 노력할 것입니다. 운영 체제 동작을 수정할 수 있기 때문에 많은 옵션이 있습니다.
Chris Stratton

전반적인 경험에 모호성을 더하는 앱의 기능을 숨기지 않고 사용자에게 더 많은 정보를 제공하는 루트 기능이 없기 때문에 기능을 사용할 수 없음을 나타내는 것이 좋습니다.
닉 폭스

아래 답변은 시스템 리스 루트에 적용 됩니까?
Piyush Kukadiya

답변:


260

다음은 세 가지 방법 중 하나를 루트로 검사하는 클래스입니다.

/** @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();
        }
    }
}

8
두 질문에 동일한 답변이 필요한 경우 시간 중복의 99 %이므로 두 질문에 동일한 답변을 게시하는 대신 속임수로 표시합니다. 감사.
Kev

2
그럴 수도 있지만 커뮤니티에서 정확한 중복 답변이 표시 되어 있음을 알려드립니다 . 답변을 조정하고 OP 문제의 세부 사항을 해결해야합니다. 복사 및 붙여 넣기 답변은 다운 보트를 유치 할 위험이 있습니다.
Kev

9
-1, 일부 전화는 su루팅하지 않고 바이너리를 포함하기 때문에이 방법을 사용할 수 없습니다 .
neevek

12
Fox Digital Copy (Beta) 앱은 checkRootMethod1 / 2 / 3 메소드뿐만 아니라 Root 및 ExecShell 클래스를 포함하여 거의 그대로의 코드를 사용합니다. 매우 재미있었습니다.
Matt Joseph

8
폭스가 셀 수없이 많은 사람을 고소한 것처럼 그들을 고소 할 수 있습니까?
케빈 파커

58

Fabric / 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();
        }
    }
}

최고의 답변. 이 라이브러리를 모든 라이브러리에서 사용하십시오. 중국어 장치에서 많은 오 탐지가 실행되고 있습니다.
Pedro Paulo Amorim

이 방법에 오 탐지가 있습니까?
Ehsan Mashhadi 2018 년

download.chainfire.eu/363/CF-Root/CF-Auto-Root/…를 사용 하여 nexus 5에서 이것을 테스트했지만 정확하지 않습니다.
Jeffrey Liu

54

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

RootTools.isRootAvailable()

참고


10
isRootAvailable ()은 경로 및 일부 하드 코딩 된 디렉토리에 su가 있는지 확인합니다. 나는 뿌리를 내리지 않는 도구가 남아 있다고 들었습니다.
밥 화이트 맨

13
RootTools.isAccessGiven ()은 루트를 검사 할뿐만 아니라 루트 권한도 요청합니다. 따라서 루팅되지 않은 장치는 항상이 방법으로 false를 반환합니다.
집합

2
@ aggregate1166877, 당신 말이 맞지만, 충분하지 않습니다. 물어볼 때 루트 권한이 필요하지 않으면 어떻게합니까? 나는 그것이 뿌리가되어 있는지 알고 싶어하지만 지금은 루트 권한이 필요하지 않습니다.
neevek

4
장치가 루팅 된 장치 인 경우에도 사용자가 권한을 거부하면 isAccessGiven ()은 false를 반환합니다.
subair_a

이것은 내가 투표권을 얻는 유일한 답입니다. 그냥 붙여 넣기 복사 유사한 무언가를 원하는 경우 아래 내 대답을 확인하거나 좀 더 자세한 내용을 원하는
rsimp

52

내 응용 프로그램에서 "su"명령을 실행하여 장치가 루팅되었는지 여부를 확인했습니다. 그러나 오늘 나는 내 코드 의이 부분을 제거했습니다. 왜?

내 응용 프로그램이 메모리 킬러가 되었기 때문입니다. 어떻게? 내 이야기를 들려 드리겠습니다.

내 응용 프로그램이 장치 속도를 늦추고 있다는 불만이있었습니다 (물론 그것이 사실이 아니라고 생각했습니다). 나는 왜 그런지 알아 내려고 노력했다. 그래서 나는 MAT를 사용하여 힙 덤프를 얻고 분석했으며 모든 것이 완벽 해 보였습니다. 그러나 내 앱을 여러 번 다시 시작한 후 장치가 실제로 느려지고 응용 프로그램을 중지해도 장치가 다시 시작되지 않는 한 더 빠르지 않다는 것을 알았습니다. 장치가 매우 느린 동안 덤프 파일을 다시 분석했습니다. 그러나 모든 것이 여전히 덤프 파일에 완벽했습니다. 그런 다음 처음에해야 할 일을했습니다. 프로세스를 나열했습니다.

$ adb shell ps

놀람; 내 응용 프로그램에 대한 많은 프로세스가있었습니다 (내 응용 프로그램의 프로세스 태그가 매니페스트에 있음). 그들 중 일부는 좀비 였고 일부는 그렇지 않았습니다.

단일 액티비티가 있고 "su"명령 만 실행하는 샘플 응용 프로그램을 사용하면 응용 프로그램을 시작할 때마다 좀비 프로세스가 생성되고 있음을 깨달았습니다. 처음 에이 좀비는 0KB를 할당하지만 무언가가 발생하는 것보다 좀비 프로세스는 내 응용 프로그램의 주요 프로세스와 거의 동일한 KB를 보유하고 있으며 표준 프로세스가되었습니다.

bugs.sun.com에 동일한 문제에 대한 버그 보고서가 있습니다. http://bugs.sun.com/view_bug.do?bug_id=6474073 명령을 찾을 수없는 좀비 간부 만들 수 위하여려고하는 경우에이 설명 () 메소드 . 그러나 나는 왜 왜 그리고 어떻게 그들이 표준 프로세스가되고 중요한 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 ()은 동일한 문제를 일으 킵니다.


5
매우 걱정입니다. 나는 똑같은 일을하는 루팅 된 장치 감지 클래스를 가지고 있습니다. 때때로 좀비 프로세스가 남겨지고 장치 속도가 느려지는 경우 등
AWT

1
GT-S5830i android 2.3.6에서 RootTools 3.4의 문제를 확인했습니다. 좀비의 대부분은 메모리를 할당 받았으며 문제는 체계적입니다. 3-4 테스트 후 장치를 다시 시작해야합니다. 테스트 결과를 공유 환경 설정에 저장하는 것이 좋습니다.
그리스도

2
Google은 이제 ProcessBuilder () 및 start () 명령을 사용하는 것이 좋습니다 .
EntangledLoops

1
@NickS 재미있는데, 어떤 명령을 시작하셨습니까?
EntangledLoops

1
@EntangledLoops. 감사합니다. 내 바이너리를 시작하고 stdin / stdout을 통해 상호 작용합니다. 나는 그것을 멈추는 방법을 다시 확인하고 한 가지 경우에 Process.destroy ()를 놓쳤다는 것을 알았습니다. 그래서 좀비는 없습니다.
Nick S

36

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

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

StericsonRootTools 라이브러리는 더 합법적으로 루트를 확인하는 것 같습니다. 또한 추가 도구와 유틸리티가 많이 있으므로 강력히 권장합니다. 그러나 루트를 구체적으로 확인하는 방법에 대한 설명은 없으며 대부분의 앱이 실제로 필요한 것보다 약간 무거울 수 있습니다.

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"이므로 사용자 ID가 실제로 0 (루트)인지 확인할 수 있습니다.

루트 액세스 권한이 부여되었는지 확인하는 샘플 방법은 다음과 같습니다.

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"실행 파일이 있는지 확인하는 것이 중요합니다. 이러한 고스트 프로세스는 시간이 지남에 따라 메모리 소비를 증가시킬 수 있습니다.


isRootAvailable () 메소드는 훌륭하게 작동합니다. 감사합니다. 그러나 AsyncTask에서 호출하는 것과 같은 ANR을 피하기 위해 메인 스레드에서 이것을 실행하지 않는 것이 좋습니다
Thunderstick

1
루트를 사용할 수 없도록하고 싶을 때와 루트를 사용하려는 것 사이의 차이점이라고 생각합니다. 장치가 루팅되지 않도록하려면 제안 된 검사가 좋습니다. 오 탐지가 발생하지만 손상된 장치에서 코드를 실행하지 않는 것이 좋습니다.
Jeffrey Blattman

1
@ DAC84 질문을 잘 모르겠습니다. isRootGiven을 실행하고 루팅 애플리케이션에서 거부하면 false를 리턴해야합니다. 그게 무슨 일이야? 경고를 피하려면 isSUotist라는 이름의 isRootAvailable을 사용하면됩니다. 루트 응용 프로그램에서 루트를 자유롭게 제공하고 관리하지 않도록 구성 할 수도 있습니다.
rsimp 2019

1
@BeeingJk는 아닙니다. 실제 테스트 인 su를 실행하지 않고도 확인할 수는 있지만 실제로는 아닙니다. 그래도 실행하기 전에 PATH에서 su를 확인해야합니다. 그러나 실제로 su를 실행하면 종종 토스트 메시지 또는 루트 관리 앱과의 상호 작용이 발생하여 원하는 것이 아닐 수 있습니다. 당신 자신의 논리를 위해 당신은 단지 su의 존재만으로 간주 될 수 있습니다. 이것은 여전히 ​​실행 가능하지만 잠금 액세스를 포함 할 수있는 일부 에뮬레이터에서 오탐 (false positive)을 줄 수 있습니다.
rsimp

1
@BeeingJk isRootAvailable은 아마도 당신이 필요로하는 모든 것이지만, 내가 만들고자하는 요점은 그런 이름이나 doSUExist가 isDeviceRooted와 같은 메소드 이름보다 더 나은 의미를 제공한다는 것입니다. 계속 진행하기 전에 전체 루트 액세스를 확인해야하는 경우 isRootGiven
rsimp

35

2017 업데이트

이제 Google Safetynet API로 할 수 있습니다 . SafetyNet API는 앱이 실행되는 Android 환경의 보안 및 호환성을 평가하는 데 도움이되는 증명 API를 제공합니다.

이 증명은 특정 장치가 변조되었거나 다른 방식으로 수정되었는지 여부를 확인하는 데 도움이됩니다.

증명 API는 다음과 같은 JWS 응답을 반환합니다.

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

이 응답을 구문 분석하면 장치가 루팅되었는지 여부를 판별하는 데 도움이 될 수 있습니다.

루팅 된 장치는 ctsProfileMatch = false를 일으키는 것으로 보입니다.

클라이언트 측에서는 할 수 있지만 서버 측에서는 파싱 응답이 권장됩니다. 안전망 API를 사용한 기본 클라이언트 서버 아키텍처는 다음과 같습니다.

여기에 이미지 설명을 입력하십시오


3
훌륭한 정보이며 다른 맥락에서 이것이 정답이라고 생각합니다. 불행히도 OP 질문은 안전하지 않은 환경에서 앱을 방어하는 것이 아니라 앱에서 루트 전용 기능을 활성화하기 위해 루트를 감지하는 것입니다. OP가 의도 한 목적을 위해이 프로세스는 지나치게 복잡해 보입니다.
rsimp

31

Java 레벨의 루트 점검은 안전한 솔루션이 아닙니다. 앱에 루팅 된 기기에서 실행되는 보안 문제가있는 경우이 솔루션을 사용하십시오.

전화에 RootCloak과 같은 앱이 없으면 Kevin의 답변이 작동합니다. 전화가 루팅되면 이러한 앱에 Java API 처리 기능이 있으며 전화가 루팅되지 않도록 이러한 API를 조롱합니다.

Kevin의 답변을 기반으로 기본 수준의 코드를 작성했으며 RootCloak에서도 작동합니다! 또한 메모리 누수 문제를 일으키지 않습니다.

#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;
     }

1
루트 감지는 두 가지 범주로 분류되어 루트 종속 기능을 활성화 한 다음 루팅 된 전화기의 보안 문제를 완화하기위한 보안 기반 조치를 사용할 수 있습니다. 루트 종속 기능의 경우 Kevin의 대답이 상당히 좋지 않습니다. 이 답변의 맥락에서 이러한 방법은 더 의미가 있습니다. 비록 방법 2를 사용하지 않고 PATH 환경 변수를 반복하여 "su"를 검색하기 위해 방법 2를 다시 작성하지만. "전화 중"인 것은 아닙니다.
rsimp

이 C 코드를 Java에서 사용하는 방법의 예를 제공 할 수 있습니까?
mrid

@mrid Android에서 Java로 JNI 호출을 수행하는 방법을 확인하십시오.
Alok Kulkarni

이 방법은 RootCloak 앱을 사용하여 루트 감지 우회를 방지합니다. 이 방법에 실패한 알려진 루트 우회 기술이 있습니까?
Nidhin

20

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");
}

감사! Kevin의 AnswercheckRootMethod4() 와 같이 이것을 사용하고 있습니다.
Sheharyar

1
절대 추가되지 == true는 아무것도 추가하지 않고 잘 보이지 않는, 부울에.
minipif

2
@smoothBlue 왜 그런가요? DevrimTuncer의 솔루션은 프로세스를 생성하지 않습니다.
FD_

1
더 나은 아이디어는 일반적인 PATH 디렉토리를 하드 코딩하는 대신 PATH를 반복하는 것입니다.
rsimp

1
if (isRooted())명시 적으로 true를 쓰지 말고 사용하십시오 . 코드 작성 패턴을 따르는 것이 좋습니다
blueware

13

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

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

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

참고


그러나 사용자는 루트 액세스 권한을 부여해야합니까? 내 목표는 기기가 루팅 된 경우 앱 실행을 중지하는 것이면 옵션이 실제로 제한됩니다
Nasz Njoka Sr.

11

이 목적으로 시스템 속성 을 설정하는 데 사용되는 일부 수정 된 빌드 ro.modversion. 일이 진행된 것 같습니다. 몇 달 전에 TheDude에서 내 빌드에는 다음이 있습니다.

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 이미지를 실행하는 1.5 SDK의 에뮬레이터에는 루트가 있으며 아마도 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있습니다. 아마도 그것이 가장 신뢰할만한 자산이라고 생각합니다.


흥미롭게 보이지만 에뮬레이터 (및 ADP)는 루트 자체를 허용하지만 응용 프로그램에서이를 사용할 수는 없습니다. 예 : $ su app_29 $ su su : uid 10029 su 허용되지 않음
miracle2k

아, 나는 그들이하지 않을 것이라고 생각합니다 ... google.com으로 끝나는 ro.build.host 검사와 결합 할 수는 없습니다. 테스트 키가 있지만 유일하게 블록 키가없는 경우 사용자에게 묻는다. 최신 장치의 빌드 호스트가 무엇인지, 전화가 아닌 것은 쉽지 않습니다.
Chris Boyle

11

RootBeer 는 Scott과 Matthew의 루트 검사 안드로이드 라이브러리입니다. 다양한 검사를 통해 장치가 루팅되었는지 여부를 나타냅니다.

자바 검사

  • CheckRootManagementApps

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

  • CheckRootCloakingApps

  • CheckTestKeys

  • checkForDangerousProps

  • checkForBusyBoxBinary

  • checkForSuBinary

  • checkSuExists

  • checkForRWSystem

기본 점검

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

  • checkForSuBinary

8

루트 감지에 기본 코드를 사용하는 것이 좋습니다. 다음은 전체 예제 입니다.

여기에 이미지 설명을 입력하십시오

자바 래퍼 :

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 래퍼 (native-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;
}

4
멋진 도구, Dima. 고마워 그것은 심지어 magisk를 잡는다.
전문가

이것은 실제 거래입니다.
Vahid Amiri

@klutch 내 게시물의 첫 번째 줄에서 작업 예 (GitHub의)에 대한 링크가
디마 Kozhevin

7

여기에 몇 가지 답변을 기반으로 한 코드가 있습니다.

 /**
   * 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;
  }

7

@Kevins의 답변에 덧붙여 최근에 그의 시스템을 사용하는 동안 Nexus 7.1이 false세 가지 방법 모두에 대해 반환되는 것을 발견했습니다 - which명령 없음 , 없음 test-keysSuperSU에 설치되지 않았습니다 /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;
    }
}

이것은 약간입니다 적게 는 SuperSU는 SU에 액세스 할 수없는 장치에 설치하기에 완전하게 가능으로 (당신이 보장 루트 액세스를 필요로하는 경우)에 유용 상황이다.

그것을 가지고하는 것이 가능하기 때문에, SuperSU는 및 근무하지만 설치 하지 에서 /system/app디렉토리,이 여분의 경우 의지 루트 (하하) 같은 경우 아웃.


장치에 설치할 수있는 다른 루트 패키지가 있으므로 좋은 대답이 아닙니다. 다른 응용 프로그램 패키지를 하드 코딩하면 모든 응용 프로그램 패키지를 예상하고 나열 할 수 없으므로
까다로울

5
    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);
        }

4

장치가 앱에서 루트 기능을 지원하는지 확인하려는 경우 두 가지 추가 아이디어 :

  1. 'su'바이너리가 있는지 확인하십시오. Runtime.getRuntime().exec()
  2. /system/app/Superuser.apk위치 에서 SuperUser.apk를 찾으십시오.

3

사용자가 RootCloak와 같이 루트를 숨기는 응용 프로그램을 사용하는 경우에도 ndk와 함께 C ++을 사용하는 것이 루트를 감지하는 가장 좋은 방법입니다. 이 코드를 RootCloak로 테스트했으며 사용자가 루트를 숨기려고해도 루트를 감지 할 수있었습니다. 따라서 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();
}

1
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


1

루트 앱과 su 바이너리를 탐지하는 모든 것을 잊어 버려라. 루트 데몬 프로세스를 확인하십시오. 이것은 터미널에서 수행 할 수 있으며 앱 내에서 터미널 명령을 실행할 수 있습니다. 이 하나의 라이너를 사용해보십시오.

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

이것을 달성하기 위해 루트 권한이 필요하지 않습니다.


0

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

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

네트워크를 사용할 수 없으므로 예외가 발생하기 때문에 코드는 확실히 방탄되지 않습니다. 이 메소드가 true를 리턴하면 99 %를 확신 할 수 있습니다. 그렇지 않으면 50 % 만 그렇지 않습니다. 네트워킹 권한도 솔루션을 망칠 수 있습니다.


나는 이것을 테스트했지만 루팅 된 장치로 true를 반환하지 않습니다.
트릭

어떤 종류의 예외가 발생하는지 보는 것이 흥미 롭습니다. 포트 바운드 예외가 이미 발생할 수 있지만 1024 미만의 범위에서 서버 포트를 만들 수없는 경우 여전히 특정 제한이 있기 때문에 루팅 값이 감소합니다.
Singagirl

-1

rootbox 에서 내 라이브러리를 사용하면 매우 쉽습니다. 아래에서 필요한 코드를 확인하십시오.

    //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.");
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.