很多情况App在下载安装,同时app再升级过程中,遭到劫持。根据用户反映更新后是一个未知的App。针对此类情况,特做了app防劫持安装防护,稍作分析只要在本地对apk文件进行包名、应用名和签名校验,如果包名和签名不一致,那么就是伪装程序,这个漏洞显而易见!废话不多说直接上代码。
public class ReadApkMessageUtils {
private static String appName;
private static String packageName;
public static String apkInfoPackageName(String absPath, Context context) {
PackageManager pm = context.getPackageManager();
PackageInfo pkgInfo = pm.getPackageArchiveInfo(absPath, PackageManager.GET_ACTIVITIES);
if (pkgInfo != null) {
ApplicationInfo appInfo = pkgInfo.applicationInfo;
/* 必须加这两句,不然下面icon获取是default icon而不是应用包的icon */
appInfo.sourceDir = absPath;
appInfo.publicSourceDir = absPath;
// 得到包名
packageName = appInfo.packageName;
}
return packageName;
}
//获取程序自身的签名
public static String getSign(Context context) {
PackageManager pm = context.getPackageManager();
List<PackageInfo> apps = pm.getInstalledPackages(PackageManager.GET_SIGNATURES);
Iterator<PackageInfo> iter = apps.iterator();
while (iter.hasNext()) {
PackageInfo info = iter.next();
String packageName = info.packageName;
//按包名读取签名
if (packageName.equals(SystemToolUtils.getAppPackageName(context))) { //根据你自己的包名替换
return getPublicKey(info.signatures[0].toByteArray());
}
}
return null;
}
//获取新下载还未安装APK的签名信息
@SuppressWarnings({"unchecked", "rawtypes"})
public static String getPackageArchiveInfo(String apkFile, int flags) {
//这个是与显示有关的, 里面涉及到一些像素显示等等, 我们使用默认的情况
DisplayMetrics metrics = new DisplayMetrics();
metrics.setToDefaults();
Object pkgParserPkg = null;
Class[] typeArgs = null;
Object[] valueArgs = null;
try {
Class<?> packageParserClass = Class.forName("android.content.pm.PackageParser");
Constructor<?> packageParserConstructor = null;
Object packageParser = null;
//由于SDK版本问题,这里需做适配,来生成不同的构造函数
if (Build.VERSION.SDK_INT > 20) {
//无参数 constructor
packageParserConstructor = packageParserClass.getDeclaredConstructor();
packageParser = packageParserConstructor.newInstance();
packageParserConstructor.setAccessible(true);//允许访问
typeArgs = new Class[2];
typeArgs[0] = File.class;
typeArgs[1] = int.class;
Method pkgParser_parsePackageMtd = packageParserClass.getDeclaredMethod("parsePackage", typeArgs);
pkgParser_parsePackageMtd.setAccessible(true);
valueArgs = new Object[2];
valueArgs[0] = new File(apkFile);
valueArgs[1] = PackageManager.GET_SIGNATURES;
pkgParserPkg = pkgParser_parsePackageMtd.invoke(packageParser, valueArgs);
} else {
//低版本有参数 constructor
packageParserConstructor = packageParserClass.getDeclaredConstructor(String.class);
Object[] fileArgs = {apkFile};
packageParser = packageParserConstructor.newInstance(fileArgs);
packageParserConstructor.setAccessible(true);//允许访问
typeArgs = new Class[4];
typeArgs[0] = File.class;
typeArgs[1] = String.class;
typeArgs[2] = DisplayMetrics.class;
typeArgs[3] = int.class;
Method pkgParser_parsePackageMtd = packageParserClass.getDeclaredMethod("parsePackage", typeArgs);
pkgParser_parsePackageMtd.setAccessible(true);
valueArgs = new Object[4];
valueArgs[0] = new File(apkFile);
valueArgs[1] = apkFile;
valueArgs[2] = metrics;
valueArgs[3] = PackageManager.GET_SIGNATURES;
pkgParserPkg = pkgParser_parsePackageMtd.invoke(packageParser, valueArgs);
}
typeArgs = new Class[2];
typeArgs[0] = pkgParserPkg.getClass();
typeArgs[1] = int.class;
Method pkgParser_collectCertificatesMtd = packageParserClass.getDeclaredMethod("collectCertificates", typeArgs);
valueArgs = new Object[2];
valueArgs[0] = pkgParserPkg;
valueArgs[1] = PackageManager.GET_SIGNATURES;
pkgParser_collectCertificatesMtd.invoke(packageParser, valueArgs);
// 应用程序信息包, 这个公开的, 不过有些函数变量没公开
Field packageInfoFld = pkgParserPkg.getClass().getDeclaredField("mSignatures");
Signature[] info = (Signature[]) packageInfoFld.get(pkgParserPkg);
return getPublicKey(info[0].toByteArray());
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (NoSuchMethodException e) {
e.printStackTrace();
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
private static String getPublicKey(byte[] signature) {
try {
/**
* Created by zeng
* 想要了解更多关于X509,请自行百度
* 创建X509证书工厂类
*/
CertificateFactory certFactory = CertificateFactory.getInstance("X.509");
//创建证书对象
X509Certificate cert = (X509Certificate) certFactory.generateCertificate(new ByteArrayInputStream(signature));
//得到证书公钥
return cert.getPublicKey().toString();
} catch (CertificateException e) {
e.printStackTrace();
}
return null;
}
}
以上是防劫持相应做本地apk校验的一种方法
针对App防劫持有以下方法:
1.升级api加入https 这个肯定不用再过多介绍,看了我介绍的retrofit 的https就明白了。
2.下载Api也需要加入https,也不用再做介绍 ,这里强调的是需要对服务器返回的文件进行Hash值校验,防止文件被篡改,通过对文件的Hash值,还要对服务端返回的自定义key的值进行校验,防止不是自己服务器返回错误的文件。
3.安装过程也必须对Apk文件进行包名和签名验证,防止Apk被恶意植入木马,或替换。