The firmware fork now provides CMD_LED_CONTROL at 0x011B (payload_led_control_t). hw.led already packs the matching <BBBHH> struct + action codes, so repointing the id makes it functional. Adds Cmd.SET_HF_FIELD_TIMEOUT (0x011A, upstream's use of that slot). README caveat updated: hw.led works with the fork, no-op on stock. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
300 lines
7.5 KiB
Python
300 lines
7.5 KiB
Python
"""Proxmark3 NG wire protocol constants and CRC."""
|
|
import struct
|
|
from enum import IntEnum
|
|
|
|
# --- Magic values ---
|
|
CMD_PREAMBLE_MAGIC = 0x61334d50 # "PM3a"
|
|
RESP_PREAMBLE_MAGIC = 0x62334d50 # "PM3b"
|
|
CMD_POSTAMBLE_NOCRC = 0x3361 # "a3"
|
|
RESP_POSTAMBLE_NOCRC = 0x3362 # "b3"
|
|
|
|
PM3_CMD_DATA_SIZE = 512
|
|
|
|
# --- Frame formats (little-endian) ---
|
|
CMD_PREAMBLE_FMT = "<IHH" # magic(4) + length_ng(2) + cmd(2) = 8 bytes
|
|
CMD_PREAMBLE_SIZE = 8
|
|
CMD_POSTAMBLE_FMT = "<H" # crc(2) = 2 bytes
|
|
CMD_POSTAMBLE_SIZE = 2
|
|
|
|
RESP_PREAMBLE_FMT = "<IHbbH" # magic(4) + length_ng(2) + status(1) + reason(1) + cmd(2) = 10 bytes
|
|
RESP_PREAMBLE_SIZE = 10
|
|
RESP_POSTAMBLE_FMT = "<H"
|
|
RESP_POSTAMBLE_SIZE = 2
|
|
|
|
# --- MIX frame: 3x uint64 args at start of payload ---
|
|
MIX_ARGS_FMT = "<QQQ"
|
|
MIX_ARGS_SIZE = 24
|
|
|
|
# --- CRC-16/A (ISO 14443-3) ---
|
|
_CRC_TABLE: list[int] = []
|
|
|
|
def _reflect8(b: int) -> int:
|
|
r = 0
|
|
for i in range(8):
|
|
if b & (1 << i):
|
|
r |= 1 << (7 - i)
|
|
return r
|
|
|
|
def _reflect16(v: int) -> int:
|
|
r = 0
|
|
for i in range(16):
|
|
if v & (1 << i):
|
|
r |= 1 << (15 - i)
|
|
return r
|
|
|
|
def _init_crc_table() -> None:
|
|
global _CRC_TABLE
|
|
if _CRC_TABLE:
|
|
return
|
|
poly = 0x1021
|
|
_CRC_TABLE = [0] * 256
|
|
for i in range(256):
|
|
c = _reflect8(i) << 8
|
|
crc = 0
|
|
for _ in range(8):
|
|
if (crc ^ c) & 0x8000:
|
|
crc = ((crc << 1) ^ poly) & 0xFFFF
|
|
else:
|
|
crc = (crc << 1) & 0xFFFF
|
|
c = (c << 1) & 0xFFFF
|
|
_CRC_TABLE[i] = _reflect16(crc)
|
|
|
|
def crc16_a(data: bytes) -> int:
|
|
"""CRC-16/A: poly=0x1021, init=0xC6C6, refin=true, refout=true, xorout=0x0000."""
|
|
_init_crc_table()
|
|
crc = _reflect16(0xC6C6)
|
|
for b in data:
|
|
crc = ((crc >> 8) ^ _CRC_TABLE[(crc & 0xFF) ^ b]) & 0xFFFF
|
|
return crc
|
|
|
|
|
|
def crc15(data: bytes) -> int:
|
|
"""ISO 15693 / NFC Type 5 CRC (CRC-16/X-25): poly=0x8408 (reflected), init=0xFFFF,
|
|
refin/refout=true, xorout=0xFFFF. Check value crc15(b"123456789") == 0x906E."""
|
|
crc = 0xFFFF
|
|
for b in data:
|
|
crc ^= b
|
|
for _ in range(8):
|
|
crc = (crc >> 1) ^ 0x8408 if (crc & 1) else (crc >> 1)
|
|
return (~crc) & 0xFFFF
|
|
|
|
|
|
def crc15_bytes(data: bytes) -> bytes:
|
|
"""ISO 15693 CRC as appended on the wire (LSB byte first)."""
|
|
c = crc15(data)
|
|
return bytes([c & 0xFF, (c >> 8) & 0xFF])
|
|
|
|
|
|
# --- Error codes ---
|
|
class PM3Status(IntEnum):
|
|
SUCCESS = 0
|
|
EUNDEF = -1
|
|
EINVARG = -2
|
|
EDEVNOTSUPP = -3
|
|
ETIMEOUT = -4
|
|
EOPABORTED = -5
|
|
ENOTIMPL = -6
|
|
ERFTRANS = -7
|
|
EIO = -8
|
|
EOVFLOW = -9
|
|
ESOFT = -10
|
|
EFLASH = -11
|
|
EMALLOC = -12
|
|
EFILE = -13
|
|
ENOTTY = -14
|
|
EINIT = -15
|
|
EWRONGANSWER = -16
|
|
EOUTOFBOUND = -17
|
|
ECARDEXCHANGE = -18
|
|
EAPDU_ENCODEFAIL = -19
|
|
EAPDU_FAIL = -20
|
|
EFAILED = -21
|
|
EPARTIAL = -22
|
|
ETEAROFF = -23
|
|
ECRC = -24
|
|
ESTATIC_NONCE = -25
|
|
ENOPACS = -26
|
|
ELENGTH = -27
|
|
ENOKEY = -28
|
|
ECRYPTO = -29
|
|
ENOFILE = -30
|
|
ENODATA = -98
|
|
EFATAL = -99
|
|
|
|
|
|
# --- Command IDs ---
|
|
class Cmd(IntEnum):
|
|
# Bootloader
|
|
DEVICE_INFO = 0x0000
|
|
FINISH_WRITE = 0x0003
|
|
HARDWARE_RESET = 0x0004
|
|
START_FLASH = 0x0005
|
|
CHIP_INFO = 0x0006
|
|
BL_VERSION = 0x0007
|
|
|
|
# Debug
|
|
DEBUG_PRINT_STRING = 0x0100
|
|
DEBUG_PRINT_INTEGERS = 0x0101
|
|
DEBUG_PRINT_BYTES = 0x0102
|
|
|
|
# System
|
|
NACK = 0x00FE
|
|
ACK = 0x00FF
|
|
BUFF_CLEAR = 0x0105
|
|
READ_MEM = 0x0106
|
|
VERSION = 0x0107
|
|
STATUS = 0x0108
|
|
PING = 0x0109
|
|
READ_MEM_DOWNLOAD = 0x010A
|
|
READ_MEM_DOWNLOADED = 0x010B
|
|
DOWNLOAD_EML_BIGBUF = 0x0110
|
|
DOWNLOADED_EML_BIGBUF = 0x0111
|
|
CAPABILITIES = 0x0112
|
|
QUIT_SESSION = 0x0113
|
|
SET_DBGMODE = 0x0114
|
|
STANDALONE = 0x0115
|
|
WTX = 0x0116
|
|
TIA = 0x0117
|
|
BREAK_LOOP = 0x0118
|
|
SET_TEAROFF = 0x0119
|
|
SET_HF_FIELD_TIMEOUT = 0x011A
|
|
LED_CONTROL = 0x011B # our firmware fork (0x011A is upstream's SET_HF_FIELD_TIMEOUT)
|
|
GET_DBGMODE = 0x0120
|
|
|
|
# FPGA
|
|
FPGA_MAJOR_MODE_OFF = 0x0500
|
|
|
|
# LF
|
|
LF_TI_READ = 0x0202
|
|
LF_TI_WRITE = 0x0203
|
|
LF_ACQ_RAW_ADC = 0x0205
|
|
LF_MOD_THEN_ACQ_RAW_ADC = 0x0206
|
|
DOWNLOAD_BIGBUF = 0x0207
|
|
DOWNLOADED_BIGBUF = 0x0208
|
|
LF_UPLOAD_SIM_SAMPLES = 0x0209
|
|
LF_SIMULATE = 0x020A
|
|
LF_HID_WATCH = 0x020B
|
|
LF_HID_SIMULATE = 0x020C
|
|
LF_SET_DIVISOR = 0x020D
|
|
LF_SIMULATE_BIDIR = 0x020E
|
|
SET_ADC_MUX = 0x020F
|
|
LF_HID_CLONE = 0x0210
|
|
LF_EM410X_CLONE = 0x0211
|
|
LF_T55XX_READBL = 0x0214
|
|
LF_T55XX_WRITEBL = 0x0215
|
|
LF_T55XX_RESET_READ = 0x0216
|
|
LF_PCF7931_READ = 0x0217
|
|
LF_EM4X_READWORD = 0x0218
|
|
LF_EM4X_WRITEWORD = 0x0219
|
|
LF_IO_WATCH = 0x021A
|
|
LF_EM4X_PROTECTWORD = 0x021B
|
|
LF_EM410X_WATCH = 0x021C
|
|
LF_SAMPLING_SET_CONFIG = 0x021D
|
|
LF_FSK_SIMULATE = 0x021E
|
|
LF_ASK_SIMULATE = 0x021F
|
|
LF_PSK_SIMULATE = 0x0220
|
|
LF_AWID_WATCH = 0x0221
|
|
LF_VIKING_CLONE = 0x0222
|
|
LF_PCF7931_WRITE = 0x0223
|
|
LF_T55XX_WAKEUP = 0x0224
|
|
LF_COTAG_READ = 0x0225
|
|
LF_T55XX_SET_CONFIG = 0x0226
|
|
LF_SAMPLING_PRINT_CONFIG = 0x0227
|
|
LF_SAMPLING_GET_CONFIG = 0x0228
|
|
LF_EM4X_LOGIN = 0x0229
|
|
LF_EM4X_BF = 0x022A
|
|
LF_T55XX_CHK_PWDS = 0x0230
|
|
LF_T55XX_DANGERRAW = 0x0231
|
|
LF_NRZ_SIMULATE = 0x0232
|
|
LF_SNIFF_RAW_ADC = 0x0360
|
|
LF_HITAG_SNIFF = 0x0370
|
|
|
|
# HF General
|
|
HF_ACQ_RAW_ADC = 0x0301
|
|
HF_ISO15693_ACQ_RAW_ADC = 0x0300
|
|
HF_ISO15693_READER = 0x0310
|
|
HF_ISO15693_SIMULATE = 0x0311
|
|
HF_ISO15693_SNIFF = 0x0312
|
|
HF_ISO15693_COMMAND = 0x0313
|
|
HF_ISO15693_FINDAFI = 0x0315
|
|
HF_ISO15693_CSETUID = 0x0316
|
|
HF_ISO15693_EML_SETMEM = 0x0331
|
|
|
|
HF_SNIFF_STREAM = 0x0337
|
|
HF_SNIFF_STATUS = 0x0338
|
|
|
|
# Sim table commands (firmware response table)
|
|
SIM_TABLE_UPLOAD = 0x0900
|
|
SIM_TABLE_CLEAR = 0x0901
|
|
SIM_TABLE_UPDATE = 0x0902
|
|
|
|
HF_ISO14443A_SNIFF = 0x0383
|
|
HF_ISO14443A_SIMULATE = 0x0384
|
|
HF_ISO14443A_READER = 0x0385
|
|
HF_ISO14443A_GET_CONFIG = 0x03B1
|
|
HF_ISO14443A_SET_CONFIG = 0x03B2
|
|
|
|
HF_ISO14443B_COMMAND = 0x0305
|
|
HF_ISO14443B_SIMULATE = 0x0381
|
|
HF_ISO14443B_SNIFF = 0x0382
|
|
|
|
# Tuning
|
|
MEASURE_ANTENNA_TUNING = 0x0400
|
|
MEASURE_ANTENNA_TUNING_HF = 0x0401
|
|
MEASURE_ANTENNA_TUNING_LF = 0x0402
|
|
LISTEN_READER_FIELD = 0x0420
|
|
HF_DROPFIELD = 0x0430
|
|
HF_DECAY = 0x0440
|
|
|
|
# MIFARE Classic
|
|
HF_MIFARE_EML_MEMCLR = 0x0601
|
|
HF_MIFARE_EML_MEMSET = 0x0602
|
|
HF_MIFARE_EML_MEMGET = 0x0603
|
|
HF_MIFARE_SIMULATE = 0x0610
|
|
HF_MIFARE_READER = 0x0611
|
|
HF_MIFARE_NESTED = 0x0612
|
|
HF_MIFARE_ACQ_ENCRYPTED_NONCES = 0x0613
|
|
HF_MIFARE_ACQ_NONCES = 0x0614
|
|
HF_MIFARE_STATIC_NESTED = 0x0615
|
|
HF_MIFARE_READBL = 0x0620
|
|
HF_MIFARE_READSC = 0x0621
|
|
HF_MIFARE_WRITEBL = 0x0622
|
|
HF_MIFARE_CHKKEYS = 0x0623
|
|
HF_MIFARE_SETMOD = 0x0624
|
|
HF_MIFARE_CHKKEYS_FAST = 0x0625
|
|
HF_MIFARE_CHKKEYS_FILE = 0x0626
|
|
HF_MIFARE_VALUE = 0x0627
|
|
HF_MIFARE_READBL_EX = 0x0628
|
|
HF_MIFARE_WRITEBL_EX = 0x0629
|
|
HF_MIFARE_SNIFF = 0x0630
|
|
HF_MIFARE_MFKEY = 0x0631
|
|
HF_MIFARE_PERSONALIZE_UID = 0x0632
|
|
HF_MIFARE_CSETBL = 0x0605
|
|
HF_MIFARE_CGETBL = 0x0606
|
|
HF_MIFARE_CIDENT = 0x0607
|
|
|
|
# MIFARE Ultralight
|
|
HF_MIFAREU_READBL = 0x0720
|
|
HF_MIFAREU_READCARD = 0x0721
|
|
HF_MIFAREU_WRITEBL = 0x0722
|
|
HF_MIFARE_NACK_DETECT = 0x0730
|
|
|
|
# HF Sniff/Plot
|
|
HF_SNIFF = 0x0800
|
|
HF_PLOT = 0x0801
|
|
FPGAMEM_DOWNLOAD = 0x0802
|
|
FPGAMEM_DOWNLOADED = 0x0803
|
|
|
|
UNKNOWN = 0xFFFF
|
|
|
|
|
|
# Sniff streaming flags
|
|
SNIFF_FLAG_STREAMING = 0x01
|
|
SNIFF_FLAG_BUTTON_TOGGLE = 0x02
|
|
|
|
# Sniff states
|
|
SNIFF_STATE_STARTED = 0
|
|
SNIFF_STATE_PAUSED = 1
|
|
SNIFF_STATE_RESUMED = 2
|
|
SNIFF_STATE_STOPPED = 3
|