From 9810b51c1335a19506437416ae200cee2a33398b Mon Sep 17 00:00:00 2001 From: Sucareto <28331534+Sucareto@users.noreply.github.com> Date: Mon, 15 Mar 2021 22:48:12 +0800 Subject: [PATCH] Add files via upload --- Arduino-Chunithm-Reader.ino | 119 ++++++++++++++++++ card.txt | 14 +++ chunihook.dll | Bin 0 -> 569344 bytes cmd.h | 239 +++++++++++++++++++++++++++++++++++ nfc.txt | 244 ++++++++++++++++++++++++++++++++++++ sg-cmd.c | 144 +++++++++++++++++++++ 6 files changed, 760 insertions(+) create mode 100644 Arduino-Chunithm-Reader.ino create mode 100644 card.txt create mode 100644 chunihook.dll create mode 100644 cmd.h create mode 100644 nfc.txt create mode 100644 sg-cmd.c diff --git a/Arduino-Chunithm-Reader.ino b/Arduino-Chunithm-Reader.ino new file mode 100644 index 0000000..647f96f --- /dev/null +++ b/Arduino-Chunithm-Reader.ino @@ -0,0 +1,119 @@ +#include "cmd.h" + +void SerialCheck() { + switch (packet_read()) { + case SG_NFC_CMD_RESET: + sg_nfc_cmd_reset(); + break; + case SG_NFC_CMD_GET_FW_VERSION: + sg_nfc_cmd_get_fw_version(); + break; + case SG_NFC_CMD_GET_HW_VERSION: + sg_nfc_cmd_get_hw_version(); + break; + case SG_NFC_CMD_POLL: + sg_nfc_cmd_poll(); + break; + case SG_NFC_CMD_MIFARE_READ_BLOCK: + sg_nfc_cmd_mifare_read_block(); + break; + case SG_NFC_CMD_FELICA_ENCAP: + sg_nfc_cmd_felica_encap(); + break; + case SG_NFC_CMD_MIFARE_AUTHENTICATE: + sg_res_init(); + break; + case SG_NFC_CMD_MIFARE_SELECT_TAG: + sg_res_init(); + break; + case SG_NFC_CMD_MIFARE_SET_KEY_AIME: + sg_nfc_cmd_mifare_set_key_aime(); + break; + case SG_NFC_CMD_MIFARE_SET_KEY_BANA: + sg_res_init(); + break; + case SG_NFC_CMD_RADIO_ON: + sg_nfc_cmd_radio_on(); + break; + case SG_NFC_CMD_RADIO_OFF: + sg_nfc_cmd_radio_off(); + break; + case SG_RGB_CMD_RESET: + sg_led_cmd_reset(); + break; + case SG_RGB_CMD_GET_INFO: + sg_led_cmd_get_info(); + break; + case SG_RGB_CMD_SET_COLOR: + sg_led_cmd_set_color(); + break; + } +} + +void setup() { + SerialUSB.begin(38400); + SerialUSB.setTimeout(0); + FastLED.addLeds(leds, NUM_LEDS); + nfc.begin(); + nfc.SAMConfig(); + memset(&req, 0, sizeof(req.bytes)); + memset(&res, 0, sizeof(res.bytes)); +} + +void loop() { + SerialCheck(); + packet_write(); +} + +static uint8_t packet_read() { + uint8_t len, r, checksum; + bool escape = false; + while (SerialUSB.available()) { + r = SerialUSB.read(); + if (r == 0xE0) { + req.frame_len = 0xFF; + continue; + } + if (req.frame_len == 0xFF) { + req.frame_len = r; + len = 1; + checksum = r; + continue; + } + if (r == 0xD0) { + escape = true; + continue; + } + if (escape) { + r++; + escape = false; + } + if (len == req.frame_len && checksum == r) { + return req.cmd; + } + req.bytes[len++] = r; + checksum += r; + } + return 0; +} + +static void packet_write() { + if (res.cmd == 0) + return; + uint8_t checksum = 0; + SerialUSB.write(0xE0); + for (uint8_t i = 0; i < res.frame_len; i++) { + uint8_t w = res.bytes[i]; + checksum += w; + if (SerialUSB.availableForWrite() < 2) + return; + if (w == 0xE0 || w == 0xD0) { + SerialUSB.write(0xD0); + SerialUSB.write(--w); + } else { + SerialUSB.write(w); + } + } + SerialUSB.write(checksum); + res.cmd = 0; +} diff --git a/card.txt b/card.txt new file mode 100644 index 0000000..4c52079 --- /dev/null +++ b/card.txt @@ -0,0 +1,14 @@ +E0 [05] 00 [7D] [42] 00 [C4] //读卡 +E0 [19] 00 [7D] [42] 00 13 01 20 10 [ 8 byte IDm ] [ 8 byte PMm ] [ ] + + +E0 [13] 00 [80] [71] 0E [ 8 byte IDm ] 06 [00] FF FF 01 0F [ ] +E0 [1A] 00 [80] [71] 00 [14] 14 01 [ 8 byte IDm ] [ 8 byte PMm ] 00 00 [ ] + + +E0 [17] 00 [81] [71] 12 [ 8 byte IDm ] 0A [0C] [ 8 byte IDm ] [ ] +E0 [13] 00 [81] [71] 00 [0D] 0D 0D [ 8 byte IDm ] 01 00 00 [ ] + + +E0 [18] 00 [82] [71] 13 [ 8 byte IDm ] 0B [A4] [ 8 byte IDm ] 00 [ ] +E0 [11] 00 [82] [71] 00 [0B] 0B A5 [ 8 byte IDm ] 00 [ ] diff --git a/chunihook.dll b/chunihook.dll new file mode 100644 index 0000000000000000000000000000000000000000..6a0b29e418c6752ecf4c393c31a59f5ce9500911 GIT binary patch literal 569344 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�{X|jEWSie)cQWN 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 EKOM>ZWdHyG literal 0 HcmV?d00001 diff --git a/cmd.h b/cmd.h new file mode 100644 index 0000000..d6066a3 --- /dev/null +++ b/cmd.h @@ -0,0 +1,239 @@ +#include "FastLED.h" +#define NUM_LEDS 6 +#define DATA_PIN A3 +#define BRI 50 +CRGB leds[NUM_LEDS]; + +#include "Adafruit_PN532.h" +#define PN532_IRQ (4) +Adafruit_PN532 nfc(PN532_IRQ, 16); +boolean readerDisabled = false; + +uint8_t AimeKey[6]; +uint8_t MifareKey[6] = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}; + +enum { + SG_NFC_CMD_GET_FW_VERSION = 0x30,//获取FW版本 + SG_NFC_CMD_GET_HW_VERSION = 0x32,//获取HW版本 + SG_NFC_CMD_RADIO_ON = 0x40,//打开读卡器天线 + SG_NFC_CMD_RADIO_OFF = 0x41,//关闭读卡器天线 + SG_NFC_CMD_POLL = 0x42,//发送卡号 + SG_NFC_CMD_MIFARE_SELECT_TAG = 0x43, + SG_NFC_CMD_MIFARE_SET_KEY_BANA = 0x50, + SG_NFC_CMD_MIFARE_READ_BLOCK = 0x52, + SG_NFC_CMD_MIFARE_SET_KEY_AIME = 0x54, + SG_NFC_CMD_MIFARE_AUTHENTICATE = 0x55, + SG_NFC_CMD_RESET = 0x62,//重置读卡器 + SG_NFC_CMD_FELICA_ENCAP = 0x71, + SG_RGB_CMD_SET_COLOR = 0x81,//LED颜色设置 + SG_RGB_CMD_RESET = 0xF5,//LED重置 + SG_RGB_CMD_GET_INFO = 0xF0,//LED信息获取 + FELICA_CMD_POLL = 0x00,//ENCAP用 + FELICA_CMD_GET_SYSTEM_CODE = 0x0c, + FELICA_CMD_NDA_A4 = 0xa4, +}; + +typedef union packet_req { + uint8_t bytes[256]; + struct { + uint8_t frame_len; + uint8_t addr; + uint8_t seq_no; + uint8_t cmd; + uint8_t payload_len; + union { + uint8_t key[6];// sg_nfc_req_mifare_set_key + struct {// sg_nfc_cmd_mifare_read_block + uint8_t uid[4]; + uint8_t block_no; + }; + struct {// sg_nfc_req_felica_encap + uint8_t IDm[8]; + uint8_t encap_len; + uint8_t code; + uint8_t felica_payload[241]; + }; + uint8_t color_payload[3];//sg_led_req_set_color + }; + }; + +} packet_req_t; + +typedef union packet_res { + uint8_t bytes[256]; + struct { + uint8_t frame_len; + uint8_t addr; + uint8_t seq_no; + uint8_t cmd; + uint8_t status; + uint8_t payload_len; + union { + char version[23];// sg_nfc_res_get_fw_version,sg_nfc_res_get_hw_version + struct {// sg_nfc_res_poll + uint8_t count; + uint8_t type; + uint8_t id_len; + union { + uint8_t uid[4]; + uint8_t IDPM[16]; + }; + }; + uint8_t block[16];// sg_nfc_res_mifare_read_block + struct {// sg_nfc_res_felica_encap + uint8_t encap_len; + uint8_t code; + uint8_t IDm[8]; + union { + struct { + uint8_t PMm[8]; + uint8_t system_code[2]; + }; + uint8_t felica_payload[241]; + }; + }; + uint8_t reset_payload; //sg_led_res_reset + uint8_t info_payload[9]; //sg_led_res_get_info + }; + }; +} packet_res_t; + +static packet_req_t req; +static packet_res_t res; + +static void sg_res_init(uint8_t payload_len = 0) { //初始化模板 + res.frame_len = 6 + payload_len; + res.addr = req.addr; + res.seq_no = req.seq_no; + res.cmd = req.cmd; + res.status = 0; + res.payload_len = payload_len; +} + +static void sg_nfc_cmd_reset() {//重置读卡器 + sg_res_init(); + res.status = 3; + readerDisabled = true; +} + +static void sg_nfc_cmd_get_fw_version() { + sg_res_init(sizeof(res.version)); + memcpy(res.version, "TN32MSEC003S F/W Ver1.2E", sizeof(res.version)); +} + +static void sg_nfc_cmd_get_hw_version() { + sg_res_init(sizeof(res.version)); + memcpy(res.version, "TN32MSEC003S H/W Ver3.0J", sizeof(res.version)); +} + +static void sg_nfc_cmd_mifare_set_key_aime() { + sg_res_init(); + memcpy(req.key, AimeKey, sizeof(AimeKey)); +} + +static void sg_led_cmd_reset() { + sg_res_init(sizeof(res.reset_payload)); + res.reset_payload = 0; + fill_solid(leds, NUM_LEDS, 0x000000); + FastLED[0].show(leds, NUM_LEDS, BRI); +} + +static void sg_led_cmd_get_info() { + sg_res_init(sizeof(res.info_payload)); + static uint8_t info[] = {'1', '5', '0', '8', '4', 0xFF, 0x10, 0x00, 0x12}; + memcpy(res.info_payload, info, 9); +} + +static void sg_led_cmd_set_color() { + uint8_t r = req.color_payload[0]; + uint8_t g = req.color_payload[1]; + uint8_t b = req.color_payload[2]; + fill_solid(leds, NUM_LEDS, CRGB(r, g, b)); + FastLED[0].show(leds, NUM_LEDS, BRI); +} + +static void sg_nfc_cmd_radio_on() { + sg_res_init(); + if (!readerDisabled) { + return; + } + nfc.startPassiveTargetIDDetection(PN532_MIFARE_ISO14443A); + readerDisabled = false; +} + +static void sg_nfc_cmd_radio_off() { + sg_res_init(); + readerDisabled = true; +} + +static void sg_nfc_cmd_poll() { //卡号发送 + uint8_t uid[4]; + uint8_t t; + //读aime(实验性) + nfc.startPassiveTargetIDDetection(PN532_MIFARE_ISO14443A); + delay(10); + if (!digitalRead(PN532_IRQ)) { + nfc.readDetectedPassiveTargetID(uid, &t); + if (nfc.mifareclassic_AuthenticateBlock(uid, t, 1, MIFARE_CMD_AUTH_B, AimeKey)) { //aime + res.type = 0x10; + res.id_len = t; + memcpy(res.uid, uid, t);//默认ID为0x01020304 + sg_res_init(7); + res.count = 1; + return; + } + } + //读普通mifare当作felica + nfc.startPassiveTargetIDDetection(PN532_MIFARE_ISO14443A); + delay(10); + if (!digitalRead(PN532_IRQ)) { + nfc.readDetectedPassiveTargetID(uid, &t); + if (nfc.mifareclassic_AuthenticateBlock(uid, t, 1, MIFARE_CMD_AUTH_A, MifareKey)) { + if (nfc.mifareclassic_ReadDataBlock(1, res.IDPM)) {//此处略过了IDm和PMm的分别读取 + res.type = 0x20; + res.id_len = sizeof(res.IDPM); + sg_res_init(19);//count,type,id_len,IDm,PMm + res.count = 1; + return; + } + } + } + sg_res_init(1); + res.count = 0; +} + +static void sg_nfc_cmd_mifare_read_block() { + if (req.block_no > 3) + return; + sg_res_init(sizeof(res.block)); + if (nfc.mifareclassic_AuthenticateBlock(req.uid, 4, req.block_no, MIFARE_CMD_AUTH_B, AimeKey)) { + if (nfc.mifareclassic_ReadDataBlock(req.block_no, res.block)) { + return; + } + } + sg_res_init(); +} + +static void sg_nfc_cmd_felica_encap() { + uint8_t code = req.code; + res.code = code + 1; + memcpy(res.IDm, req.IDm, sizeof(req.IDm)); + switch (code) { + case FELICA_CMD_POLL: + sg_res_init(0x14); + res.system_code[0] = 0x00; + res.system_code[1] = 0x00; + break; + case FELICA_CMD_GET_SYSTEM_CODE: + sg_res_init(0x0D); + res.felica_payload[0] = 0x01; + res.felica_payload[1] = 0x00; + res.felica_payload[2] = 0x00; + break; + case FELICA_CMD_NDA_A4: + sg_res_init(0x0B); + res.felica_payload[0] = 0x00; + break; + } + res.encap_len = res.payload_len; +} diff --git a/nfc.txt b/nfc.txt new file mode 100644 index 0000000..3e47c40 --- /dev/null +++ b/nfc.txt @@ -0,0 +1,244 @@ +N.B. Quoted strings are NOT NUL-terminated unless otherwise noted. +Useful reading: https://www.nxp.com/docs/en/data-sheet/MF1S50YYX_V1.pdf + +(AiMe branded cards are Mifare Classic cards. Other technologies exist though) + +Summary +------- + +Hardware: + Assembly connector: + 5V Host in + Tx;Rx;GND Host RS232 in + Tx;Rx;GND Daisy-chain out + Main board (probably LED controller): + Silk "837-15084" + CN1: Host 5V power in, ?V NFC-sub power out + CN2: Host RS232 Tx;Rx;GND in, NFC-sub Tx;Rx out + CN3: LED-Sub power and data(?) out + DIPSW1: Set to hex nibble 8. + Contains ADM3222 RS232 transceiver IC + Contains ATMega32 MCU + NFC subboard: + Sticker: Model "TN32MSEC003S" + CN1: ?V power and Tx;Rx;GND in, Tx;Rx;GND ass'y CN daisy out. + DIPSW1: Set to hex nibble 0. + Contains ATmega168 MCU + Contains ADM3202A RS232 transceiver IC + Contains shielded RF circuit + Entire non-ground-plane PCB area is visible through the chassis + torx screws lol + LED subboard: + Silk: "837-15120" + CN1: ?V power and Tx;Rx;GND in. + Five RGB LEDs and a bunch of resistors + No visible logic ICs..? + No DIPSW. + +JVS framing: + E0 sync + D0 escape (+1 to unescape) + Checksum is sum of bytes after unescaping + +Frame header: + Frame length (including length byte itself) + Address + Sequence no, hopefully loops before hitting esc byte... + Command byte + +Bus addressing: + Low nibble set using DIPSWs + High nibble ??? + Daisy chaining mechanism unknown (RS232 wires probably multi-tap) + +Startup +------- + +Addr 00 Command 62: + Req: + 00 Payload length + Resp: + 00 Status byte + 00 Payload length + Description: + Unknown. Reset? + +Addr 00 Command 30: + Req: + 00 Payload length + Resp: + 00 Status byte + 17 Payload length + .. "TN32MSEC003S F/W Ver1.2E" + Description: + Get firmware version + +Addr 00 Command 32: + Req: + 00 Payload length + Resp: + 00 Status byte + 17 Payload length + .. "TN32MSEC003S H/W Ver3.0J" + Description: + Get hardware version + +Addr 08 Command f5: + Req: + 00 Payload length + Resp: + 00 Status byte + 00 Payload length + Description: + LED sub-board reset. + Won't accept LED commands until you do this. + +Addr 08 Command f0: + Req: + 00 Payload length + Resp: + 00 Status byte + 09 Payload length + .. "15084" (part nr for LED board) + FF ?? + 11 ?? + 00 ?? + 12 ?? + Description: + Get board "info" + +Addr 00 Command 54: + Req: + 06 Payload length + 57 'W' + 43 'C' + 43 'C' + 46 'F' + 76 'v' + 32 '2' + Resp: + 00 Status byte? + 00 ?? + Description: + Set Mifare KeyA. + "WCCF" might refer this this SEGA arcade game: + https://en.wikipedia.org/wiki/World_Club_Champion_Football + It's quite old and has AiMe readers, maybe where they first appeared? + +Addr 00 Command 50: + Req: + 06 Payload length + 60 ?? + 90 ?? + D0 ?? (This is escaped of course) + 06 ?? + 32 ?? + F5 ?? + Resp: + 00 Status byte + 00 Payload length + Description: + Possibly Mifare KeyB. + +Polling +------- + +Addr 00 Command 40: + Req: + 01 Payload length + 03 ?? + Resp: + 00 Status byte + 00 Payload length + Description: + Poll some other NFC technology? + +Addr 00 Command 42: + Req: + 00 Payload length + Resp if no MiFare card: + 00 Status byte + 01 Payload length + 00 (represents nothing i guess) + Resp if MiFare card: + 00 Status byte? + 07 Payload length + 01 Chunk length? + 10 ?? Block size maybe? + 04 Chunk length? + .. Mifare UID, four bytes. + Description: + Check for Mifare card presence? + +Addr 00 Command 41: + Req: + 00 Payload length + Resp: + 00 Status byte + 00 Payload length + Description: + Unknown. Poll some other NFC technology? + +Card read +--------- + +Addr 00 Command 43: + Req: + 04 Payload length + .. Mifare UID, four bytes. + Resp: + 00 Status byte + 00 Payload length + Description: + Select MiFare by UID? + +Addr 00 Command 55: + Req: + 05 Payload length + .. Mifare UID, four bytes. + 03 ?? + Resp: + 00 Status byte + 00 Payload length + Description: + Unknown. + Block 3 on a Mifare sector contains keys and an access control list. + It is generally not accessed directly (unless being provisioned?) + +Addr 00 Command 52: + Req: + 05 Payload length + .. Mifare UID, four bytes. + .. Block number, 1 or 2. + Resp for Block 1: + 00 Status byte + 10 Payload length (1 block) + .. "SBSD" + 00 00 00 00 + 00 00 00 00 + 00 4E C6 22 + Resp for Block 2: + 00 Status byte + 10 Payload length (1 block) + .. 00 00 00 00 00 00 xx xx + xx xx xx xx xx xx xx xx + Description: + Probably reads blocks 1 and 2 from Mifare sector 0. + Block 0 contains the "vendor information" and UID. + Block 1 contents are unknown, probably AiMe DB info. + Block 2 last 10 bytes hex are printed on the card ("local unique id"). + (Block 3 contains encryption keys so is not allowed to be read) + +LED +--- + +Addr 08 Command 81: + Req: + 03 Payload length + ff Red intensity + ff Green intensity + ff Blue intensity + Resp: + None! Command is not acknowledged + Description: + Set LED color diff --git a/sg-cmd.c b/sg-cmd.c new file mode 100644 index 0000000..84d00f5 --- /dev/null +++ b/sg-cmd.c @@ -0,0 +1,144 @@ +#include + +#include "board/sg-cmd.h" +#include "board/sg-frame.h" + +#include "hook/iobuf.h" + +#include "util/dprintf.h" + +union sg_req_any { + struct sg_req_header req; + uint8_t bytes[256]; +}; + +union sg_res_any { + struct sg_res_header res; + uint8_t bytes[256]; +}; + +static HRESULT sg_req_validate(const void *ptr, size_t nbytes); + +static void sg_res_error( + struct sg_res_header *res, + const struct sg_req_header *req); + +static HRESULT sg_req_validate(const void *ptr, size_t nbytes) +{ + const struct sg_req_header *req; + size_t payload_len; + + assert(ptr != NULL); + + if (nbytes < sizeof(*req)) { + dprintf("SG Cmd: Request header truncated\n"); + + return E_FAIL; + } + + req = ptr; + + if (req->hdr.frame_len != nbytes) { + dprintf("SG Cmd: Frame length mismatch: got %i exp %i\n", + req->hdr.frame_len, + (int) nbytes); + + return E_FAIL; + } + + payload_len = req->hdr.frame_len - sizeof(*req); + + if (req->payload_len != payload_len) { + dprintf("SG Cmd: Payload length mismatch: got %i exp %i\n", + req->payload_len, + (int) payload_len); + + return E_FAIL; + } + + return S_OK; +} + +void sg_req_transact( + struct iobuf *res_frame, + const uint8_t *req_bytes, + size_t req_nbytes, + sg_dispatch_fn_t dispatch, + void *ctx) +{ + printf("req: "); + for (uint8_t i = 0; i < req_nbytes; i++) + printf("%02X ", req_bytes[i]); + printf("\n"); + + struct iobuf req_span; + union sg_req_any req; + union sg_res_any res; + HRESULT hr; + + assert(res_frame != NULL); + assert(req_bytes != NULL); + assert(dispatch != NULL); + + req_span.bytes = req.bytes; + req_span.nbytes = sizeof(req.bytes); + req_span.pos = 0; + + hr = sg_frame_decode(&req_span, req_bytes, req_nbytes); + + if (FAILED(hr)) { + return; + } + + hr = sg_req_validate(req.bytes, req_span.pos); + + if (FAILED(hr)) { + return; + } + + hr = dispatch(ctx, &req, &res); + + if (hr != S_FALSE) { + if (FAILED(hr)) { + sg_res_error(&res.res, &req.req); + } + + sg_frame_encode(res_frame, res.bytes, res.res.hdr.frame_len); + + printf("res: "); + for (uint8_t i = 0; i < res_frame->pos; i++) + printf("%02X ", res_frame->bytes[i]); + printf("\n"); + } +} + +void sg_res_init( + struct sg_res_header *res, + const struct sg_req_header *req, + size_t payload_len) +{ + assert(res != NULL); + assert(req != NULL); + + res->hdr.frame_len = sizeof(*res) + payload_len; + res->hdr.addr = req->hdr.addr; + res->hdr.seq_no = req->hdr.seq_no; + res->hdr.cmd = req->hdr.cmd; + res->status = 0; + res->payload_len = payload_len; +} + +static void sg_res_error( + struct sg_res_header *res, + const struct sg_req_header *req) +{ + assert(res != NULL); + assert(req != NULL); + + res->hdr.frame_len = sizeof(*res); + res->hdr.addr = req->hdr.addr; + res->hdr.seq_no = req->hdr.seq_no; + res->hdr.cmd = req->hdr.cmd; + res->status = 1; + res->payload_len = 0; +}