From 2137284a938b872bc1cbbe305a665c678f8a4ab7 Mon Sep 17 00:00:00 2001 From: iceman1001 Date: Wed, 12 Mar 2025 16:41:06 +0100 Subject: [PATCH] style\n Some improvements to `trace list -t seos` annotations. --- CHANGELOG.md | 1 + client/deps/cliparser/cliparser.c | 41 +++++-- client/src/cmdhf14a.c | 25 ++-- client/src/cmdhficlass.c | 15 +-- client/src/cmdhflist.c | 150 ++++++++++++++++++++---- client/src/cmdhflist.h | 3 +- client/src/cmdhflto.c | 2 +- client/src/cmdhfmf.c | 3 +- client/src/cmdlfhid.c | 3 + client/src/cmdtrace.c | 10 +- client/src/fileutils.c | 2 +- client/src/mifare/mad.c | 24 +++- client/src/pm3line_vocabulary.h | 1 + client/src/util.c | 189 +++++++++++++++++++++--------- doc/commands.json | 21 +++- doc/commands.md | 1 + include/pm3_cmd.h | 1 + 17 files changed, 366 insertions(+), 126 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 60ba2958d..a50827e96 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,7 @@ All notable changes to this project will be documented in this file. This project uses the changelog in accordance with [keepchangelog](http://keepachangelog.com/). Please use this to write notable changes, which is not the same as git commit log... ## [unreleased][unreleased] +- Changed `trace list -t seos` - improved annotation (@iceman1001) - Added `make commands` to regenerate commands documentation files and autocompletion data independently of `make style` (@doegox) - Added texecom identification, thanks @en4rab ! (@iceman1001) - Added `hf 15 slixprotectpage` command diff --git a/client/deps/cliparser/cliparser.c b/client/deps/cliparser/cliparser.c index 0ea0bd33b..acefe7105 100644 --- a/client/deps/cliparser/cliparser.c +++ b/client/deps/cliparser/cliparser.c @@ -180,8 +180,10 @@ int CLIParserParseStringEx(CLIParserContext *ctx, const char *str, void *vargtab // parse params for (int i = 0; i < len; i++) { + switch (state) { - case PS_FIRST: // first char + case PS_FIRST: { // first char + if (!clueData || str[i] == '-') { // first char before space is '-' - next element - option OR not "clueData" for not-option fields state = PS_OPTION; @@ -193,24 +195,32 @@ int CLIParserParseStringEx(CLIParserContext *ctx, const char *str, void *vargtab } } spaceptr = NULL; - case PS_ARGUMENT: - if (state == PS_FIRST) + } + case PS_ARGUMENT: { + + if (state == PS_FIRST) { state = PS_ARGUMENT; - if (str[i] == '"') { + } + + if (str[i] == '"' || str[i] == '\'') { state = PS_QUOTE; break; } + if (isSpace(str[i])) { spaceptr = bufptr; state = PS_FIRST; } + *bufptr = str[i]; bufptr++; break; - case PS_OPTION: + } + + case PS_OPTION: { + if (isSpace(str[i])) { state = PS_FIRST; - *bufptr = 0x00; bufptr++; argv[argc++] = bufptr; @@ -220,17 +230,22 @@ int CLIParserParseStringEx(CLIParserContext *ctx, const char *str, void *vargtab *bufptr = str[i]; bufptr++; break; - case PS_QUOTE: - if (str[i] == '"') { + } + case PS_QUOTE: { + + if (str[i] == '"' || str[i] == '\'') { *bufptr++ = 0x00; state = PS_FIRST; } else { - if (isSpace(str[i]) == false) { + +// if (isSpace(str[i]) == false) { *bufptr++ = str[i]; - } +// } } break; } + } + if (bufptr > bufptrend) { PrintAndLogEx(ERR, "ERROR: Line too long\n"); fflush(stdout); @@ -296,8 +311,9 @@ int CLIParamBinToBuf(struct arg_str *argstr, uint8_t *data, int maxdatalen, int int CLIParamStrToBuf(struct arg_str *argstr, uint8_t *data, int maxdatalen, int *datalen) { *datalen = 0; - if (!argstr->count) + if (!argstr->count) { return 0; + } uint8_t tmpstr[MAX_INPUT_ARG_LENGTH + 1] = {0}; int ibuf = 0; @@ -319,8 +335,9 @@ int CLIParamStrToBuf(struct arg_str *argstr, uint8_t *data, int maxdatalen, int ibuf = MIN(ibuf, (sizeof(tmpstr) / 2)); tmpstr[ibuf] = 0; - if (ibuf == 0) + if (ibuf == 0) { return 0; + } if (ibuf > maxdatalen) { PrintAndLogEx(ERR, "Parameter error: string too long (%i chars), expected MAX %i chars\n", ibuf, maxdatalen); diff --git a/client/src/cmdhf14a.c b/client/src/cmdhf14a.c index 30b45c68c..eab7dfce8 100644 --- a/client/src/cmdhf14a.c +++ b/client/src/cmdhf14a.c @@ -1207,28 +1207,35 @@ static int CmdExchangeAPDU(bool chainingin, const uint8_t *datain, int datainlen } uint16_t cmdc = 0; - if (chainingin) + if (chainingin) { cmdc = ISO14A_SEND_CHAINING; + } // "Command APDU" length should be 5+255+1, but javacard's APDU buffer might be smaller - 133 bytes // https://stackoverflow.com/questions/32994936/safe-max-java-card-apdu-data-command-and-respond-size // here length PM3_CMD_DATA_SIZE=512 // timeout must be authomatically set by "get ATS" - if (datain) + if (datain) { SendCommandMIX(CMD_HF_ISO14443A_READER, ISO14A_APDU | ISO14A_NO_DISCONNECT | cmdc, (datainlen & 0x1FF), 0, datain, datainlen & 0x1FF); - else + } else { SendCommandMIX(CMD_HF_ISO14443A_READER, ISO14A_APDU | ISO14A_NO_DISCONNECT | cmdc, 0, 0, NULL, 0); + } PacketResponseNG resp; - if (WaitForResponseTimeout(CMD_ACK, &resp, timeout)) { + if (WaitForResponseTimeout(CMD_ACK, &resp, timeout) == false) { + PrintAndLogEx(DEBUG, "ERR: APDU: Reply timeout"); + return PM3_EAPDU_FAIL; + } + const uint8_t *recv = resp.data.asBytes; int iLen = resp.oldarg[0]; uint8_t res = resp.oldarg[1]; int dlen = iLen - 2; - if (dlen < 0) + if (dlen < 0) { dlen = 0; + } *dataoutlen += dlen; if (maxdataoutlen && *dataoutlen > maxdataoutlen) { @@ -1243,7 +1250,7 @@ static int CmdExchangeAPDU(bool chainingin, const uint8_t *datain, int datainlen return PM3_SUCCESS; } - if (!iLen) { + if (iLen == 0) { PrintAndLogEx(DEBUG, "ERR: APDU: No APDU response"); return PM3_EAPDU_FAIL; } @@ -1272,10 +1279,6 @@ static int CmdExchangeAPDU(bool chainingin, const uint8_t *datain, int datainlen PrintAndLogEx(DEBUG, "ERR: APDU: ISO 14443A CRC error"); return PM3_EAPDU_FAIL; } - } else { - PrintAndLogEx(DEBUG, "ERR: APDU: Reply timeout"); - return PM3_EAPDU_FAIL; - } return PM3_SUCCESS; } @@ -2779,7 +2782,7 @@ int infoHF14A(bool verbose, bool do_nack_test, bool do_aid_search) { if ((isMagic & MAGIC_FLAG_GEN_2) == MAGIC_FLAG_GEN_2) { PrintAndLogEx(HINT, "Hint: use `" _YELLOW_("hf mf") "` commands"); } else { - PrintAndLogEx(HINT, "Hint: try " _YELLOW_("`hf mf`") " commands"); + PrintAndLogEx(HINT, "Hint: try " _YELLOW_("`hf mf info`")); } } diff --git a/client/src/cmdhficlass.c b/client/src/cmdhficlass.c index 70dc6b1ab..aa73c7e1c 100644 --- a/client/src/cmdhficlass.c +++ b/client/src/cmdhficlass.c @@ -1414,7 +1414,7 @@ static int iclass_decode_credentials_new_pacs(uint8_t *d) { uint8_t pad = d[offset]; - PrintAndLogEx(INFO, "%u , %u", offset, pad); + PrintAndLogEx(DEBUG, "%u , %u", offset, pad); char *binstr = (char *)calloc((PICOPASS_BLOCK_SIZE * 8) + 1, sizeof(uint8_t)); if (binstr == NULL) { @@ -1424,17 +1424,16 @@ static int iclass_decode_credentials_new_pacs(uint8_t *d) { uint8_t n = PICOPASS_BLOCK_SIZE - offset - 2; bytes_2_binstr(binstr, d + offset + 2, n); - PrintAndLogEx(NORMAL, ""); - PrintAndLogEx(SUCCESS, "PACS......... " _GREEN_("%s"), sprint_hex_inrow(d + offset + 2, n)); - PrintAndLogEx(SUCCESS, "padded bin... " _GREEN_("%s") " ( %zu )", binstr, strlen(binstr)); + PrintAndLogEx(DEBUG, "PACS......... " _GREEN_("%s"), sprint_hex_inrow(d + offset + 2, n)); + PrintAndLogEx(DEBUG, "padded bin... " _GREEN_("%s") " ( %zu )", binstr, strlen(binstr)); binstr[strlen(binstr) - pad] = '\0'; - PrintAndLogEx(SUCCESS, "bin.......... " _GREEN_("%s") " ( %zu )", binstr, strlen(binstr)); + PrintAndLogEx(DEBUG, "bin.......... " _GREEN_("%s") " ( %zu )", binstr, strlen(binstr)); size_t hexlen = 0; uint8_t hex[16] = {0}; binstr_2_bytes(hex, &hexlen, binstr); - PrintAndLogEx(SUCCESS, "hex.......... " _GREEN_("%s"), sprint_hex_inrow(hex, hexlen)); + PrintAndLogEx(DEBUG, "hex.......... " _GREEN_("%s"), sprint_hex_inrow(hex, hexlen)); uint32_t top = 0, mid = 0, bot = 0; if (binstring_to_u96(&top, &mid, &bot, binstr) != strlen(binstr)) { @@ -2852,8 +2851,9 @@ static int CmdHFiClass_ReadBlock(const char *Cmd) { return PM3_SUCCESS; bool use_sc = IsCardHelperPresent(verbose); - if (use_sc == false) + if (use_sc == false) { return PM3_SUCCESS; + } // crypto helper available. PrintAndLogEx(INFO, "----------------------------- " _CYAN_("Cardhelper") " -----------------------------"); @@ -3234,6 +3234,7 @@ void print_iclass_sio(uint8_t *iclass_dump, size_t dump_len, bool verbose) { size_t sio_length; detect_credential(iclass_dump, dump_len, &is_legacy, &is_se, &is_sr, &sio_start, &sio_length); + // sanity checks if (sio_start == NULL) { return; } diff --git a/client/src/cmdhflist.c b/client/src/cmdhflist.c index fdd189568..5b0bb7636 100644 --- a/client/src/cmdhflist.c +++ b/client/src/cmdhflist.c @@ -70,8 +70,14 @@ static uint8_t *gs_mfuc_key = NULL; */ uint8_t iso14443A_CRC_check(bool isResponse, uint8_t *d, uint8_t n) { - if (n < 3) return 2; - if (isResponse && (n == 5)) return 2; + if (n < 3) { + return 2; + } + + if (isResponse && (n == 5)) { + return 2; + } + if (d[1] == 0x50 && d[0] >= ISO14443A_CMD_ANTICOLL_OR_SELECT && d[0] <= ISO14443A_CMD_ANTICOLL_OR_SELECT_3) { @@ -80,6 +86,25 @@ uint8_t iso14443A_CRC_check(bool isResponse, uint8_t *d, uint8_t n) { return check_crc(CRC_14443_A, d, n); } +uint8_t seos_CRC_check(bool isResponse, uint8_t *d, uint8_t n) { + if (n < 3) { + return 2; + } + + // 5 bytes response Card busy 0xFA have crc, the rest is most likely 14a anticollision + if ((n == 5) && (d[0] != 0xFA)) { + return 2; + } + + if (d[1] == 0x50 && + d[0] >= ISO14443A_CMD_ANTICOLL_OR_SELECT && + d[0] <= ISO14443A_CMD_ANTICOLL_OR_SELECT_3) { + return 2; + } + return check_crc(CRC_14443_A, d, n); +} + + uint8_t mifare_CRC_check(bool isResponse, uint8_t *data, uint8_t len) { switch (MifareAuthState) { case masNone: @@ -423,14 +448,14 @@ int applyIso14443a(char *exp, size_t size, uint8_t *cmd, uint8_t cmdsize, bool i snprintf(exp, size, "?"); break; } - case NTAG_I2C_FASTWRITE: + case NTAG_I2C_FASTWRITE: { if (cmdsize == 69) snprintf(exp, size, "FAST WRITE (" _MAGENTA_("%d-%d") ")", cmd[1], cmd[2]); else snprintf(exp, size, "?"); break; - - default: + } + default: { if ((cmd[0] & 0xF0) == 0xD0 && (cmdsize == 4 || cmdsize == 5)) { snprintf(exp, size, "PPS - CID=%x", cmd[0] & 0x0F) ; } else if ((cmd[0] & 0xF0) == 0x60 && (cmdsize == 4)) { @@ -440,7 +465,10 @@ int applyIso14443a(char *exp, size_t size, uint8_t *cmd, uint8_t cmdsize, bool i return PM3_ESOFT; } } + } + } else { + if (gs_mfuc_state == 1) { if ((cmd[0] == 0xAF) && (cmdsize == 11)) { // register RndB @@ -450,6 +478,7 @@ int applyIso14443a(char *exp, size_t size, uint8_t *cmd, uint8_t cmdsize, bool i gs_mfuc_state = 0; } } + if (gs_mfuc_state == 3) { if ((cmd[0] == 0x00) && (cmdsize == 11)) { // register RndA' @@ -1748,49 +1777,115 @@ void annotateCryptoRF(char *exp, size_t size, uint8_t *cmd, uint8_t cmdsize) { } } -void annotateSeos(char *exp, size_t size, uint8_t *cmd, uint8_t cmdsize) { +void annotateSeos(char *exp, size_t size, uint8_t *cmd, uint8_t cmdsize, bool isResponse) { + + if (cmd[0] == 0xFA && cmdsize == 5) { + snprintf(exp, size, (isResponse) ? "BUSY" : "DONE?"); + return; + } // it's basically a ISO14443a tag, so try annotation from there if (applyIso14443a(exp, size, cmd, cmdsize, false) != PM3_SUCCESS) { int pos = 0; switch (cmd[0]) { - case 2: - case 3: - pos = 2; - break; case 0: + case 2: + case 3: { pos = 1; break; - default: + } + default: { pos = 2; break; } + } - if (memcmp(cmd + pos, "\x00\xa4\x04\x00\x0a", 5) == 0) { - snprintf(exp, size, "SELECT AID"); + if (memcmp(cmd + pos, "\x00\xA4\x04\x00", 4) == 0) { + uint8_t n = cmd[pos + 4]; + snprintf(exp, size, "SELECT AID " _WHITE_("%s"), sprint_hex_inrow(cmd + pos + 4 + 1, n)); + return; + } + + if (memcmp(cmd + pos, "\x80\xA5\x00\x00", 4) == 0) { + snprintf(exp, size, "SELECT GDF"); + return; } if (memcmp(cmd + pos, "\x80\xA5\x04\x00", 4) == 0) { - snprintf(exp, size, "SELECT ADF / OID"); + uint8_t n = cmd[pos + 4 + 2]; + snprintf(exp, size, "SELECT OID " _WHITE_("%s"), sprint_hex_inrow(cmd + pos + 4 + 2 + 1, n)); + return; } - if (memcmp(cmd + pos, "\x00\x87\x00\x01\x04\x7c\x02\x81\x00", 9) == 0) { - snprintf(exp, size, "GET CHALLENGE"); + if (memcmp(cmd + pos, "\x80\xA5\x07", 3) == 0) { + uint8_t ks = cmd[pos + 3]; + snprintf(exp, size, "SELECT GDF " _WHITE_("(") " key " _MAGENTA_("%02X") " )", ks); + return; } - if (memcmp(cmd + pos, "\x00\x87\x00\x01\x2c", 5) == 0) { - snprintf(exp, size, "MUTUAL AUTHENTICATION"); + if (memcmp(cmd + pos, "\x00\x87\x00", 3) == 0) { + uint8_t ks = cmd[pos + 3]; + if (memcmp(cmd + pos + 3 + 1, "\x04\x7c\x02\x81\x00", 5) == 0) { + snprintf(exp, size, "GET CHALLENGE " _WHITE_("(") " key " _MAGENTA_("%02X") " )", ks); + } + return; } - if (memcmp(cmd + pos, "\x0c\xcb\x3f\xff", 4) == 0) { + if (memcmp(cmd + pos, "\x00\x87\x00", 3) == 0) { + uint8_t ks = cmd[pos + 3]; + if (memcmp(cmd + pos + 3 + 1, "\x2C\x7C\x2A\x82\x28", 5) == 0) { + snprintf(exp, size, "MUTUAL AUTHENTICATION " _WHITE_("(") " key " _MAGENTA_("%02X") " )", ks); + } + return; + } + + if (memcmp(cmd + pos, "\x0C\xCB\x3F\xFF", 4) == 0) { snprintf(exp, size, "GET DATA"); + return; } - // apply ISO7816 annotations? -// if (annotateIso7816(exp, size, cmd, cmdsize) == 0) { -// } - // apply SEOS annotations? + if (memcmp(cmd + pos, "\x0C\xDB\x3F\xFF", 4) == 0) { + snprintf(exp, size, "UPDATE DATA"); + return; + } + + if (memcmp(cmd + pos, "\x0C\xED\x06\x00", 4) == 0) { + snprintf(exp, size, "DELETE DATA"); + return; + } + + if (memcmp(cmd + pos, "\x0C\x41\x0C\x03", 4) == 0) { + snprintf(exp, size, "CREATE ADF"); + return; + } + + if (isResponse) { + + if (memcmp(cmd + pos, "\xCD\x02", 2) == 0) { + + uint8_t ea = cmd[pos + 2]; + uint8_t ha = cmd[pos + 3]; + + char eas[10] = {0}; + if (ea == SEOS_ENCRYPTION_2K3DES) { + strcat(eas, "2K3DES"); + } else if (ea == SEOS_ENCRYPTION_3K3DES) { + strcat(eas, "3K3DES"); + } else if (ea == SEOS_ENCRYPTION_AES) { + strcat(eas, "AES"); + } + + char has[10] = {0}; + if (ha == SEOS_HASHING_SHA1) { + strcat(has, "SHA1"); + } else if (ha == SEOS_HASHING_SHA256) { + strcat(has, "SHA256"); + } + snprintf(exp, size, "%s / %s", eas, has); + return; + } + } } } @@ -1830,11 +1925,13 @@ void annotateLegic(char *exp, size_t size, uint8_t *cmd, uint8_t cmdsize) { uint16_t address = (cmd[2] << 7) | cmd[1] >> 1; - if (cmdBit == LEGIC_READ) + if (cmdBit == LEGIC_READ) { snprintf(exp, size, "READ Byte(%d)", address); + } - if (cmdBit == LEGIC_WRITE) + if (cmdBit == LEGIC_WRITE) { snprintf(exp, size, "WRITE Byte(%d)", address); + } break; } case 21: { @@ -1854,8 +1951,9 @@ void annotateLegic(char *exp, size_t size, uint8_t *cmd, uint8_t cmdsize) { break; } case 12: - default: + default: { break; + } } } diff --git a/client/src/cmdhflist.h b/client/src/cmdhflist.h index 92f54e4af..98bf27318 100644 --- a/client/src/cmdhflist.h +++ b/client/src/cmdhflist.h @@ -46,6 +46,7 @@ uint8_t felica_CRC_check(uint8_t *d, uint8_t n); uint8_t mifare_CRC_check(bool isResponse, uint8_t *data, uint8_t len); uint8_t iso15693_CRC_check(uint8_t *d, uint8_t n); uint8_t iclass_CRC_check(bool isResponse, uint8_t *d, uint8_t n); +uint8_t seos_CRC_check(bool isResponse, uint8_t *d, uint8_t n); int applyIso14443a(char *exp, size_t size, uint8_t *cmd, uint8_t cmdsize, bool is_response); @@ -68,7 +69,7 @@ void annotateMifare(char *exp, size_t size, uint8_t *cmd, uint8_t cmdsize, void annotateLTO(char *exp, size_t size, uint8_t *cmd, uint8_t cmdsize); void annotateCryptoRF(char *exp, size_t size, uint8_t *cmd, uint8_t cmdsize); -void annotateSeos(char *exp, size_t size, uint8_t *cmd, uint8_t cmdsize); +void annotateSeos(char *exp, size_t size, uint8_t *cmd, uint8_t cmdsize, bool isResponse); bool DecodeMifareData(uint8_t *cmd, uint8_t cmdsize, uint8_t *parity, bool isResponse, uint8_t *mfData, size_t *mfDataLen, const uint64_t *dicKeys, uint32_t dicKeysCount); bool NTParityChk(AuthData_t *ad, uint32_t ntx); diff --git a/client/src/cmdhflto.c b/client/src/cmdhflto.c index 6ffe115c0..7542114b1 100644 --- a/client/src/cmdhflto.c +++ b/client/src/cmdhflto.c @@ -682,7 +682,7 @@ static int CmdHfLTOWriteBlock(const char *Cmd) { int res = wrblLTO(blk, block_data, true); if (res == PM3_SUCCESS) - PrintAndLogEx(HINT, "Try use 'hf lto rdbl' for verification"); + PrintAndLogEx(HINT, "Try `" _YELLOW_("hf lto rdbl") "` to verify"); return res; } diff --git a/client/src/cmdhfmf.c b/client/src/cmdhfmf.c index 3a9afca3e..c2d20cb4c 100644 --- a/client/src/cmdhfmf.c +++ b/client/src/cmdhfmf.c @@ -8267,7 +8267,7 @@ static int CmdHF14AGen4Load(const char *cmd) { arg_lit0("v", "verbose", "verbose output"), arg_str0("f", "file", "", "Specify a filename for dump file"), arg_lit0(NULL, "emu", "from emulator memory"), - arg_int0(NULL, "start", "", "index of block to start writing (default 0)"), + arg_int0(NULL, "start", "", "index of block to start writing (def 0)"), arg_int0(NULL, "end", "", "index of block to end writing (default last block)"), arg_param_end }; @@ -10234,7 +10234,6 @@ static command_t CommandTable[] = { {"gdmparsecfg", CmdHF14AGen4_GDM_ParseCfg, AlwaysAvailable, "Parse config block to card"}, {"gdmsetblk", CmdHF14AGen4_GDM_SetBlk, IfPm3Iso14443a, "Write block to card"}, {"-----------", CmdHelp, IfPm3Iso14443a, "----------------------- " _CYAN_("ndef") " -----------------------"}, -// {"ice", CmdHF14AMfice, IfPm3Iso14443a, "collect MIFARE Classic nonces to file"}, {"ndefformat", CmdHFMFNDEFFormat, IfPm3Iso14443a, "Format MIFARE Classic Tag as NFC Tag"}, {"ndefread", CmdHFMFNDEFRead, IfPm3Iso14443a, "Read and print NDEF records from card"}, {"ndefwrite", CmdHFMFNDEFWrite, IfPm3Iso14443a, "Write NDEF records to card"}, diff --git a/client/src/cmdlfhid.c b/client/src/cmdlfhid.c index bb1946414..6b651dccc 100644 --- a/client/src/cmdlfhid.c +++ b/client/src/cmdlfhid.c @@ -575,11 +575,13 @@ static int CmdHIDBrute(const char *Cmd) { PrintAndLogEx(INFO, "Facility code.... %u", card_hi.FacilityCode); PrintAndLogEx(INFO, "Card number...... %" PRIu64, card_hi.CardNumber); PrintAndLogEx(INFO, "Delay............ " _YELLOW_("%d"), delay); + if (strcmp(field, "fc") == 0) { PrintAndLogEx(INFO, "Field............ " _YELLOW_("fc")); } else if (strcmp(field, "cn") == 0) { PrintAndLogEx(INFO, "Field............ " _YELLOW_("cn")); } + switch (direction) { case 0: PrintAndLogEx(INFO, "Direction........ " _YELLOW_("both")); @@ -594,6 +596,7 @@ static int CmdHIDBrute(const char *Cmd) { break; } } + PrintAndLogEx(NORMAL, ""); PrintAndLogEx(INFO, "Started bruteforcing HID Prox reader"); PrintAndLogEx(INFO, "Press " _GREEN_("pm3 button") " or " _GREEN_("") " to abort simulation"); diff --git a/client/src/cmdtrace.c b/client/src/cmdtrace.c index 113c89f7f..07ffc3377 100644 --- a/client/src/cmdtrace.c +++ b/client/src/cmdtrace.c @@ -561,9 +561,11 @@ static uint16_t printTraceLine(uint16_t tracepos, uint16_t traceLen, uint8_t *tr case ISO_14443A: case MFDES: case LTO: - case SEOS: crcStatus = iso14443A_CRC_check(hdr->isResponse, frame, data_len); break; + case SEOS: + crcStatus = seos_CRC_check(hdr->isResponse, frame, data_len); + break; case ISO_7816_4: crcStatus = iso14443A_CRC_check(hdr->isResponse, frame, data_len) == 1 ? 3 : 0; crcStatus = iso14443B_CRC_check(frame, data_len) == 1 ? 4 : crcStatus; @@ -803,6 +805,9 @@ static uint16_t printTraceLine(uint16_t tracepos, uint16_t traceLen, uint8_t *tr case ICLASS: annotateIclass(explanation, sizeof(explanation), frame, data_len, hdr->isResponse); break; + case SEOS: + annotateSeos(explanation, sizeof(explanation), frame, data_len, hdr->isResponse); + break; default: break; } @@ -839,9 +844,6 @@ static uint16_t printTraceLine(uint16_t tracepos, uint16_t traceLen, uint8_t *tr case PROTO_CRYPTORF: annotateCryptoRF(explanation, sizeof(explanation), frame, data_len); break; - case SEOS: - annotateSeos(explanation, sizeof(explanation), frame, data_len); - break; case PROTO_FMCOS20: annotateFMCOS20(explanation, sizeof(explanation), frame, data_len); break; diff --git a/client/src/fileutils.c b/client/src/fileutils.c index 73b992fd2..2ba24d2e4 100644 --- a/client/src/fileutils.c +++ b/client/src/fileutils.c @@ -2412,7 +2412,6 @@ int loadFileDICTIONARY_safe_ex(const char *preferredName, const char *suffix, vo // larger keys than expected is skipped if (strlen(line) > keylen) { - PrintAndLogEx(INFO, "larger %zu - %s", strlen(line), line); continue; } @@ -3085,6 +3084,7 @@ int pm3_load_dump(const char *fn, void **pdump, size_t *dumplen, size_t maxdumpl PrintAndLogEx(WARNING, "fail, cannot allocate memory"); return PM3_EMALLOC; } + res = loadFileJSON(fn, *pdump, maxdumplen, dumplen, NULL); if (res == PM3_SUCCESS) { return res; diff --git a/client/src/mifare/mad.c b/client/src/mifare/mad.c index 10335994f..a3365bb04 100644 --- a/client/src/mifare/mad.c +++ b/client/src/mifare/mad.c @@ -355,8 +355,16 @@ int MAD1DecodeAndPrint(uint8_t *sector, bool swapmad, bool verbose, bool *haveMA aid ); } else { - char fmt[60]; - snprintf(fmt, sizeof(fmt), (ibs == i) ? _MAGENTA_(" %02d [%04X]%s") : " %02d [" _GREEN_("%04X") "]%s", i, aid, "%s"); + char fmt[80]; + snprintf(fmt + , sizeof(fmt) + , (ibs == i) ? + _MAGENTA_(" %02d [%04X] %s") : + " %02d [" _GREEN_("%04X") "] %s" + , i + , aid + , "%s" + ); print_aid_description(mad_known_aids, aid, fmt, verbose); prev_aid = aid; } @@ -408,8 +416,16 @@ int MAD2DecodeAndPrint(uint8_t *sector, bool swapmad, bool verbose) { aid ); } else { - char fmt[60]; - snprintf(fmt, sizeof(fmt), (ibs == i) ? _MAGENTA_(" %02d [%04X]%s") : " %02d [" _GREEN_("%04X") "]%s", i + 16, aid, "%s"); + char fmt[80]; + snprintf(fmt + , sizeof(fmt) + , (ibs == i) ? + _MAGENTA_(" %02d [%04X] %s") : + " %02d [" _GREEN_("%04X") "] %s" + , i + 16 + , aid + , "%s" + ); print_aid_description(mad_known_aids, aid, fmt, verbose); prev_aid = aid; } diff --git a/client/src/pm3line_vocabulary.h b/client/src/pm3line_vocabulary.h index 048a92ec8..2447b924f 100644 --- a/client/src/pm3line_vocabulary.h +++ b/client/src/pm3line_vocabulary.h @@ -202,6 +202,7 @@ const static vocabulary_t vocabulary[] = { { 0, "hf 15 slixeasenable" }, { 0, "hf 15 slixprivacydisable" }, { 0, "hf 15 slixprivacyenable" }, + { 0, "hf 15 slixprotectpage" }, { 0, "hf 15 passprotectafi" }, { 0, "hf 15 passprotecteas" }, { 0, "hf 15 findafi" }, diff --git a/client/src/util.c b/client/src/util.c index 29ed40eb2..5db38bf86 100644 --- a/client/src/util.c +++ b/client/src/util.c @@ -152,10 +152,10 @@ void FillFileNameByUID(char *filenamePrefix, const uint8_t *uid, const char *ext int len = strlen(filenamePrefix); - for (int j = 0; j < uidlen; j++) { + for (int i = 0; i < uidlen; i++) { // This is technically not the safest option, but there is no way to make this work without changing the function signature // Possibly todo for future PR, but given UID lenghts are defined by program and not variable, should not be an issue - snprintf(filenamePrefix + len + j * 2, 3, "%02X", uid[j]); + snprintf(filenamePrefix + len + i * 2, 3, "%02X", uid[i]); } strcat(filenamePrefix, ext); @@ -196,7 +196,7 @@ bool CheckStringIsHEXValue(const char *value) { } for (size_t i = 0; i < strlen(value); i++) { - if (!isxdigit(value[i])) { + if (isxdigit(value[i]) == 0) { return false; } } @@ -220,11 +220,13 @@ void ascii_to_buffer(uint8_t *buf, const uint8_t *hex_data, const size_t hex_len } size_t m = (min_str_len > i) ? min_str_len : 0; - if (m > hex_max_len) + if (m > hex_max_len) { m = hex_max_len; + } - for (; i < m; i++, tmp++) + for (; i < m; i++, tmp++) { *tmp = ' '; + } // remove last space *tmp = '\0'; @@ -234,8 +236,9 @@ void hex_to_buffer(uint8_t *buf, const uint8_t *hex_data, const size_t hex_len, const size_t min_str_len, const size_t spaces_between, bool uppercase) { // sanity check - if (buf == NULL || hex_len < 1) + if (buf == NULL || hex_len < 1) { return; + } // 1. hex string length. // 2. byte array to be converted to string @@ -252,18 +255,22 @@ void hex_to_buffer(uint8_t *buf, const uint8_t *hex_data, const size_t hex_len, *(tmp++) = b2s((hex_data[i] >> 4), uppercase); *(tmp++) = b2s(hex_data[i], uppercase); - for (size_t j = 0; j < spaces_between; j++) + for (size_t j = 0; j < spaces_between; j++) { *(tmp++) = ' '; + } } i *= (2 + spaces_between); size_t m = (min_str_len > i) ? min_str_len : 0; - if (m > hex_max_len) - m = hex_max_len; - while (m--) + if (m > hex_max_len) { + m = hex_max_len; + } + + while (m--) { *(tmp++) = ' '; + } // remove last space *tmp = '\0'; @@ -274,9 +281,9 @@ void hex_to_buffer(uint8_t *buf, const uint8_t *hex_data, const size_t hex_len, void print_hex(const uint8_t *data, const size_t len) { if (data == NULL || len == 0) return; - for (size_t i = 0; i < len; i++) + for (size_t i = 0; i < len; i++) { PrintAndLogEx(NORMAL, "%02x " NOLF, data[i]); - + } PrintAndLogEx(NORMAL, ""); } @@ -621,10 +628,13 @@ char *sprint_breakdown_bin(color_t color, const char *bs, int width, int padn, i } int hex_to_bytes(const char *hexValue, uint8_t *bytesValue, size_t maxBytesValueLen) { + char buf[4] = {0}; int indx = 0; int bytesValueLen = 0; + while (hexValue[indx]) { + if (hexValue[indx] == '\t' || hexValue[indx] == ' ') { indx++; continue; @@ -684,6 +694,7 @@ void bytes_to_bytebits(const void *src, const size_t srclen, void *dest) { uint32_t i = srclen * 8; size_t j = srclen; + while (j--) { uint8_t b = s[j]; d[--i] = (b >> 0) & 1; @@ -740,20 +751,33 @@ int param_getptr(const char *line, int *bg, int *en, int paramnum) { *en = 0; // skip spaces - while (line[*bg] == ' ' || line[*bg] == '\t')(*bg)++; + while (line[*bg] == ' ' || line[*bg] == '\t') { + (*bg)++; + } + if (*bg >= len) { return 1; } for (i = 0; i < paramnum; i++) { - while (line[*bg] != ' ' && line[*bg] != '\t' && line[*bg] != '\0')(*bg)++; - while (line[*bg] == ' ' || line[*bg] == '\t')(*bg)++; - if (line[*bg] == '\0') return 1; + while (line[*bg] != ' ' && line[*bg] != '\t' && line[*bg] != '\0') { + (*bg)++; + } + + while (line[*bg] == ' ' || line[*bg] == '\t') { + (*bg)++; + } + + if (line[*bg] == '\0') { + return 1; + } } *en = *bg; - while (line[*en] != ' ' && line[*en] != '\t' && line[*en] != '\0')(*en)++; + while (line[*en] != ' ' && line[*en] != '\t' && line[*en] != '\0') { + (*en)++; + } (*en)--; @@ -763,7 +787,9 @@ int param_getptr(const char *line, int *bg, int *en, int paramnum) { int param_getlength(const char *line, int paramnum) { int bg, en; - if (param_getptr(line, &bg, &en, paramnum)) return 0; + if (param_getptr(line, &bg, &en, paramnum)) { + return 0; + } return en - bg + 1; } @@ -775,10 +801,13 @@ char param_getchar(const char *line, int paramnum) { char param_getchar_indx(const char *line, int indx, int paramnum) { int bg, en; - if (param_getptr(line, &bg, &en, paramnum)) return 0x00; + if (param_getptr(line, &bg, &en, paramnum)) { + return 0; + } - if (bg + indx > en) + if (bg + indx > en) { return '\0'; + } return line[bg + indx]; } @@ -795,7 +824,9 @@ uint8_t param_get8(const char *line, int paramnum) { */ uint8_t param_getdec(const char *line, int paramnum, uint8_t *destination) { uint8_t val = param_get8ex(line, paramnum, 255, 10); - if ((int8_t) val == -1) return 1; + if ((int8_t) val == -1) { + return 1; + } (*destination) = val; return 0; } @@ -808,49 +839,56 @@ uint8_t param_getdec(const char *line, int paramnum, uint8_t *destination) { uint8_t param_isdec(const char *line, int paramnum) { int bg, en; //TODO, check more thorougly - if (!param_getptr(line, &bg, &en, paramnum)) return 1; + if (!param_getptr(line, &bg, &en, paramnum)) { + return 1; + } // return strtoul(&line[bg], NULL, 10) & 0xff; - return 0; } uint8_t param_get8ex(const char *line, int paramnum, int deflt, int base) { int bg, en; - if (!param_getptr(line, &bg, &en, paramnum)) + if (param_getptr(line, &bg, &en, paramnum) == 0) { return strtoul(&line[bg], NULL, base) & 0xff; - else + } else { return deflt; + } } uint32_t param_get32ex(const char *line, int paramnum, int deflt, int base) { int bg, en; - if (!param_getptr(line, &bg, &en, paramnum)) + if (param_getptr(line, &bg, &en, paramnum) == 0) { return strtoul(&line[bg], NULL, base); - else + } else { return deflt; + } } uint64_t param_get64ex(const char *line, int paramnum, int deflt, int base) { int bg, en; - if (!param_getptr(line, &bg, &en, paramnum)) + if (param_getptr(line, &bg, &en, paramnum) == 0) { return strtoull(&line[bg], NULL, base); - else + } else { return deflt; + } } float param_getfloat(const char *line, int paramnum, float deflt) { int bg, en; - if (!param_getptr(line, &bg, &en, paramnum)) + if (param_getptr(line, &bg, &en, paramnum) == 0) { return strtof(&line[bg], NULL); - else + } else { return deflt; + } } int param_gethex_ex(const char *line, int paramnum, uint8_t *data, int *hexcnt) { int bg, en, i; uint32_t temp; - if (param_getptr(line, &bg, &en, paramnum)) return 1; + if (param_getptr(line, &bg, &en, paramnum)) { + return 1; + } *hexcnt = en - bg + 1; @@ -860,7 +898,9 @@ int param_gethex_ex(const char *line, int paramnum, uint8_t *data, int *hexcnt) } for (i = 0; i < *hexcnt; i += 2) { - if (!(isxdigit(line[bg + i]) && isxdigit(line[bg + i + 1]))) return 1; + if (!(isxdigit(line[bg + i]) && isxdigit(line[bg + i + 1]))) { + return 1; + } sscanf((char[]) {line[bg + i], line[bg + i + 1], 0}, "%X", &temp); data[i / 2] = temp & 0xff; @@ -873,14 +913,16 @@ int param_gethex_to_eol(const char *line, int paramnum, uint8_t *data, int maxda int bg, en; - if (param_getptr(line, &bg, &en, paramnum)) + if (param_getptr(line, &bg, &en, paramnum)) { return 1; + } *datalen = 0; char buf[5] = {0}; int indx = bg; while (line[indx]) { + if (line[indx] == '\t' || line[indx] == ' ') { indx++; continue; @@ -910,9 +952,10 @@ int param_gethex_to_eol(const char *line, int paramnum, uint8_t *data, int maxda indx++; } - if (strlen(buf) > 0) + if (strlen(buf) > 0) { //error when not completed hex bytes return 3; + } return 0; } @@ -927,6 +970,7 @@ int param_getbin_to_eol(const char *line, int paramnum, uint8_t *data, int maxda char buf[5] = {0}; int indx = bg; while (line[indx]) { + if (line[indx] == '\t' || line[indx] == ' ') { indx++; continue; @@ -992,11 +1036,14 @@ int hextobinarray_n(char *target, char *source, int sourcelen) { char *start = source; // process 4 bits (1 hex digit) at a time while (sourcelen--) { + char x = *(source++); + // capitalize if (x >= 'a' && x <= 'f') { x -= 32; } + // convert to numeric value if (x >= '0' && x <= '9') { x -= '0'; @@ -1006,6 +1053,7 @@ int hextobinarray_n(char *target, char *source, int sourcelen) { PrintAndLogEx(INFO, "(hextobinarray) discovered unknown character %c %d at idx %d of %s", x, x, (int16_t)(source - start), start); return 0; } + // output for (i = 0 ; i < 4 ; ++i, ++count) { *(target++) = (x >> (3 - i)) & 1; @@ -1053,15 +1101,20 @@ int binarray_2_hex(char *target, const size_t targetlen, const char *source, siz uint32_t t = 0; // written target chars uint32_t r = 0; // consumed bits uint8_t w = 0; // wrong bits separator printed + for (size_t s = 0 ; s < srclen; s++) { + if ((source[s] == 0) || (source[s] == 1)) { w = 0; x += (source[s] << (3 - i)); i++; + if (i == 4) { + if (t >= targetlen - 2) { return r; } + snprintf(target + t, targetlen - t, "%X", x); t++; r += 4; @@ -1069,10 +1122,13 @@ int binarray_2_hex(char *target, const size_t targetlen, const char *source, siz i = 0; } } else { + if (i > 0) { + if (t >= targetlen - 5) { return r; } + snprintf(target + t, targetlen - t, "%X[%i]", x, i); t += 4; r += i; @@ -1080,13 +1136,17 @@ int binarray_2_hex(char *target, const size_t targetlen, const char *source, siz i = 0; w = 1; } + if (w == 0) { + if (t >= targetlen - 2) { return r; } + snprintf(target + t, targetlen - t, " "); t++; } + r++; } } @@ -1107,9 +1167,9 @@ int binstr_2_binarray(uint8_t *target, char *source, int length) { while (length--) { char x = *(source++); // convert from binary value - if (x >= '0' && x <= '1') + if (x >= '0' && x <= '1') { x -= '0'; - else { + } else { PrintAndLogEx(WARNING, "(binstring2binarray) discovered unknown character %c %d at idx %d of %s", x, x, (int16_t)(source - start), start); return 0; } @@ -1165,8 +1225,9 @@ void hex_xor_token(uint8_t *d, const uint8_t *x, int dn, int xn) { // return parity bit required to match type uint8_t GetParity(const uint8_t *bits, uint8_t type, int length) { int x; - for (x = 0 ; length > 0 ; --length) + for (x = 0 ; length > 0 ; --length) { x += bits[length - 1]; + } x %= 2; return x ^ type; } @@ -1190,24 +1251,30 @@ void wiegand_add_parity_swapped(uint8_t *target, const uint8_t *source, uint8_t // Pack a bitarray into a uint32_t. uint32_t PackBits(uint8_t start, uint8_t len, const uint8_t *bits) { - if (len > 32) return 0; + if (len > 32) { + return 0; + } int i = start; int j = len - 1; uint32_t tmp = 0; - for (; j >= 0; --j, ++i) + for (; j >= 0; --j, ++i) { tmp |= bits[i] << j; + } return tmp; } uint64_t HornerScheme(uint64_t num, uint64_t divider, uint64_t factor) { + uint64_t remaind = 0, quotient = 0, result = 0; remaind = num % divider; quotient = num / divider; - if (!(quotient == 0 && remaind == 0)) + + if (!(quotient == 0 && remaind == 0)) { result += HornerScheme(quotient, divider, factor) * factor + remaind; + } return result; } @@ -1228,25 +1295,28 @@ int detect_num_CPUs(void) { return sysinfo.dwNumberOfProcessors; #else int count = sysconf(_SC_NPROCESSORS_ONLN); - if (count <= 0) + if (count <= 0) { count = 1; + } return count; #endif } void str_lower(char *s) { - for (size_t i = 0; i < strlen(s); i++) + for (size_t i = 0; i < strlen(s); i++) { s[i] = tolower(s[i]); } +} void str_upper(char *s) { strn_upper(s, strlen(s)); } void strn_upper(char *s, size_t n) { - for (size_t i = 0; i < n; i++) + for (size_t i = 0; i < n; i++) { s[i] = toupper(s[i]); } +} // check for prefix in string bool str_startswith(const char *s, const char *pre) { return strncmp(pre, s, strlen(pre)) == 0; @@ -1265,8 +1335,9 @@ bool str_endswith(const char *s, const char *suffix) { // Replace unprintable characters with a dot in char buffer void clean_ascii(unsigned char *buf, size_t len) { for (size_t i = 0; i < len; i++) { - if (!isprint(buf[i])) + if (isprint(buf[i]) == 0) { buf[i] = '.'; + } } } @@ -1279,11 +1350,11 @@ void str_cleanrn(char *buf, size_t len) { // replace char in buffer void str_creplace(char *buf, size_t len, char from, char to) { for (size_t i = 0; i < len; i++) { - if (buf[i] == from) + if (buf[i] == from) { buf[i] = to; } } - +} char *str_dup(const char *src) { return str_ndup(src, strlen(src)); @@ -1365,8 +1436,9 @@ int binstring_to_u96(uint32_t *hi2, uint32_t *hi, uint32_t *lo, const char *str) for (;;) { int res = sscanf(&str[i], "%1u", &n); - if ((res != 1) || (n > 1)) + if ((res != 1) || (n > 1)) { break; + } *hi2 = (*hi2 << 1) | (*hi >> 31); *hi = (*hi << 1) | (*lo >> 31); @@ -1388,8 +1460,9 @@ int binarray_to_u96(uint32_t *hi2, uint32_t *hi, uint32_t *lo, const uint8_t *ar int i = 0; for (; i < arrlen; i++) { uint8_t n = arr[i]; - if (n > 1) + if (n > 1) { break; + } *hi2 = (*hi2 << 1) | (*hi >> 31); *hi = (*hi << 1) | (*lo >> 31); @@ -1445,17 +1518,20 @@ int byte_strstr(const uint8_t *src, size_t srclen, const uint8_t *pattern, size_ for (size_t i = 0; i < max; i++) { // compare only first byte - if (src[i] != pattern[0]) + if (src[i] != pattern[0]) { continue; + } // try to match rest of the pattern for (int j = plen - 1; j >= 1; j--) { - if (src[i + j] != pattern[j]) + if (src[i + j] != pattern[j]) { break; + } - if (j == 1) + if (j == 1) { return i; + } } } return -1; @@ -1467,17 +1543,20 @@ int byte_strstr(const uint8_t *src, size_t srclen, const uint8_t *pattern, size_ int byte_strrstr(const uint8_t *src, size_t srclen, const uint8_t *pattern, size_t plen) { for (int i = srclen - plen; i >= 0; i--) { // compare only first byte - if (src[i] != pattern[0]) + if (src[i] != pattern[0]) { continue; + } // try to match rest of the pattern for (int j = plen - 1; j >= 1; j--) { - if (src[i + j] != pattern[j]) + if (src[i + j] != pattern[j]) { break; + } - if (j == 1) + if (j == 1) { return i; + } } } return -1; diff --git a/doc/commands.json b/doc/commands.json index 4c349414b..8e00c0031 100644 --- a/doc/commands.json +++ b/doc/commands.json @@ -2064,6 +2064,23 @@ ], "usage": "hf 15 slixprivacyenable [-h] -p " }, + "hf 15 slixprotectpage": { + "command": "hf 15 slixprotectpage", + "description": "Defines protection pointer address of user mem and access cond. for pages", + "notes": [ + "hf 15 slixprotectpage -w deadbeef -p 3 -h 3" + ], + "offline": false, + "options": [ + "-h, --help This help", + "-r, --readpw read password, 4 hex bytes", + "-w, --writepw write password, 4 hex bytes", + "-p, --ptr protection pointer page (0-78), if 0 entire user mem", + "-l, --lo page protection flags of lo page (0-None, 1-ReadPR, 2-WritePR)", + "-i, --hi page protection flags of hi page (0-None, 1-ReadPR, 2-WritePR)" + ], + "usage": "hf 15 slixprotectpage [-h] [-r ] [-w ] [-p ] -l -i " + }, "hf 15 slixwritepwd": { "command": "hf 15 slixwritepwd", "description": "Write a password on a SLIX family ISO-15693 tag.nSome tags do not support all different password types.", @@ -13213,8 +13230,8 @@ } }, "metadata": { - "commands_extracted": 759, + "commands_extracted": 760, "extracted_by": "PM3Help2JSON v1.00", - "extracted_on": "2025-03-11T08:20:44" + "extracted_on": "2025-03-12T08:23:41" } } diff --git a/doc/commands.md b/doc/commands.md index 032f7dd6d..960e40a2b 100644 --- a/doc/commands.md +++ b/doc/commands.md @@ -261,6 +261,7 @@ Check column "offline" for their availability. |`hf 15 slixeasenable `|N |`Enable EAS mode on SLIX ISO-15693 tag` |`hf 15 slixprivacydisable`|N |`Disable privacy mode on SLIX ISO-15693 tag` |`hf 15 slixprivacyenable`|N |`Enable privacy mode on SLIX ISO-15693 tag` +|`hf 15 slixprotectpage `|N |`Protect pages on SLIX ISO-15693 tag` |`hf 15 passprotectafi `|N |`Password protect AFI - Cannot be undone` |`hf 15 passprotecteas `|N |`Password protect EAS - Cannot be undone` |`hf 15 findafi `|N |`Brute force AFI of an ISO-15693 tag` diff --git a/include/pm3_cmd.h b/include/pm3_cmd.h index b93d2b1d6..a71e09a1e 100644 --- a/include/pm3_cmd.h +++ b/include/pm3_cmd.h @@ -1,3 +1,4 @@ +//----------------------------------------------------------------------------- // Copyright (C) Proxmark3 contributors. See AUTHORS.md for details. // // This program is free software: you can redistribute it and/or modify