如何以编程方式检测 NFC 芯片的内存 size/page 号

How to programmatically detect the memory size/page number of an NFC chip

除了探测特定页码并检查它们是否存在之外,以编程方式检测 NFC 芯片(MIFARE Ultralight 或 NTAGxxx)的内存大小或页数的最简单方法是什么?

返回的 ATR 中或存储在芯片内存保护区的某处是否有特定字节可以判断它是特定的 NTAGxxx 芯片,还是页数或字节数?

一般来说,您只能通过广泛的指纹识别和使用读取命令进行探测来确定这一点。

  1. 指纹识别:这意味着您需要有一个制造商特定特征的数据库(例如,从数据表中收集,而且——不幸的是,更常见的是——从经验测试中收集,因为此类信息是没有(公开?或根本没有?)记录大多数标签)您可以检查您的标签。

  2. 正在探测:

    • 从第 0 页开始
    • 阅读页面
    • 成功时:将读取地址增加 4 页并重新开始
    • 失败时:将读取地址减 1 并再次尝试读取,直到找到可读地址
    • 在扇区结束时:从下一个扇区重新开始

    这样你就可以找到最后一个可读页面地址,它不一定是标签内存的"real"端(例如,末尾可能有受保护的页面)。

如果您的 Ultralight 和 NTAG 标签是 EV1 标签(即它们实现了 Ultralight EV1 命令集),则公开一个 GET_VERSION 命令,您可以使用它来检索存储大小信息。版本信息结合所有现有标签指纹的数据库(您可以从 Ultraight/NTAG 数据表中获取该信息)将使您能够可靠地1 确定标签类型,并且,因此,内存大小。

1)除了一些以"NTAG"的形式出售的假冒芯片是模仿真正NTAG芯片的参数但似乎有内存大小,缺乏命令支持,等与自己模仿参数的芯片不匹配

当谈到遵循 NFC 论坛 2 类标签操作规范的 NFC 标签时,您还可以依赖编码到功能容器中的标签内存大小。不过,此内存大小不一定是物理内存大小。

我认为,从接受的答案来看,下面的代码是有道理的,

int numberOfPages = 0;
int tagType = ulTag.getType();
if(tagType == MifareUltralight.TYPE_ULTRALIGHT) {
    numberOfPages = 16;
} else if(tagType == MifareUltralight.TYPE_ULTRALIGHT_C) {
    numberOfPages = 47;
}

getMaxTransceiveLength() 用于检索可以使用 transceive(byte[]) 发送的最大字节数。 收发命令分别使用前 3 个字节作为标志、命令和页码。

我们需要检查下面的代码是否有效?

int totalBytes = ulTag.getMaxTransceiveLength() + 3;
int totalPages =  totalBytes / ulTag.PAGE_SIZE;

对于 MIFARE ULTRALIGHT,您可以通过这种方式查看版本。

public static final int OFFSET_128_BYTE =  0x025;
public static final int OFFSET_48_BYTE  =  0x010;

public static int checkType(MifareUltralight mrfid){
    int cfgOffset = -1;
    byte[] response = null;
    byte[] writeResponse = null;
    try {
        response = mrfid.transceive(new byte[]{
                (byte) 0x60 // GET_VERSION
        });
    } catch (TagLostException tle) {
        Utility.log("setPasswordAndEnable GET_VERSION, TagLostException ... ");
        return cfgOffset;
    } catch (IOException ioe) {
        Utility.log("setPasswordAndEnable GET_VERSION, IOException ... ");
        return cfgOffset;
    }
    if ((response != null) && (response.length >= 8)) {
        // success
        if ((response[0] == (byte) 0x00) && (response[1] == (byte) 0x04)) {
            // tag is from NXP
            if (response[2] == (byte) 0x03) {
                // MIFARE Ultralight
                if ((response[4] == (byte) 0x01) && (response[5] == (byte) 0x00)) {
                    // MIFARE Ultralight EV1 (V0)
                    switch (response[6]) {
                        case (byte) 0x0B:
                            // MF0UL11
                            cfgOffset = OFFSET_48_BYTE;
                            break;
                        case (byte) 0x0E:
                            // MF0UL21
                            cfgOffset = OFFSET_128_BYTE;
                            break;

                        default:
                            // unknown
                            break;
                    }
                }
            }
        }
    }
    return cfgOffset;
}