尧图精选

手机杀毒扫描全判安全?用 TaoToken 接入的 Codex 对着 antivirus.db 排查

🕒 发布时间:2026/9/18 1:20:30 📁 来源:尧图网络
手机杀毒 demo 跑起来进度条走完弹出一句「扫描完毕,你的手机很安全」——但 antivirus.db 里明明有测试 md5怎么一个病毒都扫不出来这个场景用 TaoToken 接入 Codex 排查之前先打开 https://taotoken.net/?utm_sourcetaotoken_aicg_blog_end 注册并创建 Key后面 Codex 的 Base URL 会填 https://taotoken.net/api。最容易被忽略的不是 AntiVirusActivity.kill() 的遍历而是病毒库有没有真的从 assets 落到 /data/data/com.example.mobilesafe/files/antivirus.db。把这条链路拆成四段AssetUtil.copy1 拷库、AntiVirusDao 开库、Md5Encoder 算签名、AntiVirusActivity 比对再让 Codex 逐段复述比一上来就改 Java 有效得多。1. 全判安全先从 antivirus.db 有没有落到 files 目录查起1.1 AssetUtil.copy1 的 file.exists() file.length()0 到底在看什么原文在 SplashActivity 创建更新线程后又起了一条拷贝线程核心判断是文件存在且长度大于 0 就认为已经拷贝成功否则调用 AssetUtil.copy1 把 assets 里的 antivirus.db 写到 getFilesDir() 下面。这个判断只说明「文件存在且不是 0 字节」并不能说明库是完整的。如果上一次拷贝到一半进程被杀文件长度大于 0 但表数据不完整下一次启动就会直接跳过拷贝。AntiVirusDao 打开这个半截库时openDatabase 不一定抛异常rawQuery 也可能正常返回一个空 cursor。每个应用查不到记录getVirusInfo 返回 nullAntiVirusActivity 就把它们全归到「安全」。排障时先让 Codex 对照 AssetUtil.copy1 的读入长度、写入长度、asset 原始 available()把「文件存在」「文件完整」分开看。更稳的做法是拷贝到临时文件校验长度和表数量后再 rename 覆盖正式库。1.2 拷库线程没跑完就点扫描Codex 帮你画出时序SplashActivity 的拷贝线程和 AntiVirusActivity 的 kill() 是两个独立的触发点。用户在拷贝还没结束时就点「一键查杀」AntiVirusDao 可能打开一个空库或不完整库db.isOpen() 照样是 truecursor.moveToFirst() 却拿不到任何记录。结果就是每个安装包都收到 SCAN_NOT_VIRUS进度条走完最后弹「你的手机很安全」。让 Codex 做一件具体的事把「SplashActivity 启动拷贝线程」「用户点击查杀」「AntiVirusDao 打开数据库」三个时间点标出来问它如果拷贝需要 800ms、用户在 300ms 就点了按钮哪些变量会是空、哪些集合会是空。Codex 不直接改代码但它能把线程交错讲清楚。确认卡点后再决定是加 CountDownLatch还是给 AssetUtil.copy1 外包一层完成回调等病毒库 ready 后再放开扫描按钮。2. AntiVirusDao 里 openDatabase 路径与 datable 查询链路对照2.1 /data/data/com.example.mobilesafe/files/antivirus.db 这个硬编码路径原文 AntiVirusDao.getVirusInfo 里把路径写死成/data/data/com.example.mobilesafe/files/antivirus.db再用 OPEN_READONLY 打开。包名一改、debug 和 release 的 applicationId 后缀不同或者安装到了工作资料里这个绝对路径指向的就不是同一个目录。db.isOpen() 返回 false 时函数直接返回 null调用方看到 null 就当成「不是病毒」界面照样走完只是全判安全。让 Codex 对照 AndroidManifest.xml 里的 package 和 build.gradle 里的 applicationId确认 files 目录到底在哪。更稳的写法是用context.getFilesDir()拼文件名而不是手写绝对路径。改完以后让 Codex 复述一遍「路径怎么来、库怎么开、null 从哪返回」再回手机验证。2.2 rawQuery 为什么永远返回空md5 字段、大小写、desc 列原文查询语句是select desc from datable where md5?。这里有两个容易踩的点。第一desc 是 SQL 关键字不同 SQLite 版本对关键字做列名的容忍度不一样如果建表时列名不叫 desc查询就会报错或返回空。第二md5 字段存的是大写还是小写十六进制。Md5Encoder 用 Integer.toHexString 得到的是小写如果病毒库生成脚本写库时用了大写where md5?就永远匹配不上。让 Codex 生成两条 SQL读者在本地 sqlite3 里执行把输出贴回对话SELECT sql FROM sqlite_master WHERE name datable; SELECT md5, desc FROM datable LIMIT 5;先看表结构再看前几条 md5 的长度和大小写。Codex 不替你连库它只根据你贴回来的结果判断是改查询、改 Md5Encoder还是重新导病毒库。2.3 cursor 忘了 close 与“安全”结论无关但会掩盖真正的 error原文注释强调 cursor 必须关闭否则内存泄漏。这个提醒没错但排障时要把两件事分开cursor 没关不会让 result 变成 null它造成的是资源泄漏真正让扫描全判安全的是 cursor.moveToFirst() 返回 false或者 db.isOpen() 为 false。如果一上来就盯着 cursor.close()容易忽略前面的空返回。可以问 Codex「把 getVirusInfo 的所有 return 点列出来每个 return 对应什么现象。」它会给你一张表db 没打开返回 null、cursor 空返回 null、查到记录返回 desc、查到空 desc 返回空字符串。拿到这张表再回手机上看是哪种 null。区分清楚以后cursor.close() 该补还是补但它不是「全判安全」的根因。3. Md5Encoder 输出的十六进制和签名串交给 Codex 逐字节对3.1 toCharsString 之前先理解 signatures[0]AntiVirusActivity.kill() 里先取info.signatures[0].toCharsString()再交给 Md5Encoder.encode。这里的输入不是 APK 文件的二进制而是 Signature.toCharsString() 返回的签名字符串。如果生成病毒库时是对 APK 文件做 MD5两边口径就不一样查不到是正常的。让 Codex 把「病毒库里的 md5 是怎么生成的」作为一个未知项列出来然后你用本地脚本对同一个 APK 分别做 toCharsString 的 MD5 和文件 MD5看哪个能对上。对不上的那条链路才是要改的地方。3.2 大小写、补零、 0xff 这些细节的比对表Md5Encoder 的逻辑是取 MessageDigest 的 MD5 字节数组对每个 byte 做 0xff再用 Integer.toHexString 转十六进制长度为 1 时前面补 0最后拼成 32 位小写字符串。这段本身没问题问题通常出在库的那一边。可以列一张对照表环节原文写法排障时核对摘要输入signatures[0].toCharsString()病毒库生成脚本是否用同一输入字节转十六进制Integer.toHexString(number)输出小写补零str.length()1 补 0确保 32 位位运算result[i] 0xff负数 byte 不变成 8 位库中存储md5 字段大小写、是否带空格、长度如果 Codex 发现库里存的是大写你有两个选择把 Md5Encoder 的结果 toUpperCase()或者在 SQL 里写where lower(md5)?。前者的影响面小后者可能要改查询语句。改完一处就让 Codex 复述「输入签名串 → 转 MD5 → 查 datable → 返回 desc」的链路再回手机验证。3.3 让 Codex 生成一段 SQL 在本地 sqlite3 里手动查把 assets 里的 antivirus.db 拷到电脑上用 sqlite3 打开执行SELECT count(*) FROM datable; SELECT * FROM datable WHERE md5 你的32位小写md5;如果 count(*) 是 0说明库本身是空的跟查询写法无关如果 count 正常但按 md5 查不到再把手机里算出来的 md5 贴回 Codex让它逐字符对比。注意这一步必须在本地 sqlite3 或 Android Studio 的 Database Inspector 里做Codex 只负责根据你贴回的结果给判断不能让它直接连你的手机或库。4. AntiVirusActivity.kill() 的遍历顺序与扫描动画4.1 getInstalledPackages(GET_SIGNATURES) 拿到的是什么原文在 kill() 里用pm.getInstalledPackages(PackageManager.GET_SIGNATURES)拿到所有安装包再逐个取info.signatures[0]做 MD5。GET_SIGNATURES 拿的是签名数组大多数应用有签名但某些系统应用或预装应用的 signatures 可能为空数组。直接取 [0] 会抛 ArrayIndexOutOfBoundsException子线程挂掉SCAN_FINISH 消息发不出来界面可能停在旋转动画上也可能因为 handler 没收到完成消息而不弹结果。让 Codex 在 kill() 里检查info.signatures ! null info.signatures.length 0先把异常堵住再谈查毒。空签名不是病毒库的问题但它会让「扫描完成」这个结论变得不可信。4.2 handler 消息顺序与“扫描完毕”弹窗的时机handler 处理三种消息SCAN_NOT_VIRUS 往 LinearLayout 顶部加「扫描 XX 安全」FIND_VIRUS 把 PackageInfo 加进 virusPackInfosSCAN_FINISH 在 virusPackInfos.size()0 时弹「扫描完毕,你的手机很安全」。如果子线程因为空签名、空库、SQL 异常提前退出SCAN_FINISH 可能根本没发如果发了但 virusPackInfos 一直是空就会弹安全。排障时让 Codex 把每个 Message 的发送点和接收点列出来特别是 count 和 progressBar1.setProgress(count) 的关系。如果进度条走完了但 count 小于 packinfos.size()说明循环提前结束跟病毒库无关是遍历本身挂了。4.3 补一个拷库完成回调再跑一遍最直接的改法是在拷贝线程里加一个「库就绪」标志。示意new Thread() { public void run() { File file new File(getFilesDir(), antivirus.db); boolean needCopy !file.exists() || file.length() 0; if (needCopy) { AssetUtil.copy1(getApplicationContext(), antivirus.db, file.getAbsolutePath(), null); } VirusDbState.setReady(true); } }.start();AntiVirusActivity.kill() 开头检查VirusDbState.isReady()没就绪就提示「病毒库正在准备请稍后再试」。这不是替代原文逻辑只是把「拷库」和「扫描」之间的时序补上。改完让 Codex 复述从 SplashActivity 到 AntiVirusActivity 的调用链再回手机跑一次看是否还全判安全。5. Codex 配置与验证5.1 ~/.codex/config.toml 里把 base_url 指到 https://taotoken.net/api前面一直说让 Codex 帮你逐段核对这里把配置补全。先打开 TaoToken 注册在控制台创建 API KeyKey 用 YOUR_API_KEY 占位。然后编辑~/.codex/config.tomlmodel YOUR_MODEL_ID model_provider taotoken [model_providers.taotoken] name TaoToken base_url https://taotoken.net/api env_key TAOTOKEN_API_KEY wire_api chat环境变量export TAOTOKEN_API_KEYYOUR_API_KEY注意 Base URL 是https://taotoken.net/api末尾不要加/v1也不要拿官网落地页当 Base URL。模型 ID 以 https://taotoken.net/?utm_sourcetaotoken_aicg_blog_end 模型广场当时列表为准不要凭记忆写不存在的 ID。改完配置重启 Codex让它先复述当前使用的 provider 和 base_url确认没有走回默认通道。5.2 用同一把 Key 在模型对话里先发一条消息配置写完后别急着让它改 Java。先用同一把 Key 在 TaoToken 模型对话 里发一条测试消息确认三件事Key 有没有复制错、模型 ID 在不在你的权限内、Base URL 是不是写成了官网地址。如果模型对话里正常、Codex 里 401优先检查 TAOTOKEN_API_KEY 是否 export 到了当前 shell以及 config.toml 里的 env_key 名字有没有拼错。5.3 排障对照表拷库、查询、MD5、游标现象可能原因在 Codex 里怎么问本地验证全部应用都弹安全病毒库没拷完对照 SplashActivity 线程和 kill() 启动点看 files 目录下 antivirus.db 大小rawQuery 返回空md5 大小写不一致对比 Md5Encoder 输出和库中字段sqlite3 查一条已知 md5扫描中途停住signatures 空数组检查 kill() 里 info.signatures[0]logcat 看异常堆栈内存泄漏cursor 未关闭检查 cursor.close 的位置与全判安全分开定位Codex 401Key 或 env_key 不对让 Codex 复述 provider 配置模型对话里用同一把 Key 测试这张表建议直接贴进对话让 Codex 按行给出检查顺序。每改一处让它复述一次查询链路再回手机验证。TaoToken 在这里只负责模型通道的 Key 和 Base URL不替代任何一行 Java 逻辑真正跑 SQL、看 logcat、验证 APK 签名的还是你本地的 Android Studio 和 sqlite3。6. 跑通之后回到控制台对一下这次调用配置和排查都过了之后去 TaoToken 模型对话 用同一把 Key 发一条消息确认通道正常长期用 Codex 写代码的话可以打开 Coding Plan 看套餐是否够用Key 在 控制台 API Keys 创建。模型 ID 还是以 https://taotoken.net/?utm_sourcetaotoken_aicg_blog_end 模型广场当时列表为准别拿旧截图里的名字硬填。再提醒一句手机杀毒 demo 的 antivirus.db 是本地 assets 里的测试库Codex 能帮你对照 AssetUtil.copy1、AntiVirusDao、Md5Encoder 和 AntiVirusActivity 的链路但诊断 SQL、编译运行、logcat 都得在本地 Android Studio 或 sqlite3 里执行再把结果贴回对话。全判安全这种问题最怕的是把「库没就绪」当成「手机真的安全」。先把拷库完成回调补上再按 md5 逐条查通常就能看到到底是什么时候返回了 null。
上一篇/下一篇内容由系统自动关联 返回资讯列表 →