feat: verify dex files

This commit is contained in:
hyb1996
2018-02-23 11:21:18 +08:00
parent 29402055ab
commit e4cda0b64f
7 changed files with 123 additions and 26 deletions

View File

@@ -4,18 +4,25 @@ import android.annotation.SuppressLint;
import android.app.Activity;
import android.app.Service;
import android.content.Context;
import android.content.Intent;
import android.content.pm.ActivityInfo;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.content.pm.ServiceInfo;
import android.content.pm.Signature;
import android.support.annotation.Nullable;
import android.util.Base64;
import android.util.Log;
import com.stardust.R;
import java.io.IOException;
import java.lang.ref.WeakReference;
import java.lang.reflect.Method;
import java.security.MessageDigest;
import java.util.List;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;
/**
* Created by Stardust on 2017/4/5.
@@ -24,13 +31,10 @@ import java.util.List;
public class DeveloperUtils {
private static final String PACKAGE_NAME = "com.stardust.scriptdroid";
private static final String SIGNATURE = "nPNPcy4Lk/eP6fLvZitP0VPbHdFCbKua77m59vis5fA=\n";
public static void ensureRunningPackageNotSelf(@Nullable String runningPackage) {
if (PACKAGE_NAME.equals(runningPackage)) {
throw new SecurityException();
}
}
private static final String SIGNATURE = "nPNPcy4Lk/eP6fLvZitP0VPbHdFCbKua77m59vis5fA=";
private static final String LOG_TAG = "DeveloperUtils";
private static final ExecutorService sExecutor = UnderuseExecutors.getExecutor();
private static final String SALT = "let\nlife\nbe\nbeautiful\nlike\nsummer\nflowers\nand\ndeath\nlike\nautumn\nleaves\n.";
public static boolean isSelfPackage(@Nullable String runningPackage) {
return PACKAGE_NAME.equals(runningPackage);
@@ -40,13 +44,15 @@ public class DeveloperUtils {
public static String getSignatureSHA(Context context, String packageName) {
try {
@SuppressLint("PackageManagerGetSignatures")
PackageInfo packageInfo = context.getPackageManager().getPackageInfo(packageName, PackageManager.GET_SIGNATURES);
PackageInfo packageInfo = getPackageInfo(context, packageName, PackageManager.GET_SIGNATURES);
if (packageInfo == null)
return null;
Signature[] signatures = packageInfo.signatures;
StringBuilder builder = new StringBuilder();
for (Signature signature : signatures) {
MessageDigest md = MessageDigest.getInstance("SHA-256");
md.update(signature.toByteArray());
final String sha = Base64.encodeToString(md.digest(), Base64.DEFAULT);
final String sha = Base64.encodeToString(md.digest(), Base64.NO_WRAP);
builder.append(sha);
}
return builder.toString();
@@ -56,16 +62,30 @@ public class DeveloperUtils {
return null;
}
private static PackageInfo getPackageInfo(Context context, String packageName, int flags) {
try {
return context.getPackageManager().getPackageInfo(packageName, flags);
} catch (PackageManager.NameNotFoundException e) {
return null;
}
}
/**
* 此方法仅防止那些不会改源码直接用apk编辑器修改应用内字符串(QQ群号)等的恶意用户行为。
* 为了开源社区的发展,请善用源码:-)
*/
public static boolean checkSignature(Context context) {
return SIGNATURE.equals(getSignatureSHA(context, context.getPackageName()));
return checkSignature(context, context.getPackageName());
}
public static boolean checkSignature(Context context, String packageName) {
return SIGNATURE.equals(getSignatureSHA(context, packageName));
String sha = getSignatureSHA(context, packageName);
if (sha == null)
return false;
if (sha.endsWith("\n")) {
sha = sha.substring(0, sha.length() - 1);
}
return SIGNATURE.equals(sha);
}
@@ -109,4 +129,76 @@ public class DeveloperUtils {
return false;
}
}
public static boolean checkDexFile(Context context, long[] crc) {
String apkPath = context.getPackageCodePath();
try {
ZipFile zipFile = new ZipFile(apkPath);
for (int i = 0; i < crc.length; i++) {
String dexFile;
if (i == 0) {
dexFile = "classes.dex";
} else {
dexFile = "classes" + (i + 1) + ".dex";
}
ZipEntry dexEntry = zipFile.getEntry(dexFile);
long dexEntryCrc = dexEntry.getCrc();
Log.d(LOG_TAG, String.valueOf(dexEntryCrc));
if (dexEntryCrc != crc[i]) {
return false;
}
}
return true;
} catch (IOException e) {
e.printStackTrace();
return false;
}
}
public static void verifyApk(Activity activity, final int crcRes) {
final WeakReference<Activity> activityWeakReference = new WeakReference<>(activity);
sExecutor.execute(new Runnable() {
@Override
public void run() {
Activity a = activityWeakReference.get();
if (a == null)
return;
if (!checkSignature(a)) {
a.finish();
return;
}
long[] crc = readCrc(a.getString(crcRes));
if (!checkDexFile(a, crc)) {
a.finish();
}
}
});
}
private static long[] readCrc(String crcStr) {
String[] crcStrings = crcStr.split("\n");
StringBuilder iHash = new StringBuilder();
long[] crc = new long[crcStrings.length - 1];
for (int i = 0; i < crc.length; i++) {
crc[i] = Long.parseLong(crcStrings[i]);
iHash.append(iHash(crcStrings[i]));
}
if (!crcStrings[crcStrings.length - 1].equals(iHash.toString())) {
return new long[crc.length];
}
return crc;
}
private static String iHash(String data) {
try {
MessageDigest md5 = MessageDigest.getInstance("MD5");
for (int i = 0; i < 8; i++) {
data = Base64.encodeToString(md5.digest((data + SALT).getBytes()), Base64.NO_WRAP);
}
return data;
} catch (Exception e) {
return null;
}
}
}

View File

@@ -1,6 +1,7 @@
package com.stardust.util;
import java.util.concurrent.Executor;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
/**
@@ -9,13 +10,13 @@ import java.util.concurrent.Executors;
public class UnderuseExecutors {
private static Executor mExecutor = Executors.newSingleThreadExecutor();
private static ExecutorService mExecutor = Executors.newSingleThreadExecutor();
public static void execute(Runnable runnable) {
mExecutor.execute(runnable);
}
public static Executor getExecutor() {
public static ExecutorService getExecutor() {
return mExecutor;
}
}