免费获取学习方案
ARTICLE DETAIL

资讯详情

深耕编程基础知识与建站技术分享的一线实战洞察。

【无小偷偷了手机第一件事是拔卡:用 SIM 广播做“换卡哨兵“,附 Android 10 的 ICCID 大坑标题】

【无小偷偷了手机第一件事是拔卡:用 SIM 广播做“换卡哨兵“,附 Android 10 的 ICCID 大坑标题】 小偷偷了手机第一件事是拔卡用 SIM 广播做换卡哨兵附 Android 10 的 ICCID 大坑PhoneGuard 开源系列第五篇仓库github.com/hnpdsyn/PhoneGuard1。上一篇讲了锁屏密码的指数退避这篇盯住另一个关键瞬间手机易主时的换卡动作。为什么盯着 SIM 卡设想手机被盗小偷第一反应是什么拔卡。原因很朴素——SIM 卡在网意味着手机号可以被拨打响铃暴露位置运营商侧有基站定位锚点短信验证码还能到达账号找回的命脉拔卡或换上一张自己的卡手机立刻变成离线孤岛机主的远程锁定、定位、响铃全部失效。所以「SIM 卡被换」这个事件是设备易主的最强信号——SimGuard 就是为这个瞬间设计的哨兵。第一层监听 SIM 状态广播安卓在 SIM 状态变化时会发系统广播 SIM_STATE_CHANGED这是个隐式广播注册方式有讲究kotlin[data-radix-scroll-area-viewport]{scrollbar-width:none;-ms-overflow-style:none;-webkit-overflow-scrolling:touch;}[data-radix-scroll-area-viewport]::-webkit-scrollbar{display:none}991234567891011121314// SimChangeReceiver.kt节选class SimChangeReceiver : BroadcastReceiver() { override fun onReceive(context: Context, intent: Intent) { if (intent.action android.intent.action.SIM_STATE_CHANGED) { val state intent.getStringExtra(ss) if (state ABSENT || state LOADED) { // ABSENT 卡被拔出LOADED 新卡加载完成 // ... 记录事件 拉起检查服务 } } }}几个要点SIM_STATE_CHANGED 的状态藏在 extra ss 里字符串枚举其中对我们有意义的是 ABSENT卡不在位和 LOADED卡加载完成也就是换上了新卡这个广播状态变化频繁飞行模式、重启、信号波动都可能触发中间态所以只过滤这两个关键值别每次广播都当真收到 LOADED 立即 startForegroundService 拉起检查服务——广播接收器的生命周期只有 10 秒左右重活必须交给服务第二层ICCID 绑定比对——这里有 Android 10 的大坑换卡哨兵的核心逻辑很直白第一次运行时记住原卡的标识之后每次 LOADED 都比对新卡是不是原卡。问题来了用什么标识卡教科书答案是 ICCID集成电路卡识别码TelephonyManager.getSimSerialNumber() 一行搞定。但 Android 10 起这条路基本断了——权限收紧后普通应用即便持有 READ_PHONE_STATE这个方法也返回 null 或抛异常需要 READ_PRIVILEGED_PHONE_STATE那是系统应用专属。实际工程里的降级方案kotlin[data-radix-scroll-area-viewport]{scrollbar-width:none;-ms-overflow-style:none;-webkit-overflow-scrolling:touch;}[data-radix-scroll-area-viewport]::-webkit-scrollbar{display:none}991234567891011121314151617// SimGuardService.kt节选private fun getCurrentSimIdentifier(): String { return try { val tm getSystemService(TELEPHONY_SERVICE) as TelephonyManager if (Build.VERSION.SDK_INT Build.VERSION_CODES.Q) { tm.simCarrierId.toString() // Android 10用 carrierId 替代 } else { Suppress(DEPRECATION) tm.simSerialNumber ?: // 旧版本仍可用 ICCID } } catch (e: SecurityException) { // 权限缺失时静默降级不能让防护本身崩溃 } catch (e: Exception) { }}这里有个工程决策要坦白carrierId运营商 ID的区分度远不如 ICCID——它只能识别换成了不同运营商的卡同运营商换卡检测不到。这是 Android 10 权限模型下普通应用的天花板imperfect 但必须接受。把局限写清楚好过假装它不存在。第三层确认换卡后的处置链比对不一致判定为疑似换卡handleSimSwapDetected依次执行高优先级通知换卡事件顶到通知栏最高通道用户在别的设备上看到绑定的手表/平板通知也能感知事件时间线换卡时间、新旧卡标识、判定结果全部落盘事后可回溯TTS 语音告警可选开启手机还在手边的话直接喊出来配合 BootReceiver 开机自启 前台服务常驻保证重启后哨兵仍然在岗。诚实地说纯软件方案的边界必须承认局限如果小偷关机 拔卡 不联网任何本地软件都无解——这是平台能力边界不是代码能突破的。所以 SimGuard 在六件套里的定位是概率性防线覆盖没关机就换卡双卡用户副卡拔换临时起意的顺手牵羊这些高频场景。真正的安全是分层的——锁屏密码LoginGuard、换卡哨兵SimGuard、事件日志SecurityLog各守一层谁都不承诺 100%但叠加起来攻破成本陡增。开源仓库觉得思路有用点个 Star ⭐https://github.com/hnpdsyn/PhoneGuard1Releases 可直接下载 APKSimGuard 对应 simguard-release.apk需授予电话权限。下篇预告系列收官 模块化架构复盘——为什么我把一个手机安全大师拆成 6 个互不相干的小 App以及这个决定在开发、上架、用户信任三个维度各自换来了什么。
返回列表