ハードウェアハッキングして NFC コントローラの命令を解析する

(Felica/Mifare/NFC チャレンジシリーズ) その他の記事はこちら 「Felica/Mifare/NFC でいろいろ実験」
https://qiita.com/nanbuwks/items/1f416d6e45a87250ee0a

-----

PN532 のユーザマニュアル
https://www.nxp.com/docs/en/user-guide/141520.pdf
には命令がありますが、イマひとつわからないです。

なので直接ハードウェアに聞いてみます。

「PN532 NFC RFID module について調べる」
https://qiita.com/nanbuwks/items/ac02bb307c9850b5e5c4

に搭載されていた NXP PN532 コントローラは SPI,I2C,HSU でホストコントローラと通信できますが、これのHSUモードがUARTとして通信しています。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/139524/7bfdc15f-58dd-58b8-cb45-caaa55d90312.png)

HSUでは、ピン27が RX として機能しています。ここの通信を調べてみます。
RX はホストコントローラから PN532 への命令が取れます。
PN532 からの返答を取得するには TX もモニターする必要がありますが、今回は RX のみを調べてみます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/139524/d4ac032d-369c-0d86-6b7e-61e8da8791b8.png)

「ESP32 で UART ダンプ」
https://qiita.com/nanbuwks/items/ac2454a19702a80b08b6

で行った、シリアル通信の内容のダンプの手法を使って中身を見てみましょう。

なお、 PN532には デバッグ用のUARTもありますが、今回はこちらは使用しませんでした。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/139524/6487ff51-2315-ee7a-adb9-1ae42e9dabe9.png)

ESP32 につなげる信号を取り出すために、基板を調べてみます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/139524/96151d6e-dd31-e717-112e-5da5916d0872.png)


白い線がHSUモードでのRXとなります。702と書いてあるチップに入ってますね。このチップは
SOT-363 パッケージの デュアル N-ch MOSFET 。
型番は2N7002DWです。

別会社ですがデータシートを見ればピン配置がわかります。



![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/139524/b7dce03f-eb84-314a-17d2-10d7ea9731f9.png)

出力の1番ピンにつながっている抵抗の端子にはんだづけしました。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/139524/cbbd9906-0f7d-3a95-f8ad-04ae9bff94ed.png)


ESP32 を取り付けて分析します。

ESP32側のスケッチです。

```

char buff[50];
//HardwareSerial Serial1(2);
void setup() {
 Serial.begin(115200);
 Serial2.begin(115200);
}

void loop() {
 int counter=0;
 for (int counter=0;counter<65536;counter++){
    sprintf(buff, "%08X: ",counter);
    Serial.print(buff);
    for (int address=0;address<16;address++){
      if (Serial2.available()) {
        int inByte = Serial2.read();
        sprintf(buff, "%02X ",inByte);
        Serial.print(buff);
      }
      delayMicroseconds(20);
    }
    Serial.println();
 }
}
```

# 通信内容


```

0000260B:
0000260C: 55 55 00 00 00 00 00 FF 02 FE D4 02 2A 00 00 00 
0000260D: FF 06 FA D4 32 05 FF 01 FF F6 00 00 00 FF 05 FB 
0000260E: D4 14 01 14 01 02 00 00 00 FF 04 FC D4 4A 01 00 
0000260F: 00 00 FF 06 FA D4 60 01 01 11 10 A9 00 
00002610:

```
これを、読み解いていきます。

## 初期化


### HSU wake up condition
```

55 55 00 00 00 
```

### GetFirmwareVersion
```
00 00 FF 02 FE D4 02 2A 00 
```

これは以下のような構成になっています。

|||
|:---:|:---:|
|  00 | プリアンブル |
| 00 FF | スタートコード |
| 02  | パケット長 |
| FE  | パケット長のチェックサム |
|  D4 02  |  パケット内容 |
| 2A | パケット内容のチェックサム |
| 00 | ポストアンブル |

となり、パケット内容は以下の構成になっています。

|パケット内容|意味|
|:---:|:---:|
|  D4  |  ホストからPN532の命令|
| 02 | 命令:GetFirmwareVersion |

### RFConfiguration

```

00 00 FF 06 FA D4 32 05 FF 01 FF F6 00
```


|||
|:---:|:---:|
|  00 | プリアンブル |
| 00 FF | スタートコード |
| 06  | パケット長 |
| FA  | パケット長のチェックサム |
|  D4 32 05 FF 01 FF  |  パケット内容 |
| F6 | パケット内容のチェックサム |
| 00 | ポストアンブル |

となり、パケット内容は以下の構成になっています。

|パケット内容|意味|
|:---:|:---:|
|  D4  |  ホストからPN532の命令|
| 32 | 命令:RFConfiguration |
| 05 | CfgItem = 0x05: MaxRetries |
| FF | MxRtyATR = 0xFF: try eternally |
| 01 | MxRtyPSL = 0x01: PSL_REQ/PPS request is sent twice in case of need |
| FF | MxRtyPassiveActivation = 0xFF: try eternally |


### SAMConfiguration

```

00 00 FF 05 FB D4 14 01 14 01 02 00 :   Mode=Normal mode,TIMEOUT=14, 
```



|||
|:---:|:---:|
|  00 | プリアンブル |
| 00 FF | スタートコード |
| 05  | パケット長 |
| FB  | パケット長のチェックサム |
| D4 14 01 14 01  |  パケット内容 |
| 02 | パケット内容のチェックサム |
| 00 | ポストアンブル |



|パケット内容|意味|
|:---:|:---:|
| D4 | ホストからPN532の命令|
| 14 | 命令:SAMConfiguration |
| 01 | Mode = 0x01: Normal mode, the SAM is not used  |
| 14 | TimeOut = 0x14: meaningless ? |
| 01 | IRQ = 0x01: P70_IRQ pin is driven by the PN532 |



ここまでが初期化。

## AutoPoll



```

 00 00 FF 04 FC D4 4A 01 00 E1 00 : InListPassiveTarget MaxTg=1,BrTy=106 kbps type A (ISO/IEC14443 Type A)  
```


|||
|:---:|:---:|
| 00 | プリアンブル |
| 00 FF | スタートコード |
| 04  | パケット長 |
| FC  | パケット長のチェックサム |
| D4 4A 01 00  |  パケット内容 |
| E1 | パケット内容のチェックサム |
| 00 | ポストアンブル |



|パケット内容|意味|
|:---:|:---:|
| D4 | ホストからPN532の命令|
| 4A | 命令:InListPassiveTarget |
| 01 | MaxTg  = 0x01: Normal mode, the SAM is not used  |
| 00 | BrTy  = 0x00: 106 kbps type A (ISO/IEC14443 Type A) |





```

00 00 FF 06 FA D4 60 01 01 11 10 A9 00
```

|||
|:---:|:---:|
| 00 | プリアンブル |
| 00 FF | スタートコード |
| 06  | パケット長 |
| FA  | パケット長のチェックサム |
| D4 60 01 01 11 10  |  パケット内容 |
| A9 | パケット内容のチェックサム |
| 00 | ポストアンブル |



|パケット内容|意味|
|:---:|:---:|
| D4 | ホストからPN532の命令|
| 60 | 命令:InAutoPoll  |
| 01 | PollNr   = 0x01: one polling, is a polling for each Type  |
| 01 | Period = 0x01:  polling period in  150 ms |
| 11 | Type 1 = 0x11:  FeliCa 212 kbps card |
| 10 | Type 2 = 0x10:  Mifare card |



## Mifare Read with Key

```
00004C59:
00004C5A: 00 00 FF 04 FC D4 4A 01 00 E1 00 
00004C5B:
...
00004C78:
00004C79: 00 00 FF 0F F1 D4 40 01 60 04 CE CF CE CF CE CF 
00004C7A: FA FB FC FD FE 00 00 00 FF 05 FB 
00004C7B: D4 40 01 30 04 B7 00 
00004C7C:

```

これを解読してみます。

```

00 00 FF 04 FC D4 4A 01 00 E1 00
```



|||
|:---:|:---:|
| 00 | プリアンブル |
| 00 FF | スタートコード |
| 04  | パケット長 |
| FC  | パケット長のチェックサム |
| D4 4A 01 00 |  パケット内容 |
| E1 | パケット内容のチェックサム |
| 00 | ポストアンブル |


|パケット内容|意味|
|:---:|:---:|
| D4 | ホストからPN532の命令|
| 4A | 命令:InListPassiveTarget |
| 01 | MaxTg  = 0x01: Normal mode, the SAM is not used  |
| 00 | BrTy  = 0x00: 106 kbps type A (ISO/IEC14443 Type A) |


```

00 00 FF 0F F1 D4 40 01 60 04 CE CF CE CF CE CF FA FB FC FD FE 00

```



|||
|:---:|:---:|
| 00 | プリアンブル |
| 00 FF | スタートコード |
| 0F  | パケット長 |
| F1  | パケット長のチェックサム |
| D4 40 01 60 04 CE CF CE CF CE CF FA FB FC FD   |  パケット内容 |
| FE | パケット内容のチェックサム |
| 00 | ポストアンブル |



|パケット内容|意味|
|:---:|:---:|
| D4 | ホストからPN532の命令|
| 40 | 命令:InDataExchange  |
| 01 | Tg  = 0x01  |
| 60 04 CE CF CE CF CE CF FA FB FC FD | Mifare Card DataOut|



| MifareCard DataOut|意味|
|:---:|:---:|
| 60 | Authentication A |
| 04 | address |
| CE CF CE CF CE CF | key |
| FA FB FC FD | UID number |


```


00 00 FF 05 FB D4 40 01 30 04 B7 00

```

|||
|:---:|:---:|
| 00 | プリアンブル |
| 00 FF | スタートコード |
| 05  | パケット長 |
| FB  | パケット長のチェックサム |
| D4 40 01 30 04 |  パケット内容 |
| B7 | パケット内容のチェックサム |
| 00 | ポストアンブル |



|パケット内容|意味|
|:---:|:---:|
| D4 | ホストからPN532の命令|
| 40 | 命令:InDataExchange  |
| 01 | Tg  = 0x01  |
| 30 | 16 byres reading|
| 04 | address 0x02|





やあ、鍵がマル見えですね！

## FeliCa Read without Encryption

サービスコード1A8Bのブロック 0x8000,0x8002,0x8003 を読んでみます。

```

0000E180:
0000E181: 00 00 FF 
0000E182: 09 F7 D4 4A 01 01 00 FE 00 01 00 E1 00 
0000E183.
.
.
0000E198:
0000E199: 00 00 FF 17 E9 D4 40 01 14 06 11 16 06 00 0A 1E 
0000E19A: 77 03 01 8B 1A 03 80 00 80 02 80 03 D4 00 
0000E19B:
```

これを解読してみます。

## FeliCa Polling

```
00 00 FF 09 F7 D4 4A 01 01 00 FE 00 01 00 E1 00


```

|||
|:---:|:---:|
| 00 | プリアンブル |
| 00 FF | スタートコード |
| 09  | パケット長 |
| F7  | パケット長のチェックサム |
| D4 4A 01 01 00 FE 00 01 00 |  パケット内容 |
| E1 | パケット内容のチェックサム |
| 00 | ポストアンブル |



|パケット内容|意味|
|:---:|:---:|
| D4 | ホストからPN532の命令|
| 4A | 命令:InListPassiveTarget |
| 01 | MaxTg  = 0x01  |
| 01 | BrTy = 0x01 : : 212 kbps (FeliCa polling) |
|  00 FE 00 01 00 | InitiatorData|



|InitiatorData|意味|
|:---:|:---:|
| 00 | Command Code = 0x00:Polling |
| FE 00 | System Code=0xFE00 |
| 00 | MaxTg  = 0x01  |
| 01 | Request Code = 0x01 : System Code request |
| 00 | Time Slot = 0x00: Maximum number of slots is 1 |




```

00 00 FF 17 E9 D4 40 01 14 06 11 16 06 00 0A 1E 77 03 01 8B 1A 03 80 00 80 02 80 03 D4 00
```

|||
|:---:|:---:|
| 00 | プリアンブル |
| 00 FF | スタートコード |
| 17  | パケット長 |
| E9  | パケット長のチェックサム |
| D4 40 01 14 06 11 16 06 00 0A 1E 77 03 01 8B 1A 03 80 00 80 02 80 03 |  パケット内容 |
| D4 | パケット内容のチェックサム |
| 00 | ポストアンブル |

## FeliCa read

|パケット内容|意味|
|:---:|:---:|
| D4 | ホストからPN532の命令|
| 40 | 命令:InDataExchange  |
| 01 | Tg  = 0x01  |
| 14 06 11 16 06 00 0A 1E 77 03 01 8B 1A 03 80 00 80 02 80 03 | DataOut = FeliCa Frame |


ここで、 FeliCa Frame の中身は以下のようになります。

| FeliCa DataOut|意味|
|:---:|:---:|
|14|Len|
|06|Cmd:Read without Encryption|
|11  16  06  00  0a  1e  77  03|IDm| 
|01 | Number of Service|
|8b 1A | Service Code List(1つ分)=0x1A8B |
| 03 | Number of Block=3 |
| 80 00 |Block List 1|
|80 02 |Block List 2|
|80 03 |Block List 3|


# 他の方法

今回はハードウェアに配線してデータを取り出しました。
しかしながらライブラリによっては同様の情報を取れるものがあるようです。
libnfc では、コンパイルオプションでログレベルをDEBUGにすることによって、データが取れるようになるようです。

https://github.com/nfc-tools/libnfc/blob/master/libnfc.conf.sample

また、シリアルポートを wireshark でキャプチャーする方法もあるらしいです。https://unix.stackexchange.com/questions/359500/wireshark-remote-capture-over-uart