一、背景介紹
現(xiàn)在市面上,諸如:MT管理器舟扎、APK Editor等軟件奄妨,可以對APK文件進(jìn)行修改垦页,提取AndroidManifest文件,修改包名、版本號、圖標(biāo)杰标、應(yīng)用名稱等。
通過修改版本號彩匕,可以跳過應(yīng)用升級檢測腔剂,從而出現(xiàn)新版本修復(fù)的漏洞無法修復(fù)的情況;還會造成新功能無法使用驼仪,新推廣的業(yè)務(wù)無法正常展開桶蝎。
二、修改檢測
通過研究谅畅,發(fā)現(xiàn)要達(dá)到上述修改目的,勢必要對APK文件進(jìn)行修改噪服,再重新打包簽名安裝毡泻。
我們知道,Android在安裝apk時粘优,會在目錄 /data/app/包名/ 路徑拷貝一份APK文件:base.apk仇味。因此,可以通過獲取到base.apk文件雹顺,計算base.apk的MD5丹墨,檢測文件是否被修改。
同時可以獲取以下信息:app的 包名嬉愧、版本號贩挣、名稱、簽名信息没酣、md5王财,上報服務(wù)接口進(jìn)行校驗,從而阻止被修改的app繼續(xù)使用裕便。
交互流程
三绒净、客戶端獲取信息
1、AppInfoUtils
public class AppInfoUtils {
// 獲取appid
public static String getAppId(Context context) {
return context == null ? "" : context.getPackageName();
}
// 獲取app名稱
public static String getAppName(Context context) {
return context == null ? "" : context.getString(context.getApplicationInfo().labelRes);
}
// 獲取base.apk的路徑
public static String getBaseApkPath(Context context) {
return context == null ? "" : context.getApplicationInfo().sourceDir;
}
// 獲取appVersionName
public static String getAppVersionName(Context context) {
try {
return context == null ? "" : context.getPackageManager().getPackageInfo(context.getPackageName(), 0).versionName;
} catch (Exception e) {
e.printStackTrace();
}
return "";
}
// 獲取appVersionCode
public static long getAppVersionCode(Context context) {
if (context == null) return 0;
try {
PackageInfo packageInfo = context.getPackageManager().getPackageInfo(context.getPackageName(), 0);
return AndroidSysUtils.isOverP9() ? packageInfo.getLongVersionCode() : packageInfo.versionCode;
} catch (Exception e) {
e.printStackTrace();
}
return 0;
}
// 獲取簽名文件的md5
public static String getSignMD5(Context context) {
if (context == null) return "";
try {
Signature[] signatures;
if (AndroidSysUtils.isOverP9()) {
PackageInfo packageInfo = context.getPackageManager().getPackageInfo(context.getPackageName(), PackageManager.GET_SIGNING_CERTIFICATES);
SigningInfo signingInfo = packageInfo.signingInfo;
signatures = signingInfo.getApkContentsSigners();
} else {
PackageInfo packageInfo = context.getPackageManager().getPackageInfo(context.getPackageName(), PackageManager.GET_SIGNATURES);
signatures = packageInfo.signatures;
}
StringBuilder stringBuilder = new StringBuilder();
for (Signature signature : signatures) {
stringBuilder.append(MD5Utils.getMd5ByByteArray(signature.toByteArray())).append(",");
}
return stringBuilder.substring(0, stringBuilder.length() - 1);
} catch (Exception e) {
e.printStackTrace();
}
return "";
}
// 獲取base.apk的md5
public static String getBaseApkMD5(Context context) {
try {
return context == null ? "" : MD5Utils.getMd5ByFile(getBaseApkPath(context));
} catch (Exception e) {
e.printStackTrace();
}
return "";
}
}
2偿衰、MD5Utils
public class MD5Utils {
public static String getMd5ByFile(String fileName) {
FileInputStream in = null;
try {
in = new FileInputStream(fileName);
MessageDigest md5 = MessageDigest.getInstance("MD5");
byte[] buffer = new byte[1024];
int readCound;
while ((readCound = in.read(buffer)) > 0) {
md5.update(buffer, 0, readCound);
}
BigInteger bi = new BigInteger(1, md5.digest());
return bi.toString(16);
} catch (Exception e) {
e.printStackTrace();
} finally {
if (in != null) {
try {
in.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
return "";
}
public static String getMd5ByByteArray(byte[] byteArray) {
try {
MessageDigest md5 = MessageDigest.getInstance("MD5");
md5.update(byteArray);
BigInteger bi = new BigInteger(1, md5.digest());
return bi.toString(16);
} catch (Exception e) {
e.printStackTrace();
}
return "";
}
}