ARTICLE DETAIL

资讯详情

深耕网站视觉设计与运营推广的一线实战洞察。

Hypatia 恶意软件扫描器 Android 安装与源码编译指南

Hypatia 恶意软件扫描器 Android 安装与源码编译指南 1. 先把 Hypatia 是谁这件事弄明白再动手装1.1 一个名字撞了三辆车第一次在群里看到有人问「Hypatia 安装报错」的时候我下意识以为是某个数学库。Hypatia 这个名字在开源圈里属于重名重灾区有人拿它命名解析器生成器有人拿它命名科学计算工具还有人拿它命名一款 Android 端的恶意软件扫描器。三种东西的安装路径完全不同出错信息也风马牛不相及。我这篇记录针对的是第三种也就是那款开源、离线、不带广告、不申请联网权限的 Android 恶意软件扫描器包名是us.spotco.malwarescanner源码托管在公开的代码托管平台上同时在 F-Droid 应用仓库里有收录。它的核心思路和传统杀毒软件不太一样它不联网查云端库也不靠行为分析而是把一大批已知恶意文件的特征片段签名预先打包成本地数据库然后拿设备上已安装的应用和存储卡里的 APK 去逐个比对。这个定位决定了它的安装过程有两层含义一层是把 APK 装到手机上另一层是把数据库喂饱。很多人只做了第一层装完之后打开一看「0 个威胁」就以为万事大吉其实数据库还空着扫了个寂寞。这也是我在后面章节要反复强调的点。1.2 它到底能扫什么扫不到什么先说能力边界这个比安装步骤还重要。Hypatia 主要做三件事扫描已经安装到系统里的应用包、扫描存储目录下的独立 APK 文件、以及在你手动开启之后做实时监视这个功能要 root 权限才能跑起来。它比对的是文件里的特征字节序列所以对已知样本的识别率相当可观尤其是那些被反复打包、只改了壳没改核心代码的老牌恶意程序。但它做不到的事情同样明确。它不做动态行为监控一个应用在后台干了什么它管不着它不做流量分析谁在偷偷上传数据它也不管它不查云端信誉库所以对刚刚出炉、还没被收录进签名库的新样本基本无感。所以我的习惯是把它当「体检里的血常规」能查出一批常见指标异常但不能替代整套体检。你要是拿它当唯一防线那思路就跑偏了。另外还有一点容易被忽略它对「非 APK 文件」的覆盖能力有限。DEX、ELF 这些它会看但压缩包套压缩包、加密 payload 这类东西它就无能为力了。所以扫描范围设得太宽反而会拉长扫描时间、拖高耗电收益还不大。1.3 谁适合装谁装了也是白装我大致把会用这个东西的人分成四类。第一类是喜欢折腾设备的人经常从各种渠道装 APK来源不干净的时候心里没底装个扫描器属于给自己的手加一道保险。第二类是帮家里人处理设备的人长辈手机里塞满了来路不明的「清理大师」「红包助手」用这个工具批量过一遍比一个个看图标靠谱。第三类是安全方向的学习者想看看签名匹配这套机制在移动端是怎么落地的源码可读、数据库格式公开是个不错的样本教材。第四类是纯粹的开源爱好者看重它不申请网络权限这一点——一个扫描器不需要联网本身就说明它没有偷偷回传数据的动机。反过来说如果你的设备从来不装第三方来源的应用只走官方商店那这个东西对你的边际价值确实不高。别为了「装个安全软件」而装安全软件这是我一直以来的观点。2. 装之前的环境盘点三条路线先选对再动手2.1 三条安装路线的对比与选型决策我先给结论九成以上的人应该走第一条路线剩下的人再往下看。路线适合人群耗时难度后续升级方式F-Droid 仓库安装普通用户、不想碰命令行5 分钟低应用内一键更新下载官方 APK 手动安装网络环境受限、想固定版本10 分钟低手动下载新包覆盖安装从源码编译想改代码、想做二次分发半天到一天高自己重新构建选路线的判断标准其实就两条你会不会改代码你需不需要锁定某个特定版本两个答案都是「否」直接走 F-Droid别折腾。我见过太多人一上来就想着「我要从源码编译这样才够深入」结果卡在 Android SDK 下载上耗掉一整个下午最后 APK 还是从 F-Droid 装的。真没必要。需要提醒的是源码编译这条路在 Android 生态里成本比在 Python、Node.js 那类生态高得多因为它牵扯到 JDK 版本、Android SDK、构建工具、签名配置一整套链条任何一个环节版本对不上都会报一堆看不懂的错。所以如果你只是为了用真的不建议走这条路。2.2 设备端的前置检查清单动手之前先花两分钟过一遍这几项能省掉后面一大堆麻烦。系统版本这款工具对 Android 版本有最低要求太老的系统装上去会直接解析失败。我这边的测试机是 Android 11 和 Android 13 各一台都正常。存储余量光 APK 本身只有几十兆但如果你要把扩展签名库全拉下来占用会到几个 GB 级别。预留 5GB 比较从容。安装未知来源权限如果走手动装 APK 的路线需要给文件管理器或浏览器开「允许安装未知应用」的权限。装完之后建议把这个开关关回去这是个好习惯。root 状态确认如果你打算用实时监视功能得先确认设备已经取得 root 并且授权管理正常工作。没有 root 也能用只是少了实时这一块。系统自带安全组件的冲突检查部分定制系统会对第三方扫描类应用做限制比如后台被冻结、被强制休眠。装完记得把它的电池优化关掉否则扫描跑到一半会被掐断。2.3 桌面端编译环境需要准备什么如果你确实要走编译路线先把这个清单里的东西备齐不要边装边找。Git拉源码用顺带管理你自己的改动。JDK注意版本Android 构建对 JDK 版本敏感版本过高或过低都会报Unsupported class file major version这类错。Android SDK 命令行工具不一定非要装完整的 Android Studio命令行工具包加平台包、构建工具包就够了能省下好几个 GB 的磁盘。Gradle项目一般自带 wrapper不用单独装但首次运行会去下载对应版本的 Gradle 发行包。可选Python如果你要自己处理签名库、做批量转换或者写比对脚本装个 Python 会方便很多。用 conda 管环境也行我个人偏好 miniconda 起一个干净环境避免和系统自带的 Python 打架。3. 路线一不写一行代码十分钟装完3.1 走 F-Droid 仓库的完整流程F-Droid 本身是一个只收录自由软件的应用商店它的客户端你可以从官网直接下载 APK。装好客户端之后第一次打开会做一次仓库索引刷新这个过程有点慢因为它要拉取全部应用的元数据。网络条件一般的时候可能要几分钟耐心等一下别反复杀进程。索引刷新完之后在搜索框里输入应用名正常情况下会直接命中。点进详情页往下翻能看到权限列表——这是我最喜欢 F-Droid 的地方它把权限列得很清楚。你会注意到这个扫描器没有网络权限这一点值得单独看一眼因为它意味着这个应用在系统层面就没法往外传数据。点击安装等它下载完自动安装即可。如果系统弹窗提示「出于安全考虑已阻止安装」去设置里给 F-Droid 客户端开一下安装权限就行。注意F-Droid 版本通常不带扩展签名库因为那些库的授权条款和仓库的分发政策不完全兼容。装完之后你还需要在应用内手动下载数据库这一步别漏。3.2 手动装 APK 时怎么确认包没被动过从代码托管平台的 Release 页面下载 APK 的人不少但很少有人做校验。我建议养成习惯因为一个扫描器如果本身被动过手脚那场面就有点讽刺了。通常发布页会同时提供 APK 文件和它的哈希值或者提供 GPG 签名文件。校验方式很简单# Linux / macOS shasum -a 256 Hypatia-release.apk # Windows PowerShell Get-FileHash .\Hypatia-release.apk -Algorithm SHA256把输出结果和发布页上写的值逐字符对比一致就可以放心装。不一致就别装了重新下载一遍再说。这一步花不了一分钟但能挡掉相当一部分「下载站二次打包」的风险。GPG 校验稍微麻烦一点需要先导入发布者的公钥然后验证签名文件。如果你只用一两次哈希校验够用了。3.3 第一次打开必须做的几件事装完之后先别急着点扫描按下面的顺序配一遍能少走弯路。第一进设置把数据库下载好。常见的有基础库和扩展库两个层级基础库体积小、命中常见样本扩展库体积大、覆盖面广。我一般是先下基础库跑一遍确认设备没问题之后再把扩展库拉下来。第二调整扫描范围。默认设置可能会把整个存储卡都扫一遍包括你的照片、视频、下载的安装包这在第一次跑的时候会非常慢。我的做法是第一次只扫「已安装应用」第二次再单独扫下载目录。第三决定要不要开实时监视。开了之后新落地的 APK 会被自动检查但需要 root而且会带来持续的后台开销。如果你设备性能一般建议关掉。第四把应用加到电池优化白名单里否则后台扫描很容易被杀掉表现为「进度条卡在某个百分比不动」。第五看一眼通知权限。扫描结果是通过通知栏推送的权限被关了你会以为它什么都没发现。4. 路线二从源码编译把整条链路打通4.1 Git 安装与仓库拉取Git 的安装各平台差异不大。Windows 上直接下安装包一路下一步注意在「Adjusting your PATH environment」那一步选第二个选项从命令行和第三方软件都能调用不然之后在命令行里敲git会提示找不到命令。macOS 上装了 Xcode 命令行工具就自带 Git或者用 Homebrew 装一个更新的版本。Linux 上一条包管理命令搞定。装完之后先做最基本的身份配置这步很多人跳过结果第一次提交时被卡住git config --global user.name your-name git config --global user.email youexample.com git config --global core.autocrlf input # Windows 上建议 truecore.autocrlf这一项在 Windows 上特别重要如果设错拉下来的脚本文件换行符会变成 CRLF构建脚本一执行就报「bad interpreter」之类的怪错。我第一次在 Windows 上编译 Android 项目就栽在这上面排查了快两个小时。然后克隆仓库。如果你的网络访问代码托管平台速度不理想可以先在浏览器里下载 ZIP 包解压效果一样只是后续不能直接git pull更新。4.2 JDK 的安装与版本选择Android 构建对 JDK 版本相当挑剔。经验值是这样较老的构建工具链配 JDK 8 或 11新一些的配 JDK 17。版本不匹配最典型的表现是构建时报Unsupported class file major version 61这类信息数字对应的是 JDK 版本号。Windows 上装 JDK 之后要手动配环境变量新建JAVA_HOME指向 JDK 安装目录然后把%JAVA_HOME%\bin追加到Path里。注意是追加不是覆盖把原来的 Path 内容删掉会导致一堆系统命令失灵这个坑我见人踩过不止一次。Linux 和 macOS 上如果同时装了多个 JDK可以用update-alternatives或者JAVA_HOME切换。macOS 上有个小工具叫jenv挺好用能按目录自动切版本。验证方式java -version javac -version两个命令输出的版本号应该一致。如果不一致说明PATH里混进了别的 JDK 的javac这种情况在 Windows 上尤其常见因为有些软件会偷偷往 Path 里塞自己的 JRE。4.3 Android SDK 与构建参数配置不装完整版 Android Studio 的话下载命令行工具包就够了。解压到一个不含中文和空格的路径下这一点很重要路径里有空格会导致一堆脚本解析失败。然后在cmdline-tools目录下建一个latest子目录把内容挪进去再用sdkmanager装平台包和构建工具sdkmanager platform-tools platforms;android-34 build-tools;34.0.0在项目根目录建一个local.properties写清楚 SDK 位置sdk.dir/path/to/android-sdkWindows 上的路径要写成C\:\\Android\\sdk这种转义形式直接写C:\Android\sdk会被当成转义字符处理报路径找不到。这个细节坑过不少人。再调一下gradle.properties里的内存参数。默认堆内存往往不够编译到一半会抛Java heap space错误org.gradle.jvmargs-Xmx4096m -XX:MaxMetaspaceSize1024m org.gradle.paralleltrue org.gradle.cachingtrue-Xmx的值按你机器的物理内存来定一般给到物理内存的三分之一到一半。我给 4GB 是为了稳妥机器只有 8GB 内存的话给 3GB 就行别把系统挤爆。4.4 首次构建与真机安装准备就绪之后执行./gradlew assembleRelease # Windows gradlew.bat assembleRelease第一次跑会下载 Gradle 发行包和一大堆依赖耗时取决于网络。如果卡在某个依赖上不动可以换成公开的软件源加速Maven 仓库和 Gradle 发行包都有对应的公开源可用改一下build.gradle里的仓库地址和gradle-wrapper.properties里的distributionUrl就行。构建成功后产物在app/build/outputs/apk/release/下。注意 Release 包需要签名才能安装没配签名的话会生成一个-unsigned.apk装不上。签名配置写在build.gradle里signingConfigs { release { storeFile file(my-release-key.jks) storePassword your-password keyAlias my-key keyPassword your-password } }密钥库用 JDK 自带的keytool生成keytool -genkeypair -v -keystore my-release-key.jks -keyalg RSA -keysize 2048 -validity 10000 -alias my-key装到设备上adb install -r app/build/outputs/apk/release/app-release.apk-r表示覆盖安装保留数据。如果提示签名不一致说明设备上已经装了一个用别的密钥签名的版本得先卸载。注意自己编译的包和设备上原有的 F-Droid 版本签名不同无法互相覆盖升级。想切换到自编译版本必须先卸载原版这会丢掉已有的扫描记录和数据库缓存。5. 数据库、扫描策略与结果解读5.1 签名数据库是怎么组织的这是很多人装完之后最迷惑的部分。数据库本质上是一堆「特征片段 威胁名称」的映射表按来源分成几个大类。基础库体积最小收录的是广泛流传的常见样本扩展库覆盖更全但体积成倍增长还有几个来自不同安全厂商的库体积更大适合对检出率有更高要求的人。我的建议是分阶段加载。先上基础库跑一遍全盘看看有没有明显问题。日常使用基础库足够了因为绝大多数用户在设备上碰到的都是那批老面孔。只有在你做样本分析、或者有明确的深度扫描需求时才值得把几个 GB 的扩展库拉下来。数据库更新频率不算高我一般是每个月手动检查一次有没有新版本。更新的方式是应用内下载覆盖不需要重装应用。5.2 扫描范围和耗电之间的取舍全盘扫描一次在中端机上大概要十几分钟到半小时主要耗时在读取文件字节和字符串匹配上。这个过程中 CPU 会持续跑高手机明显发热耗电也快。我的做法是插着充电器、连上 Wi-Fi虽然它不联网但至少屏幕可以调暗、把手机放着别动让它安安静静跑完。扫描线程数这个设置项值得调。线程多了理论上更快但也会更快耗尽电池、更容易触发系统温控降频。中端机给 2 到 4 个线程比较合适旗舰机给 4 到 6 个也行。我实测过把线程拉满速度并没有线性提升反而因为频繁上下文切换变得更慢这个结论和很多人的直觉相反。还有一点实时监视开启后每次有新的 APK 落地就会触发一次检查如果这段时间你在批量安装应用会感觉到明显的卡顿。批量操作的时候我会临时把它关掉。5.3 扫描结果怎么读日志怎么导出扫完之后结果页会列出命中的条目每条包含文件路径和匹配到的威胁名称。这里有个容易误判的地方路径在/data/app/或者存储卡下载目录下的独立 APK通常可以直接删掉路径指向系统分区里的文件就要格外谨慎删了可能导致系统功能异常。我个人的处理流程是先看路径再看威胁名称最后决定动作。对于明确是下载目录里的安装包直接删。对于已安装应用的命中通常意味着这个应用有问题卸载是唯一安全的选择。对于系统目录下的命中我的建议是先做记录、导出报告、再去社区查证是不是误报不要贸然动手。导出这块工具本身提供的结果分享功能可以生成文本清单方便你存档或者发到社区里讨论。我在帮别人处理设备的时候习惯先导出一份留底处理完再导出一份前后对比能看出哪些是历史遗留、哪些是新冒出来的。6. 我踩过的坑问题排查速查表6.1 安装与首次运行阶段现象大概率原因处理方式解析包时出现问题下载包不完整或系统版本过低重新下载确认系统版本满足要求安装被系统阻止未开启未知来源安装权限在设置里给安装来源授权打开后闪退系统组件冲突或数据残留清除应用数据后重开仍不行则卸载重装扫描进度长期停在 0%存储权限未授予检查权限设置授予文件读取权限扫出 0 个结果但明显可疑数据库为空或未下载进设置下载基础签名库后重扫6.2 编译阶段的典型报错报错一Unsupported class file major version。这就是 JDK 版本和构建工具链对不上。数字 52 对应 JDK 855 对应 JDK 1161 对应 JDK 17。照着报错里的数字换对应版本的 JDK 就行。报错二SDK location not found。要么是没建local.properties要么是路径写错要么是 Windows 上的反斜杠没转义。三个方向依次排查。报错三Java heap space。调大gradle.properties里的-Xmx。我一开始给 2GB编译到资源合并阶段就爆了加到 4GB 之后一次过。报错四构建卡在下载依赖不动。换成公开的软件源或者手动把依赖包放到本地缓存目录里。这种情况在代码托管平台访问不顺畅的时候特别常见。报错五Failed to install the following Android SDK packages as some licences have not been accepted。用sdkmanager --licenses跑一遍把所有协议手动同意掉。6.3 使用阶段的异常有一段时间我的设备上扫描会在 70% 左右卡死反复重启也没用。后来发现是系统的电池优化策略在后台把进程冻结了把它加进白名单之后就正常了。这个问题的迷惑性在于它表现得像应用崩溃其实是被系统掐掉的。还有一次是实时监视功能开了之后通知栏里出现大量重复告警同一个文件被反复报告。原因是监控目录覆盖了两个有重叠的路径同一份文件被两套规则各扫了一遍。把重叠的路径去掉之后就清净了。另外提醒一句扫描期间如果同时插着 SD 卡扫描时间会明显拉长因为外置存储的读取速度比内置存储慢不少而且频繁唤醒会带来额外的功耗。有条件的话先扫内置存储再单独扫外置卡。6.4 三个我印象最深的独家坑第一个是数据库下载到一半断网。由于不做断点续传下载中断后数据库文件会处于一个不完整的中间状态应用读取时不会明确报错只是检出率莫名其妙变低。解决办法是把数据库目录里的残留文件删干净重新下一次。判断方法是对比文件大小和官方标注的大小是否一致。第二个是系统语言导致的界面错乱。部分版本在某些语言环境下设置页会出现文字截断看起来像是某个选项消失了。切成英文界面就能看到完整的选项。这不是功能问题纯粹是布局适配没做全知道这点就不会慌。第三个是扫描过程中拔插存储卡。这个操作会让扫描进程的路径索引失效表现是扫描突然跳到 100% 然后显示异常结果或者直接退回到主界面。扫描期间别动存储这算是个使用纪律。7. 几个能明显提升效率的实操心得7.1 建立自己的扫描节奏而不是想起来才扫我现在的习惯是固定周期做这件事装完一批新应用之后扫一次插上外置存储之前扫一次家里长辈的设备每个月帮他们扫一次。固定的节奏比「感觉不对劲了才扫」靠谱得多因为这类扫描工具的价值在于覆盖已知样本而不是发现未知威胁规律性使用才能体现它的意义。另外建议把设备的应用来源收一收。我从很久之前就不再从论坛附件、网盘分享这类渠道装应用了唯一例外是确实需要、且能找到官方发布页的。这件事做下来扫描命中的次数肉眼可见地变少了。工具能帮上忙但源头上的自律作用更大。7.2 把编译环境当成一次性投入配好就复用从源码编译这条路第一次配置确实费劲但配好之后就很省事。我现在的做法是把 JDK、Android SDK、Gradle 缓存都放在一个固定目录里换项目直接复用不用每次重来。Gradle 缓存目录尤其值得保留它能让第二次构建的速度提升好几倍。如果你同时还在折腾 Python、Node.js、Docker 这些环境我的建议是给每个生态单独建目录、单独配环境变量不要全都塞进系统PATH里。混在一起久了迟早会碰到版本互相干扰的问题那时候排查起来非常痛苦。用 conda 或者类似的虚拟环境工具隔离 Python 环境是个好习惯我在处理签名库脚本的时候就专门起了一个干净环境避免和系统 Python 的依赖打架。再补一句关于构建产物的存放我习惯把每次编译出来的 APK 按版本号重命名后存档同时把对应的local.properties和签名配置单独备份。设备出问题时想回滚到旧版本手边有现成的包会省很多事。这些看着是小事真到用的时候就知道值了。最后分享一个判断异常的小窍门如果一个文件被扫出问题但你看不明白威胁名称先把它复制一份到隔离目录再处理不要原地删。等你查清楚了确实是误报还能还原回去。手动删除的东西没有回收站这个原则我在处理任何安全告警时都守着。
返回列表