谁在偷偷连你的手机?拆解安卓 USB 调试入侵“自动拍照取证“的完整实现

发布时间:2026/10/24 3:57:36
谁在偷偷连你的手机?拆解安卓 USB 调试入侵“自动拍照取证“的完整实现

谁在偷偷连你的手机?拆解安卓 USB 调试入侵"自动拍照取证"的完整实现

上一篇文章发了我的开源手机安全六件套(GitHub 传送门),评论区有朋友对「ADB 入侵自动拍照」最感兴趣,这篇就把它的实现掰开揉碎讲清楚。

先说风险:USB 调试就是一个常开的门

很多开发者(包括曾经的我)手机的开发者选项常年开着、USB 调试永远不关。这意味着:

任何一台装了 adb 的电脑,只要拿到数据线接触你手机的机会,就能静默安装应用、拉取文件、截取屏幕,甚至执行 shell 命令

部分恶意公共充电桩也走 USB 通道做数据嗅探

更麻烦的是:这个过程在手机上几乎无感知,被连了你都不知道

传统安全软件只做「提醒」,但提醒的问题在于——事发时你可能根本不在手机旁。所以 AdbGuard 的思路是:不仅告警,还要取证。谁连的、什么时候连的、人在哪,全部留下证据。

整体设计:双通道监听 + 防抖

要抓住"USB 设备接入"这个瞬间,安卓给了两条路:

Manifest 静态注册 UsbDeviceAttachedReceiver:App 被杀也能收到系统广播,做兜底

前台服务内运行时注册:服务存活时实时响应,做主通道

同一个 U 盘插入,两个通道可能同时收到广播。所以需要一个统一的处理入口 + 防抖去重:

kotlin

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

// UsbAlertHelper.kt(节选)

object UsbAlertHelper {

private var lastAction = ""

private var lastEventAt = 0L

fun handleUsbEvent(context: Context, action: String?, deviceName: String): Boolean {

if (action != UsbManager.ACTION_USB_DEVICE_ATTACHED &&

action != UsbManager.ACTION_USB_DEVICE_DETACHED

) return false

// 防抖:双通道可能同时收到同一广播

val now = System.currentTimeMillis()

if (action == lastAction && now - lastEventAt < 2000) return false

lastAction = action

lastEventAt = now

// ... 告警 + 取证

}

}

静态注册的接收器只负责转发:

kotlin

1

2

3

4

5

6

7

8

9

10

11

12

13

// UsbPlugReceiver.kt —— Manifest 静态注册兜底通道

class UsbPlugReceiver : BroadcastReceiver() {

override fun onReceive(context: Context, intent: Intent) {

try {

UsbAlertHelper.handleUsbEvent(

context.applicationContext,

intent.action,

UsbAlertHelper.deviceNameOf(intent)

)

} catch (_: Exception) { }

}

}

一个容易被忽略的细节:ACTION_USB_DEVICE_ATTACHED 是系统受保护广播,只有系统才能发送,所以 exported=true 也不用担心伪造——第三方 App 伪造不了这个 action。

设备名提取:一个小坑

告警里要告诉用户"什么东西插进来了",从 Intent 里取 UsbDevice。安卓 13 前后取法不同,要兼容:

kotlin

1

2

3

4

5

6

7

8

9

10

11

@Suppress("DEPRECATION")

fun deviceNameOf(intent: Intent): String {

val device: UsbDevice? = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {

intent.getParcelableExtra(UsbManager.EXTRA_DEVICE, UsbDevice::class.java)

} else {

@Suppress("DEPRECATION")

intent.getParcelableExtra(UsbManager.EXTRA_DEVICE)

}

return device?.productName?.takeIf { it.isNotBlank() } ?: "未知设备"

}

这个函数能直接把"某人的电脑""充电宝""钓鱼线"区分出来——至少在告警里你能看到设备的 productName。

关键一步:拉起前置拍照取证

确认是"接入"事件后,通过自定义 action 拉起前台服务执行拍照:

kotlin

1

2

3

4

5

6

7

8

9

10

const val ACTION_USB_PHOTO = "com.phonGuard.adbguard.action.USB_PHOTO"

// 事件处理中:立即告警(不等轮询)+ 写时间线 + 触发取证

if (config.usbPlugAlertEnabled) {

NotificationHelper.sendAlertNotification(

context, AdbGuardApp.CHANNEL_ADB_GUARD,

title, message, USB_ALERT_ID

)

}

拍照环节有几个实打实的坑,都踩过:

后台启动相机受限:Android 9+ 对后台进程打开相机有严格限制,必须走前台服务(startForegroundService)并在通知栏可见,这正是把监听逻辑放进常驻服务的原因

相机权限与隐私声明:取证拍照必须在隐私政策里明确声明"安全取证用途",上架应用商店时这是审核点

连拍而非单拍:单张照片可能拍糊/闭眼,连拍 3-5 张间隔抓拍,取证成功率直线上升

写入要快:拍照后立即落盘到应用私有目录(MODE_PRIVATE),再更新 UI 列表——取证数据第一时间固化,防止进程被杀丢证据

再补两道保险:TTS 语音 + 事件时间线

TTS 语音播报:连接事件确认后,直接语音喊出来——"检测到 USB 设备接入"。人在旁边时这是最即时的威慑;不在旁边,语音也会吓退操作者

事件时间线:每次事件(接入/断开/拍照)写入 SecurityLogger,带时间戳和严重等级,事后在 App 里可以完整回溯"那天下午到底发生了什么"

kotlin

1

2

3

4

5

6

7

8

9

10

// 事件入时间线(节选)

app.securityLogger.logEvent(

SecurityLogger.SecurityEvent(

type = "usb",

subType = if (attached) "attached" else "detached",

message = message,

severity = if (attached) 2 else 0

)

)

小结

整套方案的核心思路一句话:感知(双通道监听)→ 确认(防抖去重)→ 告警(通知+TTS)→ 取证(连拍落盘)→ 留痕(事件时间线) 。

安全工具最难的不是某个 API 怎么调,而是把"事发瞬间"的完整链条都接住——任何一个环节断了,证据就没了。

开源仓库(觉得思路有用就点个 Star ⭐):

https://github.com/hnpdsyn/PhoneGuard1

Releases 里可以直接下载 6 个 APK,AdbGuard 对应 adbguard-release.apk。

下篇预告:应用锁「假崩溃 + 图标伪装」是怎么骗过窥屏者的——假弹窗的绘制时机、伪装图标的 activity-alias 配置,都会拆开讲。关注不迷路。