Last active
January 14, 2020 06:11
-
-
Save aslamanver/560727f71beb7076f48458c455591545 to your computer and use it in GitHub Desktop.
Tamper Detection - Android tamper detection, signature verification, root check and emulator detection in standard way.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| android { | |
| // ... | |
| signingConfigs { | |
| release { | |
| storeFile file("/home/user/Documents/sign/test.jks") | |
| storePassword "Test#123" | |
| keyAlias "test" | |
| keyPassword "Test#123" | |
| v1SigningEnabled true | |
| v2SigningEnabled true | |
| } | |
| } | |
| buildTypes { | |
| release { | |
| minifyEnabled true | |
| shrinkResources true | |
| useProguard true | |
| debuggable false | |
| proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' | |
| signingConfig signingConfigs.release | |
| } | |
| debug { | |
| signingConfig signingConfigs.release | |
| } | |
| } | |
| } | |
| dependencies { | |
| // ... | |
| implementation 'com.scottyab:rootbeer-lib:0.0.7' | |
| implementation 'com.crashlytics.sdk.android:crashlytics:2.9.9' | |
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| import android.content.Context; | |
| import android.content.pm.PackageInfo; | |
| import android.content.pm.PackageManager; | |
| import android.content.pm.Signature; | |
| import android.os.Build; | |
| import android.provider.Settings; | |
| import android.util.Base64; | |
| import com.scottyab.rootbeer.RootBeer; | |
| import java.io.BufferedReader; | |
| import java.io.File; | |
| import java.io.IOException; | |
| import java.io.InputStreamReader; | |
| import java.io.UnsupportedEncodingException; | |
| import java.security.MessageDigest; | |
| import java.security.NoSuchAlgorithmException; | |
| import io.fabric.sdk.android.services.common.CommonUtils; | |
| public class TC { | |
| /* | |
| * -------------------------------------------------------------------------------------------- | |
| * Base64 encoded SHA Key of application signature from test.jks | |
| * B18EE15CC134393CEA12EB467BC3B15BB12793E2 | |
| * -------------------------------------------------------------------------------------------- | |
| * | |
| * */ | |
| private static final String Sx1526 = "QjE4RUUxNUNDMTM0MzkzQ0VBMTJFQjQ2N0JDM0IxNUJCMTI3OTNFMg=="; | |
| /* | |
| * ---------------------- | |
| * Signature verification | |
| * ---------------------- | |
| * | |
| * */ | |
| public static boolean s(Context context) { | |
| try { | |
| PackageInfo packageInfo = context.getPackageManager().getPackageInfo(context.getPackageName(), PackageManager.GET_SIGNATURES); | |
| for (Signature signature : packageInfo.signatures) { | |
| // PLog.d("SIGN_KEY", gs1(signature.toByteArray())); | |
| if (b6s(Sx1526).equals(gs1(signature.toByteArray()))) { | |
| // PLog.d("SIGN_KEY", "VERIFIED SIGNATURE"); | |
| return true; | |
| } | |
| } | |
| } catch (Exception e) { | |
| e.printStackTrace(); | |
| } | |
| return false; | |
| } | |
| /* | |
| * ----------- | |
| * Root check | |
| * ----------- | |
| * | |
| * */ | |
| public static boolean r(Context context) { | |
| boolean isEmulator = e(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(); | |
| } | |
| } | |
| } | |
| /* | |
| * ----------------- | |
| * Root fabric check | |
| * ----------------- | |
| * | |
| * */ | |
| public static boolean rf(Context context) { | |
| return CommonUtils.isRooted(context); | |
| } | |
| /* | |
| * -------------- | |
| * Emulator check | |
| * -------------- | |
| * | |
| * */ | |
| public static boolean e(Context context) { | |
| String androidId = Settings.Secure.getString(context.getContentResolver(), "android_id"); | |
| return "sdk".equals(Build.PRODUCT) || "google_sdk".equals(Build.PRODUCT) || androidId == null; | |
| } | |
| /* | |
| * ------------- | |
| * SHA1 - Getter | |
| * ------------- | |
| * | |
| * */ | |
| private static String gs1(byte[] sig) throws NoSuchAlgorithmException { | |
| MessageDigest digest = MessageDigest.getInstance("SHA1"); | |
| digest.update(sig); | |
| byte[] hashtext = digest.digest(); | |
| return bth(hashtext); | |
| } | |
| /* | |
| * ---------------- | |
| * As root - check | |
| * ---------------- | |
| * | |
| * */ | |
| public static Boolean ar(String command) { | |
| try { | |
| Process process = Runtime.getRuntime().exec("su"); | |
| process.getOutputStream().write(command.getBytes()); | |
| process.getOutputStream().flush(); | |
| process.getOutputStream().close(); | |
| BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream())); | |
| int read; | |
| char[] buffer = new char[4096]; | |
| StringBuffer output = new StringBuffer(); | |
| while ((read = reader.read(buffer)) > 0) { | |
| output.append(buffer, 0, read); | |
| } | |
| reader.close(); | |
| process.waitFor(); | |
| // return output.toString(); | |
| return true; | |
| } catch (IOException e) { | |
| return false; | |
| } catch (InterruptedException e) { | |
| return false; | |
| } | |
| } | |
| /* | |
| * ------------------ | |
| * Root beer - check | |
| * ------------------ | |
| * | |
| * */ | |
| public static boolean rb(Context context) { | |
| return new RootBeer(context).isRooted(); | |
| } | |
| /* | |
| * ---------------- | |
| * Execute app scan | |
| * ---------------- | |
| * | |
| * */ | |
| public static void eas(Context context) { | |
| if (cte(context)) { | |
| System.exit(1); | |
| throw new RuntimeException(); | |
| } | |
| } | |
| /* | |
| * -------------------------------------------------------------------------------------------- | |
| * Private methods | |
| * -------------------------------------------------------------------------------------------- | |
| * | |
| * | |
| * ------------ | |
| * Bytes to Hex | |
| * ------------ | |
| * | |
| * */ | |
| private static String bth(byte[] bytes) { | |
| final char[] hexArray = {'0', '1', '2', '3', '4', '5', '6', '7', '8', | |
| '9', 'A', 'B', 'C', 'D', 'E', 'F'}; | |
| char[] hexChars = new char[bytes.length * 2]; | |
| int v; | |
| for (int j = 0; j < bytes.length; j++) { | |
| v = bytes[j] & 0xFF; | |
| hexChars[j * 2] = hexArray[v >>> 4]; | |
| hexChars[j * 2 + 1] = hexArray[v & 0x0F]; | |
| } | |
| return new String(hexChars); | |
| } | |
| /* | |
| * ---------------- | |
| * Base64 to String | |
| * ---------------- | |
| * | |
| * */ | |
| private static String b6s(String base64) throws UnsupportedEncodingException { | |
| byte[] data = Base64.decode(base64, Base64.DEFAULT); | |
| return new String(data, "UTF-8"); | |
| } | |
| /* | |
| * ------------------- | |
| * Can throw exception | |
| * ------------------- | |
| * | |
| * */ | |
| private static boolean cte(Context context) { | |
| return TC.e(context) || TC.r(context) || TC.rf(context) || TC.ar("ls -a") || TC.rb(context) || !TC.s(context); | |
| } | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment