1
0
mirror of synced 2024-11-23 21:40:58 +01:00

bpreader: add card reading

This commit is contained in:
Kevin Trocolli 2023-09-25 03:16:26 -04:00
parent f7cd4f9d28
commit b5b797377f
5 changed files with 176 additions and 171 deletions

View File

@ -12,12 +12,15 @@
#include "util/dump.h" #include "util/dump.h"
#include "util/hexstr.h" #include "util/hexstr.h"
#include "iccard/aime.h"
#include "iccard/mifare.h"
#include "board/bpreader.h" #include "board/bpreader.h"
const uint8_t BPREADER_CMD_GO_NEXT[6] = { 0x00, 0x00, 0xFF, 0x00, 0xFF, 0x00 }; const uint8_t BPREADER_CMD_GO_NEXT[6] = { 0x00, 0x00, 0xFF, 0x00, 0xFF, 0x00 };
static bool flg = true; static bool flg = true;
static bool flg_second_pass = false; static struct mifare card;
static HRESULT bp_handle_irp(struct irp *irp); static HRESULT bp_handle_irp(struct irp *irp);
static HRESULT bp_handle_irp_locked(struct irp *irp); static HRESULT bp_handle_irp_locked(struct irp *irp);
static HRESULT crack_bpreader_request(); static HRESULT crack_bpreader_request();
@ -35,7 +38,11 @@ static HRESULT bpreader_set_output_cmd();
static HRESULT bpreader_unk_08_cmd(); static HRESULT bpreader_unk_08_cmd();
static HRESULT bpreader_unk_06_cmd(); static HRESULT bpreader_unk_06_cmd();
static HRESULT bpreader_unk_0c_cmd(); static HRESULT bpreader_unk_0c_cmd();
static HRESULT bpreader_unk_44_cmd();
static HRESULT bpreader_unk_52_cmd();
static HRESULT bpreader_unk_54_cmd(); static HRESULT bpreader_unk_54_cmd();
static HRESULT bpreader_send_mifare_cmd();
static HRESULT bpreader_empty_resp();
static struct bpreader_config *config; static struct bpreader_config *config;
static struct uart bp_uart; static struct uart bp_uart;
@ -54,6 +61,7 @@ HRESULT bpreader_init(struct bpreader_config *cfg, uint16_t port)
if (cfg->port > 0) { if (cfg->port > 0) {
port = cfg->port; port = cfg->port;
} }
uint8_t sec1[16] = { 0x00, 0x02, 0x4E, 0x42, 0x47, 0x49, 0x43, 0x36, 0x3A, 0xE7, 0xB5, 0x59, 0x45, 0x64, 0x0F, 0x3D };
uart_init(&bp_uart, port); uart_init(&bp_uart, port);
bp_uart.written.bytes = bp_written_bytes; bp_uart.written.bytes = bp_written_bytes;
@ -61,6 +69,13 @@ HRESULT bpreader_init(struct bpreader_config *cfg, uint16_t port)
bp_uart.readable.bytes = bp_readable_bytes; bp_uart.readable.bytes = bp_readable_bytes;
bp_uart.readable.nbytes = sizeof(bp_readable_bytes); bp_uart.readable.nbytes = sizeof(bp_readable_bytes);
InitializeCriticalSection(&bp_lock); InitializeCriticalSection(&bp_lock);
HRESULT hr = aime_card_populate(&card, cfg->access_code_bytes, sizeof(cfg->access_code_bytes));
//memcpy_s(card.sectors[0].blocks[1].bytes, sizeof(card.sectors[0].blocks[1].bytes), sec1, sizeof(sec1));
if (FAILED(hr)) {
return hr;
}
dprintf("Reader: Init\n"); dprintf("Reader: Init\n");
return iohook_push_handler(bp_handle_irp); return iohook_push_handler(bp_handle_irp);
@ -116,121 +131,13 @@ static HRESULT bp_handle_irp_locked(struct irp *irp)
} }
else if (irp->op == IRP_OP_READ) { else if (irp->op == IRP_OP_READ) {
if (flg) { if (!read_ct) {
if (!read_ct || flg_second_pass) { hr = crack_bpreader_request();
hr = crack_bpreader_request();
}
} else { } else {
switch (bp_uart.written.bytes[3]) { // We need a delay here in order to properly
case 0x02: // "timeout" the DLL's read request.
if (!read_ct) { Sleep(1);
dprintf("Reader: Unknown 0x02\n"); }
uint8_t buff[] = { 0x00, 0x00, 0xFF, 0x05,
0xFB, 0xD5, 0x0D, 0x00, 0x06, 0x00, 0x18, 0x00 };
hr = iobuf_write(&bp_uart.readable, buff, sizeof(buff));
}
bp_uart.written.pos = 0;
break;
case 0x03:
if (!read_ct) {
dprintf("Reader: Unknown 0x03\n");
uint8_t buff[] = { 0x00, 0x00, 0xFF, 0x02,
0xFE, 0xD5, 0x19, 0x12, 0x00 };
hr = iobuf_write(&bp_uart.readable, buff, sizeof(buff));
}
bp_uart.written.pos = 0;
break;
case 0x04:
if (!read_ct && bp_uart.written.bytes[6] == 0x0E && last_cmd == 0x04) {
dprintf("Reader: Unknown second 0x04\n");
uint8_t buff[] = { 0x00, 0x00, 0xFF, 0x02,
0xFE, 0xD5, 0x0F, 0x1C, 0x00 };
hr = iobuf_write(&bp_uart.readable, buff, sizeof(buff));
}
else if (!read_ct) {
dprintf("Reader: Unknown 0x04\n");
uint8_t buff[] = { 0x00, 0x00, 0xFF, 0x02,
0xFE, 0xD5, 0x33, 0xF8, 0x00 };
hr = iobuf_write(&bp_uart.readable, buff, sizeof(buff));
}
bp_uart.written.pos = 0;
break;
case 0x06:
if (!read_ct) {
dprintf("Reader: Unknown 0x06\n");
uint8_t buff[] = { 0x00, 0x00, 0xFF, 0x02,
0xFE, 0xD5, 0x33, 0xF8, 0x00 };
hr = iobuf_write(&bp_uart.readable, buff, sizeof(buff));
}
bp_uart.written.pos = 0;
break;
case 0x05:
if (!read_ct) {
dprintf("Reader: Unknown 0x05\n");
uint8_t buff[] = { 0x00, 0x00, 0xFF, 0x03,
0xFD, 0xD5, 0x09, 0x00, 0x22, 0x00 };
hr = iobuf_write(&bp_uart.readable, buff, sizeof(buff));
}
bp_uart.written.pos = 0;
break;
case 0x0E:
if (!read_ct) {
dprintf("Reader: Unknown 0x0E\n");
uint8_t buff[] = { 0x00, 0x00, 0xff, 0x02,
0xfe, 0xd5, 0x33, 0xf8, 0x00 };
hr = iobuf_write(&bp_uart.readable, buff, sizeof(buff));
}
bp_uart.written.pos = 0;
break;
case 0x12:
if (!read_ct) {
dprintf("Reader: Unknown 0x12\n");
uint8_t buff[] = { 0x00, 0x00, 0xFF, 0x0a,
0xf6, 0xd5, 0x07, 0xff, 0x3f, 0x0e, 0xf1, 0xff, 0x3f, 0x0e, 0xf1, 0xaa, 0x00 };
hr = iobuf_write(&bp_uart.readable, buff, sizeof(buff));
}
bp_uart.written.pos = 0;
break;
case 0x14:
if (!read_ct) {
dprintf("Reader: Unknown 0x14\n");
uint8_t buff[] = { 0x00, 0x00, 0xFF, 0x20, 0xE0, 0xD5, 0xA1,
0x00, 0x1D, 0x07, // Unknown
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // IDm
0x00, 0x00, 0x01, // Unknown
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // IDm
0x00, 0x78, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Unknown
0x00, 0x00 };
hr = iobuf_write(&bp_uart.readable, buff, sizeof(buff));
}
bp_uart.written.pos = 0;
break;
case 0x18:
if (!read_ct) {
dprintf("Reader: Unknown 0x18\n");
uint8_t buff[] = { 0x00, 0x00, 0xFF, 0x0D,
0xF3, 0xD5, 0x07, 0xDC, 0xF4, 0x3F, 0x11, 0x4D, 0x85, 0x61, 0xF1, 0x26,
0x6A, 0x87, 0xC9, 0x00 };
hr = iobuf_write(&bp_uart.readable, buff, sizeof(buff));
}
bp_uart.written.pos = 0;
break;
default:
dprintf("Reader: Unknown Command %02X\n", last_cmd);
dump_iobuf(&bp_uart.written);
break;
}
}
read_ct++; read_ct++;
} }
@ -245,9 +152,7 @@ static HRESULT bp_handle_irp_locked(struct irp *irp)
} }
#endif #endif
if (!flg_second_pass) { bp_uart.written.pos = 0; // consume the written buffer
bp_uart.written.pos = 0; // consume the written buffer
}
return hr; return hr;
} }
@ -265,19 +170,19 @@ static HRESULT crack_bpreader_request() {
switch(header.cmd) { switch(header.cmd) {
case 0x06: case 0x06:
dprintf("Reader: Cmd 0x06\n"); // dprintf("Reader: Cmd 0x06\n");
return bpreader_unk_06_cmd(); return bpreader_unk_06_cmd();
case 0x08: case 0x08:
dprintf("Reader: Cmd 0x08\n"); // dprintf("Reader: Cmd 0x08\n");
return bpreader_unk_08_cmd(); return bpreader_unk_08_cmd();
case 0x12: case 0x12:
dprintf("Reader: Cmd 0x12\n"); // dprintf("Reader: Cmd 0x12\n");
return bpreader_generic_cmd(0x13); return bpreader_generic_cmd(0x13);
case 0x18: case 0x18:
dprintf("Reader: Initialize\n"); // dprintf("Reader: Initialize\n");
return bpreader_init_cmd(); return bpreader_init_cmd();
case 0x0C: case 0x0C:
@ -289,15 +194,19 @@ static HRESULT crack_bpreader_request() {
return bpreader_generic_cmd(0x0F); return bpreader_generic_cmd(0x0F);
case 0x32: case 0x32:
dprintf("Reader: Cmd 0x32\n"); // dprintf("Reader: Cmd 0x32\n");
return bpreader_generic_cmd(0x33); return bpreader_generic_cmd(0x33);
case 0x40: case 0x40:
dprintf("Reader: Read Banapass\n"); // 01 30 00 -> Chip ID; 01 30 01 -> Thing after chip ID; 01 30 02 -> Access Code; 01 60 30 -> send key a // dprintf("Reader: Read Mifare\n");
break; return bpreader_send_mifare_cmd();
case 0x44:
dprintf("Reader: Cmd 0x44\n");
return bpreader_unk_44_cmd();
case 0x4A: case 0x4A:
dprintf("Reader: Poll Card\n"); // dprintf("Reader: Poll Card\n");
return bpreader_poll_card_cmd(); return bpreader_poll_card_cmd();
case 0xA0: case 0xA0:
@ -305,8 +214,8 @@ static HRESULT crack_bpreader_request() {
break; break;
case 0x52: case 0x52:
dprintf("Reader: Cmd 0x52\n"); //dprintf("Reader: Cmd 0x52\n");
break; return bpreader_unk_52_cmd();
case 0x54: case 0x54:
dprintf("Reader: Cmd 0x54\n"); dprintf("Reader: Cmd 0x54\n");
@ -420,7 +329,7 @@ static HRESULT bpreader_poll_card_cmd()
int data_len = 1; int data_len = 1;
size_t resp_len = 0; size_t resp_len = 0;
if (GetAsyncKeyState(VK_RETURN) & 0x8000) { if (GetAsyncKeyState(config->insert_card) & 0x8000) {
dprintf("Reader: Touch card %ls\n", config->access_code); dprintf("Reader: Touch card %ls\n", config->access_code);
data.card_present = 0x01; data.card_present = 0x01;
@ -429,12 +338,18 @@ static HRESULT bpreader_poll_card_cmd()
data.atqa[1] = 0x04; data.atqa[1] = 0x04;
data.sak = 0x08; data.sak = 0x08;
data.unknown5 = 0x04; data.unknown5 = 0x04;
memcpy_s(data.serial_number, sizeof(data.serial_number), config->chip_id_bytes, 4); memcpy_s(data.serial_number, sizeof(data.serial_number), config->access_code_bytes, 4);
data_len = sizeof(data); data_len = sizeof(data);
resp_len = build_bpreader_response((uint8_t *)&data, data_len, &header, buff, sizeof(buff));
} }
else {
resp_len = build_bpreader_response((uint8_t *)&data, data_len, &header, buff, sizeof(buff)); data_len = 3;
uint8_t empty_buff[1] = {0};
resp_len = build_bpreader_response((uint8_t *)&empty_buff, data_len, &header, buff, sizeof(buff));
}
if (resp_len > 0) { if (resp_len > 0) {
HRESULT hr = iobuf_write(&bp_uart.readable, buff, resp_len); HRESULT hr = iobuf_write(&bp_uart.readable, buff, resp_len);
return hr; return hr;
@ -471,14 +386,14 @@ static HRESULT bpreader_unk_54_cmd()
uint8_t bfr_data[1] = { 0x00 }; uint8_t bfr_data[1] = { 0x00 };
size_t resp_len = 0; size_t resp_len = 0;
resp_len = build_bpreader_response(bfr_data, sizeof(bfr_data), &header, buff, sizeof(buff)); resp_len = build_bpreader_response(bfr_data, 1, &header, buff, sizeof(buff));
if (resp_len > 0) { if (resp_len > 0) {
HRESULT hr = iobuf_write(&bp_uart.readable, buff, resp_len); HRESULT hr = iobuf_write(&bp_uart.readable, buff, resp_len);
return hr; return hr;
} else { } else {
dprintf("Reader: No data to return in bpreader_set_output_cmd!\n"); dprintf("Reader: No data to return in bpreader_unk_54_cmd!\n");
return E_FAIL; return E_FAIL;
} }
} }
@ -532,3 +447,90 @@ static HRESULT bpreader_unk_0c_cmd()
return E_FAIL; return E_FAIL;
} }
} }
static HRESULT bpreader_send_mifare_cmd()
{
struct bpreader_read_mifare_req req = { bp_uart.written.bytes[7], bp_uart.written.bytes[8], bp_uart.written.bytes[9] };
struct bpreader_cmd_header header = { 0x00, 0x00, 0xFF, 0x03, 0xFD, 0xD5, 0x41 };
uint8_t data[17] = { 0x00 };
uint8_t buff[26] = { 0x00 };
size_t resp_len = 0;
switch (req.subcmd) {
case MIFARE_CMD_READ:
struct bpreader_read_mifare_resp_data resp;
if (req.block > 3) {
dprintf("Reader: MIFARE block number %d is out of range!\n", req.block);
return E_FAIL;
}
dprintf("Reader: Mifare Read Block 0x%02X\n", req.block);
resp.padding = 0;
memcpy_s(resp.block, sizeof(resp.block), card.sectors[0].blocks[req.block].bytes, sizeof(card.sectors[0].blocks[req.block].bytes));
header.data_len = 0x13;
resp_len = build_bpreader_response((uint8_t *)&resp, 0x11, &header, buff, sizeof(buff));
break;
case MIFARE_CMD_AUTH_KEY_A:
dprintf("Reader: Mifare Authentication with Key A\n");
return iobuf_write(&bp_uart.readable, BPREADER_CMD_GO_NEXT, sizeof(BPREADER_CMD_GO_NEXT));
break;
case MIFARE_CMD_AUTH_KEY_B:
dprintf("Reader: Mifare Authentication with Key B\n");
resp_len = build_bpreader_response(data, 1, &header, buff, sizeof(buff));
break;
default:
dprintf("Reader: Unknown Mifare Command 0x%02x\n", req.subcmd);
return E_FAIL;
}
if (resp_len > 0) {
return iobuf_write(&bp_uart.readable, buff, resp_len);
} else {
dprintf("Reader: No data to return in bpreader_send_mifare_cmd!\n");
return E_FAIL;
}
}
static HRESULT bpreader_unk_52_cmd()
{
struct bpreader_cmd_header header = { 0x00, 0x00, 0xFF, 0x04, 0xFC, 0xD5, 0x53 };
uint8_t buff[10] = { 0x00 };
uint8_t bfr_data[2] = { 0x01, 0x00 };
size_t resp_len = 0;
resp_len = build_bpreader_response(bfr_data, 2, &header, buff, sizeof(buff));
if (resp_len > 0) {
HRESULT hr = iobuf_write(&bp_uart.readable, buff, resp_len);
return hr;
} else {
dprintf("Reader: No data to return in bpreader_unk_52_cmd!\n");
return E_FAIL;
}
}
static HRESULT bpreader_unk_44_cmd()
{
struct bpreader_cmd_header header = { 0x00, 0x00, 0xFF, 0x04, 0xFC, 0xD5, 0x45 };
uint8_t buff[10] = { 0x00 };
uint8_t bfr_data[2] = { 0x01, 0x00 };
size_t resp_len = 0;
resp_len = build_bpreader_response(bfr_data, 2, &header, buff, sizeof(buff));
if (resp_len > 0) {
HRESULT hr = iobuf_write(&bp_uart.readable, buff, resp_len);
return hr;
} else {
dprintf("Reader: No data to return in bpreader_unk_44_cmd!\n");
return E_FAIL;
}
}

View File

@ -6,46 +6,31 @@
struct bpreader_config { struct bpreader_config {
bool enable; bool enable;
uint16_t port; uint16_t port;
uint8_t insert_card;
wchar_t access_code[21]; wchar_t access_code[21];
wchar_t chip_id[33];
uint8_t access_code_bytes[10]; uint8_t access_code_bytes[10];
uint8_t chip_id_bytes[16];
}; };
HRESULT bpreader_init(struct bpreader_config *cfg, uint16_t port); HRESULT bpreader_init(struct bpreader_config *cfg, uint16_t port);
/* /*
bpreader packet format WIP bpreader packet format WIP
n = final offset maybe rcs956?
Response packet
| Offset | Meaning | | Offset | Meaning |
|--------|--------------------------------------------------------------------| |--------|--------------------------------------------------------------------|
| 0 | Always 00 | | 0 | Preamble, Always 00 |
| 1 | Always 00 | | 1 | Packet start, Always 00 |
| 2 | Always FF | Header | 2 | Always FF | Header
| 3 | len(Data), 0 means no data | | 3 | len(Data), 0 means no data |
| 4 | FF - ((sum of header bytes) & FF) if data is present | | 4 | (0x100 - data_len) & FF if data is present |
|--------|--------------------------------------------------------------------| |--------|--------------------------------------------------------------------|
| 5 | Always D5 (resp) or D4 (req) if data is present, identifier? | | 5 | Always 0xD5 (success) or 0x7F (failure) |
| 6 | Command if data is present | Data | 6 | Command if data is present | Data
| 7..n-2 | Data if data is present | | 7..n-2 | Data if data is present |
|--------|--------------------------------------------------------------------| |--------|--------------------------------------------------------------------|
| n-1 | FF - ((sum of head + data bytes) & FF) if data is present, else FF | Footer | n-1 | (FF - sum of data bytes) & FF if data is present, else FF | Footer
| n | Always 00 | | n | Postamble, Always 00 |
Commands
| Command | Response | Meaning | Response Data (not including leading 0xD5) |
|---------|----------|----------------------------------------------------|------------------------------------------------------------------------|
| 0x00 | 0x00 | Wait Next Command | None |
| 0x02 | 0x05 | Unknown | 0x0D, 0x00, 0x06, 0x00 |
| 0x03 | 0x02 | Unknown, first command and then sent randomly | 0x19 |
| 0x04 | 0x02 | Unknown, only command seen to change it's req data | 0x0F/0x33 |
| 0x05 | 0x03 | Unknown | 0x09, 0x00 |
| 0x06 | 0x02 | Unknown | 0x0F |
| 0x09 | 0x18 | Poll for card | 0x4B, 0x01, 0x01, 0x14, 0x01, [IDm], [PMm], [Sys Code] |
| 0x0E | 0x02 | Unknown | 0x33 |
| 0x12 | 0x0A | Unknown | 0x07, 0xFF, 0x3F, 0x0E, 0xF1, 0xFF, 0x3F, 0x0E, 0xF1 |
| 0x14 | 0x20 | Unknown | a lot, see bpreader.c |
| 0x18 | 0x0D | Unknown | 0x07, 0xDC, 0xF4, 0x3F, 0x11, 0x4D, 0x85, 0x61, 0xF1, 0x26, 0x6A, 0x87 |
*/ */
#pragma pack(push, 1) #pragma pack(push, 1)
@ -64,6 +49,17 @@ struct bpreader_cmd_footer {
uint8_t padding; uint8_t padding;
}; };
struct bpreader_read_mifare_req {
uint8_t unk0;
uint8_t subcmd; // 0x30 for read, 0x60/61 for write?
uint8_t block;
};
struct bpreader_read_mifare_resp_data {
uint8_t padding;
uint8_t block[16];
};
struct bpreader_poll_banapass_data { struct bpreader_poll_banapass_data {
uint8_t card_present; uint8_t card_present;
uint8_t unknown1; // 0x01 uint8_t unknown1; // 0x01

View File

@ -17,6 +17,7 @@ void bpreader_config_load(struct bpreader_config *cfg, const wchar_t *filename)
cfg->enable = GetPrivateProfileIntW(L"reader", L"enable", 1, filename); cfg->enable = GetPrivateProfileIntW(L"reader", L"enable", 1, filename);
cfg->port = GetPrivateProfileIntW(L"reader", L"port", 0, filename); cfg->port = GetPrivateProfileIntW(L"reader", L"port", 0, filename);
cfg->insert_card = GetPrivateProfileIntW(L"reader", L"insert_card", VK_RETURN, filename);
GetPrivateProfileStringW( GetPrivateProfileStringW(
L"reader", L"reader",
L"access_code", L"access_code",
@ -24,23 +25,11 @@ void bpreader_config_load(struct bpreader_config *cfg, const wchar_t *filename)
cfg->access_code, cfg->access_code,
_countof(cfg->access_code), _countof(cfg->access_code),
filename); filename);
GetPrivateProfileStringW(
L"reader",
L"chip_id",
L"00000000000000000000000000000000",
cfg->chip_id,
_countof(cfg->chip_id),
filename);
HRESULT hr = wstr_to_bytes(cfg->access_code, 20, cfg->access_code_bytes, sizeof(cfg->access_code_bytes)); HRESULT hr = wstr_to_bytes(cfg->access_code, 20, cfg->access_code_bytes, sizeof(cfg->access_code_bytes));
if (FAILED(hr)) { if (FAILED(hr)) {
dprintf("Reader: wstr_to_bytes 1 failed! 0x%lX", hr); dprintf("Reader: wstr_to_bytes 1 failed! 0x%lX", hr);
} }
hr = wstr_to_bytes(cfg->chip_id, 32, cfg->chip_id_bytes, sizeof(cfg->chip_id_bytes));
if (FAILED(hr)) {
dprintf("Reader: wstr_to_bytes 2 failed! 0x%lX", hr);
}
} }
void usio_config_load(struct usio_config *cfg, const wchar_t *filename) void usio_config_load(struct usio_config *cfg, const wchar_t *filename)

View File

@ -41,6 +41,11 @@ HRESULT aime_card_populate(
mifare->sectors[0].blocks[2].bytes[6 + i] = b; mifare->sectors[0].blocks[2].bytes[6 + i] = b;
} }
// Set the card ID, nothing else matters in the first block
mifare->sectors[0].blocks[0].bytes[0] = luid[0];
mifare->sectors[0].blocks[0].bytes[1] = luid[1];
mifare->sectors[0].blocks[0].bytes[2] = luid[2];
mifare->sectors[0].blocks[0].bytes[3] = luid[3];
sprintf_s(accessCode, sizeof accessCode, "%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x", sprintf_s(accessCode, sizeof accessCode, "%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x",
luid[0], luid[1], luid[2], luid[3], luid[4], luid[5], luid[6], luid[7], luid[8], luid[9]); luid[0], luid[1], luid[2], luid[3], luid[4], luid[5], luid[6], luid[7], luid[8], luid[9]);

View File

@ -2,6 +2,19 @@
#include <stdint.h> #include <stdint.h>
enum mifare_cmd {
MIFARE_CMD_AUTH_KEY_A = 0x60,
MIFARE_CMD_AUTH_KEY_B = 0x61,
MIFARE_CMD_PERSONALIZE_UID = 0x40,
MIFARE_CMD_SET_MOD_TYPE = 0x43,
MIFARE_CMD_READ = 0x30,
MIFARE_CMD_WRITE = 0xA0,
MIFARE_CMD_DECREMENT = 0xC0,
MIFARE_CMD_INCREMENT = 0xC1,
MIFARE_CMD_RESTORE = 0xC2,
MIFARE_CMD_TRANSFER = 0xB0,
};
struct mifare_block { struct mifare_block {
uint8_t bytes[16]; uint8_t bytes[16];
}; };