一张水卡的数据解析及利用

设备 & 软件

唔,目前最便宜的能玩的 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 的分析

通过观察发现 Key A 的结构如下:

0A A1 1E 91 5B 81

数据位 作用 计算
0A 扇区号 0A
A1 1E 91 5B 取反 UID ~5E ~E1 ~6E ~A4
81 固定值 81

块 0 的分析

通过多次比对数据改动和刷卡机显示的数额,猜测出一些数据位的作用如下:

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)

黑色标注的数据猜测为无实际作用的数据。

块 1 的分析

这里的数据是固定的,不知道用途,但可以猜测出第一位和最后一位为校验位:

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);
}

写在结尾

⚠⚠⚠ 本篇文章仅供学习,请勿用于非法用途!⚠⚠⚠

你可能感兴趣的:(IC卡,安全,c语言,NFC,IC卡,M1T)