背景
VirusTotal 是一个非常流行的在线恶意软件扫描平台,用于检查文件或网址是否包含恶意代码。它集成了多个杀毒引擎(例如 Avast、Kaspersky、McAfee 等),开发者可以通过上传 APK 文件或输入 URL 来检测是否有病毒或恶意行为。
某些应用市场上架apk需要通过VirusTotal检测,可能会遇到误报问题,比如:
这是一个通过静态分析的过程,通过对文件内容进行扫描并与指纹特征相匹配来得出结果,估计是因为算法匹配有问题,导致误报了,我们的apk并没有存在恶意代码。
要让扫描变绿,首先要了解具体是哪里的代码或者资源有问题,从网站给出的报告结果来看,并没有得出什么具体信息,它只是给出了病毒特征得名称Trojan-Dropper.AndroidOS.MGV,而Google得检测更是没有任何信息,只是说检查未通过。 幸好VirusTotal的检测是支持上传任意文件的,而且可以无限制上传检查,那么我们就可以通过排除法,不断上传检测,得到我们想要的结果。当然我们不是通过修改源码打出apk包,不断上传apk包,这样只能碰运气了。
我们可以解压出apk,针对里面的dex,so和资源文件进行单独上传检测,得出是哪一部分文件出了问题,然后不断排除细化缩小范围,得到具体出问题代码片段。这里我举例dex中的代码存在问题,如何定位到是哪一行代码存在“病毒”。
处理过程
1. 找到是什么类型文件报毒
首先,我们把解压apk出来的文件分成3个部分并压缩成zip,上传VirusTotal,这3个部分为:
dex.zip:所有Android虚拟机字节码文件classes.dex组成的zip
lib.zip:所有linux elf格式的文件.so组成的zip
ext.zip:剩余的其他资源等文件
上传3个文件,看是哪一部分被检测到”病毒“,然后继续细化排除。
2. 找到对应的dex文件
我这里是dex存在问题,那么就定位到具体是哪个dex,classes.dex可能也是有10个文件,那最多要上传10次,那也是挺麻烦的。我们可以采用二分法进行排除,减少上传次数。把classes.dex分成两部分,压缩成zip进行上传,慢慢细化,找到具体是哪一个dex文件存在问题。后面对dex里面的代码进行排除定位,那更是体力活,通过二分法减少上传次数。
3. 找到对应的smali文件
对于dex文件中的代码,我们可以通过smali 和 baksmali进行汇编和反汇编,得到smali代码,然后删除一半文件,汇编打包成dex上传VirusTotal检测,看是否检测通过。
也可以使用apktool的,apktool也是通过使用smali 和 baksmali进行处理的,打包apk时生成的dex在build文件夹下,apktool会进行多一些代码变化检查和打包apk的步骤,打包apk步骤无视即可。
下载smali 和 baksmali地址:bitbucket.org/JesusFreke/…
使用方法:
反汇编:
java -jar baksmali.jar disassemble classes.dex` `汇编:java -jar smali.jar assemble ./out -o classes.dexapktool
官网:apktool.org/
apktool d simple.apk
apktool b simple
通过一番体力操作,重复的删除smali文件和检测,就可以筛选出是哪个smali文件了,只要汇编这个文件成dex,上传这个文件,那就会检测不通过,问题就出这里了
4.找到对应的代码
找到smali文件后,下面就可以对文件中的指令进行删除和检测。smali语法并不复杂,而且我们也不需要写什么smali代码,只是删除一下代码,看得懂结构即可。
定义类:
.class public Lcom/unity3d/services/core/api/Broadcast; .super Ljava/lang/Object; .source "Broadcast.java"
定义方法:
.method public static addBroadcastListener(Ljava/lang/String;Ljava/lang/String;Lorg/json/JSONArray;Lcom/unity3d/services/core/webview/bridge/WebViewCallback;)V
.end method
定义变量:
.field private static final volumeChangeMonitor:Lcom/unity3d/services/core/device/VolumeChangeMonitor;
我们可以从尾部代码,按照行数开始删除,删除一半的.method块,以此往复,经过几轮检测,就能找到存在问题的方法块了。
修复风险问题
既然找到了源头,就可以对照一下源码进行对比分析。
打开jadx-gui,把有问题的classes.dex拖进去查看反汇编的代码,找到对应方法与源码进行对比,看有什么不同。然后尝试修改方法名,删除方法内容等处理继续上传,定位具体问题,就能从根本上规避掉检测风险了。
引起报毒的问题可能出乎意料,也许是方法名问题,这个要根据实际改一下方法,检测后才能知道了。我这里贴一下我这边出现的问题,是因为使用java的lambda表达式,自动生成方法名有问题:
源码:
public void releaseEglSurface(final Runnable completionCallback) {
renderThreadHandler.postAtFrontOfQueue(() -> {
eglBase.releaseSurface();
completionCallback.run();
});
}
混淆编译后的有问题的代码:
public void lambda$releaseEglSurface$6(Runnable runnable) {
this.eglBase.releaseSurface();
runnable.run();
}
可以看出lambda表达式自动生成了lambda6方法,而这个方法可能被认为是恶意程序指纹,所有就被检测到了,这个应该是安全厂商算法检测有问题。
这里可以把postAtFrontOfQueue里面的lambda表达式,改成使用new Runnable,run方法执行即可,不让编译器自动生成方法,这样就解决误报的问题。
public void releaseEglSurface(final Runnable completionCallback) {
renderThreadHandler.postAtFrontOfQueue(new EglRunnable(eglBase, completionCallback));
}
总结
安全厂商检测误报问题,只是给出了恶意程序代号名称,但是并没有公布恶意具体原因和细节,这就导致了我们解决困难。apk检测处于一个黑盒子过程,找到根本问题是比较不容易的过程,也可以通过碰运气,加强混淆,或者不混淆代码,移除sdk等绕过风险,不一定能解决。这里通过VirusTotal可无限制验证,然后使用排除法,结合dex的编译修改,不断的缩小范围验证,找到了误报的代码,解决了问题。后续可能是so,资源文件出现误报问题,也可以使用排除法解决。资源的出现问题,找到对应资源替换即可,so的出现问题,就需要使用IDA Pro, Ghidra,Hex Editor等工具,对so的二进制进行分析修改验证了。