Skip to content

Instantly share code, notes, and snippets.

@chris-carneiro
Last active July 6, 2021 04:19
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save chris-carneiro/50fed7b225cab52389f8fb16a946d63a to your computer and use it in GitHub Desktop.
Save chris-carneiro/50fed7b225cab52389f8fb16a946d63a to your computer and use it in GitHub Desktop.
How to check android apk integrity/tampering at runtime
package com.twinpeek.android.app;
import java.io.File;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.security.NoSuchProviderException;
import android.content.Context;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.pm.Signature;
import android.os.Build;
import android.util.Log;
/**
* Created by ChrisC on 20/06/17.
*/
public class TamperUtils {
private TamperUtils() {
//prevent instantiation
}
private static final String SIGNATURE = "Your apk signature";
private static final String PLAY_STORE_APP_ID = "com.android.vending";
public static final String TAG = TamperUtils.class.getSimpleName();
//computed the sha1 hash of the signature
public static String getSHA1(byte[] sig) throws NoSuchProviderException, NoSuchAlgorithmException {
MessageDigest digest = MessageDigest.getInstance("SHA1", "BC");
digest.update(sig);
byte[] hashtext = digest.digest();
return bytesToHex(hashtext);
}
//util method to convert byte array to hex string
public static String bytesToHex(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);
}
/**
* Query the signature for this application to detect whether it matches the
* signature of the real developer. If it doesn't the app must have been
* resigned, which indicates it may been tampered with.
*
* @param context
* @return true if the app's signature matches the expected signature.
* @throws PackageManager.NameNotFoundException
*/
public static boolean isAppSignatureValid(Context context) {
PackageInfo packageInfo = null;
try {
packageInfo = context.getPackageManager().getPackageInfo(
context.getPackageName(), PackageManager.GET_SIGNATURES);
//note sample just checks the first signature
for (Signature signature : packageInfo.signatures) {
// SHA1 the signature
String sha1 = getSHA1(signature.toByteArray());
// check is matches hardcoded value
return SIGNATURE.equals(sha1);
}
} catch (PackageManager.NameNotFoundException | NoSuchAlgorithmException | NoSuchProviderException e) {
Log.e(TAG, e.getMessage(), e);
}
return false;
}
public static boolean isFromPlayStore(final Context context) {
final String installer = context.getPackageManager()
.getInstallerPackageName(context.getPackageName());
return installer != null && installer.startsWith(PLAY_STORE_APP_ID);
}
public static boolean isDebuggable(Context context) {
return (context.getApplicationInfo().flags & ApplicationInfo.FLAG_DEBUGGABLE) != 0;
}
public static boolean isEmulator() {
return Build.FINGERPRINT.startsWith("generic")
|| Build.FINGERPRINT.startsWith("unknown")
|| Build.MODEL.contains("google_sdk")
|| Build.MODEL.contains("Emulator")
|| Build.MODEL.contains("Android SDK built for x86")
|| Build.MANUFACTURER.contains("Genymotion")
|| (Build.BRAND.startsWith("generic") && Build.DEVICE.startsWith("generic"))
|| "google_sdk".equals(Build.PRODUCT);
}
private 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;
}
public static boolean isRooted() {
return findBinary("su");
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment