Welcome to OStack Knowledge Sharing Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
296 views
in Technique[技术] by (71.8m points)

android - how to find the rooted device programmatically?

Before installing my application,i want it to check whether the device is rooted or not.I have used the following code

private static boolean isRooted()

                 return findBinary("su");
        }
    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;
    } 

It is working properly.But i have heard that the filename "su" can be changed and also a file with the name "su" can be created in the nonrooted devices.In that cases,this source is not dependable.so i want to know some other way to find the rooted device other than searching for "su". I have used the following code

   Public static boolean checkRootMethod1()

     {
        String buildTags = android.os.Build.TAGS;

        if (buildTags != null && buildTags.contains("test-keys")) {

            return true;
        }

        return false;
    }

It is not working properly.For rooted devices it works as expected.But for SOME unrooted devices also it shows as rooted.Since the output is variying for differnt devices,i cannot find a solution..any help would be appreciated

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Answer

0 votes
by (71.8m points)

If you want to restrict usage of your app in the rooted device then you have to think of 2 points:
1) Restrict downloading of the app in the rooted device.
2) Restrict sideloading of the app in the rooted device.

Follow below steps to restrict downloading from the play store:
1) Go to play store console.
2) On the left side menu, go to release management.
3) In that, go to device catalog.
4) Then you will get 3 tab options, go to Excluded devices.
5) You will get an option to specify exclusion rules. click on manage exclusion rules.
6) You can see a chooser for SafetyNet exclusion. choose the option: exclude devices that don't pass basic integrity, as well as devices that are uncertified by google.

Follow below steps to restrict the sideloading of the app:
1) Obtain an API key using: https://developer.android.com/training/safetynet/attestation.html#obtain-api-key

2) Add safetynet dependency in your gradle file.
implementation 'com.google.android.gms:play-services-safetynet:17.0.0'

3) I have put below code in my BaseActivity which my other activities extend, so If a hacker with the rooted device tries to sideload and tries to enter at any activity then below code executes.

    private void ifGooglePlayServicesValid() {
        if (GoogleApiAvailability.getInstance().isGooglePlayServicesAvailable(getApplicationContext())
                == ConnectionResult.SUCCESS) {
            // The SafetyNet Attestation API is available.
            callSafetyNetAttentationApi();

        } else {
            // Prompt user to update Google Play services.
        }
    }

    private void callSafetyNetAttentationApi() {
        SafetyNet.getClient(this).attest(generateNonce(), SAFETY_NET_CHECK_API_KEY)
            .addOnSuccessListener(this,
                response -> {
                    // Use response.getJwsResult() to get the result data.
                    String jwsResponse = decodeJws(response.getJwsResult());
                    try {
                        JSONObject attestationResponse = new JSONObject(jwsResponse);
                        boolean ctsProfileMatch = attestationResponse.getBoolean("ctsProfileMatch");
                        boolean basicIntegrity = attestationResponse.getBoolean("basicIntegrity");
                        if (!ctsProfileMatch || !basicIntegrity) {
                            // this indicates it's rooted/tampered device
                        }
                    } catch (JSONException e) {
                        // json exception
                    }
                })
            .addOnFailureListener(this, e -> {
                // An error occurred while communicating with the service.
            });
    }

    public String decodeJws(String jwsResult) {
        if (jwsResult == null) {
            return null;
        }
        final String[] jwtParts = jwsResult.split("\.");
        if (jwtParts.length == 3) {
            return new String(Base64.decode(jwtParts[1], Base64.DEFAULT));
        } else {
            return null;
        }
    }

    private byte[] generateNonce() {
        byte[] nonce = new byte[16];
        new SecureRandom().nextBytes(nonce);
        return nonce;
    }

SAFETY_NET_CHECK_API_KEY is the key obtained in the 1st step.

The Attestation API returns a JWS response which looks like:

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

The JWS response contains ctsProfileMatch and basicIntegrity that indicate the device status.

enter image description here
Reference: https://developer.android.com/training/safetynet/attestation.html


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome to OStack Knowledge Sharing Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...