一、静态完整性校验
1. 对Dex文件进行完整性校验
- 由于Android 稍高版本在安装apk的过程中会把classes.dex 抠出来转化为odex或者vdex等其他格式的优化文件,原apk中无classes.dex,但是会存在除classes.dex外的其他多dex文件(比如classes2.dex,classes3.dex等),故无法校验classes.dex,也无法校验整个APK文件哈希值
- 通过读取apk包中的除classes.dex外的其他dex文件,并计算其CRC的值,将得到的CRC与原始CRC进行比较,判断是否被修改过,原始CRC值保存在资源文件或其他自定义文件中相关代码:
String apkPath=context.getPackageCodePath();//获取Apk的路径
ZipFile zipFile=new ZipFile(apkPath);
ZipEntry dexEntry=ZipFile.getEntry("classes.dex");//读取zip包中的classes.dex文件
String dexCRC=dexEntry.getCrc().toString();//计算dex的CRC文件值
- 对Dex文件的局部(可以是头部,代码段等等)求MD5值,并将MD5写入dex文 件的末尾或其他不影响dex文件加载 运行的位置(本人暂时还没碰到)
相关代码:MessageDigest digest=MessageDigest.getInstance("MD5");
byte[] bytes=new byte[1024];
FileInputStream fileinputStream=new FileInputStream(new File(dexPath));
int byteCount;
while(fileinputStream.read(bytes)!=-1){
digest.update(bytes,0,byteCount);
}
BigInteger bigInteger=new BigInteger(1,digest.digest());//计算dex的哈希
String MD5=bigInteger.toString();//dex的哈希值
2、对Apk文件进行完整性校验(Android 5.0以下)
通过APK包的MD5摘要进行判断文件是否被修改过,这里需要计算APK的MD5值,然后将MD5上传到服务器进行判断,或者等待服务器下发原始文件的MD5值,进行比较判断。
相关代码:
String apkPath=context.getPackageCodePath();//获取Apk的路径
MessageDigest digest=MessageDigest.getInstance("MD5");
byte[] bytes=new byte[1024];
FileInputStream fileinputStream=new FileInputStream(new File(apkPath));//读取apk文件
int byteCount;
while(fileinputStream.read(bytes)!=-1){
digest.update(bytes,0,byteCount);
}
BigInteger bigInteger=new BigInteger(1,digest.digest());//计算apk的哈希
String MD5=bigInteger.toString();//apk的哈希值
3、签名信息校验。
<1>.通过android.content.pm.PackageInfo.getPacketInfo()函数获取包信息,然后拿到签名信息
<2>.该签名信息为一个byte数组,可以转换成 X.509格式的证书信息,或者直接对该签名信息求哈希
<3>.现阶段为了增加分析的难度,通常都会将部分代码放到.so文件中。
相关代码:
PackageInfo packageInfo=content.getPackageManager()
.getPackageInfo(content.getPackageName()
PackageManager.GET_SIGNATURES);//获取包信息
Signature[] signature=packageInfo.signatures;
Signature sign=signature[0];
MessageDigest digest=MessageDigest.getInstance("MD5");
digest.update(sign);
二、静态完整性校验apk的一些逆向思路
1.获取apk签名需要获取apk的路径,我们可以在获取apk路径的地方下断点。这样可以定位到相关校验代码。通过修改参数,传入一个官方apk路径,也可以绕过校验。
Context.getPackageCodePath() 用来获得当前应用程序对应的 apk 文件的路径:/data/app/包名/xxx.apk
Context.getPackageResourcePath() 获取该程序的安装包路径 : /data/app/包名/xxx.apk
packageInfo.applicationInfo.sourceDir 这里面也可以获取apk路径
2.大部分应用在获取签名信息时都会调用系统api,我们可以在相关的api下断:
- V1签名(Android 7.0以下):
android.content.pm.PackageInfo.getPacketInfo(ClassName,flags).signatures
这里需要注意 当flags为64的时候,该函数会获取签名信息
所以需要在getpackageinfo下断点,当flags为64的时候,就是获取签名信息
- V2签名(Android 7-9):
// 1.反射实例化PackageParser对象
Object packageParser = getPackageParser(path);
// 2.反射获取parsePackage方法
Object packageObject = getPackageInfo(path,packageParser);
// 3.调用collectCertificates方法
Method collectCertificatesMethod = packageParser.getClass(). getDeclaredMethod("collectCertificates",packageObject.getClass(),int.class);
collectCertificatesMethod.invoke(packageParser,packageObject,0);
// 4.获取mSignatures属性
Field signaturesField = packageObject.getClass().getDeclaredField("mSignatures");
signaturesField.setAccessible(true);
Signature[] mSignatures = (Signature[]) signaturesField.get(packageObject);
- V3签名(Android 9及以上):
这几个是v3签名的系统验证函数PackageManagerService.InstallPackageLI()
PackageParser.collectCertificates()
ApkSignatureVerifier.verify()
ApkSignatureSchemeV3Verifier.verify()
ApkSigningBlockUtils.findSignature()
ApkSigningBlockUtils.findApkSigningBlock()
ApkSigningBlockUtils.findApkSignatureSchemeBlock()
SignatureInfo.SignatureInfo()
ApkSignatureSchemeV3Verifier.verify()
ApkSignatureSchemeV3Verifier.verifySigner()
ApkSignatureSchemeV3Verifier.verifyAdditionalAttributes()
ApkSignatureSchemeV3Verifier.verifyProofOfRotationStruct()
ApkSignatureSchemeV3Verifier.VerifiedProofOfRotation()
3.可以在获取哈希相关的api处下断
4.可以通过弹出窗口或者toast提示进行代码回溯,定位到签名校验的附近
三、动态完整性
1.Xpose Hook 检测
(1)检测关键字
de.robv.android.xposed.XposedHelpers类的
静态fieldCache字段 保存被hook的字段信息
静态methodCache字段 保存被hook的方法信息
静态constructorCache字段 保存被hook的类信息
(2)检测内存
检测内存映射列表中是否包含如下文件:
XposedBridge.so
XposedBridge.jar
(3)检测方法的调用栈
handleHookMethod
invokeOriginalMethodNative
注:
1>.在dalvik.system.NativeStart.main方法后出现
de.robv.android.xposed.XposedBridge.main的方法调用
2>如果Xposed hook了调用栈里的一个方法,
还会有de.robv.android.xposed.XposedBridge.handleHookedMethod
和de.robv.android.xposed.XposedBridge.invokeOriginalMethodNative调用
(4)检测标记位
de.robv.android.xposed.XposedBridge类
静态disableHooks 成员变量
这个字段表示是否对当前应用进行hook操作
2.Frida Hook 检测
(1) 检测进程
FridaServer通过TCP与PC上的Frida进行通信,所以可以检测进程中是否存在 FridaServer进程
(2) 检测端口
默认端口:27047
非默认端口:
1>.用nmap -sV来找到开放端口
2>对每个开放端口发送D-Bus认证协议
3>哪个端口回复了哪个就是FridaServer进程的端口
(3) 检测内存
搜索内存是否存在以下两个文件
frida-gadget.so
frida-agent.so`
(4) 暴力扫描
在映射的so文件中扫描Frida的库特征,例如:“gadgets”,“LIBFRIDA”等等,这 两个在Frida的所有版本中都有存在
3.Cydia Substrate Hook检测
(1)检测包名
检测设备安装目录是否存在com.saurik.substrate
(2)检测调用栈
com.android.internal.os.ZygoteInit
com.saurik.substrate.MS$2
注:
1>.在dalvik.system.NativeStart.main调用后会出现2次
com.android.internal.os.ZygoteInit.main,而不是一次。
2>.如果Substrate hook了调用栈里的一个方法,还会出现com.saurik.substrate.MS$2.invoked、
com.saurik.substrate.MS$MethodPointer.invoke和跟Substrate扩展相关的方法
(3)检测内存
检测内存映射中是否存在com.saurik.substrate文件
4.动态完整性校验的一些逆向思路
1.搜索字符串
例如:搜索 de.robv.android.xposed.XposedBridge 或者搜索上述字符串的base64编码后的值
2.获取堆栈的函数下断
大部分应用都是主动触发异常,然后获取堆栈信息,判断里面是否有相关hook的关键字
可以对 getStackTrace() 下断,这个是获取堆栈的api
3.对文件读取函数下断
如果获取内存,需要打开当前进程的 /proc文件,可以对文件读取相关的api下断
例如: new file 、new FildeReader
4.对相关的获取包名下断
函数 getPackageName() 获取相关的包名信息,判断参数里是否包含hook工具的包名
---
[培训]《安卓高级研修班(网课)》月薪三万计划,掌握调试、分析还原ollvm、vmp的方法,定制art虚拟机自动化脱壳的方法
最后于 2020-6-17 11:43
被陌殇编辑
,原因: