当你将一张智能卡插入读卡器时,卡片会立即发送一串看似随机的字节序列——这就是ATR(Answer To Reset,复位应答)。这串编码如同智能卡的"身份证",包含了卡片的工作参数、支持协议和关键特性。理解ATR的每个字节,就像掌握了一把打开智能卡通信大门的钥匙。
ATR是智能卡在复位后发送给读卡器的第一组数据,通常由2到33个字节组成。这组数据遵循ISO/IEC 7816-3标准定义的结构,可以分为几个关键部分:
一个典型的ATR示例(SIM卡常见):
code复制3B 9F 96 80 1F C7 80 31 E0 73 FE 21 1B 67 4A 4C 75 30 34 05 00 3B 7A 18 00 00 00 6A
注意:ATR解析必须从第一个字节开始顺序处理,每个字节的含义取决于它在序列中的位置以及前面字节的指示。
TS字节只有两种合法值:
0x3B(直接约定):表示正向约定,逻辑1=高电平,LSB优先传输0x3F(反向约定):表示反向约定,逻辑1=低电平,MSB优先传输实际应用中,约95%的卡片使用0x3B约定。当读卡器检测到非这两种值时,应立即终止通信。
T0字节的高4位和低4位分别提供关键信息:
| 比特位 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
|---|---|---|---|---|---|---|---|---|
| 含义 | TA1存在 | TB1存在 | TC1存在 | TD1存在 | 历史字符数 |
例如,T0=0x9F表示:
1001)1111)TA1字节编码了F(时钟率调整因子)和D(比特率调整因子)参数:
python复制def decode_TA1(ta1):
F_table = [372, 372, 558, 744, 1116, 1488, 1860, 'RFU']
D_table = [1, 2, 4, 8, 16, 32, 64, 12]
high_nibble = (ta1 & 0xF0) >> 4
low_nibble = ta1 & 0x0F
F = F_table[high_nibble % 8]
D = D_table[low_nibble % 8]
return F, D
典型值TA1=0x96对应F=512,D=8,表示卡片支持较高的通信速率。
TB1指示卡片所需的VPP编程电压(现代卡片通常不需要):
| 字段 | PI1 | II | 保留 |
|---|---|---|---|
| 比特 | 7-5 | 4-1 | 0 |
TC1定义字符间需要增加的额外保护时间N(单位etu):
code复制N = TC1值
多数卡片使用TC1=0x00,表示不增加额外保护时间。
TDx字节不仅指示是否存在更多接口字符,还声明支持的协议:
| 比特位 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
|---|---|---|---|---|---|---|---|---|
| 含义 | TA(i+1) | TB(i+1) | TC(i+1) | TD(i+1) | 协议类型 |
常见协议类型:
0x00:T=0,字符导向协议0x01:T=1,块导向协议0x0F:专用协议检查物理连接
分析ATR接收情况
0x3B或0x3F参数协商失败处理
提示:当遇到不兼容卡片时,可尝试强制使用T=0协议和默认参数(F=372,D=1)进行基础通信。
SIM卡典型ATR:3B 9F 96 80 1F C7 80 31 E0 73 FE 21 1B 67 4A 4C 75 30 34 05 00 3B 7A 18 00 00 00 6A
金融IC卡ATR:3B 6D 00 00 80 31 80 65 B0 85 02 02 F3 12 12 00 82 90 00
历史字符不仅包含制造商信息,还可能编码:
解码示例(TLV格式):
code复制Tag 0x80: 卡片制造商
Tag 0x81: 芯片型号
Tag 0x82: 操作系统版本
某些特殊卡片可能使用:
处理建议:
ATR分析可用于:
典型安全检查点:
| 工具类型 | 推荐型号 | 特点 |
|---|---|---|
| 通用读卡器 | ACS ACR122U | 支持PC/SC,开发友好 |
| 专业分析仪 | Proxmark3 | 射频与协议分析 |
| 开发板 | STM32F10x | 内置智能卡接口 |
Python ATR解析示例:
python复制import binascii
def parse_atr(atr_hex):
atr = binascii.unhexlify(atr_hex.replace(' ',''))
pos = 0
ts = atr[pos]
pos += 1
# 解析T0和接口字符
t0 = atr[pos]
pos += 1
# 提取各接口字符
interface_chars = {}
for flag, name in [(0x10, 'TA1'), (0x20, 'TB1'),
(0x40, 'TC1'), (0x80, 'TD1')]:
if t0 & flag:
interface_chars[name] = atr[pos]
pos += 1
# 继续解析后续接口字符...
return {'TS': ts, 'T0': t0, **interface_chars}
在实际项目中遇到的最棘手问题往往是那些不符合标准的卡片行为。有一次调试中发现某批次卡片在TA1中声明支持高速模式,但实际通信时却频繁出错。最终发现是卡片时钟电路设计缺陷,通过强制使用较低速率解决了问题。这种案例提醒我们,ATR信息需要验证,不能盲目信任。