NOIR

Project Url: Verlintas/NOIR
Introduction: 手机隐私审计:用 adb 扫出 Android 应用的权限、上报端点与数据流向,并给出可回滚的处置。Desktop Android leak audit: permissions, endpoints, data flows, reversible fixes.
More: Author   ReportBugs   
Tags:
  ███╗   ██╗ ██████╗ ██╗██████╗
  ████╗  ██║██╔═══██╗██║██╔══██╗
  ██╔██╗ ██║██║   ██║██║██████╔╝
  ██║╚██╗██║██║   ██║██║██╔══██╗
  ██║ ╚████║╚██████╔╝██║██║  ██║
  ╚═╝  ╚═══╝ ╚═════╝ ╚═╝╚═╝  ╚═╝

build release license platforms

把「这台手机到底能泄露什么」变成一份可复现的报告。

NOIR 是一个跑在电脑上的审计工具,通过 adb 读取已连接 Android 设备的应用清单、权限和 APK 里的网络端点,给出分类、风险评分和可执行的处置建议。它也可以在需要时调用 jadx 对单个应用做反编译和关键字检索。

定位:审计与自我防护。面向自己的设备、自己的应用、已授权的安全研究。


它做什么

  1. 盘点:列出所有「有网络权限 + 敏感权限」的应用,以及它们各自拿到了什么。
  2. 抽取端点:在设备端用 unzip + strings 从每个 APK 的 dex 里提取 URL,只回传几 KB,不把整包拉回电脑。
  3. 分类与打分:按包名和域名识别厂商(Xiaomi / Google / 腾讯 / 高德 …)与类别(遥测、广告、定位、输入法 …),结合权限和上报端点计算风险分。
  4. 数据关联:把权限翻译成"能拿到什么数据"(位置/通讯录/短信/通话/麦克风/相机/屏幕/应用列表/设备标识/文件/日历/账号),再和上报域名配对,得出"哪类数据可能流向哪里"。
  5. 共享 UID:识别 android.uid.system 这类共享用户,列出哪些包实际上共享同一套权限并集。
  6. 清单层:解析 AndroidManifest.xml 二进制 AXML,找出没有权限保护的导出组件、明文流量、备份、debuggable、targetSdk。
  7. 给建议:区分「可安全自动执行」「需人工确认」「不建议动」,并为每条动作生成回滚命令。
  8. 必要时深挖:deep 会把单个 APK 拉到本地,用 jadx 反编译后检索 Runtime.exec、setApplicationEnabledSetting、DexClassLoader、MediaProjection 等高风险调用。

它不做什么

  • 不联网、不上传任何数据、不需要账号。
  • 不修改系统分区,不需要 root。
  • 不会自动卸载任何东西,除非你显式加 --aggressive。

第一次运行

noir

不带参数会进入向导:

  1. 检查 adb,没有就问你要不要自动下载(~/.noir/tools)
  2. 检查设备,没有就打印开发者模式 / USB 调试的开通步骤,然后等你插线(最多 180 秒)
  3. 问一句「开始扫描?」,扫完直接出仪表盘
  4. 问一句「执行这 19 条安全处置?」,y 就执行,并写好回滚文件

全程只会问两三个 y/n,其余都是自动的。

环境自检

noir doctor
NOIR doctor
  platform  Mac OS X aarch64
  java      17.0.19
  adb       /opt/homebrew/.../platform-tools/adb
            Version 37.0.1-15733141
  device    178b0d2f  device  2509FPN0BC
  jadx      /opt/homebrew/bin/jadx
noir setup          # 缺什么下什么
noir setup --yes    # 不询问,适合脚本

出现问题时

所有错误都会带「怎么办」,而不是抛异常栈:

╭─ ERROR ──────────────────────────────────────────────────────────╮
│ 指定的 adb 不存在:/nonexistent/adb                              │
├──────────────────────────────────────────────────────────────────┤
│ 检查路径,或运行 noir setup 自动安装。                           │
╰──────────────────────────────────────────────────────────────────╯

没有设备时给出的是可执行步骤:

╭─ 没有检测到设备 ──────────────────────────────────────────────╮
│  1. 打开开发者模式                                           │
│     小米 / HyperOS:设置 → 我的设备 → 全部参数 → 连点「MIUI 版本」7 次
│  2. 开启 USB 调试:设置 → 系统 → 开发者选项 → USB 调试        │
│  3. 插线后手机弹「允许 USB 调试吗?」→ 勾选「一律允许」        │
│  4. 还不行就换一根数据线(很多线只能充电)                    │
╰──────────────────────────────────────────────────────────────╯

已连接但没授权时,会单独提示「撤销 USB 调试授权后重插」。

支持平台

系统 状态
macOS (Intel / Apple Silicon) ✅
Windows 10 / 11 ✅
Debian / Ubuntu ✅

依赖:

  • JDK 17+
  • Android platform-tools(adb)
  • jadx(仅 deep 命令需要)

程序会自动在常见安装位置查找 adb 和 jadx;找不到时可以用 --adb / --jadx 指定。

# macOS
brew install --cask android-platform-tools jadx

# Debian / Ubuntu
sudo apt install adb
# jadx: https://github.com/skylot/jadx/releases

# Windows (scoop)
scoop install adb jadx

安装

方式一:下载即用(推荐)

到 Releases 下载对应平台的 portable 包:

noir-v0.1.1-macOS-portable.zip
noir-v0.1.1-Windows-portable.zip
noir-v0.1.1-Linux-portable.zip
SHA256SUMS.txt

解压后双击启动器即可,包内自带运行时,不需要安装 Java:

系统 双击这个 如果被拦
macOS Start NOIR.command 右键 → 打开;或 xattr -dr com.apple.quarantine "Start NOIR.command" NOIR.app
Windows Start NOIR.bat SmartScreen → 更多信息 → 仍要运行(批处理会切到 UTF-8 并调用包内 JRE)
Linux Start-NOIR.sh 先 chmod +x Start-NOIR.sh,或在终端执行

启动器会打开一个终端窗口跑完向导,并在结束时停住(不会一闪而过)。

adb 和 jadx 不在包里——首次运行会询问是否自动下载到 ~/.noir/tools。

方式二:从源码构建

git clone https://github.com/Verlintas/NOIR
cd NOIR
./gradlew :cli:installDist
./cli/build/install/noir/bin/noir --about

构建

git clone <repo> && cd NOIR
./gradlew :cli:installDist

产物:

cli/build/install/noir/bin/noir        # macOS / Linux
cli/build/install/noir/bin/noir.bat    # Windows

也可以直接:

./gradlew :cli:run --args="devices"

界面

NOIR dashboard on a Xiaomi 17 Pro Max

真机输出(macOS Terminal):渐变 logo、CJK 对齐的表格、按风险着色的分数、 每处理完一个 APK 就刷新一次的进度条。

noir ui 会把结果渲染成一块等宽仪表盘;颜色和边框都会按终端能力自动降级:

  • --ascii:纯 ASCII 边框,给旧版 Windows 控制台
  • --no-color / --color:关闭或强制 ANSI 颜色(默认按是否为 TTY 自动判断)
  • --quiet:只留仪表盘,不打印扫描过程
  • --no-clear:不清屏(方便把输出重定向到文件)

用法

noir --about              # 它是什么、会写什么、许可
noir --version
noir devices
noir scan                 # 全量扫描,输出 JSON / Markdown / 域名清单
noir ui                   # 扫描并渲染仪表盘
noir auto                 # 扫描 + 分类 + 生成处置计划(dry-run)
noir auto --apply         # 执行安全的 appops 动作(可回滚)
noir auto --apply --aggressive   # 额外卸载被标记的用户应用
noir deep com.example.app # 拉包 + jadx + 关键字检索
noir block com.example.app        # 打印处置命令
noir block com.example.app --apply

常用参数:

参数 说明
--serial <id> 指定设备
--out <dir> 输出目录(默认 noir-out)
--min <score> 只保留分数 ≥ N 的应用
--limit <n> 最多输出 N 条
--only <regex> 只扫描匹配的包名,例如 --only 'com\.miui\.'
--adb <path> / --jadx <path> 手动指定工具路径
--yes 自动化场景下跳过确认

DeepScan:一条命令反编译 + 处置

noir deepscan                  # 反编译风险最高的 15 个应用
noir deepscan --top 30         # 更多
noir deepscan --all            # 全部被标记应用(慢,占磁盘)
noir deepscan --jobs 4         # 并行 jadx 数量

流程是:扫描 → 按风险排序 → 逐个 adb pull + jadx → 统计代码级信号 (Runtime.exec、DexClassLoader、setApplicationEnabledSetting、 enabled_accessibility_services、MediaProjection、SystemProperties.set、 短信/设备标识、WebView 桥、无障碍/通知监听/设备管理员 …)→ 解析清单 (导出组件、明文流量、备份、debuggable)→ 识别 SDK(Umeng/Firebase/AppsFlyer…) → 输出 deepscan.md / deepscan.tsv → 询问是否清理。

noir deepscan --apply          # 分析后执行安全 appops
noir deepscan --apply --purge  # 再逐个确认卸载高风险用户应用

反编译结果按 包名 + APK 路径 缓存,第二次跑同一个应用会直接复用。

输出

noir-out/
├── report.json      # 结构化结果(应用、权限、端点、建议)
├── report.md        # 人类可读报告
├── domains.txt      # 去重后的上报域名,可直接导入 Pi-hole / AdGuard
└── rollback.txt     # 本次动作的回滚命令

隐私

  • 工具本身不联网:没有遥测、没有版本检查、没有崩溃上报
  • 扫描结果只写在你指定的 --out 目录
  • 唯一的外部请求发生在 noir setup(下载 adb/jadx)和 noir deep(jadx 是本地进程)
  • 报告里可能包含你设备上的包名和端点,分享前自行检查

缓存

扫描结果按 包名 + APK 路径 缓存在 ~/.noir/endpoints.tsv。应用更新后路径变化会自动重新分析,重复扫描通常只需几秒。


架构

adb ──▶ pm / dumpsys ──▶ Dumpsys parser ──┐
                                          ├─▶ cache (pkg + apk path)
adb ──▶ unzip/strings ──▶ endpoints ──────┘        │
                                                   ▼
                                          Classifier ─▶ score ─▶ Advisor
                                                   │              │
                                                   ▼              ▼
                                              report.json    plan.txt
                                              report.md      rollback.txt
                                                   │
                                                   ▼
                                                 Tui / CLI

deep 走另一条支线:adb pull → jadx → 源码 grep。

设计取舍

  • 端点抽取在设备端完成:adb shell 内跑 unzip | strings | grep,避免把几百个 APK 拉回电脑。设备缺少 unzip/strings 时自动退化为本地解析。
  • 共享 UID 特殊处理:Android 上 android.uid.system 下的应用(常见 50+ 个)权限记录在 dumpsys package 尾部的 Shared users 段。解析器会把这些权限合并回每个包,否则最该被看见的应用反而会消失。
  • 建议分级:只有 appops 级别的动作标记为可自动执行;卸载必须显式开启。
  • 无遥测:程序不包含任何上报逻辑。

常见问题

需要 root 吗? 不需要。全部通过 adb 的 shell 权限完成;卸载只是 --user 0 级别,APK 仍在系统分区。

会改坏手机吗? 默认完全只读。--apply 只改 app-ops,每条都能用 default 还原,rollback.txt 会写出来。 noir block 的卸载也会先打印 pm install-existing 的恢复命令。

为什么有些应用只给"人工复核"? 系统应用不能真正卸载(--user 0 卸载也会被 OTA 或恢复出厂装回来),所以只建议 app-ops 或 DNS 层拦截。

DNS 拦截为什么只导出不执行? 跨平台改 DNS 很容易把设备网断掉(我们在开发中踩过),所以只产出 domains.txt, 由你导入 Pi-hole / AdGuard / 路由器。

支持 iOS 吗? 不支持。core 是按 Kotlin Multiplatform 组织的,但审计逻辑依赖 Android 的 dumpsys 和 APK 格式。

参与

  • CONTRIBUTING.md — 构建、测试、规则放在哪
  • SECURITY.md — 漏洞报告方式(请不要开公开 issue)
  • Issues — bug 请附上 noir doctor 输出

路线图

  • 等宽仪表盘(noir ui)
  • DeepScan(批量反编译 + 代码级信号)
  • 自带运行时的双击即用包(jpackage)
  • 图形界面(Compose Multiplatform)
  • 规则文件可配置(YAML),支持社区规则包
  • 与 apktool / jadx 的批量流水线
  • 设备端 DNS/VPN 拦截方案导出
  • 扫描结果签名与版本对比

免责声明

NOIR 只应用于你自己的设备,或你获得明确授权的设备。它给出的是静态证据 ("这个应用具备这个能力,并且代码里有这个端点"),不能证明某次具体传输发生过。 请结合网络层证据自行判断。

许可

MIT © 2026 Verlintas

Apps
About Me
GitHub: Trinea
Facebook: Dev Tools
AI Daily Digest