当前位置: 首页 > news >正文

Android NFC 标签读写Demo与历史漏洞概述

文章目录

  • 前言
  • NFC基础
    • 1.1 RFID区别
    • 1.2 工作模式
    • 1.3 日常应用
  • NFC标签
    • 2.1 标签应用
    • 2.2 应用实践
    • 2.3 标签预览
    • 2.4 前台调度
  • NFC开发
    • 3.1 NDEF数据
    • 3.2 标签的调度
    • 3.3 读写Demo
    • 3.4 Demo演示
  • 历史漏洞
    • 4.1 中继攻击
    • 4.2 预览伪造
    • 4.3 篡改卡片
    • 4.4 其它漏洞
  • 总结

前言

NFC 作为 Android 手机一个重要的短距特性,基本在所有 Android 中高端机型上均有支持,但说实话本人原先却很少了解和使用……为了深入探索 NFC 的安全风险,本文先来记录学习下 Android NFC 的一些基础知识、玩法、标签读写开发,并简述下业界对 NFC 特性发现的相关历史漏洞情况。

NFC基础

近场通信(Near Field Communication,简称 NFC),是一套能让两个电子设备(其中一个通常是移动设备,例如智能手机)在相距几厘米之内进行通信的通信协议。NFC 一般需要 4cm 或者更短的距离来建立初始化连接,其工作频率为 13.65 兆赫兹,通信速率为 106-848 kbit/秒。

NFC 近场通信技术由非接触式射频识别(RFID)演变而来,由飞利浦半导体(现恩智浦半导体,缩写 NXP)、诺基亚和索尼共同于2004年研制开发,属于一种非接触式识别和互联技术。它通过在单一芯片上集成感应式读卡器、感应式卡片和点对点通信的功能,利用移动终端实现移动支付、电子票务、门禁、移动身份识别、防伪等应用。

1.1 RFID区别

射频识别(RFID)是 Radio Frequency Identification 的缩写,它把数字信息编码到 RFID 标签中,阅读器通过无线电波读取到这些信息。其原理为阅读器与标签之间进行非接触式的数据通信,达到识别目标的目的。

在这里我给大家举几个例子:

  • 第一个是大商场里优衣库、以纯等服装门店,店员会在衣服上会使用 RFID 标签进行防盗,当门禁读取到没有付款的产品的 RFID 信息,就会报警;
  • 第二个是汽车的无钥匙进入系统,当驾驶员身装钥匙进入到汽车的识别范围之内,汽车门把手一拉就打开了,当然,有的汽车为了更加安全,还增加了指纹识别。两个同时具备时,车门才会被打开。这个应用的要点是要有车钥匙和读取车钥匙信息的系统。
  • 第三个是智能仓储系统,现在很多企业都开始了智能仓储的建设。他智能在储存在仓库的每个货物上面都有一个 RFID 标签,仓库内有天线。仓库管理者能清楚的知道每种货物存放的位置及数量。这样就可以实现很多功能了,比如设定货物的最小储存值,当货物低于储存值,系统会自动下单采购,同时还可以节省掉年终盘点库存的工作。这个应用的要点是要有带有 RFID 的标签和读取设备。

以上只说了3种应用场景,如果您想是上网查查,还能查出至少十几种应用场景。归根结底其实射频识别的两大应用场景就是:身份识别、物品供应链运输链管理(严格的说物品供应链管理也属于身份识别)。

【区别】与 RFID 技术一样,NFC 技术的信息也是通过频谱中无线频率部分的电磁感应耦合方式传递,但两者之间还是存在很大的区别。NFC 是一种提供轻松、安全、迅速的通信的无线连接技术,其传输范围比 RFID 小,RFID 的传输范围可以达到几米、甚至几十米,但由于 NFC 采取了独特的信号衰减技术,相对于 RFID 来说 NFC 具有距离近、带宽高、能耗低等特点。此外,NFC 具有与现有非接触智能卡技术兼容的特点,从应用前景看 NFC 目前来看更多的是针对于消费类电子设备相互通讯,有源 RFID 则更擅长在长距离识别。

而 NFC、蓝牙和红外技术之间的差异则可概括如下图:
在这里插入图片描述
简而言之,NFC 功能具有直观性强、操作简单、成本低、建立连接速度更快等优点,同时也存在与蓝牙等常用通信工具相比使用距离短与传输速率低等缺点。目前 NFC 已应用在许多领域之上,如:公交出行、门禁系统、移动支付、设备连接等方面,所以研究 NFC 技术具有一定的实用意义与重要性。

1.2 工作模式

由于 NFC 通信总是由一个发起者和一个接收者组成,发起者会主动发送电磁场,可以为被动接收者提供电源,其基本原理和收音机类似。正是由于被动式接收者可以通过发起者提供电源,因此接收者可以有非常简单的形式。

NFC 技术主要有三种工作模式,分别是:

  1. 仿真卡模式(Card Emulation Mode): 此模式的数据在支持NFC的手机或其它电子设备中,可以简单理解成“刷手机”。其本质为将支持 NFC 功能的电子设备(如NFC 手机),当作借书卡、公交卡、银行卡、门禁卡等 IC 卡使用。其实现的基本原理是将 IC 卡中信息凭证封装成数据包并存储在支持 NFC 的设备中。此外除了支持 NFC 的设备,刷电子设备处还需要 NFC 射频器,通过接收 NFC 射频器发送的信号与一系列验证之后,电子设备将 IC 卡中相应信息传入射频器中,最终从射频器传输到射频器所连接的电脑终端,完成相应处理操作。
  2. 读卡器模式(Reader/writer mode): 数据在 NFC 芯片中,可以简单理解成“刷标签”。该模式的本质为通过支持 NFC 的电子设备从带有 NFC 芯片的标签、物品等媒介中读写信息。NFC 标签工作原理为:其不需要外部供电,当支持 NFC 的设备向标签读写数据时,支持 NFC 的设备会发送某种磁场,该磁场会主动向 NFC 标签供电,从而顺利完成读写工作。
  3. 点对点模式(P2P mode): 该模式与蓝牙、红外差不多,用于不同 NFC 设备之间进行数据交换,不过这个模式已经没有有“刷”的感觉了。该模式用于不同 NFC 设备之间进行数据交换,其有效距离一般不能超过 4 厘米,传输建立速度与传输速率比红外和蓝牙技术快很多。该模式下 NFC 进行传输时,通常还会使用到 Android Beam 技术进行传输数据,当使用 Android Beam 传输数据的两部设备不再限于 4 厘米之内。实际应用:华为手机的 NFC 文件互传功能。

1.3 日常应用

说太多概念性的东西容易让人云里雾里,不如先来介绍下 NFC 在日常生活中的简单应用场景。

华为钱包 APP 以及 小米钱包 APP 均将日常生活中我们常用的 NFC 功能集成到自家 APP 当中,用户使用起来相当方便。比如华为钱包 APP 的 “卡包” 允许添加 门禁卡、银行卡、交通卡、会员卡 等电子卡信息:
在这里插入图片描述
具体教程可参见华为消费者官网指导文档:如何将实体门禁卡添加到华为钱包、使用华为钱包交通卡刷卡的相关说明 等。

通过以上钱包 APP 我们可以很方便地在手机上使用 NFC 的仿真卡模式(Card Emulation Mode) 来完成刷门禁、公交支付、银行卡支付等操作,而不用随时携带钥匙、公交卡、银行卡等实体物件。

NFC标签

上面简单介绍了 NFC 仿真卡模式的应用,下文将对 NFC 的读卡器模式进行重点介绍,在读卡器模式下,NFC 手机可以通过触碰 NFC 标签(Tag),从中读取非接触标签中的内容,采集数据并发送到对应的应用进行处理。

2.1 标签应用

NFC 标签的应用就是把一些信息写入一个 NFC 标签内,用户只需用 NFC 手机在 NFC 标签上挥一挥就可以立即获得相关的信息。读者可在某宝上搜索 “NFC 标签” 或 “NFC 贴纸” 即可很轻松买到。
在这里插入图片描述
知乎上关于 NFC 标签有很多趣味玩法与教程:

链接内容概述
用手机制作NFC标签实现一碰连网让客人通过 NFC 标签实现一碰连接家中网络
通过粘贴NFC标签,让墙壁“智能化”借助 NFC 标签记录墙壁内水管位置、防止装修误伤
华为一碰传 NFC 标签制作使用 NFC 标签让非华为手机与华为的 PC 能碰一碰传文件、多屏协同
【黑科技】NFC模拟门卡门禁借助 NFC 标签复制小区门禁卡(有局限性)
用NFC贴纸实现安卓aTimeLogger自动记录借助 NFC 标签生成一个时间记录器

以上教程中用到的 NFC 标签一键读写的 APP 工具:

  1. NFC 标签读写工具:NFC Tools Pro 下载地址;
  2. NFC Tools的任务辅助工具:NFC Task下载地址(写入 NFC 标签的任务在被读取并执行时部分需要该工具辅助完成);

注意:海外网络在 Google Play 可下载,国内可用以上提供的吾爱网站的链接。

2.2 应用实践

下面来看看如何具体使用 NFC Tools Pro 自动化读写 NFC 标签,我将演示的是创建拨打电话的 NFC 标签。

1、安装并打开 NFC Tools,靠近购买到手的 NFC 标签贴纸,可读取到标签信息(标签内容暂时为空):
在这里插入图片描述
2、切换到 NFC Tools 的 “写” 功能页,选择 “添加记录”:
在这里插入图片描述
此时程序会提供多种 NFC 标签内容供用户选择,比如写入文本(一些重要提示比如上文提到的墙内水管坐标)、写入URL(让他人碰一碰即可访问你的个人网站)、碰一碰自动创建联系人、碰一碰拨打电话、碰一碰启动指定应用、碰一碰连接指定 WIFI 等:
在这里插入图片描述
3、此处先演示写入“碰一碰拨打电话”的标签数据:
在这里插入图片描述
4、填写指定电话号码后返回上层页面,点击“写/xx字节”:
在这里插入图片描述
靠近待写入数据的 NFC 标签贴纸,即可写入:
在这里插入图片描述
在这里插入图片描述
5、然后手机在打开 NFC 功能的情况下再次碰一碰 NFC 标签,手机将自动跳转到拨号界面:
在这里插入图片描述
注意如果是谷歌的手机的话,则是跳转到 NFC 标签预览页面,用户点击拨号预览的标签后会直接拨打电话:
在这里插入图片描述
同理可借助 NFC Tools 很方便地向 NFC 标签写入碰一碰即可创建联系人、连接WIFI等操作的内容,此处不再演示。

2.3 标签预览

但想补充一个能触发 NFC 标签预览的操作:写入地理坐标。
在这里插入图片描述
写入成功后扫描标签并不会自动跳转到地图软件,而是由系统的 NFC 服务捕获到 NFC 标签信息并弹出提示:
在这里插入图片描述
点击后会弹出手机上可显示、响应标签信息的应用列表,此处选择 Android 原生的 com.android.apps.tag 标签预览应用:
在这里插入图片描述
点击后即可看到 NFC 标签的内容:
在这里插入图片描述
此处点击地图标签的话会跳转到高德地图并定位到具体的坐标位置(可用于标识某个位置,比如国外有大学生借助该标签功能定位他们的自行车:NFC贴纸记录GPS地址,帮助车主找自行车);如果点击拨号标签的话则会自动打出电话(注意不再只是跳转到拨号键盘了)。

【对比】国产手机(比如某为)与谷歌原生 Pixel 手机在识别 NFC 标签的时候有以下区别:

  1. 能触发标签预览的标签:对于扫描到的地理位置标签、发送短信标签、拨打电话标签(不完全统计是这3个),谷歌手机都会默认跳转到标签预览页面供用户进一步确认,而国产手机只针对地理位置标签会跳转到预览页面,其它两个标签则是直接跳转到短信发送、拨号页面;
  2. 其它默认不预览的标签:无论谷歌手机还是国产手机,对于打开 URL、启动应用的标签,都是直接响应,而不是跳转到标签预览页面等待用户进一步确认;
  3. 有个很神奇的地方是:对于创建联系人的标签,谷歌手机和国产手机均会在扫描到标签后自动创建并保存联系人,无需用户进一步确认(那么有可能你的手机碰一碰别人的 NFC 标签,发现通讯录多了一堆乱七八糟的联系人……)。

此处特意补充 NFC 的标签预览功能是因为曾经发生过一个 NFC 标签预览导致的漏洞,具体参见看雪译文:NFC 虚假标签 (False Tag) 漏洞 - CVE-2019-9295。

2.4 前台调度

NFC 前台调度机制主要是一种用于在手机当前前台运行的程序中,处理识别到的 NFC tag 的技术,使得当前 Activity 能直接响应 NFC 标签,而不需要用户再手动选择所有能处理该标签数据的 Activity。

在实现当前 Activity 能直接响应 NFC 标签这个功能中使用到了 TAG 分发系统,而 TAG 分发系统又包括了 Intent 发布系统与前台 Activity 发布系统,它们协调帮助正确地识别一个 NFC tag 是否是你的 Activity 想要处理的。具体流程为:

  1. 前台发布系统:允许一个 Activity 覆盖掉 Intent 发布系统而首先处理此 tag Intent,这要求你将要处理 Tag Intent 的 Activity 运行在前台,这样当一个 NFC tag 被扫描到,系统先检测前台的 Activity 是否支持处理此 Intent,如果支持,即将此 Intent 传给此Activity,如果不支持则转到 Intent 发布系统;
  2. Intent 发布系统:检查所有 Activities 的 intent filters,找出那些定义了可以处理此 tag 的 Activity,如果有多个 Activity 都配置了处理同一个 tag Intent,那么将使用 Activity 选择器来让用户选择使用哪个 Activity。用户选择之后,将使用选择的 Activity 来处理此 Intent。

以上关于 NFC 前台调度机制和 Intent 分发流程的理解很简单,比如当你的 NFC Tools 应用置于前台去扫描 “发送短信” 的 NFC 标签时,手机并不会跳转到短信应用,而是由 NFC Tools 自动响应标签数据,即读取内容并显示:
在这里插入图片描述
再比如,如果是华为钱包 APP 目前处于前台的话,扫描到的 NFC 标签也将优先交由华为钱包响应和处理,可快速识别并添加公交卡、银行卡、门禁卡等。

NFC开发

本章节将讲述如何通过代码实现 NFC 标签的读写,掌握简单的 NFC 开发知识。而在描述 NFC 功能的实现时,需要掌了解一些基础的知识点,例如: NDEF 数据格式、NFC 标签调度系统(上文已介绍)、NFC 标签映射的实现、Android Beam 等。

3.1 NDEF数据

NEDF 全称为 NFC Data Exchange Format,它是 NFC 组织约定的 NFC tag 中的数据交换格式,可带有 URL、vCard 和 NFC 定义的各种数据类型。

不同的 NFC 标签之间差异很大:

  1. 有的只支持简单的读写操作;
  2. 而有的却会采用支持一次性写入的芯片,将 NFC 标签设计成只读的;
  3. 当然也存在一些复杂的 NFC 标签,例如有些 NFC 标签可以通过硬件加密的方式限制对某一区域的访问;
  4. 还有一些标签自带操作环境,允许 NFC 设备与这些标签进行更复杂的交互。

这些标签中的数据也会采用不同的格式,但 Android SDK API 主要遵循 NFC论坛标准(Forum Standard),进行 NFC 操作时用的是 NDEF 消息格式来发送和接收 NFC 数据。

NDEF 格式其实就类似于硬盘的 NTFS,下面我们看一下 NDEF 数据:

1、NDEF 数据的操作

Android SDK API 支持如下 3 种 NDEF 数据的操作:

  • 从 NFC 标签读取 NDEF 格式的数据;
  • 向 NFC 标签写入 NDEF 格式的数据;
  • 通过 Android Beam 技术将 NDEF 数据发送到另一部 NFC 设备。

用于描述 NDEF 格式数据的两个类:

类名描述
NdefMessage描述 NDEF 格式的信息,实际上我们写入 NFC 标签的就是 NdefMessage 对象
NdefRecord描述 NDEF 信息的一个信息段,一个 NdefMessage 可能包含一个或者多个 NdefRecord

NdefMessage 和 NdefRecord 是 Android NFC 技术的核心类,无论读写 NDEF 格式的 NFC 标签,还是通过 Android Beam 技术传递 Ndef 格式的数据,都需要这两个类。

Android 系统对 NFC 的支持主要在 android.nfc 和 android.nfc.tech 两个包中。android.nfc 包中主要类如下:

类名描述
NfcManager用于管理 Android 设备中指出的所有 NFC Adapter,因为大多安卓设备只支持一个 NFC Adapter 的原因,所有在这种情况下可以直接使用 getDefaultAapater 获取系统支持的 Adapter
NfcAdapterNFCAdapter 对象用于定义一个 Intent,使系统在检测到 NFC Tag 时通知你定义的 Activity,并提供用来注册 forground tag 消息发送的方法等

而 android.nfc.tech 包中则定义了可以对 Tag 进行的读写操作的类,这些类按照其使用的技术类型可以分成不同的类如:NfcA,NfcB,NfcF 以及MifareClassic 等。Tag 代表一个被动式 Tag 对象,可以代表一个标签,卡片,钥匙扣等。当 Android 设备检测到一个 Tag 时,会创建一个 Tag 对象,将其放在 Intent 对象,然后发送到相应的 Activity。

Android Beam

在 Android 中将 NDEF 数据从一台设备发送到另一部 NFC 设备的功能的实现是通过 Android Beam 的技术来实现的。Android Beam 是一个基于近场通信所做的一款应用程序,可让用户对几乎任何东西进行分享,无论是联系人、图片、网页链接还是 YouTube 链接。

Android Beam 功能允许设备把一个 NDEF 消息推送到物理/硬件上相互监听的另一个设备上。因为这种交互提供了比其他无线技术更容易的发送数据的方法,所以 NFC 不需要手动的设备发现或配对要求,两个设备在接近到一定范围时会自动的连接。 Android Beam 通过一组 NFC API 来使用,以便应用程序能够在设备之间来传输信息。

2、非NDEF数据的操作

对于某些特殊需求,可能要存任意的数据,对于这些数据,我们就需要自定义格式。这些数据格式实际上就是普通的字节流,至于字节流中的数据代表什么,就由开发人员自己定义了。

更多 NFC 数据格式的解析,可查阅:Android NFC 学习笔记。

3.2 标签的调度

NFC 标签调度系统用于分析扫描到的 NFC 标签,通过解析数据,在被扫描到的数据中尝试找到最适合的应用程序。

当系统检测到一个 NFC 标签的时候,会自动去寻找最合适的 activity 去处理这个 intent,而此 Intent 定义了三种 Action:

Action解释
ACTION_NDEF_DISCOVERED如果扫描到包含 NDEF 负载的标签,并且可识别其类型,则使用此 Intent 启动 Activity。这是优先级最高的 Intent,标签调度系统会尽可能尝试使用此 Intent 启动 Activity,在行不通时才会尝试使用其他 Intent。 适用于上文描述的使用标准NDEF格式的情况。
ACTION_TECH_DISCOVERED如果没有登记要处理 NDEF_DISCOVERED Intent 的 Activity,则标签调度系统会尝试使用此 Intent 来启动应用。此外,如果扫描到的标签包含无法映射到 MIME 类型或 URI 的 NDEF 数据,或者该标签不包含 NDEF 数据,但它使用了已知的标签技术,那么也会直接启动此 Intent。 适用于使用自定义 非标准NDEF数据格式的情况。
ACTION_TAG_DISCOVERED如果没有处理 ACTION_NDEF_DISCOVERED 或者 ACTION_TECH_DISCOVERED Intent 的 Activity,则使用此 Intent 启动 Activity

NFC 标签调度系统对 NFC 标签进行解析的过程为:

  1. 对接收到的 NFC 标签数据进行解析,搞清楚标签中标识数据负载的 MIME 类型或 URI 等信息。
  2. 将 MIME 类型或 URI 等数据以及数据负载封装到一个 Intent 中。
  3. 基于 Intent 来启动 Activity,将 Intent 对象发送给感兴趣的应用程序(以下几点为对第三点完成过程的具体阐述)。
  4. 用解析 NFC 标签时由标签调度系统创建的 Intent 对象(ACTION_NDEF_DISCOVERED 或 ACTION_TECH_DISCOVERED)来尝试启动 Activity;
  5. 如果没有对应的处理 Intent 的 Activity,那么就会尝试使用下一个优先级的 Intent(ACTION_TECH_DISCOVERED 或ACTION_TAG_DISCOVERED)来启动 Activity,直到有对应的应用程序来处理这个 Intent,或者是直到标签调度系统尝试了所有可能的 Intent。
  6. 如果没有应用程序来处理任何类型的 Intent,那么就不做任何事情。

在这里插入图片描述

3.3 读写Demo

下面来完整编写一个 NFC 标签读写的 Demo 程序,参考: Android Nfc技术解析、使用。

我下面主要实现以下 3 个功能:
在这里插入图片描述
完整代码结构如下:
在这里插入图片描述

1、在 AndroidMainfest.xml 中申请 NFC 权限,定义几个需要用到的 Activity 组件:

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"package="com.tr0e.nfctag"><uses-permission android:name="android.permission.NFC" /><!-- 要求当前设备必须要有NFC芯片 --><uses-feature android:name="android.hardware.nfc" android:required="true" /><applicationandroid:allowBackup="true"android:icon="@mipmap/ic_launcher"android:label="@string/app_name"android:roundIcon="@mipmap/ic_launcher_round"android:supportsRtl="true"android:theme="@style/AppTheme"><activity android:name=".Activity.readTagInfoActivity" android:launchMode="singleTop" android:exported="true"/><activity android:name=".Activity.writeRunAppTagActivity" android:launchMode="singleTop" android:exported="true"/><activity android:name=".Activity.writeUrlTagActivity"android:launchMode="singleTop"android:exported="true"><intent-filter><action android:name="android.nfc.action.NDEF_DISCOVERED" /><category android:name="android.intent.category.DEFAULT" /></intent-filter></activity><activity android:name=".MainActivity" android:exported="true"><intent-filter><action android:name="android.intent.action.MAIN" /><category android:name="android.intent.category.LAUNCHER" /></intent-filter></activity></application>
</manifest>

2、 MainActivity 类:通过自定义数组与数组适配器在 activity_main.xml 布局文件中实现了列表视图,通过 switch 语句使得在主界面可以实现自定义数组中所具有的操作,并且在该类的 Oncreate 方法中进行了 NFCAdapter 的获取与判断设备是否支持 NFC 功能的实现。具体代码为:

package com.tr0e.nfctag;import android.content.Intent;
import android.nfc.NfcAdapter;
import android.os.Bundle;
import android.view.View;
import android.widget.AdapterView;
import android.widget.ArrayAdapter;
import android.widget.ListView;
import android.widget.TextView;import com.tr0e.nfctag.Activity.readTagInfoActivity;
import com.tr0e.nfctag.Activity.writeRunAppTagActivity;
import com.tr0e.nfctag.Activity.writeUrlTagActivity;public class MainActivity extends BaseNfcActivity {private TextView ifo_NFC;private static final String[] strs = new String[]{"写入跳转到URL个人网站的NFC标签","写入自动打开短信APP应用的NFC标签","读取NFC标签中的Uri数据和基本信息",};@Overridepublic void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);ifo_NFC = findViewById(R.id.ifo_NFC);// NFC适配器,所有的关于NFC的操作从该适配器进行mNfcAdapter = NfcAdapter.getDefaultAdapter(this);if (!ifNFCUse()) {return;}ListView listView = findViewById(R.id.listview);listView.setAdapter(new ArrayAdapter<>(this, android.R.layout.simple_list_item_1, strs));listView.setOnItemClickListener(new AdapterView.OnItemClickListener() {@Overridepublic void onItemClick(AdapterView<?> parent, View view, int position, long id) {switchActivity(position);}});}private void switchActivity(int position) {switch (position) {case 0: //写入跳转到URL个人网站的NFC标签startActivity(new Intent(this, writeUrlTagActivity.class));break;case 1: //写入自动打开指定APP应用的NFC标签startActivity(new Intent(this, writeRunAppTagActivity.class));break;case 2: //读取NFC标签中的Uri数据和基本信息startActivity(new Intent(this, readTagInfoActivity.class));break;default:break;}}/*** 检测工作,判断设备的NFC支持情况** @return*/protected Boolean ifNFCUse() {if (mNfcAdapter == null) {ifo_NFC.setText("设备不支持NFC!");return false;}if (mNfcAdapter != null && !mNfcAdapter.isEnabled()) {ifo_NFC.setText("请在系统设置中先启用NFC功能!");return false;}return true;}
}

视图文件如下:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"android:layout_width="match_parent"android:layout_height="match_parent"android:orientation="vertical"><TextViewandroid:id="@+id/ifo_NFC"android:layout_width="match_parent"android:layout_height="wrap_content"android:text="该设备支持NFC,请写入数据!" /><ListViewandroid:id="@+id/listview"android:layout_width="match_parent"android:layout_height="match_parent" />
</LinearLayout>

2、BaseNfcActivity 类 :进行 NFC 相关操作的基础类,为其它进行 NFC 操作的类服务,其它类需继承它。具体代码为:

package com.tr0e.nfctag;import android.app.PendingIntent;
import android.content.Intent;
import android.nfc.NfcAdapter;import androidx.appcompat.app.AppCompatActivity;/*** Description:* 子类在onNewIntent方法中进行NFC标签相关操作。* launchMode设置为singleTop或singelTask,保证Activity的重用唯一* 在onNewIntent方法中执行intent传递过来的Tag数据* 将NFC标签卡靠近手机后部(NFC标签卡可网上自行购买)*/
public class BaseNfcActivity extends AppCompatActivity {protected NfcAdapter mNfcAdapter;private PendingIntent mPendingIntent;/*** onCreat->onStart->onResume->onPause->onStop->onDestroy* 启动Activity,界面可见时.*/@Overrideprotected void onStart() {super.onStart();//此处adapter需要重新获取,否则无法获取messagemNfcAdapter = NfcAdapter.getDefaultAdapter(this);//一旦截获NFC消息,就会通过PendingIntent调用窗口mPendingIntent = PendingIntent.getActivity(this, 0, new Intent(this, getClass()), PendingIntent.FLAG_MUTABLE);}/*** 获得焦点,按钮可以点击*/@Overridepublic void onResume() {super.onResume();//设置处理优于所有其他NFC的处理if (mNfcAdapter != null)mNfcAdapter.enableForegroundDispatch(this, mPendingIntent, null, null);}/*** 暂停Activity,界面获取焦点,按钮可以点击*/@Overridepublic void onPause() {super.onPause();//恢复默认状态if (mNfcAdapter != null)mNfcAdapter.disableForegroundDispatch(this);}
}

3、writeUrlTagActivity 类:具体实现的功能为解析 nfc 数据 tag,往 nfc 标签中写入数据(即我的个人博客的链接)并自动访问该网站,具体实现为:

package com.tr0e.nfctag.Activity;import android.content.Intent;
import android.net.Uri;
import android.nfc.NdefMessage;
import android.nfc.NdefRecord;
import android.nfc.NfcAdapter;
import android.nfc.Tag;
import android.nfc.tech.Ndef;
import android.nfc.tech.NdefFormatable;
import android.os.Bundle;
import android.widget.Toast;import com.tr0e.nfctag.BaseNfcActivity;
import com.tr0e.nfctag.R;public class writeUrlTagActivity extends BaseNfcActivity {@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_write_url_tag);}@Overridepublic void onNewIntent(Intent intent) {//1.获取Tag对象super.onNewIntent(intent);Tag detectedTag = intent.getParcelableExtra(NfcAdapter.EXTRA_TAG);writeNFCTag(detectedTag);}/*** 往标签写数据的方法** @param tag*/public void writeNFCTag(Tag tag) {if (tag == null) {return;}NdefMessage ndefMessage = new NdefMessage(new NdefRecord[]{NdefRecord.createUri(Uri.parse("https://tr0e.github.io"))});//转换成字节获得大小int size = ndefMessage.toByteArray().length;try {//2.判断NFC标签的数据类型(通过Ndef.get方法)Ndef ndef = Ndef.get(tag);//判断是否为NDEF标签if (ndef != null) {ndef.connect();//判断是否支持可写if (!ndef.isWritable()) {return;}//判断标签的容量是否够用if (ndef.getMaxSize() < size) {return;}//3.写入数据ndef.writeNdefMessage(ndefMessage);Toast.makeText(this, "写入成功", Toast.LENGTH_SHORT).show();} else { //当我们买回来的NFC标签是没有格式化的,或者没有分区的执行此步//Ndef格式类NdefFormatable format = NdefFormatable.get(tag);//判断是否获得了NdefFormatable对象,有一些标签是只读的或者不允许格式化的if (format != null) {//连接format.connect();//格式化并将信息写入标签format.format(ndefMessage);Toast.makeText(this, "写入成功",Toast.LENGTH_SHORT).show();} else {Toast.makeText(this, "写入失败", Toast.LENGTH_SHORT).show();}}} catch (Exception e) {}}
}

其视图文件:

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"android:id="@+id/activity_main"android:layout_width="match_parent"android:layout_height="match_parent"><TextViewandroid:id="@+id/textView"android:gravity="center"android:layout_width="match_parent"android:layout_height="wrap_content"android:text="请将NFC标签或者贴纸靠近手机背面"/><ImageViewandroid:layout_width="wrap_content"android:layout_height="wrap_content"android:src="@drawable/nfc_run"/>
</RelativeLayout>

4、writeRunAppTagActivity 类:具体实现的功能为解析 nfc 数据 tag,往 nfc 标签中写入数据,使得碰一碰该 NFC 标签时可自动打开短信 APP,具体实现为:

package com.tr0e.nfctag.Activity;import android.content.Intent;
import android.nfc.NdefMessage;
import android.nfc.NdefRecord;
import android.nfc.NfcAdapter;
import android.nfc.Tag;
import android.nfc.tech.Ndef;
import android.nfc.tech.NdefFormatable;
import android.os.Bundle;
import android.widget.Toast;import com.tr0e.nfctag.BaseNfcActivity;
import com.tr0e.nfctag.R;public class writeRunAppTagActivity extends BaseNfcActivity {private String mPackageName = "com.android.mms";//短信@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_write_run_app_tag);}@Overridepublic void onNewIntent(Intent intent) {super.onNewIntent(intent);if (mPackageName == null)return;//1.获取Tag对象Tag detectedTag = intent.getParcelableExtra(NfcAdapter.EXTRA_TAG);writeNFCTag(detectedTag);}/*** 往标签写数据的方法** @param tag*/public void writeNFCTag(Tag tag) {if (tag == null) {return;}NdefMessage ndefMessage = new NdefMessage(new NdefRecord[]{NdefRecord.createApplicationRecord(mPackageName)});//转换成字节获得大小int size = ndefMessage.toByteArray().length;try {//2.判断NFC标签的数据类型(通过Ndef.get方法)Ndef ndef = Ndef.get(tag);//判断是否为NDEF标签if (ndef != null) {ndef.connect();//判断是否支持可写if (!ndef.isWritable()) {return;}//判断标签的容量是否够用if (ndef.getMaxSize() < size) {return;}//3.写入数据ndef.writeNdefMessage(ndefMessage);Toast.makeText(this, "写入成功", Toast.LENGTH_SHORT).show();} else { //当我们买回来的NFC标签是没有格式化的,或者没有分区的执行此步//Ndef格式类NdefFormatable format = NdefFormatable.get(tag);//判断是否获得了NdefFormatable对象,有一些标签是只读的或者不允许格式化的if (format != null) {//连接format.connect();//格式化并将信息写入标签format.format(ndefMessage);Toast.makeText(this, "写入成功", Toast.LENGTH_SHORT).show();} else {Toast.makeText(this, "写入失败", Toast.LENGTH_SHORT).show();}}} catch (Exception e) {}}
}

其视图文件为:

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"android:id="@+id/activity_main"android:layout_width="match_parent"android:layout_height="match_parent"><TextViewandroid:id="@+id/textView"android:gravity="center"android:layout_width="match_parent"android:layout_height="wrap_content"android:text="请将NFC标签或者贴纸靠近手机背面"/><ImageViewandroid:layout_width="wrap_content"android:layout_height="wrap_content"android:src="@drawable/nfc_run"/>
</RelativeLayout>

5、readTagInfoActivity 类:具体实现的功能为解析 nfc 数据 tag,读取标签类型、容量、URI数据等,具体实现为:

package com.tr0e.nfctag.Activity;import android.content.Intent;
import android.net.Uri;
import android.nfc.NdefMessage;
import android.nfc.NdefRecord;
import android.nfc.NfcAdapter;
import android.nfc.Tag;
import android.nfc.tech.Ndef;
import android.os.Bundle;
import android.os.Parcelable;
import android.util.Log;
import android.widget.TextView;import com.tr0e.nfctag.BaseNfcActivity;
import com.tr0e.nfctag.R;
import com.tr0e.nfctag.UriPrefix;import java.nio.charset.Charset;
import java.util.Arrays;public class readTagInfoActivity extends BaseNfcActivity {private TextView mNfcText;private String mTagText;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_read_tag_info);mNfcText = findViewById(R.id.tv_nfctext);}@Overridepublic void onNewIntent(Intent intent) {//获取Tag对象super.onNewIntent(intent);Tag detectedTag = intent.getParcelableExtra(NfcAdapter.EXTRA_TAG);//获取Ndef的实例Ndef ndef = Ndef.get(detectedTag);mTagText = "1、NFC标签类型: " + ndef.getType() + "\n2、该标签最大容量: " + ndef.getMaxSize() + " bytes\n";readNfcTag(intent);mNfcText.setText(mTagText);}/*** 读取NFC标签Uri*/private void readNfcTag(Intent intent) {if (NfcAdapter.ACTION_NDEF_DISCOVERED.equals(intent.getAction())) {Parcelable[] rawMsgs = intent.getParcelableArrayExtra(NfcAdapter.EXTRA_NDEF_MESSAGES);NdefMessage ndefMessage = null;int contentSize = 0;if (rawMsgs != null) {if (rawMsgs.length > 0) {ndefMessage = (NdefMessage) rawMsgs[0];contentSize = ndefMessage.toByteArray().length;} else {return;}}try {NdefRecord ndefRecord = ndefMessage.getRecords()[0];Uri uri = parse(ndefRecord);mTagText += "3、读取到的标签中Uri信息:" + uri.toString() + "\n4、当前该标签已写入的数据大小:" + contentSize + " bytes";} catch (Exception e) {}}}/*** 解析NdefRecord中Uri数据** @param record* @return*/public static Uri parse(NdefRecord record) {short tnf = record.getTnf();if (tnf == NdefRecord.TNF_WELL_KNOWN) {return parseWellKnown(record);} else if (tnf == NdefRecord.TNF_ABSOLUTE_URI) {return parseAbsolute(record);}throw new IllegalArgumentException("Unknown TNF " + tnf);}/*** 处理绝对的Uri* 没有Uri识别码,也就是没有Uri前缀,存储的全部是字符串** @param ndefRecord 描述NDEF信息的一个信息段,一个NdefMessage可能包含一个或者多个NdefRecord* @return*/private static Uri parseAbsolute(NdefRecord ndefRecord) {//获取所有的字节数据byte[] payload = ndefRecord.getPayload();Uri uri = Uri.parse(new String(payload, Charset.forName("UTF-8")));return uri;}/*** 处理已知类型的Uri** @param ndefRecord* @return*/private static Uri parseWellKnown(NdefRecord ndefRecord) {//判断数据是否是Uri类型的if (!Arrays.equals(ndefRecord.getType(), NdefRecord.RTD_URI))return null;//获取所有的字节数据byte[] payload = ndefRecord.getPayload();String prefix = UriPrefix.URI_PREFIX_MAP.get(payload[0]);byte[] prefixBytes = prefix.getBytes(Charset.forName("UTF-8"));byte[] fullUri = new byte[prefixBytes.length + payload.length - 1];System.arraycopy(prefixBytes, 0, fullUri, 0, prefixBytes.length);System.arraycopy(payload, 1, fullUri, prefixBytes.length, payload.length - 1);Uri uri = Uri.parse(new String(fullUri, Charset.forName("UTF-8")));return uri;}
}

其视图文件为:

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"android:id="@+id/activity_main"android:layout_width="match_parent"android:layout_height="match_parent"><TextViewandroid:id="@+id/textView"android:gravity="center"android:layout_width="match_parent"android:layout_height="wrap_content"android:text="请将NFC标签或者贴纸靠近手机背面"/><TextViewandroid:id="@+id/tv_nfctext"android:layout_width="match_parent"android:layout_height="wrap_content"android:layout_below="@+id/textView"android:layout_alignParentLeft="true"android:layout_alignParentStart="true"android:layout_marginTop="10dp"/><ImageViewandroid:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_below="@id/tv_nfctext"android:src="@drawable/nfc_read"/>
</RelativeLayout>

注意到此处有个工具类 UriPrefix,用于方便的获取 Uri 前缀:

package com.tr0e.nfctag;import java.util.HashMap;
import java.util.Map;public class UriPrefix {public static final Map<Byte, String> URI_PREFIX_MAP = new HashMap<>();// 预先定义已知Uri前缀static {URI_PREFIX_MAP.put((byte) 0x00, "");URI_PREFIX_MAP.put((byte) 0x01, "http://www.");URI_PREFIX_MAP.put((byte) 0x02, "https://www.");URI_PREFIX_MAP.put((byte) 0x03, "http://");URI_PREFIX_MAP.put((byte) 0x04, "https://");URI_PREFIX_MAP.put((byte) 0x05, "tel:");URI_PREFIX_MAP.put((byte) 0x06, "mailto:");URI_PREFIX_MAP.put((byte) 0x07, "ftp://anonymous:anonymous@");URI_PREFIX_MAP.put((byte) 0x08, "ftp://ftp.");URI_PREFIX_MAP.put((byte) 0x09, "ftps://");URI_PREFIX_MAP.put((byte) 0x0A, "sftp://");URI_PREFIX_MAP.put((byte) 0x0B, "smb://");URI_PREFIX_MAP.put((byte) 0x0C, "nfs://");URI_PREFIX_MAP.put((byte) 0x0D, "ftp://");URI_PREFIX_MAP.put((byte) 0x0E, "dav://");URI_PREFIX_MAP.put((byte) 0x0F, "news:");URI_PREFIX_MAP.put((byte) 0x10, "telnet://");URI_PREFIX_MAP.put((byte) 0x11, "imap:");URI_PREFIX_MAP.put((byte) 0x12, "rtsp://");URI_PREFIX_MAP.put((byte) 0x13, "urn:");URI_PREFIX_MAP.put((byte) 0x14, "pop:");URI_PREFIX_MAP.put((byte) 0x15, "sip:");URI_PREFIX_MAP.put((byte) 0x16, "sips:");URI_PREFIX_MAP.put((byte) 0x17, "tftp:");URI_PREFIX_MAP.put((byte) 0x18, "btspp://");URI_PREFIX_MAP.put((byte) 0x19, "btl2cap://");URI_PREFIX_MAP.put((byte) 0x1A, "btgoep://");URI_PREFIX_MAP.put((byte) 0x1B, "tcpobex://");URI_PREFIX_MAP.put((byte) 0x1C, "irdaobex://");URI_PREFIX_MAP.put((byte) 0x1D, "file://");URI_PREFIX_MAP.put((byte) 0x1E, "urn:epc:id:");URI_PREFIX_MAP.put((byte) 0x1F, "urn:epc:tag:");URI_PREFIX_MAP.put((byte) 0x20, "urn:epc:pat:");URI_PREFIX_MAP.put((byte) 0x21, "urn:epc:raw:");URI_PREFIX_MAP.put((byte) 0x22, "urn:epc:");URI_PREFIX_MAP.put((byte) 0x23, "urn:nfc:");}
}

至此整个 NFC 标签读写的 Demo 工程构建完毕。

3.4 Demo演示

1、先看下写入 “写入跳转到URL个人网站的NFC标签” 的功能,点击视图按钮后跳转到如下页面:
在这里插入图片描述
在这里插入图片描述
靠近 NFC 标签贴纸,弹出 “写入成功” 的提示:
在这里插入图片描述
此时退出 demo 程序(避免在前台触发 NFC 前台调度机制),重新碰一碰该 NFC 标签,将自动打开浏览器并跳转到我的个人博客网站:
在这里插入图片描述
在这里插入图片描述
2、此时若返回 demo 程序点击 “读取NFC标签中的Uri数据和基本信息” 视图,碰一碰扫描 NFC 标签,将读取出刚才写入标签的 uri 信息:
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
3、最后看下 “写入自动打开短信APP应用的NFC标签” 功能,同样点击试图按钮后靠近 NFC 贴纸写入数据,然后将 demo 程序退至后台,扫描 NFC 标签即可自动打开短信 APP:
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

其它更多 NFC 标签读写的实现代码,请参考: Android Nfc技术解析、使用。

历史漏洞

学习完 NFC 的基础知识、实践应用、标签读写开发等基础入门知识后,接下来看下 NFC 的相关历史 CVE 漏洞的概况。

4.1 中继攻击

车联网安全系列——特斯拉 NFC 中继攻击(CVE-2020-15912),此文介绍了特斯拉汽车由于 NFC 中继攻击导致车门被解锁的原理和过程。
在这里插入图片描述
实现以上攻击的方法操作起来并不难,只需要在车主手机或密钥卡和车辆的附近放置一台解码感应设备,采集相应的数字密码传输给汽车旁的接收设备,此时车辆会误以为接收设备就是密钥,经过简单的代码执行就能解锁车辆打开车门开走汽车。整个中继攻击的过程最大的难点是突破 NFC 芯片硬件设计上对于感应过程中建立会话连接的响应时间限制。

更多关于 NFC 中继攻击的信息可以参考以下材料:

  1. 看雪论坛:[原创]NFC中继攻击分享 ;
  2. 如何使用NFCGate对Android进行NFC安全研究 ;
  3. 安卓手机的NFC功能可截取非接触IC卡交互数据,用户需警惕;
  4. 中国知网-西电硕士论文:《NFC中继攻击的实现与防治方法》;

4.2 预览伪造

看雪译文:NFC 虚假标签 (False Tag) 漏洞 - CVE-2019-9295 ,介绍了 NFC 历史上一个关于Android 标签查看器 (com.android.apps.tags.TagViewer) 的标签预览界面的漏洞。
在这里插入图片描述

该漏洞有两种主要的攻击方案:

  1. 可能会随机出现一个弹出窗口, 警告用户已扫描了 NFC 标签 (由恶意应用生成),用户必须与之交互才能选择一个应用程序来处理;
  2. 用户扫描真正的标签后, 恶意应用可以在默认系统应用程序处理该标签之前拦截并更改标签的内容。 示例: 用户扫描包含电话号码的公司的标签,恶意应用可以立刻将电话号码更改为另一个电话号码,并且用户无法知道电话号码已更改 (除非通过’未中毒’的手机扫描标签)。

在方案 1 或 2 中, 用户最终可能会被诱骗点击链接,导致他们进入恶意网站,给错误的电话号码打电话/发短信或使用 NFC 标签进行任何其他操作。

本人在研究 NFC 标签预览的过程中也发现了一个类似的漏洞,已报送 Google,细节就不介绍了哈哈,等 Android 安全团队承认该漏洞并修复后再公开。

4.3 篡改卡片

看雪论坛:NFC手机:攻破交通卡,文章介绍了一款利用 NFC 手机攻击交通卡的恶意软件。将该款恶意软件安装在 NFC 手机中,窃贼只需将此手机轻轻靠近圣地亚哥交通卡(bip!-card)即可任意篡改卡中的余额。这种攻击方式有可能被黑客应用到其他 IC 卡攻击上,对其进行盗刷或其他恶意行为。

圣地亚哥交通卡(bip!-card)属于非接触式IC卡,而且已被黑客破解,因此可以通过NFC手机进行数据读写操作。该恶意软件正是利用这一点,在NFC手机中安装该恶意软件后即可读取该交通卡信息,同时向交通卡的扇区中写入数据,任意篡改余额信息。

更多关于 NFC 卡的破解文章可参考:

  1. [黑科技] NFC模拟门卡门禁;
  2. [看雪原创] 食堂饭卡破解 ;
  3. [看雪原创] 从NFC卡破解分析看ACR122U读写原理;

4.4 其它漏洞

从 CVE 官网搜索 的数据来看,NFC 公开漏洞历史上大致有 140 余个,除了上面提到的几个漏洞外,还有以下一些典型的漏洞:

  1. Android NFC 条件竞争漏洞分析(CVE-2021-0870);
  2. CVE-2019-2114 通过NFC文件共享实现静默安装漏洞;
  3. CVE-2021-39807 Guest账户无需任何权限即可开启NFC;

另外还有很多驱动层的溢出、越界漏洞,感兴趣的读者请自行查阅分析。

总结

本文从零到一介绍了 NFC 的基础知识、实践应用场景,并着重介绍了 NFC 标签的应用,以及讲述了 NFC 标签读写的开发基础示例,最后分析了 NFC 历史漏洞的概况。全文旨在为下一步深入分析 NFC 短距领域的安全风险作铺垫,希望后续能顺利挖掘到相关的高价值漏洞。

本文参考文章:

  1. NFC标签应用的实施;
  2. RFID和NFC有什么区别;
  3. 浅谈Android开发中的NFC功能;
  4. Android Nfc技术解析、使用;
  5. 安卓NFC标签读取快速开发教程(附源代码);

相关文章:

Android NFC 标签读写Demo与历史漏洞概述

文章目录前言NFC基础1.1 RFID区别1.2 工作模式1.3 日常应用NFC标签2.1 标签应用2.2 应用实践2.3 标签预览2.4 前台调度NFC开发3.1 NDEF数据3.2 标签的调度3.3 读写Demo3.4 Demo演示历史漏洞4.1 中继攻击4.2 预览伪造4.3 篡改卡片4.4 其它漏洞总结前言 NFC 作为 Android 手机一…...

亿级高并发电商项目-- 实战篇 --万达商城项目 六(编写角色管理、用户权限(Spring Security认证授权)、管理员管理等模块)

专栏&#xff1a;高并发---前后端分布式 &#x1f44f;作者简介&#xff1a;大家好&#xff0c;我是小童&#xff0c;Java开发工程师&#xff0c;CSDN博客博主&#xff0c;Java领域新星创作者 &#x1f4d5;系列专栏&#xff1a;前端、Java、Java中间件大全、微信小程序、微信…...

博视像元获近5000万元融资,主攻半导体前道及锂电高端部件供应

这两年各大车企与电池厂商都在快速新建产能&#xff0c;尤其上游原材料成本大增&#xff0c;反映到产业链上巨头都在寻求增效&#xff0c;高端制造技术投入也大幅增长。比如这家&#xff0c;高端工业相机提供商「博视像元」近期宣布完成近5000万的天使加轮融资&#xff0c;投资…...

SpringCloud-断路器Hystrix

一、降级使用1、添加依赖<!--hystrix--><dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-netflix-hystrix</artifactId></dependency>2、启动类添加注解EnableCircuitBreakerSpringBoot…...

JavaScript精简笔记

文章目录基础语法函数1.1、函数的使用预解析对象1.1、创建对象基础语法 函数 1.1、函数的使用 函数在使用时分为两步&#xff1a;声明函数和调用函数 ①声明函数 //声明函数 function 函数名(){//函数体代码 }function 是声明函数的关键字,必须小写由于函数一般是为了实现…...

MySQL常用函数汇总

1 MySQL 字符串函数函数描述实例ASCII(s)返回字符串 s 的第一个字符的 ASCII 码。返回 CustomerName 字段第一个字母的 ASCII 码&#xff1a;SELECT ASCII(CustomerName) AS NumCodeOfFirstCharFROM Customers;CHAR_LENGTH(s)返回字符串 s 的字符数返回字符串 RUNOOB 的字符数S…...

100M网口客户电脑插上网线就断线,自己工厂正常,是什么问题导致?

Hqst&#xff08;华强盛科技&#xff09;导读&#xff1a;物联工程师100M网口产品出现客户电脑插上网线就显示断线&#xff0c;无法通信&#xff0c;在自己工厂又正常使用&#xff0c;是什么问题&#xff1f;问&#xff1a;100M 网口&#xff0c; 使用改电路&#xff0c; 产品出…...

从零开始学习无人机 00 硬件配置

遥控器 型号 乐迪Radiolink AT9S Pro 固件更新 对遥控器固件作更新 乐迪Radiolink AT9S Pro 固件更新 光流传感器 型号 思动智能ThoneFlow-3901U 开发文档 Pmw3901光流传感器PX4开发文档 距离传感器 型号 空循环Nooploop TOFSense-F Pro 开发文档 TOFSense-F官方…...

免翻在Chrome上使用新必应(New Bing)聊天机器人

这里不讲如何加入New Bing内测 文章目录免翻使用New Bing用Chrome(非Edge)使用新必应聊天机器人免翻使用New Bing 第一个是免翻&#xff0c;需要一个浏览器插件Header Editor&#xff0c;扩展商店或者百度自行下载安装吧。打开该插件&#xff0c;添加一个规则 为方便填写&…...

LA@特征值和特征向量

文章目录特征值和特征向量例例求解方阵的特征值和特征向量&#x1f388;特征多项式特征方程方阵特征值和特征向量的性质证明推论衍生特征值更一般的转置和特征值其他结论(方阵多项式的特征值与方阵本身特征值的关系)特征向量线性相关性特征值和特征向量 许多定量分析模型中,常常…...

transpose代码学习

论文&#xff1a;TransPose: Keypoint Localization via Transformer Sen Yang Zhibin Quan Mu Nie Wankou Yang* School of Automation, Southeast University, Nanjing 210096, China {yangsenius, 101101872, niemu, wkyang}seu.edu.cn 下载地址&#xff1a;https://arxiv.o…...

【Redis】Redis 常用数据类型操作 ② ( 数据库操作 | 切换数据库 | 查询当前数据库键个数 | 清空当前数据库 | 清空所有数据库 )

文章目录一、Redis 数据库操作1、切换数据库2、查询当前数据库键个数3、清空当前数据库4、清空所有数据库一、Redis 数据库操作 在之前的博客 【Redis】Redis 数据库 安装、配置、访问 ( Redis 简介 | 下载 Redis 安装包 | 安装 Redis 数据库 | 命令行访问 Redis | 使用可视化工…...

最简单的物体识别例子

第一步下载百度EASYDL工具。 网址EasyDL 图像 然后下载本地训练工具包&#xff1a; 本地下载&#xff0c;运行。 首先创建数据集&#xff0c; 完成&#xff0c;创建目标任务。 选择物体检测创建任务 选择训练&#xff0c;将数据集引入 通用型小型设备SDK 选择这个可以本地直…...

指针——“C”

各位CSDN的uu们你们好呀&#xff0c;今天&#xff0c;小雅兰学习的内容是指针&#xff0c;这次只会讲一些很简单的知识点&#xff0c;更详细的指针知识会在以后的博客中逐步剖析清楚&#xff0c;那么现在&#xff0c;就让我们进入指针的世界吧 指针是什么 指针和指针类型 野指…...

学习 Linux 内核书籍推荐

原文链接&#xff0c;欢迎关注&#xff1a; 你为什么学习 Linux 内核&#xff1f; - CodeAllen的回答 - 知乎 https://www.zhihu.com/question/31369673/answer/2894981254 主要是工作需要&#xff0c;其实对于我自己的工作来说&#xff0c;在Linux开发的具体业务和算法才是重…...

深圳硬件黑客松活动,开放报名!

开源社KAIYUANSHE近期微信公众号订阅功能做调整啦&#xff01;没有被星标的账号在信息流里可能不显示大图了&#xff01;快星标⭐我们&#xff0c;就可以及时看到发布的文章啦&#xff01;STEP01 点击右上角标志STEP02 点击【设为星标】近年来&#xff0c;创客文化越来越受到人…...

力扣sql简单篇练习(十七)

力扣sql简单篇练习(十七) 1 销售分析| 1.1 题目内容 1.1.1 基本题目信息 1.1.2 示例输入输出 1.2 示例sql语句 # 可以考虑使用all函数 SELECT seller_id FROM Sales GROUP BY seller_id HAVING sum(price)>all(SELECT sum(price)FROM SalesGROUP BY seller_id )1.3 运行…...

Linux网络技术学习(六)—— 网络设备初始化(II)

文章目录初始化选项模块选项设备处理层初始化&#xff1a;net_dev_init用户空间辅助程序kmod解析热插拔虚拟设备虚拟设备范例通过/proc文件系统调整初始化选项 内核内建的组件以及模块加载的组件都能输入参数&#xff0c;使用户调整组件所实现的功能、重写默认值等 模块选项&…...

一手教你如何搭建Hadoop基于Zookeeper的集群(5台主机)

文章目录一、设计集群图二、准备五台虚拟机2.1、下载安装文件2.2、创建虚拟机2.3、配置网络2.4、修改主机名称2.5、关闭防火墙2.6、同步时间2.7、设置/etc/hosts文件2.8、设置免密登录2.9、为后面可以主备替换安装psmisc三、安装JDK3.1、安装jdk3.2、测试jdk是否安装成功3.3、将…...

Spring Cloud是什么?怎么理解Spring Cloud?

简介Spring Cloud项目的官方网址&#xff1a;https://projects.spring.io/spring-cloud/ Spring Cloud 并不是一个项目&#xff0c;而是一组项目的集合。在 Spring Cloud中包含了很多的子项目&#xff0c;每一个子项目都是一种微服务开发过程中遇到的问题的一种解决方案。它利…...

robotframework + selenium自动化测试常见的问题

1、 插入中文数据提示 FAIL UnicodeEncodeError: ‘latin-1’ codec can’t encode characters in position 92-107: ordinal not in range(25 DataBaseLibrary插入中文乱码的解决&#xff1a;修改D:\Python27\Lib\site-packages\DatabaseLibrary\connection_manager.py里的co…...

2023春招java面试题及答案

2023春招java面试题及答案总结1.以下Dubbo服务负载均衡策略中&#xff0c;哪一个策略的功能是相同参数的请求总是发到同一个提供者&#xff08;&#xff09;2.如下代码&#xff1a;请问编译运行的结果是什么&#xff1f;3.给出如下代码&#xff1a;请问编译运行的结果是什么&am…...

QT+OpenGL光照

QTOpenGL光照 本篇完整工程见gitee:QtOpenGL 对应点的tag&#xff0c;由turbolove提供技术支持&#xff0c;您可以关注博主或者私信博主 颜色 现实生活中看到的物体的颜色并不是这个物体真正拥有的颜色&#xff0c;而是它所反射的颜色 太阳光能被看见的白光是多找演的的组合…...

OpenCV-PyQT项目实战(7)项目案例03:鼠标框选

欢迎关注『OpenCV-PyQT项目实战 Youcans』系列&#xff0c;持续更新中 OpenCV-PyQT项目实战&#xff08;1&#xff09;安装与环境配置 OpenCV-PyQT项目实战&#xff08;2&#xff09;QtDesigner 和 PyUIC 快速入门 OpenCV-PyQT项目实战&#xff08;3&#xff09;信号与槽机制 …...

vue2版本《后台管理模式》(上)

后台管理模式项目开发经验总结如下&#xff0c;希望对你们有些帮助&#xff1a; 文章目录一、app 出口位置二 、 index.js 路由配置三、package.json 文件四、 main.js 既然安装插件那就需要引入五、 跨域问题总结首先需要一个完整的v2版本的项目 vue2版本思路&#xff1a;首先…...

C++与C基础重叠部分

Cmake CPP程序开发过程 计算机硬件—>机器语言—>汇编—>cppcpp—>机器(gcc)Make(makefile)—>本地智能批处理翻译机制Cmake—>跨平台生成不同设备上的makefile进行执行 Cpp基础学习 基本知识 基本格式 #include<iostream> using namespace std;…...

神经网络基础部件-卷积层详解

前言 在全连接层构成的多层感知机网络中&#xff0c;我们要通过将图像数据展平成一维向量来送入模型&#xff0c;但这会忽略了每个图像的空间结构信息。理想的策略应该是要利用相近像素之间的相互关联性&#xff0c;将图像数据二维矩阵送给模型中学习。 卷积神经网络(convolu…...

【计算机网络】HTTPS协议原理

文章目录一、认识HTTPS协议二、为什么要发明HTTPS三、HTTP与HTTPS的区别四、常见的加密方式1. 对称加密2. 非对称加密3. 数据摘要4. 数字签名五、HTTPS的原理探究方案1&#xff1a;只使用对称加密方案2&#xff1a;只使用非对称加密方案3&#xff1a;双方都使用非对称加密方案4…...

21岁,华科博士在读,我的赛事Top经验

Datawhale干货 作者&#xff1a;vaew&#xff0c;华中科技大学&#xff0c;博士二年级在读简介笔者vaew&#xff0c;21岁&#xff0c;现为华中科技大学机械科学与工程学院陶波教授课题组博士二年级学生。主要研究方向是基于视触融合的机器人灵巧操作。学业之余的研究兴趣包括图…...

基于ThinkPHP6.0+Vue+uni-app的多商户商城系统好用吗?

likeshop多商户商城系统适用于B2B2C、多商户、商家入驻、平台商城场景。完美契合平台自营联营加盟等多种经营方式使用&#xff0c;系统拥有丰富的营销玩法&#xff0c;强大的分销能力&#xff0c;支持官方旗舰店&#xff0c;商家入驻&#xff0c;平台抽佣商家独立结算&#xff…...

做视频网站需要/百度标记号码认证平台

在网络环境中的攻击类型 消息保密性范畴&#xff1a;    1、泄密  2、传输分析   消息认证/数字签名&#xff1a;  3、伪装  4、内容修改 插入/删除/转换/修改(消息内容) 5、顺序修改 插入/删除/重排&#xff08;以消息为单位&#xff09; 6、计时修改 延时…...

wordpress 微博侧边栏/正规电商培训学校排名

问: 我用的是vc2008 express版。 自己写了一个服务功能的lib&#xff0c;在一个客户程序中要调用我自己写的这个lib。无论lib还是客户端程序都是非mfc的&#xff0c;只用到了最基本的win32程序框架和opengl库。 在本机可以运行&#xff0c;使用MTD方式编译后&#xff…...

做网站一定要买/搜狗搜索网页版

异常 原因 没有以管理员身份打开DOS窗口。 解决...

行业网站建设公司/怎样看网页的友情链接

我们逛街&#xff0c;经常会看到店铺关门或转让&#xff0c;是什么原因搞得我们实体经济前景一片暗淡呢&#xff1f;我们简单分析下&#xff1a; 1、消费模式的改变&#xff1a;随着互联网的快速发展和普及&#xff0c;以及电子商务的发展&#xff0c;人们的生活观念也逐渐受到…...

重庆建材网站建设/网站seo综合诊断

现在服务器的大多数是管理员来远程管理的&#xff0c;服务器所在的机房重地闲人免进&#xff0c;既安全&#xff0c;又能使得管理员们足不出户就可以登陆服务器。 远程登录服务器需要在服务器端开启“允许远程连接到此计算机”&#xff0c;右击“我的电脑”--“远程”--勾选“允…...

flash网站整站源码免费下载/建设网站制作

搜索热词工具介绍redis-port 是一组开源工具集合&#xff0c;主要用于 Redis 节点间的数据库同步、数据导入、数据导出&#xff0c;支持 Redis 的跨版本数据迁移&#xff0c;工具集中包括以下工具&#xff1a;redis-sync&#xff1a;支持在 Redis 实例之间进行数据迁移。redis-…...