唔,目前最便宜的能玩的 NFC 读卡器就是 PN532 模块了,连接电脑的话还需要一个 TTL 转 USB 工具,这里使用 CH340 转接板。
连接好读卡器的串口排线,就可以使用 M1T 等软件尝试解密钥了。
使用 M1T 解析出 IC 卡的 Key B 为 A9 DE 7F 3C EB 1F
。
读取数据进行分析,以下是该卡的第 10 和 11 扇区以及 0 扇区的块 0,除此之外的扇区数据均为空。
Section 0
Block 0: 5E E1 6E A4 75 08 04 00 01 DD 54 AF A4 43 D6 1D
Section 10
Block 0: 23 0A 01 00 09 F5 00 D6 02 00 5E 00 00 5E 00 62
Block 1: 1B F5 03 EE 04 0A AA 00 A0 01 83 1B 00 9E 00 84
Block 2: 23 0A 01 00 09 F5 00 D6 02 00 5E 00 00 5E 00 62
Block 3: 0A A1 1E 91 5B 81 7F 07 88 69 A9 DE 7F 3C EB 1F
Section 11
Block 0: C1 3E 12 2C 00 C1 00 00 00 00 06 00 00 06 00 B6
Block 1: 1B F5 03 EE 04 0A AA 00 A0 01 83 1B 00 9E 00 84
Block 2: C1 3E 12 2C 00 C1 00 00 00 00 06 00 00 06 00 B6
Block 3: 0B A1 1E 91 5B 81 7F 07 88 69 A9 DE 7F 3C EB 1F
通过观察发现,这些扇区的 Key A 并不相同,而 Key B 为固定值;并且第 10 扇区和第 11 扇区中块 0 和块 2 的数据相同。
通过多次刷卡发现,第 10 扇区会被刷卡机修改,而第 11 扇区未被使用。
接下来的分析重点将放在 Key A 和第 10 扇区上。
通过观察发现 Key A 的结构如下:
0A A1 1E 91 5B 81
数据位 | 作用 | 计算 |
---|---|---|
0A | 扇区号 | 0A |
A1 1E 91 5B | 取反 UID | ~5E ~E1 ~6E ~A4 |
81 | 固定值 | 81 |
通过多次比对数据改动和刷卡机显示的数额,猜测出一些数据位的作用如下:
23 0A 01 00 09 F5 00 D6 02 00 5E 00 00 5E 00 62
数据位 | 作用 | 计算 |
---|---|---|
23 | 异或校验 | 0A ^ 01 ^ 00 ^ 09 ^ F5 ^ 00 ^ D6 ^ 02 ^ 00 ^ 5E ^ 00 ^ 00 ^ 5E ^ 00 |
0A | 和校验 | 01 + 00 + 09 |
01 00 | 剩余数额 | 0.01CNY * 100 |
F5 | 和取反校验 | ~ (01 + 00 + 09) |
D6 02 | 上次使用数额 | 7.26CNY * 100 |
5E | 使用次数 | |
62 | 和取反校验 | ~ (0A + 01 + 00 + 09 + F5 + 00 + D6 + 02 + 00 + 5E + 00 + 00 + 5E + 00) |
黑色标注的数据猜测为无实际作用的数据。
这里的数据是固定的,不知道用途,但可以猜测出第一位和最后一位为校验位:
1B F5 03 EE 04 0A AA 00 A0 01 83 1B 00 9E 00 84
数据位 | 作用 | 计算 |
---|---|---|
1B | 异或校验 | F5 ^ 03 ^ EE ^ 04 ^ 0A ^ AA ^ 00 ^ A0 ^ 01 ^ 83 ^ 1B ^ 00 ^ 9E ^ 00 |
84 | 和取反校验 | ~ (F5 + 03 + EE + 04 + 0A + AA + 00 + A0 + 01 + 83 + 1B + 00 + 9E + 00) |
通过比对,尝试将不同的数据位置零后也可以正常使用。
先构建 M1 卡的结构
// M1Card.h
#pragma once
#include
typedef struct KEY_TAG {
uint8_t KeyA[6];
uint8_t AccessBits[4];
uint8_t KeyB[6];
} KEY, *PKEY;
typedef struct SECTOR_TAG {
uint8_t Data0[16];
uint8_t Data1[16];
uint8_t Data2[16];
KEY Key;
} SECTOR, *PSECTOR;
然后构建解析出的水卡的结构
// MyCard.h
#pragma once
#include
#include "M1Card.h"
typedef struct CARDDATA_TAG {
uint8_t OverallXorCheck;
uint8_t NumberSumCheck;
union {
uint16_t Number;
struct {
uint8_t NumberByte01;
uint8_t NumberByte02;
};
};
uint8_t Padding1Byte01;
uint8_t NumberSumXorCheck;
uint8_t Padding5Bytes[5];
uint8_t UsageCountAddNum;
uint8_t Padding1Byte02;
uint8_t UsageCountSumCheck;
uint8_t Padding1Byte03;
uint8_t OverallSumXorCheck;
} CARDDATA, *PCARDDATA;
typedef struct KEYA_TAG {
uint8_t Index;
uint8_t UIDCheck[4];
uint8_t Padding1Byte;
} KEYA, *PKEYA;
void MyCard_SetCard(PSECTOR pSectorArray, uint8_t* pUIDBuffer, int number);
最后写出利用函数
// MyCard.c
#include "M1Card.h"
#include "MyCard.h"
#include
uint8_t gAcs[4] = { 0x7F, 0x07, 0x88, 0x69 };
uint8_t gKeyB[6] = { 0xA9, 0xDE, 0x7F, 0x3C, 0xEB, 0x1F };
uint8_t gIdData[16] = { 0x1D, 0xF5, 0x03, 0xEE, 0x04, 0x0A, 0xAA, 0x00,
0xA0, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0xC0 };
void MyCard_SetUIDFromUID(PSECTOR pSectorArray, uint8_t* pUIDBuffer) {
uint8_t check = 0;
for (int i = 0; i < 4; i++) {
pSectorArray->Data0[i] = pUIDBuffer[i];
check ^= pUIDBuffer[i];
}
pSectorArray->Data0[4] = check;
*((uint16_t*) (pSectorArray->Data0 + 5)) = 0x0408;
pSectorArray->Data0[7] = 0x00;
}
void MyCard_SetKeyFromUID(PSECTOR pSectorArray, uint8_t* pUIDBuffer) {
for (int i = 0; i < 16; i++) {
PKEY pKey = (PKEY) &pSectorArray[i].Key;
/* ACs */
memcpy_s(pKey->AccessBits, 4, gAcs, 4);
/* KeyB */
memcpy_s(pKey->KeyB, 6, gKeyB, 6);
/* KeyA */
PKEYA pKeyA = (PKEYA) &pKey->KeyA;
pKeyA->Index = (uint8_t) i;
pKeyA->Padding1Byte = 0x81;
for (int i = 0; i < 4; i++) {
pKeyA->UIDCheck[i] = ~pUIDBuffer[i];
}
}
}
void MyCard_SetDataSector(PSECTOR pSectorArray, int number) {
PCARDDATA pData = (PCARDDATA)(pSectorArray + 10)->Data0;
memset(pData, 0, 16);
pData->Number = number;
pData->NumberSumCheck = pData->NumberByte01 + pData->NumberByte02 + pData->Padding1Byte01;
pData->NumberSumXorCheck = ~pData->NumberSumCheck;
// pData->UsageCountAddNum = pData->UsageCountSumCheck = 0x00;
int sum = 0;
for (int i = 1; i < sizeof(CARDDATA) / sizeof(uint8_t) - 2; i++) {
pData->OverallXorCheck ^= ((uint8_t*) pData)[i];
sum += ((uint8_t*) pData)[i];
}
pData->OverallSumXorCheck = (uint8_t) ~sum;
memcpy_s((pSectorArray + 10)->Data2, 16, (pSectorArray + 10)->Data0, 16);
}
void MyCard_SetDataIDSector(PSECTOR pSectorArray) {
memcpy_s((pSectorArray + 10)->Data1, 16, gIdData, 16);
}
void MyCard_SetCard(PSECTOR pSectorArray, uint8_t* pUIDBuffer, int number) {
MyCard_SetUIDFromUID(pSectorArray, pUIDBuffer);
MyCard_SetKeyFromUID(pSectorArray, pUIDBuffer);
MyCard_SetDataIDSector(pSectorArray);
MyCard_SetDataSector(pSectorArray, number);
}