关于android:如何以编程方式找到有根设备?

how to find the rooted device programmatically?

在安装我的应用程序之前,我希望它检查设备是否已root。我已使用以下代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
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;
    }

它工作正常。但是我听说可以更改文件名" su",并且可以在非root用户的设备中创建一个名称为" su"的文件。在这种情况下,此源不可靠。除了搜索" su"外,我还想知道其他方法来找到有根的设备。
我使用了以下代码

1
2
3
4
5
6
7
8
9
10
11
12
   Public static boolean checkRootMethod1()

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

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

            return true;
        }

        return false;
    }

它不能正常工作。对于有根设备,它按预期工作。但是对于某些无根设备,它也显示为有根。由于不同设备的输出各不相同,所以我找不到解决方案。任何帮助将不胜感激<铅>


如果您想限制在根设备上使用应用程序,则必须考虑两点:
1)限制在根目录设备中下载该应用程序。
2)限制根设备中应用的侧载。

请按照以下步骤限制从Play商店下载:
1)进入商店控制台。
2)在左侧菜单上,转到发布管理。
3)在那,去设备目录。
4)然后,您将获得3个选项卡选项,转到"排除的设备"。
5)您将获得一个指定排除规则的选项。点击管理排除规则。
6)您可以看到SafetyNet排除项的选择器。选择选项:排除未通过基本完整性的设备以及未经google认证的设备。

请按照以下步骤限制应用的侧面加载:
1)使用以下网址获取API密钥:https://developer.android.com/training/safetynet/attestation.html#obtain-api-key

2)在gradle文件中添加safetynet依赖项。
implementation 'com.google.android.gms:play-services-safetynet:17.0.0'

3)我在我的其他活动扩展的BaseActivity中放置了下面的代码,因此,如果拥有根设备的黑客尝试侧加载并尝试输入任何活动,则将执行下面的代码。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
    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.

Attestation API返回一个JWS响应,如下所示:

1
2
3
4
5
6
7
8
9
{
 "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,
}

JWS响应包含ctsProfileMatch和basicIntegrity,它们指示设备状态。
enter


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
public static boolean checkRooted()
{
    try
    {
        Process p = Runtime.getRuntime().exec("su", null, new File("/"));
        DataOutputStream os = new DataOutputStream( p.getOutputStream());
        os.writeBytes("pwd\
");
        os.writeBytes("exit\
");
        os.flush();
        p.waitFor();
        p.destroy();
    }
    catch (Exception e)
    {
        return false;
    }

    return true;
}

尝试以下代码:-

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
 /**
   * Checks if the device is rooted.
   *
   * @return <wyn>true</wyn> if the device is rooted, <wyn>false</wyn> otherwise.
   */
  public static boolean isRooted() {

    // 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 (Exception e1) {
      // ignore
    }

    // try executing commands
    return canExecuteCommand("/system/xbin/which su")
        || canExecuteCommand("/system/bin/which su") || canExecuteCommand("which su");
  }

  // executes a command on the system
  private static boolean canExecuteCommand(String command) {
    boolean executedSuccesfully;
    try {
      Runtime.getRuntime().exec(command);
      executedSuccesfully = true;
    } catch (Exception e) {
      executedSuccesfully = false;
    }

    return executedSuccesfully;
  }

请参见以下链接:-

确定是否在有根设备上运行

确定Android设备是否以编程方式植根?