Xác định nếu chạy trên thiết bị đã root


292

Ứng dụng của tôi có một phần chức năng nhất định sẽ chỉ hoạt động trên thiết bị có sẵn root. Thay vì làm cho tính năng này bị lỗi khi nó được sử dụng (và sau đó hiển thị thông báo lỗi thích hợp cho người dùng), tôi muốn kiểm tra một cách âm thầm nếu root có sẵn trước, và nếu không, hãy ẩn các tùy chọn tương ứng ở vị trí đầu tiên .

Có cách nào để làm việc này không?


11
Không có cách đáng tin cậy để làm như vậy; các câu trả lời dưới đây kiểm tra các đặc điểm chung nhưng một thiết bị nhất định có thể không được bắt nguồn theo cách chung. Nếu việc kiểm tra root trở nên phổ biến, các giải pháp root có thể sẽ bắt đầu nỗ lực che giấu bản thân. Vì họ có thể sửa đổi hành vi của hệ điều hành, họ có rất nhiều tùy chọn để làm như vậy.
Chris Stratton

Có thể tốt hơn để chỉ ra rằng chức năng này không khả dụng do thiếu khả năng root cung cấp thêm thông tin cho người dùng thay vì che giấu các khả năng của ứng dụng của bạn thêm sự mơ hồ vào trải nghiệm chung.
nick cáo

Có câu trả lời dưới đây làm việc cho Systemless Root ?
Piyush Kukadiya

Câu trả lời:


260

Đây là một lớp sẽ kiểm tra Root một trong ba cách.

/** @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
Nếu hai câu hỏi đảm bảo câu trả lời giống hệt nhau thì chúng là 99% số lần trùng lặp, vì vậy hãy gắn cờ dưới dạng bản sao thay vì đăng cùng một câu trả lời trên cả hai. Cảm ơn.
Kev

2
Điều đó có thể là như vậy, tuy nhiên tôi chỉ cho bạn biết rằng các câu trả lời trùng lặp chính xác được cộng đồng gắn cờ . Bạn nên điều chỉnh câu trả lời của mình và giải quyết các chi tiết cụ thể về vấn đề của OP. Sao chép và dán câu trả lời có nguy cơ từ việc thu hút downvote.
Kev

9
-1, phương pháp này không khả thi, vì một số điện thoại bao gồm sunhị phân trong khi không được root.
neevek

12
Chỉ muốn cho bạn biết, ứng dụng Fox Digital Copy (Beta) sử dụng mã của bạn gần như nguyên văn, bao gồm các lớp Root và ExecShell, cũng như các phương thức checkRootMethod1 / 2/3. Tìm thấy nó rất thú vị.
Matt Joseph

8
Tôi có thể kiện họ như Fox kiện vô số người khác không?
Kevin Parker

58

Nếu bạn đang sử dụng Crashlytics Fabric / Firebase, bạn có thể gọi

CommonUtils.isRooted(context)

Đây là cách thực hiện hiện tại của phương pháp đó:

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

Câu trả lời hay nhất đến nay. Vui lòng sử dụng thư mục này trên bất kỳ thư viện nào, có rất nhiều lỗi tích cực chạy trên thiết bị Trung Quốc.
Pedro Paulo Amorim

Có bất kỳ dương tính giả trong phương pháp này?
Ehsan Mashhadi

Tôi đã thử nghiệm điều này trên nexus 5 với download.chainfire.eu/363/CF-Root/CF-Auto-Root/ Kẻ , điều này không chính xác.
Jeffrey Liu

54

Thư viện RootTools cung cấp các phương thức đơn giản để kiểm tra root:

RootTools.isRootAvailable()

Tài liệu tham khảo


10
isRootAv Available () chỉ kiểm tra sự tồn tại của su trong đường dẫn và một số thư mục được mã hóa cứng khác. Tôi đã nghe nói rằng một số công cụ bỏ phiếu sẽ để lại su ở đó, vì vậy điều này sẽ cho kết quả dương tính giả.
Bob Whiteman

13
RootTools.isAccessGiven () sẽ không chỉ kiểm tra root mà còn yêu cầu quyền root; vì vậy một thiết bị không được phân phối sẽ luôn trả về false với phương thức này.
tổng

2
@ tổng hợp1166877, bạn đúng, nhưng nó không đủ tốt, nếu tôi không yêu cầu quyền root khi tôi yêu cầu thì sao? Tôi chỉ muốn biết nếu nó được root, nhưng tôi không cần sự cho phép root vào lúc này.
neevek

4
isAccessGiven () trả về false khi người dùng từ chối quyền mặc dù thiết bị đã được root.
subair_a

Đây là câu trả lời duy nhất tôi thấy có giá trị bỏ phiếu. Kiểm tra câu trả lời của tôi dưới đây nếu bạn muốn một cái gì đó tương tự như chỉ sao chép dán vào, hoặc muốn biết thêm chi tiết
rsimp

52

Trong ứng dụng của mình, tôi đã kiểm tra xem thiết bị có được root hay không bằng cách thực hiện lệnh "su". Nhưng hôm nay tôi đã xóa phần mã này. Tại sao?

Bởi vì ứng dụng của tôi đã trở thành một kẻ giết người bộ nhớ. Làm sao? Hãy để tôi nói cho bạn nghe câu chuyện của tôi.

Có một số phàn nàn rằng ứng dụng của tôi làm chậm thiết bị (Tất nhiên tôi nghĩ điều đó không thể là sự thật). Tôi đã cố gắng tìm hiểu tại sao. Vì vậy, tôi đã sử dụng MAT để lấy các đống và phân tích, và mọi thứ dường như hoàn hảo. Nhưng sau khi khởi chạy lại ứng dụng của tôi nhiều lần, tôi nhận ra rằng thiết bị đang thực sự chậm hơn và việc dừng ứng dụng của tôi không làm cho nó nhanh hơn (trừ khi tôi khởi động lại thiết bị). Tôi đã phân tích các tập tin kết xuất một lần nữa trong khi thiết bị rất chậm. Nhưng mọi thứ vẫn hoàn hảo cho tập tin kết xuất. Sau đó, tôi đã làm những gì phải được thực hiện đầu tiên. Tôi liệt kê các quy trình.

$ adb shell ps

Ngất ngây; có nhiều quy trình cho ứng dụng của tôi (với thẻ quy trình của ứng dụng của tôi ở tệp kê khai). Một số trong số họ là zombie một số trong số họ không.

Với một ứng dụng mẫu có một Hoạt động duy nhất và thực hiện lệnh "su", tôi nhận ra rằng một quy trình zombie đang được tạo ra trong mỗi lần khởi chạy ứng dụng. Lúc đầu, những thây ma này phân bổ 0KB nhưng hơn là có gì đó xảy ra và các quy trình zombie đang giữ gần như KB như quy trình chính của ứng dụng của tôi và chúng trở thành các quy trình nổi bật.

Có một báo cáo lỗi cho cùng một vấn đề trên bug.sun.com: http://bugs.sun.com/view_orms.do?orms_id=6474073 điều này giải thích nếu không tìm thấy lệnh zombie sẽ được tạo bằng phương thức exec () . Nhưng tôi vẫn không hiểu tại sao và làm thế nào họ có thể trở thành các quy trình nổi bật và nắm giữ KB đáng kể. (Điều này không xảy ra mọi lúc)

Bạn có thể thử nếu bạn muốn với mẫu mã bên dưới;

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

Phương pháp thực hiện lệnh đơn giản;

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) {
            }
        }
    }
}

Tóm lại; Tôi không có lời khuyên nào cho bạn để xác định xem thiết bị có được root hay không. Nhưng nếu tôi là bạn, tôi sẽ không sử dụng Runtime.getR nb (). Exec ().

Nhân tiện; RootTools.isRootAv Available () gây ra vấn đề tương tự.


5
Điều đó rất đáng lo ngại. Tôi đã có một lớp phát hiện thiết bị gốc đã làm điều tương tự - sau khi đọc nó, tôi đã xác nhận những gì aegean chi tiết ở trên. Thỉnh thoảng các quá trình zombie bị bỏ lại, chậm thiết bị, v.v ...
AWT

1
Tôi xác nhận sự cố với RootTools 3.4 trên Android GT.6 S5830i. Hầu hết các zombie được phân bổ bộ nhớ và vấn đề là có hệ thống. Tôi cần khởi động lại thiết bị sau 3-4 thử nghiệm. Tôi khuyên bạn nên lưu kết quả kiểm tra vào sở thích được chia sẻ.
Chúa Kitô

2
Google hiện khuyên bạn nên sử dụng ProcessBuilder () và lệnh start ().
EntangledLoops

1
@NickS Thú vị, nhưng bạn đã khởi chạy lệnh nào? Tôi không gặp vấn đề tương tự ở đây khi ban hành lệnh trên nhiều điện thoại Android có cấp API khác nhau từ 9 - 23.
EntangledLoops

1
@EntangledLoops. Cảm ơn bạn. Tôi khởi chạy nhị phân của riêng mình và tương tác với nó thông qua stdin / stdout. Tôi đã kiểm tra lại cách tôi dừng nó và phát hiện ra rằng tôi đã bỏ lỡ Process.destroy () trong một trong các trường hợp. Vì vậy, không có zombie.
Nick S

36

Nhiều câu trả lời được liệt kê ở đây có vấn đề cố hữu:

  • Kiểm tra các khóa kiểm tra có tương quan với quyền truy cập root nhưng không nhất thiết đảm bảo nó
  • Các thư mục "PATH" phải được lấy từ biến môi trường "PATH" thực tế thay vì được mã hóa cứng
  • Sự tồn tại của "su" thực thi không nhất thiết có nghĩa là thiết bị đã được root
  • "Mà" thực thi có thể hoặc không thể được cài đặt, và bạn nên để hệ thống giải quyết đường dẫn của nó nếu có thể
  • Chỉ vì ứng dụng SuperUser được cài đặt trên thiết bị không có nghĩa là thiết bị đã có quyền truy cập root

Các RootTools thư viện từ Stericson dường như được kiểm tra gốc hợp pháp hơn. Nó cũng có rất nhiều công cụ và tiện ích bổ sung vì vậy tôi đánh giá cao nó. Tuy nhiên, không có lời giải thích nào về cách nó đặc biệt kiểm tra root và nó có thể nặng hơn một chút so với hầu hết các ứng dụng thực sự cần.

Tôi đã thực hiện một vài phương thức tiện ích dựa trên thư viện RootTools. Nếu bạn chỉ đơn giản muốn kiểm tra xem "su" thực thi có trên thiết bị hay không, bạn có thể sử dụng phương pháp sau:

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

Phương pháp này chỉ đơn giản là lặp qua các thư mục được liệt kê trong biến môi trường "PATH" và kiểm tra xem tệp "su" có tồn tại trong một trong số chúng không.

Để thực sự kiểm tra quyền truy cập root, lệnh "su" phải thực sự được chạy. Nếu một ứng dụng như SuperUser được cài đặt, thì tại thời điểm này, ứng dụng có thể yêu cầu quyền truy cập root hoặc nếu ứng dụng đã được cấp / từ chối thì bánh mì nướng có thể được hiển thị cho biết liệu quyền truy cập có được cấp / từ chối hay không. Một lệnh tốt để chạy là "id" để bạn có thể xác minh rằng id người dùng thực tế là 0 (root).

Đây là một phương pháp mẫu để xác định xem quyền truy cập root đã được cấp chưa:

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

Điều quan trọng là phải thực sự kiểm tra chạy lệnh "su" vì một số trình giả lập có cài đặt sẵn "su", nhưng chỉ cho phép một số người dùng nhất định truy cập vào nó như vỏ adb.

Điều quan trọng nữa là phải kiểm tra sự tồn tại của "su" thực thi trước khi thử chạy nó, bởi vì Android đã được biết là không xử lý đúng các quy trình cố gắng chạy các lệnh bị thiếu. Các quá trình ma này có thể chạy tiêu thụ bộ nhớ theo thời gian.


Phương thức isRootAv Available () hoạt động rất tốt, cảm ơn bạn. Tôi khuyên bạn không nên chạy cái này trên luồng chính tuy nhiên để tránh ANR, chẳng hạn như cuộc gọi từ AsyncTask
Thunderstick

1
Tôi nghĩ đó là sự khác biệt giữa muốn đảm bảo root không có sẵn và muốn đảm bảo nó là có. Nếu bạn muốn đảm bảo một thiết bị không được root, kiểm tra đề xuất là tốt. Bạn sẽ nhận được thông báo sai nhưng không sao khi không chạy mã trên thiết bị bị xâm nhập là mối quan tâm chính của bạn.
Jeffrey Blattman

1
@ DAC84 Tôi không chắc là tôi hiểu câu hỏi của bạn. Nếu bạn chạy isRootGiven và từ chối trên ứng dụng root của bạn thì nó sẽ trả về false. Đó không phải là những gì đang xảy ra? Nếu bạn muốn tránh cảnh báo, bạn chỉ có thể sử dụng isRootAv Available cũng có thể được đặt tên là doesSUExist. Bạn cũng có thể cố gắng định cấu hình ứng dụng gốc của mình để đưa ra root một cách tự do và không quản lý nó.
rsimp

1
@BeeingJk không thực sự, mặc dù đó thực sự là thứ bạn có thể kiểm tra nhất mà không cần chạy su, đây là thử nghiệm thực sự. Bạn cần kiểm tra su trong PATH trước khi thử thực hiện nó. Tuy nhiên, thực tế việc thực thi su thường dẫn đến một tin nhắn bánh mì nướng hoặc tương tác với một ứng dụng quản lý root có thể không phải là điều bạn muốn. Đối với logic của riêng bạn, bạn có thể coi sự tồn tại của su đủ. Điều này vẫn có thể cung cấp dương tính giả trong một số trình giả lập có thể chứa quyền truy cập su nhưng có thể khóa vào nó.
rsimp

1
@BeeingJk isRootAv Available có lẽ là tất cả những gì bạn cần, nhưng điểm tôi đang cố gắng tạo ra là một cái tên như thế hoặc thậm chí khôngSUExist cung cấp ngữ nghĩa tốt hơn tên phương thức như isDeviceRooted không hoàn toàn đúng. Nếu bạn thực sự cần xác minh quyền truy cập root đầy đủ trước khi tiếp tục, bạn cần thử chạy một lệnh với su giống như lệnh được mã hóa trong isRootGiven
rsimp

35

Cập nhật 2017

Bạn có thể làm điều đó ngay bây giờ với Google Safetynet API . API SafetyNet cung cấp API chứng thực giúp bạn đánh giá tính bảo mật và khả năng tương thích của môi trường Android nơi ứng dụng của bạn chạy.

Chứng thực này có thể giúp xác định xem thiết bị cụ thể có bị giả mạo hay sửa đổi hay không.

API chứng thực trả về phản hồi JWS như thế này

{
  "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,
}

Phân tích phản hồi này có thể giúp bạn xác định xem thiết bị có được root hay không

Các thiết bị đã root dường như gây ra ctsProfileMatch = false.

Bạn có thể làm điều đó ở phía máy khách nhưng khuyến nghị phân tích cú pháp ở phía máy chủ. Một kiến ​​trúc máy chủ máy khách cơ bản với API mạng an toàn sẽ trông như thế này: -

nhập mô tả hình ảnh ở đây


3
Thông tin tuyệt vời, và trong một bối cảnh khác tôi tin rằng đây sẽ là câu trả lời chính xác. Thật không may, câu hỏi của OP không phải là bảo vệ ứng dụng của anh ta khỏi các môi trường không an toàn, mà là phát hiện root để kích hoạt các tính năng chỉ root trong ứng dụng của anh ta. Đối với các mục đích dự định của OP, quá trình này có vẻ quá phức tạp.
rsimp

31

Kiểm tra root ở cấp độ Java không phải là một giải pháp an toàn. Nếu ứng dụng của bạn có Mối quan tâm bảo mật để chạy trên thiết bị Rooted, thì vui lòng sử dụng giải pháp này.

Câu trả lời của Kevin hoạt động trừ khi điện thoại cũng có một ứng dụng như RootCloak. Các ứng dụng như vậy có Xử lý các API Java sau khi điện thoại được root và chúng giả định các API này để trả lại điện thoại không được root.

Tôi đã viết mã cấp độ gốc dựa trên câu trả lời của Kevin, nó hoạt động ngay cả với RootCloak! Ngoài ra, nó không gây ra bất kỳ vấn đề rò rỉ bộ nhớ.

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

}

Trong mã Java của bạn, bạn cần tạo RootUtils lớp trình bao bọc để thực hiện các cuộc gọi riêng

    public boolean checkRooted() {

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

1
Tôi nghĩ rằng phát hiện root rơi vào hai loại, cho phép các tính năng phụ thuộc vào root và sau đó là các biện pháp dựa trên bảo mật để cố gắng giảm thiểu các vấn đề bảo mật với điện thoại đã root. Đối với các tính năng phụ thuộc vào root, tôi thấy câu trả lời của Kevin khá kém. Trong bối cảnh của câu trả lời này, các phương pháp này có ý nghĩa hơn. Mặc dù tôi sẽ viết lại phương thức 2 để không sử dụng phương thức nào và thay vào đó lặp lại qua biến môi trường PATH để tìm kiếm "su". "Mà" không được đảm bảo trên điện thoại.
rsimp

bạn có thể vui lòng cung cấp một ví dụ về cách sử dụng mã c này trong java không?
mrid

@mrid Vui lòng kiểm tra cách thực hiện các cuộc gọi JNI từ Java trên Android.
Alok Kulkarni

Phương pháp này ngăn chặn phát hiện root bằng cách sử dụng ứng dụng RootCloak. Có bất kỳ kỹ thuật byepass gốc đã biết mà thất bại các phương pháp này?
Nidhin

20

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

Nếu bạn không muốn sử dụng tệp jar, chỉ cần sử dụng mã:

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

Chương trình sẽ cố gắng tìm thư mục su:

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

Thí dụ:

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

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

Cảm ơn! Tôi đang sử dụng điều này như checkRootMethod4()với Câu trả lời của Kevin .
Sheharyar

1
Không bao giờ thêm == truevào một boolean, nó không thêm gì và trông không đẹp.
minipif

2
@smoothBlue Tại sao vậy? Nó không sinh ra bất kỳ Quy trình nào như giải pháp của DevrimTuncer.
FD_

1
Một ý tưởng tốt hơn sẽ là lặp đi lặp lại qua PATH, thay vì mã hóa cứng các thư mục PATH điển hình
rsimp

1
Sử dụng, if (isRooted())kiểm tra thay vì viết rõ ràng đúng. Tốt hơn là nên tuân theo các mẫu viết mã
blucare

13

Thay vì sử dụng isRootAv Available (), bạn có thể sử dụng isAccessGiven (). Trực tiếp từ wiki RootTools :

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

RootTools.isAccessGiven () không chỉ kiểm tra xem thiết bị đã được root, nó còn gọi su cho ứng dụng của bạn, yêu cầu quyền và trả về true nếu ứng dụng của bạn được cấp quyền root thành công. Điều này có thể được sử dụng như kiểm tra đầu tiên trong ứng dụng của bạn để đảm bảo rằng bạn sẽ được cấp quyền truy cập khi bạn cần.

Tài liệu tham khảo


nhưng người dùng phải cấp quyền truy cập root phải không? vì vậy, nếu mục đích của tôi là ngăn ứng dụng của tôi chạy nếu thiết bị đã được root thì các tùy chọn của tôi thực sự bị hạn chế
Nasz Njoka Sr.

11

Một số bản dựng được sửa đổi được sử dụng để đặt thuộc tính hệ thống ro.modversion cho mục đích này. Mọi thứ dường như đã chuyển sang; bản dựng của tôi từ TheDude vài tháng trước có cái này:

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]

Trình giả lập từ SDK 1.5, mặt khác, chạy hình ảnh 1.5, cũng có root, có lẽ tương tự như Android Dev Phone 1 (mà bạn có lẽ muốn cho phép) và có điều này:

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]

Đối với các bản dựng bán lẻ, tôi không có một tay, nhưng các tìm kiếm khác nhau theo site:xda-developers.comthông tin. Đây là một G1 ở Hà Lan , bạn có thể thấy ro.build.tagsnó không có test-keys, và tôi nghĩ đó có lẽ là tài sản đáng tin cậy nhất để sử dụng.


Điều đó có vẻ thú vị, nhưng: Mặc dù trình giả lập (và ADP) không cho phép root mỗi se, họ không cho phép các ứng dụng sử dụng nó, tức là: $ su app_29 $ su su: uid 10029 không được phép su
miracle2k

À, tôi cho rằng họ sẽ không ... bạn có thể kết hợp nó với một kiểm tra cho ro.build.host (không phải) kết thúc trong google.com, nếu họ là những người duy nhất có khóa kiểm tra nhưng chặn su mà không có hỏi người dùng. Phụ thuộc vào máy chủ bản dựng dành cho các thiết bị mới hơn, những thứ không phải điện thoại ... không dễ dàng.
Chris Boyle

11

RootBeer là thư viện kiểm tra root của Scott và Matthew. Nó sử dụng các kiểm tra khác nhau để cho biết thiết bị có được root hay không.

Kiểm tra Java

  • CheckRootQuản lý ứng dụng

  • CheckPotivelyDangerousAppss

  • CheckRootCloakingApps

  • CheckTestKeys

  • checkForDangerousProps

  • kiểm traForBusyBoxBinary

  • kiểm traForSuBinary

  • kiểm tra

  • kiểm tra hệ thống

Kiểm tra bản địa

Chúng tôi gọi qua trình kiểm tra gốc của chúng tôi để chạy một số kiểm tra riêng của nó. Kiểm tra gốc thường khó khăn hơn để che giấu, vì vậy một số ứng dụng áo choàng gốc chỉ chặn tải các thư viện gốc có chứa các từ khóa nhất định.

  • kiểm traForSuBinary

8

Tôi đề nghị sử dụng mã gốc để phát hiện root. Dưới đây là một ví dụ làm việc đầy đủ .

nhập mô tả hình ảnh ở đây

Bao bọc 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();
    }
}

Trình bao bọc JNI (bản địa-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();
}

hằng số:

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

phát hiện root từ mã gốc:

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
Công cụ tuyệt vời, Dima. Cảm ơn rất nhiều. Nó thậm chí còn bắt được magisk.
chuyên gia

Đây là thỏa thuận thực sự.
Vahid Amiri

@klutch có liên kết đến ví dụ hoạt động (github) ở dòng đầu tiên của bài viết của tôi
Dima Kozhevin

7

Đây là mã của tôi dựa trên một số câu trả lời ở đây:

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

Ngoài câu trả lời của @Kevins, gần đây tôi đã tìm thấy trong khi sử dụng hệ thống của mình, rằng Nexus 7.1 đã trở lại falsecho cả ba phương thức - Không có whichlệnh, không test-keysSuperSUkhông được cài đặt trong/system/app .

Tôi đã thêm điều này:

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

Điều này hơi ít hữu ích trong một số trường hợp (nếu bạn cần quyền truy cập root được bảo đảm) vì SuperSU hoàn toàn có thể được cài đặt trên các thiết bị không có quyền truy cập SU.

Tuy nhiên, vì SuperSU có thể được cài đặt và hoạt động nhưng không có trong /system/appthư mục, trường hợp bổ sung này sẽ root (haha) trong các trường hợp như vậy.


Đây không phải là một câu trả lời hay vì bạn có các gói root khác có thể được cài đặt trên thiết bị của bạn. Mã hóa cứng các gói ứng dụng khác sẽ rất khó khăn như bạn không thể mong đợi và liệt kê tất cả chúng
xanh

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

Hai ý tưởng bổ sung, nếu bạn muốn kiểm tra xem một thiết bị có khả năng root từ ứng dụng của bạn không:

  1. Kiểm tra sự tồn tại của nhị phân 'su': chạy "which su" từ Runtime.getRuntime().exec()
  2. Tìm SuperUser.apk ở /system/app/Superuser.apkvị trí

3

Sử dụng C ++ với ndk là cách tiếp cận tốt nhất để phát hiện root ngay cả khi người dùng đang sử dụng các ứng dụng ẩn gốc của mình như RootCloak. Tôi đã kiểm tra mã này với RootCloak và tôi đã có thể phát hiện ra root ngay cả khi người dùng đang cố gắng che giấu nó. Vì vậy, tập tin cpp của bạn muốn:

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

Và bạn sẽ gọi hàm từ mã java của bạn như sau

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

API chứng thực mạng an toàn của các dịch vụ Google play mà chúng tôi có thể đánh giá thiết bị và xác định xem nó có bị root / giả mạo hay không.

Vui lòng xem qua câu trả lời của tôi để xử lý các thiết bị đã root:
https://stackoverflow.com/a/58304556/3908895


1

Quên tất cả những gì phát hiện các ứng dụng gốc và nhị phân su. Kiểm tra quá trình daemon gốc. Điều này có thể được thực hiện từ thiết bị đầu cuối và bạn có thể chạy các lệnh thiết bị đầu cuối trong một ứng dụng. Hãy thử một lớp lót này.

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

Bạn không cần sự cho phép root để đạt được điều này.


0

Quả thực đó là một câu hỏi thú vị và cho đến nay không ai xứng đáng nhận giải thưởng. Tôi sử dụng mã sau đây:

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

Mã này chắc chắn không chống đạn, vì mạng không thể có sẵn để bạn có được một ngoại lệ. Nếu phương pháp này trả về true thì 99% bạn có thể chắc chắn, nếu không thì chỉ 50% là không. Sự cho phép mạng cũng có thể làm hỏng giải pháp.


Tôi đã thử nghiệm điều này và nó không trở lại đúng với thiết bị đã root của tôi.
thủ thuật

Thật thú vị khi xem loại ngoại lệ bạn nhận được. Bạn có thể nhận được cổng ngoại lệ bị ràng buộc, tuy nhiên nếu bạn không thể tạo cổng máy chủ trong phạm vi dưới 1024, nó sẽ giảm giá trị của root, vì bạn vẫn có một số hạn chế nhất định.
Singagirl

-1

Sử dụng thư viện của tôi tại rootbox , nó khá dễ dàng. Kiểm tra mã yêu cầu dưới đây:

    //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.");
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.