diff --git a/CHANGELOG.md b/CHANGELOG.md index 8a40af5cc..94ab07612 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,13 +3,34 @@ 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] +- Added Inner range aid and mad entries (@iceman1001) +- Changed `mem spiffs` - Use all available space in SPI flash (@ANTodorov) +- Fixed wrong size check in MifareSim (@iceman1001) +- Fixed `hf mf sim` not to respond to authentication attempts for sectors out of bound for selected Mifare type (@piotrva) +- Added option to build against non-default python3 with CMake as well (@doegox) +- Added option to build against non-default python3 with Makefile (@ANTodorov) +- Changed `hf 14a info` `hf mf info` - now detects FM1216-137 CPU cards (@iceman1001) +- Changed `hf iclass configcard` expanding the list of available options and functionalities (@antiklesys) +- Fixed `intertic.py` - missing comma in array (@iceman1001) +- Added improved algorithm for `hf iclass legrec` leveraging reduced entropy from hash0 constraints (@antiklesys) +- Fixed `hf iclass configcard` when generating elite or keyroll elite configcards for Rev.C legacy readers (@antiklesys) +- Changed `hf mf c*` - now accepts a --gdm flag to write using uscuid/gdm 20/23 alt magic wakeup (@nvx) +- Changed `pm3_console()` - Python/Lua/C: replace `passthru` by `capture` and `quiet` (@doegox) +- Fixed `hf iclass list` - annotation crc handled better (@iceman1001) +- Fixed `hf_mf_uscuid_prog.lua` - bad divisions and code style fixes (@iceman1001) +- Changed `hf iclass info` - now checks for cards silicon version (@antiklesys) +- Changed `hf iclass legrec` - updated script implementation to ensure functionality (@antiklesys) +- Added recovered iclass custom key to dictionary (@antiklesys) +- Added support for all Hitag S response protocol mode (@douniwan5788) +- Fixed 'hf_young.c' - flags declaration was missing a semicolon (@jakkpotts) +- Changed `hf mf sim` - add option to allow key b to be used even if readable (@doegox) - Changed `data num` - outputed binary strings are now properly zero padded (@iceman1001) - Changed `hf iclass info` - now tries default keys and decode if legacy (@iceman1001) - Changed `hf iclass chk` - now loads dictionary file by default (@iceman1001) - Added an Makefile variable `DONT_BUILD_NATIVE` in mfd_aes_brute Makefile to easify downstream package - Auto detect whether compile option `march=native` is supported for mfd_aes_brute Makefile - Changed `hf mf sim` - support data-first and nested reader attacks (@doegox) -- Fixed em4x50_read() - `lf search` and `lf em 4x50 rdbl -b ` does not coredump reading EM4450 tag (@ANTodorov) +- Fixed `lf search` and `lf em 4x50 rdbl -b ` does not coredump reading EM4450 tag (@ANTodorov) - Fixed flashing - client doesnt fail every other flash attempt (@iceman1001) - Changed `pref show` - add option to dump as JSON (@doegox) - Changed `mf_backdoor_dump.py`- use faster ecfill/eview (@doegox) @@ -39,7 +60,7 @@ This project uses the changelog in accordance with [keepchangelog](http://keepac - Changed `lf hitag hts read` -> `lf hitag hts rdbl` to fit rest of client command names (@iceman1001) - Changed `hf mf info` - Better handling when printing ATS (@iceman1001) - Changed to also try the MFC_B key when extracting memory (@iceman1001) -- Fixed parallel `make -j check` Thanks @elboulangero (@iceman1001) +- Fixed `make -j check` Thanks @elboulangero (@iceman1001) - Added support for 8268/8310 (@douniwan5788) - Changed scripting string params to accept 1024 chars, Thanks @evildaemond! (@iceman1001) - Added detection for FM11NT021 (@iceman1001) @@ -1717,7 +1738,7 @@ This project uses the changelog in accordance with [keepchangelog](http://keepac - Added `lf t55xx recoverpw` - adds a new password recovery using bitflips and partial flips if password write went bad. (@alexgrin) - `hf legic` - added improved legic data mapping. (jason) - `hf mf mifare` - added possibility to target key A|B (@douniwan5788) - - Added `analyse lcr` - added a new main command group, to help analysing bytes & bits & nibbles. (@iceman1001) + - Added `analyse lrc` - added a new main command group, to help analysing bytes & bits & nibbles. (@iceman1001) - Added `lf nedap` - added identification of a NEDAP tag. (@iceman1001) - `lf viking clone` - fixed a bug. (@iceman1001) - Added bitsliced bruteforce solver in `hf mf hardnested` (@Aczid) diff --git a/Makefile b/Makefile index 5a07b580a..960fc7557 100644 --- a/Makefile +++ b/Makefile @@ -308,15 +308,15 @@ style: @command -v astyle >/dev/null || ( echo "Please install 'astyle' package first" ; exit 1 ) # Remove spaces & tabs at EOL, add LF at EOF if needed on *.c, *.h, *.cpp. *.lua, *.py, *.pl, Makefile, *.v, pm3 find . \( -not -path "./cov-int/*" -and -not -path "./fpga*/xst/*" -and \( -name "*.[ch]" -or \( -name "*.cpp" -and -not -name "*.moc.cpp" \) -or -name "*.lua" -or -name "*.py" -or -name "*.pl" -or -name "Makefile" -or -name "*.v" -or -name "pm3" \) \) \ - -exec perl -pi -e 's/[ \t]+$$//' {} \; \ - -exec sh -c "tail -c1 {} | xxd -p | tail -1 | grep -q -v 0a$$" \; \ - -exec sh -c "echo >> {}" \; + -exec perl -pi -e 's/[ \t]+$$//' {} \; \ + -exec sh -c "tail -c1 {} | xxd -p | tail -1 | grep -q -v 0a$$" \; \ + -exec sh -c "echo >> {}" \; # Apply astyle on *.c, *.h, *.cpp find . \( -not -path "./cov-int/*" -and \( \( -name "*.[ch]" -and -not -name "ui_overlays.h" \) -or \( -name "*.cpp" -and -not -name "*.moc.cpp" \) \) \) -exec astyle --formatted --mode=c --suffix=none \ - --indent=spaces=4 --indent-switches \ - --keep-one-line-blocks --max-instatement-indent=60 \ - --style=google --pad-oper --unpad-paren --pad-header \ - --align-pointer=name {} \; + --indent=spaces=4 --indent-switches \ + --keep-one-line-blocks --max-continuation-indent=60 \ + --style=google --pad-oper --unpad-paren --pad-header \ + --align-pointer=name {} \; # Update commands.md [ -x client/proxmark3 ] && client/proxmark3 -m | tr -d '\r' > doc/commands.md # Make sure python3 is installed diff --git a/Makefile.defs b/Makefile.defs index 1ef2aa09d..2496057fa 100644 --- a/Makefile.defs +++ b/Makefile.defs @@ -53,7 +53,7 @@ USERMOD = usermod -aG ADDUSER = adduser GETENT_BL = getent group bluetooth - +PYTHON3_PKGCONFIG ?= python3 PATHSEP=/ PREFIX ?= /usr/local diff --git a/armsrc/Standalone/hf_aveful.c b/armsrc/Standalone/hf_aveful.c index 1ca06ddb0..f9122f031 100644 --- a/armsrc/Standalone/hf_aveful.c +++ b/armsrc/Standalone/hf_aveful.c @@ -248,10 +248,11 @@ void RunMod(void) { state = STATE_SEARCH; } } else if (state == STATE_EMUL) { - uint16_t flags = FLAG_7B_UID_IN_DATA; + uint16_t flags = 0; + FLAG_SET_UID_IN_DATA(flags, 7); Dbprintf("Starting simulation, press " _GREEN_("pm3 button") " to stop and go back to search state."); - SimulateIso14443aTag(7, flags, card.uid, 0, NULL); + SimulateIso14443aTag(7, flags, card.uid, 0, NULL, 0); // Go back to search state if user presses pm3-button state = STATE_SEARCH; diff --git a/armsrc/Standalone/hf_cardhopper.c b/armsrc/Standalone/hf_cardhopper.c index 68088a72c..4b4321a37 100644 --- a/armsrc/Standalone/hf_cardhopper.c +++ b/armsrc/Standalone/hf_cardhopper.c @@ -33,7 +33,10 @@ #ifdef CARDHOPPER_USB #define cardhopper_write usb_write #define cardhopper_read usb_read_ng -#define cardhopper_data_available usb_poll_validate_length +bool cardhopper_data_available(void); +bool cardhopper_data_available(void) { + return usb_read_ng_has_buffered_data() || usb_poll_validate_length(); +} #else #define cardhopper_write usart_writebuffer_sync #define cardhopper_read usart_read_ng @@ -107,14 +110,22 @@ void RunMod(void) { break; } - if (memcmp(magicREAD, modeRx.dat, sizeof(magicREAD)) == 0) { + if (modeRx.len == 0) { + DbpString(_CYAN_("[@]") " Zero length message"); + continue; + } + + if (modeRx.len == sizeof(magicREAD) && memcmp(magicREAD, modeRx.dat, sizeof(magicREAD)) == 0) { DbpString(_CYAN_("[@]") " I am a READER. I talk to a CARD."); become_reader(); - } else if (memcmp(magicCARD, modeRx.dat, sizeof(magicCARD)) == 0) { + } else if (modeRx.len == sizeof(magicCARD) && memcmp(magicCARD, modeRx.dat, sizeof(magicCARD)) == 0) { DbpString(_CYAN_("[@]") " I am a CARD. I talk to a READER."); become_card(); - } else if (memcmp(magicEND, modeRx.dat, sizeof(magicEND)) == 0) { + } else if (modeRx.len == sizeof(magicEND) && memcmp(magicEND, modeRx.dat, sizeof(magicEND)) == 0) { break; + } else if (modeRx.len == sizeof(magicRSRT) && memcmp(magicRSRT, modeRx.dat, sizeof(magicRSRT)) == 0) { + DbpString(_CYAN_("[@]") " Got RESET but already reset."); + continue; } else { DbpString(_YELLOW_("[!]") " unknown mode!"); Dbhexdump(modeRx.len, modeRx.dat, true); @@ -142,7 +153,12 @@ static void become_reader(void) { WDT_HIT(); read_packet(rx); - if (memcmp(magicRSRT, rx->dat, sizeof(magicRSRT)) == 0) break; + if (rx->len == sizeof(magicRSRT) && memcmp(magicRSRT, rx->dat, sizeof(magicRSRT)) == 0) break; + + if (BUTTON_PRESS()) { + DbpString(_CYAN_("[@]") " Button pressed - Breaking from reader loop"); + break; + } if (rx->dat[0] == ISO14443A_CMD_RATS && rx->len == 4) { // got RATS from reader, reset the card @@ -206,7 +222,7 @@ static void become_card(void) { iso14443a_setup(FPGA_HF_ISO14443A_TAGSIM_LISTEN); uint8_t tagType; - uint16_t flags; + uint16_t flags = 0; uint8_t data[PM3_CMD_DATA_SIZE] = { 0 }; packet_t ats = { 0 }; prepare_emulation(&tagType, &flags, data, &ats); @@ -297,7 +313,7 @@ static void prepare_emulation(uint8_t *tagType, uint16_t *flags, uint8_t *data, } memcpy(data, uidRx.dat, uidRx.len); - *flags = (uidRx.len == 10 ? FLAG_10B_UID_IN_DATA : (uidRx.len == 7 ? FLAG_7B_UID_IN_DATA : FLAG_4B_UID_IN_DATA)); + FLAG_SET_UID_IN_DATA(*flags, uidRx.len); DbpString(_CYAN_("[@]") " UID:"); Dbhexdump(uidRx.len, data, false); Dbprintf(_CYAN_("[@]") " Flags: %hu", *flags); @@ -476,7 +492,7 @@ static void read_packet(packet_t *packet) { if (packet->len == 0x50 && dataReceived >= sizeof(PacketResponseNGPreamble) && packet->dat[0] == 0x4D && packet->dat[1] == 0x33 && packet->dat[2] == 0x61) { // PM3 NG packet magic - DbpString(_CYAN_("[@]") " PM3 NG packet recieved - ignoring"); + DbpString(_CYAN_("[@]") " PM3 NG packet received - ignoring"); // clear any remaining buffered data while (cardhopper_data_available()) { diff --git a/armsrc/Standalone/hf_colin.c b/armsrc/Standalone/hf_colin.c index 143f97c4f..ad45cda1a 100644 --- a/armsrc/Standalone/hf_colin.c +++ b/armsrc/Standalone/hf_colin.c @@ -717,33 +717,10 @@ readysim: SpinOff(100); LED_C_ON(); - /* uint16_t flags = 0; - switch (colin_p_card.uidlen) { - case 10: - flags = FLAG_10B_UID_IN_DATA; - break; - case 7: - flags = FLAG_7B_UID_IN_DATA; - break; - case 4: - flags = FLAG_4B_UID_IN_DATA; - break; - default: - flags = FLAG_UID_IN_EMUL; - break; - } - // Use UID, SAK, ATQA from EMUL, if uid not defined - if ((flags & (FLAG_4B_UID_IN_DATA | FLAG_7B_UID_IN_DATA | FLAG_10B_UID_IN_DATA)) == 0) { - flags |= FLAG_UID_IN_EMUL; - } - flags |= FLAG_MF_1K; - if ((flags & (FLAG_4B_UID_IN_DATA | FLAG_7B_UID_IN_DATA | FLAG_10B_UID_IN_DATA)) == 0) { - flags |= FLAG_UID_IN_EMUL; - } - flags = 0x10; - */ - uint16_t flags = FLAG_UID_IN_EMUL; +// FLAG_SET_UID_IN_DATA(flags, colin_p_card.uidlen); + FLAG_SET_UID_IN_EMUL(flags); + FLAG_SET_MF_SIZE(flags, MIFARE_1K_MAX_BYTES); DbprintfEx(FLAG_NEWLINE, "\n\n\n\n\n\n\n\nn\n\nn\n\n\nflags: %d (0x%02x)", flags, flags); cjSetCursLeft(); SpinOff(1000); diff --git a/armsrc/Standalone/hf_craftbyte.c b/armsrc/Standalone/hf_craftbyte.c index 06d032f27..6eb2ae2a2 100644 --- a/armsrc/Standalone/hf_craftbyte.c +++ b/armsrc/Standalone/hf_craftbyte.c @@ -79,13 +79,8 @@ void RunMod(void) { } } else if (state == STATE_EMUL) { uint16_t flags = 0; - if (card.uidlen == 4) { - flags |= FLAG_4B_UID_IN_DATA; - } else if (card.uidlen == 7) { - flags |= FLAG_7B_UID_IN_DATA; - } else if (card.uidlen == 10) { - flags |= FLAG_10B_UID_IN_DATA; - } else { + FLAG_SET_UID_IN_DATA(flags, card.uidlen); + if (IS_FLAG_UID_IN_EMUL(flags)) { Dbprintf("Unusual UID length, something is wrong. Try again please."); state = STATE_READ; continue; @@ -94,22 +89,22 @@ void RunMod(void) { Dbprintf("Starting simulation, press " _GREEN_("pm3 button") " to stop and go back to search state."); if (card.sak == 0x08 && card.atqa[0] == 0x04 && card.atqa[1] == 0) { DbpString("Mifare Classic 1k"); - SimulateIso14443aTag(1, flags, card.uid, 0, NULL); + SimulateIso14443aTag(1, flags, card.uid, 0, NULL, 0); } else if (card.sak == 0x08 && card.atqa[0] == 0x44 && card.atqa[1] == 0) { DbpString("Mifare Classic 4k "); - SimulateIso14443aTag(8, flags, card.uid, 0, NULL); + SimulateIso14443aTag(8, flags, card.uid, 0, NULL, 0); } else if (card.sak == 0x00 && card.atqa[0] == 0x44 && card.atqa[1] == 0) { DbpString("Mifare Ultralight"); - SimulateIso14443aTag(2, flags, card.uid, 0, NULL); + SimulateIso14443aTag(2, flags, card.uid, 0, NULL, 0); } else if (card.sak == 0x20 && card.atqa[0] == 0x04 && card.atqa[1] == 0x03) { DbpString("Mifare DESFire"); - SimulateIso14443aTag(3, flags, card.uid, 0, NULL); + SimulateIso14443aTag(3, flags, card.uid, 0, NULL, 0); } else if (card.sak == 0x20 && card.atqa[0] == 0x44 && card.atqa[1] == 0x03) { DbpString("Mifare DESFire Ev1/Plus/JCOP"); - SimulateIso14443aTag(3, flags, card.uid, 0, NULL); + SimulateIso14443aTag(3, flags, card.uid, 0, NULL, 0); } else { Dbprintf("Unrecognized tag type -- defaulting to Mifare Classic emulation"); - SimulateIso14443aTag(1, flags, card.uid, 0, NULL); + SimulateIso14443aTag(1, flags, card.uid, 0, NULL, 0); } // Go back to search state if user presses pm3-button diff --git a/armsrc/Standalone/hf_mattyrun.c b/armsrc/Standalone/hf_mattyrun.c index c899aefe2..ad1e6b863 100644 --- a/armsrc/Standalone/hf_mattyrun.c +++ b/armsrc/Standalone/hf_mattyrun.c @@ -556,19 +556,7 @@ void RunMod(void) { } uint16_t simflags = 0; - switch (mattyrun_card.uidlen) { - case 4: - simflags |= FLAG_4B_UID_IN_DATA; - break; - case 7: - simflags |= FLAG_7B_UID_IN_DATA; - break; - case 10: - simflags |= FLAG_10B_UID_IN_DATA; - break; - default: - break; - } + FLAG_SET_UID_IN_DATA(simflags, mattyrun_card.uidlen); uint16_t atqa = (uint16_t)bytes_to_num(mattyrun_card.atqa, 2); SpinDelay(1000); diff --git a/armsrc/Standalone/hf_mfcsim.c b/armsrc/Standalone/hf_mfcsim.c index bba01d472..667696f86 100644 --- a/armsrc/Standalone/hf_mfcsim.c +++ b/armsrc/Standalone/hf_mfcsim.c @@ -143,7 +143,9 @@ void RunMod(void) { //Start to simulate Dbprintf(_YELLOW_("[Slot: %d] Simulation start, Press button to change next card."), i); - uint16_t simflags = FLAG_UID_IN_EMUL | FLAG_MF_1K; + uint16_t simflags = 0; + FLAG_SET_MF_SIZE(simflags, MIFARE_1K_MAX_BYTES); + FLAG_SET_UID_IN_EMUL(simflags); Mifare1ksim(simflags, 0, NULL, 0, 0); Dbprintf(_YELLOW_("[Slot: %d] Simulation end, Write Back to dump file!"), i); diff --git a/armsrc/Standalone/hf_msdsal.c b/armsrc/Standalone/hf_msdsal.c index 93848b986..c26ef07de 100644 --- a/armsrc/Standalone/hf_msdsal.c +++ b/armsrc/Standalone/hf_msdsal.c @@ -209,7 +209,8 @@ void RunMod(void) { bool chktoken = false; // UID 4 bytes(could be 7 bytes if needed it) - uint8_t flags = FLAG_4B_UID_IN_DATA; + uint8_t flags = 0; + FLAG_SET_UID_IN_DATA(flags, 4); // in case there is a read command received we shouldn't break uint8_t data[PM3_CMD_DATA_SIZE] = {0x00}; diff --git a/armsrc/Standalone/hf_reblay.c b/armsrc/Standalone/hf_reblay.c index 0655e1d92..d9f60abcf 100644 --- a/armsrc/Standalone/hf_reblay.c +++ b/armsrc/Standalone/hf_reblay.c @@ -81,7 +81,8 @@ void RunMod() { // UID 4 bytes(could be 7 bytes if needed it) - uint8_t flags = FLAG_4B_UID_IN_DATA; + uint8_t flags = 0; + FLAG_SET_UID_IN_DATA(flags, 4); // in case there is a read command received we shouldn't break uint8_t data[PM3_CMD_DATA_SIZE] = {0x00}; diff --git a/armsrc/Standalone/hf_tcprst.c b/armsrc/Standalone/hf_tcprst.c index c07bd1de8..8fb8621b3 100644 --- a/armsrc/Standalone/hf_tcprst.c +++ b/armsrc/Standalone/hf_tcprst.c @@ -110,7 +110,8 @@ void RunMod(void) { #define DYNAMIC_RESPONSE_BUFFER_SIZE 64 #define DYNAMIC_MODULATION_BUFFER_SIZE 512 - uint8_t flags = FLAG_7B_UID_IN_DATA; // ST25TA have 7B UID + uint8_t flags = 0; + FLAG_SET_UID_IN_DATA(flags, 7); // ST25TA have 7B UID uint8_t data[PM3_CMD_DATA_SIZE] = {0x00}; // in case there is a read command received we shouldn't break // to initialize the emulation diff --git a/armsrc/Standalone/hf_young.c b/armsrc/Standalone/hf_young.c index aefc6da71..62d215dba 100644 --- a/armsrc/Standalone/hf_young.c +++ b/armsrc/Standalone/hf_young.c @@ -55,7 +55,7 @@ void RunMod(void) { card_clone_t uids[OPTS]; iso14a_card_select_t card[OPTS]; - uint8_t params = (MAGIC_SINGLE | MAGIC_DATAIN); + uint8_t params = (MAGIC_SINGLE | MAGIC_WUPC | MAGIC_DATAIN); LED(selected + 1, 0); @@ -177,14 +177,14 @@ void RunMod(void) { MifareCGetBlock(c->arg[0], c->arg[1], c->d.asBytes); break; - mfCSetUID provides example logic for UID set workflow: + mf_chinese_set_uid provides example logic for UID set workflow: -Read block0 from card in field with MifareCGetBlock() -Configure new values without replacing reserved bytes memcpy(block0, uid, 4); // Copy UID bytes from byte array // Mifare UID BCC block0[4] = block0[0]^block0[1]^block0[2]^block0[3]; // BCC on byte 5 Bytes 5-7 are reserved SAK and ATQA for mifare classic - -Use mfCSetBlock(0, block0, oldUID, wantWipe, MAGIC_SINGLE) to write it + -Use mf_chinese_set_block(0, block0, oldUID, wantWipe, MAGIC_SINGLE | MAGIC_WUPC) to write it */ uint8_t oldBlock0[16] = {0}, newBlock0[16] = {0}; // arg0 = Flags, arg1=blockNo @@ -236,7 +236,8 @@ void RunMod(void) { int button_pressed = BUTTON_HELD(1000); if (button_pressed == BUTTON_NO_CLICK) { // No button action, proceed with sim - uint16_t flags = FLAG_4B_UID_IN_DATA; + uint16_t flags = 0; + FLAG_SET_UID_IN_DATA(flags, 4); uint8_t data[PM3_CMD_DATA_SIZE] = {0}; // in case there is a read command received we shouldn't break memcpy(data, uids[selected].uid, uids[selected].uidlen); @@ -244,7 +245,7 @@ void RunMod(void) { uint64_t tmpuid = bytes_to_num(uids[selected].uid, uids[selected].uidlen); if (uids[selected].uidlen == 7) { - flags = FLAG_7B_UID_IN_DATA; + FLAG_SET_UID_IN_DATA(flags, 7); Dbprintf("Simulating ISO14443a tag with uid: %014" PRIx64 " [Bank: %d]", tmpuid, selected); } else { Dbprintf("Simulating ISO14443a tag with uid: %08" PRIx64 " [Bank: %d]", tmpuid, selected); @@ -252,25 +253,25 @@ void RunMod(void) { if (uids[selected].sak == 0x08 && uids[selected].atqa[0] == 0x04 && uids[selected].atqa[1] == 0) { DbpString("Mifare Classic 1k"); - SimulateIso14443aTag(1, flags, data, 0, NULL); + SimulateIso14443aTag(1, flags, data, 0, NULL, 0); } else if (uids[selected].sak == 0x18 && uids[selected].atqa[0] == 0x02 && uids[selected].atqa[1] == 0) { DbpString("Mifare Classic 4k (4b uid)"); - SimulateIso14443aTag(8, flags, data, 0, NULL); + SimulateIso14443aTag(8, flags, data, 0, NULL, 0); } else if (uids[selected].sak == 0x08 && uids[selected].atqa[0] == 0x44 && uids[selected].atqa[1] == 0) { DbpString("Mifare Classic 4k (7b uid)"); - SimulateIso14443aTag(8, flags, data, 0, NULL); + SimulateIso14443aTag(8, flags, data, 0, NULL, 0); } else if (uids[selected].sak == 0x00 && uids[selected].atqa[0] == 0x44 && uids[selected].atqa[1] == 0) { DbpString("Mifare Ultralight"); - SimulateIso14443aTag(2, flags, data, 0, NULL); + SimulateIso14443aTag(2, flags, data, 0, NULL, 0); } else if (uids[selected].sak == 0x20 && uids[selected].atqa[0] == 0x04 && uids[selected].atqa[1] == 0x03) { DbpString("Mifare DESFire"); - SimulateIso14443aTag(3, flags, data, 0, NULL); + SimulateIso14443aTag(3, flags, data, 0, NULL, 0); } else if (uids[selected].sak == 0x20 && uids[selected].atqa[0] == 0x44 && uids[selected].atqa[1] == 0x03) { DbpString("Mifare DESFire Ev1/Plus/JCOP"); - SimulateIso14443aTag(3, flags, data, 0, NULL); + SimulateIso14443aTag(3, flags, data, 0, NULL, 0); } else { Dbprintf("Unrecognized tag type -- defaulting to Mifare Classic emulation"); - SimulateIso14443aTag(1, flags, data, 0, NULL); + SimulateIso14443aTag(1, flags, data, 0, NULL, 0); } } else if (button_pressed == BUTTON_SINGLE_CLICK) { diff --git a/armsrc/appmain.c b/armsrc/appmain.c index e1fad8c89..060425bfc 100644 --- a/armsrc/appmain.c +++ b/armsrc/appmain.c @@ -1655,7 +1655,8 @@ static void PacketReceived(PacketCommandNG *packet) { uint8_t rats[20]; } PACKED; struct p *payload = (struct p *) packet->data.asBytes; - SimulateIso14443aTag(payload->tagtype, payload->flags, payload->uid, payload->exitAfter, payload->rats); // ## Simulate iso14443a tag - pass tag type & UID + SimulateIso14443aTag(payload->tagtype, payload->flags, payload->uid, + payload->exitAfter, payload->rats, sizeof(payload->rats)); // ## Simulate iso14443a tag - pass tag type & UID break; } case CMD_HF_ISO14443A_SIM_AID: { @@ -1673,7 +1674,10 @@ static void PacketReceived(PacketCommandNG *packet) { bool enumerate; } PACKED; struct p *payload = (struct p *) packet->data.asBytes; - SimulateIso14443aTagAID(payload->tagtype, payload->flags, payload->uid, payload->rats, payload->aid, payload->response, payload->apdu, payload->aid_len, payload->respond_len, payload->apdu_len, payload->enumerate); // ## Simulate iso14443a tag - pass tag type, UID, rats, aid, resp, apdu + SimulateIso14443aTagAID(payload->tagtype, payload->flags, payload->uid, + payload->rats, sizeof(payload->rats), payload->aid, payload->response, + payload->apdu, payload->aid_len, payload->respond_len, + payload->apdu_len, payload->enumerate); // ## Simulate iso14443a tag - pass tag type, UID, rats, aid, resp, apdu break; } case CMD_HF_ISO14443A_ANTIFUZZ: { @@ -2762,11 +2766,11 @@ static void PacketReceived(PacketCommandNG *packet) { break; } - if (payload->startidx == DEFAULT_T55XX_KEYS_OFFSET) { + if (payload->startidx == DEFAULT_T55XX_KEYS_OFFSET_P(spi_flash_p64k)) { Flash_CheckBusy(BUSY_TIMEOUT); Flash_WriteEnable(); Flash_Erase4k(3, 0xC); - } else if (payload->startidx == DEFAULT_MF_KEYS_OFFSET) { + } else if (payload->startidx == DEFAULT_MF_KEYS_OFFSET_P(spi_flash_p64k)) { Flash_CheckBusy(BUSY_TIMEOUT); Flash_WriteEnable(); Flash_Erase4k(3, 0x8); @@ -2776,11 +2780,11 @@ static void PacketReceived(PacketCommandNG *packet) { Flash_CheckBusy(BUSY_TIMEOUT); Flash_WriteEnable(); Flash_Erase4k(3, 0xA); - } else if (payload->startidx == DEFAULT_ICLASS_KEYS_OFFSET) { + } else if (payload->startidx == DEFAULT_ICLASS_KEYS_OFFSET_P(spi_flash_p64k)) { Flash_CheckBusy(BUSY_TIMEOUT); Flash_WriteEnable(); Flash_Erase4k(3, 0xB); - } else if (payload->startidx == FLASH_MEM_SIGNATURE_OFFSET) { + } else if (payload->startidx == FLASH_MEM_SIGNATURE_OFFSET_P(spi_flash_p64k)) { Flash_CheckBusy(BUSY_TIMEOUT); Flash_WriteEnable(); Flash_Erase4k(3, 0xF); @@ -2803,7 +2807,7 @@ static void PacketReceived(PacketCommandNG *packet) { LED_B_OFF(); break; } - if (page < 3) { + if (page < spi_flash_p64k-1) { isok = Flash_WipeMemoryPage(page); // let spiffs check and update its info post flash erase rdv40_spiffs_check(); @@ -2850,7 +2854,7 @@ static void PacketReceived(PacketCommandNG *packet) { LED_B_ON(); rdv40_validation_t *info = (rdv40_validation_t *)BigBuf_malloc(sizeof(rdv40_validation_t)); - bool isok = Flash_ReadData(FLASH_MEM_SIGNATURE_OFFSET, info->signature, FLASH_MEM_SIGNATURE_LEN); + bool isok = Flash_ReadData(FLASH_MEM_SIGNATURE_OFFSET_P(spi_flash_p64k), info->signature, FLASH_MEM_SIGNATURE_LEN); if (FlashInit()) { Flash_UniqueID(info->flashid); @@ -2859,6 +2863,23 @@ static void PacketReceived(PacketCommandNG *packet) { reply_mix(CMD_ACK, isok, 0, 0, info, sizeof(rdv40_validation_t)); BigBuf_free(); + LED_B_OFF(); + break; + } + case CMD_FLASHMEM_PAGES64K: { + + LED_B_ON(); + + bool isok = false; + if (FlashInit()) { + isok = true; + if (g_dbglevel >= DBG_DEBUG) { + Dbprintf(" CMD_FLASHMEM_PAGE64K 0x%02x (%d 64k pages)", spi_flash_p64k, spi_flash_p64k); + } + FlashStop(); + } + reply_mix(CMD_ACK, isok, 0, 0, &spi_flash_p64k, sizeof(uint8_t)); + LED_B_OFF(); break; } diff --git a/armsrc/hitagS.c b/armsrc/hitagS.c index edee23a4d..b247d802a 100644 --- a/armsrc/hitagS.c +++ b/armsrc/hitagS.c @@ -65,12 +65,13 @@ typedef enum modulation { MC8K } MOD; +static uint8_t protocol_mode = HITAGS_UID_REQ_ADV1; static MOD m = AC2K; // used modulation static uint32_t reader_selected_uid; static int rotate_uid = 0; static int sof_bits; // number of start-of-frame bits static uint8_t pwdh0, pwdl0, pwdl1; // password bytes -static uint8_t rnd[] = {0x74, 0x12, 0x44, 0x85}; // random number +static uint8_t rnd[] = {0x85, 0x44, 0x12, 0x74}; // random number static uint16_t timestamp_high = 0; // Timer Counter 2 overflow count, ~47min #define TIMESTAMP (AT91C_BASE_TC2->TC_SR &AT91C_TC_COVFS ? timestamp_high += 1 : 0, ((timestamp_high << 16) + AT91C_BASE_TC2->TC_CV) / T0) @@ -178,7 +179,7 @@ static void hitag_send_bit(int bit, bool ledcontrol) { while (AT91C_BASE_TC0->TC_CV < T0 * 32) {}; LOW(GPIO_SSC_DOUT); - while (AT91C_BASE_TC0->TC_CV < T0 * 64) {}; + while (AT91C_BASE_TC0->TC_CV < T0 * 64) {}; } else { // AC coding -_-_ @@ -310,7 +311,7 @@ static void hitag_reader_send_bit(int bit, bool ledcontrol) { if (ledcontrol) LED_A_ON(); // Reset clock for the next bit AT91C_BASE_TC0->TC_CCR = AT91C_TC_SWTRG; - while (AT91C_BASE_TC0->TC_CV != 0); + while (AT91C_BASE_TC0->TC_CV != 0) {}; // Binary puls length modulation (BPLM) is used to encode the data stream // This means that a transmission of a one takes longer than that of a zero @@ -355,7 +356,8 @@ static void hitag_reader_send_frame(const uint8_t *frame, size_t frame_len, bool } // send EOF AT91C_BASE_TC0->TC_CCR = AT91C_TC_SWTRG; - while (AT91C_BASE_TC0->TC_CV != 0); + while (AT91C_BASE_TC0->TC_CV != 0) {}; + HIGH(GPIO_SSC_DOUT); // Wait for 4-10 times the carrier period @@ -371,7 +373,6 @@ static void hts_stop_clock(void) { } static void hts_init_clock(void) { - // Enable Peripheral Clock for // Timer Counter 0, used to measure exact timing before answering // Timer Counter 1, used to capture edges of the tag frames @@ -383,18 +384,17 @@ static void hts_init_clock(void) { // Disable timer during configuration hts_stop_clock(); - // TC0: Capture mode, clock source = MCK/32 (TIMER_CLOCK3), no triggers + // TC0: Capture mode, default timer source = MCK/32 (TIMER_CLOCK3), no triggers AT91C_BASE_TC0->TC_CMR = AT91C_TC_CLKS_TIMER_DIV3_CLOCK; - // TC1: Capture mode, clock source = MCK/32 (TIMER_CLOCK3), TIOA is external trigger, - // external trigger falling edge, set RA on falling edge of TIOA. - AT91C_BASE_TC1->TC_CMR = - AT91C_TC_CLKS_TIMER_DIV3_CLOCK | // MCK/32 (TIMER_CLOCK3) - AT91C_TC_ETRGEDG_FALLING | // external trigger on falling edge - AT91C_TC_ABETRG | // TIOA is used as an external trigger - AT91C_TC_LDRA_FALLING; // load RA on on falling edge + // TC1: Capture mode, default timer source = MCK/32 (TIMER_CLOCK3), TIOA is external trigger, + AT91C_BASE_TC1->TC_CMR = AT91C_TC_CLKS_TIMER_DIV3_CLOCK // use MCK/32 (TIMER_CLOCK3) + | AT91C_TC_ABETRG // TIOA is used as an external trigger + | AT91C_TC_ETRGEDG_FALLING // external trigger on falling edge + | AT91C_TC_LDRA_RISING // load RA on on rising edge of TIOA + | AT91C_TC_LDRB_FALLING; // load RB on on falling edge of TIOA - // TC2: Capture mode, clock source = MCK/32 (TIMER_CLOCK3), no triggers + // TC2: Capture mode, default timer source = MCK/32 (TIMER_CLOCK3), no triggers AT91C_BASE_TC2->TC_CMR = AT91C_TC_CLKS_TIMER_DIV3_CLOCK; // Enable and reset counters @@ -407,7 +407,7 @@ static void hts_init_clock(void) { // synchronized startup procedure // In theory, with MCK/32, we shouldn't be waiting longer than 32 instruction statements, right? - while (AT91C_BASE_TC0->TC_CV != 0) {}; // wait until TC0 returned to zero + while (AT91C_BASE_TC0->TC_CV != 0) {}; // wait until TC0 returned to zero // reset timestamp timestamp_high = 0; @@ -429,24 +429,35 @@ static int check_select(const uint8_t *rx, uint32_t uid) { return 0; } -static void hts_set_frame_modulation(void) { - switch (tag.mode) { - case HT_STANDARD: { +static void hts_set_frame_modulation(uint8_t mode, bool ac_seq) { + switch (mode) { + case HITAGS_UID_REQ_STD: { sof_bits = 1; - m = MC4K; + if (ac_seq) + m = AC2K; + else + m = MC4K; break; } - case HT_ADVANCED: { - sof_bits = 6; - m = MC4K; + case HITAGS_UID_REQ_ADV1: + case HITAGS_UID_REQ_ADV2: { + if (ac_seq) { + sof_bits = 3; + m = AC2K; + } else { + sof_bits = 6; + m = MC4K; + } break; } - case HT_FAST_ADVANCED: { - sof_bits = 6; - m = MC8K; - break; - } - default: { + case HITAGS_UID_REQ_FADV: { + if (ac_seq) { + sof_bits = 3; + m = AC4K; + } else { + sof_bits = 6; + m = MC8K; + } break; } } @@ -463,7 +474,7 @@ static void hts_handle_reader_command(uint8_t *rx, const size_t rxlen, // Reset the transmission frame length *txlen = 0; // Reset the frame modulation - hts_set_frame_modulation(); + hts_set_frame_modulation(protocol_mode, false); // Try to find out which command was send by selecting on length (in bits) switch (rxlen) { @@ -475,24 +486,15 @@ static void hts_handle_reader_command(uint8_t *rx, const size_t rxlen, if (rx[0] == HITAGS_UID_REQ_STD) { DBG Dbprintf("HT_STANDARD"); - tag.mode = HT_STANDARD; - sof_bits = 1; - m = AC2K; - } - - if (rx[0] == HITAGS_UID_REQ_ADV1 || rx[0] == HITAGS_UID_REQ_ADV2) { + } else if (rx[0] == HITAGS_UID_REQ_ADV1 || rx[0] == HITAGS_UID_REQ_ADV2) { DBG Dbprintf("HT_ADVANCED"); - tag.mode = HT_ADVANCED; - sof_bits = 3; - m = AC2K; + } else if (rx[0] == HITAGS_UID_REQ_FADV) { + DBG Dbprintf("HT_FAST_ADVANCED"); } - if (rx[0] == HITAGS_UID_REQ_FADV) { - DBG Dbprintf("HT_FAST_ADVANCED"); - tag.mode = HT_FAST_ADVANCED; - sof_bits = 3; - m = AC4K; - } + protocol_mode = rx[0]; + hts_set_frame_modulation(protocol_mode, true); + //send uid as a response *txlen = 32; memcpy(tx, tag.data.pages[HITAGS_UID_PADR], HITAGS_PAGE_SIZE); @@ -515,7 +517,7 @@ static void hts_handle_reader_command(uint8_t *rx, const size_t rxlen, tx[3] = 0xff; - if (tag.mode != HT_STANDARD) { + if (protocol_mode != HITAGS_UID_REQ_STD) { //add crc8 *txlen += 8; crc = CRC_PRESET; @@ -536,7 +538,7 @@ static void hts_handle_reader_command(uint8_t *rx, const size_t rxlen, rotate_uid++; *txlen = 32; // init crypt engine - state = ht2_hitag2_init(reflect48(tag.data.s.key), reflect32(tag.data.s.uid_le), reflect32(*(uint32_t *)rx)); + state = ht2_hitag2_init(REV64(tag.data.s.key), REV32(tag.data.s.uid_le), REV32(*(uint32_t *)rx)); DBG Dbhexdump(8, tx, false); for (int i = 0; i < 4; i++) { @@ -549,7 +551,7 @@ static void hts_handle_reader_command(uint8_t *rx, const size_t rxlen, tx[2] = ht2_hitag2_byte(&state) ^ tag.data.s.pwdl0; tx[3] = ht2_hitag2_byte(&state) ^ tag.data.s.pwdl1; - if (tag.mode != HT_STANDARD) { + if (protocol_mode != HITAGS_UID_REQ_STD) { //add crc8 *txlen += 8; crc = CRC_PRESET; @@ -616,7 +618,7 @@ static void hts_handle_reader_command(uint8_t *rx, const size_t rxlen, tx[3] = 0xFF; } - if (tag.mode != HT_STANDARD) { + if (protocol_mode != HITAGS_UID_REQ_STD) { //add crc8 *txlen += 8; crc = CRC_PRESET; @@ -638,7 +640,7 @@ static void hts_handle_reader_command(uint8_t *rx, const size_t rxlen, //send page,...,page+3 data memcpy(tx, tag.data.pages[page], *txlen / 8); - if (tag.mode != HT_STANDARD) { + if (protocol_mode != HITAGS_UID_REQ_STD) { //add crc8 crc = CRC_PRESET; for (int i = 0; i < *txlen / 8; i++) { @@ -751,40 +753,7 @@ void hts_simulate(bool tag_mem_supplied, const uint8_t *data, bool ledcontrol) { // Disable modulation at default, which means release resistance LOW(GPIO_SSC_DOUT); - // Enable Peripheral Clock for - // Timer Counter 0, used to measure exact timing before answering - // Timer Counter 1, used to capture edges of the tag frames - // Timer Counter 2, used to log trace time - AT91C_BASE_PMC->PMC_PCER |= (1 << AT91C_ID_TC0) | (1 << AT91C_ID_TC1) | (1 << AT91C_ID_TC2); - - AT91C_BASE_PIOA->PIO_BSR = GPIO_SSC_FRAME; - - // Disable timer during configuration - hts_stop_clock(); - - // TC0: Capture mode, default timer source = MCK/32 (TIMER_CLOCK3), no triggers - AT91C_BASE_TC0->TC_CMR = AT91C_TC_CLKS_TIMER_DIV3_CLOCK; - - // TC1: Capture mode, default timer source = MCK/32 (TIMER_CLOCK3), TIOA is external trigger, - // external trigger rising edge, load RA on rising edge of TIOA. - AT91C_BASE_TC1->TC_CMR = AT91C_TC_CLKS_TIMER_DIV3_CLOCK - | AT91C_TC_ETRGEDG_RISING | AT91C_TC_ABETRG | AT91C_TC_LDRA_RISING; - // TC2: Capture mode, default timer source = MCK/32 (TIMER_CLOCK3), no triggers - AT91C_BASE_TC2->TC_CMR = AT91C_TC_CLKS_TIMER_DIV3_CLOCK; - - // Enable and reset counter - AT91C_BASE_TC0->TC_CCR = AT91C_TC_CLKEN | AT91C_TC_SWTRG; - AT91C_BASE_TC1->TC_CCR = AT91C_TC_CLKEN | AT91C_TC_SWTRG; - AT91C_BASE_TC2->TC_CCR = AT91C_TC_CLKEN | AT91C_TC_SWTRG; - - // Assert a sync signal. This sets all timers to 0 on next active clock edge - AT91C_BASE_TCB->TCB_BCR = 1; - - // synchronized startup procedure - while (AT91C_BASE_TC0->TC_CV != 0); // wait until TC0 returned to zero - - // reset timestamp - timestamp_high = 0; + hts_init_clock(); if (ledcontrol) LED_D_ON(); @@ -800,7 +769,7 @@ void hts_simulate(bool tag_mem_supplied, const uint8_t *data, bool ledcontrol) { if (AT91C_BASE_TC1->TC_SR & AT91C_TC_LDRAS) { // Retrieve the new timing values - int ra = (AT91C_BASE_TC1->TC_RA / T0) + overflow; + int rb = (AT91C_BASE_TC1->TC_RB / T0) + overflow; overflow = 0; // Reset timer every frame, we have to capture the last edge for timing @@ -811,15 +780,15 @@ void hts_simulate(bool tag_mem_supplied, const uint8_t *data, bool ledcontrol) { if (start_time == 0) start_time = TIMESTAMP - HITAG_T_LOW; // Capture reader frame - if (ra >= HITAG_T_STOP) { + if (rb >= HITAG_T_STOP) { if (rxlen != 0) { //DbpString("weird0?"); } - } else if (ra >= HITAG_T_1_MIN) { + } else if (rb >= HITAG_T_1_MIN) { // '1' bit rx[rxlen / 8] |= 1 << (7 - (rxlen % 8)); rxlen++; - } else if (ra >= HITAG_T_0_MIN) { + } else if (rb >= HITAG_T_0_MIN) { // '0' bit rx[rxlen / 8] |= 0 << (7 - (rxlen % 8)); rxlen++; @@ -890,21 +859,29 @@ static void hts_receive_frame(uint8_t *rx, size_t sizeofrx, size_t *rxlen, uint3 bool bSkip = true; uint32_t errorCount = 0; bool bStarted = false; + uint16_t next_edge_event = AT91C_TC_LDRBS; + int double_speed = (m == AC4K || m == MC8K) ? 2 : 1; - uint32_t ra_i = 0, h2 = 0, h3 = 0, h4 = 0; + uint32_t rb_i = 0, h2 = 0, h3 = 0, h4 = 0; uint8_t edges[160] = {0}; - // Dbprintf("TC0_CV:%i TC1_CV:%i TC1_RA:%i", AT91C_BASE_TC0->TC_CV, AT91C_BASE_TC1->TC_CV ,AT91C_BASE_TC1->TC_RA); + // Dbprintf("TC0_CV:%i TC1_CV:%i TC1_RB:%i TIMESTAMP:%u", AT91C_BASE_TC0->TC_CV, AT91C_BASE_TC1->TC_CV, + // AT91C_BASE_TC1->TC_RB, TIMESTAMP); // Receive tag frame, watch for at most T0*HITAG_T_PROG_MAX periods while (AT91C_BASE_TC0->TC_CV < (T0 * HITAG_T_PROG_MAX)) { - // Check if falling edge in tag modulation is detected - if (AT91C_BASE_TC1->TC_SR & AT91C_TC_LDRAS) { + // Check if edge in tag modulation is detected + if (AT91C_BASE_TC1->TC_SR & next_edge_event) { + + next_edge_event = next_edge_event ^ (AT91C_TC_LDRAS | AT91C_TC_LDRBS); + + // only use AT91C_TC_LDRBS falling edge for now + if (next_edge_event == AT91C_TC_LDRBS) continue; // Retrieve the new timing values - uint32_t ra = AT91C_BASE_TC1->TC_RA / T0; - edges[ra_i++] = ra; + uint32_t rb = AT91C_BASE_TC1->TC_RB / T0; + edges[rb_i++] = rb; // Reset timer every frame, we have to capture the last edge for timing AT91C_BASE_TC0->TC_CCR = AT91C_TC_SWTRG; @@ -916,7 +893,7 @@ static void hts_receive_frame(uint8_t *rx, size_t sizeofrx, size_t *rxlen, uint3 // Capture tag frame (manchester decoding using only falling edges) if (bStarted == false) { - if (ra >= HITAG_T_WAIT_RESP) { + if (rb >= HITAG_T_WAIT_RESP) { bStarted = true; // We always receive a 'one' first, which has the falling edge after a half period |-_| @@ -926,39 +903,69 @@ static void hts_receive_frame(uint8_t *rx, size_t sizeofrx, size_t *rxlen, uint3 errorCount++; } - } else if (ra >= HITAG_T_TAG_CAPTURE_FOUR_HALF) { - - // Manchester coding example |-_|_-|-_| (101) - rx[(*rxlen) / 8] |= 0 << (7 - ((*rxlen) % 8)); - (*rxlen)++; - - rx[(*rxlen) / 8] |= 1 << (7 - ((*rxlen) % 8)); - (*rxlen)++; - h4++; - } else if (ra >= HITAG_T_TAG_CAPTURE_THREE_HALF) { - - // Manchester coding example |_-|...|_-|-_| (0...01) - rx[(*rxlen) / 8] |= 0 << (7 - ((*rxlen) % 8)); - (*rxlen)++; - - // We have to skip this half period at start and add the 'one' the second time - if (bSkip == false) { - rx[(*rxlen) / 8] |= 1 << (7 - ((*rxlen) % 8)); - (*rxlen)++; - } - - lastbit = !lastbit; - bSkip = !bSkip; - h3++; - } else if (ra >= HITAG_T_TAG_CAPTURE_TWO_HALF) { - // Manchester coding example |_-|_-| (00) or |-_|-_| (11) - // bit is same as last bit - rx[(*rxlen) / 8] |= lastbit << (7 - ((*rxlen) % 8)); - (*rxlen)++; - h2++; } else { - // Ignore weird value, is to small to mean anything - errorCount++; + // Anticollision Coding + if (m == AC2K || m == AC4K) { + if (rb >= HITAG_T_TAG_CAPTURE_FOUR_HALF / double_speed) { + // Anticollision Coding example |--__|--__| (00) + lastbit = 0; + rx[(*rxlen) / 8] |= lastbit << (7 - ((*rxlen) % 8)); + (*rxlen)++; + } else if (rb >= HITAG_T_TAG_CAPTURE_THREE_HALF / double_speed) { + // Anticollision Coding example |-_-_|--__| (10) or |--__|-_-_| (01) + lastbit = !lastbit; + rx[(*rxlen) / 8] |= lastbit << (7 - ((*rxlen) % 8)); + (*rxlen)++; + + bSkip = !!lastbit; + } else if (rb >= HITAG_T_TAG_CAPTURE_TWO_HALF / double_speed) { + // Anticollision Coding example |-_-_| (1) + if (bSkip == false) { + lastbit = 1; + rx[(*rxlen) / 8] |= lastbit << (7 - ((*rxlen) % 8)); + (*rxlen)++; + } + + bSkip = !bSkip; + } else { + // Ignore weird value, is to small to mean anything + errorCount++; + } + } else { + // Manchester coding + if (rb >= HITAG_T_TAG_CAPTURE_FOUR_HALF / double_speed) { + // Manchester coding example |-_|_-|-_| (101) + rx[(*rxlen) / 8] |= 0 << (7 - ((*rxlen) % 8)); + (*rxlen)++; + + rx[(*rxlen) / 8] |= 1 << (7 - ((*rxlen) % 8)); + (*rxlen)++; + h4++; + } else if (rb >= HITAG_T_TAG_CAPTURE_THREE_HALF / double_speed) { + // Manchester coding example |_-|...|_-|-_| (0...01) + rx[(*rxlen) / 8] |= 0 << (7 - ((*rxlen) % 8)); + (*rxlen)++; + + // We have to skip this half period at start and add the 'one' the second time + if (bSkip == false) { + rx[(*rxlen) / 8] |= 1 << (7 - ((*rxlen) % 8)); + (*rxlen)++; + } + + lastbit = !lastbit; + bSkip = !bSkip; + h3++; + } else if (rb >= HITAG_T_TAG_CAPTURE_TWO_HALF / double_speed) { + // Manchester coding example |_-|_-| (00) or |-_|-_| (11) + // bit is same as last bit + rx[(*rxlen) / 8] |= lastbit << (7 - ((*rxlen) % 8)); + (*rxlen)++; + h2++; + } else { + // Ignore weird value, is to small to mean anything + errorCount++; + } + } } } @@ -979,10 +986,10 @@ static void hts_receive_frame(uint8_t *rx, size_t sizeofrx, size_t *rxlen, uint3 } DBG Dbprintf("RX0 %i:%02X.. err:%i resptime:%i h2:%i h3:%i h4:%i edges:", *rxlen, rx[0], errorCount, *resptime, h2, h3, h4); - DBG Dbhexdump(ra_i, edges, false); + DBG Dbhexdump(rb_i, edges, false); } -static int hts_send_receive(const uint8_t *tx, size_t txlen, uint8_t *rx, size_t sizeofrx, size_t *prxbits, int t_wait, bool ledcontrol, bool ac_seq) { +static int hts_send_receive(const uint8_t *tx, size_t txlen, uint8_t *rx, size_t sizeofrx, size_t *rxlen, int t_wait, bool ledcontrol, bool ac_seq) { uint32_t start_time; // Send and store the reader command @@ -1010,73 +1017,41 @@ static int hts_send_receive(const uint8_t *tx, size_t txlen, uint8_t *rx, size_t // Enable and reset external trigger in timer for capturing future frames AT91C_BASE_TC1->TC_CCR = AT91C_TC_CLKEN | AT91C_TC_SWTRG; - size_t rxlen = 0; - hts_receive_frame(rx, sizeofrx, &rxlen, &start_time, ledcontrol); - int k = 0; + hts_set_frame_modulation(protocol_mode, ac_seq); + + hts_receive_frame(rx, sizeofrx, rxlen, &start_time, ledcontrol); // Check if frame was captured and store it - if (rxlen > 0) { + if (*rxlen > 0) { + DBG { + uint8_t response_bit[sizeofrx * 8]; - uint8_t response_bit[sizeofrx * 8]; - - for (size_t i = 0; i < rxlen; i++) { - response_bit[i] = (rx[i / 8] >> (7 - (i % 8))) & 1; - } - - DBG Dbprintf("htS: rxlen...... %zu", rxlen); - DBG Dbprintf("htS: sizeofrx... %zu", sizeofrx); - DBG DbpString("htS: response_bit:"); - DBG Dbhexdump(rxlen, response_bit, false); - - memset(rx, 0x00, sizeofrx); - - if (ac_seq) { - - // Tag Response is AC encoded - // We used UID Request Advanced, meaning AC SEQ SOF is 111. - for (int i = 7; i < rxlen; i += 2) { - - rx[k / 8] |= response_bit[i] << (7 - (k % 8)); - - k++; - - if (k > 8 * sizeofrx) { - break; - } + for (size_t i = 0; i < *rxlen; i++) { + response_bit[i] = (rx[i / 8] >> (7 - (i % 8))) & 1; } - // TODO: It's very confusing to reinterpreter the MC to AC; we should implement a more straightforward approach. - // add the lost bit zero, when AC64 last bit is zero - if (k % 8 == 7) { - k++; - } - - if (g_dbglevel >= DBG_EXTENDED) { - DbpString("htS: ac sequence compress"); - Dbhexdump(k / 8, rx, false); - } - - } else { - - if (g_dbglevel >= DBG_EXTENDED) { - DbpString("htS: skipping 6 bit header"); - } - - // ignore first 6 bits: SOF (actually 1 or 6 depending on response protocol) - // or rather a header. - for (size_t i = 6; i < rxlen; i++) { - - rx[k / 8] |= response_bit[i] << (7 - (k % 8)); - k++; - - if (k > 8 * sizeofrx) { - break; - } + Dbprintf("htS: rxlen...... %zu", *rxlen); + Dbprintf("htS: sizeofrx... %zu", sizeofrx); + DbpString("htS: response_bit:"); + Dbhexdump(*rxlen, response_bit, false); + Dbprintf("htS: skipping %d bit SOF", sof_bits); + if ((rx[0] >> (8 - sof_bits)) != ((1 << sof_bits) - 1)) { + DbpString("htS: Warning, not all bits of SOF are 1"); } } - LogTraceBits(rx, k, start_time, TIMESTAMP, false); + + // remove first sof_bits bits SOF + for (size_t i = 0; i < (*rxlen + 8) / 8; i++) { + rx[i] <<= sof_bits; + if (i + 1 < (*rxlen + 8) / 8) { + rx[i] |= (rx[i + 1] >> (8 - sof_bits)); + } + } + + *rxlen -= sof_bits; + + LogTraceBits(rx, *rxlen, start_time, TIMESTAMP, false); } - *prxbits = k; return PM3_SUCCESS; } @@ -1112,7 +1087,9 @@ static int hts_select_tag(const lf_hitag_data_t *packet, uint8_t *tx, size_t siz // UID request FAdvanced 11010 size_t txlen = 0; size_t rxlen = 0; - uint8_t cmd = HITAGS_UID_REQ_ADV1; + + protocol_mode = packet->mode; + uint8_t cmd = protocol_mode; txlen = concatbits(tx, txlen, &cmd, 0, 5); hts_send_receive(tx, txlen, rx, sizeofrx, &rxlen, t_wait, ledcontrol, true); @@ -1135,7 +1112,7 @@ static int hts_select_tag(const lf_hitag_data_t *packet, uint8_t *tx, size_t siz hts_send_receive(tx, txlen, rx, sizeofrx, &rxlen, HITAG_T_WAIT_SC, ledcontrol, false); - if (rxlen != 40) { + if (rxlen != 32 + (protocol_mode == HITAGS_UID_REQ_STD ? 0 : 8)) { DBG Dbprintf("Select UID failed! %i", rxlen); return -3; } @@ -1157,7 +1134,7 @@ static int hts_select_tag(const lf_hitag_data_t *packet, uint8_t *tx, size_t siz key_le = *(uint64_t *)packet->key; - uint64_t state = ht2_hitag2_init(reflect48(key_le), reflect32(tag.data.s.uid_le), reflect32(*(uint32_t *)rnd)); + uint64_t state = ht2_hitag2_init(REV64(key_le), REV32(tag.data.s.uid_le), REV32(*(uint32_t *)rnd)); uint8_t auth_ks[4]; for (int i = 0; i < 4; i++) { @@ -1234,7 +1211,7 @@ static int hts_select_tag(const lf_hitag_data_t *packet, uint8_t *tx, size_t siz hts_send_receive(tx, txlen, rx, sizeofrx, &rxlen, HITAG_T_WAIT_SC, ledcontrol, false); - if (rxlen != 40) { + if (rxlen != 32 + (protocol_mode == HITAGS_UID_REQ_STD ? 0 : 8)) { DBG Dbprintf("Authenticate failed! " _RED_("%i"), rxlen); return -8; } @@ -1249,7 +1226,7 @@ static int hts_select_tag(const lf_hitag_data_t *packet, uint8_t *tx, size_t siz pwdl1 = 0; if (packet->cmd == HTSF_KEY) { - uint64_t state = ht2_hitag2_init(reflect48(key_le), reflect32(tag.data.s.uid_le), reflect32(*(uint32_t *)rnd)); + uint64_t state = ht2_hitag2_init(REV64(key_le), REV32(tag.data.s.uid_le), REV32(*(uint32_t *)rnd)); for (int i = 0; i < 4; i++) { ht2_hitag2_byte(&state); } @@ -1295,6 +1272,12 @@ void hts_read(const lf_hitag_data_t *payload, bool ledcontrol) { while ((BUTTON_PRESS() == false) && (data_available() == false)) { + if (payload->page_count == 0) { + if (page_addr > tag.max_page) break; + } else if (page_addr > 255 || page_addr >= payload->page + payload->page_count) { + break; + } + WDT_HIT(); size_t rxlen = 0; @@ -1310,9 +1293,9 @@ void hts_read(const lf_hitag_data_t *payload, bool ledcontrol) { hts_send_receive(tx, txlen, rx, ARRAYLEN(rx), &rxlen, HITAG_T_WAIT_SC, ledcontrol, false); - if (rxlen != 40) { + if (rxlen != 32 + (protocol_mode == HITAGS_UID_REQ_STD ? 0 : 8)) { DBG Dbprintf("Read page failed!"); - card.pages_reason[page_index] = -4; + card.pages_reason[page_index] = -11; // status = PM3_ERFTRANS; // goto read_end; page_addr++; @@ -1362,18 +1345,12 @@ void hts_read(const lf_hitag_data_t *payload, bool ledcontrol) { //if the authentication is done with a challenge the key and password are unknown DBG Dbprintf("Page[ 2]: __ __ __ __"); DBG Dbprintf("Page[ 3]: __ __ __ __"); - card.pages_reason[page_index++] = -4; - card.pages_reason[page_index++] = -4; + card.pages_reason[page_index++] = -11; + card.pages_reason[page_index++] = -11; } // since page 2+3 are not accessible when LKP == 1 and AUT == 1 fastforward to next readable page page_addr = 4; } - - if (payload->page_count == 0) { - if (page_addr > tag.max_page) break; - } else if (page_addr > 255 || page_addr >= payload->page + payload->page_count) { - break; - } } read_end: @@ -1505,7 +1482,8 @@ int hts_read_uid(uint32_t *uid, bool ledcontrol, bool send_answer) { // UID request standard 00110 // UID request Advanced 1100x // UID request FAdvanced 11010 - uint8_t cmd = HITAGS_UID_REQ_ADV1; + protocol_mode = HITAGS_UID_REQ_ADV1; + uint8_t cmd = protocol_mode; size_t rxlen = 0; uint8_t rx[HITAG_FRAME_LEN] = { 0x00 }; diff --git a/armsrc/iclass.c b/armsrc/iclass.c index 00f9abe1c..ec6578ded 100644 --- a/armsrc/iclass.c +++ b/armsrc/iclass.c @@ -2157,96 +2157,61 @@ out: } } -void generate_single_key_block_inverted(const uint8_t *startingKey, uint32_t index, uint8_t *keyBlock) { - uint32_t carry = index; +static void generate_single_key_block_inverted_opt(const uint8_t *startingKey, uint32_t index, uint8_t *keyBlock) { + + uint8_t bits_index = index / 16383; + uint8_t ending_bits[] = { //all possible 70 combinations of 4x0 and 4x1 as key ending bits + 0x0F, 0x17, 0x1B, 0x1D, 0x1E, 0x27, 0x2B, 0x2D, 0x2E, 0x33, + 0x35, 0x36, 0x39, 0x3A, 0x3C, 0x47, 0x4B, 0x4D, 0x4E, 0x53, + 0x55, 0x56, 0x59, 0x5A, 0x5C, 0x63, 0x65, 0x66, 0x69, 0x6A, + 0x6C, 0x71, 0x72, 0x74, 0x78, 0x87, 0x8B, 0x8D, 0x8E, 0x93, + 0x95, 0x96, 0x99, 0x9A, 0x9C, 0xA3, 0xA5, 0xA6, 0xA9, 0xAA, + 0xAC, 0xB1, 0xB2, 0xB4, 0xB8, 0xC3, 0xC5, 0xC6, 0xC9, 0xCA, + 0xCC, 0xD1, 0xD2, 0xD4, 0xD8, 0xE1, 0xE2, 0xE4, 0xE8, 0xF0 + }; + + uint8_t binary_endings[8]; // Array to store binary values for each ending bit + // Extract each bit from the ending_bits[k] and store it in binary_endings + uint8_t ending = ending_bits[bits_index]; + for (int i = 7; i >= 0; i--) { + binary_endings[i] = ending & 1; + ending >>= 1; + } + + uint8_t binary_mids[8]; // Array to store the 2-bit chunks of index + // Iterate over the 16-bit integer and store 2 bits at a time in the result array + for (int i = 0; i < 8; i++) { + // Shift and mask to get 2 bits and store them as an 8-bit value + binary_mids[7 - i] = (index >> (i * 2)) & 0x03; // 0x03 is a mask for 2 bits (binary 11) + } memcpy(keyBlock, startingKey, PICOPASS_BLOCK_SIZE); - for (int j = PICOPASS_BLOCK_SIZE - 1; j >= 0; j--) { - uint8_t increment_value = carry & 0x07; // Use only the last 3 bits of carry - keyBlock[j] = increment_value; // Set the last 3 bits, assuming first 5 bits are always 0 - - carry >>= 3; // Shift right by 3 bits for the next byte - if (carry == 0) { - // If no more carry, break early to avoid unnecessary loops - break; - } + // Start from the second byte, index 1 as we're never gonna touch the first byte + for (int i = 1; i < PICOPASS_BLOCK_SIZE; i++) { + // Clear the last bit of the current byte (AND with 0xFE) + keyBlock[i] &= 0xF8; + // Set the last bit to the corresponding value from binary_endings (OR with binary_endings[i]) + keyBlock[i] |= ((binary_mids[i] & 0x03) << 1) | (binary_endings[i] & 0x01); } } void iClass_Recover(iclass_recover_req_t *msg) { bool shallow_mod = false; - - LED_A_ON(); - Dbprintf(_RED_("Interrupting this process will render the card unusable!")); - - Iso15693InitReader(); - //Authenticate with AA2 with the standard key to get the AA2 mac - //Step0 Card Select Routine - - uint32_t eof_time = 0; - picopass_hdr_t hdr = {0}; - - bool res = select_iclass_tag(&hdr, msg->req2.use_credit_key, &eof_time, shallow_mod); - //bool res = select_iclass_tag(&hdr, true, &eof_time, shallow_mod); - if (res == false) { - Dbprintf(_RED_("Unable to select card! Stopping.")); - goto out; - } else { - DbpString(_GREEN_("Card selected successfully!")); - } - - //Step1 Authenticate with AA2 using K2 - - uint8_t mac2[4] = {0}; - uint32_t start_time = eof_time + DELAY_ICLASS_VICC_TO_VCD_READER; - res = authenticate_iclass_tag(&msg->req2, &hdr, &start_time, &eof_time, mac2); - if (res == false) { - Dbprintf(_RED_("Unable to authenticate with AA2 using K2! Stopping.")); - goto out; - } else { - DbpString(_GREEN_("AA2 authentication with K2 successful!")); - } - + uint8_t zero_key[PICOPASS_BLOCK_SIZE] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; + uint8_t genkeyblock[PICOPASS_BLOCK_SIZE] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; + uint32_t index = msg->index; + int bits_found = -1; + bool recovered = false; + bool completed = false; uint8_t div_key2[8] = {0}; - memcpy(div_key2, hdr.key_c, 8); + uint32_t eof_time = 0; + uint32_t start_time = 0; + uint8_t read_check_cc[] = { 0x80 | ICLASS_CMD_READCHECK, 0x18 }; //block 24 + read_check_cc[0] = 0x10 | ICLASS_CMD_READCHECK; //use credit key + uint8_t read_check_cc2[] = { 0x80 | ICLASS_CMD_READCHECK, 0x02 }; //block 2 -> to check Kd macs - //cycle reader to reset cypher state and be able to authenticate with k1 trace - switch_off(); - Iso15693InitReader(); - DbpString(_YELLOW_("Cycled Reader...")); - - //Step0 Card Select Routine - - eof_time = 0; - //hdr = {0}; - res = select_iclass_tag(&hdr, false, &eof_time, shallow_mod); - if (res == false) { - Dbprintf(_RED_("Unable to select card after reader cycle! Stopping.")); - goto out; - } else { - DbpString(_GREEN_("Card selected successfully!")); - } - - //Step1 Authenticate with AA1 using trace - - uint8_t mac1[4] = {0}; - start_time = eof_time + DELAY_ICLASS_VICC_TO_VCD_READER; - res = authenticate_iclass_tag(&msg->req, &hdr, &start_time, &eof_time, mac1); - if (res == false) { - Dbprintf(_RED_("Unable to authenticate on AA1 using macs! Stopping.")); - goto out; - } else { - DbpString(_GREEN_("Authenticated with AA1 with macs!")); - } - - //Step2 Privilege Escalation: attempt to read AA2 with credentials for AA1 - uint8_t blockno = 24; - uint8_t cmd_read[] = {ICLASS_CMD_READ_OR_IDENTIFY, blockno, 0x00, 0x00}; - AddCrc(cmd_read + 1, 1); - uint8_t resp[10]; - DbpString(_YELLOW_("Attempting privilege escalation...")); - res = iclass_send_cmd_with_retries(cmd_read, sizeof(cmd_read), resp, sizeof(resp), 10, 3, &start_time, ICLASS_READER_TIMEOUT_OTHERS, &eof_time, shallow_mod); + /* iclass_mac_table is a series of weak macs, those weak macs correspond to the different combinations of the last 3 bits of each key byte. */ static uint8_t iclass_mac_table[8][8] = { //Reference weak macs table { 0x00, 0x00, 0x00, 0x00, 0xBF, 0x5D, 0x67, 0x7F }, //Expected mac when last 3 bits of each byte are: 000 @@ -2258,107 +2223,228 @@ void iClass_Recover(iclass_recover_req_t *msg) { { 0x00, 0x00, 0x00, 0x00, 0x1A, 0xA7, 0x66, 0x46 }, //Expected mac when last 3 bits of each byte are: 110 { 0x00, 0x00, 0x00, 0x00, 0xE2, 0xD5, 0x69, 0xE9 } //Expected mac when last 3 bits of each byte are: 111 }; - //Viewing the weak macs table card 24 bits (3x8) in the form of a 24 bit decimal number - static uint32_t iclass_mac_table_bit_values[8] = {0, 2396745, 4793490, 7190235, 9586980, 11983725, 14380470, 16777215}; - /* iclass_mac_table is a series of weak macs, those weak macs correspond to the different combinations of the last 3 bits of each key byte. - If we concatenate the last three bits of each key byte, we have a 24 bits long binary string. - If we convert that string to decimal we obtain the decimal numbers in iclass_mac_table_bit_values - Xorring the index of iterations against those decimal numbers allows us to retrieve the what was the corresponding sequence of bits of the original key in decimal format. */ - - uint8_t zero_key[PICOPASS_BLOCK_SIZE] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; - uint32_t index = 1; - int bits_found = -1; + LED_A_ON(); + DbpString(_RED_("Interrupting this process will render the card unusable!")); + memcpy(div_key2, msg->nfa, 8); //START LOOP + uint32_t loops = 1; + while (bits_found == -1) { + bool card_select = false; + bool card_auth = false; + int reinit_tentatives = 0; + uint8_t original_mac[8] = {0}; + uint16_t resp_len = 0; + int res2; + uint8_t resp[10] = {0}; + uint8_t mac1[4] = {0}; + uint8_t mac2[4] = {0}; + picopass_hdr_t hdr = {0}; + bool res = false; - //Step3 Calculate New Key - uint8_t genkeyblock[PICOPASS_BLOCK_SIZE]; - uint8_t genkeyblock_old[PICOPASS_BLOCK_SIZE]; - uint8_t xorkeyblock[PICOPASS_BLOCK_SIZE]; - generate_single_key_block_inverted(zero_key, index, genkeyblock); - - //NOTE BEFORE UPDATING THE KEY WE NEED TO KEEP IN MIND KEYS ARE XORRED - //xor the new key against the previously generated key so that we only update the difference - if (index != 0) { - generate_single_key_block_inverted(zero_key, index - 1, genkeyblock_old); - for (int i = 0; i < 8 ; i++) { - xorkeyblock[i] = genkeyblock[i] ^ genkeyblock_old[i]; + while (!card_select || !card_auth) { + Iso15693InitReader(); //has to be at the top as it starts tracing + if (!msg->debug) { + set_tracing(false); //disable tracing to prevent crashes - set to true for debugging + } else { + if (loops == 1) { + clear_trace(); //if we're debugging better to clear the trace but do it only on the first loop + } } - } else { - memcpy(xorkeyblock, genkeyblock, PICOPASS_BLOCK_SIZE); + if (msg->test) { + Dbprintf(_YELLOW_("*Cycled Reader*") " ----------------- TEST Index - Loops: "_YELLOW_("%3d / %3d") " --------------*", loops, msg->loop); + } else { + Dbprintf(_YELLOW_("*Cycled Reader*") " ----------------- Index: "_RED_("%3d")" Loops: "_YELLOW_("%3d / %3d") " --------------*", index, loops, msg->loop); + } + //Step0 Card Select Routine + eof_time = 0; //reset eof time + res = select_iclass_tag(&hdr, false, &eof_time, shallow_mod); + if (res == false) { + DbpString(_RED_("Unable to select card after reader cycle! Retrying...")); + } else { + DbpString(_GREEN_("Card selected successfully!")); + card_select = true; + } + + //Step1 Authenticate with AA1 using trace + if (card_select) { + memcpy(original_mac, msg->req.key, 8); + start_time = eof_time + DELAY_ICLASS_VICC_TO_VCD_READER; + res = authenticate_iclass_tag(&msg->req, &hdr, &start_time, &eof_time, mac1); + if (res == false) { + DbpString(_RED_("Unable to authenticate on AA1 using macs! Retrying...")); + } else { + DbpString(_GREEN_("AA1 authentication with macs successful!")); + card_auth = true; + } + } + if (!card_auth || !card_select) { + reinit_tentatives++; + switch_off(); + } + if (reinit_tentatives == 5) { + DbpString(_RED_("Unable to select or authenticate with card multiple times! Stopping.")); + goto out; + } + } + + //Step2 Privilege Escalation: attempt to read AA2 with credentials for AA1 + uint8_t blockno = 24; + uint8_t cmd_read[] = {ICLASS_CMD_READ_OR_IDENTIFY, blockno, 0x00, 0x00}; + AddCrc(cmd_read + 1, 1); + int priv_esc_tries = 0; + bool priv_esc = false; + while (!priv_esc) { + //The privilege escalation is done with a readcheck and not just a normal read! + iclass_send_as_reader(read_check_cc, sizeof(read_check_cc), &start_time, &eof_time, shallow_mod); + // expect a 8-byte response here + res2 = GetIso15693AnswerFromTag(resp, sizeof(resp), ICLASS_READER_TIMEOUT_OTHERS, &eof_time, false, true, &resp_len); + if (res2 != PM3_SUCCESS || resp_len != 8) { + DbpString(_YELLOW_("Privilege Escalation -> ")_RED_("Read failed! Trying again...")); + priv_esc_tries++; + } else { + DbpString(_YELLOW_("Privilege Escalation -> ")_GREEN_("Response OK!")); + priv_esc = true; + } + if (priv_esc_tries == 5) { + DbpString(_RED_("Unable to complete privilege escalation! Stopping.")); + goto out; + } + } + + //Step3 Calculate New Key (Optimised Algo V2) + generate_single_key_block_inverted_opt(zero_key, index, genkeyblock); + if (msg->test) { + memcpy(genkeyblock, zero_key, PICOPASS_BLOCK_SIZE); } //Step4 Calculate New Mac - bool use_mac = true; uint8_t wb[9] = {0}; blockno = 3; wb[0] = blockno; - memcpy(wb + 1, xorkeyblock, 8); + memcpy(wb + 1, genkeyblock, 8); doMAC_N(wb, sizeof(wb), div_key2, mac2); - - //Step5 Perform Write - - DbpString("Generated XOR Key: "); - Dbhexdump(8, xorkeyblock, false); - - if (iclass_writeblock_ext(blockno, xorkeyblock, mac2, use_mac, shallow_mod)) { - Dbprintf("Write block [%3d/0x%02X] " _GREEN_("successful"), blockno, blockno); - } else { - Dbprintf("Write block [%3d/0x%02X] " _RED_("failed"), blockno, blockno); - if (index > 1) { - Dbprintf(_RED_("Card is likely to be unusable!")); + bool use_mac = true; + bool written = false; + bool write_error = false; + while (written == false && write_error == false) { + //Step5 Perform Write + if (iclass_writeblock_ext(blockno, genkeyblock, mac2, use_mac, shallow_mod)) { + DbpString("Wrote key: "); + Dbhexdump(8, genkeyblock, false); } + //Reset cypher state + iclass_send_as_reader(read_check_cc2, sizeof(read_check_cc2), &start_time, &eof_time, shallow_mod); + res2 = GetIso15693AnswerFromTag(resp, sizeof(resp), ICLASS_READER_TIMEOUT_OTHERS, &eof_time, false, true, &resp_len); + //try to authenticate with the original mac to verify the write happened + memcpy(msg->req.key, original_mac, 8); + res = authenticate_iclass_tag(&msg->req, &hdr, &start_time, &eof_time, mac1); + if (msg->test) { + if (res != true) { + DbpString(_RED_("*** CARD EPURSE IS SILENT! RISK OF BRICKING! DO NOT EXECUTE KEY UPDATES! SCAN IT ON READER FOR EPURSE UPDATE, COLLECT NEW TRACES AND TRY AGAIN! ***")); + goto out; + } else { + DbpString(_GREEN_("*** CARD EPURSE IS LOUD! OK TO ATTEMPT KEY RETRIEVAL! RUN AGAIN WITH -notest ***")); + completed = true; + goto out; + } + } else { + if (res != true) { + DbpString("Write Operation : "_GREEN_("VERIFIED! Card Key Updated!")); + written = true; + } else { + DbpString("Write Operation : "_RED_("FAILED! Card Key is the Original. Retrying...")); + write_error = true; + } + } + } + + if (!write_error) { + //Step6 Perform 8 authentication attempts + 1 to verify if we found the weak key + for (int i = 0; i < 8 ; ++i) { + iclass_send_as_reader(read_check_cc2, sizeof(read_check_cc2), &start_time, &eof_time, shallow_mod); + res2 = GetIso15693AnswerFromTag(resp, sizeof(resp), ICLASS_READER_TIMEOUT_OTHERS, &eof_time, false, true, &resp_len); + //need to craft the authentication payload accordingly + memcpy(msg->req.key, iclass_mac_table[i], 8); + res = authenticate_iclass_tag(&msg->req, &hdr, &start_time, &eof_time, mac1); //mac1 here shouldn't matter + if (res == true) { + bits_found = i; + DbpString(_RED_("--------------------------------------------------------")); + Dbprintf("Decimal Value of last 3 bits: " _GREEN_("[%3d]"), bits_found); + DbpString(_RED_("--------------------------------------------------------")); + recovered = true; + } + } + + //regardless of bits being found, restore the original key and verify it + bool reverted = false; + uint8_t revert_retries = 0; + while (!reverted) { + //Regain privilege escalation with a readcheck + iclass_send_as_reader(read_check_cc, sizeof(read_check_cc), &start_time, &eof_time, shallow_mod); + res2 = GetIso15693AnswerFromTag(resp, sizeof(resp), ICLASS_READER_TIMEOUT_OTHERS, &eof_time, false, true, &resp_len); + + DbpString(_YELLOW_("Attempting to restore the original key. ")); + if (iclass_writeblock_ext(blockno, genkeyblock, mac2, use_mac, shallow_mod)) { + DbpString("Restore of Original Key "_GREEN_("successful.")); + } else { + DbpString("Restore of Original Key " _RED_("failed.")); + } + DbpString(_YELLOW_("Verifying Key Restore...")); + //Do a readcheck first to reset the cypher state + iclass_send_as_reader(read_check_cc2, sizeof(read_check_cc2), &start_time, &eof_time, shallow_mod); + res2 = GetIso15693AnswerFromTag(resp, sizeof(resp), ICLASS_READER_TIMEOUT_OTHERS, &eof_time, false, true, &resp_len); + + //need to craft the authentication payload accordingly + memcpy(msg->req.key, original_mac, 8); + res = authenticate_iclass_tag(&msg->req, &hdr, &start_time, &eof_time, mac1); + if (res == true) { + DbpString("Restore of Original Key "_GREEN_("VERIFIED! Card is usable again.")); + reverted = true; + if (recovered) { + goto restore; + } + } else { + DbpString("Restore of Original Key "_RED_("VERIFICATION FAILED! Trying again...")); + } + + revert_retries++; + if (revert_retries >= 7) { //must always be an odd number! + Dbprintf(_RED_("Attempted to restore original key for %3d times and failed. Stopping. Card is likely unusable."), revert_retries); + goto out; + } + } + + } + + if (loops >= msg->loop) { + completed = true; goto out; } - //Step6 Perform 8 authentication attempts - - for (int i = 0; i < 8 ; ++i) { - //need to craft the authentication payload accordingly - memcpy(msg->req.key, iclass_mac_table[i], 8); - res = authenticate_iclass_tag(&msg->req, &hdr, &start_time, &eof_time, mac1); //mac1 here shouldn't matter - if (res == true) { - bits_found = iclass_mac_table_bit_values[i] ^ index; - Dbprintf("Found Card Bits Index: " _GREEN_("[%3d]"), index); - Dbprintf("Mac Table Bit Values: " _GREEN_("[%3d]"), iclass_mac_table_bit_values[i]); - Dbprintf("Decimal Value of Partial Key: " _GREEN_("[%3d]"), bits_found); - goto restore; - } + if (!write_error) { //if there was a write error, re-run the loop for the same key index + loops++; + index++; } - index++; + }//end while restore: ;//empty statement for compilation - uint8_t partialkey[PICOPASS_BLOCK_SIZE]; - convertToHexArray(bits_found, partialkey); + uint8_t partialkey[PICOPASS_BLOCK_SIZE] = {0}; - uint8_t resetkey[PICOPASS_BLOCK_SIZE]; - convertToHexArray(index, resetkey); - - //Calculate reset Mac - - bool use_mac = true; - uint8_t wb[9] = {0}; - blockno = 3; - wb[0] = blockno; - memcpy(wb + 1, resetkey, 8); - doMAC_N(wb, sizeof(wb), div_key2, mac2); - - //Write back the card to the original key - DbpString(_YELLOW_("Restoring Card to the original key using Reset Key: ")); - Dbhexdump(8, resetkey, false); - if (iclass_writeblock_ext(blockno, resetkey, mac2, use_mac, shallow_mod)) { - Dbprintf("Restore of Original Key "_GREEN_("successful. Card is usable again.")); - } else { - Dbprintf("Restore of Original Key " _RED_("failed. Card is likely unusable.")); + for (int i = 0; i < PICOPASS_BLOCK_SIZE; i++) { + partialkey[i] = genkeyblock[i] ^ bits_found; } + //Print the 24 bits found from k1 - DbpString(_YELLOW_("Raw Key Partial Bytes: ")); + DbpString(_RED_("--------------------------------------------------------")); + DbpString(_RED_("SUCCESS! Raw Key Partial Bytes: ")); Dbhexdump(8, partialkey, false); + DbpString(_RED_("--------------------------------------------------------")); switch_off(); reply_ng(CMD_HF_ICLASS_RECOVER, PM3_SUCCESS, NULL, 0); @@ -2366,6 +2452,10 @@ restore: out: switch_off(); - reply_ng(CMD_HF_ICLASS_RECOVER, PM3_ESOFT, NULL, 0); + if (completed) { + reply_ng(CMD_HF_ICLASS_RECOVER, PM3_EINVARG, NULL, 0); + } else { + reply_ng(CMD_HF_ICLASS_RECOVER, PM3_ESOFT, NULL, 0); + } } diff --git a/armsrc/iclass.h b/armsrc/iclass.h index 1480ef56c..2185fd794 100644 --- a/armsrc/iclass.h +++ b/armsrc/iclass.h @@ -71,6 +71,5 @@ bool authenticate_iclass_tag(iclass_auth_req_t *payload, picopass_hdr_t *hdr, ui uint8_t get_pagemap(const picopass_hdr_t *hdr); void iclass_send_as_reader(uint8_t *frame, int len, uint32_t *start_time, uint32_t *end_time, bool shallow_mod); -void generate_single_key_block_inverted(const uint8_t *startingKey, uint32_t index, uint8_t *keyBlock); void iClass_Recover(iclass_recover_req_t *msg); #endif diff --git a/armsrc/iso14443a.c b/armsrc/iso14443a.c index 16ba37c65..d02055b0d 100644 --- a/armsrc/iso14443a.c +++ b/armsrc/iso14443a.c @@ -554,7 +554,7 @@ RAMFUNC int ManchesterDecoding(uint8_t bit, uint16_t offset, uint32_t non_real_t if (IsManchesterModulationNibble1(Demod.twoBits >> Demod.syncBit)) { // modulation in first half if (IsManchesterModulationNibble2(Demod.twoBits >> Demod.syncBit)) { // ... and in second half = collision - if (!Demod.collisionPos) { + if (Demod.collisionPos == 0) { Demod.collisionPos = (Demod.len << 3) + Demod.bitCount; } } // modulation in first half only - Sequence D = 1 @@ -589,6 +589,7 @@ RAMFUNC int ManchesterDecoding(uint8_t bit, uint16_t offset, uint32_t non_real_t } Demod.endTime = Demod.startTime + 8 * (9 * Demod.len + Demod.bitCount + 1); } else { // no modulation in both halves - End of communication + if (Demod.bitCount > 0) { // there are some remaining data bits Demod.shiftReg >>= (9 - Demod.bitCount); // right align the decoded bits Demod.output[Demod.len++] = Demod.shiftReg & 0xff; // and add them to the output @@ -600,6 +601,7 @@ RAMFUNC int ManchesterDecoding(uint8_t bit, uint16_t offset, uint32_t non_real_t Demod.parityBits <<= (8 - (Demod.len & 0x0007)); // left align remaining parity bits Demod.parity[Demod.parityLen++] = Demod.parityBits; // and store them } + if (Demod.len) { return true; // we are finished with decoding the raw data sequence } else { // nothing received. Start over @@ -624,11 +626,13 @@ static RAMFUNC int ManchesterDecoding_Thinfilm(uint8_t bit) { if (Demod.state == DEMOD_14A_UNSYNCD) { if (Demod.highCnt < 2) { // wait for a stable unmodulated signal + if (Demod.twoBits == 0x0000) { Demod.highCnt++; } else { Demod.highCnt = 0; } + } else { Demod.syncBit = 0xFFFF; // not set if ((Demod.twoBits & 0x7700) == 0x7000) Demod.syncBit = 7; @@ -639,6 +643,7 @@ static RAMFUNC int ManchesterDecoding_Thinfilm(uint8_t bit) { else if ((Demod.twoBits & 0x03B8) == 0x0380) Demod.syncBit = 2; else if ((Demod.twoBits & 0x01DC) == 0x01C0) Demod.syncBit = 1; else if ((Demod.twoBits & 0x00EE) == 0x00E0) Demod.syncBit = 0; + if (Demod.syncBit != 0xFFFF) { Demod.startTime = (GetCountSspClk() & 0xfffffff8); Demod.startTime -= Demod.syncBit; @@ -647,38 +652,49 @@ static RAMFUNC int ManchesterDecoding_Thinfilm(uint8_t bit) { Demod.state = DEMOD_14A_MANCHESTER_DATA; } } + } else { if (IsManchesterModulationNibble1(Demod.twoBits >> Demod.syncBit)) { // modulation in first half + if (IsManchesterModulationNibble2(Demod.twoBits >> Demod.syncBit)) { // ... and in second half = collision - if (!Demod.collisionPos) { + if (Demod.collisionPos == 0) { Demod.collisionPos = (Demod.len << 3) + Demod.bitCount; } } // modulation in first half only - Sequence D = 1 Demod.bitCount++; Demod.shiftReg = (Demod.shiftReg << 1) | 0x1; // in both cases, add a 1 to the shiftreg + if (Demod.bitCount == 8) { // if we decoded a full byte - Demod.output[Demod.len++] = (Demod.shiftReg & 0xff); + Demod.output[Demod.len++] = (Demod.shiftReg & 0xFF); Demod.bitCount = 0; Demod.shiftReg = 0; } + Demod.endTime = Demod.startTime + 8 * (8 * Demod.len + Demod.bitCount + 1) - 4; + } else { // no modulation in first half + if (IsManchesterModulationNibble2(Demod.twoBits >> Demod.syncBit)) { // and modulation in second half = Sequence E = 0 Demod.bitCount++; Demod.shiftReg = (Demod.shiftReg << 1); // add a 0 to the shiftreg if (Demod.bitCount >= 8) { // if we decoded a full byte - Demod.output[Demod.len++] = (Demod.shiftReg & 0xff); + Demod.output[Demod.len++] = (Demod.shiftReg & 0xFF); Demod.bitCount = 0; Demod.shiftReg = 0; } Demod.endTime = Demod.startTime + 8 * (8 * Demod.len + Demod.bitCount + 1); + } else { // no modulation in both halves - End of communication - if (Demod.bitCount > 0) { // there are some remaining data bits + + if (Demod.bitCount) { // there are some remaining data bits Demod.shiftReg <<= (8 - Demod.bitCount); // left align the decoded bits - Demod.output[Demod.len++] = Demod.shiftReg & 0xff; // and add them to the output + Demod.output[Demod.len++] = Demod.shiftReg & 0xFF; // and add them to the output + +// Dbprintf("A | len... %u - %u == 0x%02x", Demod.len, Demod.bitCount, Demod.output[0]); return true; } + if (Demod.len) { return true; // we are finished with decoding the raw data sequence } else { // nothing received. Start over @@ -1092,7 +1108,8 @@ bool prepare_allocated_tag_modulation(tag_response_info_t *response_info, uint8_ } } -bool SimulateIso14443aInit(uint8_t tagType, uint16_t flags, uint8_t *data, uint8_t *iRATs, tag_response_info_t **responses, +bool SimulateIso14443aInit(uint8_t tagType, uint16_t flags, uint8_t *data, + uint8_t *iRATs, size_t irats_len, tag_response_info_t **responses, uint32_t *cuid, uint32_t counters[3], uint8_t tearings[3], uint8_t *pages) { uint8_t sak = 0; // The first response contains the ATQA (note: bytes are transmitted in reverse order). @@ -1255,29 +1272,37 @@ bool SimulateIso14443aInit(uint8_t tagType, uint16_t flags, uint8_t *data, uint8 } } - // copy the iRATs if supplied + // copy the iRATs if supplied. + // iRATs is a pointer to 20 byte array + // rRATS is a 40 byte array if ((flags & FLAG_RATS_IN_DATA) == FLAG_RATS_IN_DATA) { - memcpy(rRATS, iRATs, sizeof(iRATs)); + memcpy(rRATS, iRATs, irats_len); // rats len is dictated by the first char of the string, add 2 crc bytes rRATS_len = (iRATs[0] + 2); + // Since its Varible length we can send value > 40 and overflow our array. + // Even if RATS protocol defined as max 40 bytes doesn't mean people try stuff + if (rRATS_len > sizeof(rRATS)) { + if (g_dbglevel >= DBG_ERROR) Dbprintf("[-] ERROR: iRATS overflow. Max %zu, got %zu", sizeof(rRATS), rRATS_len); + return false; + } } // if uid not supplied then get from emulator memory - if ((memcmp(data, "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", 10) == 0) || ((flags & FLAG_UID_IN_EMUL) == FLAG_UID_IN_EMUL)) { + if ((memcmp(data, "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", 10) == 0) || IS_FLAG_UID_IN_EMUL(flags)) { if (tagType == 2 || tagType == 7) { uint16_t start = MFU_DUMP_PREFIX_LENGTH; uint8_t emdata[8]; emlGet(emdata, start, sizeof(emdata)); memcpy(data, emdata, 3); // uid bytes 0-2 memcpy(data + 3, emdata + 4, 4); // uid bytes 3-7 - flags |= FLAG_7B_UID_IN_DATA; + FLAG_SET_UID_IN_DATA(flags, 7); } else { emlGet(data, 0, 4); - flags |= FLAG_4B_UID_IN_DATA; + FLAG_SET_UID_IN_DATA(flags, 4); } } - if ((flags & FLAG_4B_UID_IN_DATA) == FLAG_4B_UID_IN_DATA) { + if (IS_FLAG_UID_IN_DATA(flags, 4)) { rUIDc1[0] = data[0]; rUIDc1[1] = data[1]; rUIDc1[2] = data[2]; @@ -1296,7 +1321,7 @@ bool SimulateIso14443aInit(uint8_t tagType, uint16_t flags, uint8_t *data, uint8 AddCrc14A(rSAKc1, sizeof(rSAKc1) - 2); *cuid = bytes_to_num(data, 4); - } else if ((flags & FLAG_7B_UID_IN_DATA) == FLAG_7B_UID_IN_DATA) { + } else if (IS_FLAG_UID_IN_DATA(flags, 7)) { rUIDc1[0] = MIFARE_SELECT_CT; // Cascade Tag marker rUIDc1[1] = data[0]; rUIDc1[2] = data[1]; @@ -1319,7 +1344,7 @@ bool SimulateIso14443aInit(uint8_t tagType, uint16_t flags, uint8_t *data, uint8 *cuid = bytes_to_num(data + 3, 4); - } else if ((flags & FLAG_10B_UID_IN_DATA) == FLAG_10B_UID_IN_DATA) { + } else if (IS_FLAG_UID_IN_DATA(flags, 10)) { rUIDc1[0] = MIFARE_SELECT_CT; // Cascade Tag marker rUIDc1[1] = data[0]; @@ -1430,7 +1455,8 @@ bool SimulateIso14443aInit(uint8_t tagType, uint16_t flags, uint8_t *data, uint8 // response to send, and send it. // 'hf 14a sim' //----------------------------------------------------------------------------- -void SimulateIso14443aTag(uint8_t tagType, uint16_t flags, uint8_t *data, uint8_t exitAfterNReads, uint8_t *iRATs) { +void SimulateIso14443aTag(uint8_t tagType, uint16_t flags, uint8_t *data, uint8_t exitAfterNReads, + uint8_t *iRATs, size_t irats_len) { #define ATTACK_KEY_COUNT 16 @@ -1472,7 +1498,7 @@ void SimulateIso14443aTag(uint8_t tagType, uint16_t flags, uint8_t *data, uint8_ .modulation_n = 0 }; - if (SimulateIso14443aInit(tagType, flags, data, iRATs, &responses, &cuid, counters, tearings, &pages) == false) { + if (SimulateIso14443aInit(tagType, flags, data, iRATs, irats_len, &responses, &cuid, counters, tearings, &pages) == false) { BigBuf_free_keep_EM(); reply_ng(CMD_HF_MIFARE_SIMULATE, PM3_EINIT, NULL, 0); return; @@ -2237,7 +2263,7 @@ int EmGetCmd(uint8_t *received, uint16_t received_max_len, uint16_t *len, uint8_ int EmSendCmd14443aRaw(const uint8_t *resp, uint16_t respLen) { volatile uint8_t b; uint16_t i = 0; - uint32_t ThisTransferTime; + uint32_t ThisTransferTime = 0; bool correction_needed; // Modulate Manchester @@ -2262,7 +2288,9 @@ int EmSendCmd14443aRaw(const uint8_t *resp, uint16_t respLen) { // wait for the FPGA to signal fdt_indicator == 1 (the FPGA is ready to queue new data in its delay line) for (uint8_t j = 0; j < 5; j++) { // allow timeout - better late than never while (!(AT91C_BASE_SSC->SSC_SR & AT91C_SSC_RXRDY)); - if (AT91C_BASE_SSC->SSC_RHR) break; + if (AT91C_BASE_SSC->SSC_RHR) { + break; + } } while ((ThisTransferTime = GetCountSspClk()) & 0x00000007); @@ -2288,7 +2316,7 @@ int EmSendCmd14443aRaw(const uint8_t *resp, uint16_t respLen) { } } LastTimeProxToAirStart = ThisTransferTime + (correction_needed ? 8 : 0); - return 0; + return PM3_SUCCESS; } int EmSend4bit(uint8_t resp) { @@ -2385,10 +2413,10 @@ bool EmLogTrace(uint8_t *reader_data, uint16_t reader_len, uint32_t reader_Start // If a response is captured return TRUE // If it takes too long return FALSE //----------------------------------------------------------------------------- -bool GetIso14443aAnswerFromTag_Thinfilm(uint8_t *receivedResponse, uint16_t resp_len, uint8_t *received_len) { +bool GetIso14443aAnswerFromTag_Thinfilm(uint8_t *receivedResponse, uint16_t rec_maxlen, uint8_t *received_len) { if (g_hf_field_active == false) { - Dbprintf("Warning: HF field is off, ignoring GetIso14443aAnswerFromTag_Thinfilm command"); + Dbprintf("Warning: HF field is off"); return false; } @@ -2399,7 +2427,7 @@ bool GetIso14443aAnswerFromTag_Thinfilm(uint8_t *receivedResponse, uint16_t resp FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_ISO14443A | FPGA_HF_ISO14443A_READER_LISTEN); // Now get the answer from the card - Demod14aInit(receivedResponse, resp_len, NULL); + Demod14aInit(receivedResponse, rec_maxlen, NULL); // clear RXRDY: uint8_t b = (uint8_t)AT91C_BASE_SSC->SSC_RHR; @@ -2415,18 +2443,17 @@ bool GetIso14443aAnswerFromTag_Thinfilm(uint8_t *receivedResponse, uint16_t resp b = (uint8_t)AT91C_BASE_SSC->SSC_RHR; if (ManchesterDecoding_Thinfilm(b)) { *received_len = Demod.len; - LogTrace(receivedResponse, Demod.len, Demod.startTime * 16 - DELAY_AIR2ARM_AS_READER, Demod.endTime * 16 - DELAY_AIR2ARM_AS_READER, NULL, false); return true; } } - if (GetTickCountDelta(receive_timer) > timeout + 100) + if (GetTickCountDelta(receive_timer) > timeout + 100) { break; + } } *received_len = Demod.len; - LogTrace(receivedResponse, Demod.len, Demod.startTime * 16 - DELAY_AIR2ARM_AS_READER, Demod.endTime * 16 - DELAY_AIR2ARM_AS_READER, NULL, false); return false; } @@ -2560,7 +2587,7 @@ void iso14443a_antifuzz(uint32_t flags) { resp[0] = 0x04; resp[1] = 0x00; - if ((flags & FLAG_7B_UID_IN_DATA) == FLAG_7B_UID_IN_DATA) { + if (IS_FLAG_UID_IN_DATA(flags, 7)) { resp[0] = 0x44; } @@ -2578,7 +2605,7 @@ void iso14443a_antifuzz(uint32_t flags) { resp[4] = resp[0] ^ resp[1] ^ resp[2] ^ resp[3]; colpos = 0; - if ((flags & FLAG_7B_UID_IN_DATA) == FLAG_7B_UID_IN_DATA) { + if (IS_FLAG_UID_IN_DATA(flags, 7)) { resp[0] = MIFARE_SELECT_CT; colpos = 8; } @@ -3917,7 +3944,9 @@ It can also continue after the AID has been selected, and respond to other reque This was forked from the original function to allow for more flexibility in the future, and to increase the processing speed of the original function. /// */ -void SimulateIso14443aTagAID(uint8_t tagType, uint16_t flags, uint8_t *data, uint8_t *iRATs, uint8_t *aid, uint8_t *resp, uint8_t *apdu, int aidLen, int respondLen, int apduLen, bool enumerate) { +void SimulateIso14443aTagAID(uint8_t tagType, uint16_t flags, uint8_t *data, + uint8_t *iRATs, size_t irats_len, uint8_t *aid, uint8_t *resp, + uint8_t *apdu, int aidLen, int respondLen, int apduLen, bool enumerate) { tag_response_info_t *responses; uint32_t cuid = 0; uint32_t counters[3] = { 0x00, 0x00, 0x00 }; @@ -3944,7 +3973,7 @@ void SimulateIso14443aTagAID(uint8_t tagType, uint16_t flags, uint8_t *data, uin .modulation_n = 0 }; - if (SimulateIso14443aInit(tagType, flags, data, iRATs, &responses, &cuid, counters, tearings, &pages) == false) { + if (SimulateIso14443aInit(tagType, flags, data, iRATs, irats_len, &responses, &cuid, counters, tearings, &pages) == false) { BigBuf_free_keep_EM(); reply_ng(CMD_HF_MIFARE_SIMULATE, PM3_EINIT, NULL, 0); return; @@ -4047,15 +4076,15 @@ void SimulateIso14443aTagAID(uint8_t tagType, uint16_t flags, uint8_t *data, uin // aid len is found as a hex value in receivedCmd[6] (Index Starts at 0) int aid_len = receivedCmd[6]; - uint8_t *recieved_aid = &receivedCmd[7]; + uint8_t *received_aid = &receivedCmd[7]; // aid enumeration flag if (enumerate == true) { Dbprintf("Received AID (%d):", aid_len); - Dbhexdump(aid_len, recieved_aid, false); + Dbhexdump(aid_len, received_aid, false); } - if (memcmp(aidFilter, recieved_aid, aid_len) == 0) { // Evaluate the AID sent by the Reader to the AID supplied + if (memcmp(aidFilter, received_aid, aid_len) == 0) { // Evaluate the AID sent by the Reader to the AID supplied // AID Response will be parsed here memcpy(dynamic_response_info.response + 2, aidResponse, respondLen + 2); dynamic_response_info.response_n = respondLen + 2; diff --git a/armsrc/iso14443a.h b/armsrc/iso14443a.h index c550a4701..24b388252 100644 --- a/armsrc/iso14443a.h +++ b/armsrc/iso14443a.h @@ -55,7 +55,8 @@ typedef struct { uint16_t shiftReg; uint16_t samples; uint16_t len; - uint32_t startTime, endTime; + uint32_t startTime; + uint32_t endTime; uint16_t output_len; uint8_t *output; uint8_t *parity; @@ -88,7 +89,8 @@ typedef struct { uint8_t parityBits; uint8_t parityLen; uint32_t fourBits; - uint32_t startTime, endTime; + uint32_t startTime; + uint32_t endTime; uint16_t output_len; uint8_t *output; uint8_t *parity; @@ -140,8 +142,17 @@ RAMFUNC bool MillerDecoding(uint8_t bit, uint32_t non_real_time); RAMFUNC int ManchesterDecoding(uint8_t bit, uint16_t offset, uint32_t non_real_time); void RAMFUNC SniffIso14443a(uint8_t param); -void SimulateIso14443aTag(uint8_t tagType, uint16_t flags, uint8_t *data, uint8_t exitAfterNReads, uint8_t *iRATs); -bool SimulateIso14443aInit(uint8_t tagType, uint16_t flags, uint8_t *data, uint8_t *iRATs, tag_response_info_t **responses, uint32_t *cuid, uint32_t counters[3], uint8_t tearings[3], uint8_t *pages); +void SimulateIso14443aTag(uint8_t tagType, uint16_t flags, uint8_t *data, uint8_t exitAfterNReads, + uint8_t *iRATs, size_t irats_len); + +void SimulateIso14443aTagAID(uint8_t tagType, uint16_t flags, uint8_t *data, + uint8_t *iRATs, size_t irats_len, uint8_t *aid, uint8_t *resp, + uint8_t *apdu, int aid_len, int respond_len, int apdu_len, bool enumerate); + +bool SimulateIso14443aInit(uint8_t tagType, uint16_t flags, uint8_t *data, + uint8_t *iRATs, size_t irats_len, tag_response_info_t **responses, + uint32_t *cuid, uint32_t counters[3], uint8_t tearings[3], uint8_t *pages); + bool GetIso14443aCommandFromReader(uint8_t *received, uint16_t received_maxlen, uint8_t *par, int *len); void iso14443a_antifuzz(uint32_t flags); void ReaderIso14443a(PacketCommandNG *c); @@ -174,9 +185,8 @@ bool EmLogTrace(uint8_t *reader_data, uint16_t reader_len, uint32_t reader_Start void ReaderMifare(bool first_try, uint8_t block, uint8_t keytype); void DetectNACKbug(void); -void SimulateIso14443aTagAID(uint8_t tagType, uint16_t flags, uint8_t *data, uint8_t *iRATs, uint8_t *aid, uint8_t *resp, uint8_t *apdu, int aid_len, int respond_len, int apdu_len, bool enumerate); -bool GetIso14443aAnswerFromTag_Thinfilm(uint8_t *receivedResponse, uint16_t resp_len, uint8_t *received_len); +bool GetIso14443aAnswerFromTag_Thinfilm(uint8_t *receivedResponse, uint16_t rec_maxlen, uint8_t *received_len); extern iso14a_polling_parameters_t WUPA_POLLING_PARAMETERS; extern iso14a_polling_parameters_t REQA_POLLING_PARAMETERS; diff --git a/armsrc/lfops.c b/armsrc/lfops.c index 137809524..b5567f020 100644 --- a/armsrc/lfops.c +++ b/armsrc/lfops.c @@ -2148,7 +2148,7 @@ void T55xx_ChkPwds(uint8_t flags, bool ledcontrol) { BigBuf_Clear_EM(); uint16_t isok = 0; uint8_t counter[2] = {0x00, 0x00}; - isok = Flash_ReadData(DEFAULT_T55XX_KEYS_OFFSET, counter, sizeof(counter)); + isok = Flash_ReadData(DEFAULT_T55XX_KEYS_OFFSET_P(spi_flash_p64k), counter, sizeof(counter)); if (isok != sizeof(counter)) goto OUT; @@ -2164,7 +2164,7 @@ void T55xx_ChkPwds(uint8_t flags, bool ledcontrol) { // adjust available pwd_count pwd_count = pwd_size_available / 4; - isok = Flash_ReadData(DEFAULT_T55XX_KEYS_OFFSET + 2, pwds, pwd_size_available); + isok = Flash_ReadData(DEFAULT_T55XX_KEYS_OFFSET_P(spi_flash_p64k) + 2, pwds, pwd_size_available); if (isok != pwd_size_available) goto OUT; diff --git a/armsrc/mifarecmd.c b/armsrc/mifarecmd.c index 96f615c64..034012fcf 100644 --- a/armsrc/mifarecmd.c +++ b/armsrc/mifarecmd.c @@ -1465,7 +1465,7 @@ void MifareStaticNested(uint8_t blockNo, uint8_t keyType, uint8_t targetBlockNo, LEDsoff(); uint64_t ui64Key = bytes_to_num(key, 6); - uint16_t len; + uint16_t len, dist1 = 160, dist2 = 320; uint8_t uid[10] = { 0x00 }; uint32_t cuid = 0, nt1 = 0, nt2 = 0, nt3 = 0; uint32_t target_nt[2] = {0x00}, target_ks[2] = {0x00}; @@ -1491,6 +1491,30 @@ void MifareStaticNested(uint8_t blockNo, uint8_t keyType, uint8_t targetBlockNo, // Main loop - get crypted nonces for target sector for (uint8_t rtr = 0; rtr < 2; rtr++) { + // distance measurement + if (mifare_classic_halt(pcs)) { + continue; + } + + if (iso14443a_select_card(uid, NULL, &cuid, true, 0, true) == false) { + continue; + }; + + if (mifare_classic_authex(pcs, cuid, blockNo, keyType, ui64Key, AUTH_FIRST, &nt1, NULL)) { + continue; + }; + + if (mifare_classic_authex(pcs, cuid, blockNo, keyType, ui64Key, AUTH_NESTED, &nt2, NULL)) { + continue; + }; + + if (mifare_classic_authex(pcs, cuid, blockNo, keyType, ui64Key, AUTH_NESTED, &nt3, NULL)) { + continue; + }; + + dist1 = nonce_distance(nt1, nt2); + dist2 = nonce_distance(nt1, nt3); + if (mifare_classic_halt(pcs)) { continue; } @@ -1509,8 +1533,8 @@ void MifareStaticNested(uint8_t blockNo, uint8_t keyType, uint8_t targetBlockNo, target_nt[0] = prng_successor(nt1, 161); target_nt[1] = prng_successor(nt1, 321); } else { - target_nt[0] = prng_successor(nt1, 160); - target_nt[1] = prng_successor(nt1, 320); + target_nt[0] = prng_successor(nt1, dist1); + target_nt[1] = prng_successor(nt1, dist2); } len = mifare_sendcmd_short(pcs, AUTH_NESTED, MIFARE_AUTH_KEYA + (targetKeyType & 0xF), targetBlockNo, receivedAnswer, sizeof(receivedAnswer), par, NULL); @@ -1765,7 +1789,7 @@ void MifareChkKeys_fast(uint32_t arg0, uint32_t arg1, uint32_t arg2, uint8_t *da BigBuf_free(); uint16_t isok = 0; uint8_t size[2] = {0x00, 0x00}; - isok = Flash_ReadData(DEFAULT_MF_KEYS_OFFSET, size, 2); + isok = Flash_ReadData(DEFAULT_MF_KEYS_OFFSET_P(spi_flash_p64k), size, 2); if (isok != 2) goto OUT; @@ -1784,7 +1808,7 @@ void MifareChkKeys_fast(uint32_t arg0, uint32_t arg1, uint32_t arg2, uint8_t *da if (datain == NULL) goto OUT; - isok = Flash_ReadData(DEFAULT_MF_KEYS_OFFSET + 2, datain, key_mem_available); + isok = Flash_ReadData(DEFAULT_MF_KEYS_OFFSET_P(spi_flash_p64k) + 2, datain, key_mem_available); if (isok != key_mem_available) goto OUT; @@ -2528,6 +2552,7 @@ out: // bit 4 - need turn off FPGA // bit 5 - need to set datain instead of issuing USB reply (called via ARM for StandAloneMode14a) // bit 6 - wipe tag. +// bit 7 - use USCUID/GDM (20/23) magic wakeup //----------------------------------------------------------------------------- void MifareCSetBlock(uint32_t arg0, uint32_t arg1, uint8_t *datain) { @@ -2596,6 +2621,22 @@ void MifareCSetBlock(uint32_t arg0, uint32_t arg1, uint8_t *datain) { mifare_classic_halt(NULL); } + if (workFlags & MAGIC_GDM_ALT_WUPC) { + ReaderTransmitBitsPar(wupGDM1, 7, NULL, NULL); + if ((ReaderReceive(receivedAnswer, sizeof(receivedAnswer), receivedAnswerPar) == 0) || (receivedAnswer[0] != 0x0a)) { + if (g_dbglevel >= DBG_ERROR) Dbprintf("wupGDM1 error"); + errormsg = MAGIC_WUPC; + break; + } + + ReaderTransmit(wupGDM2, sizeof(wupC2), NULL); + if ((ReaderReceive(receivedAnswer, sizeof(receivedAnswer), receivedAnswerPar) == 0) || (receivedAnswer[0] != 0x0a)) { + if (g_dbglevel >= DBG_ERROR) Dbprintf("wupGDM2 error"); + errormsg = MAGIC_WUPC; + break; + } + } + // write block if (workFlags & MAGIC_WUPC) { ReaderTransmitBitsPar(wupC1, 7, NULL, NULL); @@ -2682,6 +2723,22 @@ void MifareCGetBlock(uint32_t arg0, uint32_t arg1, uint8_t *datain) { //loop doesn't loop just breaks out if error or done while (true) { + if (workFlags & MAGIC_GDM_ALT_WUPC) { + ReaderTransmitBitsPar(wupGDM1, 7, NULL, NULL); + if ((ReaderReceive(receivedAnswer, sizeof(receivedAnswer), receivedAnswerPar) == 0) || (receivedAnswer[0] != 0x0a)) { + if (g_dbglevel >= DBG_ERROR) Dbprintf("wupGDM1 error"); + errormsg = MAGIC_WUPC; + break; + } + + ReaderTransmit(wupGDM2, sizeof(wupC2), NULL); + if ((ReaderReceive(receivedAnswer, sizeof(receivedAnswer), receivedAnswerPar) == 0) || (receivedAnswer[0] != 0x0a)) { + if (g_dbglevel >= DBG_ERROR) Dbprintf("wupGDM2 error"); + errormsg = MAGIC_WUPC; + break; + } + } + if (workFlags & MAGIC_WUPC) { ReaderTransmitBitsPar(wupC1, 7, NULL, NULL); if ((ReaderReceive(receivedAnswer, sizeof(receivedAnswer), receivedAnswerPar) == 0) || (receivedAnswer[0] != 0x0a)) { @@ -3038,18 +3095,25 @@ void MifareHasStaticEncryptedNonce(uint8_t block_no, uint8_t key_type, uint8_t * iso14443a_setup(FPGA_HF_ISO14443A_READER_LISTEN); - uint8_t enc_counter = 0; + uint8_t first_nt_counter = 0; + uint8_t first_nt_repetition_counter = 0; + uint8_t nested_nt_session_counter = 0; + uint8_t nested_nt_repetition_counter = 0; + uint8_t first_and_nested_nt_repetition_counter = 0; uint8_t key_auth_cmd = MIFARE_AUTH_KEYA + key_type; uint8_t key_auth_cmd_nested = MIFARE_AUTH_KEYA + key_type_nested; uint64_t ui64key = bytes_to_num(key, 6); uint64_t ui64key_nested = bytes_to_num(key_nested, 6); uint32_t oldntenc = 0; bool need_first_auth = true; - uint32_t cuid; - uint32_t nt; - uint32_t old_nt; - uint32_t ntenc; - uint8_t ntencpar; + uint32_t cuid = 0; + uint32_t nt = 0; + uint32_t old_nt = 0; + uint32_t nt_first = 0; + uint32_t old_nt_first = 0; + uint32_t ntenc = 0; + uint8_t ntencpar = 0; + bool is_last_auth_first_auth = true; if (nr_nested == 0) { cuid = 0; if (iso14443a_select_card(NULL, NULL, &cuid, true, 0, true) == false) { @@ -3057,78 +3121,108 @@ void MifareHasStaticEncryptedNonce(uint8_t block_no, uint8_t key_type, uint8_t * retval = PM3_ESOFT; goto OUT; } - if (mifare_classic_authex_cmd(pcs, cuid, block_no, key_auth_cmd, ui64key, AUTH_FIRST, &old_nt, NULL, NULL, NULL, corruptnrar, corruptnrarparity)) { + if (mifare_classic_authex_cmd(pcs, cuid, block_no, key_auth_cmd, ui64key, AUTH_FIRST, &nt, NULL, NULL, NULL, corruptnrar, corruptnrarparity)) { if (g_dbglevel >= DBG_ERROR) Dbprintf("Auth error"); retval = PM3_ESOFT; goto OUT; }; - } - for (uint8_t i = 0; i < nr_nested; i++) { - if (need_first_auth) { - cuid = 0; + first_nt_counter++; + } else for (uint8_t i = 0; i < nr_nested; i++) { + if (need_first_auth) { + cuid = 0; - if (hardreset) { - if (g_dbglevel >= DBG_EXTENDED) { - Dbprintf("RF reset"); + if (hardreset) { + if (g_dbglevel >= DBG_EXTENDED) { + Dbprintf("RF reset"); + } + // some cards need longer than mf_reset_card() to see effect on nT + FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); + SpinDelay(150); + iso14443a_setup(FPGA_HF_ISO14443A_READER_LISTEN); } - // some cards need longer than mf_reset_card() to see effect on nT - FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); - SpinDelay(150); - iso14443a_setup(FPGA_HF_ISO14443A_READER_LISTEN); - } - if (g_dbglevel >= DBG_EXTENDED) { - Dbprintf("select"); - } - if (iso14443a_select_card(NULL, NULL, &cuid, true, 0, true) == false) { - retval = PM3_ESOFT; - goto OUT; - } - if (mifare_classic_authex_cmd(pcs, cuid, block_no, key_auth_cmd, ui64key, AUTH_FIRST, &old_nt, NULL, NULL, NULL, corruptnrar, corruptnrarparity)) { - if (g_dbglevel >= DBG_ERROR) Dbprintf("Auth error"); - retval = PM3_ESOFT; - goto OUT; - }; - if (!reset && !hardreset) { - need_first_auth = false; - } - if (addread) { - uint8_t dataread[16] = {0x00}; - mifare_classic_readblock(pcs, block_no, dataread); - } - if (addauth) { - if (mifare_classic_authex_cmd(pcs, cuid, block_no, key_auth_cmd, ui64key, AUTH_NESTED, &nt, NULL, NULL, NULL, false, false)) { + if (g_dbglevel >= DBG_EXTENDED) { + Dbprintf("select"); + } + if (iso14443a_select_card(NULL, NULL, &cuid, true, 0, true) == false) { + retval = PM3_ESOFT; + goto OUT; + } + if (mifare_classic_authex_cmd(pcs, cuid, block_no, key_auth_cmd, ui64key, AUTH_FIRST, &nt_first, NULL, NULL, NULL, corruptnrar, corruptnrarparity)) { if (g_dbglevel >= DBG_ERROR) Dbprintf("Auth error"); retval = PM3_ESOFT; goto OUT; - } else if (g_dbglevel >= DBG_EXTENDED) { - Dbprintf("Nonce distance: %i", nonce_distance(old_nt, nt)); + }; + is_last_auth_first_auth = true; + first_nt_counter++; + if ((first_nt_counter > 1) && (old_nt_first == nt_first)) { + first_nt_repetition_counter++; + } + old_nt_first = nt_first; + if (!reset && !hardreset) { + need_first_auth = false; + } + if (addread) { + uint8_t dataread[16] = {0x00}; + mifare_classic_readblock(pcs, block_no, dataread); + } + if (addauth) { + if (mifare_classic_authex_cmd(pcs, cuid, block_no, key_auth_cmd, ui64key, AUTH_NESTED, &nt, NULL, NULL, NULL, false, false)) { + if (g_dbglevel >= DBG_ERROR) Dbprintf("Auth error"); + retval = PM3_ESOFT; + goto OUT; + } else if (g_dbglevel >= DBG_EXTENDED) { + Dbprintf("Nonce distance: %5i (first nonce <> nested nonce)", nonce_distance(nt_first, nt)); + } + is_last_auth_first_auth = false; + if (nt == nt_first) { + first_and_nested_nt_repetition_counter++; + } + old_nt = nt; } - old_nt = nt; } - } - nt = 0; - ntenc = 0; - if (mifare_classic_authex_cmd(pcs, cuid, incblk2 ? block_no_nested + (i * 4) : block_no_nested, key_auth_cmd_nested, ui64key_nested, AUTH_NESTED, &nt, &ntenc, &ntencpar, NULL, false, false)) { - if (g_dbglevel >= DBG_ERROR) Dbprintf("Nested auth error"); - need_first_auth = true; - } else if (g_dbglevel >= DBG_EXTENDED) { - Dbprintf("Nonce distance: %i", nonce_distance(old_nt, nt)); - } - old_nt = nt; - if (oldntenc == 0) { + nt = 0; + ntenc = 0; + if (mifare_classic_authex_cmd(pcs, cuid, incblk2 ? block_no_nested + (i * 4) : block_no_nested, key_auth_cmd_nested, ui64key_nested, AUTH_NESTED, &nt, &ntenc, &ntencpar, NULL, false, false)) { + if (g_dbglevel >= DBG_ERROR) Dbprintf("Nested auth error"); + need_first_auth = true; + } else if (g_dbglevel >= DBG_EXTENDED) { + if (is_last_auth_first_auth) { + Dbprintf("Nonce distance: %5i (first nonce <> nested nonce)", nonce_distance(nt_first, nt)); + } else { + Dbprintf("Nonce distance: %5i", nonce_distance(old_nt, nt)); + } + } + nested_nt_session_counter++; + is_last_auth_first_auth = false; + old_nt = nt; + if (nt == nt_first) { + first_and_nested_nt_repetition_counter++; + } + if ((nested_nt_session_counter > 1) && (oldntenc == ntenc)) { + nested_nt_repetition_counter++; + } oldntenc = ntenc; - } else if (ntenc == oldntenc) { - enc_counter++; } - } - if (enc_counter) { + data[1] = (cuid >> 24) & 0xFF; + data[2] = (cuid >> 16) & 0xFF; + data[3] = (cuid >> 8) & 0xFF; + data[4] = (cuid >> 0) & 0xFF; + if (first_and_nested_nt_repetition_counter) { + data[0] = NONCE_SUPERSTATIC; + data[5] = (nt >> 24) & 0xFF; + data[6] = (nt >> 16) & 0xFF; + data[7] = (nt >> 8) & 0xFF; + data[8] = (nt >> 0) & 0xFF; + } else if (first_nt_repetition_counter) { + data[0] = NONCE_STATIC; + data[5] = (nt_first >> 24) & 0xFF; + data[6] = (nt_first >> 16) & 0xFF; + data[7] = (nt_first >> 8) & 0xFF; + data[8] = (nt_first >> 0) & 0xFF; + } else if (nested_nt_repetition_counter) { data[0] = NONCE_STATIC_ENC; - data[1] = (cuid >> 24) & 0xFF; - data[2] = (cuid >> 16) & 0xFF; - data[3] = (cuid >> 8) & 0xFF; - data[4] = (cuid >> 0) & 0xFF; data[5] = (nt >> 24) & 0xFF; data[6] = (nt >> 16) & 0xFF; data[7] = (nt >> 8) & 0xFF; @@ -3140,6 +3234,15 @@ void MifareHasStaticEncryptedNonce(uint8_t block_no, uint8_t key_type, uint8_t * data[13] = ntencpar; } else { data[0] = NONCE_NORMAL; + data[5] = (nt >> 24) & 0xFF; + data[6] = (nt >> 16) & 0xFF; + data[7] = (nt >> 8) & 0xFF; + data[8] = (nt >> 0) & 0xFF; + data[9] = (ntenc >> 24) & 0xFF; + data[10] = (ntenc >> 16) & 0xFF; + data[11] = (ntenc >> 8) & 0xFF; + data[12] = (ntenc >> 0) & 0xFF; + data[13] = ntencpar; } OUT: diff --git a/armsrc/mifaresim.c b/armsrc/mifaresim.c index 6fb3287fa..aa93e8770 100644 --- a/armsrc/mifaresim.c +++ b/armsrc/mifaresim.c @@ -184,8 +184,23 @@ static bool IsAccessAllowed(uint8_t blockNo, uint8_t keytype, uint8_t action) { } } -static bool MifareSimInit(uint16_t flags, uint8_t *datain, uint16_t atqa, uint8_t sak, tag_response_info_t **responses, uint32_t *cuid, uint8_t *uid_len, uint8_t **rats, uint8_t *rats_len) { +static uint8_t MifareMaxSector(uint16_t flags) { + if (IS_FLAG_MF_SIZE(flags, MIFARE_MINI_MAX_BYTES)) { + return MIFARE_MINI_MAXSECTOR; + } else if (IS_FLAG_MF_SIZE(flags, MIFARE_1K_MAX_BYTES)) { + return MIFARE_1K_MAXSECTOR; + } else if (IS_FLAG_MF_SIZE(flags, MIFARE_2K_MAX_BYTES)) { + return MIFARE_2K_MAXSECTOR; + } else if (IS_FLAG_MF_SIZE(flags, MIFARE_4K_MAX_BYTES)) { + return MIFARE_4K_MAXSECTOR; + } else { + return MIFARE_4K_MAXSECTOR; + } +} +static bool MifareSimInit(uint16_t flags, uint8_t *uid, uint16_t atqa, uint8_t sak, tag_response_info_t **responses, uint32_t *cuid, uint8_t *uid_len, uint8_t **rats, uint8_t *rats_len) { + + uint8_t uid_tmp[10] = {0}; // SPEC: https://www.nxp.com/docs/en/application-note/AN10833.pdf // ATQA static uint8_t rATQA_Mini[] = {0x04, 0x00}; // indicate Mifare classic Mini 4Byte UID @@ -236,84 +251,79 @@ static bool MifareSimInit(uint16_t flags, uint8_t *datain, uint16_t atqa, uint8_ // Can be set from emulator memory or incoming data // Length: 4,7,or 10 bytes - // Get UID, SAK, ATQA from EMUL - if ((flags & FLAG_UID_IN_EMUL) == FLAG_UID_IN_EMUL) { + if (IS_FLAG_UID_IN_EMUL(flags)) { + if (uid == NULL) { + uid = uid_tmp; + } + // Get UID, SAK, ATQA from EMUL uint8_t block0[16]; emlGet(block0, 0, 16); - - // If uid size defined, copy only uid from EMUL to use, backward compatibility for 'hf_colin.c', 'hf_mattyrun.c' - if ((flags & (FLAG_4B_UID_IN_DATA | FLAG_7B_UID_IN_DATA | FLAG_10B_UID_IN_DATA)) != 0) { - memcpy(datain, block0, 10); // load 10bytes from EMUL to the datain pointer. to be used below. - } else { - // Check for 4 bytes uid: bcc corrected and single size uid bits in ATQA - if ((block0[0] ^ block0[1] ^ block0[2] ^ block0[3]) == block0[4] && (block0[6] & 0xc0) == 0) { - flags |= FLAG_4B_UID_IN_DATA; - memcpy(datain, block0, 4); - rSAK[0] = block0[5]; - memcpy(rATQA, &block0[6], sizeof(rATQA)); - } - // Check for 7 bytes UID: double size uid bits in ATQA - else if ((block0[8] & 0xc0) == 0x40) { - flags |= FLAG_7B_UID_IN_DATA; - memcpy(datain, block0, 7); - rSAK[0] = block0[7]; - memcpy(rATQA, &block0[8], sizeof(rATQA)); - } else { - Dbprintf("ERROR: " _RED_("Invalid dump. UID/SAK/ATQA not found")); - return false; - } + // Check for 4 bytes uid: bcc corrected and single size uid bits in ATQA + if ((block0[0] ^ block0[1] ^ block0[2] ^ block0[3]) == block0[4] && (block0[6] & 0xc0) == 0) { + FLAG_SET_UID_IN_DATA(flags, 4); + memcpy(uid, block0, 4); + rSAK[0] = block0[5]; + memcpy(rATQA, &block0[6], sizeof(rATQA)); + } + // Check for 7 bytes UID: double size uid bits in ATQA + else if ((block0[8] & 0xc0) == 0x40) { + FLAG_SET_UID_IN_DATA(flags, 7); + memcpy(uid, block0, 7); + rSAK[0] = block0[7]; + memcpy(rATQA, &block0[8], sizeof(rATQA)); + } else { + Dbprintf("ERROR: " _RED_("Invalid dump. UID/SAK/ATQA not found")); + return false; + } + } else { + if (uid == NULL) { + Dbprintf("ERROR: " _RED_("Missing UID")); + return false; } - } // Tune tag type, if defined directly // Otherwise use defined by default or extracted from EMUL - if ((flags & FLAG_MF_MINI) == FLAG_MF_MINI) { + if (IS_FLAG_MF_SIZE(flags, MIFARE_MINI_MAX_BYTES)) { memcpy(rATQA, rATQA_Mini, sizeof(rATQA)); rSAK[0] = rSAK_Mini; if (g_dbglevel > DBG_NONE) Dbprintf("Enforcing Mifare Mini ATQA/SAK"); - } else if ((flags & FLAG_MF_1K) == FLAG_MF_1K) { + } else if (IS_FLAG_MF_SIZE(flags, MIFARE_1K_MAX_BYTES)) { memcpy(rATQA, rATQA_1k, sizeof(rATQA)); rSAK[0] = rSAK_1k; if (g_dbglevel > DBG_NONE) Dbprintf("Enforcing Mifare 1K ATQA/SAK"); - } else if ((flags & FLAG_MF_2K) == FLAG_MF_2K) { + } else if (IS_FLAG_MF_SIZE(flags, MIFARE_2K_MAX_BYTES)) { memcpy(rATQA, rATQA_2k, sizeof(rATQA)); rSAK[0] = rSAK_2k; *rats = rRATS; *rats_len = sizeof(rRATS); if (g_dbglevel > DBG_NONE) Dbprintf("Enforcing Mifare 2K ATQA/SAK with RATS support"); - } else if ((flags & FLAG_MF_4K) == FLAG_MF_4K) { + } else if (IS_FLAG_MF_SIZE(flags, MIFARE_4K_MAX_BYTES)) { memcpy(rATQA, rATQA_4k, sizeof(rATQA)); rSAK[0] = rSAK_4k; if (g_dbglevel > DBG_NONE) Dbprintf("Enforcing Mifare 4K ATQA/SAK"); } // Prepare UID arrays - if ((flags & FLAG_4B_UID_IN_DATA) == FLAG_4B_UID_IN_DATA) { // get UID from datain - memcpy(rUIDBCC1, datain, 4); + if (IS_FLAG_UID_IN_DATA(flags, 4)) { + memcpy(rUIDBCC1, uid, 4); *uid_len = 4; - if (g_dbglevel >= DBG_EXTENDED) - Dbprintf("MifareSimInit - FLAG_4B_UID_IN_DATA => Get UID from datain: %02X - Flag: %02X - UIDBCC1: %02X", FLAG_4B_UID_IN_DATA, flags, rUIDBCC1); - - // save CUID *cuid = bytes_to_num(rUIDBCC1, 4); // BCC rUIDBCC1[4] = rUIDBCC1[0] ^ rUIDBCC1[1] ^ rUIDBCC1[2] ^ rUIDBCC1[3]; + if (g_dbglevel >= DBG_EXTENDED) + Dbprintf("MifareSimInit - Flags: %04X - BCC1: %02X", flags, rUIDBCC1[4]); if (g_dbglevel > DBG_NONE) { Dbprintf("4B UID: %02x%02x%02x%02x", rUIDBCC1[0], rUIDBCC1[1], rUIDBCC1[2], rUIDBCC1[3]); } // Correct uid size bits in ATQA rATQA[0] = (rATQA[0] & 0x3f); // single size uid - - } else if ((flags & FLAG_7B_UID_IN_DATA) == FLAG_7B_UID_IN_DATA) { - memcpy(&rUIDBCC1[1], datain, 3); - memcpy(rUIDBCC2, datain + 3, 4); + } else if (IS_FLAG_UID_IN_DATA(flags, 7)) { + memcpy(&rUIDBCC1[1], uid, 3); + memcpy(rUIDBCC2, uid + 3, 4); *uid_len = 7; - if (g_dbglevel >= DBG_EXTENDED) - Dbprintf("MifareSimInit - FLAG_7B_UID_IN_DATA => Get UID from datain: %02X - Flag: %02X - UIDBCC1: %02X", FLAG_7B_UID_IN_DATA, flags, rUIDBCC1); - // save CUID *cuid = bytes_to_num(rUIDBCC2, 4); // CascadeTag, CT @@ -321,6 +331,8 @@ static bool MifareSimInit(uint16_t flags, uint8_t *datain, uint16_t atqa, uint8_ // BCC rUIDBCC1[4] = rUIDBCC1[0] ^ rUIDBCC1[1] ^ rUIDBCC1[2] ^ rUIDBCC1[3]; rUIDBCC2[4] = rUIDBCC2[0] ^ rUIDBCC2[1] ^ rUIDBCC2[2] ^ rUIDBCC2[3]; + if (g_dbglevel >= DBG_EXTENDED) + Dbprintf("MifareSimInit - Flags: %04X - BCC1: %02X - BCC2: %02X", flags, rUIDBCC1[4], rUIDBCC2[4]); if (g_dbglevel > DBG_NONE) { Dbprintf("7B UID: %02x %02x %02x %02x %02x %02x %02x", rUIDBCC1[1], rUIDBCC1[2], rUIDBCC1[3], rUIDBCC2[0], rUIDBCC2[1], rUIDBCC2[2], rUIDBCC2[3]); @@ -328,15 +340,11 @@ static bool MifareSimInit(uint16_t flags, uint8_t *datain, uint16_t atqa, uint8_ // Correct uid size bits in ATQA rATQA[0] = (rATQA[0] & 0x3f) | 0x40; // double size uid - - } else if ((flags & FLAG_10B_UID_IN_DATA) == FLAG_10B_UID_IN_DATA) { - memcpy(&rUIDBCC1[1], datain, 3); - memcpy(&rUIDBCC2[1], datain + 3, 3); - memcpy(rUIDBCC3, datain + 6, 4); + } else if (IS_FLAG_UID_IN_DATA(flags, 10)) { + memcpy(&rUIDBCC1[1], uid, 3); + memcpy(&rUIDBCC2[1], uid + 3, 3); + memcpy(rUIDBCC3, uid + 6, 4); *uid_len = 10; - if (g_dbglevel >= DBG_EXTENDED) - Dbprintf("MifareSimInit - FLAG_10B_UID_IN_DATA => Get UID from datain: %02X - Flag: %02X - UIDBCC1: %02X", FLAG_10B_UID_IN_DATA, flags, rUIDBCC1); - // save CUID *cuid = bytes_to_num(rUIDBCC3, 4); // CascadeTag, CT @@ -346,7 +354,8 @@ static bool MifareSimInit(uint16_t flags, uint8_t *datain, uint16_t atqa, uint8_ rUIDBCC1[4] = rUIDBCC1[0] ^ rUIDBCC1[1] ^ rUIDBCC1[2] ^ rUIDBCC1[3]; rUIDBCC2[4] = rUIDBCC2[0] ^ rUIDBCC2[1] ^ rUIDBCC2[2] ^ rUIDBCC2[3]; rUIDBCC3[4] = rUIDBCC3[0] ^ rUIDBCC3[1] ^ rUIDBCC3[2] ^ rUIDBCC3[3]; - + if (g_dbglevel >= DBG_EXTENDED) + Dbprintf("MifareSimInit - Flags: %04X - BCC1: %02X - BCC2: %02X - BCC3: %02X", flags, rUIDBCC1[4], rUIDBCC2[4], rUIDBCC3[4]); if (g_dbglevel > DBG_NONE) { Dbprintf("10B UID: %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x", rUIDBCC1[1], rUIDBCC1[2], rUIDBCC1[3], @@ -361,11 +370,11 @@ static bool MifareSimInit(uint16_t flags, uint8_t *datain, uint16_t atqa, uint8_ Dbprintf("ERROR: " _RED_("UID size not defined")); return false; } - if (flags & FLAG_FORCED_ATQA) { + if (flags & FLAG_ATQA_IN_DATA) { rATQA[0] = atqa >> 8; rATQA[1] = atqa & 0xff; } - if (flags & FLAG_FORCED_SAK) { + if (flags & FLAG_SAK_IN_DATA) { rSAK[0] = sak; } if (g_dbglevel > DBG_NONE) { @@ -454,17 +463,11 @@ static bool MifareSimInit(uint16_t flags, uint8_t *datain, uint16_t atqa, uint8_ /** *MIFARE 1K simulate. * -*@param flags : -* FLAG_INTERACTIVE - In interactive mode, we are expected to finish the operation with an ACK -* FLAG_4B_UID_IN_DATA - means that there is a 4-byte UID in the data-section, we're expected to use that -* FLAG_7B_UID_IN_DATA - means that there is a 7-byte UID in the data-section, we're expected to use that -* FLAG_10B_UID_IN_DATA - use 10-byte UID in the data-section not finished -* FLAG_NR_AR_ATTACK - means we should collect NR_AR responses for bruteforcing later -* FLAG_NESTED_AUTH_ATTACK - means that we support nested authentication attack +*@param flags: See pm3_cmd.h for the full definitions *@param exitAfterNReads, exit simulation after n blocks have been read, 0 is infinite ... * (unless reader attack mode enabled then it runs util it gets enough nonces to recover all keys attempted) */ -void Mifare1ksim(uint16_t flags, uint8_t exitAfterNReads, uint8_t *datain, uint16_t atqa, uint8_t sak) { +void Mifare1ksim(uint16_t flags, uint8_t exitAfterNReads, uint8_t *uid, uint16_t atqa, uint8_t sak) { tag_response_info_t *responses; uint8_t cardSTATE = MFEMUL_NOFIELD; uint8_t uid_len = 0; // 4, 7, 10 @@ -475,6 +478,7 @@ void Mifare1ksim(uint16_t flags, uint8_t exitAfterNReads, uint8_t *datain, uint1 uint8_t cardWRBL = 0; uint8_t cardAUTHSC = 0; + uint8_t cardMaxSEC = MifareMaxSector(flags); uint8_t cardAUTHKEY = AUTHKEYNONE; // no authentication uint32_t cardRr = 0; uint32_t ans = 0; @@ -498,12 +502,6 @@ void Mifare1ksim(uint16_t flags, uint8_t exitAfterNReads, uint8_t *datain, uint1 uint8_t rats_len = 0; - // if fct is called with NULL we need to assign some memory since this pointer is passed around - uint8_t datain_tmp[10] = {0}; - if (datain == NULL) { - datain = datain_tmp; - } - //Here, we collect UID,sector,keytype,NT,AR,NR,NT2,AR2,NR2 // This will be used in the reader-only attack. @@ -522,7 +520,7 @@ void Mifare1ksim(uint16_t flags, uint8_t exitAfterNReads, uint8_t *datain, uint1 // free eventually allocated BigBuf memory but keep Emulator Memory BigBuf_free_keep_EM(); - if (MifareSimInit(flags, datain, atqa, sak, &responses, &cuid, &uid_len, &rats, &rats_len) == false) { + if (MifareSimInit(flags, uid, atqa, sak, &responses, &cuid, &uid_len, &rats, &rats_len) == false) { BigBuf_free_keep_EM(); return; } @@ -786,6 +784,14 @@ void Mifare1ksim(uint16_t flags, uint8_t exitAfterNReads, uint8_t *datain, uint1 if (g_dbglevel >= DBG_EXTENDED) Dbprintf("[MFEMUL_WORK] KEY %c: %012" PRIx64, (cardAUTHKEY == 0) ? 'A' : 'B', emlGetKey(cardAUTHSC, cardAUTHKEY)); + // sector out of range - do not respond + if (cardAUTHSC >= cardMaxSEC) { + cardAUTHKEY = AUTHKEYNONE; // not authenticated + cardSTATE_TO_IDLE(); + if (g_dbglevel >= DBG_EXTENDED) Dbprintf("[MFEMUL_WORK] Out of range sector %d(0x%02x)", cardAUTHSC, cardAUTHSC); + break; + } + // first authentication crypto1_deinit(pcs); @@ -917,14 +923,16 @@ void Mifare1ksim(uint16_t flags, uint8_t exitAfterNReads, uint8_t *datain, uint1 // Compliance of MIFARE Classic EV1 1K Datasheet footnote of Table 8 // If access bits show that key B is Readable, any subsequent memory access will be refused. + // Some cards don't respect it so we can also skip it with FLAG_MF_USE_READ_KEYB + if ((flags & FLAG_MF_USE_READ_KEYB) != FLAG_MF_USE_READ_KEYB) { + if (cardAUTHKEY == AUTHKEYB && IsKeyBReadable(blockNo)) { + EmSend4bit(mf_crypto1_encrypt4bit(pcs, CARD_NACK_NA)); + FpgaDisableTracing(); - if (cardAUTHKEY == AUTHKEYB && IsKeyBReadable(blockNo)) { - EmSend4bit(mf_crypto1_encrypt4bit(pcs, CARD_NACK_NA)); - FpgaDisableTracing(); - - if (g_dbglevel >= DBG_ERROR) - Dbprintf("[MFEMUL_WORK] Access denied: Reader tried to access memory on authentication with key B while key B is readable in sector (0x%02x)", cardAUTHSC); - break; + if (g_dbglevel >= DBG_ERROR) + Dbprintf("[MFEMUL_WORK] Access denied: Reader tried to access memory on authentication with key B while key B is readable in sector (0x%02x)", cardAUTHSC); + break; + } } } diff --git a/armsrc/mifaresim.h b/armsrc/mifaresim.h index b22659df6..575ec1d83 100644 --- a/armsrc/mifaresim.h +++ b/armsrc/mifaresim.h @@ -41,6 +41,6 @@ #define AUTHKEYB 1 #define AUTHKEYNONE 0xff -void Mifare1ksim(uint16_t flags, uint8_t exitAfterNReads, uint8_t *datain, uint16_t atqa, uint8_t sak); +void Mifare1ksim(uint16_t flags, uint8_t exitAfterNReads, uint8_t *uid, uint16_t atqa, uint8_t sak); #endif diff --git a/armsrc/mifareutil.c b/armsrc/mifareutil.c index 60372aa3f..9f4b87674 100644 --- a/armsrc/mifareutil.c +++ b/armsrc/mifareutil.c @@ -480,7 +480,7 @@ int mifare_ultra_aes_auth(uint8_t keyno, uint8_t *keybytes) { mbedtls_aes_setkey_enc(&actx, key, 128); mbedtls_aes_crypt_cbc(&actx, MBEDTLS_AES_ENCRYPT, sizeof(enc_rnd_ab), IV, rnd_ab, enc_rnd_ab); - // send & recieve + // send & receive len = mifare_sendcmd(MIFARE_ULAES_AUTH_2, enc_rnd_ab, sizeof(enc_rnd_ab), resp, sizeof(resp), respPar, NULL); if (len != 19) { if (g_dbglevel >= DBG_ERROR) Dbprintf("Cmd Error: %02x - expected 19 got " _RED_("%u"), resp[0], len); diff --git a/armsrc/spiffs.c b/armsrc/spiffs.c index 7604f6db7..9c5f96042 100644 --- a/armsrc/spiffs.c +++ b/armsrc/spiffs.c @@ -18,7 +18,6 @@ // SPIFFS api for RDV40 Integration //----------------------------------------------------------------------------- -#define SPIFFS_CFG_PHYS_SZ (1024 * 192) #define SPIFFS_CFG_PHYS_ERASE_SZ (4 * 1024) #define SPIFFS_CFG_PHYS_ADDR (0) #define SPIFFS_CFG_LOG_PAGE_SZ (256) diff --git a/armsrc/spiffs_config.h b/armsrc/spiffs_config.h index f1d54a471..1e00afc3c 100644 --- a/armsrc/spiffs_config.h +++ b/armsrc/spiffs_config.h @@ -236,7 +236,7 @@ typedef uint8_t u8_t; // Instead of giving parameters in config struct, singleton build must // give parameters in defines below. #ifndef SPIFFS_CFG_PHYS_SZ -#define SPIFFS_CFG_PHYS_SZ(ignore) (1024*192) +#define SPIFFS_CFG_PHYS_SZ(ignore) (1024 * 64 * (spi_flash_p64k - 1)) #endif #ifndef SPIFFS_CFG_PHYS_ERASE_SZ #define SPIFFS_CFG_PHYS_ERASE_SZ(ignore) (4*1024) diff --git a/client/CMakeLists.txt b/client/CMakeLists.txt index 766014bc9..544fe5395 100644 --- a/client/CMakeLists.txt +++ b/client/CMakeLists.txt @@ -44,6 +44,9 @@ endif() find_package(PkgConfig) +# Allow specifying a Python version via cmake option +set(PYTHON3_PKGCONFIG "python3" CACHE STRING "Python3 package config version suffix") + if (NOT SKIPQT EQUAL 1) if(APPLE AND EXISTS /usr/local/opt/qt5) @@ -87,8 +90,8 @@ if (NOT SKIPBT EQUAL 1) endif (NOT SKIPBT EQUAL 1) if (NOT SKIPPYTHON EQUAL 1) - pkg_search_module(PYTHON3 QUIET python3) - pkg_search_module(PYTHON3EMBED QUIET python3-embed) + pkg_search_module(PYTHON3 QUIET ${PYTHON3_PKGCONFIG}) + pkg_search_module(PYTHON3EMBED QUIET ${PYTHON3_PKGCONFIG}-embed) endif (NOT SKIPPYTHON EQUAL 1) # If cross-compiled, we need to init source and build. @@ -611,11 +614,11 @@ if (SKIPPYTHON EQUAL 1) message(STATUS "Python3 library: skipped") else (SKIPPYTHON EQUAL 1) if (PYTHON3EMBED_FOUND) - message(STATUS "Python3 library: Python3 embed found, enabled") + message(STATUS "Python3 library: ${PYTHON3_PKGCONFIG}-embed found, enabled") elseif (PYTHON3_FOUND) - message(STATUS "Python3 library: Python3 found, enabled") + message(STATUS "Python3 library: ${PYTHON3_PKGCONFIG} found, enabled") else (PYTHON3EMBED_FOUND) - message(STATUS "Python3 library: Python3 not found, disabled") + message(STATUS "Python3 library: ${PYTHON3_PKGCONFIG} not found, disabled") endif (PYTHON3EMBED_FOUND) endif(SKIPPYTHON EQUAL 1) diff --git a/client/Makefile b/client/Makefile index 71f4220ed..f7a59ed3f 100644 --- a/client/Makefile +++ b/client/Makefile @@ -282,15 +282,15 @@ endif ## Python3 (optional) ifneq ($(SKIPPYTHON),1) # since python3.8, applications willing to embed python must use -embed: - PYTHONINCLUDES = $(shell $(PKG_CONFIG_ENV) pkg-config --cflags python3-embed 2>/dev/null) - PYTHONLDLIBS = $(shell $(PKG_CONFIG_ENV) pkg-config --libs python3-embed 2>/dev/null) + PYTHONINCLUDES = $(shell $(PKG_CONFIG_ENV) pkg-config --cflags $(PYTHON3_PKGCONFIG)-embed 2>/dev/null) + PYTHONLDLIBS = $(shell $(PKG_CONFIG_ENV) pkg-config --libs $(PYTHON3_PKGCONFIG)-embed 2>/dev/null) ifneq ($(PYTHONLDLIBS),) PYTHONLIBLD = $(PYTHONLDLIBS) PYTHONLIBINC = $(subst -I,-isystem ,$(PYTHONINCLUDES)) PYTHON_FOUND = 1 else - PYTHONINCLUDES = $(shell $(PKG_CONFIG_ENV) pkg-config --cflags python3 2>/dev/null) - PYTHONLDLIBS = $(shell $(PKG_CONFIG_ENV) pkg-config --libs python3 2>/dev/null) + PYTHONINCLUDES = $(shell $(PKG_CONFIG_ENV) pkg-config --cflags $(PYTHON3_PKGCONFIG) 2>/dev/null) + PYTHONLDLIBS = $(shell $(PKG_CONFIG_ENV) pkg-config --libs $(PYTHON3_PKGCONFIG) 2>/dev/null) ifneq ($(PYTHONLDLIBS),) PYTHONLIBLD = $(PYTHONLDLIBS) PYTHONLIBINC = $(subst -I,-isystem ,$(PYTHONINCLUDES)) @@ -531,7 +531,7 @@ ifeq ($(SKIPPYTHON),1) $(info Python3 library: skipped) else ifeq ($(PYTHON_FOUND),1) - $(info Python3 library: Python3 v$(shell $(PKG_CONFIG_ENV) pkg-config --modversion python3) found, enabled) + $(info Python3 library: Python3 v$(shell $(PKG_CONFIG_ENV) pkg-config --modversion $(PYTHON3_PKGCONFIG)) found, enabled) else $(info Python3 library: Python3 not found, disabled) endif diff --git a/client/dictionaries/iclass_default_keys.dic b/client/dictionaries/iclass_default_keys.dic index c717cd88f..d1d76d2b1 100644 --- a/client/dictionaries/iclass_default_keys.dic +++ b/client/dictionaries/iclass_default_keys.dic @@ -39,3 +39,5 @@ C1B74D7478053AE2 # # default iCLASS RFIDeas 6B65797374726B72 +# Retrieved from Custom Keyed Systems +E9924C13F4BFA82C diff --git a/client/dictionaries/mfc_default_keys.dic b/client/dictionaries/mfc_default_keys.dic index ae14be206..99717cc50 100644 --- a/client/dictionaries/mfc_default_keys.dic +++ b/client/dictionaries/mfc_default_keys.dic @@ -354,6 +354,8 @@ AFBECD121004 # Onity S1 A/B 8A19D40CF2B5 # +3961EA82C46D +# # 24-7 D21762B2DE3B 0E83A374B513 @@ -2674,4 +2676,25 @@ CE7FCCBBA5D8 F8E385E5A2A0 B27678B5C4AE D68D7EBB9551 -7AB63F082328 \ No newline at end of file +7AB63F082328 +# +# Payment cards used by Eurest on certain campuses +7E2BC58168EB +# +# Shower cards provided by Seijsener +291A65CBEA7B +344A359BBAD9 +476572726974 +4D696368656C +4F3748E6C826 +69D40AF8B353 +72DEA10F21DF +74845AA8E3F1 +8C3C43EDCC55 +ACD30DFFB434 +D1A27C8EC5DF +F14D329CBDBE +# +# Hotel cards from Austria +AB287B3B4903 +7B0DEDA7E162 diff --git a/client/experimental_client_with_swig/01make_client_cmake.sh b/client/experimental_client_with_swig/01make_client_cmake.sh index 456227a7a..e85171d00 100755 --- a/client/experimental_client_with_swig/01make_client_cmake.sh +++ b/client/experimental_client_with_swig/01make_client_cmake.sh @@ -13,3 +13,4 @@ ln -s build/proxmark3 . ) ln -s ../pyscripts/pm3.py +ln -s ../lualibs/dkjson.lua diff --git a/client/experimental_client_with_swig/01make_client_makefile.sh b/client/experimental_client_with_swig/01make_client_makefile.sh index 93ab04ca2..0ebb2953f 100755 --- a/client/experimental_client_with_swig/01make_client_makefile.sh +++ b/client/experimental_client_with_swig/01make_client_makefile.sh @@ -1,4 +1,8 @@ #!/bin/bash -cd .. -make -j +( + cd .. + make -j +) +ln -s ../pyscripts/pm3.py +ln -s ../lualibs/dkjson.lua diff --git a/client/experimental_client_with_swig/testembedded.lua b/client/experimental_client_with_swig/testembedded.lua index e4ad8311e..4857a5372 100755 --- a/client/experimental_client_with_swig/testembedded.lua +++ b/client/experimental_client_with_swig/testembedded.lua @@ -10,4 +10,16 @@ for line in p.grabbed_output:gmatch("[^\r\n]+") do end print("Device:", p.name) -p:console("Rem passthru remark! :coffee:", true) +p:console("Rem passthru remark! :coffee:", false, false) + +local json = require("dkjson") +print("Fetching prefs:") +p:console("prefs show --json") +local prefs, err = json.decode(p.grabbed_output) +if not prefs then + print("Error decoding JSON: ", err) +else + print("Save path: ", prefs['file.default.savepath']) + print("Dump path: ", prefs['file.default.dumppath']) + print("Trace path:", prefs['file.default.tracepath']) +end diff --git a/client/experimental_client_with_swig/testembedded.py b/client/experimental_client_with_swig/testembedded.py index 6dbff0403..78564a314 100755 --- a/client/experimental_client_with_swig/testembedded.py +++ b/client/experimental_client_with_swig/testembedded.py @@ -11,4 +11,12 @@ for line in p.grabbed_output.split('\n'): if "uC:" in line: print(line) print("Device:", p.name) -p.console("Rem passthru remark! :coffee:", True) +p.console("Rem passthru remark! :coffee:", capture=False, quiet=False) + +import json +print("Fetching prefs:") +p.console("prefs show --json") +prefs = json.loads(p.grabbed_output) +print("Save path: ", prefs['file.default.savepath']) +print("Dump path: ", prefs['file.default.dumppath']) +print("Trace path:", prefs['file.default.tracepath']) diff --git a/client/experimental_lib/CMakeLists.txt b/client/experimental_lib/CMakeLists.txt index c3197cef4..e9ac8bb33 100644 --- a/client/experimental_lib/CMakeLists.txt +++ b/client/experimental_lib/CMakeLists.txt @@ -45,6 +45,9 @@ endif() find_package(PkgConfig) +# Allow specifying a Python version via cmake option +set(PYTHON3_PKGCONFIG "python3" CACHE STRING "Python3 package config version suffix") + if (NOT SKIPQT EQUAL 1) if(APPLE AND EXISTS /usr/local/opt/qt5) @@ -88,8 +91,8 @@ if (NOT SKIPBT EQUAL 1) endif (NOT SKIPBT EQUAL 1) if (NOT SKIPPYTHON EQUAL 1) - pkg_search_module(PYTHON3 QUIET python3) - pkg_search_module(PYTHON3EMBED QUIET python3-embed) + pkg_search_module(PYTHON3 QUIET ${PYTHON3_PKGCONFIG}) + pkg_search_module(PYTHON3EMBED QUIET ${PYTHON3_PKGCONFIG}-embed) endif (NOT SKIPPYTHON EQUAL 1) # If cross-compiled, we need to init source and build. @@ -612,11 +615,11 @@ if (SKIPPYTHON EQUAL 1) message(STATUS "Python3 library: skipped") else (SKIPPYTHON EQUAL 1) if (PYTHON3EMBED_FOUND) - message(STATUS "Python3 library: Python3 embed found, enabled") + message(STATUS "Python3 library: ${PYTHON3_PKGCONFIG}-embed found, enabled") elseif (PYTHON3_FOUND) - message(STATUS "Python3 library: Python3 found, enabled") + message(STATUS "Python3 library: ${PYTHON3_PKGCONFIG} found, enabled") else (PYTHON3EMBED_FOUND) - message(STATUS "Python3 library: Python3 not found, disabled") + message(STATUS "Python3 library: ${PYTHON3_PKGCONFIG} not found, disabled") endif (PYTHON3EMBED_FOUND) endif(SKIPPYTHON EQUAL 1) diff --git a/client/experimental_lib/example_c/test.c b/client/experimental_lib/example_c/test.c index bd1900154..6debb332c 100644 --- a/client/experimental_lib/example_c/test.c +++ b/client/experimental_lib/example_c/test.c @@ -9,6 +9,6 @@ int main(int argc, char *argv[]) { } pm3 *p; p = pm3_open(argv[1]); - pm3_console(p, "hw status", true); + pm3_console(p, "hw status", false, false); pm3_close(p); } diff --git a/client/experimental_lib/example_c/test_grab.c b/client/experimental_lib/example_c/test_grab.c index 69b2b7ebf..ee573fc31 100644 --- a/client/experimental_lib/example_c/test_grab.c +++ b/client/experimental_lib/example_c/test_grab.c @@ -18,7 +18,7 @@ int main(int argc, char *argv[]) { p = pm3_open(argv[1]); // Execute the command - pm3_console(p, "hw status", false); + pm3_console(p, "hw status", true, true); const char *buf = pm3_grabbed_output_get(p); const char *line_start = buf; diff --git a/client/experimental_lib/example_lua/01link_lib.sh b/client/experimental_lib/example_lua/01link_lib.sh index 5f9fb5f68..317e94573 100755 --- a/client/experimental_lib/example_lua/01link_lib.sh +++ b/client/experimental_lib/example_lua/01link_lib.sh @@ -1,3 +1,4 @@ #!/bin/bash -ln -fs ../build/libpm3rrg_rdv4.so pm3.so +ln -sf ../build/libpm3rrg_rdv4.so pm3.so +ln -sf ../../lualibs/dkjson.lua . diff --git a/client/experimental_lib/example_lua/test.lua b/client/experimental_lib/example_lua/test.lua index e8f7ad735..03c0dc149 100755 --- a/client/experimental_lib/example_lua/test.lua +++ b/client/experimental_lib/example_lua/test.lua @@ -1,4 +1,4 @@ -#!/usr/bin/env lua +#!/usr/bin/env lua5.4 local pm3 = require("pm3") p=pm3.pm3("/dev/ttyACM0") @@ -12,4 +12,16 @@ for line in p.grabbed_output:gmatch("[^\r\n]+") do end print("Device:", p.name) -p:console("Rem passthru remark! :coffee:", true) +p:console("Rem passthru remark! :coffee:", false, false) + +local json = require("dkjson") +print("Fetching prefs:") +p:console("prefs show --json") +local prefs, err = json.decode(p.grabbed_output) +if not prefs then + print("Error decoding JSON: ", err) +else + print("Save path: ", prefs['file.default.savepath']) + print("Dump path: ", prefs['file.default.dumppath']) + print("Trace path:", prefs['file.default.tracepath']) +end diff --git a/client/experimental_lib/example_py/test.py b/client/experimental_lib/example_py/test.py index 7d9b4580a..fc932b7e5 100755 --- a/client/experimental_lib/example_py/test.py +++ b/client/experimental_lib/example_py/test.py @@ -11,4 +11,12 @@ for line in p.grabbed_output.split('\n'): if "uC:" in line: print(line) print("Device:", p.name) -p.console("Rem passthru remark! :coffee:", True) +p.console("Rem passthru remark! :coffee:", capture=False, quiet=False) + +import json +print("Fetching prefs:") +p.console("prefs show --json") +prefs = json.loads(p.grabbed_output) +print("Save path: ", prefs['file.default.savepath']) +print("Dump path: ", prefs['file.default.dumppath']) +print("Trace path:", prefs['file.default.tracepath']) diff --git a/client/include/pm3.h b/client/include/pm3.h index b19fe7174..92e99bacd 100644 --- a/client/include/pm3.h +++ b/client/include/pm3.h @@ -21,7 +21,7 @@ typedef struct pm3_device pm3; pm3 *pm3_open(const char *port); -int pm3_console(pm3 *dev, const char *cmd, bool passthru); +int pm3_console(pm3 *dev, const char *cmd, bool capture, bool quiet); const char *pm3_grabbed_output_get(pm3 *dev); const char *pm3_name_get(pm3 *dev); void pm3_close(pm3 *dev); diff --git a/client/luascripts/hf_14a_read-ltocm.lua b/client/luascripts/hf_14a_read_ltocm.lua similarity index 100% rename from client/luascripts/hf_14a_read-ltocm.lua rename to client/luascripts/hf_14a_read_ltocm.lua diff --git a/client/luascripts/hf_legic.lua b/client/luascripts/hf_legic.lua index fb98b777e..705ddcbf2 100644 --- a/client/luascripts/hf_legic.lua +++ b/client/luascripts/hf_legic.lua @@ -332,14 +332,29 @@ local function split(str, sep) return fields end +--- +-- join table with a separator +local function join(list, sep) + local sep = sep or ',' + local len = #list + if len == 0 then return "" end + local s = list[1] + for i = 2, len do + s = s .. sep .. list[i] + end + return s +end + --- -- check availability of file function file_check(file_name) if not file_name then return false, "" end local arr = split(file_name, ".") - local path = core.search_file(arr[1], "."..arr[2]) - if (path == nil) then return false end + local ext = table.remove(arr) + local name = join(arr, '.') + local path = core.search_file(name, "."..ext) + if (path == nil) then return false, "" end local file_found = io.open(path, "r") if file_found == nil then @@ -380,7 +395,9 @@ function getInputBytes(infile) local bytes = {} local arr = split(infile, ".") - local path = core.search_file(arr[1], "."..arr[2]) + local ext = table.remove(arr) + local name = join(arr, '.') + local path = core.search_file(name, "."..ext) if (path == nil) then oops("failed to read from file ".. infile); return false; end local fhi,err = io.open(path,"rb") @@ -443,7 +460,7 @@ function bytesToTag(bytes, tag) tag.WRC=("%d"):format(bbit("0x"..bytes[8],4,3)) tag.RD=("%d"):format(bbit("0x"..bytes[8],7,1)) if (tag.Type=="SAM" and tag.raw=='9f') then - tag.Stamp_len=(tonumber(0xfc,10)-tonumber(bbit("0x"..tag.DCFh,0,8),10)) + tag.Stamp_len=(0xfc-bbit("0x"..tag.DCFh,0,8)) elseif (tag.Type=="SAM" and (tag.raw=='08' or tag.raw=='09')) then tag.Stamp_len = tonumber(tag.raw,10) end @@ -756,20 +773,16 @@ end -- read from pm3 into virtual-tag function readFromPM3() local tag, bytes, infile - --infile="legic.temp" infile=getRandomTempName() core.console("hf legic dump -f "..infile) tag=readFile(infile..".bin") res, path = file_check(infile..".bin") - if not res then return nil end - os.remove(path) + if res then os.remove(path) end - res, path = file_check(infile..".eml") - os.remove(path) res, path = file_check(infile..".json") - os.remove(path) + if res then os.remove(path) end return tag end @@ -1690,7 +1703,7 @@ function getTokenType(DCFl) 0x30–0x6f SAM 0x70–0x7f GAM ]]-- - local tt = tonumber(bbit("0x"..DCFl,0,7),10) + local tt = bbit("0x"..DCFl,0,7) if (tt >= 0 and tt <= 47) then tt = "IAM" elseif (tt == 49) then tt = "SAM63" elseif (tt == 48) then tt = "SAM64" @@ -1744,9 +1757,9 @@ function getSegmentData(bytes, start, index) -- wrp (write proteted) = byte 2 segment.WRP = tonumber(bytes[start+2],16) -- wrc (write control) - bit 4-6 of byte 3 - segment.WRC = tonumber(bbit("0x"..bytes[start+3],4,3),16) + segment.WRC = bbit("0x"..bytes[start+3],4,3) -- rd (read disabled) - bit 7 of byte 3 - segment.RD = tonumber(bbit("0x"..bytes[start+3],7,1),16) + segment.RD = bbit("0x"..bytes[start+3],7,1) -- crc byte 4 segment.crc = bytes[start+4] -- segment-data starts at segment.len - segment.header - segment.crc diff --git a/client/luascripts/hf_mf_ultimatecard.lua b/client/luascripts/hf_mf_ultimatecard.lua index 26a5aaee2..09040eea5 100644 --- a/client/luascripts/hf_mf_ultimatecard.lua +++ b/client/luascripts/hf_mf_ultimatecard.lua @@ -191,7 +191,7 @@ local function read_config() if magicconfig == nil then lib14a.disconnect(); return nil, "can't read configuration, "..err_lock end if #magicconfig ~= 64 and #magicconfig ~= 68 then lib14a.disconnect(); return nil, "partial read of configuration, "..err_lock end if gtumode == '00' then gtustr = 'Pre-write/Shadow Mode' - elseif gtumode == '01' then gtustr = 'Restore Mode' + elseif gtumode == '01' or gtumode == '04' then gtustr = 'Restore Mode' elseif gtumode == '02' then gtustr = 'Disabled' elseif gtumode == '03' then gtustr = 'Disabled, high speed R/W mode for Ultralight' end @@ -553,7 +553,7 @@ local function write_gtu(gtu) if gtu == '00' then print('Enabling GTU Pre-Write') send('CF'.._key..'32'..gtu) - elseif gtu == '01' then + elseif gtu == '01' or gtu == '04' then print('Enabling GTU Restore Mode') send('CF'.._key..'32'..gtu) elseif gtu == '02' then diff --git a/client/luascripts/hf_mf_uscuid_prog.lua b/client/luascripts/hf_mf_uscuid_prog.lua index b5dcbcd87..344747f90 100644 --- a/client/luascripts/hf_mf_uscuid_prog.lua +++ b/client/luascripts/hf_mf_uscuid_prog.lua @@ -2,67 +2,82 @@ local cmds = require('commands') local getopt = require('getopt') local lib14a = require('read14a') local utils = require('utils') -local ansicolors = require('ansicolors') +local cl = require('ansicolors') local bxor = bit32.bxor - copyright = '\nLicensed under GNU GPL v3.0. Team orangeBlue.' -author = 'Team '..ansicolors.yellow..'orange'..ansicolors.cyan..'Blue'..ansicolors.reset -- Disinformation -version = 'v1.0' -date = 'Created - Aug 2023' +author = 'Team '..cl.yellow..'orange'..cl.cyan..'Blue'..cl.reset -- Disinformation +version = 'v1.0.1' desc = [[ -Script to set UID on USCUID using any means possible. No warranties given! -See below for capabilities. +Script to set UID on USCUID using any means possible. This script is compatible with the ICs listed below: * GDMIC * UCUID * M1-7B * Other chips, showing up as "Gen 4 GDM" -This script does not claim full compatibility with the ICs listed below: + +This script does *NOT* claim full compatibility with the ICs listed below: * UFUID * PFUID* -WHY? Unfortunately, these are cut down versions. Checks show that they only acknowledge bytes 0-1, 7, 8, and 15 of the configuration. +Why? +Unfortunately, these are cut down versions. +Checks show that they only acknowledge bytes 0-1, 7, 8, and 15 of the configuration. * WARNING: The config commands are inversed. Nothing will work. -Ready to start? Set the first 2 bytes of your config to 7AFF and use -t 4. +Ready to start? + +Set the first 2 bytes of your config to 7AFF and use -t 4. + ]] example = [[ -- Set UID 7 bytes long via 20-23 wakeup 1. script run hf_mf_uscuid_prog -t 2 -u 04A72B85489F51 + -- Set UID 4 bytes long via 40-43 wakeup 2. script run hf_mf_uscuid_prog -t 4 -u A72B571 + -- Read sector 0 + 3. script run hf_mf_uscuid_prog -S 0 ]] usage = [[ -script run hf_mf_uscuid_uid_prog [-h] [-u ] [-t] [-3] [-s ] [-w 1] [-R/-B ] [-S/-E ] [-g/-c/-b/-2/-7/-d/-a/-n/-r <0/1>] +script run hf_mf_uscuid_uid_prog [-h] [-u ] [-t] [-3] [-s ] [-w 1] [-R -B ] [-S -E ] [-g -c -b -2 -7 -d -a -n -r <0/1>] ]] arguments = [[ -h this help -t Magic wakeup type (2 for 0x20, 4 for 0x40) -u New tag UID -s New signature data + -3 Update UID for F3 Perso -w 1 Wipe tag (take caution!) - -R Read block + -B Read backdoor block - -S Read sector -E Read backdoor sector + -R Read block + -S Read sector + [ConfigStar] - To enable an option, pass "1". To disable, pass "0". Unmarked data will not be edited. - -g Gen1 mode - -c Gen1 command (1 for 20-23; 0 for 40-43) - -b Block key B if readable by ACL - -2 Gen2/CUID mode - -7 CL2 mode (1 for F0 unfused; 0 for off) - -d Shadow mode + Unmarked data will not be edited. + + How to use: + To ENABLE an option, pass "1" + To DISABLE an option, pass "0" + -a Magic auth + -b Block key B if readable by ACL + -c Gen1 command (1 for 20-23; 0 for 40-43) + -d Shadow mode + -g Gen1 mode -n Static encrypted nonces -r Signature sector + -2 Gen2/CUID mode + -7 CL2 mode (1 for F0 unfused; 0 for off) ]] + changelog = [[ Welcome, proxmark user! Here's a secret changelog of this script as its' life started. @@ -78,23 +93,26 @@ v1.0 - Memory access. Just like in the proxmark client. -- [[ Start introducing functions that get called later on ]] -- -- give up local function oops(err) - print(ansicolors.red.."[!]"..ansicolors.reset..' ERROR:', err) + print(cl.red.."[!]"..cl.reset..' ERROR:', err) core.clearCommandBuffer() return nil, err end + local function help() print(copyright) print(author) print(version) print(desc) - print(ansicolors.cyan..'Usage'..ansicolors.reset) + print(cl.cyan..'Usage'..cl.reset) print(usage) - print(ansicolors.cyan..'Arguments'..ansicolors.reset) + print(cl.cyan..'Arguments'..cl.reset) print(arguments) - print(ansicolors.cyan..'Example usage'..ansicolors.reset) + print(cl.cyan..'Example usage'..cl.reset) print(example) end + -- Sorry, didn't care to figure out custom bit amounts with the 14a lua lib. So here's this thing +-- 20/23 local function wupc2() return { [0] = 'hf 14a raw -akb 7 20', @@ -102,6 +120,7 @@ local function wupc2() } end +-- 40/43 local function wupc() return{ [0] = 'hf 14a raw -akb 7 40', @@ -112,6 +131,7 @@ end local function makenuid(uid) core.console('ana nuid -d '..uid) end + local function sendCmds(cmds) for i = 0, #cmds do if cmds[i] then @@ -120,41 +140,61 @@ local function sendCmds(cmds) end end end + local function wakeupmagic(writetype) - if writetype=="2" then sendCmds(wupc2()) elseif writetype=="4" then sendCmds(wupc()) end + if writetype == "2" then + sendCmds(wupc2()) + elseif writetype == "4" then + sendCmds(wupc()) + end end + local function calculate_block0(useruid) local uidbytes = utils.ConvertHexToBytes(useruid) local i = 1 local bcc = bxor(uidbytes[i], uidbytes[i+1]); - local length = #useruid / 2; + + -- floor division + local length = #useruid // 2; -- bcc - for i = 3, length, 1 do bcc = bxor(bcc, uidbytes[i]) end + for i = 3, length, 1 do + bcc = bxor(bcc, uidbytes[i]) + end -- block0 local block0 = "" - for i = 1, length, 1 do block0 = block0..string.format('%02X', uidbytes[i]) end + for i = 1, length, 1 do + block0 = block0..string.format('%02X', uidbytes[i]) + end return block0..string.format('%02X', bcc) end + local function cltwo_block0(uid) payload = uid payload = payload .. "884400000000000000" return payload end + local function SectorHeader(sector) - print("["..ansicolors.yellow.."="..ansicolors.reset.."] # | sector "..ansicolors.green..string.format("%02d", sector)..ansicolors.reset.." / "..ansicolors.green..string.format("0x%02X", sector)..ansicolors.reset) - print("["..ansicolors.yellow.."="..ansicolors.reset.."] ----+------------------------------------------------") + if sector == nil then return end + + print("["..cl.yellow.."="..cl.reset.."] # | sector "..cl.green..string.format("%02d", sector)..cl.reset.." / "..cl.green..string.format("0x%02X", sector)..cl.reset) + print("["..cl.yellow.."="..cl.reset.."] ----+------------------------------------------------") end + local function BlockParser(data, block) - if block == "0" or block == 0 then -- for block 0 - print("["..ansicolors.yellow.."="..ansicolors.reset.."] "..string.format("%02d", block).." | "..ansicolors.red..string.sub(data,1,2).." "..string.sub(data,3,4).." "..string.sub(data,5,6).." "..string.sub(data,7,8).." "..string.sub(data,9,10).." "..string.sub(data,11,12).." "..string.sub(data,13,14).." "..string.sub(data,15,16).." "..string.sub(data,17,18).." "..string.sub(data,19,20).." "..string.sub(data,21,22).." "..string.sub(data,23,24).." "..string.sub(data,25,26).." "..string.sub(data,27,28).." "..string.sub(data,29,30).." "..string.sub(data,31,32)..ansicolors.reset) - elseif (block+1)%4 == 0 then -- for ST - print("["..ansicolors.yellow.."="..ansicolors.reset.."] "..string.format("%02d", block).." | "..ansicolors.yellow..string.sub(data,1,2).." "..string.sub(data,3,4).." "..string.sub(data,5,6).." "..string.sub(data,7,8).." "..string.sub(data,9,10).." "..string.sub(data,11,12).." "..ansicolors.magenta..string.sub(data,13,14).." "..string.sub(data,15,16).." "..string.sub(data,17,18).." "..ansicolors.reset..string.sub(data,19,20).." "..ansicolors.yellow..string.sub(data,21,22).." "..string.sub(data,23,24).." "..string.sub(data,25,26).." "..string.sub(data,27,28).." "..string.sub(data,29,30).." "..string.sub(data,31,32)..ansicolors.reset) - else - print("["..ansicolors.yellow.."="..ansicolors.reset.."] "..string.format("%02d", block).." | "..string.sub(data,1,2).." "..string.sub(data,3,4).." "..string.sub(data,5,6).." "..string.sub(data,7,8).." "..string.sub(data,9,10).." "..string.sub(data,11,12).." "..string.sub(data,13,14).." "..string.sub(data,15,16).." "..string.sub(data,17,18).." "..string.sub(data,19,20).." "..string.sub(data,21,22).." "..string.sub(data,23,24).." "..string.sub(data,25,26).." "..string.sub(data,27,28).." "..string.sub(data,29,30).." "..string.sub(data,31,32)) end + if data == nil or block == nil then return end + if block == "0" or block == 0 then -- for block 0 + print("["..cl.yellow.."="..cl.reset.."] "..string.format("%02d", block).." | "..cl.red..string.sub(data,1,2).." "..string.sub(data,3,4).." "..string.sub(data,5,6).." "..string.sub(data,7,8).." "..string.sub(data,9,10).." "..string.sub(data,11,12).." "..string.sub(data,13,14).." "..string.sub(data,15,16).." "..string.sub(data,17,18).." "..string.sub(data,19,20).." "..string.sub(data,21,22).." "..string.sub(data,23,24).." "..string.sub(data,25,26).." "..string.sub(data,27,28).." "..string.sub(data,29,30).." "..string.sub(data,31,32)..cl.reset) + elseif (block+1)%4 == 0 then -- for ST + print("["..cl.yellow.."="..cl.reset.."] "..string.format("%02d", block).." | "..cl.yellow..string.sub(data,1,2).." "..string.sub(data,3,4).." "..string.sub(data,5,6).." "..string.sub(data,7,8).." "..string.sub(data,9,10).." "..string.sub(data,11,12).." "..cl.magenta..string.sub(data,13,14).." "..string.sub(data,15,16).." "..string.sub(data,17,18).." "..cl.reset..string.sub(data,19,20).." "..cl.yellow..string.sub(data,21,22).." "..string.sub(data,23,24).." "..string.sub(data,25,26).." "..string.sub(data,27,28).." "..string.sub(data,29,30).." "..string.sub(data,31,32)..cl.reset) + else + print("["..cl.yellow.."="..cl.reset.."] "..string.format("%02d", block).." | "..string.sub(data,1,2).." "..string.sub(data,3,4).." "..string.sub(data,5,6).." "..string.sub(data,7,8).." "..string.sub(data,9,10).." "..string.sub(data,11,12).." "..string.sub(data,13,14).." "..string.sub(data,15,16).." "..string.sub(data,17,18).." "..string.sub(data,19,20).." "..string.sub(data,21,22).." "..string.sub(data,23,24).." "..string.sub(data,25,26).." "..string.sub(data,27,28).." "..string.sub(data,29,30).." "..string.sub(data,31,32)) + end end + local function sendRaw(rawdata, keep) flags = lib14a.ISO14A_COMMAND.ISO14A_RAW + lib14a.ISO14A_COMMAND.ISO14A_APPEND_CRC if keep == true then flags = flags + lib14a.ISO14A_COMMAND.ISO14A_NO_DISCONNECT end @@ -178,24 +218,28 @@ end local function readconf() configbuffer = sendRaw("E000", true) if string.len(configbuffer) ~= 36 then - oops("Tag sent wrong length of config!") - lib14a.disconnect() - return 1 end - return utils.ConvertHexToBytes(string.sub(configbuffer,1,32)) + oops("Tag sent wrong length of config!") + lib14a.disconnect() + return 1 + end + return utils.ConvertHexToBytes(string.sub(configbuffer, 1, 32)) end + local function writeconf(configbuffer) configbuffer=utils.ConvertBytesToHex(configbuffer) - print(ansicolors.yellow.."[|]".. ansicolors.reset .." The new config is: "..configbuffer) + print(cl.yellow.."[|]".. cl.reset .." The new config is: "..configbuffer) if sendRaw("E100", true) == "0A" then if sendRaw(configbuffer, true) == "0A" then - print(ansicolors.yellow.."[/]".. ansicolors.reset .." Config updated successfully") + print(cl.yellow.."[/]".. cl.reset .." Config updated successfully") else oops("Tag did not ACK config update!") lib14a.disconnect() - return 1 end + return 1 + end else oops("Tag did not ACK `E100` command!") lib14a.disconnect() - return 1 end + return 1 + end end -- End config functions @@ -244,22 +288,25 @@ function main(args) if o == 'w' then wipe = true end -- So one odd thing I noticed is the bool args like -h, -w don't work without a 2nd argument. So you now must do -h 1.. what? Why? -- ConfigStar - if o == 'g' then if a == "1" then gen1 = true elseif a == "0" then gen1 = false end end - if o == 'c' then if a == "1" then gen1com= true elseif a == "0" then gen1com= false end end - if o == 'b' then if a == "1" then keyblock= true elseif a == "0" then keyblock= false end end - if o == '2' then if a == "1" then cuid= true elseif a == "0" then cuid= false end end - if o == '7' then if a == "1" then cl2mode= true elseif a == "0" then cl2mode= false end end - if o == 'd' then if a == "1" then shadowmode = true elseif a == "0" then shadowmode = false end end - if o == 'a' then if a == "1" then magicauth= true elseif a == "0" then magicauth= false end end - if o == 'n' then if a == "1" then statenc= true elseif a == "0" then statenc= false end end - if o == 'r' then if a == "1" then sigsec = true elseif a == "0" then sigsec= false end end + if o == 'g' then if a == "1" then gen1 = true elseif a == "0" then gen1 = false end end + if o == 'c' then if a == "1" then gen1com= true elseif a == "0" then gen1com= false end end + if o == 'b' then if a == "1" then keyblock= true elseif a == "0" then keyblock= false end end + if o == '2' then if a == "1" then cuid= true elseif a == "0" then cuid= false end end + if o == '7' then if a == "1" then cl2mode= true elseif a == "0" then cl2mode= false end end + if o == 'd' then if a == "1" then shadowmode = true elseif a == "0" then shadowmode = false end end + if o == 'a' then if a == "1" then magicauth= true elseif a == "0" then magicauth= false end end + if o == 'n' then if a == "1" then statenc= true elseif a == "0" then statenc= false end end + if o == 'r' then if a == "1" then sigsec = true elseif a == "0" then sigsec= false end end end - if gen1 ~= nil or gen1com~= nil or keyblock~= nil or cuid~= nil or cl2mode~= nil or shadowmode~= nil or magicauth~= nil or statenc~= nil or sigsec~= nil then configwrite = true end - if targetbblk then if tonumber(targetbblk)>63 then oops("Block is above 63") return 1 end end - if targetblk then if tonumber(targetblk)>63 then oops("Block is above 63") return 1 end end - if targetsec then if tonumber(targetsec)>15 then oops("Sector is above 15") return 1 end end - if targetbsec then if tonumber(targetbsec)>15 then oops("Sector is above 15") return 1 end end + if gen1 ~= nil or gen1com~= nil or keyblock~= nil or cuid~= nil or cl2mode~= nil or shadowmode~= nil or magicauth~= nil or statenc~= nil or sigsec~= nil then + configwrite = true + end + + if targetbblk then if tonumber(targetbblk) > 63 then oops("Block is above 63") return 1 end end + if targetblk then if tonumber(targetblk) > 63 then oops("Block is above 63") return 1 end end + if targetsec then if tonumber(targetsec) > 15 then oops("Sector is above 15") return 1 end end + if targetbsec then if tonumber(targetbsec) > 15 then oops("Sector is above 15") return 1 end end -- -- Alright, here's the logic. -- 1. Set the write type (0x20, 0x40, 8000 auth, etc...) @@ -267,33 +314,42 @@ function main(args) -- 3. Form data to write -- 4. Issue commands if wipe == true then - print(ansicolors.red.."[/]"..ansicolors.reset.." Wipe issued! Nullifying other arguments!") - print(ansicolors.red.."[-]"..ansicolors.reset.." DO NOT REMOVE YOUR TAG!") + + print(cl.red.."[/]"..cl.reset.." Wipe issued! Nullifying other arguments!") + print(cl.red.."[-]"..cl.reset.." DO NOT REMOVE YOUR TAG!") + uid = nil signature = nil configwrite = nil + wakeupmagic(writetype) if sendRaw("F000", true) ~= "0A" then oops("DANGER! Tag did not ACK wipe command. The field has NOT been reset.") print("[ ] If you think the wipe succeeded, immediately do this:") print("hf 14a raw -kc E100; hf 14a raw -c 7AFF0000000000000000000000000008") - return 1 end + return 1 + end + writeconf(utils.ConvertHexToBytes("7AFF0000000000000000005A00000008")) + sendRaw("F800", true) -- here you only wipe the backdoor blocks and they're not super critical so might as well not check. sendRaw("A000", true) -- By this point I just rely on the tag. sendRaw("DE7715B8040804000000000000000000", true) - for i =0,15 do - blk=string.format("%02x", 4*i+3):gsub("0x","") + + for i =0, 15 do + blk=string.format("%02x", 4 * i + 3):gsub("0x","") sendRaw("A0"..blk, true) - sendRaw("FFFFFFFFFFFFFF078069FFFFFFFFFFFF",true) - sendRaw("A8"..blk,true) - sendRaw("FFFFFFFFFFFFFF078069FFFFFFFFFFFF",true) + sendRaw("FFFFFFFFFFFFFF078069FFFFFFFFFFFF", true) + sendRaw("A8"..blk, true) + sendRaw("FFFFFFFFFFFFFF078069FFFFFFFFFFFF", true) end + sendRaw("A807", true) - sendRaw("75CCB59C9BED70F0F8694B791BEA7BCC",true) - print(ansicolors.yellow.."[-]"..ansicolors.reset.." Wipe completed successfully") + sendRaw("75CCB59C9BED70F0F8694B791BEA7BCC", true) + print(cl.yellow.."[-]"..cl.reset.." Wipe completed successfully") lib14a.disconnect() end + -- Separator if targetblk or targetbblk or targetsec or targetbsec then uid = nil @@ -301,18 +357,51 @@ function main(args) configwrite = nil wakeupmagic(writetype) print("") + if targetblk or targetsec then - if targetblk then data = sendRaw("30"..string.format("%02x", targetblk), false) end - if targetblk then SectorHeader(targetblk/4) else SectorHeader(targetsec) end - if targetblk then BlockParser(data, targetblk) else for i=0,3 do BlockParser(sendRaw("30"..string.format("%02x", targetsec*4+i), true), targetsec*4+i) end end + if targetblk then + data = sendRaw("30"..string.format("%02x", targetblk), false) + end + + if targetblk then + -- floor division... + SectorHeader(targetblk // 4) + else + SectorHeader(targetsec) + end + + if targetblk then + BlockParser(data, targetblk) + else + for i=0, 3 do + BlockParser(sendRaw("30"..string.format("%02x", targetsec * 4 + i), true), targetsec * 4 + i) + end + end + elseif targetbblk or targetbsec then - if targetbblk then data=sendRaw("38"..string.format("%02x", targetbblk), false) end - if targetbblk then SectorHeader(targetbblk/4) else SectorHeader(targetbsec) end - if targetbblk then BlockParser(data, targetbblk) else for i=0,3 do BlockParser(sendRaw("38"..string.format("%02x", targetbsec*4+i), true), targetbsec*4+i) end end + if targetbblk then + data = sendRaw("38"..string.format("%02x", targetbblk), false) + end + + if targetbblk then + -- floor division + SectorHeader(targetbblk // 4) + else + SectorHeader(targetbsec) + end + + if targetbblk then + BlockParser(data, targetbblk) + else + for i =0, 3 do + BlockParser(sendRaw("38"..string.format("%02x", targetbsec * 4 + i), true), targetbsec * 4 + i) + end + end -- Actually is there an sprintf_hex in lua? end - lib14a.disconnect() + lib14a.disconnect() end + -- Separator if uid then if writetype == "2" or writetype == "4" then @@ -325,72 +414,101 @@ function main(args) payload = payload .. "04000000000000000000" elseif string.len(uid) == 14 then -- Same logic, but with raw anticollision data because that's what the tag accepts. :P - payload = calculate_block0("88"..string.sub(uid,1,6)) + payload = calculate_block0("88"..string.sub(uid, 1, 6)) payload = payload .. "04" - payload = payload .. calculate_block0(string.sub(uid,7,14)) + payload = payload .. calculate_block0(string.sub(uid, 7, 14)) payload = payload .. "08" payload = payload .. "00000000" end end + core.clearCommandBuffer() -- Now, let's write! 1. We wake up the tag in magic mode. -- 2. We will deal with the "easier" 7 byte UID stuff - if uid then if string.len(uid) == 14 then + wakeupmagic(writetype) - if f3perso == true then print("[?] WARNING: F3 perso write is set, but 7 byte UID is passed. Ignoring -3 argument") end + if f3perso == true then + print("[?] WARNING: F3 perso write is set, but 7 byte UID is passed. Ignoring -3 argument") + end + local configdata = readconf() + if configdata[10] ~= 0x5A and configdata[10] ~= 0xC3 and configdata[10] ~= 0xA5 then -- Enable CL2 mode if necessary print("[?] WARNING: Tag is not in 7 byte UID mode. Automatically updating to F0 unfused") - print(ansicolors.yellow.."[-]".. ansicolors.reset .." This is because the configuration byte responsible for CL2 was not found to be equal to 0x5A, 0xC3 or 0xA5, but rather: ".. string.format("%02x", configdata[10])) - print(ansicolors.yellow.."[\\]".. ansicolors.reset .." The old config is: ".. utils.ConvertBytesToHex(configdata)) - configdata[10]=0x5A + print(cl.yellow.."[-]".. cl.reset .." This is because the configuration byte responsible for CL2 was not found to be equal to 0x5A, 0xC3 or 0xA5, but rather: ".. string.format("%02x", configdata[10])) + print(cl.yellow.."[\\]".. cl.reset .." The old config is: ".. utils.ConvertBytesToHex(configdata)) + configdata[10] = 0x5A writeconf(configdata) end + if sendRaw("A800", true) ~= "0A" then oops("Tag did not ACK `A800` command!") lib14a.disconnect() - return 1 end + return 1 + end + print("[?] WARNING: nUID should be updated with this value:") - print(makenuid(uid)) - print(ansicolors.yellow.."[/]".. ansicolors.reset .." Use `--f3d` to update nUID for Perso F3 only.") - if sendRaw(payload, true) ~= "0A" then - oops("Tag did not ACK data to write!") - lib14a.disconnect() - return 1 end - print(ansicolors.yellow.."[-]".. ansicolors.reset .." Updating real block 0") - if sendRaw("A000", true) ~= "0A" then - oops("Tag did not ACK `A000` command!") - lib14a.disconnect() - return 1 end - if sendRaw(cltwo_block0(uid), false) ~="0A" then oops("Tag did not ACK data to write!") end - -- Now, let's work with 4 byte UIDs. - elseif string.len(uid)==8 then - wakeupmagic(writetype) - local configdata = readconf() - if configdata[10] == 0x69 or f3perso == true then -- If we have Perso: F3, then write backdoor blk 1 - if f3perso == true then print ("[?] WARNING: F3 flag enabled. Updating UID used for F3 perso") end - if sendRaw("A801", true) ~= "0A" then + print(makenuid(uid)) + print(cl.yellow.."[/]".. cl.reset .." Use `--f3d` to update nUID for Perso F3 only.") + + if sendRaw(payload, true) ~= "0A" then + oops("Tag did not ACK data to write!") + lib14a.disconnect() + return 1 + end + + print(cl.yellow.."[-]".. cl.reset .." Updating real block 0") + if sendRaw("A000", true) ~= "0A" then + oops("Tag did not ACK `A000` command!") + lib14a.disconnect() + return 1 + end + + if sendRaw(cltwo_block0(uid), false) ~="0A" then + oops("Tag did not ACK data to write!") + end + + -- Now, let's work with 4 byte UIDs. + elseif string.len(uid) == 8 then + + wakeupmagic(writetype) + local configdata = readconf() + + if configdata[10] == 0x69 or f3perso == true then -- If we have Perso: F3, then write backdoor blk 1 + + if f3perso == true then + print ("[?] WARNING: F3 flag enabled. Updating UID used for F3 perso") + end + + if sendRaw("A801", true) ~= "0A" then oops("Tag did not ACK `A801` command!") lib14a.disconnect() - return 1 end + return 1 + end + else -- Otherwise write real block 0. if configdata[10] == 0x5a or configdata[10] == 0xc3 or configdata[10] == 0xa5 then -- Disable CL2 if necessary print("[?] WARNING: Tag is not in 4 byte UID mode. Automatically disabling") - print(ansicolors.yellow.."[-]".. ansicolors.reset .." This is because the configuration byte responsible for CL2 was found to be equal to: ".. string.format("%02x", configdata[10])) - print(ansicolors.yellow.."[\\]".. ansicolors.reset .." The old config is: ".. utils.ConvertBytesToHex(configdata)) - configdata[10]=0x00 + print(cl.yellow.."[-]".. cl.reset .." This is because the configuration byte responsible for CL2 was found to be equal to: ".. string.format("%02x", configdata[10])) + print(cl.yellow.."[\\]".. cl.reset .." The old config is: ".. utils.ConvertBytesToHex(configdata)) + configdata[10] = 0x00 writeconf(configdata) end + if sendRaw("A000", true) ~= "0A" then oops("Tag did not ACK `A000` command!") lib14a.disconnect() - return 1 end + return 1 + end end - if sendRaw(payload, false) ~= "0A" then oops("Tag did not ACK data to write!") end + + if sendRaw(payload, false) ~= "0A" then + oops("Tag did not ACK data to write!") end end end + -- Separator if signature then wakeupmagic(writetype) @@ -400,35 +518,49 @@ function main(args) configdata[14] = 0x5A writeconf(configdata) end + if sendRaw("A805", true) ~= "0A" then oops("Tag did not ACK `A805` command!") lib14a.disconnect() - return 1 end + return 1 + end + if sendRaw(string.sub(signature,1,32), true) ~= "0A" then oops("Tag did not ACK data 1 to write!") lib14a.disconnect() - return 1 end + return 1 + end + if sendRaw("A806", true) ~= "0A" then oops("Tag did not ACK `A806` command!") lib14a.disconnect() - return 1 end + return 1 + end + if sendRaw(string.sub(signature,33,64), false) ~= "0A" then - oops("Tag did not ACK data 2 to write!") + oops("Tag did not ACK data 2 to write!") lib14a.disconnect() - return 1 end + return 1 + end end + if configwrite then - print(ansicolors.yellow.."[|]"..ansicolors.reset.." Welcome to ConfigStar!") + + print(cl.yellow.."[|]"..cl.reset.." Welcome to ConfigStar!") wakeupmagic(writetype) - config=readconf() + config = readconf() + if (gen1 == false and magicauth == false) or ((config[1]==0x85 and config[2] == 0x00) and magicauth==false) or ((config[12]==0x00) and gen1 == false) then - oops("What you are about to do is potentially dangerous. \n If you really want to continue (potentially leaving your tag in an unusable state), enter this line as given, without quotation marks:\n \"Yes, do as I say!\"") - local ans=io.read() - if ans ~="Yes, do as I say!" then + oops("What you are about to do is potentially dangerous. \nIf you really want to continue (potentially leaving your tag in an unusable state), enter this line as given, without quotation marks:\n \"yes\"") + local ans = io.read() + if ans ~="yes" then lib14a.disconnect() return 1 - else print(ansicolors.red.."[/]"..ansicolors.reset.." Brace yourself.") end + else + print(cl.red.."[/]"..cl.reset.." Brace yourself.") end + + end -- Baby oh baby -- Prepare for disappointment if gen1 == true then @@ -438,49 +570,59 @@ function main(args) config[1] = 0x85 config[2] = 0x00 end + if gen1com == true then config[3] = 0x85 elseif gen1com == false then config[3] = 0x00 end + if keyblock == true then config[7] = 0x5A elseif keyblock == false then config[7] = 0x00 end + if cuid == true then config[8] = 0x5A elseif cuid == false then config[8] = 0x00 end + if cl2mode == true then config[10] = 0x5A elseif cl2mode == false then config[10] = 0x00 end + if shadowmode == true then config[11] = 0x5A elseif shadowmode == false then config[11] = 0x00 end + if magicauth == true then config[12] = 0x5A elseif magicauth == false then config[12] = 0x00 end + if statenc == true then config[13] = 0x5A elseif statenc == false then config[13] = 0x00 end + if sigsec == true then config[14] = 0x5A elseif sigsec == false then config[14] = 0x00 end + writeconf(config) - print(ansicolors.yellow.."[\\]"..ansicolors.reset.." Completed!") + print(cl.yellow.."[\\]"..cl.reset.." Completed!") lib14a.disconnect() end end + main(args) diff --git a/client/luascripts/hf_mfu_uidkeycalc_italy.lua b/client/luascripts/hf_mfu_pwdgen_italy.lua similarity index 92% rename from client/luascripts/hf_mfu_uidkeycalc_italy.lua rename to client/luascripts/hf_mfu_pwdgen_italy.lua index d2fbdadde..25b111866 100644 --- a/client/luascripts/hf_mfu_uidkeycalc_italy.lua +++ b/client/luascripts/hf_mfu_pwdgen_italy.lua @@ -2,7 +2,7 @@ local bin = require('bin') local getopt = require('getopt') local lib14a = require('read14a') local utils = require('utils') -local ansicolors = require('ansicolors') +local cl = require('ansicolors') copyright = '' author = "Iceman" @@ -10,13 +10,15 @@ version = 'v1.0.2' desc = [[ This script calculates mifare Ultralight-EV1 pwd based on uid diversification for an Italian ticketsystem Algo not found by me. -]] + +]].. "You can also look at the native pm3 command `" .. cl.yellow .. "hf mfu pwdgen -h" .. cl.reset .. "`\n" + example =[[ -- if called without, it reads tag uid - script run hf_mfu_uidkeycalc_italy + script run hf_mfu_pwdgen_italy -- - script run hf_mfu_uidkeycalc_italy -u 11223344556677 + script run hf_mfu_pwdgen_italy -u 11223344556677 ]] usage = [[ script run hf_mfu_uidkeycalc_italy -h -u " @@ -56,11 +58,11 @@ local function help() print(author) print(version) print(desc) - print(ansicolors.cyan..'Usage'..ansicolors.reset) + print(cl.cyan..'Usage'..cl.reset) print(usage) - print(ansicolors.cyan..'Arguments'..ansicolors.reset) + print(cl.cyan..'Arguments'..cl.reset) print(arguments) - print(ansicolors.cyan..'Example usage'..ansicolors.reset) + print(cl.cyan..'Example usage'..cl.reset) print(example) end -- diff --git a/client/pyscripts/findbits.py b/client/pyscripts/findbits.py index 809465a2e..4cc3b92d9 100755 --- a/client/pyscripts/findbits.py +++ b/client/pyscripts/findbits.py @@ -31,7 +31,7 @@ def invert(data): def search(target,data): location = data.find(target) if location >= 0: - print('*** Match at bit {:d}: {}<{}>{}'.format(location, data[:location],target,data[location+len(target):])) + print('*** Match at bit {:d}: {}<{}>{}'.format(location, data[:location], target,data[location + len(target):])) else: print('Not found') diff --git a/client/pyscripts/fm11rf08_full.py b/client/pyscripts/fm11rf08_full.py new file mode 100644 index 000000000..e684f904d --- /dev/null +++ b/client/pyscripts/fm11rf08_full.py @@ -0,0 +1,995 @@ +#!/usr/bin/env python3 + +# ------------------------------------------------------------------------------ +# Imports +# +import re +import os +import sys +import argparse +import pm3 +import struct +import json + +from fm11rf08s_recovery import recovery + +author = "@csBlueChip" +script_ver = "1.2.0" + +# Copyright @csBlueChip + +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. + +# See LICENSE.txt for the text of the license. + +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +# The original version of this script can be found at: +# https://github.com/csBlueChip/Proxmark_Stuff/tree/main/MiFare_Docs/Fudan_RF08(S)/PM3_Script +# The original version is released with an MIT Licence. +# Or please reach out to me [BlueChip] personally for alternative licenses. + + +# optional color support .. `pip install ansicolors` +try: + from colors import color +except ModuleNotFoundError: + def color(s, fg=None): + _ = fg + return str(s) + + +def initlog(): + """Print and Log: init globals + +globals: +- logbuffer (W) +- logfile (W) +""" + global logbuffer + global logfile + logbuffer = '' + logfile = None + + +def startlog(uid, dpath, append=False): + """Print and Log: set logfile and flush logbuffer + +globals: +- logbuffer (RW) +- logfile (RW) +""" + global logfile + global logbuffer + + logfile = f"{dpath}hf-mf-{uid.hex().upper()}-log.txt" + if append is False: + with open(logfile, 'w'): + pass + if logbuffer != '': + with open(logfile, 'a') as f: + f.write(logbuffer) + logbuffer = '' + + +def lprint(s='', end='\n', flush=False, prompt="[" + color("=", fg="yellow") + "] ", log=True): + """Print and Log + +globals: +- logbuffer (RW) +- logfile (R) +""" + + s = f"{prompt}" + f"\n{prompt}".join(s.split('\n')) + print(s, end=end, flush=flush) + + if log is True: + global logbuffer + if logfile is not None: + with open(logfile, 'a') as f: + f.write(s + end) + else: + # buffering + logbuffer += s + end + + +def main(): + """== MAIN == + +globals: +- p (W) +""" + global p + p = pm3.pm3() # console interface + initlog() + + if not checkVer(): + return + dpath = getPrefs() + args = parseCli() + + # No logfile name yet + lprint("Fudan FM11RF08[S] full card recovery") + lprint("\nDump folder... " + color(f"{dpath}", fg="yellow")) + + # FIXME: script is announced as for RF08 and for RF08S but it comprises RF32N key + # and if RF08 is supported, all other NXP/Infineon with same backdoor can be treated + # by the same script (once properly implemented, see other FIXME) + bdkey, blk0 = getBackdoorKey() + if bdkey is None: + return + uid = getUIDfromBlock0(blk0) + startlog(uid, dpath, append=False) + decodeBlock0(blk0) + fudanValidate(blk0, args.validate) + + mad = False + keyfile = f"{dpath}hf-mf-{uid.hex().upper()}-key.bin" + + # FIXME: nr of sectors depend on the tag. RF32N is 40, RF32 is 64, RF08 is 16, RF08S is 16+1 + # Currently loadKeys is hardcoded for RF08S + if args.force or (key := loadKeys(keyfile)) is None: + if args.recover is False: + s = color("--recover", fg="yellow") + lprint(f" Keys not loaded, use {s} to run recovery script [slow]", prompt="[" + color("!", fg="red") + "]") + else: + # FIXME: recovery() is only for RF08S. TODO for the other ones with a "darknested" attack + keyfile = recoverKeys() + key = loadKeys(keyfile) + + if key is not None: + ret, mad, key = verifyKeys(key) + if ret is False: + if args.nokeys is False: + s = color("--nokeys", fg="yellow") + lprint(f" Use {s} to keep going past this point", prompt="[" + color("!", fg="red") + "]") + return + + # FIXME: nr of blocks depend on the tag. RF32 is 256, RF08 is 64, RF08S is 64+8 + # Currently readBlocks is hardcoded for RF08S + data, blkn = readBlocks(bdkey, args.fast) + data = patchKeys(data, key) + + dump18 = diskDump(data, uid, dpath) # save it before you do anything else + + dumpData(data, blkn) + + # FIXME: nr of blocks depend on the tag. RF32 is 256, RF08 is 64, RF08S is 64+8, + # Currently dumpAcl is hardcoded for RF08S + dumpAcl(data) + + if (mad is True) or (args.mad is True): + dumpMad(dump18) + + if (args.bambu is True) or (detectBambu(data) is True): + dumpBambu(data) + + lprint("\nTadah!") + + return + + +def getPrefs(): + """Get PM3 preferences + +globals: +- p (R) +""" + p.console("prefs show --json") + prefs = json.loads(p.grabbed_output) + dpath = prefs['file.default.dumppath'] + os.path.sep + return dpath + + +def checkVer(): + """Assert python version""" + required_version = (3, 8) + if sys.version_info < required_version: + print(f"Python version: {sys.version}") + print(f"The script needs at least Python v{required_version[0]}.{required_version[1]}. Abort.") + return False + return True + + +def parseCli(): + """Parse the CLi arguments""" + parser = argparse.ArgumentParser(description='Full recovery of Fudan FM11RF08* cards.') + + parser.add_argument('-n', '--nokeys', action='store_true', help='extract data even if keys are missing') + parser.add_argument('-r', '--recover', action='store_true', help='run key recovery script if required') + parser.add_argument('-f', '--force', action='store_true', help='force recovery of keys') + parser.add_argument('-b', '--bambu', action='store_true', help='force Bambu tag decode') + parser.add_argument('-m', '--mad', action='store_true', help='force M.A.D. decode') + parser.add_argument('-v', '--validate', action='store_true', help='check Fudan signature (requires internet)') + parser.add_argument('--fast', action='store_true', help='use ecfill for faster card transactions') + + args = parser.parse_args() + + if args.force is True: + args.recover = True + return args + + +def getBackdoorKey(): + """Find backdoor key +[=] # | sector 00 / 0x00 | ascii +[=] ----+-------------------------------------------------+----------------- +[=] 0 | 5C B4 9C A6 D2 08 04 00 04 59 92 25 BF 5F 70 90 | \\........Y.%._p. + +globals: +- p (R) +""" + + # FM11RF08S FM11RF08 FM11RF32 + dklist = ["A396EFA4E24F", "A31667A8CEC1", "518b3354E760"] + + lprint("\nTrying known backdoor keys...") + + bdkey = "" + for k in dklist: + cmd = f"hf mf rdbl -c 4 --key {k} --blk 0" + lprint(f"\n`{cmd}`", end='', flush=True) + res = p.console(cmd) + for line in p.grabbed_output.split('\n'): + if " | " in line and "# | s" not in line: + blk0 = line[10:56+1] + if res == 0: + s = color('ok', fg='green') + lprint(f" ( {s} )", prompt='') + bdkey = k + break + s = color('fail', fg='yellow') + lprint(f" ( {s} ) [{res}]", prompt='') + + if bdkey == "": + lprint("\n Unknown key, or card not detected.", prompt="[" + color("!", fg="red") + "]") + return None, None + return bdkey, blk0 + + +def getUIDfromBlock0(blk0): + """Extract UID from block 0""" + uids = blk0[0:11] # UID string : "11 22 33 44" + uid = bytes.fromhex(uids.replace(' ', '')) # UID (bytes) : 11223344 + return uid + + +def decodeBlock0(blk0): + """Extract data from block 0""" + lprint() + lprint(" UID BCC ++----- RF08 ID -----++") + lprint(" ! ! SAK !! !!") + lprint(" ! ! ! ATQA !! Fudan Sig !!") + lprint(" !---------. !. !. !---. VV .---------------. VV") + # 0 12 15 18 24 27 45 + # ! ! ! ! ! ! ! + # 00 11 22 33 44 55 66 77 88 99 AA BB CC DD EE FF + lprint(f" Block 0 : {blk0}") + + # --- decode block 0 --- + + uid = getUIDfromBlock0(blk0) + bcc = int(blk0[12:14], 16) # BCC + chk = 0 # calculate checksum + for h in uid: + chk ^= h + + sak = int(blk0[15:17], 16) # SAK + atqa = int(blk0[18:23].replace(' ', ''), 16) # 0x7788 + + fida = int(blk0[24:26], 16) # Fudan ID 0x88 + fidb = int(blk0[45:47], 16) # Fudan ID 0xFF + # fid = (fida<<8)|fidb # Fudan ID 0x88FF + + hash = blk0[27:44] # Fudan hash "99 AA BB CC DD EE" + + type = f"[{fida:02X}:{fidb:02X}]" # type/name + if fidb == 0x90: + if fida == 0x01 or fida == 0x03 or fida == 0x04: + type += " - Fudan FM11RF08S" + + elif fidb == 0x1D: + if fida == 0x01 or fida == 0x02 or fida == 0x03: + type += " - Fudan FM11RF08" + + elif fidb == 0x91 or fidb == 0x98: + type += " - Fudan FM11RF08 (never seen in the wild)" + + else: + type += " - Unknown (please report)" + + # --- show results --- + + lprint() + + if bcc == chk: + desc = "verified" + else: + desc = f"fail. Expected {chk:02X}" + lprint(f" UID/BCC : {uid.hex().upper()}/{bcc:02X} - {desc}") + + if sak == 0x01: + desc = "NXP MIFARE TNP3xxx 1K" + elif sak == 0x08: + desc = "NXP MIFARE CLASSIC 1k | Plus 1k | Ev1 1K" + elif sak == 0x09: + desc = "NXP MIFARE Mini 0.3k" + elif sak == 0x10: + desc = "NXP MIFARE Plus 2k" + elif sak == 0x18: + desc = "NXP MIFARE Classic 4k | Plus 4k | Ev1 4k" + else: + desc = "{unknown}" + lprint(f" SAK : {sak:02X} - {desc}") + lprint(f" ATQA : {atqa:04X}") # show ATQA + lprint(f" Fudan ID : {type}") # show type + lprint(f" Fudan Sig: {hash}") # show ?Partial HMAC? + + +def fudanValidate(blk0, live=False): + """Fudan validation""" + url = "https://rfid.fm-uivs.com/nfcTools/api/M1KeyRest" + hdr = "Content-Type: application/text; charset=utf-8" + post = f"{blk0.replace(' ', '')}" + + lprint(f"\n Validator:\n`wget -q -O -" + f" --header=\"{hdr}\"" + f" --post-data \"{post}\"" + f" {url}" + " | json_pp`") + + if live: + # Warning, this import causes a "double free or corruption" crash if the script is called twice... + # So for now we limit the import only when really needed + try: + import requests + except ModuleNotFoundError: + s = color("not found", fg="red") + lprint(f"Python module 'requests' {s}, please install!", prompt="[" + color("!", fg="red") + "] ") + return + lprint("\nCheck Fudan signature (requires internet)...") + + headers = {"Content-Type": "application/text; charset=utf-8"} + resp = requests.post(url, headers=headers, data=post) + + if resp.status_code != 200: + lprint(f"HTTP Error {resp.status_code} - check request not processed") + + else: + r = json.loads(resp.text) + if r['data'] is not None: + desc = f" {{{r['data']}}}" + else: + desc = "" + lprint(f"The man from Fudan, he say: {r['code']} - {r['message']}{desc}") + else: + s = color('--validate', fg="yellow") + lprint(f'\n Use {s} to perform Fudan signature check automatically', prompt='[?]') + + +def loadKeys(keyfile): + """Load keys from file + +If keys cannot be loaded AND --recover is specified, then run key recovery +""" + key = [[b'' for _ in range(2)] for _ in range(17)] # create a fresh array + + lprint("\nLoad keys from file... " + color(f"{keyfile}", fg="yellow")) + + try: + with (open(keyfile, "rb")) as fh: + for ab in [0, 1]: + for sec in range((16+2)-1): + key[sec][ab] = fh.read(6) + + except IOError: + return None + + return key + + +def recoverKeys(): + """Run key recovery script""" + badrk = 0 # 'bad recovered key' count (ie. not recovered) + + lprint("\nRunning recovery script, ETA: Less than 30 minutes") + + lprint('\n`-._,-\'"`-._,-"`-._,-\'"`-._,-\'"`-._,-\'"`-._,-\'"`-._,-\'"`-._,-\'"`-._,') + + r = recovery(quiet=False) + keyfile = r['keyfile'] + rkey = r['found_keys'] + # fdump = r['dumpfile'] + # rdata = r['data'] + + lprint('`-._,-\'"`-._,-"`-._,-\'"`-._,-\'"`-._,-\'"`-._,-\'"`-._,-\'"`-._,-\'"`-._,') + + for k in range(0, 16+1): + for ab in [0, 1]: + if rkey[k][ab] == "": + if badrk == 0: + lprint("Some keys were not recovered: ", end='') + else: + lprint(", ", end='', prompt='') + badrk += 1 + + kn = k + if kn > 15: + kn += 16 + lprint(f"[{kn}/", end='', prompt='') + lprint("A]" if ab == 0 else "B]", end='', prompt='') + if badrk > 0: + lprint() + return keyfile + + +def verifyKeys(key): + """Verify keys + +globals: +- p (R) +""" + + badk = 0 + mad = False + + lprint("Checking keys...") + + for sec in range(0, 16+1): # 16 normal, 1 dark + sn = sec + if (sn > 15): + sn = sn + 16 + + for ab in [0, 1]: + bn = (sec * 4) + 3 + if bn >= 64: + bn += 64 + + cmd = f"hf mf rdbl -c {ab} --key {key[sec][ab].hex()} --blk {bn}" + lprint(f" `{cmd}`", end='', flush=True) + + res = p.console(cmd, capture=False) + lprint(" " * (3-len(str(bn))), end='', prompt='') + if res == 0: + s = color("ok", fg="green") + lprint(f" ( {s} )", end='', prompt='') + else: + s = color("fail", fg="red") + lprint(f" ( {s} )", end='', prompt='') + badk += 1 + key[sec][ab] = b'' + + # check for Mifare Application Directory + if (sec == 0) and (ab == 0) \ + and (key[0][0] == b'\xa0\xa1\xa2\xa3\xa4\xa5'): + mad = True + lprint(" - MAD Key", prompt='') + else: + lprint("", prompt='') + + if badk > 0: + s = color(f'{badk}', fg="red") + e = "s exist" if badk != 1 else " exists" + lprint(f" {s} bad key{e}", prompt="[" + color("!", fg="red") + "]") + rv = False, mad, key + + else: + lprint("All keys verified") + rv = True, mad, key + + if mad is True: + lprint("MAD key detected") + + return rv + + +def readBlocks(bdkey, fast=False): + """ +Read all block data - INCLUDING advanced verification blocks + +[=] # | sector 00 / 0x00 | ascii +[=] ----+-------------------------------------------------+----------------- +[=] 0 | 5C B4 9C A6 D2 08 04 00 04 59 92 25 BF 5F 70 90 | \\........Y.%._p. + +globals: +- p (R) +""" + data = [] + blkn = list(range(0, 63 + 1)) + list(range(128, 135 + 1)) + + lprint("\nLoad blocks {0..63, 128..135}[64 + 8 = 72] from the card") + + blkn_todo = blkn + if fast: + # Try fast dump first + # The user uses keyhole #0 (-a) + # The vendor uses keyhole #1 (-b) + # The thief uses keyhole #4 (backdoor) + # |___ + cmd = f"hf mf ecfill -c 4 --key {bdkey}" + lprint(f"`{cmd}`", flush=True, log=False) + p.console(cmd) + for line in p.grabbed_output.split('\n'): + if "ok" in line: + cmd = "hf mf eview" + lprint(f"`{cmd}`", flush=True, log=False) + p.console(cmd) + for line in p.grabbed_output.split('\n'): + if " | " in line and "sec | blk | data" not in line: + lsub = line[11:83] + data.append(lsub) + blkn_todo = list(range(128, 135+1)) + + bad = 0 + for n in blkn_todo: + cmd = f"hf mf rdbl -c 4 --key {bdkey} --blk {n}" + lprint(f" `{cmd}`", flush=True, log=False, end='') + + for retry in range(5): + p.console(cmd) + + found = False + for line in p.grabbed_output.split('\n'): + if " | " in line and "# | s" not in line: + lsub = line[4:76] + data.append(lsub) + found = True + if found: + break + + s = color("ok", fg="green") + if not found: + data.append(f"{n:3d} | -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- | ----------------") + bad += 1 + s = color("fail", fg="red") + + lprint(" " * (3 - len(str(n))), flush=True, log=False, end='', prompt='') + lprint(f' ( {s} )', flush=True, log=False, prompt='') + + s = color("ok", fg="green") + if bad > 0: + s = color("fail", fg="red") + + lprint(f'Loading ( {s} )', log=False) + return data, blkn + + +def patchKeys(data, key): + """Patch keys in to data + 3 | 00 00 00 00 00 00 87 87 87 69 00 00 00 00 00 00 | .........i...... +""" + lprint("\nPatching keys in to data") + + for sec in range(0, 16 + 1): + blk = (sec * 4) + 3 # find "trailer" for this sector + if key is not None: + if key[sec][0] == b'': + keyA = "-- -- -- -- -- -- " + else: + kstr = key[sec][0].hex() + keyA = "".join([kstr[i:i+2] + " " for i in range(0, len(kstr), 2)]) + + if key[sec][1] == b'': + keyB = "-- -- -- -- -- -- " + else: + kstr = key[sec][1].hex() + keyB = "".join([kstr[i:i+2] + " " for i in range(0, len(kstr), 2)]) + + data[blk] = data[blk][:6] + keyA + data[blk][24:36] + keyB + + else: + data[blk] = data[blk][:6] + "-- -- -- -- -- -- " + data[blk][24:36] + "-- -- -- -- -- --" + return data + + +def dumpData(data, blkn): + """Dump data""" + lprint() + lprint("===========") + lprint(" Card Data") + lprint("===========") + lprint() + + cnt = 0 + for n in blkn: + sec = (cnt // 4) + if sec > 15: + sec = sec + 16 + + if (n % 4 == 0): + lprint(f"{sec:2d}:{data[cnt]}") + else: + lprint(f" :{data[cnt]}") + + cnt += 1 + if (cnt % 4 == 0) and (n != blkn[-1]): # Space between sectors + lprint() + + +def detectBambu(data): + """Let's try to detect a Bambu card by the date strings...""" + try: + dl = bytes.fromhex(data[12][6:53]).decode('ascii').rstrip('\x00') + dls = dl[2:13] + ds = bytes.fromhex(data[13][6:41]).decode('ascii').rstrip('\x00') + except Exception: + return False + + # ds 24_03_22_16 + # dl 2024_03_22_16_29 + # yy y y m m d d h h m m + exp = r"20[2-3][0-9]_[0-1][0-9]_[0-3][0-9]_[0-2][0-9]_[0-5][0-9]" + + if re.search(exp, dl) and (ds == dls): + lprint("\nBambu date strings detected.") + return True + else: + lprint("\nBambu date strings not detected.") + return False + + +def dumpBambu(data): + """Dump bambu details + +https://github.com/Bambu-Research-Group/RFID-Tag-Guide/blob/main/README.md + + 6 18 30 42 53 + | | | | | + 3 | 00 00 00 00 00 00 87 87 87 69 00 00 00 00 00 00 | .........i...... +""" + try: + lprint() + lprint("===========") + lprint(" Bambu Tag") + lprint("===========") + lprint() + lprint("Decompose as Bambu tag .. ", end='') + + MaterialVariantIdentifier_s = bytes.fromhex(data[1][6:29]).decode('ascii').rstrip('\x00') + UniqueMaterialIdentifier_s = bytes.fromhex(data[1][30:53]).decode('ascii').rstrip('\x00') # [**] 8not16 + + FilamentType_s = bytes.fromhex(data[2][6:53]).decode('ascii').rstrip('\x00') + + DetailedFilamentType_s = bytes.fromhex(data[4][6:53]).decode('ascii').rstrip('\x00') + + Colour_rgba = int(data[5][6:17].replace(' ', ''), 16) + SpoolWeight_g = int(data[5][21:23] + data[5][18:20], 16) + Block5_7to8 = data[5][24:29] + FilamentDiameter_mm = struct.unpack('f', bytes.fromhex(data[5][30:41].replace(' ', '')))[0] + Block5_12to15 = data[5][42:50] + + DryingTemperature_c = int(data[6][9:11] + data[6][6:8], 16) + DryingTime_h = int(data[6][15:17] + data[6][12:14], 16) + BedTemperatureType_q = int(data[6][21:23] + data[6][18:20], 16) + BedTemperature_c = int(data[6][27:29] + data[6][24:26], 16) + MaxTemperatureForHotend_c = int(data[6][33:35] + data[6][30:32], 16) + MinTemperatureForHotend_c = int(data[6][39:41] + data[6][36:38], 16) + Block6_12to15 = data[6][42:50] + + # XCamInfo_x = bytes.fromhex(data[8][6:41].replace(' ', '')) + XCamInfo_x = data[8][6:41] + NozzleDiameter_q = struct.unpack('f', bytes.fromhex(data[8][42:53].replace(' ', '')))[0] + + # TrayUID_s = bytes.fromhex(data[9][6:53]).decode('ascii').rstrip('\x00') #[**] !ascii + TrayUID_s = data[9][6:53] + + Block10_0to3 = data[10][6:17] + SpoolWidth_um = int(data[10][21:23] + data[14][18:20], 16) + Block10_6to15 = data[10][24:50] + + ProductionDateTime_s = bytes.fromhex(data[12][6:53]).decode('ascii').rstrip('\x00') + + ShortProductionDateTime_s = bytes.fromhex(data[13][6:53]).decode('ascii').rstrip('\x00') + + # Block14_0to3 = data[14][6:17] + FilamentLength_m = int(data[14][21:23] + data[14][18:20], 16) + # Block14_6to15 = data[14][24:51] + + # (16blocks * 16bytes = 256) * 8bits = 2048 bits + hblk = [42, + 44, 45, 46, + 48, 49, 50, + 52, 53, 54, + 56, 57, 58, + 60, 61, 62] + Hash = [] + for b in hblk: + Hash.append(data[b][6:53]) + + lprint("[offset:length]", prompt='') + lprint(" Block 1:") + lprint(f" [ 0: 8] MaterialVariantIdentifier_s = \"{MaterialVariantIdentifier_s}\"") + lprint(f" [ 8: 8] UniqueMaterialIdentifier_s = \"{UniqueMaterialIdentifier_s}\"") + lprint(" Block 2:") + lprint(f" [ 0:16] FilamentType_s = \"{FilamentType_s}\"") + lprint(" Block 4:") + lprint(f" [ 0:16] DetailedFilamentType_s = \"{DetailedFilamentType_s}\"") + lprint(" Block 5:") + lprint(f" [ 0: 4] Colour_rgba = 0x{Colour_rgba:08X}") + lprint(f" [ 4: 2] SpoolWeight_g = {SpoolWeight_g}g") + lprint(f" [ 6: 2] Block5_7to8 = {{{Block5_7to8}}}") + lprint(f" [ 8: 4] FilamentDiameter_mm = {FilamentDiameter_mm}mm") + lprint(f" [12: 4] Block5_12to15 = {{{Block5_12to15}}}") + lprint(" Block 6:") + lprint(f" [ 0: 2] DryingTemperature_c = {DryingTemperature_c}^C") + lprint(f" [ 2: 2] DryingTime_h = {DryingTime_h}hrs") + lprint(f" [ 4: 4] BedTemperatureType_q = {BedTemperatureType_q}") + lprint(f" [ 6: 2] BedTemperature_c = {BedTemperature_c}^C") + lprint(f" [ 8: 2] MaxTemperatureForHotend_c = {MaxTemperatureForHotend_c}^C") + lprint(f" [10: 2] MinTemperatureForHotend_c = {MinTemperatureForHotend_c}^C") + lprint(f" [12: 4] Block6_12to15 = {{{Block6_12to15}}}") + lprint(" Block 8:") + lprint(f" [ 0:12] XCamInfo_x = {{{XCamInfo_x}}}") + lprint(f" [12: 4] NozzleDiameter_q = {NozzleDiameter_q:.6f}__") + lprint(" Block 9:") + # lprint(f" [ 0:16] TrayUID_s = \"{TrayUID_s}\"") + lprint(f" [ 0:16] TrayUID_s = {{{TrayUID_s}}} ; not ASCII") + lprint(" Block 10:") + lprint(f" [ 0: 4] Block10_0to3 = {{{Block10_0to3}}}") + lprint(f" [ 4: 2] SpoolWidth_um = {SpoolWidth_um}um") + lprint(f" [ 6:10] Block10_6to15 = {{{Block10_6to15}}}") + lprint(" Block 12:") + lprint(f" [ 0:16] ProductionDateTime_s = \"{ProductionDateTime_s}\"") + lprint(" Block 13:") + lprint(f" [ 0:16] ShortProductionDateTime_s = \"{ShortProductionDateTime_s}\"") + lprint(" Block 14:") + lprint(f" [ 0: 4] Block10_0to3 = {{{Block10_0to3}}}") + lprint(f" [ 4: 2] FilamentLength_m = {FilamentLength_m}m") + lprint(f" [ 6:10] Block10_6to15 = {{{Block10_6to15}}}") + lprint(f"\n Blocks {hblk}:") + for i in range(0, len(hblk)): + lprint(f" [ 0:16] HashBlock[{i:2d}] = {{{Hash[i]}}} // #{hblk[i]:2d}") + + except Exception as e: + lprint(prompt='') + lprint(f"Failed: {e}") + + +# +============================================================================= +# Dump ACL +# +# ,-------------------. +# ( 2.2 : ACCESS BITS ) +# `-------------------' + +# The Access bits on both (used) Sectors is the same: 78 77 88 + +# Let's reorganise that according to the official spec Fig 9. +# Access C1 C2 C3 +# ========== =========== +# 78 77 88 --> 78 87 87 +# ab cd ef --> cb fa ed + +# The second nybble of each byte is the inverse of the first nybble. +# It is there to trap tranmission errors, so we can just ignore it/them. + +# So our Access Control value is : {c, f, e} == {7, 8, 8} + +# Let's convert those nybbles to binary +# (c) 7 --> 0111 +# (f) 8 --> 1000 +# (e) 8 --> 1000 +# |||| ...and transpose them: +# |||| +# |||`--- 100 - Block 0 Access bits +# ||`---- 100 - Block 1 Access bits +# |`----- 100 - Block 2 Access bits +# `------ 011 - Block 3 Access bits [Sector Trailer] + +# Now we can use the lookup table [Table 3] to work out what we can do +# with the Sector Trailer (Block(S,3)): + +# | Key A | | Access Bits | | Key B | +# | read ¦ write | | read ¦ write | | read ¦ write | +# +------¦-------+ +------¦-------+ +------¦-------+ +# 000 : | -- ¦ KeyA | | KeyA ¦ -- | | KeyA ¦ KeyA | +# 001 : | -- ¦ KeyA | | KeyA ¦ KeyA | | KeyA ¦ KeyA | Transport Mode +# 010 : | -- ¦ -- | | KeyA ¦ -- | | KeyA ¦ -- | + +# 011 : | -- ¦ KeyB | | A+B ¦ KeyB | | -- ¦ KeyB | <-- Our Card! + +# 100 : | -- ¦ KeyB | | A+B ¦ -- | | -- ¦ KeyB | +# 101 : | -- ¦ -- | | A+B ¦ KeyB | | -- ¦ -- | +# 110 : | -- ¦ -- | | A+B ¦ -- | | -- ¦ -- | }__ +# 111 : | -- ¦ -- | | A+B ¦ -- | | -- ¦ -- | } The Same!? + +# Our card uses 011, for (both of) the (used) Sector Trailer(s). So: +# Both Key A and Key B can READ the Access Bits +# Key B can (additionally) WRITE to Key A, Key B (itself), and the Access Bits + +# Then we can do a similar lookup for the 3 data Blocks (in this Sector) +# This time using [Table 4] + +# | Data | Counter | +# | read ¦ write | Inc ¦ Dec | +# +------¦-------+------¦------+ +# 000 : | A+B ¦ A+B | A+B ¦ A+B | Transport Mode +# 001 : | A+B ¦ -- | -- ¦ A+B | +# 010 : | A+B ¦ -- | -- ¦ -- | +# 011 : | KeyB ¦ KeyB | -- ¦ -- | + +# 100 : | A+B ¦ KeyB | -- ¦ -- | <-- Our Card! + +# 101 : | KeyB ¦ -- | -- ¦ -- | +# 110 : | A+B ¦ KeyB | KeyB ¦ A+B | +# 111 : | -- ¦ -- | -- ¦ -- | + +# Our card uses 100, for all of the (used) Sectors. So: +# Both Key A and Key B can READ the Block +# Only Key B can WRITE to the Block +# The block cannot be used as a "counter" because: +# Neither key can perform increment nor decrement commands + +# WARNING: +# IF YOU PLAN TO CHANGE ACCESS BITS, RTFM, THERE IS MUCH TO CONSIDER ! +# ============================================================================== +def dumpAcl(data): + """Dump ACL + + 6 18 24 27 30 33 42 53 + | | | | | | | | + 3 | 00 00 00 00 00 00 87 87 87 69 00 00 00 00 00 00 | .........i...... + ab cd ef +""" + aclkh = [] # key header + aclk = [""] * 8 # key lookup + aclkx = [] # key output + + lprint() + lprint("=====================") + lprint(" Access Control List") + lprint("=====================") + lprint() + + aclkh.append(" _______________________________________________________ ") + aclkh.append("| | Sector Trailers |") + aclkh.append("| |----------------------------------------------|") + aclkh.append("| Sector |____Key_A_____||_Access_Bits__||____Key_B_____|") + aclkh.append("| | read ¦ write || read ¦ write || read ¦ write |") + aclkh.append("|--------+------¦-------++------¦-------++------¦-------|") + # "| xx | -- ¦ KeyA || KeyA ¦ -- || KeyA ¦ KeyA |" + aclk[0] = "| -- ¦ KeyA || KeyA ¦ -- || KeyA ¦ KeyA | [000]" # noqa: E222 + aclk[1] = "| -- ¦ KeyA || KeyA ¦ KeyA || KeyA ¦ KeyA | [001]" # noqa: E222 + aclk[2] = "| -- ¦ -- || KeyA ¦ -- || KeyA ¦ -- | [010]" # noqa: E222 + aclk[3] = "| -- ¦ KeyB || A+B ¦ KeyB || -- ¦ KeyB | [011]" # noqa: E222 + aclk[4] = "| -- ¦ KeyB || A+B ¦ -- || -- ¦ KeyB | [100]" # noqa: E222 + aclk[5] = "| -- ¦ -- || A+B ¦ KeyB || -- ¦ -- | [101]" # noqa: E222 + aclk[6] = "| -- ¦ -- || A+B ¦ -- || -- ¦ -- | [110]" # noqa: E222 # yes, the same!? + aclk[7] = "| -- ¦ -- || A+B ¦ -- || -- ¦ -- | [111]" # noqa: E222 # ... + + acldh = [] # data header + acld = [""] * 8 # data lookup + acldx = [] # data output + + acldh.append(" _____________________________________ ") + acldh.append("| | Data Blocks |") + acldh.append("| |-----------------------------|") + acldh.append("| Block | Data || Counter |") + acldh.append("| | read ¦ write || Inc ¦ Dec |") + acldh.append("|-------+------¦-------++------¦------+") + # "| xxx | A+B ¦ A+B || A+B ¦ A+B | " + acld[0] = "| A+B ¦ A+B || A+B ¦ A+B | [000]" # noqa: E222 + acld[1] = "| A+B ¦ -- || -- ¦ A+B | [001]" # noqa: E222 + acld[2] = "| A+B ¦ -- || -- ¦ -- | [010]" # noqa: E222 + acld[3] = "| KeyB ¦ KeyB || -- ¦ -- | [011]" # noqa: E222 + acld[4] = "| A+B ¦ KeyB || -- ¦ -- | [100]" # noqa: E222 + acld[5] = "| KeyB ¦ -- || -- ¦ -- | [101]" # noqa: E222 + acld[6] = "| A+B ¦ KeyB || KeyB ¦ A+B | [110]" # noqa: E222 + acld[7] = "| -- ¦ -- || -- ¦ -- | [111]" # noqa: E222 + + idx = [[]] * (16+2) + + # --- calculate the ACL indices for each sector:block --- + for d in data: + bn = int(d[0:3], 10) + + if ((bn % 4) == 3): + sn = (bn // 4) + sec = sn if sn < 16 else sn - 16 + + c = int(d[27], 16) + f = int(d[31], 16) + e = int(d[30], 16) + r0 = ((c & (2**0)) << 2) | ((f & (2**0)) << 1) | ((e & (2**0)) ) # noqa: E202 + r1 = ((c & (2**1)) << 1) | ((f & (2**1)) ) | ((e & (2**1)) >> 1) # noqa: E202 + r2 = ((c & (2**2)) ) | ((f & (2**2)) >> 1) | ((e & (2**2)) >> 2) # noqa: E202 + r3 = ((c & (2**3)) >> 1) | ((f & (2**3)) >> 2) | ((e & (2**3)) >> 3) # noqa: E221 + idx[sec] = [r0, r1, r2, r3] + + # --- build the ACL conversion table --- + for d in data: + bn = int(d[0:3], 10) + sn = (bn // 4) + sec = sn if sn < 16 else sn - 16 + + if ((bn % 4) == 3): + aclkx.append(f"| {sn:2d} " + aclk[idx[sec][bn % 4]] + + f" {{{d[24:32]}}} -> {{{d[27]}{d[31]}{d[30]}}}") + else: + acldx.append(f"| {bn:3d} " + acld[idx[sec][bn % 4]]) + + # --- print it all out --- + for line in aclkh: + lprint(f" {line}") + i = 0 + for line in aclkx: + lprint(f" {line}") + if (i % 4) == 3: + lprint(" | | ¦ || ¦ || ¦ |") + i += 1 + + lprint() + + for line in acldh: + lprint(f" {line}") + i = 0 + for line in acldx: + lprint(f" {line}") + if (i % 3) == 2: + lprint(" | | ¦ || ¦ |") + i += 1 + + +def diskDump(data, uid, dpath): + """Full Dump""" + dump18 = f'{dpath}hf-mf-{uid.hex().upper()}-dump18.bin' + + lprint(f'\nDump card data to file... ' + color(dump18, fg='yellow')) + + bad = False + try: + with open(dump18, 'wb') as f: + for d in data: + if '--' in d[6:53]: + bad = True + b = bytes.fromhex(d[6:53].replace(' ', '').replace('--', 'FF')) + f.write(b) + if bad: + lprint('Bad data exists, and has been saved as 0xFF') + + s = color('ok', fg='green') + lprint(f' Save file operations ( {s} )', prompt='[+]') + + except Exception as e: + s = color('fail', fg='red') + lprint(f' Save file operations: {e} ( {s} )', prompt='[!]') + + return dump18 + + +def dumpMad(dump18): + """Dump MAD + +globals: +- p (R) +""" + + lprint() + lprint("====================================") + lprint(" MiFare Application Directory (MAD)") + lprint("====================================") + lprint() + + cmd = f"hf mf mad --force --verbose --file {dump18}" + lprint(f"`{cmd}`", log=False) + + lprint('\n`-._,-\'"`-._,-"`-._,-\'"`-._,-\'"`-._,-\'"`-._,-\'"`-._,-\'"`-._,-\'"`-._,\n') + + p.console(cmd) + + for line in p.grabbed_output.split('\n'): + lprint(line, prompt='') + + lprint('`-._,-\'"`-._,-"`-._,-\'"`-._,-\'"`-._,-\'"`-._,-\'"`-._,-\'"`-._,-\'"`-._,') + + +if __name__ == "__main__": + main() diff --git a/client/pyscripts/fm11rf08s_recovery.py b/client/pyscripts/fm11rf08s_recovery.py index 3368749a1..9f7270340 100755 --- a/client/pyscripts/fm11rf08s_recovery.py +++ b/client/pyscripts/fm11rf08s_recovery.py @@ -44,17 +44,22 @@ NUM_SECTORS = 16 NUM_EXTRA_SECTORS = 1 DICT_DEF = "mfc_default_keys.dic" DEFAULT_KEYS = set() -if os.path.basename(os.path.dirname(os.path.dirname(sys.argv[0]))) == 'client': +if __name__ == '__main__': + DIR_PATH = os.path.dirname(os.path.abspath(sys.argv[0])) +else: + DIR_PATH = os.path.dirname(os.path.abspath(__file__)) + +if os.path.basename(os.path.dirname(DIR_PATH)) == 'client': # dev setup - TOOLS_PATH = os.path.normpath(os.path.join(f"{os.path.dirname(sys.argv[0])}", + TOOLS_PATH = os.path.normpath(os.path.join(DIR_PATH, "..", "..", "tools", "mfc", "card_only")) - DICT_DEF_PATH = os.path.normpath(os.path.join(f"{os.path.dirname(sys.argv[0])}", + DICT_DEF_PATH = os.path.normpath(os.path.join(DIR_PATH, "..", "dictionaries", DICT_DEF)) else: # assuming installed - TOOLS_PATH = os.path.normpath(os.path.join(f"{os.path.dirname(sys.argv[0])}", + TOOLS_PATH = os.path.normpath(os.path.join(DIR_PATH, "..", "tools")) - DICT_DEF_PATH = os.path.normpath(os.path.join(f"{os.path.dirname(sys.argv[0])}", + DICT_DEF_PATH = os.path.normpath(os.path.join(DIR_PATH, "dictionaries", DICT_DEF)) tools = { @@ -70,469 +75,94 @@ for tool, bin in tools.items(): print(f"Cannot find {bin}, abort!") exit() -parser = argparse.ArgumentParser(description='A script combining staticnested* tools ' - 'to recover all keys from a FM11RF08S card.') -parser.add_argument('-x', '--init-check', action='store_true', help='Run an initial fchk for default keys') -parser.add_argument('-y', '--final-check', action='store_true', help='Run a final fchk with the found keys') -parser.add_argument('-k', '--keep', action='store_true', help='Keep generated dictionaries after processing') -parser.add_argument('-d', '--debug', action='store_true', help='Enable debug mode') -args = parser.parse_args() -start_time = time.time() -p = pm3.pm3() +def recovery(init_check=False, final_check=False, keep=False, debug=False, supply_chain=False, quiet=True): + def show(s='', prompt="[" + color("=", fg="yellow") + "] ", **kwargs): + if not quiet: + s = f"{prompt}" + f"\n{prompt}".join(s.split('\n')) + print(s, **kwargs) -p.console("hf 14a read") -uid = None + start_time = time.time() + p = pm3.pm3() -for line in p.grabbed_output.split('\n'): - if "UID:" in line: - uid = int(line[10:].replace(' ', '')[-8:], 16) + p.console("hf 14a read") + uid = None -if uid is None: - print("Card not found") - exit() -print("UID: " + color(f"{uid:08X}", fg="green")) - - -def print_key(sec, key_type, key): - kt = ['A', 'B'][key_type] - print(f"Sector {sec:2} key{kt} = " + color(key, fg="green")) - -p.console("prefs show --json") -prefs = json.loads(p.grabbed_output) -save_path = prefs['file.default.dumppath'] + os.path.sep - -found_keys = [["", ""] for _ in range(NUM_SECTORS + NUM_EXTRA_SECTORS)] -if args.init_check: - print("Checking default keys...") - p.console("hf mf fchk") for line in p.grabbed_output.split('\n'): - if "[+] 0" in line: - res = [x.strip() for x in line.split('|')] - sec = int(res[0][4:]) - if res[3] == '1': - found_keys[sec][0] = res[2] - print_key(sec, 0, found_keys[sec][0]) - if res[5] == '1': - found_keys[sec][1] = res[4] - print_key(sec, 1, found_keys[sec][1]) + if "UID:" in line: + uid = int(line[10:].replace(' ', '')[-8:], 16) -print("Getting nonces...") -nonces_with_data = "" -for key in BACKDOOR_KEYS: - cmd = f"hf mf isen --collect_fm11rf08s_with_data --key {key}" - p.console(cmd) - for line in p.grabbed_output.split('\n'): - if "Wrong" in line or "error" in line: - break - if "Saved" in line: - nonces_with_data = line[line.index("`"):].strip("`") - if nonces_with_data != "": - break + if uid is None: + show("Card not found") + return + show("UID: " + color(f"{uid:08X}", fg="green")) -if (nonces_with_data == ""): - print("Error getting nonces, abort.") - exit() + def show_key(sec, key_type, key): + kt = ['A', 'B'][key_type] + show(f"Sector {sec:2} key{kt} = " + color(key, fg="green")) -try: - with open(nonces_with_data, 'r') as file: - # Load and parse the JSON data - dict_nwd = json.load(file) -except json.decoder.JSONDecodeError: - print(f"Error parsing {nonces_with_data}, abort.") - exit() + p.console("prefs show --json") + prefs = json.loads(p.grabbed_output) + save_path = prefs['file.default.dumppath'] + os.path.sep -nt = [["", ""] for _ in range(NUM_SECTORS + NUM_EXTRA_SECTORS)] -nt_enc = [["", ""] for _ in range(NUM_SECTORS + NUM_EXTRA_SECTORS)] -par_err = [["", ""] for _ in range(NUM_SECTORS + NUM_EXTRA_SECTORS)] -data = ["" for _ in range(NUM_SECTORS * 4)] -for sec in range(NUM_SECTORS + NUM_EXTRA_SECTORS): - real_sec = sec - if sec >= NUM_SECTORS: - real_sec += 16 - nt[sec][0] = dict_nwd["nt"][f"{real_sec}"]["a"].lower() - nt[sec][1] = dict_nwd["nt"][f"{real_sec}"]["b"].lower() - nt_enc[sec][0] = dict_nwd["nt_enc"][f"{real_sec}"]["a"].lower() - nt_enc[sec][1] = dict_nwd["nt_enc"][f"{real_sec}"]["b"].lower() - par_err[sec][0] = dict_nwd["par_err"][f"{real_sec}"]["a"] - par_err[sec][1] = dict_nwd["par_err"][f"{real_sec}"]["b"] -for blk in range(NUM_SECTORS * 4): - data[blk] = dict_nwd["blocks"][f"{blk}"] + found_keys = [["", ""] for _ in range(NUM_SECTORS + NUM_EXTRA_SECTORS)] + if init_check: + show("Checking default keys...") + p.console("hf mf fchk") + for line in p.grabbed_output.split('\n'): + if "[+] 0" in line: + res = [x.strip() for x in line.split('|')] + sec = int(res[0][4:]) + if res[3] == '1': + found_keys[sec][0] = res[2] + show_key(sec, 0, found_keys[sec][0]) + if res[5] == '1': + found_keys[sec][1] = res[4] + show_key(sec, 1, found_keys[sec][1]) -print("Generating first dump file") -dumpfile = f"{save_path}hf-mf-{uid:08X}-dump.bin" -with (open(dumpfile, "wb")) as f: - for sec in range(NUM_SECTORS): - for b in range(4): - d = data[(sec * 4) + b] - if b == 3: - ka = found_keys[sec][0] - kb = found_keys[sec][1] - if ka == "": - ka = "FFFFFFFFFFFF" - if kb == "": - kb = "FFFFFFFFFFFF" - d = ka + d[12:20] + kb - f.write(bytes.fromhex(d)) -print(f"Data has been dumped to `{dumpfile}`") - -elapsed_time1 = time.time() - start_time -minutes = int(elapsed_time1 // 60) -seconds = int(elapsed_time1 % 60) -print("----Step 1: " + color(f"{minutes:2}", fg="yellow") + " minutes " + - color(f"{seconds:2}", fg="yellow") + " seconds -----------") - -if os.path.isfile(DICT_DEF_PATH): - print(f"Loading {DICT_DEF}") - with open(DICT_DEF_PATH, 'r', encoding='utf-8') as file: - for line in file: - if line[0] != '#' and len(line) >= 12: - DEFAULT_KEYS.add(line[:12]) -else: - print(f"Warning, {DICT_DEF} not found.") - -print("Running staticnested_1nt & 2x1nt when doable...") -keys = [[set(), set()] for _ in range(NUM_SECTORS + NUM_EXTRA_SECTORS)] -all_keys = set() -duplicates = set() -# Availability of filtered dicts -filtered_dicts = [[False, False] for _ in range(NUM_SECTORS + NUM_EXTRA_SECTORS)] -found_default = [[False, False] for _ in range(NUM_SECTORS + NUM_EXTRA_SECTORS)] -for sec in range(NUM_SECTORS + NUM_EXTRA_SECTORS): - real_sec = sec - if sec >= NUM_SECTORS: - real_sec += 16 - if found_keys[sec][0] != "" and found_keys[sec][1] != "": - continue - if found_keys[sec][0] == "" and found_keys[sec][1] == "" and nt[sec][0] != nt[sec][1]: - for key_type in [0, 1]: - cmd = [tools["staticnested_1nt"], f"{uid:08X}", f"{real_sec}", - nt[sec][key_type], nt_enc[sec][key_type], par_err[sec][key_type]] - if args.debug: - print(' '.join(cmd)) - subprocess.run(cmd, capture_output=True) - cmd = [tools["staticnested_2x1nt"], - f"keys_{uid:08x}_{real_sec:02}_{nt[sec][0]}.dic", f"keys_{uid:08x}_{real_sec:02}_{nt[sec][1]}.dic"] - if args.debug: - print(' '.join(cmd)) - subprocess.run(cmd, capture_output=True) - filtered_dicts[sec][key_type] = True - for key_type in [0, 1]: - keys_set = set() - with (open(f"keys_{uid:08x}_{real_sec:02}_{nt[sec][key_type]}_filtered.dic")) as f: - while line := f.readline().rstrip(): - keys_set.add(line) - keys[sec][key_type] = keys_set.copy() - duplicates.update(all_keys.intersection(keys_set)) - all_keys.update(keys_set) - # Prioritize default keys - keys_def_set = DEFAULT_KEYS.intersection(keys_set) - keys_set.difference_update(DEFAULT_KEYS) - # Prioritize sector 32 keyB starting with 0000 - if real_sec == 32: - keyb32cands = set(x for x in keys_set if x.startswith("0000")) - keys_def_set.update(keyb32cands) - keys_set.difference_update(keyb32cands) - if len(keys_def_set) > 0: - found_default[sec][key_type] = True - with (open(f"keys_{uid:08x}_{real_sec:02}_{nt[sec][key_type]}_filtered.dic", "w")) as f: - for k in keys_def_set: - f.write(f"{k}\n") - for k in keys_set: - f.write(f"{k}\n") - else: # one key not found or both identical - if found_keys[sec][0] == "": - key_type = 0 - else: - key_type = 1 - cmd = [tools["staticnested_1nt"], f"{uid:08X}", f"{real_sec}", - nt[sec][key_type], nt_enc[sec][key_type], par_err[sec][key_type]] - if args.debug: - print(' '.join(cmd)) - subprocess.run(cmd, capture_output=True) - keys_set = set() - with (open(f"keys_{uid:08x}_{real_sec:02}_{nt[sec][key_type]}.dic")) as f: - while line := f.readline().rstrip(): - keys_set.add(line) - keys[sec][key_type] = keys_set.copy() - duplicates.update(all_keys.intersection(keys_set)) - all_keys.update(keys_set) - # Prioritize default keys - keys_def_set = DEFAULT_KEYS.intersection(keys_set) - keys_set.difference_update(DEFAULT_KEYS) - if len(keys_def_set) > 0: - found_default[sec][key_type] = True - with (open(f"keys_{uid:08x}_{real_sec:02}_{nt[sec][key_type]}.dic", "w")) as f: - for k in keys_def_set: - f.write(f"{k}\n") - for k in keys_set: - f.write(f"{k}\n") - -print("Looking for common keys across sectors...") -keys_filtered = [[set(), set()] for _ in range(NUM_SECTORS + NUM_EXTRA_SECTORS)] -for dup in duplicates: - for sec in range(NUM_SECTORS + NUM_EXTRA_SECTORS): - for key_type in [0, 1]: - if dup in keys[sec][key_type]: - keys_filtered[sec][key_type].add(dup) - -# Availability of duplicates dicts -duplicates_dicts = [[False, False] for _ in range(NUM_SECTORS + NUM_EXTRA_SECTORS)] -first = True -for sec in range(NUM_SECTORS + NUM_EXTRA_SECTORS): - real_sec = sec - if sec >= NUM_SECTORS: - real_sec += 16 - for key_type in [0, 1]: - if len(keys_filtered[sec][key_type]) > 0: - if first: - print("Saving duplicates dicts...") - first = False - with (open(f"keys_{uid:08x}_{real_sec:02}_{nt[sec][key_type]}_duplicates.dic", "w")) as f: - keys_set = keys_filtered[sec][key_type].copy() - keys_def_set = DEFAULT_KEYS.intersection(keys_set) - keys_set.difference_update(DEFAULT_KEYS) - for k in keys_def_set: - f.write(f"{k}\n") - for k in keys_set: - f.write(f"{k}\n") - duplicates_dicts[sec][key_type] = True - -print("Computing needed time for attack...") -candidates = [[0, 0] for _ in range(NUM_SECTORS + NUM_EXTRA_SECTORS)] -for sec in range(NUM_SECTORS + NUM_EXTRA_SECTORS): - real_sec = sec - if sec >= NUM_SECTORS: - real_sec += 16 - for key_type in [0, 1]: - if found_keys[sec][0] == "" and found_keys[sec][1] == "" and duplicates_dicts[sec][key_type]: - kt = ['a', 'b'][key_type] - dic = f"keys_{uid:08x}_{real_sec:02}_{nt[sec][key_type]}_duplicates.dic" - with open(dic, 'r') as file: - count = sum(1 for _ in file) -# print(f"dic {dic} size {count}") - candidates[sec][key_type] = count - if nt[sec][0] == nt[sec][1]: - candidates[sec][key_type ^ 1] = 1 - for key_type in [0, 1]: - if found_keys[sec][0] == "" and found_keys[sec][1] == "" and filtered_dicts[sec][key_type] and candidates[sec][0] == 0 and candidates[sec][1] == 0: - if found_default[sec][key_type]: - # We assume the default key is correct - candidates[sec][key_type] = 1 - else: - kt = ['a', 'b'][key_type] - dic = f"keys_{uid:08x}_{real_sec:02}_{nt[sec][key_type]}_filtered.dic" - with open(dic, 'r') as file: - count = sum(1 for _ in file) -# print(f"dic {dic} size {count}") - candidates[sec][key_type] = count - if found_keys[sec][0] == "" and found_keys[sec][1] == "" and nt[sec][0] == nt[sec][1] and candidates[sec][0] == 0 and candidates[sec][1] == 0: - if found_default[sec][0]: - # We assume the default key is correct - candidates[sec][0] = 1 - candidates[sec][1] = 1 - else: - key_type = 0 - kt = ['a', 'b'][key_type] - dic = f"keys_{uid:08x}_{real_sec:02}_{nt[sec][key_type]}.dic" - with open(dic, 'r') as file: - count = sum(1 for _ in file) -# print(f"dic {dic} size {count}") - candidates[sec][0] = count - candidates[sec][1] = 1 - -if args.debug: - for sec in range(NUM_SECTORS + NUM_EXTRA_SECTORS): - real_sec = sec - if sec >= NUM_SECTORS: - real_sec += 16 - print(f" {real_sec:03} | {real_sec*4+3:03} | {candidates[sec][0]:6} | {candidates[sec][1]:6} ") -total_candidates = sum(candidates[sec][0] + candidates[sec][1] for sec in range(NUM_SECTORS + NUM_EXTRA_SECTORS)) - -elapsed_time2 = time.time() - start_time - elapsed_time1 -minutes = int(elapsed_time2 // 60) -seconds = int(elapsed_time2 % 60) -print("----Step 2: " + color(f"{minutes:2}", fg="yellow") + " minutes " + - color(f"{seconds:2}", fg="yellow") + " seconds -----------") - -# fchk: 147 keys/s. Correct key found after 50% of candidates on average -FCHK_KEYS_S = 147 -foreseen_time = (total_candidates / 2 / FCHK_KEYS_S) + 5 -minutes = int(foreseen_time // 60) -seconds = int(foreseen_time % 60) -print("Still about " + color(f"{minutes:2}", fg="yellow") + " minutes " + - color(f"{seconds:2}", fg="yellow") + " seconds to run...") - -abort = False -print("Brute-forcing keys... Press any key to interrupt") -for sec in range(NUM_SECTORS + NUM_EXTRA_SECTORS): - real_sec = sec - if sec >= NUM_SECTORS: - real_sec += 16 - for key_type in [0, 1]: - # If we have a duplicates dict - # note: we skip if we already know one key - # as using 2x1nt1key later will be faster - if found_keys[sec][0] == "" and found_keys[sec][1] == "" and duplicates_dicts[sec][key_type]: - kt = ['a', 'b'][key_type] - dic = f"keys_{uid:08x}_{real_sec:02}_{nt[sec][key_type]}_duplicates.dic" - cmd = f"hf mf fchk --blk {real_sec * 4} -{kt} -f {dic} --no-default" - if args.debug: - print(cmd) - p.console(cmd) - for line in p.grabbed_output.split('\n'): - if "aborted via keyboard" in line: - abort = True - if "found:" in line: - found_keys[sec][key_type] = line[30:].strip() - print_key(real_sec, key_type, found_keys[sec][key_type]) - if nt[sec][0] == nt[sec][1] and found_keys[sec][key_type ^ 1] == "": - found_keys[sec][key_type ^ 1] = found_keys[sec][key_type] - print_key(real_sec, key_type ^ 1, found_keys[sec][key_type ^ 1]) - if abort: - break - if abort: - break - - for key_type in [0, 1]: - # If we have a filtered dict - # note: we skip if we already know one key - # as using 2x1nt1key later will be faster - if found_keys[sec][0] == "" and found_keys[sec][1] == "" and filtered_dicts[sec][key_type]: - # Use filtered dict - kt = ['a', 'b'][key_type] - dic = f"keys_{uid:08x}_{real_sec:02}_{nt[sec][key_type]}_filtered.dic" - cmd = f"hf mf fchk --blk {real_sec * 4} -{kt} -f {dic} --no-default" - if args.debug: - print(cmd) - p.console(cmd) - for line in p.grabbed_output.split('\n'): - if "aborted via keyboard" in line: - abort = True - if "found:" in line: - found_keys[sec][key_type] = line[30:].strip() - print_key(real_sec, key_type, found_keys[sec][key_type]) - if abort: - break - if abort: - break - - # If one common key for the sector - if found_keys[sec][0] == "" and found_keys[sec][1] == "" and nt[sec][0] == nt[sec][1]: - key_type = 0 - # Use regular dict - kt = ['a', 'b'][key_type] - dic = f"keys_{uid:08x}_{real_sec:02}_{nt[sec][key_type]}.dic" - cmd = f"hf mf fchk --blk {real_sec * 4} -{kt} -f {dic} --no-default" - if args.debug: - print(cmd) + show("Getting nonces...") + nonces_with_data = "" + for key in BACKDOOR_KEYS: + cmd = f"hf mf isen --collect_fm11rf08s_with_data --key {key}" p.console(cmd) for line in p.grabbed_output.split('\n'): - if "aborted via keyboard" in line: - abort = True - if "found:" in line: - found_keys[sec][0] = line[30:].strip() - found_keys[sec][1] = line[30:].strip() - print_key(real_sec, 0, found_keys[sec][key_type]) - print_key(real_sec, 1, found_keys[sec][key_type]) - if abort: - break + if "Wrong" in line or "error" in line: + break + if "Saved" in line: + nonces_with_data = line[line.index("`"):].strip("`") + if nonces_with_data != "": + break - # If one key is missing, use the other one with 2x1nt1key - if ((found_keys[sec][0] == "") ^ (found_keys[sec][1] == "")) and nt[sec][0] != nt[sec][1]: - if (found_keys[sec][0] == ""): - key_type_source = 1 - key_type_target = 0 - else: - key_type_source = 0 - key_type_target = 1 - if duplicates_dicts[sec][key_type_target]: - dic = f"keys_{uid:08x}_{real_sec:02}_{nt[sec][key_type_target]}_duplicates.dic" - elif filtered_dicts[sec][key_type_target]: - dic = f"keys_{uid:08x}_{real_sec:02}_{nt[sec][key_type_target]}_filtered.dic" - else: - dic = f"keys_{uid:08x}_{real_sec:02}_{nt[sec][key_type_target]}.dic" - cmd = [tools["staticnested_2x1nt1key"], nt[sec][key_type_source], found_keys[sec][key_type_source], dic] - if args.debug: - print(' '.join(cmd)) - result = subprocess.run(cmd, capture_output=True, text=True).stdout - keys = set() - for line in result.split('\n'): - if "MATCH:" in line: - keys.add(line[12:]) - if len(keys) > 1: - kt = ['a', 'b'][key_type_target] - cmd = f"hf mf fchk --blk {real_sec * 4} -{kt} --no-default" - for k in keys: - cmd += f" -k {k}" - if args.debug: - print(cmd) - p.console(cmd) - for line in p.grabbed_output.split('\n'): - if "aborted via keyboard" in line: - abort = True - if "found:" in line: - found_keys[sec][key_type_target] = line[30:].strip() - elif len(keys) == 1: - found_keys[sec][key_type_target] = keys.pop() - if found_keys[sec][key_type_target] != "": - print_key(real_sec, key_type_target, found_keys[sec][key_type_target]) - if abort: - break + if (nonces_with_data == ""): + show("Error getting nonces, abort.") + return -if abort: - print("Brute-forcing phase aborted via keyboard!") - args.final_check = False + try: + with open(nonces_with_data, 'r') as file: + # Load and parse the JSON data + dict_nwd = json.load(file) + except json.decoder.JSONDecodeError: + show(f"Error parsing {nonces_with_data}, abort.") + return -if args.final_check: - print("Letting fchk do a final dump, just for confirmation and display...") - keys_set = set([i for sl in found_keys for i in sl if i != ""]) - with (open(f"keys_{uid:08x}.dic", "w")) as f: - for k in keys_set: - f.write(f"{k}\n") - cmd = f"hf mf fchk -f keys_{uid:08x}.dic --no-default --dump" - if args.debug: - print(cmd) - p.console(cmd, passthru=True) -else: - plus = "[" + color("+", fg="green") + "] " - print() - print(plus + color("found keys:", fg="green")) - print() - print(plus + "-----+-----+--------------+---+--------------+----") - print(plus + " Sec | Blk | key A |res| key B |res") - print(plus + "-----+-----+--------------+---+--------------+----") + nt = [["", ""] for _ in range(NUM_SECTORS + NUM_EXTRA_SECTORS)] + nt_enc = [["", ""] for _ in range(NUM_SECTORS + NUM_EXTRA_SECTORS)] + par_err = [["", ""] for _ in range(NUM_SECTORS + NUM_EXTRA_SECTORS)] + data = ["" for _ in range(NUM_SECTORS * 4)] for sec in range(NUM_SECTORS + NUM_EXTRA_SECTORS): real_sec = sec if sec >= NUM_SECTORS: real_sec += 16 - keys = [["", 0], ["", 0]] - for key_type in [0, 1]: - if found_keys[sec][key_type] == "": - keys[key_type] = [color("------------", fg="red"), color("0", fg="red")] - else: - keys[key_type] = [color(found_keys[sec][key_type], fg="green"), color("1", fg="green")] - print(plus + f" {real_sec:03} | {real_sec*4+3:03} | {keys[0][0]} | {keys[0][1]} | {keys[1][0]} | {keys[1][1]} ") - print(plus + "-----+-----+--------------+---+--------------+----") - print(plus + "( " + color("0", fg="red") + ":Failed / " + - color("1", fg="green") + ":Success )") - print() - print(plus + "Generating binary key file") - keyfile = f"{save_path}hf-mf-{uid:08X}-key.bin" - unknown = False - with (open(keyfile, "wb")) as f: - for key_type in [0, 1]: - for sec in range(NUM_SECTORS + NUM_EXTRA_SECTORS): - k = found_keys[sec][key_type] - if k == "": - k = "FFFFFFFFFFFF" - unknown = True - f.write(bytes.fromhex(k)) - print(plus + "Found keys have been dumped to `" + color(keyfile, fg="yellow")+"`") - if unknown: - print("[" + color("=", fg="yellow") + "] --[ " + color("FFFFFFFFFFFF", fg="yellow") + - " ]-- has been inserted for unknown keys") - print(plus + "Generating final dump file") + nt[sec][0] = dict_nwd["nt"][f"{real_sec}"]["a"].lower() + nt[sec][1] = dict_nwd["nt"][f"{real_sec}"]["b"].lower() + nt_enc[sec][0] = dict_nwd["nt_enc"][f"{real_sec}"]["a"].lower() + nt_enc[sec][1] = dict_nwd["nt_enc"][f"{real_sec}"]["b"].lower() + par_err[sec][0] = dict_nwd["par_err"][f"{real_sec}"]["a"] + par_err[sec][1] = dict_nwd["par_err"][f"{real_sec}"]["b"] + for blk in range(NUM_SECTORS * 4): + data[blk] = dict_nwd["blocks"][f"{blk}"] + + show("Generating first dump file") dumpfile = f"{save_path}hf-mf-{uid:08X}-dump.bin" with (open(dumpfile, "wb")) as f: for sec in range(NUM_SECTORS): @@ -547,29 +177,479 @@ else: kb = "FFFFFFFFFFFF" d = ka + d[12:20] + kb f.write(bytes.fromhex(d)) - print(plus + "Data has been dumped to `" + color(dumpfile, fg="yellow")+"`") + show(f"Data has been dumped to `{dumpfile}`") -# Remove generated dictionaries after processing -if not args.keep: - print(plus + "Removing generated dictionaries...") + elapsed_time1 = time.time() - start_time + minutes = int(elapsed_time1 // 60) + seconds = int(elapsed_time1 % 60) + show("----Step 1: " + color(f"{minutes:2}", fg="yellow") + " minutes " + + color(f"{seconds:2}", fg="yellow") + " seconds -----------") + + if os.path.isfile(DICT_DEF_PATH): + show(f"Loading {DICT_DEF}") + with open(DICT_DEF_PATH, 'r', encoding='utf-8') as file: + for line in file: + if line[0] != '#' and len(line) >= 12: + DEFAULT_KEYS.add(line[:12]) + else: + show(f"Warning, {DICT_DEF} not found.") + + dict_dnwd = None + def_nt = ["" for _ in range(NUM_SECTORS)] + if supply_chain: + try: + default_nonces = f'{save_path}hf-mf-{uid:04X}-default_nonces.json' + with open(default_nonces, 'r') as file: + # Load and parse the JSON data + dict_dnwd = json.load(file) + for sec in range(NUM_SECTORS): + def_nt[sec] = dict_dnwd["nt"][f"{sec}"].lower() + show(f"Loaded default nonces from {default_nonces}.") + except FileNotFoundError: + pass + except json.decoder.JSONDecodeError: + show(f"Error parsing {default_nonces}, skipping.") + + show("Running staticnested_1nt & 2x1nt when doable...") + keys = [[set(), set()] for _ in range(NUM_SECTORS + NUM_EXTRA_SECTORS)] + all_keys = set() + duplicates = set() + # Availability of filtered dicts + filtered_dicts = [[False, False] for _ in range(NUM_SECTORS + NUM_EXTRA_SECTORS)] + found_default = [[False, False] for _ in range(NUM_SECTORS + NUM_EXTRA_SECTORS)] + for sec in range(NUM_SECTORS + NUM_EXTRA_SECTORS): + real_sec = sec + if sec >= NUM_SECTORS: + real_sec += 16 + if found_keys[sec][0] != "" and found_keys[sec][1] != "": + continue + if found_keys[sec][0] == "" and found_keys[sec][1] == "" and nt[sec][0] != nt[sec][1]: + for key_type in [0, 1]: + cmd = [tools["staticnested_1nt"], f"{uid:08X}", f"{real_sec}", + nt[sec][key_type], nt_enc[sec][key_type], par_err[sec][key_type]] + if debug: + print(' '.join(cmd)) + subprocess.run(cmd, capture_output=True) + cmd = [tools["staticnested_2x1nt"], + f"keys_{uid:08x}_{real_sec:02}_{nt[sec][0]}.dic", f"keys_{uid:08x}_{real_sec:02}_{nt[sec][1]}.dic"] + if debug: + print(' '.join(cmd)) + subprocess.run(cmd, capture_output=True) + filtered_dicts[sec][key_type] = True + for key_type in [0, 1]: + keys_set = set() + with (open(f"keys_{uid:08x}_{real_sec:02}_{nt[sec][key_type]}_filtered.dic")) as f: + while line := f.readline().rstrip(): + keys_set.add(line) + keys[sec][key_type] = keys_set.copy() + duplicates.update(all_keys.intersection(keys_set)) + all_keys.update(keys_set) + if dict_dnwd is not None and sec < NUM_SECTORS: + # Prioritize keys from supply-chain attack + cmd = [tools["staticnested_2x1nt1key"], def_nt[sec], "FFFFFFFFFFFF", + f"keys_{uid:08x}_{real_sec:02}_{nt[sec][key_type]}_filtered.dic"] + if debug: + print(' '.join(cmd)) + result = subprocess.run(cmd, capture_output=True, text=True).stdout + keys_def_set = set() + for line in result.split('\n'): + if "MATCH:" in line: + keys_def_set.add(line[12:]) + keys_set.difference_update(keys_def_set) + else: + # Prioritize default keys + keys_def_set = DEFAULT_KEYS.intersection(keys_set) + keys_set.difference_update(keys_def_set) + # Prioritize sector 32 keyB starting with 0000 + if real_sec == 32: + keyb32cands = set(x for x in keys_set if x.startswith("0000")) + keys_def_set.update(keyb32cands) + keys_set.difference_update(keyb32cands) + if len(keys_def_set) > 0: + found_default[sec][key_type] = True + with (open(f"keys_{uid:08x}_{real_sec:02}_{nt[sec][key_type]}_filtered.dic", "w")) as f: + for k in keys_def_set: + f.write(f"{k}\n") + for k in keys_set: + f.write(f"{k}\n") + else: # one key not found or both identical + if found_keys[sec][0] == "": + key_type = 0 + else: + key_type = 1 + cmd = [tools["staticnested_1nt"], f"{uid:08X}", f"{real_sec}", + nt[sec][key_type], nt_enc[sec][key_type], par_err[sec][key_type]] + if debug: + print(' '.join(cmd)) + subprocess.run(cmd, capture_output=True) + keys_set = set() + with (open(f"keys_{uid:08x}_{real_sec:02}_{nt[sec][key_type]}.dic")) as f: + while line := f.readline().rstrip(): + keys_set.add(line) + keys[sec][key_type] = keys_set.copy() + duplicates.update(all_keys.intersection(keys_set)) + all_keys.update(keys_set) + if dict_dnwd is not None and sec < NUM_SECTORS: + # Prioritize keys from supply-chain attack + cmd = [tools["staticnested_2x1nt1key"], def_nt[sec], "FFFFFFFFFFFF", + f"keys_{uid:08x}_{real_sec:02}_{nt[sec][key_type]}.dic"] + if debug: + print(' '.join(cmd)) + result = subprocess.run(cmd, capture_output=True, text=True).stdout + keys_def_set = set() + for line in result.split('\n'): + if "MATCH:" in line: + keys_def_set.add(line[12:]) + keys_set.difference_update(keys_def_set) + else: + # Prioritize default keys + keys_def_set = DEFAULT_KEYS.intersection(keys_set) + keys_set.difference_update(keys_def_set) + if len(keys_def_set) > 0: + found_default[sec][key_type] = True + with (open(f"keys_{uid:08x}_{real_sec:02}_{nt[sec][key_type]}.dic", "w")) as f: + for k in keys_def_set: + f.write(f"{k}\n") + for k in keys_set: + f.write(f"{k}\n") + + show("Looking for common keys across sectors...") + keys_filtered = [[set(), set()] for _ in range(NUM_SECTORS + NUM_EXTRA_SECTORS)] + for dup in duplicates: + for sec in range(NUM_SECTORS + NUM_EXTRA_SECTORS): + for key_type in [0, 1]: + if dup in keys[sec][key_type]: + keys_filtered[sec][key_type].add(dup) + + # Availability of duplicates dicts + duplicates_dicts = [[False, False] for _ in range(NUM_SECTORS + NUM_EXTRA_SECTORS)] + first = True for sec in range(NUM_SECTORS + NUM_EXTRA_SECTORS): real_sec = sec if sec >= NUM_SECTORS: real_sec += 16 for key_type in [0, 1]: - for append in ["", "_filtered", "_duplicates"]: - file_name = f"keys_{uid:08x}_{real_sec:02}_{nt[sec][key_type]}{append}.dic" - if os.path.isfile(file_name): - os.remove(file_name) + if len(keys_filtered[sec][key_type]) > 0: + if first: + show("Saving duplicates dicts...") + first = False + with (open(f"keys_{uid:08x}_{real_sec:02}_{nt[sec][key_type]}_duplicates.dic", "w")) as f: + keys_set = keys_filtered[sec][key_type].copy() + keys_def_set = DEFAULT_KEYS.intersection(keys_set) + keys_set.difference_update(DEFAULT_KEYS) + for k in keys_def_set: + f.write(f"{k}\n") + for k in keys_set: + f.write(f"{k}\n") + duplicates_dicts[sec][key_type] = True -elapsed_time3 = time.time() - start_time - elapsed_time1 - elapsed_time2 -minutes = int(elapsed_time3 // 60) -seconds = int(elapsed_time3 % 60) -print("----Step 3: " + color(f"{minutes:2}", fg="yellow") + " minutes " + - color(f"{seconds:2}", fg="yellow") + " seconds -----------") + show("Computing needed time for attack...") + candidates = [[0, 0] for _ in range(NUM_SECTORS + NUM_EXTRA_SECTORS)] + for sec in range(NUM_SECTORS + NUM_EXTRA_SECTORS): + real_sec = sec + if sec >= NUM_SECTORS: + real_sec += 16 + for key_type in [0, 1]: + if found_keys[sec][0] == "" and found_keys[sec][1] == "" and duplicates_dicts[sec][key_type]: + kt = ['a', 'b'][key_type] + dic = f"keys_{uid:08x}_{real_sec:02}_{nt[sec][key_type]}_duplicates.dic" + with open(dic, 'r') as file: + count = sum(1 for _ in file) + # print(f"dic {dic} size {count}") + candidates[sec][key_type] = count + if nt[sec][0] == nt[sec][1]: + candidates[sec][key_type ^ 1] = 1 + for key_type in [0, 1]: + if ((found_keys[sec][0] == "" and found_keys[sec][1] == "" and + filtered_dicts[sec][key_type] and candidates[sec][0] == 0 and + candidates[sec][1] == 0)): + if found_default[sec][key_type]: + # We assume the default key is correct + candidates[sec][key_type] = 1 + else: + kt = ['a', 'b'][key_type] + dic = f"keys_{uid:08x}_{real_sec:02}_{nt[sec][key_type]}_filtered.dic" + with open(dic, 'r') as file: + count = sum(1 for _ in file) + # print(f"dic {dic} size {count}") + candidates[sec][key_type] = count + if ((found_keys[sec][0] == "" and found_keys[sec][1] == "" and + nt[sec][0] == nt[sec][1] and candidates[sec][0] == 0 and + candidates[sec][1] == 0)): + if found_default[sec][0]: + # We assume the default key is correct + candidates[sec][0] = 1 + candidates[sec][1] = 1 + else: + key_type = 0 + kt = ['a', 'b'][key_type] + dic = f"keys_{uid:08x}_{real_sec:02}_{nt[sec][key_type]}.dic" + with open(dic, 'r') as file: + count = sum(1 for _ in file) + # print(f"dic {dic} size {count}") + candidates[sec][0] = count + candidates[sec][1] = 1 -elapsed_time = time.time() - start_time -minutes = int(elapsed_time // 60) -seconds = int(elapsed_time % 60) -print("---- TOTAL: " + color(f"{minutes:2}", fg="yellow") + " minutes " + - color(f"{seconds:2}", fg="yellow") + " seconds -----------") + if debug: + for sec in range(NUM_SECTORS + NUM_EXTRA_SECTORS): + real_sec = sec + if sec >= NUM_SECTORS: + real_sec += 16 + show(f" {real_sec:03} | {real_sec*4+3:03} | {candidates[sec][0]:6} | {candidates[sec][1]:6} ") + total_candidates = sum(candidates[sec][0] + candidates[sec][1] for sec in range(NUM_SECTORS + NUM_EXTRA_SECTORS)) + + elapsed_time2 = time.time() - start_time - elapsed_time1 + minutes = int(elapsed_time2 // 60) + seconds = int(elapsed_time2 % 60) + show("----Step 2: " + color(f"{minutes:2}", fg="yellow") + " minutes " + + color(f"{seconds:2}", fg="yellow") + " seconds -----------") + + # fchk: 147 keys/s. Correct key found after 50% of candidates on average + FCHK_KEYS_S = 147 + foreseen_time = (total_candidates / 2 / FCHK_KEYS_S) + 5 + minutes = int(foreseen_time // 60) + seconds = int(foreseen_time % 60) + show("Still about " + color(f"{minutes:2}", fg="yellow") + " minutes " + + color(f"{seconds:2}", fg="yellow") + " seconds to run...") + + abort = False + show("Brute-forcing keys... Press any key to interrupt") + for sec in range(NUM_SECTORS + NUM_EXTRA_SECTORS): + real_sec = sec + if sec >= NUM_SECTORS: + real_sec += 16 + for key_type in [0, 1]: + # If we have a duplicates dict + # note: we skip if we already know one key + # as using 2x1nt1key later will be faster + if found_keys[sec][0] == "" and found_keys[sec][1] == "" and duplicates_dicts[sec][key_type]: + kt = ['a', 'b'][key_type] + dic = f"keys_{uid:08x}_{real_sec:02}_{nt[sec][key_type]}_duplicates.dic" + cmd = f"hf mf fchk --blk {real_sec * 4} -{kt} -f {dic} --no-default" + if debug: + print(cmd) + p.console(cmd) + for line in p.grabbed_output.split('\n'): + if "aborted via keyboard" in line: + abort = True + if "found:" in line: + found_keys[sec][key_type] = line[30:].strip() + show_key(real_sec, key_type, found_keys[sec][key_type]) + if nt[sec][0] == nt[sec][1] and found_keys[sec][key_type ^ 1] == "": + found_keys[sec][key_type ^ 1] = found_keys[sec][key_type] + show_key(real_sec, key_type ^ 1, found_keys[sec][key_type ^ 1]) + if abort: + break + if abort: + break + + for key_type in [0, 1]: + # If we have a filtered dict + # note: we skip if we already know one key + # as using 2x1nt1key later will be faster + if found_keys[sec][0] == "" and found_keys[sec][1] == "" and filtered_dicts[sec][key_type]: + # Use filtered dict + kt = ['a', 'b'][key_type] + dic = f"keys_{uid:08x}_{real_sec:02}_{nt[sec][key_type]}_filtered.dic" + cmd = f"hf mf fchk --blk {real_sec * 4} -{kt} -f {dic} --no-default" + if debug: + print(cmd) + p.console(cmd) + for line in p.grabbed_output.split('\n'): + if "aborted via keyboard" in line: + abort = True + if "found:" in line: + found_keys[sec][key_type] = line[30:].strip() + show_key(real_sec, key_type, found_keys[sec][key_type]) + if abort: + break + if abort: + break + + # If one common key for the sector + if found_keys[sec][0] == "" and found_keys[sec][1] == "" and nt[sec][0] == nt[sec][1]: + key_type = 0 + # Use regular dict + kt = ['a', 'b'][key_type] + dic = f"keys_{uid:08x}_{real_sec:02}_{nt[sec][key_type]}.dic" + cmd = f"hf mf fchk --blk {real_sec * 4} -{kt} -f {dic} --no-default" + if debug: + print(cmd) + p.console(cmd) + for line in p.grabbed_output.split('\n'): + if "aborted via keyboard" in line: + abort = True + if "found:" in line: + found_keys[sec][0] = line[30:].strip() + found_keys[sec][1] = line[30:].strip() + show_key(real_sec, 0, found_keys[sec][key_type]) + show_key(real_sec, 1, found_keys[sec][key_type]) + if abort: + break + + # If one key is missing, use the other one with 2x1nt1key + if ((found_keys[sec][0] == "") ^ (found_keys[sec][1] == "")) and nt[sec][0] != nt[sec][1]: + if (found_keys[sec][0] == ""): + key_type_source = 1 + key_type_target = 0 + else: + key_type_source = 0 + key_type_target = 1 + if duplicates_dicts[sec][key_type_target]: + dic = f"keys_{uid:08x}_{real_sec:02}_{nt[sec][key_type_target]}_duplicates.dic" + elif filtered_dicts[sec][key_type_target]: + dic = f"keys_{uid:08x}_{real_sec:02}_{nt[sec][key_type_target]}_filtered.dic" + else: + dic = f"keys_{uid:08x}_{real_sec:02}_{nt[sec][key_type_target]}.dic" + cmd = [tools["staticnested_2x1nt1key"], nt[sec][key_type_source], found_keys[sec][key_type_source], dic] + if debug: + print(' '.join(cmd)) + result = subprocess.run(cmd, capture_output=True, text=True).stdout + keys = set() + for line in result.split('\n'): + if "MATCH:" in line: + keys.add(line[12:]) + if len(keys) > 1: + kt = ['a', 'b'][key_type_target] + cmd = f"hf mf fchk --blk {real_sec * 4} -{kt} --no-default" + for k in keys: + cmd += f" -k {k}" + if debug: + print(cmd) + p.console(cmd) + for line in p.grabbed_output.split('\n'): + if "aborted via keyboard" in line: + abort = True + if "found:" in line: + found_keys[sec][key_type_target] = line[30:].strip() + elif len(keys) == 1: + found_keys[sec][key_type_target] = keys.pop() + if found_keys[sec][key_type_target] != "": + show_key(real_sec, key_type_target, found_keys[sec][key_type_target]) + if abort: + break + + if abort: + show("Brute-forcing phase aborted via keyboard!") + final_check = False + + plus = "[" + color("+", fg="green") + "] " + if final_check: + show("Letting fchk do a final dump, just for confirmation and display...") + keys_set = set([i for sl in found_keys for i in sl if i != ""]) + with (open(f"keys_{uid:08x}.dic", "w")) as f: + for k in keys_set: + f.write(f"{k}\n") + cmd = f"hf mf fchk -f keys_{uid:08x}.dic --no-default --dump" + if debug: + print(cmd) + p.console(cmd, capture=False, quiet=False) + else: + show() + show(color("found keys:", fg="green"), prompt=plus) + show(prompt=plus) + show("-----+-----+--------------+---+--------------+----", prompt=plus) + show(" Sec | Blk | key A |res| key B |res", prompt=plus) + show("-----+-----+--------------+---+--------------+----", prompt=plus) + for sec in range(NUM_SECTORS + NUM_EXTRA_SECTORS): + real_sec = sec + if sec >= NUM_SECTORS: + real_sec += 16 + keys = [["", 0], ["", 0]] + for key_type in [0, 1]: + if found_keys[sec][key_type] == "": + keys[key_type] = [color("------------", fg="red"), color("0", fg="red")] + else: + keys[key_type] = [color(found_keys[sec][key_type], fg="green"), color("1", fg="green")] + show(f" {real_sec:03} | {real_sec*4+3:03} | " + + f"{keys[0][0]} | {keys[0][1]} | {keys[1][0]} | {keys[1][1]} ", prompt=plus) + show("-----+-----+--------------+---+--------------+----", prompt=plus) + show("( " + color("0", fg="red") + ":Failed / " + + color("1", fg="green") + ":Success )", prompt=plus) + show() + show("Generating binary key file", prompt=plus) + keyfile = f"{save_path}hf-mf-{uid:08X}-key.bin" + unknown = False + with (open(keyfile, "wb")) as f: + for key_type in [0, 1]: + for sec in range(NUM_SECTORS + NUM_EXTRA_SECTORS): + k = found_keys[sec][key_type] + if k == "": + k = "FFFFFFFFFFFF" + unknown = True + f.write(bytes.fromhex(k)) + show("Found keys have been dumped to `" + color(keyfile, fg="yellow")+"`", prompt=plus) + if unknown: + show(" --[ " + color("FFFFFFFFFFFF", fg="yellow") + + " ]-- has been inserted for unknown keys", prompt="[" + color("=", fg="yellow") + "]") + show("Generating final dump file", prompt=plus) + dumpfile = f"{save_path}hf-mf-{uid:08X}-dump.bin" + with (open(dumpfile, "wb")) as f: + for sec in range(NUM_SECTORS): + for b in range(4): + d = data[(sec * 4) + b] + if b == 3: + ka = found_keys[sec][0] + kb = found_keys[sec][1] + if ka == "": + ka = "FFFFFFFFFFFF" + if kb == "": + kb = "FFFFFFFFFFFF" + d = ka + d[12:20] + kb + f.write(bytes.fromhex(d)) + show("Data has been dumped to `" + color(dumpfile, fg="yellow")+"`", prompt=plus) + + # Remove generated dictionaries after processing + if not keep: + show("Removing generated dictionaries...", prompt=plus) + for sec in range(NUM_SECTORS + NUM_EXTRA_SECTORS): + real_sec = sec + if sec >= NUM_SECTORS: + real_sec += 16 + for key_type in [0, 1]: + for append in ["", "_filtered", "_duplicates"]: + file_name = f"keys_{uid:08x}_{real_sec:02}_{nt[sec][key_type]}{append}.dic" + if os.path.isfile(file_name): + os.remove(file_name) + + elapsed_time3 = time.time() - start_time - elapsed_time1 - elapsed_time2 + minutes = int(elapsed_time3 // 60) + seconds = int(elapsed_time3 % 60) + show("----Step 3: " + color(f"{minutes:2}", fg="yellow") + " minutes " + + color(f"{seconds:2}", fg="yellow") + " seconds -----------") + + elapsed_time = time.time() - start_time + minutes = int(elapsed_time // 60) + seconds = int(elapsed_time % 60) + show("---- TOTAL: " + color(f"{minutes:2}", fg="yellow") + " minutes " + + color(f"{seconds:2}", fg="yellow") + " seconds -----------") + + return {'keyfile': keyfile, 'found_keys': found_keys, 'dumpfile': dumpfile, 'data': data} + + +def main(): + parser = argparse.ArgumentParser(description='A script combining staticnested* tools ' + 'to recover all keys from a FM11RF08S card.') + parser.add_argument('-x', '--init-check', action='store_true', help='Run an initial fchk for default keys') + parser.add_argument('-y', '--final-check', action='store_true', help='Run a final fchk with the found keys') + parser.add_argument('-k', '--keep', action='store_true', help='Keep generated dictionaries after processing') + parser.add_argument('-d', '--debug', action='store_true', help='Enable debug mode') + parser.add_argument('-s', '--supply-chain', action='store_true', help='Enable supply-chain mode. ' + 'Look for hf-mf-XXXXXXXX-default_nonces.json') + # Such json can be produced from the json saved by + # "hf mf isen --collect_fm11rf08s --key A396EFA4E24F" on a wiped card, then processed with + # jq '{Created: .Created, FileType: "fm11rf08s_default_nonces", nt: .nt | del(.["32"]) | map_values(.a)}' + args = parser.parse_args() + + recovery( + init_check=args.init_check, + final_check=args.final_check, + keep=args.keep, + debug=args.debug, + supply_chain=args.supply_chain, + quiet=False + ) + + +if __name__ == '__main__': + main() diff --git a/client/pyscripts/intertic.py b/client/pyscripts/intertic.py index 82d39f57f..8d6d865d6 100644 --- a/client/pyscripts/intertic.py +++ b/client/pyscripts/intertic.py @@ -90,11 +90,11 @@ def Describe_Usage_1(Usage, ContractMediumEndDate, Certificate): unk = Usage.nom_bits(65) EventValidityTimeFirstStamp = Usage.nom(11) - print(' EventDateStamp : {} ({})'.format(EventDateStamp, (datetime(1997, 1, 1) + timedelta(days = ContractMediumEndDate - EventDateStamp)).strftime('%Y-%m-%d'))); + print(' EventDateStamp : {} ({})'.format(EventDateStamp, (datetime(1997, 1, 1) + timedelta(days = ContractMediumEndDate - EventDateStamp)).strftime('%Y-%m-%d'))) print(' EventTimeStamp : {} ({:02d}:{:02d})'. format(EventTimeStamp, EventTimeStamp // 60, EventTimeStamp % 60)) - print(' unk1... :', unk); + print(' unk1... :', unk) print(' EventValidityTimeFirstStamp: {} ({:02d}:{:02d})'. format(EventValidityTimeFirstStamp, EventValidityTimeFirstStamp // 60, EventValidityTimeFirstStamp % 60)) - print(' left... :', Usage.nom_bits_left()); + print(' left... :', Usage.nom_bits_left()) print(' [CER] Usage : {:04x}'.format(Certificate.nom(16))) def Describe_Usage_1_1(Usage, ContractMediumEndDate, Certificate): @@ -110,18 +110,18 @@ def Describe_Usage_1_1(Usage, ContractMediumEndDate, Certificate): EventCountPassengers_mb = Usage.nom(4) EventValidityTimeFirstStamp = Usage.nom(11) - print(' DateStamp : {} ({})'.format(EventDateStamp, (datetime(1997, 1, 1) + timedelta(days = ContractMediumEndDate - EventDateStamp)).strftime('%Y-%m-%d'))); + print(' DateStamp : {} ({})'.format(EventDateStamp, (datetime(1997, 1, 1) + timedelta(days = ContractMediumEndDate - EventDateStamp)).strftime('%Y-%m-%d'))) print(' TimeStamp : {} ({:02d}:{:02d})'. format(EventTimeStamp, EventTimeStamp // 60, EventTimeStamp % 60)) - print(' unk0... :', unk0); + print(' unk0... :', unk0) print(' Code/Nature : 0x{:x} ({})'.format(EventCode_Nature, TYPE_EventCode_Nature.get(EventCode_Nature, '?'))) print(' Code/Type : 0x{:x} ({})'.format(EventCode_Type, TYPE_EventCode_Type.get(EventCode_Type, '?'))) - print(' unk1... :', unk1); + print(' unk1... :', unk1) print(' GeoVehicleId : {}'. format(EventGeoVehicleId)) print(' GeoRouteId : {}'. format(EventGeoRouteId)) print(' Direction : {} ({})'. format(EventGeoRoute_Direction, TYPE_EventGeoRoute_Direction.get(EventGeoRoute_Direction, '?'))) print(' Passengers(?) : {}'. format(EventCountPassengers_mb)) print(' ValidityTimeFirstStamp: {} ({:02d}:{:02d})'. format(EventValidityTimeFirstStamp, EventValidityTimeFirstStamp // 60, EventValidityTimeFirstStamp % 60)) - print(' left... :', Usage.nom_bits_left()); + print(' left... :', Usage.nom_bits_left()) print(' [CER] Usage : {:04x}'.format(Certificate.nom(16))) def Describe_Usage_1_2(Usage, ContractMediumEndDate, Certificate): @@ -143,19 +143,19 @@ def Describe_Usage_1_2(Usage, ContractMediumEndDate, Certificate): 0x1: 'tramway', } - print(' DateStamp : {} ({})'.format(EventDateStamp, (datetime(1997, 1, 1) + timedelta(days = ContractMediumEndDate - EventDateStamp)).strftime('%Y-%m-%d'))); + print(' DateStamp : {} ({})'.format(EventDateStamp, (datetime(1997, 1, 1) + timedelta(days = ContractMediumEndDate - EventDateStamp)).strftime('%Y-%m-%d'))) print(' TimeStamp : {} ({:02d}:{:02d})'. format(EventTimeStamp, EventTimeStamp // 60, EventTimeStamp % 60)) print(' Count(?) : {}'. format(EventCount_mb)) - print(' unk0... :', unk0); + print(' unk0... :', unk0) print(' Code/Nature(?) : 0x{:x} ({})'.format(EventCode_Nature_mb, TYPE_EventCode_Nature_Reims.get(EventCode_Nature_mb, '?'))) print(' Code/Type(?) : 0x{:x} ({})'.format(EventCode_Type_mb, TYPE_EventCode_Type.get(EventCode_Type_mb, '?'))) - print(' unk1... :', unk1); + print(' unk1... :', unk1) print(' GeoVehicleId : {}'. format(EventGeoVehicleId)) print(' GeoRouteId : {}'. format(EventGeoRouteId)) print(' Direction : {} ({})'. format(EventGeoRoute_Direction, TYPE_EventGeoRoute_Direction.get(EventGeoRoute_Direction, '?'))) print(' Passengers(?) : {}'. format(EventCountPassengers_mb)) print(' ValidityTimeFirstStamp: {} ({:02d}:{:02d})'. format(EventValidityTimeFirstStamp, EventValidityTimeFirstStamp // 60, EventValidityTimeFirstStamp % 60)) - print(' left... :', Usage.nom_bits_left()); + print(' left... :', Usage.nom_bits_left()) print(' [CER] Usage : {:04x}'.format(Certificate.nom(16))) @@ -171,17 +171,17 @@ def Describe_Usage_2(Usage, ContractMediumEndDate, Certificate): EventCountPassengers_mb = Usage.nom(4) EventValidityTimeFirstStamp = Usage.nom(11) - print(' DateStamp : {} ({})'.format(EventDateStamp, (datetime(1997, 1, 1) + timedelta(days = ContractMediumEndDate - EventDateStamp)).strftime('%Y-%m-%d'))); + print(' DateStamp : {} ({})'.format(EventDateStamp, (datetime(1997, 1, 1) + timedelta(days = ContractMediumEndDate - EventDateStamp)).strftime('%Y-%m-%d'))) print(' TimeStamp : {} ({:02d}:{:02d})'. format(EventTimeStamp, EventTimeStamp // 60, EventTimeStamp % 60)) - print(' unk0... :', unk0); + print(' unk0... :', unk0) print(' Code/Nature : 0x{:x} ({})'.format(EventCode_Nature, TYPE_EventCode_Nature.get(EventCode_Nature, '?'))) print(' Code/Type : 0x{:x} ({})'.format(EventCode_Type, TYPE_EventCode_Type.get(EventCode_Type, '?'))) - print(' unk1... :', unk1); + print(' unk1... :', unk1) print(' GeoRouteId : {}'. format(EventGeoRouteId)) print(' Direction : {} ({})'. format(EventGeoRoute_Direction, TYPE_EventGeoRoute_Direction.get(EventGeoRoute_Direction, '?'))) print(' Passengers(?) : {}'. format(EventCountPassengers_mb)) print(' ValidityTimeFirstStamp: {} ({:02d}:{:02d})'. format(EventValidityTimeFirstStamp, EventValidityTimeFirstStamp // 60, EventValidityTimeFirstStamp % 60)) - print(' left... :', Usage.nom_bits_left()); + print(' left... :', Usage.nom_bits_left()) print(' [CER] Usage : {:04x}'.format(Certificate.nom(16))) def Describe_Usage_3(Usage, ContractMediumEndDate, Certificate): @@ -190,11 +190,11 @@ def Describe_Usage_3(Usage, ContractMediumEndDate, Certificate): unk = Usage.nom_bits(27) EventValidityTimeFirstStamp = Usage.nom(11) - print(' EventDateStamp : {} ({})'.format(EventDateStamp, (datetime(1997, 1, 1) + timedelta(days = ContractMediumEndDate - EventDateStamp)).strftime('%Y-%m-%d'))); + print(' EventDateStamp : {} ({})'.format(EventDateStamp, (datetime(1997, 1, 1) + timedelta(days = ContractMediumEndDate - EventDateStamp)).strftime('%Y-%m-%d'))) print(' EventTimeStamp : {} ({:02d}:{:02d})'. format(EventTimeStamp, EventTimeStamp // 60, EventTimeStamp % 60)) - print(' unk1... :', unk); + print(' unk1... :', unk) print(' EventValidityTimeFirstStamp: {} ({:02d}:{:02d})'. format(EventValidityTimeFirstStamp, EventValidityTimeFirstStamp // 60, EventValidityTimeFirstStamp % 60)) - print(' left... :', Usage.nom_bits_left()); + print(' left... :', Usage.nom_bits_left()) print(' [CER] Usage : {:04x}'.format(Certificate.nom(16))) def Describe_Usage_4(Usage, ContractMediumEndDate, Certificate): @@ -203,16 +203,16 @@ def Describe_Usage_4(Usage, ContractMediumEndDate, Certificate): unk = Usage.nom_bits(63) EventValidityTimeFirstStamp = Usage.nom(11) - print(' EventDateStamp : {} ({})'.format(EventDateStamp, (datetime(1997, 1, 1) + timedelta(days = ContractMediumEndDate - EventDateStamp)).strftime('%Y-%m-%d'))); + print(' EventDateStamp : {} ({})'.format(EventDateStamp, (datetime(1997, 1, 1) + timedelta(days = ContractMediumEndDate - EventDateStamp)).strftime('%Y-%m-%d'))) print(' EventTimeStamp : {} ({:02d}:{:02d})'. format(EventTimeStamp, EventTimeStamp // 60, EventTimeStamp % 60)) - print(' unk1... :', unk); + print(' unk1... :', unk) print(' EventValidityTimeFirstStamp: {} ({:02d}:{:02d})'. format(EventValidityTimeFirstStamp, EventValidityTimeFirstStamp // 60, EventValidityTimeFirstStamp % 60)) - print(' left... :', Usage.nom_bits_left()); + print(' left... :', Usage.nom_bits_left()) print(' [CER] Usage : {:04x}'.format(Certificate.nom(16))) def Describe_Usage_Generic(Usage, ContractMediumEndDate, Certificate): print(' !!! GENERIC DUMP - please provide full file dump to benjamin@gentilkiwi.com - especially if NOT empty !!!') - print(' left... :', Usage.nom_bits_left()); + print(' left... :', Usage.nom_bits_left()) print(' [CER] Usage : {:04x}'.format(Certificate.nom(16))) print(' !!! Trying Usage_1 (the most common) !!!') Usage.reset() @@ -239,6 +239,9 @@ FRA_OrganizationalAuthority_Contract_Provider = { 0x008: { 15: InterticHelper('Angoulême', 'STGA', Describe_Usage_1_1), # May have a problem with date ? }, + 0x013: { + 1: InterticHelper('Avignon', 'Orizo'), + }, 0x021: { 1: InterticHelper('Bordeaux', 'TBM / Keolis', Describe_Usage_1_1), }, @@ -256,6 +259,7 @@ FRA_OrganizationalAuthority_Contract_Provider = { }, 0x502: { 83: InterticHelper('Annecy', 'Sibra', Describe_Usage_2), + 84: InterticHelper('Bourg-en-Bresse', 'Rubis / Keolis'), 10: InterticHelper('Clermont-Ferrand', 'T2C'), }, 0x907: { @@ -374,7 +378,7 @@ def main(): return 3 - print('PID (product): 0x{:02x} (flipflop?: {})'.format(PID, (PID & 0x10) != 0)); + print('PID (product): 0x{:02x} (flipflop?: {})'.format(PID, (PID & 0x10) != 0)) print('KeyId : 0x{:1x}'.format(KeyId)) print() @@ -397,10 +401,11 @@ def main(): Distribution_left = Distribution_Data.nom_bits_left() print('DISTRIBUTION') - print(' CountryCode : {:03x} - {}'.format(CountryCode, ISO_Countries.get(CountryCode, '?'))); - print(' OrganizationalAuthority : {:03x}'.format(OrganizationalAuthority)); - print(' ContractApplicationVersionNumber:', ContractApplicationVersionNumber); - print(' ContractProvider :', ContractProvider); + print(' CountryCode : {:03x} - {}'.format(CountryCode, ISO_Countries.get(CountryCode, '?'))) + print(' OrganizationalAuthority : {:03x}'.format(OrganizationalAuthority)) + print(' ContractApplicationVersionNumber:', ContractApplicationVersionNumber) + print(' ContractProvider :', ContractProvider) + if (CountryCode == 0x250): oa = FRA_OrganizationalAuthority_Contract_Provider.get(OrganizationalAuthority) if (oa is not None): @@ -408,9 +413,10 @@ def main(): if (s is not None): print(' ~ Authority & Provider ~ : {} ({})'.format(s.OrganizationalAuthority, s.ContractProvider)) Describe_Usage = s.UsageDescribeFunction - print(' ContractTariff :', ContractTariff); - print(' ContractMediumEndDate : {} ({})'.format(ContractMediumEndDate, (datetime(1997, 1, 1) + timedelta(days = ContractMediumEndDate)).strftime('%Y-%m-%d'))); - print(' left... :', Distribution_left); + + print(' ContractTariff :', ContractTariff) + print(' ContractMediumEndDate : {} ({})'.format(ContractMediumEndDate, (datetime(1997, 1, 1) + timedelta(days = ContractMediumEndDate)).strftime('%Y-%m-%d'))) + print(' left... :', Distribution_left) print(' [CER] Distribution : {:08x}'.format(Distribution_Cer.nom(32))) print() diff --git a/client/pyscripts/mf_backdoor_dump.py b/client/pyscripts/mf_backdoor_dump.py index bb564544d..2831db5de 100644 --- a/client/pyscripts/mf_backdoor_dump.py +++ b/client/pyscripts/mf_backdoor_dump.py @@ -26,7 +26,7 @@ for bk, sz in BACKDOOR_KEYS: print("Error reading the tag:") print("\n".join(output)) break - elif "[-] Fill ( fail )" in output: + elif "[-] Fill ( fail )" in output: continue elif "[+] Fill ( ok )" not in output: print("Unexpected output, exiting:") diff --git a/client/pyscripts/pm3.py b/client/pyscripts/pm3.py index ead49777a..26e90a774 100644 --- a/client/pyscripts/pm3.py +++ b/client/pyscripts/pm3.py @@ -66,8 +66,8 @@ class pm3(object): _pm3.pm3_swiginit(self, _pm3.new_pm3(*args)) __swig_destroy__ = _pm3.delete_pm3 - def console(self, cmd, passthru=False): - return _pm3.pm3_console(self, cmd, passthru) + def console(self, cmd, capture=True, quiet=True): + return _pm3.pm3_console(self, cmd, capture, quiet) name = property(_pm3.pm3_name_get) grabbed_output = property(_pm3.pm3_grabbed_output_get) diff --git a/client/pyscripts/spi_flash_decode.py b/client/pyscripts/spi_flash_decode.py index 8faa795ba..0f125844b 100644 --- a/client/pyscripts/spi_flash_decode.py +++ b/client/pyscripts/spi_flash_decode.py @@ -78,20 +78,27 @@ p = pm3.pm3() p.console("hw status") rex = re.compile("...\\s([0-9a-fA-F]{2})\\s/\\s([0-9a-fA-F]{4})") + for line in p.grabbed_output.split('\n'): # [#] JEDEC Mfr ID / Dev ID... 85 / 6015 if " JEDEC " not in line: continue + match = re.findall(rex, line) mid = int(match[0][0], 16) did = int(match[0][1], 16) did_h = did >> 8 did_l = did & 0xff t = None + if mid in spi: + mfr = spi[mid]['manufacturer'] + if did_h in spi[mid]: + if did_l in spi[mid][did_h]: + t = spi[mid][did_h][did_l] print("\n Manufacturer... " + color(f"{mfr}", fg="green") + "\n Device......... " + color(f"{t['part']}", fg="green") + diff --git a/client/pyscripts/theremin.py b/client/pyscripts/theremin.py index a286d6193..790fc6bf8 100755 --- a/client/pyscripts/theremin.py +++ b/client/pyscripts/theremin.py @@ -39,8 +39,7 @@ v = 0 out_freq = min_freq # Spawn the Proxmark3 client -pm3_proc = Popen([pm3_client, pm3_reader_dev_file, "-c", pm3_tune_cmd], - bufsize=0, env={}, stdin=DEVNULL, stdout=PIPE, stderr=DEVNULL) +pm3_proc = Popen([pm3_client, pm3_reader_dev_file, "-c", pm3_tune_cmd], bufsize=0, env={}, stdin=DEVNULL, stdout=PIPE, stderr=DEVNULL) mv_recbuf = "" # Read voltages from the Proxmark3, generate the sine wave, output to soundcard diff --git a/client/resources/aid_desfire.json b/client/resources/aid_desfire.json index e5374f217..b3dc2bcfd 100644 --- a/client/resources/aid_desfire.json +++ b/client/resources/aid_desfire.json @@ -199,6 +199,14 @@ "Description": "Access Control", "Type": "pacs" }, + { + "AID": "F47300", + "Vendor": "Inner Range", + "Country": "AU", + "Name": "Sifer-P, Sifer-U Credential", + "Description": "Inner Range access control", + "Type": "pacs" + }, { "AID": "F48120", "Vendor": "Gallagher Group Limited via PEC (New Zealand) Limited", @@ -415,6 +423,30 @@ "Description": "", "Type": "alarm system" }, + { + "AID": "010010", + "Vendor": "ASSA ABLOY", + "Country": "GB", + "Name": "Campus Card", + "Description": "Campus Card", + "Type": "student" + }, + { + "AID": "030020", + "Vendor": "Algonquin College of Applied Arts and Technology", + "Country": "CA", + "Name": "Campus Card", + "Description": "Campus Card", + "Type": "student" + }, + { + "AID": "050030", + "Vendor": "Algonquin College of Applied Arts and Technology", + "Country": "CA", + "Name": "Campus Card", + "Description": "Campus Card", + "Type": "student" + }, { "AID": "05845F", "Vendor": "InterCard GmbH Kartensysteme", @@ -423,6 +455,14 @@ "Description": "Campus Card", "Type": "student" }, + { + "AID": "070090", + "Vendor": "Algonquin College of Applied Arts and Technology", + "Country": "CA", + "Name": "Campus Card", + "Description": "Campus Card", + "Type": "student" + }, { "AID": "15845F", "Vendor": "InterCard GmbH Kartensysteme", @@ -639,6 +679,38 @@ "Description": "Campus Card", "Type": "student" }, + { + "AID": "CA1827", + "Vendor": "Transact Campus Inc.", + "Country": "US", + "Name": "Transact Campus ID [Custom AID]", + "Description": "Campus Card", + "Type": "student" + }, + { + "AID": "EEE010", + "Vendor": "ASSA ABLOY", + "Country": "GB", + "Name": "Campus Card", + "Description": "Campus Card", + "Type": "student" + }, + { + "AID": "F33480", + "Vendor": "Besucherausweis", + "Country": "DE", + "Name": "Visitor's Pass", + "Description": "Besucherausweis", + "Type": "student" + }, + { + "AID": "F482D0", + "Vendor": "Besucherausweis", + "Country": "DE", + "Name": "Visitor's Pass", + "Description": "Besucherausweis", + "Type": "student" + }, { "AID": "F48EF1", "Vendor": "TU Delft", @@ -1143,4 +1215,4 @@ "Description": "Used by AKL AT HOP, DXB nol, and SEA ORCA", "Type": "transport" } -] +] \ No newline at end of file diff --git a/client/resources/mad.json b/client/resources/mad.json index a9158fa2a..bd457ca8b 100644 --- a/client/resources/mad.json +++ b/client/resources/mad.json @@ -4690,6 +4690,13 @@ "service_provider": "Enkoa", "system_integrator": "Juan Cruz Iriondo" }, + { + "application": "Access control", + "company": "Inner Range", + "mad": "0x4730", + "service_provider": "Inner Range", + "system_integrator": "Inner Range" + }, { "application": "Trade fair card Deutsche Messe AG, Hannover", "company": "Systemform GmbH", diff --git a/client/src/atrs.c b/client/src/atrs.c index d8f11587f..e851cf8f9 100644 --- a/client/src/atrs.c +++ b/client/src/atrs.c @@ -24,22 +24,26 @@ // get a ATR description based on the atr bytes // returns description of the best match const char *getAtrInfo(const char *atr_str) { + size_t slen = strlen(atr_str); int match = -1; - // skip last element of AtrTable - for (int i = 0; i < ARRAYLEN(AtrTable) - 1; ++i) { - if (strlen(AtrTable[i].bytes) != slen) + // skip last element of AtrTable + for (size_t i = 0; i < ARRAYLEN(AtrTable) - 1; ++i) { + + if (strlen(AtrTable[i].bytes) != slen) { continue; + } if (strstr(AtrTable[i].bytes, ".") != NULL) { + char *tmp_atr = calloc(slen, sizeof(uint8_t)); if (tmp_atr == NULL) { PrintAndLogEx(FAILED, "failed to allocate memory"); return NULL; } - for (int j = 0; j < slen; j++) { + for (size_t j = 0; j < slen; j++) { tmp_atr[j] = (AtrTable[i].bytes[j] == '.') ? '.' : atr_str[j]; } diff --git a/client/src/cmdanalyse.c b/client/src/cmdanalyse.c index d3456950d..d5f86a161 100644 --- a/client/src/cmdanalyse.c +++ b/client/src/cmdanalyse.c @@ -40,10 +40,10 @@ static int CmdHelp(const char *Cmd); static uint8_t calculateLRC(const uint8_t *d, uint8_t n) { - uint8_t lcr = 0; + uint8_t lrc = 0; for (uint8_t i = 0; i < n; i++) - lcr ^= d[i]; - return lcr; + lrc ^= d[i]; + return lrc; } /* static uint16_t matrixadd ( uint8_t* bytes, uint8_t len){ @@ -242,17 +242,17 @@ static int CmdAnalyseLfsr(const char *Cmd) { return PM3_SUCCESS; } -static int CmdAnalyseLCR(const char *Cmd) { +static int CmdAnalyseLRC(const char *Cmd) { CLIParserContext *ctx; - CLIParserInit(&ctx, "analyse lcr", + CLIParserInit(&ctx, "analyse lrc", "Specifying the bytes of a UID with a known LRC will find the last byte value\n" "needed to generate that LRC with a rolling XOR. All bytes should be specified in HEX.", - "analyse lcr -d 04008064BA -> Target (BA) requires final LRC XOR byte value: 5A" + "analyse lrc -d 04008064BA -> Target (BA) requires final LRC XOR byte value: 5A" ); void *argtable[] = { arg_param_begin, - arg_str1("d", "data", "", "bytes to calc missing XOR in a LCR"), + arg_str1("d", "data", "", "bytes to calc missing XOR in a LRC"), arg_param_end }; CLIExecWithReturn(ctx, Cmd, argtable, true); @@ -1171,7 +1171,7 @@ static int CmdAnalyseUnits(const char *Cmd) { static command_t CommandTable[] = { {"help", CmdHelp, AlwaysAvailable, "This help"}, - {"lcr", CmdAnalyseLCR, AlwaysAvailable, "Generate final byte for XOR LRC"}, + {"lrc", CmdAnalyseLRC, AlwaysAvailable, "Generate final byte for XOR LRC"}, {"crc", CmdAnalyseCRC, AlwaysAvailable, "Stub method for CRC evaluations"}, {"chksum", CmdAnalyseCHKSUM, AlwaysAvailable, "Checksum with adding, masking and one's complement"}, {"dates", CmdAnalyseDates, AlwaysAvailable, "Look for datestamps in a given array of bytes"}, diff --git a/client/src/cmddata.c b/client/src/cmddata.c index 35573b88a..b0765ae0b 100644 --- a/client/src/cmddata.c +++ b/client/src/cmddata.c @@ -3069,7 +3069,7 @@ static int CmdNumCon(const char *Cmd) { "Function takes a decimal or hexdecimal number and print it in decimal/hex/binary\n" "Will print message if number is a prime number\n", "data num --dec 2023\n" - "data num --hex 0x1000\n" + "data num --hex 2A\n" ); void *argtable[] = { @@ -3171,7 +3171,7 @@ static int CmdNumCon(const char *Cmd) { pn = (hlen * 4) - slen + 1; } } - PrintAndLogEx(SUCCESS, "%s%.*s%s",radix[i].desc, pn, pad, s); + PrintAndLogEx(SUCCESS, "%s%.*s%s", radix[i].desc, pn, pad, s); } } @@ -3384,9 +3384,9 @@ static int envelope_square(const int *in, int *out, size_t len) { static int CmdEnvelope(const char *Cmd) { CLIParserContext *ctx; - CLIParserInit(&ctx, "data envelop", - "Create an square envelop of the samples", - "data envelop" + CLIParserInit(&ctx, "data envelope", + "Create an square envelope of the samples", + "data envelope" ); void *argtable[] = { arg_param_begin, diff --git a/client/src/cmdflashmem.c b/client/src/cmdflashmem.c index a43989886..d1e9ab672 100644 --- a/client/src/cmdflashmem.c +++ b/client/src/cmdflashmem.c @@ -50,6 +50,29 @@ static int CmdHelp(const char *Cmd); //------------------------------------------------------------------------------------- +int rdv4_get_flash_pages64k(uint8_t *pages64k) { + if (pages64k == NULL) { + return PM3_EINVARG; + } + + clearCommandBuffer(); + SendCommandNG(CMD_FLASHMEM_PAGES64K, NULL, 0); + PacketResponseNG resp; + if (WaitForResponseTimeout(CMD_ACK, &resp, 2500) == false) { + PrintAndLogEx(WARNING, "rdv4_get_flash_pages64k() timeout while waiting for reply"); + return PM3_ETIMEOUT; + } + + uint8_t isok = resp.oldarg[0] & 0xFF; + if (isok == false) { + PrintAndLogEx(FAILED, "fail reading from flash (pages 64k)"); + return PM3_EFLASH; + } + + memcpy(pages64k, (uint8_t *)resp.data.asBytes, sizeof(uint8_t)); + return PM3_SUCCESS; +} + int rdv4_get_signature(rdv40_validation_t *out) { if (out == NULL) { return PM3_EINVARG; @@ -98,8 +121,16 @@ int rdv4_validate(rdv40_validation_t *mem) { } static int rdv4_sign_write(uint8_t *signature, uint8_t slen) { + + uint8_t spi_flash_pages = 0; + int res = rdv4_get_flash_pages64k(&spi_flash_pages); + if (res != PM3_SUCCESS) { + PrintAndLogEx(ERR, "failed to get flash pages (%x)", res); + return res; + } + flashmem_old_write_t payload = { - .startidx = FLASH_MEM_SIGNATURE_OFFSET, + .startidx = FLASH_MEM_SIGNATURE_OFFSET_P(spi_flash_pages), .len = FLASH_MEM_SIGNATURE_LEN, }; memcpy(payload.data, signature, slen); @@ -117,7 +148,7 @@ static int rdv4_sign_write(uint8_t *signature, uint8_t slen) { return PM3_EFAILED; } } - PrintAndLogEx(SUCCESS, "Writing signature at offset %u ( "_GREEN_("ok") " )", FLASH_MEM_SIGNATURE_OFFSET); + PrintAndLogEx(SUCCESS, "Writing signature at offset %u ( "_GREEN_("ok") " )", FLASH_MEM_SIGNATURE_OFFSET_P(spi_flash_pages)); return PM3_SUCCESS; } @@ -201,15 +232,21 @@ static int CmdFlashMemLoad(const char *Cmd) { PrintAndLogEx(INFO, "treating file as T55xx passwords"); } + uint8_t spi_flash_pages = 0; + int res = rdv4_get_flash_pages64k(&spi_flash_pages); + if (res != PM3_SUCCESS) { + PrintAndLogEx(ERR, "failed to get flash pages count (%x)", res); + return res; + } + size_t datalen = 0; uint32_t keycount = 0; - int res = 0; uint8_t keylen = 0; - uint8_t *data = calloc(FLASH_MEM_MAX_SIZE, sizeof(uint8_t)); + uint8_t *data = calloc(FLASH_MEM_MAX_SIZE_P(spi_flash_pages), sizeof(uint8_t)); switch (d) { case DICTIONARY_MIFARE: - offset = DEFAULT_MF_KEYS_OFFSET; + offset = DEFAULT_MF_KEYS_OFFSET_P(spi_flash_pages); keylen = 6; res = loadFileDICTIONARY(filename, data + 2, &datalen, keylen, &keycount); if (res || !keycount) { @@ -227,7 +264,7 @@ static int CmdFlashMemLoad(const char *Cmd) { datalen += 2; break; case DICTIONARY_T55XX: - offset = DEFAULT_T55XX_KEYS_OFFSET; + offset = DEFAULT_T55XX_KEYS_OFFSET_P(spi_flash_pages); keylen = 4; res = loadFileDICTIONARY(filename, data + 2, &datalen, keylen, &keycount); if (res || !keycount) { @@ -245,7 +282,7 @@ static int CmdFlashMemLoad(const char *Cmd) { datalen += 2; break; case DICTIONARY_ICLASS: - offset = DEFAULT_ICLASS_KEYS_OFFSET; + offset = DEFAULT_ICLASS_KEYS_OFFSET_P(spi_flash_pages); res = loadFileDICTIONARY(filename, data + 2, &datalen, keylen, &keycount); if (res || !keycount) { free(data); @@ -268,7 +305,7 @@ static int CmdFlashMemLoad(const char *Cmd) { return PM3_EFILE; } - if (datalen > FLASH_MEM_MAX_SIZE) { + if (datalen > FLASH_MEM_MAX_SIZE_P(spi_flash_pages)) { PrintAndLogEx(ERR, "error, filesize is larger than available memory"); free(data); return PM3_EOVFLOW; @@ -351,8 +388,15 @@ static int CmdFlashMemDump(const char *Cmd) { }; CLIExecWithReturn(ctx, Cmd, argtable, false); + uint8_t spi_flash_pages = 0; + int res = rdv4_get_flash_pages64k(&spi_flash_pages); + if (res != PM3_SUCCESS) { + PrintAndLogEx(ERR, "failed to get flash pages count (%x)", res); + return res; + } + int offset = arg_get_int_def(ctx, 1, 0); - int len = arg_get_int_def(ctx, 2, FLASH_MEM_MAX_SIZE); + int len = arg_get_int_def(ctx, 2, FLASH_MEM_MAX_SIZE_P(spi_flash_pages)); bool view = arg_get_lit(ctx, 3); int fnlen = 0; char filename[FILE_PATH_SIZE] = {0}; @@ -409,15 +453,22 @@ static int CmdFlashMemWipe(const char *Cmd) { // initialwipe = arg_get_lit(ctx, 2); CLIParserFree(ctx); - if (page < 0 || page > 2) { - PrintAndLogEx(WARNING, "page must be 0, 1 or 2"); + uint8_t spi_flash_pages = 0; + int res = rdv4_get_flash_pages64k(&spi_flash_pages); + if (res != PM3_SUCCESS) { + PrintAndLogEx(ERR, "failed to get flash pages count (%x)", res); + return res; + } + + if (page < 0 || page > (spi_flash_pages - 2)) { + PrintAndLogEx(WARNING, "page must be between 0 and %d", spi_flash_pages - 2); return PM3_EINVARG; } clearCommandBuffer(); SendCommandMIX(CMD_FLASHMEM_WIPE, page, initialwipe, 0, NULL, 0); PacketResponseNG resp; - if (!WaitForResponseTimeout(CMD_ACK, &resp, 8000)) { + if (!WaitForResponseTimeout(CMD_ACK, &resp, 10000)) { PrintAndLogEx(WARNING, "timeout while waiting for reply."); return PM3_ETIMEOUT; } diff --git a/client/src/cmdflashmem.h b/client/src/cmdflashmem.h index 60bdd5857..ad8727204 100644 --- a/client/src/cmdflashmem.h +++ b/client/src/cmdflashmem.h @@ -32,4 +32,5 @@ typedef enum { int CmdFlashMem(const char *Cmd); int rdv4_get_signature(rdv40_validation_t *out); int rdv4_validate(rdv40_validation_t *mem); +int rdv4_get_flash_pages64k(uint8_t *pages64k); #endif diff --git a/client/src/cmdhf14a.c b/client/src/cmdhf14a.c index a662710a0..d5fbf2633 100644 --- a/client/src/cmdhf14a.c +++ b/client/src/cmdhf14a.c @@ -830,20 +830,11 @@ int CmdHF14ASim(const char *Cmd) { bool useUIDfromEML = true; if (uid_len > 0) { - switch (uid_len) { - case 10: - flags |= FLAG_10B_UID_IN_DATA; - break; - case 7: - flags |= FLAG_7B_UID_IN_DATA; - break; - case 4: - flags |= FLAG_4B_UID_IN_DATA; - break; - default: - PrintAndLogEx(ERR, "Please specify a 4, 7, or 10 byte UID"); - CLIParserFree(ctx); - return PM3_EINVARG; + FLAG_SET_UID_IN_DATA(flags, uid_len); + if (IS_FLAG_UID_IN_EMUL(flags)) { + PrintAndLogEx(ERR, "Please specify a 4, 7, or 10 byte UID"); + CLIParserFree(ctx); + return PM3_EINVARG; } PrintAndLogEx(SUCCESS, "Emulating " _YELLOW_("ISO/IEC 14443 type A tag")" with " _GREEN_("%d byte UID (%s)"), uid_len, sprint_hex(uid, uid_len)); useUIDfromEML = false; @@ -866,7 +857,7 @@ int CmdHF14ASim(const char *Cmd) { } if (useUIDfromEML) { - flags |= FLAG_UID_IN_EMUL; + FLAG_SET_UID_IN_EMUL(flags); } struct { @@ -1660,12 +1651,14 @@ static int CmdHF14AAntiFuzz(const char *Cmd) { struct { uint8_t flag; } PACKED param; - param.flag = FLAG_4B_UID_IN_DATA; - - if (arg_get_lit(ctx, 2)) - param.flag = FLAG_7B_UID_IN_DATA; - if (arg_get_lit(ctx, 3)) - param.flag = FLAG_10B_UID_IN_DATA; + param.flag = 0; + FLAG_SET_UID_IN_DATA(param.flag, 4); + if (arg_get_lit(ctx, 2)) { + FLAG_SET_UID_IN_DATA(param.flag, 7); + } + if (arg_get_lit(ctx, 3)) { + FLAG_SET_UID_IN_DATA(param.flag, 10); + } CLIParserFree(ctx); clearCommandBuffer(); @@ -1843,6 +1836,7 @@ static int detect_nxp_card_print(uint8_t sak, uint16_t atqa, uint64_t select_sta } else if ((sak & 0x28) == 0x28) { printTag("SmartMX with MIFARE Classic 1K"); printTag("FM1208-10 with MIFARE Classic 1K"); + printTag("FM1216-137 with MIFARE Classic 1K"); type |= MTCLASSIC; } else if ((sak & 0x08) == 0x08) { if (select_status == 1) { @@ -3725,20 +3719,11 @@ int CmdHF14AAIDSim(const char *Cmd) { bool useUIDfromEML = true; if (uid_len > 0) { - switch (uid_len) { - case 10: - flags |= FLAG_10B_UID_IN_DATA; - break; - case 7: - flags |= FLAG_7B_UID_IN_DATA; - break; - case 4: - flags |= FLAG_4B_UID_IN_DATA; - break; - default: - PrintAndLogEx(ERR, "Please specify a 4, 7, or 10 byte UID"); - CLIParserFree(ctx); - return PM3_EINVARG; + FLAG_SET_UID_IN_DATA(flags, uid_len); + if (IS_FLAG_UID_IN_EMUL(flags)) { + PrintAndLogEx(ERR, "Please specify a 4, 7, or 10 byte UID"); + CLIParserFree(ctx); + return PM3_EINVARG; } PrintAndLogEx(SUCCESS, "Emulating " _YELLOW_("ISO/IEC 14443 type A tag")" with " _GREEN_("%d byte UID (%s)"), uid_len, sprint_hex(uid, uid_len)); useUIDfromEML = false; @@ -3757,7 +3742,7 @@ int CmdHF14AAIDSim(const char *Cmd) { } if (useUIDfromEML) { - flags |= FLAG_UID_IN_EMUL; + FLAG_SET_UID_IN_EMUL(flags); } struct { diff --git a/client/src/cmdhf14b.c b/client/src/cmdhf14b.c index 43eeff9fc..189b1f15b 100644 --- a/client/src/cmdhf14b.c +++ b/client/src/cmdhf14b.c @@ -1225,7 +1225,7 @@ static int CmdHF14Binfo(const char *Cmd) { // #define ISO14443B_READ_BLK 0x08 // #define ISO14443B_WRITE_BLK 0x09 -static int read_sr_block(uint8_t blockno, uint8_t *out) { +static int read_sr_block(uint8_t blockno, uint8_t *out, uint16_t out_len) { struct { uint8_t blockno; } PACKED payload; @@ -1240,7 +1240,7 @@ static int read_sr_block(uint8_t blockno, uint8_t *out) { } if (resp.status == PM3_SUCCESS && out) { - memcpy(out, resp.data.asBytes, resp.length); + memcpy(out, resp.data.asBytes, MIN(out_len, resp.length)); } return resp.status; } @@ -1411,7 +1411,7 @@ static bool HF14B_ask_ct_reader(bool verbose) { return false; } -static bool HF14B_picopass_reader(bool verbose) { +bool HF14B_picopass_reader(bool verbose, bool info) { iso14b_raw_cmd_t packet = { .flags = (ISO14B_CONNECT | ISO14B_SELECT_PICOPASS | ISO14B_DISCONNECT), @@ -1437,8 +1437,10 @@ static bool HF14B_picopass_reader(bool verbose) { return false; } memcpy(card, resp.data.asBytes, sizeof(picopass_hdr_t)); - PrintAndLogEx(NORMAL, ""); - PrintAndLogEx(SUCCESS, "iCLASS / Picopass CSN: " _GREEN_("%s"), sprint_hex(card->csn, sizeof(card->csn))); + if (info) { + PrintAndLogEx(NORMAL, ""); + PrintAndLogEx(SUCCESS, "iCLASS / Picopass CSN: " _GREEN_("%s"), sprint_hex(card->csn, sizeof(card->csn))); + } free(card); return true; } @@ -1581,8 +1583,8 @@ static int CmdHF14BSriRdBl(const char *Cmd) { uint8_t blocks = (cardtype == 1) ? 0x7F : 0x0F; */ - uint8_t out[4] = {0}; - int status = read_sr_block(blockno, out); + uint8_t out[ST25TB_SR_BLOCK_SIZE] = {0}; + int status = read_sr_block(blockno, out, sizeof(out)); if (status == PM3_SUCCESS) { PrintAndLogEx(SUCCESS, "block %02u... " _GREEN_("%s") " | " _GREEN_("%s"), blockno, sprint_hex(out, sizeof(out)), sprint_ascii(out, sizeof(out))); } @@ -1626,7 +1628,7 @@ static int CmdHF14BSriWrbl(const char *Cmd) { CLIExecWithReturn(ctx, Cmd, argtable, false); int blockno = arg_get_int_def(ctx, 1, -1); int dlen = 0; - uint8_t data[4] = {0, 0, 0, 0}; + uint8_t data[ST25TB_SR_BLOCK_SIZE] = {0, 0, 0, 0}; int res = CLIParamHexToBuf(arg_get_str(ctx, 2), data, sizeof(data), &dlen); if (res) { CLIParserFree(ctx); @@ -1694,8 +1696,8 @@ static int CmdHF14BSriWrbl(const char *Cmd) { } // verify - uint8_t out[4] = {0}; - status = read_sr_block(blockno, out); + uint8_t out[ST25TB_SR_BLOCK_SIZE] = {0}; + status = read_sr_block(blockno, out, sizeof(out)); if (status == PM3_SUCCESS) { if (memcmp(data, out, 4) == 0) { PrintAndLogEx(SUCCESS, "SRx write block ( " _GREEN_("ok") " )"); @@ -1972,7 +1974,7 @@ static int CmdHF14BRestore(const char *Cmd) { // verify uint8_t out[ST25TB_SR_BLOCK_SIZE] = {0}; - status = read_sr_block(blockno, out); + status = read_sr_block(blockno, out, sizeof(out)); if (status == PM3_SUCCESS) { if (memcmp(data + blockno * ST25TB_SR_BLOCK_SIZE, out, ST25TB_SR_BLOCK_SIZE) == 0) { printf("\33[2K\r"); @@ -3034,6 +3036,7 @@ int infoHF14B(bool verbose, bool do_aid_search) { // get and print general info about all known 14b chips int readHF14B(bool loop, bool verbose, bool read_plot) { bool found = false; + bool info = true; int res = PM3_SUCCESS; do { found = false; @@ -3049,7 +3052,7 @@ int readHF14B(bool loop, bool verbose, bool read_plot) { goto plot; // Picopass - found |= HF14B_picopass_reader(verbose); + found |= HF14B_picopass_reader(verbose, info); if (found) goto plot; diff --git a/client/src/cmdhf14b.h b/client/src/cmdhf14b.h index 009395ba2..067718507 100644 --- a/client/src/cmdhf14b.h +++ b/client/src/cmdhf14b.h @@ -31,4 +31,6 @@ int select_card_14443b_4(bool disconnect, iso14b_card_select_t *card); int infoHF14B(bool verbose, bool do_aid_search); int readHF14B(bool loop, bool verbose, bool read_plot); +bool HF14B_picopass_reader(bool verbose, bool info); + #endif diff --git a/client/src/cmdhffido.c b/client/src/cmdhffido.c index a39b1157d..2032dca95 100644 --- a/client/src/cmdhffido.c +++ b/client/src/cmdhffido.c @@ -202,7 +202,7 @@ static int CmdHFFidoRegister(const char *cmd) { if (cpplain) { memset(cdata, 0x00, 32); - chlen = sizeof(cdata); + chlen = sizeof(cdata) - 1; // CLIGetStrWithReturn does not guarantee string to be null-terminated CLIGetStrWithReturn(ctx, 5, cdata, &chlen); if (chlen > 16) { PrintAndLogEx(ERR, "ERROR: challenge parameter length in ASCII mode must be less than 16 chars instead of: %d", chlen); @@ -226,7 +226,7 @@ static int CmdHFFidoRegister(const char *cmd) { if (applain) { memset(adata, 0x00, 32); - applen = sizeof(adata); + applen = sizeof(adata) - 1; // CLIGetStrWithReturn does not guarantee string to be null-terminated CLIGetStrWithReturn(ctx, 6, adata, &applen); if (applen > 16) { PrintAndLogEx(ERR, "ERROR: application parameter length in ASCII mode must be less than 16 chars instead of: %d", applen); @@ -245,8 +245,9 @@ static int CmdHFFidoRegister(const char *cmd) { return PM3_EINVARG; } } - if (applen) + if (applen) { memmove(&data[32], adata, 32); + } CLIParserFree(ctx); @@ -516,7 +517,7 @@ static int CmdHFFidoAuthenticate(const char *cmd) { if (cpplain) { memset(hdata, 0x00, 32); - hdatalen = sizeof(hdata); + hdatalen = sizeof(hdata) - 1; // CLIGetStrWithReturn does not guarantee string to be null-terminated CLIGetStrWithReturn(ctx, 9, hdata, &hdatalen); if (hdatalen > 16) { PrintAndLogEx(ERR, "ERROR: challenge parameter length in ASCII mode must be less than 16 chars instead of: %d", hdatalen); @@ -542,7 +543,7 @@ static int CmdHFFidoAuthenticate(const char *cmd) { if (applain) { memset(hdata, 0x00, 32); - hdatalen = sizeof(hdata); + hdatalen = sizeof(hdata) - 1; // CLIGetStrWithReturn does not guarantee string to be null-terminated CLIGetStrWithReturn(ctx, 10, hdata, &hdatalen); if (hdatalen > 16) { PrintAndLogEx(ERR, "ERROR: application parameter length in ASCII mode must be less than 16 chars instead of: %d", hdatalen); diff --git a/client/src/cmdhfgallagher.c b/client/src/cmdhfgallagher.c index b83bfd455..be27c72fe 100644 --- a/client/src/cmdhfgallagher.c +++ b/client/src/cmdhfgallagher.c @@ -1311,6 +1311,52 @@ static int CmdGallagherDecode(const char *cmd) { return PM3_SUCCESS; } +static int CmdGallagherEncode(const char *cmd) { + CLIParserContext *ctx; + CLIParserInit(&ctx, "hf gallagher encode", + "Encode a Gallagher credential block\n" + "Credential block can be specified with or without the bitwise inverse.", + "hf gallagher encode --rc 1 --fc 22153 --cn 1253518 --il 1" + ); + + void *argtable[] = { + arg_param_begin, + arg_u64_1("r", "rc", "", "Region code. 4 bits max"), + arg_u64_1("f", "fc", "", "Facility code. 2 bytes max"), + arg_u64_1("c", "cn", "", "Card number. 3 bytes max"), + arg_u64_1("i", "il", "", "Issue level. 4 bits max"), + arg_param_end + }; + CLIExecWithReturn(ctx, cmd, argtable, false); + + uint64_t region_code = arg_get_u64(ctx, 1); // uint4, input will be validated later + uint64_t facility_code = arg_get_u64(ctx, 2); // uint16 + uint64_t card_number = arg_get_u64(ctx, 3); // uint24 + uint64_t issue_level = arg_get_u64(ctx, 4); // uint4 + + CLIParserFree(ctx); + + GallagherCredentials_t creds = { + .region_code = region_code, + .facility_code = facility_code, + .card_number = card_number, + .issue_level = issue_level, + }; + + + uint8_t contents[16] = {0}; + + gallagher_encode_creds(contents, &creds); + for (int i = 0; i < 8; i++) { + contents[i + 8] = contents[i] ^ 0xFF; + } + + PrintAndLogEx(SUCCESS, "Raw: " _YELLOW_("%s"), sprint_hex_inrow(contents, ARRAYLEN(contents) / 2)); + PrintAndLogEx(SUCCESS, "Bitwise: " _YELLOW_("%s"), sprint_hex_inrow(contents, ARRAYLEN(contents))); + + return PM3_SUCCESS; +} + static command_t CommandTable[] = { {"help", CmdHelp, AlwaysAvailable, "This help"}, @@ -1319,6 +1365,7 @@ static command_t CommandTable[] = { {"delete", CmdGallagherDelete, IfPm3Iso14443, "Delete Gallagher credentials from a DESFire card"}, {"diversifykey", CmdGallagherDiversify, AlwaysAvailable, "Diversify Gallagher key"}, {"decode", CmdGallagherDecode, AlwaysAvailable, "Decode Gallagher credential block"}, + {"encode", CmdGallagherEncode, AlwaysAvailable, "Encode Gallagher credential block"}, {NULL, NULL, NULL, NULL} }; diff --git a/client/src/cmdhficlass.c b/client/src/cmdhficlass.c index f891b728c..bb95d9a4b 100644 --- a/client/src/cmdhficlass.c +++ b/client/src/cmdhficlass.c @@ -40,6 +40,7 @@ #include "crypto/asn1utils.h" // ASN1 decoder #include "preferences.h" #include "generator.h" +#include "cmdhf14b.h" #define NUM_CSNS 9 @@ -263,80 +264,62 @@ static uint8_t card_app2_limit[] = { 0xff, }; -static iclass_config_card_item_t iclass_config_types[13] = { - {"Audio/Visual #1 - Beep ON, LED Off, Flash GREEN on read", {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xBF, 0x18, 0xAC, 0x00, 0xA8, 0x8F, 0xA7, 0x80, 0xA9, 0x01}}, - {"Audio/Visual #2 - Beep ON, LED RED, Host must flash GREEN", {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x87, 0x18, 0xAC, 0x00, 0xA8, 0x1F, 0xA7, 0x80, 0xA9, 0x01}}, - {"Audio/Visual #3 - Beep ON, LED Off, Host must flash RED and/or GREEN", {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xBF, 0x18, 0xAC, 0x00, 0xA8, 0x0F, 0xA9, 0x03, 0xA7, 0x80}}, - {"Keypad Output #1 - Buffer ONE key (8 bit Dorado)", {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xBF, 0x18, 0xAE, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}, - {"Keypad Output #2 - Buffer ONE to FIVE keys (standard 26 bit)", {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xBF, 0x18, 0xAE, 0x0B, 0xAF, 0xFF, 0xAD, 0x15, 0xB3, 0x03}}, - {"Keypad Output #3 - Local PIN verify", {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xBF, 0x18, 0xAD, 0x6D, 0xB3, 0x03, 0x00, 0x00, 0x00, 0x00}}, - {"Mifare CSN #1 - 32 bit reverse output", {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xBF, 0x18, 0xAC, 0x01, 0xA7, 0x80, 0xA8, 0x9F, 0xA9, 0x01}}, - {"Mifare CSN #2 - 16 bit output", {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xBF, 0x18, 0xAC, 0x02, 0xA7, 0x80, 0xA8, 0x9F, 0xA9, 0x01}}, - {"Mifare CSN #3 - 34 bit output", {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xBF, 0x18, 0xAC, 0x03, 0xA7, 0x80, 0xA8, 0x9F, 0xA9, 0x01}}, - {"Keyroll DISABLE - Set ELITE Key and DISABLE Keyrolling", {0x0C, 0x00, 0x00, 0x01, 0x00, 0x00, 0xBF, 0x18, 0xBF, 0x01, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}}, - {"Keyroll ENABLE - Set ELITE Key and ENABLE Keyrolling", {0x0C, 0x00, 0x00, 0x01, 0x00, 0x00, 0xBF, 0x18, 0xBF, 0x03, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}}, - {"Reset READER - Reset READER to defaults", {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}, - {"Reset ENROLLER - Reset ENROLLER to defaults", {0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1C, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0xFF, 0xFF, 0xFF}} +static iclass_config_card_item_t iclass_config_options[30] = { + //Byte A8 - LED Operations + {"(LED) - Led idle (Off) / Led read (Off)", {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xBF, 0x18, 0xA8, 0x0F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}, + {"(LED) - Led idle (Red) / Led read (Off)", {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xBF, 0x18, 0xA8, 0x1F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}, + {"(LED) - Led idle (Grn) / Led read (Off)", {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xBF, 0x18, 0xA8, 0x2F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}, + {"(LED) - Led idle (Amber) / Led read (Off)", {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xBF, 0x18, 0xA8, 0x3F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}, + {"(LED) - Led idle (Off) / Led read (Red)", {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xBF, 0x18, 0xA8, 0x4F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}, + {"(LED) - Led idle (Red) / Led read (Red)", {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xBF, 0x18, 0xA8, 0x5F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}, + {"(LED) - Led idle (Grn) / Led read (Red)", {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xBF, 0x18, 0xA8, 0x6F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}, + {"(LED) - Led idle (Amber) / Led read (Red)", {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xBF, 0x18, 0xA8, 0x7F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}, + {"(LED) - Led idle (Off) / Led read (Grn)", {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xBF, 0x18, 0xA8, 0x8F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}, + {"(LED) - Led idle (Red) / Led read (Grn)", {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xBF, 0x18, 0xA8, 0x9F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}, + {"(LED) - Led idle (Grn) / Led read (Grn)", {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xBF, 0x18, 0xA8, 0xAF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}, + {"(LED) - Led idle (Amber) / Led read (Red)", {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xBF, 0x18, 0xA8, 0xBF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}, + {"(LED) - Led idle (Off) / Led read (Amber)", {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xBF, 0x18, 0xA8, 0xCF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}, + {"(LED) - Led idle (Red) / Led read (Amber)", {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xBF, 0x18, 0xA8, 0xDF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}, + {"(LED) - Led idle (Grn) / Led read (Amber)", {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xBF, 0x18, 0xA8, 0xEF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}, + {"(LED) - Led idle (Amber) / Led read (Amber)", {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xBF, 0x18, 0xA8, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}, + //Byte A9 - Potentially associated with led blinking / led heartbeat operations? + //Byte A6 - Potentially associated with beep pitch? + //Byte A7 - BEEP Operations + {"(BEEP) - Beep on Read (On)", {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xBF, 0x18, 0xA7, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}, + {"(BEEP) - Beep on Read (Off)", {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xBF, 0x18, 0xA7, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}, + //Byte AC - MIFARE CSN Operations + {"(MIFARE) - CSN Default Output", {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xBF, 0x18, 0xAC, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}, + {"(MIFARE) - CSN 32 bit Reverse Output", {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xBF, 0x18, 0xAC, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}, + {"(MIFARE) - CSN 16 bit Output", {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xBF, 0x18, 0xAC, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}, + {"(MIFARE) - CSN 34 bit Output", {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xBF, 0x18, 0xAC, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}, + //Bytes AD, AE, AF, B3 - Keypad Operations + not fully mapped + {"(KEYPAD Output) - Buffer ONE key (8 bit Dorado)", {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xBF, 0x18, 0xAE, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}, + {"(KEYPAD Output) - Buffer ONE to FIVE keys (standard 26 bit)", {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xBF, 0x18, 0xAE, 0x0B, 0xAF, 0xFF, 0xAD, 0x15, 0xB3, 0x03}}, + {"(KEYPAD Output) - Local PIN verify", {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xBF, 0x18, 0xAD, 0x6D, 0xB3, 0x03, 0x00, 0x00, 0x00, 0x00}}, + //iClass Elite Key Operations + {"(ELITE Key) - Set ELITE Key and Enable Dual key (Elite + Standard)", {0x0C, 0x00, 0x00, 0x01, 0x00, 0x00, 0xBF, 0x18, 0xBF, 0x01, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}}, + {"(ELITE Key) - Set ELITE Key and ENABLE Keyrolling", {0x0C, 0x00, 0x00, 0x01, 0x00, 0x00, 0xBF, 0x18, 0xBF, 0x03, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}}, + {"(ELITE Key) - Set ELITE Key and DISABLE Standard Key", {0x0C, 0x00, 0x00, 0x01, 0x00, 0x00, 0xBF, 0x18, 0xBF, 0x05, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}}, + //Erroneous / incorrect reader behaviors + //Reset Operations + {"(RESET) - Reset READER to defaults", {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}, + {"(RESET) - Reset ENROLLER to defaults", {0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1C, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0xFF, 0xFF, 0xFF}} + //Reader Master Key Operations }; -static bool check_config_card(const iclass_config_card_item_t *o) { - if (o == NULL || strlen(o->desc) == 0) { - PrintAndLogEx(INFO, "No data available"); - PrintAndLogEx(HINT, "Try `" _YELLOW_("hf iclass config -l") "` to download from cardhelper"); - return false; - } - return true; -} - -static int load_config_cards(void) { - PrintAndLogEx(INFO, "detecting cardhelper..."); - if (IsCardHelperPresent(false) == false) { - PrintAndLogEx(FAILED, "failed to detect cardhelper"); - return PM3_ENODATA; - } - - for (int i = 0; i < ARRAYLEN(iclass_config_types); ++i) { - - PrintAndLogEx(INPLACE, "loading setting %i", i); - iclass_config_card_item_t *ret = &iclass_config_types[i]; - - uint8_t desc[70] = {0}; - if (GetConfigCardStrByIdx(i, desc) == PM3_SUCCESS) { - memcpy(ret->desc, desc, sizeof(desc)); - } - - uint8_t blocks[16] = {0}; - if (GetConfigCardByIdx(i, blocks) == PM3_SUCCESS) { - memcpy(ret->data, blocks, sizeof(blocks)); - } - } - PrintAndLogEx(NORMAL, ""); - PrintAndLogEx(HINT, "Try `" _YELLOW_("hf iclass configcard -p") "` to list all"); - return PM3_SUCCESS; -} - static const iclass_config_card_item_t *get_config_card_item(int idx) { - if (idx > -1 && idx < 14) { - return &iclass_config_types[idx]; + if (idx > -1 && idx < ARRAYLEN(iclass_config_options)) { + return &iclass_config_options[idx]; } - return &iclass_config_types[13]; + return &iclass_config_options[ARRAYLEN(iclass_config_options)]; } static void print_config_cards(void) { - if (check_config_card(&iclass_config_types[0])) { - PrintAndLogEx(INFO, "---- " _CYAN_("Config cards available") " ------------"); - for (int i = 0; i < ARRAYLEN(iclass_config_types) ; ++i) { - PrintAndLogEx(INFO, "%2d, %s", i, iclass_config_types[i].desc); - } - PrintAndLogEx(NORMAL, ""); - } -} - -static void print_config_card(const iclass_config_card_item_t *o) { - if (check_config_card(o)) { - PrintAndLogEx(INFO, "description... " _YELLOW_("%s"), o->desc); - PrintAndLogEx(INFO, "data.......... " _YELLOW_("%s"), sprint_hex_inrow(o->data, sizeof(o->data))); + PrintAndLogEx(INFO, "---- " _CYAN_("Config cards options") " ------------"); + for (int i = 0; i < ARRAYLEN(iclass_config_options) ; ++i) { + PrintAndLogEx(INFO, "%2d, %s", i, iclass_config_options[i].desc); } + PrintAndLogEx(NORMAL, ""); } static void iclass_encrypt_block_data(uint8_t *blk_data, uint8_t *key) { @@ -349,10 +332,7 @@ static void iclass_encrypt_block_data(uint8_t *blk_data, uint8_t *key) { mbedtls_des3_free(&ctx); } -static int generate_config_card(const iclass_config_card_item_t *o, uint8_t *key, bool got_kr, uint8_t *card_key, bool got_krki, bool use_elite) { - if (check_config_card(o) == false) { - return PM3_EINVARG; - } +static int generate_config_card(const iclass_config_card_item_t *o, uint8_t *key, bool got_kr, uint8_t *card_key, bool got_eki, bool use_elite, bool got_mk, uint8_t *master_key) { // generated config card header picopass_hdr_t configcard; @@ -361,7 +341,7 @@ static int generate_config_card(const iclass_config_card_item_t *o, uint8_t *ke memcpy(&configcard.conf, "\xFF\xFF\xFF\xFF\xF9\xFF\xFF\xBC", 8); memcpy(&configcard.epurse, "\xFE\xFF\xFF\xFF\xFF\xFF\xFF\xFF", 8); - if (got_krki) { + if (got_eki) { HFiClassCalcDivKey(configcard.csn, card_key, configcard.key_d, use_elite); } else { // defaulting to AA1 ki 0 @@ -377,7 +357,7 @@ static int generate_config_card(const iclass_config_card_item_t *o, uint8_t *ke if (res == PM3_SUCCESS) { cc = &iclass_last_known_card; // calc diversified key for selected card - if (got_krki) { + if (got_eki) { HFiClassCalcDivKey(cc->csn, card_key, cc->key_d, use_elite); } else { // defaulting to AA1 ki 0 @@ -387,6 +367,7 @@ static int generate_config_card(const iclass_config_card_item_t *o, uint8_t *ke PrintAndLogEx(FAILED, "failed to read a card"); PrintAndLogEx(INFO, "falling back to default config card"); } + PrintAndLogEx(INFO, "Generating "_YELLOW_("%s"), o->desc); // generate dump file uint8_t app1_limit = cc->conf.app_limit; @@ -405,12 +386,31 @@ static int generate_config_card(const iclass_config_card_item_t *o, uint8_t *ke memcpy(data, cc, sizeof(picopass_hdr_t)); print_picopass_header(cc); + // KEYROLL need to encrypt + uint8_t key_en[16] = {0}; + uint8_t *keyptr_en = NULL; + size_t keylen = 0; + int res_key = loadFile_safe(ICLASS_DECRYPTION_BIN, "", (void **)&keyptr_en, &keylen); + if (res_key != PM3_SUCCESS) { + PrintAndLogEx(ERR, "Failed to find iclass_decryptionkey.bin"); + free(data); + return PM3_EINVARG; + } + + if (keylen != 16) { + PrintAndLogEx(ERR, "Failed to load transport key from file"); + free(keyptr_en); + free(data); + return PM3_EINVARG; + } + memcpy(key_en, keyptr_en, sizeof(key_en)); + free(keyptr_en); // Keyrolling configuration cards are special. - if (strstr(o->desc, "Keyroll") != NULL) { + if (strstr(o->desc, "ELITE") != NULL) { if (got_kr == false) { - PrintAndLogEx(ERR, "please specify KEYROLL key!"); + PrintAndLogEx(ERR, "please specify ELITE key!"); free(data); return PM3_EINVARG; } @@ -437,28 +437,6 @@ static int generate_config_card(const iclass_config_card_item_t *o, uint8_t *ke bool old = GetFlushAfterWrite(); SetFlushAfterWrite(true); - // KEYROLL need to encrypt - uint8_t key_en[16] = {0}; - uint8_t *keyptr_en = NULL; - if (IsCardHelperPresent(false) == false) { - size_t keylen = 0; - int res_key = loadFile_safe(ICLASS_DECRYPTION_BIN, "", (void **)&keyptr_en, &keylen); - if (res_key != PM3_SUCCESS) { - PrintAndLogEx(ERR, "Failed to find iclass_decryptionkey.bin"); - free(data); - return PM3_EINVARG; - } - - if (keylen != 16) { - PrintAndLogEx(ERR, "Failed to load transport key from file"); - free(keyptr_en); - free(data); - return PM3_EINVARG; - } - memcpy(key_en, keyptr_en, sizeof(key_en)); - free(keyptr_en); - } - PrintAndLogEx(INFO, "Setting up encryption... " NOLF); uint8_t ffs[8] = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}; if (IsCardHelperPresent(false) != false) { @@ -499,11 +477,15 @@ static int generate_config_card(const iclass_config_card_item_t *o, uint8_t *ke memcpy(data + (0x0D * 8), lkey, sizeof(enckey1)); } // encrypted 0xFF - for (uint8_t i = 0x0E; i < 0x14; i++) { + for (uint8_t i = 0x0E; i < 0x13; i++) { memcpy(data + (i * 8), ffs, sizeof(ffs)); } PrintAndLogEx(NORMAL, "( " _GREEN_("ok") " )"); + //Block 13 (This is needed for Rev.C readers!) + uint8_t block_0x13[PICOPASS_BLOCK_SIZE] = {0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1C}; + memcpy(data + (0x13 * 8), block_0x13, sizeof(block_0x13)); + // encrypted partial keyroll key 14 PrintAndLogEx(INFO, "Setting encrypted partial key14... " NOLF); uint8_t foo[8] = {0x15}; @@ -554,6 +536,15 @@ static int generate_config_card(const iclass_config_card_item_t *o, uint8_t *ke } else { memcpy(data, cc, sizeof(picopass_hdr_t)); memcpy(data + (6 * 8), o->data, sizeof(o->data)); + if (strstr(o->desc, "Custom") != NULL) { + if (got_mk == false) { + PrintAndLogEx(ERR, "please specify New Master Key!"); + free(data); + return PM3_EINVARG; + } + iclass_encrypt_block_data(master_key, key_en); + memcpy(data + (0x07 * 8), master_key, PICOPASS_BLOCK_SIZE); + } } //Send to device @@ -2711,7 +2702,7 @@ static int CmdHFiClassRestore(const char *Cmd) { } static int iclass_read_block_ex(uint8_t *KEY, uint8_t blockno, uint8_t keyType, bool elite, bool rawkey, bool replay, bool verbose, - bool auth, bool shallow_mod, uint8_t *out, bool print) { + bool auth, bool shallow_mod, uint8_t *out, bool print) { iclass_auth_req_t payload = { .use_raw = rawkey, @@ -2761,7 +2752,7 @@ static int iclass_read_block_ex(uint8_t *KEY, uint8_t blockno, uint8_t keyType, } static int iclass_read_block(uint8_t *KEY, uint8_t blockno, uint8_t keyType, bool elite, bool rawkey, bool replay, bool verbose, - bool auth, bool shallow_mod, uint8_t *out) { + bool auth, bool shallow_mod, uint8_t *out) { return iclass_read_block_ex(KEY, blockno, keyType, elite, rawkey, replay, verbose, auth, shallow_mod, out, true); } @@ -3713,10 +3704,10 @@ static int CmdHFiClassCheckKeys(const char *Cmd) { if ((strlen(filename) == 0) && (use_vb6kdf == false)) { if (use_elite) { - PrintAndLogEx(INFO,"Using default elite dictionary"); + PrintAndLogEx(INFO, "Using default elite dictionary"); snprintf(filename, sizeof(filename), ICLASS_DEFAULT_KEY_ELITE_DIC); } else { - PrintAndLogEx(INFO,"Using default dictionary"); + PrintAndLogEx(INFO, "Using default dictionary"); snprintf(filename, sizeof(filename), ICLASS_DEFAULT_KEY_DIC); } } @@ -4758,9 +4749,9 @@ static int CmdHFiClassEncode(const char *Cmd) { }; CLIExecWithReturn(ctx, Cmd, argtable, false); - // TODO: very confusing sizes... buf of 70, parser len to 63 instead of 70-1, tests for len > 127, loop with 64... - uint8_t bin[70] = {0}; - int bin_len = 63; + // can only do one block of 8 bytes currently. There are room for two blocks in the specs. + uint8_t bin[65] = {0}; + int bin_len = sizeof(bin) - 1; // CLIGetStrWithReturn does not guarantee string to be null-terminated CLIGetStrWithReturn(ctx, 1, bin, &bin_len); int key_nr = arg_get_int_def(ctx, 2, -1); @@ -4800,8 +4791,10 @@ static int CmdHFiClassEncode(const char *Cmd) { bool use_sc = false; CLIGetHexWithReturn(ctx, 6, enc_key, &enc_key_len); + // FC / CN / Issue Level wiegand_card_t card; memset(&card, 0, sizeof(wiegand_card_t)); + card.FacilityCode = arg_get_u32_def(ctx, 7, 0); card.CardNumber = arg_get_u32_def(ctx, 8, 0); card.IssueLevel = arg_get_u32_def(ctx, 9, 0); @@ -4829,8 +4822,8 @@ static int CmdHFiClassEncode(const char *Cmd) { have_enc_key = true; } - if (bin_len > 127) { - PrintAndLogEx(ERR, "Binary wiegand string must be less than 128 bits"); + if (bin_len > 64) { + PrintAndLogEx(ERR, "Binary wiegand string must be less than 64 bits"); return PM3_EINVARG; } @@ -5001,20 +4994,17 @@ static int CmdHFiClassConfigCard(const char *Cmd) { "Manage reader configuration card via Cardhelper or internal database,\n" "The generated config card will be uploaded to device emulator memory.\n" "You can start simulating `hf iclass sim -t 3` or use the emul commands", - "hf iclass configcard -l --> download config card settings from cardhelper\n" "hf iclass configcard -p --> print all config cards in the database\n" - "hf iclass configcard --ci 1 --> view config card setting in slot 1\n" - "hf iclass configcard -g --ci 0 --> generate config file from slot 0" + "hf iclass configcard --g 0 --> generate config file with option 0" ); void *argtable[] = { arg_param_begin, - arg_int0(NULL, "ci", "", "use config slot at index"), + arg_int0(NULL, "g", "", "use config option"), arg_int0(NULL, "ki", "", "Card Key - index to select key from memory 'hf iclass managekeys'"), - arg_int0(NULL, "krki", "", "Elite Keyroll Key - index to select key from memory 'hf iclass managekeys'"), + arg_int0(NULL, "eki", "", "Elite Key - index to select key from memory 'hf iclass managekeys'"), + arg_int0(NULL, "mrki", "", "Standard Master Key - index to select key from memory 'hf iclass managekeys'"), arg_lit0(NULL, "elite", "Use elite key for the the Card Key ki"), - arg_lit0("g", NULL, "generate card dump file"), - arg_lit0("l", NULL, "load available cards"), arg_lit0("p", NULL, "print available cards"), arg_param_end }; @@ -5023,21 +5013,20 @@ static int CmdHFiClassConfigCard(const char *Cmd) { int ccidx = arg_get_int_def(ctx, 1, -1); int card_kidx = arg_get_int_def(ctx, 2, -1); int kidx = arg_get_int_def(ctx, 3, -1); - bool elite = arg_get_lit(ctx, 4); - bool do_generate = arg_get_lit(ctx, 5); - bool do_load = arg_get_lit(ctx, 6); - bool do_print = arg_get_lit(ctx, 7); + int midx = arg_get_int_def(ctx, 4, -1); + bool elite = arg_get_lit(ctx, 5); + bool do_print = arg_get_lit(ctx, 6); CLIParserFree(ctx); - bool got_krki = false; + bool got_eki = false; uint8_t card_key[8] = {0}; if (card_kidx >= 0) { if (card_kidx < ICLASS_KEYS_MAX) { - got_krki = true; + got_eki = true; memcpy(card_key, iClass_Key_Table[card_kidx], 8); PrintAndLogEx(SUCCESS, "Using card key[%d] " _GREEN_("%s"), card_kidx, sprint_hex(iClass_Key_Table[card_kidx], 8)); } else { - PrintAndLogEx(ERR, "--krki number is invalid"); + PrintAndLogEx(ERR, "--ki number is invalid"); return PM3_EINVARG; } } @@ -5050,14 +5039,23 @@ static int CmdHFiClassConfigCard(const char *Cmd) { memcpy(keyroll_key, iClass_Key_Table[kidx], 8); PrintAndLogEx(SUCCESS, "Using keyroll key[%d] " _GREEN_("%s"), kidx, sprint_hex(iClass_Key_Table[kidx], 8)); } else { - PrintAndLogEx(ERR, "--ki number is invalid"); + PrintAndLogEx(ERR, "--eki number is invalid"); return PM3_EINVARG; } } - if (do_load) { - if (load_config_cards() != PM3_SUCCESS) { - PrintAndLogEx(INFO, "failed to load, check your cardhelper"); + bool got_mk = false; + uint8_t master_key[8] = {0}; + if (midx >= 0) { + if (midx < ICLASS_KEYS_MAX) { + got_mk = true; + uint8_t key_iclass_format[8] = {0}; + permutekey(iClass_Key_Table[midx], key_iclass_format); + memcpy(master_key, key_iclass_format, 8); + PrintAndLogEx(SUCCESS, "Using key[%d] as new Reader's Master Key" _GREEN_("%s"), midx, sprint_hex(iClass_Key_Table[midx], 8)); + } else { + PrintAndLogEx(ERR, "--mrki number is invalid"); + return PM3_EINVARG; } } @@ -5065,22 +5063,21 @@ static int CmdHFiClassConfigCard(const char *Cmd) { print_config_cards(); } - if (ccidx > -1 && ccidx < ARRAYLEN(iclass_config_types)) { + if (ccidx > -1 && ccidx < ARRAYLEN(iclass_config_options)) { const iclass_config_card_item_t *item = get_config_card_item(ccidx); - print_config_card(item); - } else { - PrintAndLogEx(ERR, "Please specify a valid configuration number!"); - } - - if (do_generate && (ccidx > -1 && ccidx < ARRAYLEN(iclass_config_types))) { - const iclass_config_card_item_t *item = get_config_card_item(ccidx); - if (strstr(item->desc, "Keyroll") != NULL) { - if (got_kr == false) { - PrintAndLogEx(ERR, "please specify KEYROLL key!"); - return PM3_EINVARG; - } + if (strstr(item->desc, "ELITE") != NULL && got_kr == false) { + PrintAndLogEx(ERR, "please specify ELITE Key (--eki) !"); + return PM3_EINVARG; } - generate_config_card(item, keyroll_key, got_kr, card_key, got_krki, elite); + if (strstr(item->desc, "Custom") != NULL && got_mk == false) { + PrintAndLogEx(ERR, "please specify New Standard Master Key (--mrki) !"); + return PM3_EINVARG; + } + if (strstr(item->desc, "Restore") != NULL && card_kidx == -1) { + PrintAndLogEx(ERR, "please specify the Current Reader's Key (--ki) !"); + return PM3_EINVARG; + } + generate_config_card(item, keyroll_key, got_kr, card_key, got_eki, elite, got_mk, master_key); } return PM3_SUCCESS; @@ -5234,7 +5231,7 @@ static command_t CommandTable[] = { {"esetblk", CmdHFiClassESetBlk, IfPm3Iclass, "Set emulator memory block data"}, {"eview", CmdHFiClassEView, IfPm3Iclass, "View emulator memory"}, {"-----------", CmdHelp, AlwaysAvailable, "---------------------- " _CYAN_("Utils") " ----------------------"}, - {"configcard", CmdHFiClassConfigCard, IfPm3Iclass, "Reader configuration card"}, + {"configcard", CmdHFiClassConfigCard, IfPm3Iclass, "Reader configuration card generator"}, {"calcnewkey", CmdHFiClassCalcNewKey, AlwaysAvailable, "Calc diversified keys (blocks 3 & 4) to write new keys"}, {"encode", CmdHFiClassEncode, AlwaysAvailable, "Encode binary wiegand to block 7"}, {"encrypt", CmdHFiClassEncryptBlk, AlwaysAvailable, "Encrypt given block data"}, @@ -5294,7 +5291,7 @@ int info_iclass(bool shallow_mod) { iclass_card_select_resp_t *r = (iclass_card_select_resp_t *)resp.data.asBytes; - uint8_t *p_response = (uint8_t*)&r->header.hdr; + uint8_t *p_response = (uint8_t *)&r->header.hdr; // no tag found or button pressed if (r->status == FLAG_ICLASS_NULL || resp.status == PM3_ERFTRANS) { return PM3_EOPABORTED; @@ -5379,6 +5376,11 @@ int info_iclass(bool shallow_mod) { uint8_t cardtype = get_mem_config(hdr); PrintAndLogEx(SUCCESS, " Card type.... " _GREEN_("%s"), card_types[cardtype]); + if (HF14B_picopass_reader(false, false)) { + PrintAndLogEx(SUCCESS, " Card chip.... "_YELLOW_("Old Silicon (14b support)")); + } else { + PrintAndLogEx(SUCCESS, " Card chip.... "_YELLOW_("NEW Silicon (No 14b support)")); + } if (legacy) { int res = PM3_ESOFT; diff --git a/client/src/cmdhfict.c b/client/src/cmdhfict.c index 52e2f9114..4432532c5 100644 --- a/client/src/cmdhfict.c +++ b/client/src/cmdhfict.c @@ -525,7 +525,7 @@ static int CmdHfIctCredential(const char *Cmd) { } // diversified key A? - int res = mfReadSector(ICT_MIFARE_SECTOR, MF_KEY_A, ICT_MIFARE_A_KEY, data); + int res = mf_read_sector(ICT_MIFARE_SECTOR, MF_KEY_A, ICT_MIFARE_A_KEY, data); if (res != PM3_SUCCESS) { free(data); return res; diff --git a/client/src/cmdhfjooki.c b/client/src/cmdhfjooki.c index 4e6cea649..b0cc1363e 100644 --- a/client/src/cmdhfjooki.c +++ b/client/src/cmdhfjooki.c @@ -531,7 +531,7 @@ static int CmdHF14AJookiSim(const char *Cmd) { g_conn.block_after_ACK = true; uint8_t blockwidth = 4, counter = 0, blockno = 0; - // 12 is the size of the struct the fct mfEmlSetMem_xt uses to transfer to device + // 12 is the size of the struct the fct mf_eml_set_mem_xt uses to transfer to device uint16_t max_avail_blocks = ((PM3_CMD_DATA_SIZE - 12) / blockwidth) * blockwidth; while (datalen) { @@ -542,7 +542,7 @@ static int CmdHF14AJookiSim(const char *Cmd) { uint16_t chunk_size = MIN(max_avail_blocks, datalen); uint16_t blocks_to_send = chunk_size / blockwidth; - if (mfEmlSetMem_xt(data + counter, blockno, blocks_to_send, blockwidth) != PM3_SUCCESS) { + if (mf_eml_set_mem_xt(data + counter, blockno, blocks_to_send, blockwidth) != PM3_SUCCESS) { PrintAndLogEx(FAILED, "Cant set emul block: %3d", blockno); free(data); return PM3_ESOFT; @@ -565,7 +565,8 @@ static int CmdHF14AJookiSim(const char *Cmd) { // NTAG, 7 byte UID in eloaded data. payload.tagtype = 7; - payload.flags = FLAG_UID_IN_EMUL; + payload.flags = 0; + FLAG_SET_UID_IN_EMUL(payload.flags); payload.exitAfter = 0; memcpy(payload.uid, uid, sizeof(uid)); diff --git a/client/src/cmdhflist.c b/client/src/cmdhflist.c index aa83bc20b..f9016a29a 100644 --- a/client/src/cmdhflist.c +++ b/client/src/cmdhflist.c @@ -131,7 +131,7 @@ uint8_t iclass_CRC_check(bool isResponse, uint8_t *d, uint8_t n) { //Commands to tag //Don't include the command byte - if (!isResponse) { + if (isResponse == false) { /** These commands should have CRC. Total length leftmost 4 READ @@ -167,7 +167,7 @@ uint8_t iclass_CRC_check(bool isResponse, uint8_t *d, uint8_t n) { In conclusion, without looking at the command; any response of length 10 or 34 should have CRC **/ - if (n != 10 && n != 34) return true; + if (n != 10 && n != 34) return 2; return check_crc(CRC_ICLASS, d, n); } diff --git a/client/src/cmdhfmf.c b/client/src/cmdhfmf.c index 10c4efc00..c93093dea 100644 --- a/client/src/cmdhfmf.c +++ b/client/src/cmdhfmf.c @@ -45,6 +45,7 @@ #include "mifare/gen4.h" #include "generator.h" // keygens. #include "fpga.h" +#include "mifare/mifarehost.h" static int CmdHelp(const char *Cmd); @@ -494,22 +495,6 @@ void mf_print_sector_hdr(uint8_t sector) { PrintAndLogEx(INFO, "----+-------------------------------------------------+-----------------"); } -static bool mf_write_block(const uint8_t *key, uint8_t keytype, uint8_t blockno, uint8_t *block) { - - uint8_t data[26]; - memcpy(data, key, MIFARE_KEY_SIZE); - memcpy(data + 10, block, MFBLOCK_SIZE); - - clearCommandBuffer(); - SendCommandMIX(CMD_HF_MIFARE_WRITEBL, blockno, keytype, 0, data, sizeof(data)); - PacketResponseNG resp; - if (WaitForResponseTimeout(CMD_ACK, &resp, 1500) == false) { - PrintAndLogEx(FAILED, "command execution time out"); - return false; - } - - return ((resp.oldarg[0] & 0xff) == 1); -} // assumes n is in number of blocks 0..255 static void mf_analyse_acl(uint16_t n, uint8_t *d) { @@ -898,29 +883,34 @@ static int CmdHF14AMfDarkside(const char *Cmd) { arg_param_begin, arg_int0(NULL, "blk", " ", "Target block"), arg_lit0("b", NULL, "Target key B instead of default key A"), - arg_int0("c", NULL, "", "Target Auth 6x"), + arg_int0("c", NULL, "", "Target key type is key A + offset"), arg_param_end }; CLIExecWithReturn(ctx, Cmd, argtable, true); uint8_t blockno = arg_get_u32_def(ctx, 1, 0) & 0xFF; - uint8_t key_type = MIFARE_AUTH_KEYA; + uint8_t key_type = MF_KEY_A; if (arg_get_lit(ctx, 2)) { PrintAndLogEx(INFO, "Targeting key B"); - key_type = MIFARE_AUTH_KEYB; + key_type = MF_KEY_B; } - uint8_t ctype = arg_get_u32_def(ctx, 3, 0) & 0xFF; - if ((ctype & 0x60) == 0x60) { - key_type = ctype; + uint8_t prev_keytype = key_type; + key_type = arg_get_int_def(ctx, 3, key_type); + if (arg_get_lit(ctx, 2) && (key_type != prev_keytype)) { + CLIParserFree(ctx); + PrintAndLogEx(WARNING, "Choose one single target key type"); + return PM3_EINVARG; } + // mf_dark_side expects the full command byte 0x6x + key_type += MIFARE_AUTH_KEYA; CLIParserFree(ctx); uint64_t key = 0; uint64_t t1 = msclock(); - int ret = mfDarkside(blockno, key_type, &key); + int ret = mf_dark_side(blockno, key_type, &key); t1 = msclock() - t1; if (ret != PM3_SUCCESS) return ret; @@ -1017,7 +1007,12 @@ static int CmdHF14AMfWrBl(const char *Cmd) { return PM3_EINVARG; } - PrintAndLogEx(INFO, "Writing block no %d, key %c - %s", blockno, (keytype == MF_KEY_B) ? 'B' : 'A', sprint_hex_inrow(key, sizeof(key))); + if (keytype < 2) { + PrintAndLogEx(INFO, "Writing block no %d, key type:%c - %s", blockno, (keytype == MF_KEY_B) ? 'B' : 'A', sprint_hex_inrow(key, sizeof(key))); + } else { + PrintAndLogEx(INFO, "Writing block no %d, key type:%02x - %s", blockno, MIFARE_AUTH_KEYA + keytype, sprint_hex_inrow(key, sizeof(key))); + } + PrintAndLogEx(INFO, "data: %s", sprint_hex(block, sizeof(block))); uint8_t data[26]; @@ -1101,7 +1096,7 @@ static int CmdHF14AMfRdBl(const char *Cmd) { uint8_t blockno = (uint8_t)b; uint8_t data[16] = {0}; - int res = mfReadBlock(blockno, keytype, key, data); + int res = mf_read_block(blockno, keytype, key, data); if (res == PM3_SUCCESS) { uint8_t sector = mfSectorNum(blockno); @@ -1177,7 +1172,7 @@ static int CmdHF14AMfRdSc(const char *Cmd) { return PM3_EMALLOC; } - int res = mfReadSector(sector, keytype, key, data); + int res = mf_read_sector(sector, keytype, key, data); if (res == PM3_SUCCESS) { uint8_t blocks = mfNumBlocksPerSector(sector); @@ -1782,7 +1777,7 @@ static int CmdHF14AMfNested(const char *Cmd) { //TODO: single mode broken? can't } // check if we can authenticate to sector - if (mfCheckKeys(blockNo, keyType, true, 1, key, &key64) != PM3_SUCCESS) { + if (mf_check_keys(blockNo, keyType, true, 1, key, &key64) != PM3_SUCCESS) { if (keyType < 2) { PrintAndLogEx(WARNING, "Wrong key. Can't authenticate to block:%3d key type:%c", blockNo, keyType ? 'B' : 'A'); } else { @@ -1792,7 +1787,7 @@ static int CmdHF14AMfNested(const char *Cmd) { //TODO: single mode broken? can't } if (singleSector) { - int16_t isOK = mfnested(blockNo, keyType, key, trgBlockNo, trgKeyType, keyBlock, !ignore_static_encrypted); + int16_t isOK = mf_nested(blockNo, keyType, key, trgBlockNo, trgKeyType, keyBlock, !ignore_static_encrypted); switch (isOK) { case PM3_ETIMEOUT: PrintAndLogEx(ERR, "command execution time out\n"); @@ -1821,14 +1816,14 @@ static int CmdHF14AMfNested(const char *Cmd) { //TODO: single mode broken? can't } else { // 16 block sector sectortrailer = trgBlockNo | 0x0f; } - mfEmlGetMem(keyBlock, sectortrailer, 1); + mf_eml_get_mem(keyBlock, sectortrailer, 1); if (trgKeyType == MF_KEY_A) num_to_bytes(key64, 6, keyBlock); else num_to_bytes(key64, 6, &keyBlock[10]); - mfEmlSetMem(keyBlock, sectortrailer, 1); + mf_elm_set_mem(keyBlock, sectortrailer, 1); PrintAndLogEx(SUCCESS, "Key transferred to emulator memory."); } return PM3_SUCCESS; @@ -1856,7 +1851,7 @@ static int CmdHF14AMfNested(const char *Cmd) { //TODO: single mode broken? can't } PrintAndLogEx(SUCCESS, "Testing known keys. Sector count "_YELLOW_("%d"), SectorsCnt); - int res = mfCheckKeys_fast(SectorsCnt, true, true, 1, ARRAYLEN(g_mifare_default_keys) + 1, keyBlock, e_sector, use_flashmemory, false); + int res = mf_check_keys_fast(SectorsCnt, true, true, 1, ARRAYLEN(g_mifare_default_keys) + 1, keyBlock, e_sector, use_flashmemory, false); if (res == PM3_SUCCESS) { PrintAndLogEx(SUCCESS, "Fast check found all keys"); goto jumptoend; @@ -1875,7 +1870,7 @@ static int CmdHF14AMfNested(const char *Cmd) { //TODO: single mode broken? can't if (e_sector[sectorNo].foundKey[trgKeyType]) continue; - int16_t isOK = mfnested(blockNo, keyType, key, mfFirstBlockOfSector(sectorNo), trgKeyType, keyBlock, calibrate); + int16_t isOK = mf_nested(blockNo, keyType, key, mfFirstBlockOfSector(sectorNo), trgKeyType, keyBlock, calibrate); switch (isOK) { case PM3_ETIMEOUT: PrintAndLogEx(ERR, "command execution time out\n"); @@ -1898,7 +1893,7 @@ static int CmdHF14AMfNested(const char *Cmd) { //TODO: single mode broken? can't e_sector[sectorNo].foundKey[trgKeyType] = 1; e_sector[sectorNo].Key[trgKeyType] = bytes_to_num(keyBlock, 6); - mfCheckKeys_fast(SectorsCnt, true, true, 2, 1, keyBlock, e_sector, false, false); + mf_check_keys_fast(SectorsCnt, true, true, 2, 1, keyBlock, e_sector, false, false); continue; default : PrintAndLogEx(ERR, "Unknown error\n"); @@ -1960,7 +1955,7 @@ jumptoend: // fast push mode g_conn.block_after_ACK = true; for (int i = 0; i < SectorsCnt; i++) { - mfEmlGetMem(keyBlock, mfFirstBlockOfSector(i) + mfNumBlocksPerSector(i) - 1, 1); + mf_eml_get_mem(keyBlock, mfFirstBlockOfSector(i) + mfNumBlocksPerSector(i) - 1, 1); if (e_sector[i].foundKey[0]) num_to_bytes(e_sector[i].Key[0], 6, keyBlock); @@ -1972,7 +1967,7 @@ jumptoend: // Disable fast mode on last packet g_conn.block_after_ACK = false; } - mfEmlSetMem(keyBlock, mfFirstBlockOfSector(i) + mfNumBlocksPerSector(i) - 1, 1); + mf_elm_set_mem(keyBlock, mfFirstBlockOfSector(i) + mfNumBlocksPerSector(i) - 1, 1); } PrintAndLogEx(SUCCESS, "keys transferred to emulator memory."); } @@ -2084,7 +2079,7 @@ static int CmdHF14AMfNestedStatic(const char *Cmd) { } // check if we can authenticate to sector - if (mfCheckKeys(blockNo, keyType, true, 1, key, &key64) != PM3_SUCCESS) { + if (mf_check_keys(blockNo, keyType, true, 1, key, &key64) != PM3_SUCCESS) { if (keyType < 2) { PrintAndLogEx(WARNING, "Wrong key. Can't authenticate to block:%3d key type:%c", blockNo, keyType ? 'B' : 'A'); } else { @@ -2116,7 +2111,7 @@ static int CmdHF14AMfNestedStatic(const char *Cmd) { } PrintAndLogEx(SUCCESS, "Testing known keys. Sector count "_YELLOW_("%d"), SectorsCnt); - int res = mfCheckKeys_fast(SectorsCnt, true, true, 1, ARRAYLEN(g_mifare_default_keys) + 1, keyBlock, e_sector, false, false); + int res = mf_check_keys_fast(SectorsCnt, true, true, 1, ARRAYLEN(g_mifare_default_keys) + 1, keyBlock, e_sector, false, false); if (res == PM3_SUCCESS) { // all keys found PrintAndLogEx(SUCCESS, "Fast check found all keys"); @@ -2135,7 +2130,7 @@ static int CmdHF14AMfNestedStatic(const char *Cmd) { if (e_sector[sectorNo].foundKey[trgKeyType]) continue; - int16_t isOK = mfStaticNested(blockNo, keyType, key, mfFirstBlockOfSector(sectorNo), trgKeyType, keyBlock); + int16_t isOK = mf_static_nested(blockNo, keyType, key, mfFirstBlockOfSector(sectorNo), trgKeyType, keyBlock); switch (isOK) { case PM3_ETIMEOUT : PrintAndLogEx(ERR, "command execution time out"); @@ -2213,7 +2208,7 @@ jumptoend: // fast push mode g_conn.block_after_ACK = true; for (int i = 0; i < SectorsCnt; i++) { - mfEmlGetMem(keyBlock, mfFirstBlockOfSector(i) + mfNumBlocksPerSector(i) - 1, 1); + mf_eml_get_mem(keyBlock, mfFirstBlockOfSector(i) + mfNumBlocksPerSector(i) - 1, 1); if (e_sector[i].foundKey[0]) num_to_bytes(e_sector[i].Key[0], 6, keyBlock); @@ -2225,7 +2220,7 @@ jumptoend: // Disable fast mode on last packet g_conn.block_after_ACK = false; } - mfEmlSetMem(keyBlock, mfFirstBlockOfSector(i) + mfNumBlocksPerSector(i) - 1, 1); + mf_elm_set_mem(keyBlock, mfFirstBlockOfSector(i) + mfNumBlocksPerSector(i) - 1, 1); } PrintAndLogEx(SUCCESS, "keys transferred to emulator memory."); } @@ -2432,7 +2427,7 @@ static int CmdHF14AMfNestedHard(const char *Cmd) { uint64_t key64 = 0; // check if we can authenticate to sector - if (mfCheckKeys(blockno, keytype, true, 1, key, &key64) != PM3_SUCCESS) { + if (mf_check_keys(blockno, keytype, true, 1, key, &key64) != PM3_SUCCESS) { if (keytype < 2) { PrintAndLogEx(WARNING, "Wrong key. Can't authenticate to block:%3d key type:%c", blockno, keytype ? 'B' : 'A'); } else { @@ -2806,7 +2801,7 @@ static int CmdHF14AMfAutoPWN(const char *Cmd) { PrintAndLogEx(NORMAL, "." NOLF); fflush(stdout); - if (mfCheckKeys(mfFirstBlockOfSector(i), j, true, 1, (keyBlock + (MIFARE_KEY_SIZE * k)), &key64) == PM3_SUCCESS) { + if (mf_check_keys(mfFirstBlockOfSector(i), j, true, 1, (keyBlock + (MIFARE_KEY_SIZE * k)), &key64) == PM3_SUCCESS) { e_sector[i].Key[j] = bytes_to_num((keyBlock + (MIFARE_KEY_SIZE * k)), MIFARE_KEY_SIZE); e_sector[i].foundKey[j] = 'D'; break; @@ -2838,7 +2833,7 @@ static int CmdHF14AMfAutoPWN(const char *Cmd) { lastChunk = true; } - res = mfCheckKeys_fast(sector_cnt, firstChunk, lastChunk, strategy, size, keyBlock + (i * MIFARE_KEY_SIZE), e_sector, false, verbose); + res = mf_check_keys_fast(sector_cnt, firstChunk, lastChunk, strategy, size, keyBlock + (i * MIFARE_KEY_SIZE), e_sector, false, verbose); if (firstChunk) { firstChunk = false; } @@ -2903,7 +2898,7 @@ static int CmdHF14AMfAutoPWN(const char *Cmd) { PrintAndLogEx(NORMAL, ""); - isOK = mfDarkside(mfFirstBlockOfSector(sectorno), MIFARE_AUTH_KEYA + keytype, &key64); + isOK = mf_dark_side(mfFirstBlockOfSector(sectorno), MIFARE_AUTH_KEYA + keytype, &key64); if (isOK != PM3_SUCCESS) goto noValidKeyFound; @@ -2957,7 +2952,7 @@ noValidKeyFound: continue; // Check if the key works - if (mfCheckKeys(mfFirstBlockOfSector(i), j, true, 1, tmp_key, &key64) == PM3_SUCCESS) { + if (mf_check_keys(mfFirstBlockOfSector(i), j, true, 1, tmp_key, &key64) == PM3_SUCCESS) { e_sector[i].Key[j] = bytes_to_num(tmp_key, MIFARE_KEY_SIZE); e_sector[i].foundKey[j] = 'R'; PrintAndLogEx(SUCCESS, "target sector %3u key type %c -- found valid key [ " _GREEN_("%s") " ]", @@ -3036,7 +3031,7 @@ skipReadBKey: (current_key_type_i == MF_KEY_B) ? 'B' : 'A'); } tryNested: - isOK = mfnested(mfFirstBlockOfSector(sectorno), keytype, key, mfFirstBlockOfSector(current_sector_i), current_key_type_i, tmp_key, calibrate); + isOK = mf_nested(mfFirstBlockOfSector(sectorno), keytype, key, mfFirstBlockOfSector(current_sector_i), current_key_type_i, tmp_key, calibrate); switch (isOK) { case PM3_ETIMEOUT: { @@ -3162,7 +3157,7 @@ tryStaticnested: (current_key_type_i == MF_KEY_B) ? 'B' : 'A'); } - isOK = mfStaticNested(mfFirstBlockOfSector(sectorno), keytype, key, mfFirstBlockOfSector(current_sector_i), current_key_type_i, tmp_key); + isOK = mf_static_nested(mfFirstBlockOfSector(sectorno), keytype, key, mfFirstBlockOfSector(current_sector_i), current_key_type_i, tmp_key); DropField(); switch (isOK) { case PM3_ETIMEOUT: { @@ -3226,13 +3221,13 @@ all_found: bool transfer_status = true; for (current_sector_i = 0; current_sector_i < sector_cnt; current_sector_i++) { - mfEmlGetMem(block, current_sector_i, 1); + mf_eml_get_mem(block, current_sector_i, 1); if (e_sector[current_sector_i].foundKey[0]) num_to_bytes(e_sector[current_sector_i].Key[0], MIFARE_KEY_SIZE, block); if (e_sector[current_sector_i].foundKey[1]) num_to_bytes(e_sector[current_sector_i].Key[1], MIFARE_KEY_SIZE, block + 10); - transfer_status |= mfEmlSetMem(block, mfFirstBlockOfSector(current_sector_i) + mfNumBlocksPerSector(current_sector_i) - 1, 1); + transfer_status |= mf_elm_set_mem(block, mfFirstBlockOfSector(current_sector_i) + mfNumBlocksPerSector(current_sector_i) - 1, 1); } PrintAndLogEx(NORMAL, "( %s )", (transfer_status) ? _GREEN_("ok") : _RED_("fail")); @@ -3402,7 +3397,7 @@ static int CmdHF14AMfChk_fast(const char *Cmd) { } if (use_flashmemory) { PrintAndLogEx(SUCCESS, "Using dictionary in flash memory"); - mfCheckKeys_fast_ex(sectorsCnt, true, true, 1, 0, keyBlock, e_sector, use_flashmemory, false, false, singleSectorParams); + mf_check_keys_fast_ex(sectorsCnt, true, true, 1, 0, keyBlock, e_sector, use_flashmemory, false, false, singleSectorParams); } else { // strategies. 1= deep first on sector 0 AB, 2= width first on all sectors @@ -3431,7 +3426,7 @@ static int CmdHF14AMfChk_fast(const char *Cmd) { if (size == keycnt - i) lastChunk = true; - int res = mfCheckKeys_fast_ex(sectorsCnt, firstChunk, lastChunk, strategy, size, keyBlock + (i * MIFARE_KEY_SIZE), e_sector, false, false, true, singleSectorParams); + int res = mf_check_keys_fast_ex(sectorsCnt, firstChunk, lastChunk, strategy, size, keyBlock + (i * MIFARE_KEY_SIZE), e_sector, false, false, true, singleSectorParams); if (firstChunk) firstChunk = false; @@ -3489,7 +3484,7 @@ out: uint8_t block[MFBLOCK_SIZE] = {0x00}; for (i = 0; i < sectorsCnt; ++i) { uint8_t b = mfFirstBlockOfSector(i) + mfNumBlocksPerSector(i) - 1; - mfEmlGetMem(block, b, 1); + mf_eml_get_mem(block, b, 1); if (e_sector[i].foundKey[0]) num_to_bytes(e_sector[i].Key[0], MIFARE_KEY_SIZE, block); @@ -3501,7 +3496,7 @@ out: // Disable fast mode on last packet g_conn.block_after_ACK = false; } - mfEmlSetMem(block, b, 1); + mf_elm_set_mem(block, b, 1); } PrintAndLogEx(SUCCESS, "Found keys have been transferred to the emulator memory"); @@ -3659,7 +3654,7 @@ static int CmdHF14AMfSmartBrute(const char *Cmd) { } int strategy = 2; // width first on all sectors - ret = mfCheckKeys_fast(sectorsCnt, firstChunk, lastChunk, strategy, keycnt, keyBlock, e_sector, false, false); + ret = mf_check_keys_fast(sectorsCnt, firstChunk, lastChunk, strategy, keycnt, keyBlock, e_sector, false, false); keys_checked += keycnt; total_keys_checked += keycnt; @@ -3703,7 +3698,7 @@ out: uint8_t block[MFBLOCK_SIZE] = {0x00}; for (i = 0; i < sectorsCnt; ++i) { uint8_t b = mfFirstBlockOfSector(i) + mfNumBlocksPerSector(i) - 1; - mfEmlGetMem(block, b, 1); + mf_eml_get_mem(block, b, 1); if (e_sector[i].foundKey[0]) num_to_bytes(e_sector[i].Key[0], MIFARE_KEY_SIZE, block); @@ -3715,7 +3710,7 @@ out: // Disable fast mode on last packet g_conn.block_after_ACK = false; } - mfEmlSetMem(block, b, 1); + mf_elm_set_mem(block, b, 1); } PrintAndLogEx(SUCCESS, "Found keys have been transferred to the emulator memory"); @@ -3910,7 +3905,7 @@ static int CmdHF14AMfChk(const char *Cmd) { uint32_t size = keycnt - c > max_keys ? max_keys : keycnt - c; - if (mfCheckKeys(b, trgKeyType, clearLog, size, &keyBlock[MIFARE_KEY_SIZE * c], &key64) == PM3_SUCCESS) { + if (mf_check_keys(b, trgKeyType, clearLog, size, &keyBlock[MIFARE_KEY_SIZE * c], &key64) == PM3_SUCCESS) { e_sector[i].Key[trgKeyType] = key64; e_sector[i].foundKey[trgKeyType] = true; clearLog = false; @@ -3987,7 +3982,7 @@ out: uint8_t block[MFBLOCK_SIZE] = {0x00}; for (int i = 0; i < sectors_cnt; ++i) { uint8_t blockno = mfFirstBlockOfSector(i) + mfNumBlocksPerSector(i) - 1; - mfEmlGetMem(block, blockno, 1); + mf_eml_get_mem(block, blockno, 1); if (e_sector[i].foundKey[0]) num_to_bytes(e_sector[i].Key[0], MIFARE_KEY_SIZE, block); @@ -3999,7 +3994,7 @@ out: // Disable fast mode on last packet g_conn.block_after_ACK = false; } - mfEmlSetMem(block, blockno, 1); + mf_elm_set_mem(block, blockno, 1); } PrintAndLogEx(SUCCESS, "Found keys have been transferred to the emulator memory"); } @@ -4066,7 +4061,7 @@ void readerAttack(sector_t *k_sector, size_t k_sectors_cnt, nonces_t data, bool //set emulator memory for keys if (setEmulatorMem) { uint8_t memBlock[16]; - mfEmlGetMem(memBlock, (sector * 4) + 3, 1); + mf_eml_get_mem(memBlock, (sector * 4) + 3, 1); if ((memBlock[6] == 0) && (memBlock[7] == 0) && (memBlock[8] == 0)) { // ACL not yet set? memBlock[6] = 0xFF; @@ -4079,7 +4074,7 @@ void readerAttack(sector_t *k_sector, size_t k_sectors_cnt, nonces_t data, bool , (sector * 4) + 3 , sprint_hex(memBlock, sizeof(memBlock)) ); - mfEmlSetMem(memBlock, (sector * 4) + 3, 1); + mf_elm_set_mem(memBlock, (sector * 4) + 3, 1); } } @@ -4117,8 +4112,10 @@ static int CmdHF14AMfSim(const char *Cmd) { arg_lit0("x", NULL, "Performs the 'reader attack', nr/ar attack against a reader."), arg_lit0("y", NULL, "Performs the nested 'reader attack'. This requires preloading nt & nt_enc in emulator memory. Implies -x."), arg_lit0("e", "emukeys", "Fill simulator keys from found keys. Requires -x or -y. Implies -i. Simulation will restart automatically."), - arg_lit0("v", "verbose", "verbose output"), - arg_lit0(NULL, "cve", "trigger CVE 2021_0430"), + // If access bits show that key B is Readable, any subsequent memory access should be refused. + arg_lit0(NULL, "allowkeyb", "Allow key B even if readable"), + arg_lit0("v", "verbose", "Verbose output"), + arg_lit0(NULL, "cve", "Trigger CVE 2021_0430"), arg_param_end }; CLIExecWithReturn(ctx, Cmd, argtable, true); @@ -4129,25 +4126,12 @@ static int CmdHF14AMfSim(const char *Cmd) { uint8_t uid[10] = {0}; CLIGetHexWithReturn(ctx, 1, uid, &uidlen); - char uidsize[8] = {0}; if (uidlen > 0) { - switch (uidlen) { - case 10: - flags |= FLAG_10B_UID_IN_DATA; - snprintf(uidsize, sizeof(uidsize), "10 byte"); - break; - case 7: - flags |= FLAG_7B_UID_IN_DATA; - snprintf(uidsize, sizeof(uidsize), "7 byte"); - break; - case 4: - flags |= FLAG_4B_UID_IN_DATA; - snprintf(uidsize, sizeof(uidsize), "4 byte"); - break; - default: - PrintAndLogEx(WARNING, "Invalid parameter for UID"); - CLIParserFree(ctx); - return PM3_EINVARG; + FLAG_SET_UID_IN_DATA(flags, uidlen); + if (IS_FLAG_UID_IN_EMUL(flags)) { + PrintAndLogEx(WARNING, "Invalid parameter for UID"); + CLIParserFree(ctx); + return PM3_EINVARG; } } @@ -4180,9 +4164,13 @@ static int CmdHF14AMfSim(const char *Cmd) { bool setEmulatorMem = arg_get_lit(ctx, 12); - bool verbose = arg_get_lit(ctx, 13); + if (arg_get_lit(ctx, 13)) { + flags |= FLAG_MF_USE_READ_KEYB; + } - if (arg_get_lit(ctx, 14)) { + bool verbose = arg_get_lit(ctx, 14); + + if (arg_get_lit(ctx, 15)) { flags |= FLAG_CVE21_0430; } CLIParserFree(ctx); @@ -4193,7 +4181,7 @@ static int CmdHF14AMfSim(const char *Cmd) { PrintAndLogEx(WARNING, "Wrong ATQA length"); return PM3_EINVARG; } - flags |= FLAG_FORCED_ATQA; + flags |= FLAG_ATQA_IN_DATA; } if (saklen > 0) { @@ -4201,12 +4189,7 @@ static int CmdHF14AMfSim(const char *Cmd) { PrintAndLogEx(WARNING, "Wrong SAK length"); return PM3_EINVARG; } - flags |= FLAG_FORCED_SAK; - } - - // Use UID, SAK, ATQA from EMUL, if uid not defined - if ((flags & (FLAG_4B_UID_IN_DATA | FLAG_7B_UID_IN_DATA | FLAG_10B_UID_IN_DATA)) == 0) { - flags |= FLAG_UID_IN_EMUL; + flags |= FLAG_SAK_IN_DATA; } size_t k_sectors_cnt = MIFARE_4K_MAXSECTOR; @@ -4218,19 +4201,19 @@ static int CmdHF14AMfSim(const char *Cmd) { } if (m0) { - flags |= FLAG_MF_MINI; + FLAG_SET_MF_SIZE(flags, MIFARE_MINI_MAX_BYTES); snprintf(csize, sizeof(csize), "MINI"); k_sectors_cnt = MIFARE_MINI_MAXSECTOR; } else if (m1) { - flags |= FLAG_MF_1K; + FLAG_SET_MF_SIZE(flags, MIFARE_1K_MAX_BYTES); snprintf(csize, sizeof(csize), "1K"); k_sectors_cnt = MIFARE_1K_MAXSECTOR; } else if (m2) { - flags |= FLAG_MF_2K; + FLAG_SET_MF_SIZE(flags, MIFARE_2K_MAX_BYTES); snprintf(csize, sizeof(csize), "2K with RATS"); k_sectors_cnt = MIFARE_2K_MAXSECTOR; } else if (m4) { - flags |= FLAG_MF_4K; + FLAG_SET_MF_SIZE(flags, MIFARE_4K_MAX_BYTES); snprintf(csize, sizeof(csize), "4K"); k_sectors_cnt = MIFARE_4K_MAXSECTOR; } else { @@ -4256,15 +4239,14 @@ static int CmdHF14AMfSim(const char *Cmd) { } } - PrintAndLogEx(INFO, _YELLOW_("MIFARE %s") " | %s UID " _YELLOW_("%s") "" + PrintAndLogEx(INFO, _YELLOW_("MIFARE %s") " | %i bytes UID " _YELLOW_("%s") "" , csize - , uidsize + , uidlen , (uidlen == 0) ? "n/a" : sprint_hex(uid, uidlen) ); - PrintAndLogEx(INFO, "Options [ numreads: %d, flags: %d (0x%02x) ]" + PrintAndLogEx(INFO, "Options [ numreads: %d, flags: 0x%04x ]" , exitAfterNReads - , flags , flags); struct { @@ -4499,7 +4481,7 @@ static int CmdHF14AMfEGetBlk(const char *Cmd) { uint8_t blockno = (uint8_t)b; uint8_t data[16] = {0x00}; - if (mfEmlGetMem(data, blockno, 1) == PM3_SUCCESS) { + if (mf_eml_get_mem(data, blockno, 1) == PM3_SUCCESS) { uint8_t sector = mfSectorNum(blockno); mf_print_sector_hdr(sector); @@ -4543,7 +4525,7 @@ static int CmdHF14AMfEGetSc(const char *Cmd) { uint8_t data[16] = {0}; for (int i = 0; i < blocks; i++) { - int res = mfEmlGetMem(data, start + i, 1); + int res = mf_eml_get_mem(data, start + i, 1); if (res == PM3_SUCCESS) { mf_print_block_one(start + i, data, verbose); } @@ -4609,7 +4591,7 @@ static int CmdHF14AMfESet(const char *Cmd) { } // 1 - blocks count - return mfEmlSetMem(data, b, 1); + return mf_elm_set_mem(data, b, 1); } int CmdHF14AMfELoad(const char *Cmd) { @@ -4762,7 +4744,7 @@ int CmdHF14AMfELoad(const char *Cmd) { size_t offset = 0; int cnt = 0; - // 12 is the size of the struct the fct mfEmlSetMem_xt uses to transfer to device + // 12 is the size of the struct the fct mf_eml_set_mem_xt uses to transfer to device uint16_t max_avail_blocks = ((PM3_CMD_DATA_SIZE - 12) / block_width) * block_width; while (bytes_read && cnt < block_cnt) { @@ -4774,7 +4756,7 @@ int CmdHF14AMfELoad(const char *Cmd) { uint16_t chunk_size = MIN(max_avail_blocks, bytes_read); uint16_t blocks_to_send = chunk_size / block_width; - if (mfEmlSetMem_xt(data + offset, cnt, blocks_to_send, block_width) != PM3_SUCCESS) { + if (mf_eml_set_mem_xt(data + offset, cnt, blocks_to_send, block_width) != PM3_SUCCESS) { PrintAndLogEx(FAILED, "Can't set emulator mem at block: %3d", cnt); free(data); return PM3_ESOFT; @@ -5049,6 +5031,7 @@ static int CmdHF14AMfECFill(const char *Cmd) { memcpy(payload.key, key, sizeof(payload.key)); clearCommandBuffer(); + uint64_t t1 = msclock(); SendCommandNG(CMD_HF_MIFARE_EML_LOAD, (uint8_t *)&payload, sizeof(payload)); PacketResponseNG resp; @@ -5056,9 +5039,10 @@ static int CmdHF14AMfECFill(const char *Cmd) { PrintAndLogEx(WARNING, "command execution time out"); return PM3_ETIMEOUT; } + t1 = msclock() - t1; if (resp.status == PM3_SUCCESS) - PrintAndLogEx(SUCCESS, "Fill ( " _GREEN_("ok") " )"); + PrintAndLogEx(SUCCESS, "Fill ( " _GREEN_("ok") " ) in " _YELLOW_("%" PRIu64) " ms", t1); else PrintAndLogEx(FAILED, "Fill ( " _RED_("fail") " )"); @@ -5122,7 +5106,7 @@ static int CmdHF14AMfEKeyPrn(const char *Cmd) { // read UID from EMUL uint8_t data[16]; - if (mfEmlGetMem(data, 0, 1) != PM3_SUCCESS) { + if (mf_eml_get_mem(data, 0, 1) != PM3_SUCCESS) { PrintAndLogEx(WARNING, "error get block 0"); free(e_sector); return PM3_ESOFT; @@ -5135,7 +5119,7 @@ static int CmdHF14AMfEKeyPrn(const char *Cmd) { // download keys from EMUL for (int i = 0; i < sectors_cnt; i++) { - if (mfEmlGetMem(data, mfFirstBlockOfSector(i) + mfNumBlocksPerSector(i) - 1, 1) != PM3_SUCCESS) { + if (mf_eml_get_mem(data, mfFirstBlockOfSector(i) + mfNumBlocksPerSector(i) - 1, 1) != PM3_SUCCESS) { PrintAndLogEx(WARNING, "error get block %d", mfFirstBlockOfSector(i) + mfNumBlocksPerSector(i) - 1); e_sector[i].foundKey[0] = false; e_sector[i].foundKey[1] = false; @@ -5179,6 +5163,7 @@ static int CmdHF14AMfCSetUID(const char *Cmd) { arg_str0("u", "uid", "", "UID, 4/7 hex bytes"), arg_str0("a", "atqa", "", "ATQA, 2 hex bytes"), arg_str0("s", "sak", "", "SAK, 1 hex byte"), + arg_lit0(NULL, "gdm", "use gdm alt (20/23) magic wakeup"), arg_param_end }; CLIExecWithReturn(ctx, Cmd, argtable, true); @@ -5196,6 +5181,7 @@ static int CmdHF14AMfCSetUID(const char *Cmd) { int slen = 0; uint8_t sak[1] = {0x00}; CLIGetHexWithReturn(ctx, 4, sak, &slen); + uint8_t gdm = arg_get_lit(ctx, 5); CLIParserFree(ctx); // sanity checks @@ -5215,14 +5201,15 @@ static int CmdHF14AMfCSetUID(const char *Cmd) { uint8_t old_uid[7] = {0}; uint8_t verify_uid[7] = {0}; - int res = mfCSetUID( + int res = mf_chinese_set_uid( uid, uidlen, (alen) ? atqa : NULL, (slen) ? sak : NULL, old_uid, verify_uid, - wipe_card + wipe_card, + gdm ); if (res) { @@ -5253,6 +5240,7 @@ static int CmdHF14AMfCWipe(const char *cmd) { arg_str0("u", "uid", "", "UID, 4 hex bytes"), arg_str0("a", "atqa", "", "ATQA, 2 hex bytes"), arg_str0("s", "sak", "", "SAK, 1 hex byte"), + arg_lit0(NULL, "gdm", "use gdm alt (20/23) magic wakeup"), arg_param_end }; CLIExecWithReturn(ctx, cmd, argtable, true); @@ -5268,6 +5256,7 @@ static int CmdHF14AMfCWipe(const char *cmd) { int slen = 0; uint8_t sak[1] = {0x00}; CLIGetHexWithReturn(ctx, 3, sak, &slen); + uint8_t gdm = arg_get_lit(ctx, 4); CLIParserFree(ctx); if (uidlen && uidlen != 4) { @@ -5283,7 +5272,7 @@ static int CmdHF14AMfCWipe(const char *cmd) { return PM3_EINVARG; } - int res = mfCWipe((uidlen) ? uid : NULL, (alen) ? atqa : NULL, (slen) ? sak : NULL); + int res = mf_chinese_wipe((uidlen) ? uid : NULL, (alen) ? atqa : NULL, (slen) ? sak : NULL, gdm); if (res) { PrintAndLogEx(ERR, "Can't wipe card. error %d", res); return PM3_ESOFT; @@ -5305,6 +5294,7 @@ static int CmdHF14AMfCSetBlk(const char *Cmd) { arg_int1("b", "blk", "", "block number"), arg_str0("d", "data", "", "bytes to write, 16 hex bytes"), arg_lit0("w", "wipe", "wipes card with backdoor cmd before writing"), + arg_lit0(NULL, "gdm", "use gdm alt (20/23) magic wakeup"), arg_param_end }; CLIExecWithReturn(ctx, Cmd, argtable, false); @@ -5316,6 +5306,7 @@ static int CmdHF14AMfCSetBlk(const char *Cmd) { CLIGetHexWithReturn(ctx, 2, data, &datalen); uint8_t wipe_card = arg_get_lit(ctx, 3); + uint8_t gdm = arg_get_lit(ctx, 4); CLIParserFree(ctx); if (b < 0 || b >= MIFARE_1K_MAXBLOCK) { @@ -5329,13 +5320,19 @@ static int CmdHF14AMfCSetBlk(const char *Cmd) { } uint8_t params = MAGIC_SINGLE; + if (gdm) { + params |= MAGIC_GDM_ALT_WUPC; + } else { + params |= MAGIC_WUPC; + } + if (wipe_card) { params |= MAGIC_WIPE; } PrintAndLogEx(INFO, "Writing block number:%2d data:%s", b, sprint_hex_inrow(data, sizeof(data))); - int res = mfCSetBlock(b, data, NULL, params); + int res = mf_chinese_set_block(b, data, NULL, params); if (res) { PrintAndLogEx(ERR, "Can't write block. error=%d", res); return PM3_ESOFT; @@ -5360,6 +5357,7 @@ static int CmdHF14AMfCLoad(const char *Cmd) { arg_lit0(NULL, "2k", "MIFARE Classic/Plus 2k"), arg_lit0(NULL, "4k", "MIFARE Classic 4k / S70"), arg_lit0(NULL, "emu", "from emulator memory"), + arg_lit0(NULL, "gdm", "use gdm alt (20/23) magic wakeup"), arg_param_end }; CLIExecWithReturn(ctx, Cmd, argtable, false); @@ -5373,6 +5371,7 @@ static int CmdHF14AMfCLoad(const char *Cmd) { bool m2 = arg_get_lit(ctx, 4); bool m4 = arg_get_lit(ctx, 5); bool fill_from_emulator = arg_get_lit(ctx, 6); + bool gdm = arg_get_lit(ctx, 7); CLIParserFree(ctx); @@ -5406,7 +5405,7 @@ static int CmdHF14AMfCLoad(const char *Cmd) { if (fill_from_emulator) { - PrintAndLogEx(INFO, "Start upload to emulator memory"); + PrintAndLogEx(INFO, "Start upload from emulator memory"); PrintAndLogEx(INFO, "." NOLF); for (int b = 0; b < block_cnt; b++) { @@ -5414,14 +5413,14 @@ static int CmdHF14AMfCLoad(const char *Cmd) { uint8_t buf8[MFBLOCK_SIZE] = {0x00}; // read from emul memory - if (mfEmlGetMem(buf8, b, 1)) { + if (mf_eml_get_mem(buf8, b, 1)) { PrintAndLogEx(WARNING, "Can't read from emul block: %d", b); return PM3_ESOFT; } // switch on field and send magic sequence if (b == 0) { - flags = MAGIC_INIT + MAGIC_WUPC; + flags = MAGIC_INIT | (gdm ? MAGIC_GDM_ALT_WUPC : MAGIC_WUPC); } // just write @@ -5435,7 +5434,7 @@ static int CmdHF14AMfCLoad(const char *Cmd) { } // write to card - if (mfCSetBlock(b, buf8, NULL, flags)) { + if (mf_chinese_set_block(b, buf8, NULL, flags)) { PrintAndLogEx(WARNING, "Can't set magic card block: %d", b); return PM3_ESOFT; } @@ -5469,7 +5468,7 @@ static int CmdHF14AMfCLoad(const char *Cmd) { // switch on field and send magic sequence if (blockno == 0) { - flags = MAGIC_INIT + MAGIC_WUPC; + flags = MAGIC_INIT | (gdm ? MAGIC_GDM_ALT_WUPC : MAGIC_WUPC); } // write @@ -5482,7 +5481,7 @@ static int CmdHF14AMfCLoad(const char *Cmd) { flags = MAGIC_HALT + MAGIC_OFF; } - if (mfCSetBlock(blockno, data + (MFBLOCK_SIZE * blockno), NULL, flags)) { + if (mf_chinese_set_block(blockno, data + (MFBLOCK_SIZE * blockno), NULL, flags)) { PrintAndLogEx(WARNING, "Can't set magic card block: %d", blockno); free(data); return PM3_ESOFT; @@ -5524,11 +5523,13 @@ static int CmdHF14AMfCGetBlk(const char *Cmd) { arg_param_begin, arg_int1("b", "blk", "", "block number"), arg_lit0("v", "verbose", "verbose output"), + arg_lit0(NULL, "gdm", "use gdm alt (20/23) magic wakeup"), arg_param_end }; CLIExecWithReturn(ctx, Cmd, argtable, false); int b = arg_get_int_def(ctx, 1, 0); bool verbose = arg_get_lit(ctx, 2); + bool gdm = arg_get_lit(ctx, 3); CLIParserFree(ctx); if (b > 255) { @@ -5537,7 +5538,7 @@ static int CmdHF14AMfCGetBlk(const char *Cmd) { uint8_t blockno = (uint8_t)b; uint8_t data[16] = {0}; - int res = mfCGetBlock(blockno, data, MAGIC_SINGLE); + int res = mf_chinese_get_block(blockno, data, MAGIC_SINGLE | (gdm ? MAGIC_GDM_ALT_WUPC : MAGIC_WUPC)); if (res) { PrintAndLogEx(ERR, "Can't read block. error=%d", res); return PM3_ESOFT; @@ -5566,11 +5567,13 @@ static int CmdHF14AMfCGetSc(const char *Cmd) { arg_param_begin, arg_int1("s", "sec", "", "sector number"), arg_lit0("v", "verbose", "verbose output"), + arg_lit0(NULL, "gdm", "use gdm alt (20/23) magic wakeup"), arg_param_end }; CLIExecWithReturn(ctx, Cmd, argtable, false); int s = arg_get_int_def(ctx, 1, 0); bool verbose = arg_get_lit(ctx, 2); + bool gdm = arg_get_lit(ctx, 3); CLIParserFree(ctx); if (s >= MIFARE_4K_MAXSECTOR) { @@ -5588,13 +5591,13 @@ static int CmdHF14AMfCGetSc(const char *Cmd) { start = 128 + (sector - 32) * 16; } - int flags = MAGIC_INIT + MAGIC_WUPC; + int flags = MAGIC_INIT + (gdm ? MAGIC_GDM_ALT_WUPC : MAGIC_WUPC); uint8_t data[16] = {0}; for (int i = 0; i < blocks; i++) { if (i == 1) flags = 0; if (i == blocks - 1) flags = MAGIC_HALT + MAGIC_OFF; - int res = mfCGetBlock(start + i, data, flags); + int res = mf_chinese_get_block(start + i, data, flags); if (res) { PrintAndLogEx(ERR, "Can't read block. %d error=%d", start + i, res); return PM3_ESOFT; @@ -5625,6 +5628,7 @@ static int CmdHF14AMfCSave(const char *Cmd) { arg_lit0(NULL, "2k", "MIFARE Classic/Plus 2k"), arg_lit0(NULL, "4k", "MIFARE Classic 4k / S70"), arg_lit0(NULL, "emu", "to emulator memory"), + arg_lit0(NULL, "gdm", "to emulator memory"), arg_param_end }; CLIExecWithReturn(ctx, Cmd, argtable, true); @@ -5638,6 +5642,7 @@ static int CmdHF14AMfCSave(const char *Cmd) { bool m2 = arg_get_lit(ctx, 4); bool m4 = arg_get_lit(ctx, 5); bool fill_emulator = arg_get_lit(ctx, 6); + bool gdm = arg_get_lit(ctx, 7); CLIParserFree(ctx); // validations @@ -5705,7 +5710,7 @@ static int CmdHF14AMfCSave(const char *Cmd) { } // switch on field and send magic sequence - uint8_t flags = MAGIC_INIT + MAGIC_WUPC; + uint8_t flags = MAGIC_INIT + (gdm ? MAGIC_GDM_ALT_WUPC : MAGIC_WUPC); for (uint16_t i = 0; i < block_cnt; i++) { // read @@ -5717,7 +5722,7 @@ static int CmdHF14AMfCSave(const char *Cmd) { flags = MAGIC_HALT + MAGIC_OFF; } - if (mfCGetBlock(i, dump + (i * MFBLOCK_SIZE), flags)) { + if (mf_chinese_get_block(i, dump + (i * MFBLOCK_SIZE), flags)) { PrintAndLogEx(WARNING, "Can't get magic card block: %d", i); PrintAndLogEx(HINT, "Verify your card size, and try again or try another tag position"); free(dump); @@ -5738,7 +5743,7 @@ static int CmdHF14AMfCSave(const char *Cmd) { // Disable fast mode on last packet g_conn.block_after_ACK = false; } - if (mfEmlSetMem(dump + (i * MFBLOCK_SIZE), i, 5) != PM3_SUCCESS) { + if (mf_elm_set_mem(dump + (i * MFBLOCK_SIZE), i, 5) != PM3_SUCCESS) { PrintAndLogEx(WARNING, "Can't set emul block: " _YELLOW_("%d"), i); } if (i % 64 == 0) { @@ -5779,6 +5784,7 @@ static int CmdHF14AMfCView(const char *Cmd) { arg_lit0(NULL, "2k", "MIFARE Classic/Plus 2k"), arg_lit0(NULL, "4k", "MIFARE Classic 4k / S70"), arg_lit0("v", "verbose", "verbose output"), + arg_lit0(NULL, "gdm", "use gdm alt (20/23) magic wakeup"), arg_param_end }; CLIExecWithReturn(ctx, Cmd, argtable, true); @@ -5787,6 +5793,7 @@ static int CmdHF14AMfCView(const char *Cmd) { bool m2 = arg_get_lit(ctx, 3); bool m4 = arg_get_lit(ctx, 4); bool verbose = arg_get_lit(ctx, 5); + bool gdm = arg_get_lit(ctx, 6); CLIParserFree(ctx); // validations @@ -5853,7 +5860,7 @@ static int CmdHF14AMfCView(const char *Cmd) { } // switch on field and send magic sequence - uint8_t flags = MAGIC_INIT + MAGIC_WUPC; + uint8_t flags = MAGIC_INIT + (gdm ? MAGIC_GDM_ALT_WUPC : MAGIC_WUPC); for (uint16_t i = 0; i < block_cnt; i++) { // read if (i == 1) { @@ -5864,7 +5871,7 @@ static int CmdHF14AMfCView(const char *Cmd) { flags = MAGIC_HALT + MAGIC_OFF; } - if (mfCGetBlock(i, dump + (i * MFBLOCK_SIZE), flags)) { + if (mf_chinese_get_block(i, dump + (i * MFBLOCK_SIZE), flags)) { PrintAndLogEx(WARNING, "Can't get magic card block: " _YELLOW_("%u"), i); PrintAndLogEx(HINT, "Verify your card size, and try again or try another tag position"); free(dump); @@ -5937,7 +5944,7 @@ static int CmdHf14AMfDecryptBytes(const char *Cmd) { PrintAndLogEx(INFO, "ar enc... %08X", ar_enc); PrintAndLogEx(INFO, "at enc... %08X", at_enc); - return tryDecryptWord(nt, ar_enc, at_enc, data, datalen); + return try_decrypt_word(nt, ar_enc, at_enc, data, datalen); } static int CmdHf14AMfSetMod(const char *Cmd) { @@ -6174,7 +6181,8 @@ static int CmdHF14AMfMAD(const char *Cmd) { arg_lit0("b", "keyb", "use key B for access printing sectors (by default: key A)"), arg_lit0(NULL, "be", "(optional, BigEndian)"), arg_lit0(NULL, "dch", "decode Card Holder information"), - arg_str0("f", "file", "", "load dump file and decode MAD"), + arg_str0("f", "file", "", "load dump file and decode MAD"), + arg_lit0(NULL, "force", "force decode (skip key check)"), arg_param_end }; CLIExecWithReturn(ctx, Cmd, argtable, true); @@ -6188,6 +6196,7 @@ static int CmdHF14AMfMAD(const char *Cmd) { bool keyB = arg_get_lit(ctx, 4); bool swapmad = arg_get_lit(ctx, 5); bool decodeholder = arg_get_lit(ctx, 6); + bool force = arg_get_lit(ctx, 8); int fnlen = 0; char filename[FILE_PATH_SIZE] = {0}; @@ -6217,7 +6226,7 @@ static int CmdHF14AMfMAD(const char *Cmd) { } // MAD detection - if (HasMADKey(dump) == false) { + if ((HasMADKey(dump) == false) && (force == false)) { PrintAndLogEx(FAILED, "No MAD key was detected in the dump file"); free(dump); return PM3_ESOFT; @@ -6302,7 +6311,7 @@ static int CmdHF14AMfMAD(const char *Cmd) { uint8_t sector10[MFBLOCK_SIZE * 4] = {0}; bool got_first = true; - if (mfReadSector(MF_MAD1_SECTOR, MF_KEY_A, (uint8_t *)g_mifare_mad_key, sector0) != PM3_SUCCESS) { + if (mf_read_sector(MF_MAD1_SECTOR, MF_KEY_A, (uint8_t *)g_mifare_mad_key, sector0) != PM3_SUCCESS) { PrintAndLogEx(WARNING, "error, read sector 0. card doesn't have MAD or doesn't have MAD on default keys"); got_first = false; } else { @@ -6312,7 +6321,7 @@ static int CmdHF14AMfMAD(const char *Cmd) { // User supplied key if (got_first == false && keylen == 6) { PrintAndLogEx(INFO, "Trying user specified key..."); - if (mfReadSector(MF_MAD1_SECTOR, MF_KEY_A, userkey, sector0) != PM3_SUCCESS) { + if (mf_read_sector(MF_MAD1_SECTOR, MF_KEY_A, userkey, sector0) != PM3_SUCCESS) { PrintAndLogEx(ERR, "error, read sector 0. card doesn't have MAD or the custom key is wrong"); } else { PrintAndLogEx(INFO, "Authentication ( " _GREEN_("ok") " )"); @@ -6326,7 +6335,7 @@ static int CmdHF14AMfMAD(const char *Cmd) { } got_first = true; - if (mfReadSector(MF_MAD2_SECTOR, MF_KEY_A, (uint8_t *)g_mifare_mad_key, sector10) != PM3_SUCCESS) { + if (mf_read_sector(MF_MAD2_SECTOR, MF_KEY_A, (uint8_t *)g_mifare_mad_key, sector10) != PM3_SUCCESS) { if (verbose) { PrintAndLogEx(ERR, "error, read sector 0x10. card doesn't have MAD 2 or doesn't have MAD 2 on default keys"); } @@ -6338,7 +6347,7 @@ static int CmdHF14AMfMAD(const char *Cmd) { // User supplied key if (got_first == false && keylen == 6) { PrintAndLogEx(INFO, "Trying user specified key..."); - if (mfReadSector(MF_MAD2_SECTOR, MF_KEY_A, userkey, sector10) != PM3_SUCCESS) { + if (mf_read_sector(MF_MAD2_SECTOR, MF_KEY_A, userkey, sector10) != PM3_SUCCESS) { if (verbose) { PrintAndLogEx(ERR, "error, read sector 10. card doesn't have MAD 2 or the custom key is wrong"); } @@ -6384,7 +6393,7 @@ static int CmdHF14AMfMAD(const char *Cmd) { for (int i = 0; i < madlen; i++) { if (aaid == mad[i]) { uint8_t vsector[MFBLOCK_SIZE * 4] = {0}; - if (mfReadSector(i + 1, keyB ? MF_KEY_B : MF_KEY_A, akey, vsector)) { + if (mf_read_sector(i + 1, keyB ? MF_KEY_B : MF_KEY_A, akey, vsector)) { PrintAndLogEx(NORMAL, ""); PrintAndLogEx(ERR, "error, read sector %d", i + 1); return PM3_ESOFT; @@ -6408,7 +6417,7 @@ static int CmdHF14AMfMAD(const char *Cmd) { if (aaid == mad[i]) { uint8_t vsector[MFBLOCK_SIZE * 4] = {0}; - if (mfReadSector(i + 1, keyB ? MF_KEY_B : MF_KEY_A, akey, vsector)) { + if (mf_read_sector(i + 1, keyB ? MF_KEY_B : MF_KEY_A, akey, vsector)) { PrintAndLogEx(NORMAL, ""); PrintAndLogEx(ERR, "error, read sector %d", i + 1); return PM3_ESOFT; @@ -6505,7 +6514,7 @@ int CmdHFMFNDEFRead(const char *Cmd) { PrintAndLogEx(INFO, "reading MAD v1 sector"); } - if (mfReadSector(MF_MAD1_SECTOR, MF_KEY_A, g_mifare_mad_key, sector0)) { + if (mf_read_sector(MF_MAD1_SECTOR, MF_KEY_A, g_mifare_mad_key, sector0)) { PrintAndLogEx(ERR, "error, read sector 0. card doesn't have MAD or doesn't have MAD on default keys"); PrintAndLogEx(HINT, "Try " _YELLOW_("`hf mf ndefread -k `") " with your custom key"); return PM3_ESOFT; @@ -6515,7 +6524,7 @@ int CmdHFMFNDEFRead(const char *Cmd) { PrintAndLogEx(INFO, "reading MAD v2 sector"); } - if (mfReadSector(MF_MAD2_SECTOR, MF_KEY_A, g_mifare_mad_key, sector10)) { + if (mf_read_sector(MF_MAD2_SECTOR, MF_KEY_A, g_mifare_mad_key, sector10)) { if (verbose) { PrintAndLogEx(ERR, "error, read sector 0x10. card doesn't have MAD 2 or doesn't have MAD 2 on default keys"); PrintAndLogEx(INFO, "Skipping MAD 2"); @@ -6541,7 +6550,7 @@ int CmdHFMFNDEFRead(const char *Cmd) { for (int i = 0; i < madlen; i++) { if (ndef_aid == mad[i]) { uint8_t vsector[MFBLOCK_SIZE * 4] = {0}; - if (mfReadSector(i + 1, keyB ? MF_KEY_B : MF_KEY_A, ndefkey, vsector)) { + if (mf_read_sector(i + 1, keyB ? MF_KEY_B : MF_KEY_A, ndefkey, vsector)) { PrintAndLogEx(ERR, "error, reading sector %d ", i + 1); return PM3_ESOFT; } @@ -6690,7 +6699,7 @@ int CmdHFMFNDEFFormat(const char *Cmd) { uint64_t key64 = 0; // check if we can authenticate to sector - if (mfCheckKeys(0, MF_KEY_A, true, 1, (uint8_t *)g_mifare_mad_key, &key64) == PM3_SUCCESS) { + if (mf_check_keys(0, MF_KEY_A, true, 1, (uint8_t *)g_mifare_mad_key, &key64) == PM3_SUCCESS) { // if used, assume KEY A is MAD/NDEF set. memcpy(keyA[0], g_mifare_mad_key, sizeof(g_mifare_mad_key)); @@ -6772,9 +6781,9 @@ skipfile: } // write to card, try B key first - if (mf_write_block(keyB[i], MF_KEY_B, b, block) == 0) { + if (mf_write_block(b, MF_KEY_B, keyB[i], block) != PM3_SUCCESS) { // try A key, - if (mf_write_block(keyA[i], MF_KEY_A, b, block) == 0) { + if (mf_write_block(b, MF_KEY_A, keyA[i], block) != PM3_SUCCESS) { return PM3_EFAILED; } } @@ -6869,7 +6878,7 @@ int CmdHFMFNDEFWrite(const char *Cmd) { uint64_t key64 = 0; // check if we can authenticate to sector - int res = mfCheckKeys(0, MF_KEY_A, true, 1, (uint8_t *)g_mifare_mad_key, &key64); + int res = mf_check_keys(0, MF_KEY_A, true, 1, (uint8_t *)g_mifare_mad_key, &key64); if (res != PM3_SUCCESS) { PrintAndLogEx(FAILED, "Sector 0 failed to authenticate with MAD default key"); PrintAndLogEx(HINT, "Verify that the tag NDEF formatted"); @@ -6944,7 +6953,7 @@ int CmdHFMFNDEFWrite(const char *Cmd) { // read MAD Sector 0, block1,2 uint8_t sector0[MFBLOCK_SIZE * 4] = {0}; - if (mfReadSector(MF_MAD1_SECTOR, MF_KEY_A, g_mifare_mad_key, sector0)) { + if (mf_read_sector(MF_MAD1_SECTOR, MF_KEY_A, g_mifare_mad_key, sector0)) { PrintAndLogEx(ERR, "error, reading sector 0. Card doesn't have MAD or doesn't have MAD on default keys"); PrintAndLogEx(HINT, "Try " _YELLOW_("`hf mf ndefread -k `") " with your custom key"); return PM3_ESOFT; @@ -6953,7 +6962,7 @@ int CmdHFMFNDEFWrite(const char *Cmd) { // read MAD Sector 10, block1,2 uint8_t sector10[MFBLOCK_SIZE * 4] = {0}; if (m4) { - if (mfReadSector(MF_MAD2_SECTOR, MF_KEY_A, g_mifare_mad_key, sector10)) { + if (mf_read_sector(MF_MAD2_SECTOR, MF_KEY_A, g_mifare_mad_key, sector10)) { PrintAndLogEx(ERR, "error, reading sector 10. Card doesn't have MAD or doesn't have MAD on default keys"); PrintAndLogEx(HINT, "Try " _YELLOW_("`hf mf ndefread -k `") " with your custom key"); return PM3_ESOFT; @@ -7015,10 +7024,11 @@ int CmdHFMFNDEFWrite(const char *Cmd) { } // write to card, try B key first - if (mf_write_block(g_mifare_default_key, MF_KEY_B, block_no, block) == 0) { + if (mf_write_block(block_no, MF_KEY_B, g_mifare_default_key, block) != PM3_SUCCESS) { // try A key, - if (mf_write_block(g_mifare_ndef_key, MF_KEY_A, block_no, block) == 0) { + + if (mf_write_block(block_no, MF_KEY_A, g_mifare_ndef_key, block) != PM3_SUCCESS) { return PM3_EFAILED; } } @@ -7173,7 +7183,7 @@ static int CmdHf14AGen3UID(const char *Cmd) { uint8_t old_uid[10] = {0}; - int res = mfGen3UID(uid, uidlen, old_uid); + int res = mf_chinese_gen_3_uid(uid, uidlen, old_uid); if (res != PM3_SUCCESS) { PrintAndLogEx(ERR, "Can't set UID"); PrintAndLogEx(HINT, "Are you sure your card is a Gen3 ?"); @@ -7213,7 +7223,7 @@ static int CmdHf14AGen3Block(const char *Cmd) { CLIParserFree(ctx); uint8_t new_block[MFBLOCK_SIZE] = {0x00}; - int res = mfGen3Block(data, datalen, new_block); + int res = mf_chinese_gen_3_block(data, datalen, new_block); if (res) { PrintAndLogEx(ERR, "Can't change manufacturer block data. error %d", res); return PM3_ESOFT; @@ -7244,7 +7254,7 @@ static int CmdHf14AGen3Freeze(const char *Cmd) { return PM3_SUCCESS; } - int res = mfGen3Freeze(); + int res = mf_chinese_gen_3_freeze(); if (res != PM3_SUCCESS) { PrintAndLogEx(ERR, "Can't lock UID changes. error %d", res); } else { @@ -8696,7 +8706,7 @@ static int CmdHF14AGen4Save(const char *Cmd) { int cnt = 0; uint16_t bytes_left = bytes ; - // 12 is the size of the struct the fct mfEmlSetMem_xt uses to transfer to device + // 12 is the size of the struct the fct mf_eml_set_mem_xt uses to transfer to device uint16_t max_avail_blocks = ((PM3_CMD_DATA_SIZE - 12) / MFBLOCK_SIZE) * MFBLOCK_SIZE; while (bytes_left > 0 && cnt < block_cnt) { @@ -8708,7 +8718,7 @@ static int CmdHF14AGen4Save(const char *Cmd) { uint16_t chunk_size = MIN(max_avail_blocks, bytes_left); uint16_t blocks_to_send = chunk_size / MFBLOCK_SIZE; - if (mfEmlSetMem_xt(dump + offset, cnt, blocks_to_send, MFBLOCK_SIZE) != PM3_SUCCESS) { + if (mf_eml_set_mem_xt(dump + offset, cnt, blocks_to_send, MFBLOCK_SIZE) != PM3_SUCCESS) { PrintAndLogEx(FAILED, "Can't set emulator mem at block: %3d", cnt); free(dump); return PM3_ESOFT; @@ -9303,7 +9313,11 @@ static int CmdHF14AMfValue(const char *Cmd) { } else { cmddata[10] = 0; - PrintAndLogEx(INFO, "Writing block no %u, key %c - %s", blockno, (keytype == MF_KEY_B) ? 'B' : 'A', sprint_hex_inrow(key, sizeof(key))); + if (keytype < 2) { + PrintAndLogEx(INFO, "Writing block no %u, key type:%c - %s", blockno, (keytype == MF_KEY_B) ? 'B' : 'A', sprint_hex_inrow(key, sizeof(key))); + } else { + PrintAndLogEx(INFO, "Writing block no %u, key type:%02x - %s", blockno, MIFARE_AUTH_KEYA + keytype, sprint_hex_inrow(key, sizeof(key))); + } } memcpy(cmddata + 11, block, sizeof(block)); @@ -9364,12 +9378,12 @@ static int CmdHF14AMfValue(const char *Cmd) { // already have data from command line } else { if (trnval == -1) { - res = mfReadBlock(blockno, keytype, key, data); + res = mf_read_block(blockno, keytype, key, data); } else { if (mfSectorNum(trnval) != mfSectorNum(blockno)) - res = mfReadBlock(trnval, transferkeytype, transferkey, data); + res = mf_read_block(trnval, transferkeytype, transferkey, data); else - res = mfReadBlock(trnval, keytype, key, data); + res = mf_read_block(trnval, keytype, key, data); } } @@ -9495,8 +9509,8 @@ static int CmdHFMFHidEncode(const char *Cmd) { PrintAndLogEx(INFO, "Writing %u - %s", (i + 1), sprint_hex_inrow(blocks + (i * MFBLOCK_SIZE), MFBLOCK_SIZE)); } - if (mf_write_block(empty, MF_KEY_A, (i + 1), blocks + (i * MFBLOCK_SIZE)) == false) { - if (mf_write_block(empty, MF_KEY_B, (i + 1), blocks + (i * MFBLOCK_SIZE)) == false) { + if (mf_write_block((i + 1), MF_KEY_A, empty, blocks + (i * MFBLOCK_SIZE)) == PM3_EFAILED) { + if (mf_write_block((i + 1), MF_KEY_B, empty, blocks + (i * MFBLOCK_SIZE)) == PM3_EFAILED) { PrintAndLogEx(WARNING, "failed writing block %d using default empty key", (i + 1)); res = false; break; @@ -9614,7 +9628,7 @@ static int CmdHF14AMfInfo(const char *Cmd) { PrintAndLogEx(INFO, "--- " _CYAN_("Keys Information")); uint8_t fkey[MIFARE_KEY_SIZE] = {0}; - uint8_t fKeyType = 0xff; + uint8_t fKeyType = 0xFF; uint64_t tmpkey = 0; mfc_algo_saflok_one(card.uid, 0, MF_KEY_A, &tmpkey); @@ -9636,14 +9650,14 @@ static int CmdHF14AMfInfo(const char *Cmd) { } uint8_t blockdata[MFBLOCK_SIZE] = {0}; - res = mfCheckKeys_fast(sectorsCnt, true, true, 1, keycnt, keyBlock, e_sector, false, verbose); + res = mf_check_keys_fast(sectorsCnt, true, true, 1, keycnt, keyBlock, e_sector, false, verbose); if (res == PM3_SUCCESS || res == PM3_EPARTIAL) { if (e_sector[0].foundKey[MF_KEY_A]) { PrintAndLogEx(SUCCESS, "Sector 0 key A... " _GREEN_("%012" PRIX64), e_sector[0].Key[MF_KEY_A]); num_to_bytes(e_sector[0].Key[MF_KEY_A], MIFARE_KEY_SIZE, fkey); - if (mfReadBlock(0, MF_KEY_A, key, blockdata) == PM3_SUCCESS) { + if (mf_read_block(0, MF_KEY_A, key, blockdata) == PM3_SUCCESS) { fKeyType = MF_KEY_A; } } @@ -9653,7 +9667,7 @@ static int CmdHF14AMfInfo(const char *Cmd) { if (fKeyType == 0xFF) { num_to_bytes(e_sector[0].Key[MF_KEY_B], MIFARE_KEY_SIZE, fkey); - if (mfReadBlock(0, MF_KEY_B, key, blockdata) == PM3_SUCCESS) { + if (mf_read_block(0, MF_KEY_B, key, blockdata) == PM3_SUCCESS) { fKeyType = MF_KEY_B; } } @@ -9667,15 +9681,19 @@ static int CmdHF14AMfInfo(const char *Cmd) { uint8_t k08s[6] = {0xA3, 0x96, 0xEF, 0xA4, 0xE2, 0x4F}; uint8_t k08[6] = {0xA3, 0x16, 0x67, 0xA8, 0xCE, 0xC1}; uint8_t k32[6] = {0x51, 0x8B, 0x33, 0x54, 0xE7, 0x60}; - if (mfReadBlock(0, 4, k08s, blockdata) == PM3_SUCCESS) { + if (mf_read_block(0, 4, k08s, blockdata) == PM3_SUCCESS) { PrintAndLogEx(SUCCESS, "Backdoor key..... " _YELLOW_("%s"), sprint_hex_inrow(k08s, sizeof(k08s))); - fKeyType = MF_KEY_BD08S; - } else if (mfReadBlock(0, 4, k08, blockdata) == PM3_SUCCESS) { + fKeyType = MF_KEY_BD; + memcpy(fkey, k08s, sizeof(fkey)); + + } else if (mf_read_block(0, 4, k08, blockdata) == PM3_SUCCESS) { PrintAndLogEx(SUCCESS, "Backdoor key..... " _YELLOW_("%s"), sprint_hex_inrow(k08, sizeof(k08))); - fKeyType = MF_KEY_BD08; - } else if (mfReadBlock(0, 4, k32, blockdata) == PM3_SUCCESS) { + fKeyType = MF_KEY_BD; + memcpy(fkey, k08, sizeof(fkey)); + } else if (mf_read_block(0, 4, k32, blockdata) == PM3_SUCCESS) { PrintAndLogEx(SUCCESS, "Backdoor key..... " _YELLOW_("%s"), sprint_hex_inrow(k32, sizeof(k32))); - fKeyType = MF_KEY_BD32; + fKeyType = MF_KEY_BD; + memcpy(fkey, k32, sizeof(fkey)); } if (fKeyType != 0xFF) { @@ -9690,33 +9708,50 @@ static int CmdHF14AMfInfo(const char *Cmd) { if (card.sak != 0x20 && memcmp(blockdata + 8, "\x62\x63\x64\x65\x66\x67\x68\x69", 8) == 0) { // backdoor might be present, or just a clone reusing Fudan MF data... PrintAndLogEx(SUCCESS, "Fudan based card"); - } else if (fKeyType == MF_KEY_BD08S && card.sak == 0x08 && memcmp(blockdata + 5, "\x08\x04\x00", 3) == 0 + } else if (fKeyType == MF_KEY_BD && memcmp(fkey, k08s, sizeof(fkey)) == 0 + && card.sak == 0x08 && memcmp(blockdata + 5, "\x08\x04\x00", 3) == 0 && (blockdata[8] == 0x03 || blockdata[8] == 0x04) && blockdata[15] == 0x90) { PrintAndLogEx(SUCCESS, "Fudan FM11RF08S"); - } else if (fKeyType == MF_KEY_BD08S && card.sak == 0x08 && memcmp(blockdata + 5, "\x00\x03\x00\x10", 4) == 0 + } else if (fKeyType == MF_KEY_BD && memcmp(fkey, k08s, sizeof(fkey)) == 0 + && card.sak == 0x08 && memcmp(blockdata + 5, "\x00\x03\x00\x10", 4) == 0 && blockdata[15] == 0x90) { PrintAndLogEx(SUCCESS, "Fudan FM11RF08S-7B"); - } else if (fKeyType == MF_KEY_BD08 && card.sak == 0x08 && memcmp(blockdata + 5, "\x08\x04\x00", 3) == 0 + } else if (fKeyType == MF_KEY_BD && memcmp(fkey, k08, sizeof(fkey)) == 0 + && card.sak == 0x08 && memcmp(blockdata + 5, "\x08\x04\x00", 3) == 0 && blockdata[15] == 0x98) { PrintAndLogEx(SUCCESS, "Fudan FM11RF08S **98"); - } else if (fKeyType == MF_KEY_BD08 && card.sak == 0x08 && memcmp(blockdata + 5, "\x08\x04\x00", 3) == 0 + } else if (fKeyType == MF_KEY_BD && memcmp(fkey, k08, sizeof(fkey)) == 0 + && card.sak == 0x08 && memcmp(blockdata + 5, "\x08\x04\x00", 3) == 0 && (blockdata[8] >= 0x01 && blockdata[8] <= 0x03) && blockdata[15] == 0x1D) { PrintAndLogEx(SUCCESS, "Fudan FM11RF08"); - } else if (fKeyType == MF_KEY_BD32 && card.sak == 0x18 && memcmp(blockdata + 5, "\x18\x02\x00\x46\x44\x53\x37\x30\x56\x30\x31", 11) == 0) { + } else if (fKeyType == MF_KEY_BD && memcmp(fkey, k32, sizeof(fkey)) == 0 + && card.sak == 0x18 && memcmp(blockdata + 5, "\x18\x02\x00\x46\x44\x53\x37\x30\x56\x30\x31", 11) == 0) { PrintAndLogEx(SUCCESS, "Fudan FM11RF32"); - } else if (fKeyType == MF_KEY_BD08 && card.sak == 0x20 && memcmp(blockdata + 8, "\x62\x63\x64\x65\x66\x67\x68\x69", 8) == 0) { + } else if (fKeyType == MF_KEY_BD && memcmp(fkey, k08, sizeof(fkey)) == 0 + && card.sak == 0x20 && memcmp(blockdata + 8, "\x62\x63\x64\x65\x66\x67\x68\x69", 8) == 0) { PrintAndLogEx(SUCCESS, "Fudan FM11RF32 (SAK=20)"); - } else if (fKeyType == MF_KEY_BD08 && card.sak == 0x28 && memcmp(blockdata + 5, "\x28\x04\x00\x90\x10\x15\x01\x00\x00\x00\x00", 11) == 0) { + } else if (fKeyType == MF_KEY_BD && memcmp(fkey, k08, sizeof(fkey)) == 0 + && card.sak == 0x28 && ( + (memcmp(blockdata + 5, "\x28\x04\x00\x90\x10\x15\x01\x00\x00\x00\x00", 11) == 0) || + (memcmp(blockdata + 5, "\x28\x04\x00\x90\x11\x15\x01\x00\x00\x00\x00", 11) == 0))) { // Note: it also has ATS = // 10 78 80 90 02 20 90 00 00 00 00 00 + UID + CRC PrintAndLogEx(SUCCESS, "Fudan FM1208-10"); - } else if (fKeyType == MF_KEY_BD08 && card.sak == 0x88 && memcmp(blockdata + 5, "\x88\x04\x00\x43", 4) == 0) { + } else if (fKeyType == MF_KEY_BD && memcmp(fkey, k08, sizeof(fkey)) == 0 + && card.sak == 0x28 && memcmp(blockdata + 5, "\x28\x04\x00\x90\x53\xB7\x0C\x00\x00\x00\x00", 11) == 0) { + // Note: it also has ATS = + // 10 78 80 B0 02 20 90 00 00 00 00 00 + UID + CRC + PrintAndLogEx(SUCCESS, "Fudan FM1216-137"); + } else if (fKeyType == MF_KEY_BD && memcmp(fkey, k08, sizeof(fkey)) == 0 + && card.sak == 0x88 && memcmp(blockdata + 5, "\x88\x04\x00\x43", 4) == 0) { PrintAndLogEx(SUCCESS, "Infineon SLE66R35"); - } else if (fKeyType == MF_KEY_BD08 && card.sak == 0x08 && memcmp(blockdata + 5, "\x88\x04\x00\x44", 4) == 0) { + } else if (fKeyType == MF_KEY_BD && memcmp(fkey, k08, sizeof(fkey)) == 0 + && card.sak == 0x08 && memcmp(blockdata + 5, "\x88\x04\x00\x44", 4) == 0) { PrintAndLogEx(SUCCESS, "NXP MF1ICS5003"); - } else if (fKeyType == MF_KEY_BD08 && card.sak == 0x08 && memcmp(blockdata + 5, "\x88\x04\x00\x45", 4) == 0) { + } else if (fKeyType == MF_KEY_BD && memcmp(fkey, k08, sizeof(fkey)) == 0 + && card.sak == 0x08 && memcmp(blockdata + 5, "\x88\x04\x00\x45", 4) == 0) { PrintAndLogEx(SUCCESS, "NXP MF1ICS5004"); - } else if (fKeyType == MF_KEY_BD08 || fKeyType == MF_KEY_BD08S || fKeyType == MF_KEY_BD32) { + } else if (fKeyType == MF_KEY_BD) { PrintAndLogEx(SUCCESS, _RED_("Unknown card with backdoor, please report details!")); } else // other cards @@ -9779,8 +9814,10 @@ static int CmdHF14AMfInfo(const char *Cmd) { if (res == NONCE_STATIC) { PrintAndLogEx(SUCCESS, "Static nonce... " _YELLOW_("yes")); fKeyType = 0xFF; // dont detect twice - } - if (res == NONCE_STATIC_ENC) { + } else if (res == NONCE_SUPERSTATIC) { + PrintAndLogEx(SUCCESS, "Static nonce... " _YELLOW_("yes, even when nested")); + fKeyType = 0xFF; // dont detect twice + } else if (res == NONCE_STATIC_ENC) { PrintAndLogEx(SUCCESS, "Static enc nonce... " _RED_("yes")); fKeyType = 0xFF; // dont detect twice } @@ -9790,9 +9827,9 @@ static int CmdHF14AMfInfo(const char *Cmd) { res = detect_classic_static_encrypted_nonce(0, fKeyType, fkey); if (res == NONCE_STATIC) { PrintAndLogEx(SUCCESS, "Static nonce... " _YELLOW_("yes")); - } - - if (res == NONCE_STATIC_ENC) { + } else if (res == NONCE_SUPERSTATIC) { + PrintAndLogEx(SUCCESS, "Static nonce... " _YELLOW_("yes, even when nested")); + } else if (res == NONCE_STATIC_ENC) { PrintAndLogEx(SUCCESS, "Static enc nonce... " _RED_("yes")); } } @@ -9966,6 +10003,7 @@ static int CmdHF14AMfISEN(const char *Cmd) { } if (collect_fm11rf08s) { + uint64_t t1 = msclock(); uint32_t flags = collect_fm11rf08s_with_data; SendCommandMIX(CMD_HF_MIFARE_ACQ_STATIC_ENCRYPTED_NONCES, flags, 0, 0, key, sizeof(key)); if (WaitForResponseTimeout(CMD_HF_MIFARE_STATIC_ENCRYPTED_NONCE, &resp, 1000)) { @@ -10011,6 +10049,9 @@ static int CmdHF14AMfISEN(const char *Cmd) { } free(dump); } + t1 = msclock() - t1; + PrintAndLogEx(SUCCESS, "time: " _YELLOW_("%" PRIu64) " ms", t1); + if (fnlen == 0) { snprintf(filename, sizeof(filename), "hf-mf-%s-nonces%s", sprint_hex_inrow(card.uid, card.uidlen), collect_fm11rf08s_with_data ? "_with_data" : ""); } @@ -10032,11 +10073,13 @@ static int CmdHF14AMfISEN(const char *Cmd) { } int res = detect_classic_static_encrypted_nonce_ex(blockn, keytype, key, blockn_nested, keytype_nested, key_nested, nr_nested, reset, hardreset, addread, addauth, incblk2, corruptnrar, corruptnrarparity, true); - if (res == NONCE_STATIC) + if (res == NONCE_STATIC) { PrintAndLogEx(SUCCESS, "Static nonce......... " _YELLOW_("yes")); - if (res == NONCE_STATIC_ENC) + } else if (res == NONCE_SUPERSTATIC) { + PrintAndLogEx(SUCCESS, "Static nonce......... " _YELLOW_("yes, even when nested")); + } else if (res == NONCE_STATIC_ENC) { PrintAndLogEx(SUCCESS, "Static enc nonce..... " _RED_("yes")); - + } if (setDeviceDebugLevel(dbg_curr, false) != PM3_SUCCESS) { return PM3_EFAILED; } diff --git a/client/src/cmdhfmfdes.c b/client/src/cmdhfmfdes.c index dc5caa94b..245fae293 100644 --- a/client/src/cmdhfmfdes.c +++ b/client/src/cmdhfmfdes.c @@ -2005,7 +2005,7 @@ static int CmdHF14ADesSelectApp(const char *Cmd) { } uint8_t dfname[32] = {0}; - int dfnamelen = 16; + int dfnamelen = 16; // since max length is 16 chars we don't have to test for 32-1 null termination CLIGetStrWithReturn(ctx, 12, dfname, &dfnamelen); bool selectmf = arg_get_lit(ctx, 13); @@ -2614,8 +2614,8 @@ static int CmdHF14ADesCreateApp(const char *Cmd) { return PM3_EINVARG; } - uint8_t dfname[250] = {0}; - int dfnamelen = 16; + uint8_t dfname[32] = {0}; + int dfnamelen = 16; // since max length is 16 chars we don't have to test for 32-1 null termination CLIGetStrWithReturn(ctx, 14, dfname, &dfnamelen); if (dfnamelen == 0) { // no text DF Name supplied diff --git a/client/src/cmdhfmfp.c b/client/src/cmdhfmfp.c index e6fcf55f9..9d9ecb880 100644 --- a/client/src/cmdhfmfp.c +++ b/client/src/cmdhfmfp.c @@ -1914,7 +1914,7 @@ static int CmdHFMFPMAD(const char *Cmd) { if (aaid == mad[i]) { uint8_t vsector[16 * 4] = {0}; - if (mfReadSector(i + 1, keyB ? MF_KEY_B : MF_KEY_A, akey, vsector)) { + if (mf_read_sector(i + 1, keyB ? MF_KEY_B : MF_KEY_A, akey, vsector)) { PrintAndLogEx(NORMAL, ""); PrintAndLogEx(ERR, "error, read sector %d", i + 1); return PM3_ESOFT; diff --git a/client/src/cmdhfmfu.c b/client/src/cmdhfmfu.c index 154146c12..de2489dba 100644 --- a/client/src/cmdhfmfu.c +++ b/client/src/cmdhfmfu.c @@ -2396,6 +2396,7 @@ static int CmdHF14AMfUInfo(const char *Cmd) { status = ul_read(0x28, ulc_conf, sizeof(ulc_conf)); if (status <= 0) { PrintAndLogEx(ERR, "Error: tag didn't answer to READ UL-C"); + PrintAndLogEx(HINT, "Hint: tag is likely fully read protected"); DropField(); return PM3_ESOFT; } diff --git a/client/src/cmdhfthinfilm.c b/client/src/cmdhfthinfilm.c index 74a76e788..6a477e5f6 100644 --- a/client/src/cmdhfthinfilm.c +++ b/client/src/cmdhfthinfilm.c @@ -125,7 +125,7 @@ int infoThinFilm(bool verbose) { SendCommandNG(CMD_HF_THINFILM_READ, NULL, 0); PacketResponseNG resp; - if (!WaitForResponseTimeout(CMD_HF_THINFILM_READ, &resp, 1500)) { + if (WaitForResponseTimeout(CMD_HF_THINFILM_READ, &resp, 1500) == false) { PrintAndLogEx(WARNING, "timeout while waiting for reply."); return PM3_ETIMEOUT; } @@ -186,9 +186,16 @@ int CmdHfThinFilmSim(const char *Cmd) { int ret; while (!(ret = kbd_enter_pressed())) { - if (WaitForResponseTimeout(CMD_HF_THINFILM_SIMULATE, &resp, 500) == 0) continue; - if (resp.status != PM3_SUCCESS) break; + + if (WaitForResponseTimeout(CMD_HF_THINFILM_SIMULATE, &resp, 500) == 0) { + continue; + } + + if (resp.status != PM3_SUCCESS) { + break; + } } + if (ret) { PrintAndLogEx(INFO, "Client side interrupted"); PrintAndLogEx(WARNING, "Simulation still running on Proxmark3 till next command or button press"); diff --git a/client/src/cmdlf.c b/client/src/cmdlf.c index a5fd678c2..b2f8ba20c 100644 --- a/client/src/cmdlf.c +++ b/client/src/cmdlf.c @@ -288,10 +288,12 @@ int CmdLFCommandRead(const char *Cmd) { uint16_t period_1 = arg_get_u32_def(ctx, 4, 0); uint16_t period_0 = arg_get_u32_def(ctx, 5, 0); uint32_t samples = arg_get_u32_def(ctx, 6, 0); + bool verbose = arg_get_lit(ctx, 7); bool keep_field_on = arg_get_lit(ctx, 8); bool add_crc_ht = arg_get_lit(ctx, 9); bool cm = arg_get_lit(ctx, 10); + CLIParserFree(ctx); if (g_session.pm3_present == false) { @@ -1124,9 +1126,10 @@ int CmdLFfskSim(const char *Cmd) { uint8_t fchigh = arg_get_u32_def(ctx, 3, 0); bool separator = arg_get_lit(ctx, 4); - int raw_len = 64; - char raw[64] = {0}; + char raw[65] = {0}; + int raw_len = sizeof(raw) - 1; // CLIGetStrWithReturn does not guarantee string to be null-terminated CLIGetStrWithReturn(ctx, 5, (uint8_t *)raw, &raw_len); + bool verbose = arg_get_lit(ctx, 6); CLIParserFree(ctx); @@ -1234,9 +1237,10 @@ int CmdLFaskSim(const char *Cmd) { bool use_ar = arg_get_lit(ctx, 5); bool separator = arg_get_lit(ctx, 6); - int raw_len = 64; - char raw[64] = {0}; + char raw[65] = {0}; + int raw_len = sizeof(raw) - 1; // CLIGetStrWithReturn does not guarantee string to be null-terminated CLIGetStrWithReturn(ctx, 7, (uint8_t *)raw, &raw_len); + bool verbose = arg_get_lit(ctx, 8); CLIParserFree(ctx); @@ -1336,17 +1340,22 @@ int CmdLFpskSim(const char *Cmd) { arg_lit0("v", "verbose", "verbose output"), arg_param_end }; + CLIExecWithReturn(ctx, Cmd, argtable, true); + bool use_psk1 = arg_get_lit(ctx, 1); bool use_psk2 = arg_get_lit(ctx, 2); bool use_psk3 = arg_get_lit(ctx, 3); bool invert = arg_get_lit(ctx, 4); + uint8_t clk = arg_get_u32_def(ctx, 5, 0); uint8_t carrier = arg_get_u32_def(ctx, 6, 2); - int raw_len = 64; - char raw[64] = {0}; + + char raw[65] = {0}; + int raw_len = sizeof(raw) - 1; // CLIGetStrWithReturn does not guarantee string to be null-terminated CLIGetStrWithReturn(ctx, 7, (uint8_t *)raw, &raw_len); bool verbose = arg_get_lit(ctx, 8); + CLIParserFree(ctx); if ((use_psk1 + use_psk2 + use_psk3) > 1) { @@ -1588,7 +1597,7 @@ static bool check_chiptype(bool getDeviceData) { // Hitag S if (read_hts_uid() == PM3_SUCCESS) { - PrintAndLogEx(SUCCESS, "Chipset detection: " _GREEN_("Hitag S / 82xx")); + PrintAndLogEx(SUCCESS, "Chipset detection: " _GREEN_("Hitag 1/S / 82xx")); PrintAndLogEx(HINT, "Hint: try " _YELLOW_("`lf hitag hts`") " commands"); retval = true; goto out; diff --git a/client/src/cmdlfem410x.c b/client/src/cmdlfem410x.c index 1acebd6ab..ebcd9ee8c 100644 --- a/client/src/cmdlfem410x.c +++ b/client/src/cmdlfem410x.c @@ -364,8 +364,8 @@ static int CmdEM410xDemod(const char *Cmd) { size_t max_len = arg_get_u32_def(ctx, 3, 0); bool invert = arg_get_lit(ctx, 4); bool amplify = arg_get_lit(ctx, 5); - int bin_len = 512; uint8_t bin[512] = {0}; + int bin_len = sizeof(bin) - 1; // CLIGetStrWithReturn does not guarantee string to be null-terminated CLIGetStrWithReturn(ctx, 6, bin, &bin_len); CLIParserFree(ctx); diff --git a/client/src/cmdlfhid.c b/client/src/cmdlfhid.c index 204505972..4ad3cbc66 100644 --- a/client/src/cmdlfhid.c +++ b/client/src/cmdlfhid.c @@ -376,9 +376,10 @@ static int CmdHIDClone(const char *Cmd) { bool q5 = arg_get_lit(ctx, 7); bool em = arg_get_lit(ctx, 8); - // TODO: very confusing sizes... buf of 70, parser len to 63 instead of 70-1, tests for len > 127, loop with 96... - int bin_len = 63; - uint8_t bin[70] = {0}; + // t5577 can do 6 blocks with 32bits == 192 bits, HID is manchester encoded and doubles in length. + // With parity, manchester and preamble we have about 3 blocks to play with. Ie: 96 bits + uint8_t bin[97] = {0}; + int bin_len = sizeof(bin) - 1; // CLIGetStrWithReturn does not guarantee string to be null-terminated CLIGetStrWithReturn(ctx, 9, bin, &bin_len); CLIParserFree(ctx); @@ -387,8 +388,8 @@ static int CmdHIDClone(const char *Cmd) { return PM3_EINVARG; } - if (bin_len > 127) { - PrintAndLogEx(ERR, "Binary wiegand string must be less than 128 bits"); + if (bin_len > 96) { + PrintAndLogEx(ERR, "Binary wiegand string must be less than 96 bits"); return PM3_EINVARG; } diff --git a/client/src/cmdlfhitaghts.c b/client/src/cmdlfhitaghts.c index e72d7194f..d1af178dd 100644 --- a/client/src/cmdlfhitaghts.c +++ b/client/src/cmdlfhitaghts.c @@ -147,6 +147,13 @@ static int process_hitags_common_args(CLIParserContext *ctx, lf_hitag_data_t *co return PM3_EINVARG; } + uint8_t mode = arg_get_int_def(ctx, 5, 3); + + if (mode > 3) { + PrintAndLogEx(WARNING, "Wrong response protocol mode, expected 0, 1, 2 or 3, got %d", mode); + return PM3_EINVARG; + } + // complete options switch (key_len) { case HITAG_PASSWORD_SIZE: @@ -194,6 +201,21 @@ static int process_hitags_common_args(CLIParserContext *ctx, lf_hitag_data_t *co PrintAndLogEx(INFO, "Authenticating to " _YELLOW_("Hitag S") " in Crypto mode"); } + switch (mode) { + case 0: + packet->mode = HITAGS_UID_REQ_STD; + break; + case 1: + packet->mode = HITAGS_UID_REQ_ADV1; + break; + case 2: + packet->mode = HITAGS_UID_REQ_ADV2; + break; + default: + packet->mode = HITAGS_UID_REQ_FADV; + break; + } + return PM3_SUCCESS; } @@ -226,6 +248,9 @@ static void print_error(int8_t reason) { case -10: PrintAndLogEx(FAILED, "Write to page failed!"); break; + case -11: + PrintAndLogEx(FAILED, "Read page failed!"); + break; default: // PM3_REASON_UNKNOWN PrintAndLogEx(DEBUG, "DEBUG: Error - Hitag S failed"); @@ -254,8 +279,9 @@ static int CmdLFHitagSRead(const char *Cmd) { arg_str0(NULL, "nrar", "", "nonce / answer writer, 8 hex bytes"), arg_lit0(NULL, "crypto", "crypto mode"), arg_str0("k", "key", "", "pwd or key, 4 or 6 hex bytes"), + arg_int0("m", "mode", "", "response protocol mode. 0 (Standard 00110), 1 (Advanced 11000), 2 (Advanced 11001), 3 (Fast Advanced 11010) (def: 3)"), arg_int0("p", "page", "", "page address to read from"), - arg_int0("c", "count", "", "how many pages to read. '0' reads all pages up to the end page (default: 1)"), + arg_int0("c", "count", "", "how many pages to read. '0' reads all pages up to the end page (def: 1)"), arg_param_end }; CLIExecWithReturn(ctx, Cmd, argtable, true); @@ -264,14 +290,14 @@ static int CmdLFHitagSRead(const char *Cmd) { if (process_hitags_common_args(ctx, &packet) < 0) return PM3_EINVARG; - uint32_t page = arg_get_int_def(ctx, 5, 0); + uint32_t page = arg_get_int_def(ctx, 6, 0); if (page > 255) { PrintAndLogEx(WARNING, "Page address Invalid."); return PM3_EINVARG; } - uint32_t count = arg_get_int_def(ctx, 6, 1); + uint32_t count = arg_get_int_def(ctx, 7, 1); if (count > HITAGS_MAX_PAGES) { PrintAndLogEx(WARNING, "No more than 64 pages can be read at once."); @@ -404,8 +430,10 @@ static int CmdLFHitagSRead(const char *Cmd) { PrintAndLogEx(NORMAL, "Key"); } else PrintAndLogEx(NORMAL, "Data"); - } else - PrintAndLogEx(INFO, "%02u | -- -- -- -- | read failed reason: " _YELLOW_("%d"), page_addr, card->pages_reason[i]); + } else { + PrintAndLogEx(INFO, "% 3u | -- -- -- -- | .... | N/A | " NOLF, page_addr); + print_error(card->pages_reason[i]); + } } PrintAndLogEx(INFO, "----+-------------+-------+------+------"); @@ -438,6 +466,7 @@ static int CmdLFHitagSWrite(const char *Cmd) { arg_str0(NULL, "nrar", "", "nonce / answer writer, 8 hex bytes"), arg_lit0(NULL, "crypto", "crypto mode"), arg_str0("k", "key", "", "pwd or key, 4 or 6 hex bytes"), + arg_int0("m", "mode", "", "response protocol mode. 0 (Standard 00110), 1 (Advanced 11000), 2 (Advanced 11001), 3 (Fast Advanced 11010) (def: 3)"), arg_int1("p", "page", "", "page address to write to"), arg_str1("d", "data", "", "data, 4 hex bytes"), arg_param_end @@ -448,12 +477,12 @@ static int CmdLFHitagSWrite(const char *Cmd) { if (process_hitags_common_args(ctx, &packet) < 0) return PM3_EINVARG; - int page = arg_get_int_def(ctx, 5, 0); + int page = arg_get_int_def(ctx, 6, 0); uint8_t data[HITAGS_PAGE_SIZE]; int data_len = 0; - int res = CLIParamHexToBuf(arg_get_str(ctx, 6), data, HITAGS_PAGE_SIZE, &data_len); + int res = CLIParamHexToBuf(arg_get_str(ctx, 7), data, HITAGS_PAGE_SIZE, &data_len); if (res != 0) { CLIParserFree(ctx); return PM3_EINVARG; @@ -538,7 +567,7 @@ static int CmdLFHitagSSim(const char *Cmd) { CLIParserFree(ctx); clearCommandBuffer(); - SendCommandNG(CMD_LF_HITAGS_SIMULATE, NULL, 0); + SendCommandMIX(CMD_LF_HITAGS_SIMULATE, false, 0, 0, NULL, 0); return PM3_SUCCESS; } @@ -602,4 +631,3 @@ int CmdLFHitagS(const char *Cmd) { clearCommandBuffer(); return CmdsParse(CommandTable, Cmd); } - diff --git a/client/src/cmdlfpac.c b/client/src/cmdlfpac.c index 0ba92534b..46c81876c 100644 --- a/client/src/cmdlfpac.c +++ b/client/src/cmdlfpac.c @@ -236,7 +236,7 @@ static int CmdPacClone(const char *Cmd) { }; CLIExecWithReturn(ctx, Cmd, argtable, false); - uint8_t cnstr[9] = {0}; + uint8_t cnstr[10] = {0}; int cnlen = sizeof(cnstr) - 1; // CLIGetStrWithReturn does not guarantee string to be null-terminated memset(cnstr, 0x00, sizeof(cnstr)); CLIGetStrWithReturn(ctx, 1, cnstr, &cnlen); diff --git a/client/src/cmdlft55xx.c b/client/src/cmdlft55xx.c index 9233ec660..48c2fa7a7 100644 --- a/client/src/cmdlft55xx.c +++ b/client/src/cmdlft55xx.c @@ -1952,7 +1952,7 @@ static int CmdT55xxDangerousRaw(const char *Cmd) { ng.bitlen = 0; memset(ng.data, 0x00, sizeof(ng.data)); - uint8_t bin[128] = {0}; + uint8_t bin[129] = {0}; int bin_len = sizeof(bin) - 1; // CLIGetStrWithReturn does not guarantee string to be null-terminated CLIGetStrWithReturn(ctx, 1, bin, &bin_len); diff --git a/client/src/cmdpiv.c b/client/src/cmdpiv.c index 8264c5936..4c297124b 100644 --- a/client/src/cmdpiv.c +++ b/client/src/cmdpiv.c @@ -107,6 +107,7 @@ enum piv_tag_t { PIV_TAG_GUID, PIV_TAG_CERT, PIV_TAG_FASCN, + PIV_TAG_INTARRAY, }; struct piv_tag { @@ -145,6 +146,14 @@ static const struct piv_tag_enum PIV_CERT_INFO[] = { PIV_ENUM_FINISH, }; +static const char *PIV_EXTLEN_INFO[] = { + "Max command len w/o secure messaging", + "Max response len w/o secure messaging", + "Max command len w/ secure messaging", + "Max response len w/ secure messaging", + NULL +}; + static const struct piv_tag piv_tags[] = { { 0x00, "Unknown ???", PIV_TAG_HEXDUMP, NULL }, { 0x01, "Name", PIV_TAG_PRINTSTR, NULL }, @@ -179,6 +188,7 @@ static const struct piv_tag piv_tags[] = { { 0x79, "Coexistent tag allocation authority", PIV_TAG_HEXDUMP, NULL }, { 0x7f21, "Intermediate CVC", PIV_TAG_HEXDUMP, NULL }, { 0x7f60, "Biometric Information Template", PIV_TAG_GENERIC, NULL }, + { 0x7f66, "Extended length buffer information", PIV_TAG_INTARRAY, PIV_EXTLEN_INFO }, { 0x80, "Cryptographic algorithm identifier", PIV_TAG_ENUM, PIV_CRYPTO_ALG }, @@ -323,12 +333,12 @@ static void piv_tag_dump_enum(const struct tlv *tlv, const struct piv_tag *tag, const struct piv_tag_enum *values = tag->data; for (size_t i = 0; values[i].name != NULL; i++) { if (values[i].value == tlv->value[0]) { - PrintAndLogEx(NORMAL, " %u - '" _YELLOW_("%s")"'", + PrintAndLogEx(NORMAL, " %" PRIu8 " - '" _YELLOW_("%s")"'", tlv->value[0], values[i].name); return; } } - PrintAndLogEx(NORMAL, " %u - " _RED_("Unknown??"), tlv->value[0]); + PrintAndLogEx(NORMAL, " %" PRIu8 " - " _RED_("Unknown??"), tlv->value[0]); } static void piv_tag_dump_tlv(const struct tlv *tlv, const struct piv_tag *tag, int level) { @@ -352,6 +362,47 @@ static void piv_tag_dump_tlv(const struct tlv *tlv, const struct piv_tag *tag, i } +static void piv_tag_dump_int_array(const struct tlv *tlv, const struct piv_tag *tag, int level) { + int index = 0; + + const char **labels = (const char **) tag->data; + const unsigned char *buf = tlv->value; + size_t left = tlv->len; + int max_labels = 0; + + while (labels[max_labels]) { + max_labels++; + } + + while (left) { + struct tlv sub_tlv; + unsigned long v = 0; + if (!tlv_parse_tl(&buf, &left, &sub_tlv)) { + PrintAndLogEx(INFO, "%*sInvalid Tag-Len", (level * 4), " "); + continue; + } + sub_tlv.value = buf; + if (index < max_labels) { + PrintAndLogEx(INFO, "%*s--%2" PRIx32 "[%02z" PRIx32 "] '%s':" NOLF, (level * 4), " ", sub_tlv.tag, sub_tlv.len, labels[index]); + } else { + PrintAndLogEx(INFO, "%*s--%2" PRIx32 "[%02z" PRIx32 "] 'Unknown item index %" PRId32 "':" NOLF, (level * 4), " ", sub_tlv.tag, sub_tlv.len, index); + } + if (sub_tlv.len <= sizeof(v)) { + // We have enough space to convert to integer + for (int i = 0; i < sub_tlv.len; i++) { + v = (v << 8) + sub_tlv.value[i]; + } + PrintAndLogEx(NORMAL, _YELLOW_("%" PRIu64) " bytes (" _YELLOW_("%" PRIx64) ")", v, v); + } else { + // Number is to big. Just print hex value + PrintAndLogEx(NORMAL, _YELLOW_("0x%s"), sprint_hex_inrow(sub_tlv.value, sub_tlv.len)); + } + buf += sub_tlv.len; + left -= sub_tlv.len; + index++; + } +} + static void piv_print_cert(const uint8_t *buf, const size_t len, int level) { char prefix[256] = {0}; PrintAndLogEx(NORMAL, ""); @@ -415,7 +466,7 @@ static void piv_print_fascn(const uint8_t *buf, const size_t len, int level) { PrintAndLogEx(NORMAL, "%s" NOLF, encoded[tmp & 0x1f]); } uint8_t lrc = buf[24] & 0x1f; - PrintAndLogEx(NORMAL, " LRC=[" _YELLOW_("%02x") "]", lrc); + PrintAndLogEx(NORMAL, " LRC=[" _YELLOW_("%02" PRIx8) "]", lrc); } static bool piv_tag_dump(const struct tlv *tlv, int level) { @@ -426,7 +477,7 @@ static bool piv_tag_dump(const struct tlv *tlv, int level) { const struct piv_tag *tag = piv_get_tag(tlv); - PrintAndLogEx(INFO, "%*s--%2x[%02zx] '%s':" NOLF, (level * 4), " ", tlv->tag, tlv->len, tag->name); + PrintAndLogEx(INFO, "%*s--%2" PRIu32 "[%02z" PRIx32 "] '%s':" NOLF, (level * 4), " ", tlv->tag, tlv->len, tag->name); switch (tag->type) { case PIV_TAG_GENERIC: @@ -440,7 +491,7 @@ static bool piv_tag_dump(const struct tlv *tlv, int level) { PrintAndLogEx(NORMAL, " '" _YELLOW_("%s")"'", sprint_hex_inrow(tlv->value, tlv->len)); break; case PIV_TAG_NUMERIC: - PrintAndLogEx(NORMAL, " " _YELLOW_("%lu"), piv_value_numeric(tlv, 0, tlv->len * 2)); + PrintAndLogEx(NORMAL, " " _YELLOW_("%" PRIu64), piv_value_numeric(tlv, 0, tlv->len * 2)); break; case PIV_TAG_YYYYMMDD: piv_tag_dump_yyyymmdd(tlv, tag, level); @@ -454,7 +505,7 @@ static bool piv_tag_dump(const struct tlv *tlv, int level) { break; case PIV_TAG_PRINTSTR: PrintAndLogEx(NORMAL, " '" NOLF); - for (size_t i = 0; i < tlv->len; i++) { + for (size_t i = 0; i < tlv->len && tlv->value[i]; i++) { PrintAndLogEx(NORMAL, _YELLOW_("%c") NOLF, tlv->value[i]); } PrintAndLogEx(NORMAL, "'"); @@ -465,9 +516,9 @@ static bool piv_tag_dump(const struct tlv *tlv, int level) { } else { struct guid guid = {0}; parse_guid(tlv->value, &guid); - PrintAndLogEx(NORMAL, " " _YELLOW_("{%08x-%04x-%04x-") NOLF, guid.part1, guid.part2, guid.part3); + PrintAndLogEx(NORMAL, " " _YELLOW_("{%08" PRIx32 "-%04" PRIx16 "-%04" PRIx16 "-") NOLF, guid.part1, guid.part2, guid.part3); for (size_t i = 0; i < 8; i++) { - PrintAndLogEx(NORMAL, _YELLOW_("%02x") NOLF, guid.data[i]); + PrintAndLogEx(NORMAL, _YELLOW_("%02" PRIx8) NOLF, guid.data[i]); } PrintAndLogEx(NORMAL, _YELLOW_("}")); } @@ -481,6 +532,9 @@ static bool piv_tag_dump(const struct tlv *tlv, int level) { piv_print_fascn(tlv->value, tlv->len, level + 2); } break; + case PIV_TAG_INTARRAY: + piv_tag_dump_int_array(tlv, tag, level + 2); + break; }; return true; @@ -627,7 +681,7 @@ static int PivGetDataByCidAndPrint(Iso7816CommandChannel channel, const struct p break; default: if (verbose == true) { - PrintAndLogEx(INFO, "APDU response status: %04x - %s", sw, GetAPDUCodeDescription(sw >> 8, sw & 0xff)); + PrintAndLogEx(INFO, "APDU response status: %04" PRIx16 " - %s", sw, GetAPDUCodeDescription(sw >> 8, sw & 0xff)); } break; } @@ -655,7 +709,7 @@ static int PivAuthenticateSign(Iso7816CommandChannel channel, uint8_t alg_id, ui const size_t MAX_NONCE_LEN = 0x7a; if (nonce_len > MAX_NONCE_LEN) { if (verbose == true) { - PrintAndLogEx(WARNING, "Nonce cannot exceed %zu bytes. Got %zu bytes.", MAX_NONCE_LEN, nonce_len); + PrintAndLogEx(WARNING, "Nonce cannot exceed %" PRIu64 " bytes. Got %" PRIu64 " bytes.", MAX_NONCE_LEN, nonce_len); } return PM3_EINVARG; } @@ -671,12 +725,12 @@ static int PivAuthenticateSign(Iso7816CommandChannel channel, uint8_t alg_id, ui size_t len = 0; int res = Iso7816ExchangeEx(channel, false, true, apdu, false, 0, buf, APDU_RES_LEN, &len, &sw); if (res != PM3_SUCCESS) { - PrintAndLogEx(FAILED, "Sending APDU failed with code %d", res); + PrintAndLogEx(FAILED, "Sending APDU failed with code %" PRId32, res); return res; } if (sw != ISO7816_OK) { if (verbose == true) { - PrintAndLogEx(INFO, "Unexpected APDU response status: %04x - %s", sw, GetAPDUCodeDescription(sw >> 8, sw & 0xff)); + PrintAndLogEx(INFO, "Unexpected APDU response status: %04" PRIx16 " - %s", sw, GetAPDUCodeDescription(sw >> 8, sw & 0xff)); } return PM3_EFAILED; } @@ -697,7 +751,7 @@ static int PivSelect(Iso7816CommandChannel channel, bool activateField, bool lea int res = Iso7816Select(channel, activateField, leaveFieldOn, applet, appletLen, buf, sizeof(buf), &len, &sw); if ((sw != 0) && (silent == false)) { - PrintAndLogEx(INFO, "APDU response status: %04x - %s", sw, GetAPDUCodeDescription(sw >> 8, sw & 0xff)); + PrintAndLogEx(INFO, "APDU response status: %04" PRIx16 " - %s", sw, GetAPDUCodeDescription(sw >> 8, sw & 0xff)); } if (res != PM3_SUCCESS || sw != ISO7816_OK) { @@ -804,7 +858,7 @@ static int CmdPIVGetData(const char *Cmd) { CLIParserFree(ctx); if ((tag_len < 1) || (tag_len > 3)) { - PrintAndLogEx(WARNING, "Tag should be between 1 and 3 bytes. Got %i", tag_len); + PrintAndLogEx(WARNING, "Tag should be between 1 and 3 bytes. Got %" PRIi32, tag_len); return PM3_EINVARG; } @@ -843,7 +897,7 @@ static int CmdPIVAuthenticateSign(const char *Cmd) { arg_str0(NULL, "aid", "", "Applet ID to select. By default A0000003080000100 will be used"), arg_str1(NULL, "nonce", "", "Nonce to sign."), arg_int0(NULL, "slot", "", "Slot number. Default will be 0x9E (card auth cert)."), - arg_int0(NULL, "alg", "", "Algorithm to use to sign. Example values: 06=RSA-1024, 07=RSA-2048, 11=ECC-P256 (default), 14=ECC-P384"), + arg_int0(NULL, "alg", "", "Algorithm to use to sign. Example values: 06=RSA-1024, 07=RSA-2048, 17=ECC-P256 (default), 20=ECC-P384"), arg_param_end }; CLIExecWithReturn(ctx, Cmd, argtable, true); diff --git a/client/src/cmdscript.c b/client/src/cmdscript.c index 8929cea68..cc1d4000a 100644 --- a/client/src/cmdscript.c +++ b/client/src/cmdscript.c @@ -440,6 +440,7 @@ static int CmdScriptRun(const char *Cmd) { Py_Initialize(); #else PyConfig py_conf; + PyStatus status; // We need to use Python mode instead of isolated to avoid breaking stuff. PyConfig_InitPythonConfig(&py_conf); // Let's still make things bit safer by being as close as possible to isolated mode. @@ -465,16 +466,38 @@ static int CmdScriptRun(const char *Cmd) { PySys_SetArgv(argc + 1, py_args); #else // The following line will implicitly pre-initialize Python - PyConfig_SetBytesArgv(&py_conf, argc + 1, argv); - + status = PyConfig_SetBytesArgv(&py_conf, argc + 1, argv); + if (PyStatus_Exception(status)) { + goto pyexception; + } // We disallowed in py_conf environment variables interfering with python interpreter's behavior. // Let's manually enable the ones we truly need. - // This is required by Proxspace to work with an isolated Python configuration - PyConfig_SetBytesString(&py_conf, &py_conf.home, getenv("PYTHONHOME")); + const char *virtual_env = getenv("VIRTUAL_ENV"); + if (virtual_env != NULL) { + size_t length = strlen(virtual_env) + strlen("/bin/python3") + 1; + char python_executable_path[length]; + snprintf(python_executable_path, length, "%s/bin/python3", virtual_env); + status = PyConfig_SetBytesString(&py_conf, &py_conf.executable, python_executable_path); + if (PyStatus_Exception(status)) { + goto pyexception; + } + } else { + // This is required by Proxspace to work with an isolated Python configuration + status = PyConfig_SetBytesString(&py_conf, &py_conf.home, getenv("PYTHONHOME")); + if (PyStatus_Exception(status)) { + goto pyexception; + } + } // This is required for allowing `import pm3` in python scripts - PyConfig_SetBytesString(&py_conf, &py_conf.pythonpath_env, getenv("PYTHONPATH")); + status = PyConfig_SetBytesString(&py_conf, &py_conf.pythonpath_env, getenv("PYTHONPATH")); + if (PyStatus_Exception(status)) { + goto pyexception; + } - Py_InitializeFromConfig(&py_conf); + status = Py_InitializeFromConfig(&py_conf); + if (PyStatus_Exception(status)) { + goto pyexception; + } // clean up PyConfig_Clear(&py_conf); @@ -508,6 +531,18 @@ static int CmdScriptRun(const char *Cmd) { PrintAndLogEx(SUCCESS, "\nfinished " _YELLOW_("%s"), filename); return PM3_SUCCESS; } + +#if PY_MAJOR_VERSION == 3 && PY_MINOR_VERSION >= 10 +pyexception: + PyConfig_Clear(&py_conf); + if (PyStatus_IsExit(status)) { + PrintAndLogEx(WARNING, "\nPython initialization failed with exitcode=%i", status.exitcode); + } + if (PyStatus_IsError(status)) { + PrintAndLogEx(WARNING, "\nPython initialization failed with exception: %s", status.err_msg); + } + return PM3_ESOFT; +#endif } #endif diff --git a/client/src/cmdsmartcard.c b/client/src/cmdsmartcard.c index a6a07c4c6..04b92a8f6 100644 --- a/client/src/cmdsmartcard.c +++ b/client/src/cmdsmartcard.c @@ -1259,7 +1259,7 @@ static int CmdPCSC(const char *Cmd) { strcpy((char *) host, "localhost"); } - uint8_t port[6] = {0}; + uint8_t port[7] = {0}; int portLen = sizeof(port) - 1; // CLIGetStrWithReturn does not guarantee string to be null-terminated CLIGetStrWithReturn(ctx, 2, port, &portLen); if (portLen == 0) { diff --git a/client/src/cmdtrace.c b/client/src/cmdtrace.c index 56e9136cc..8e19c3bb5 100644 --- a/client/src/cmdtrace.c +++ b/client/src/cmdtrace.c @@ -1548,6 +1548,7 @@ int CmdTraceList(const char *Cmd) { tracepos = printTraceLine(tracepos, gs_traceLen, gs_trace, protocol, show_wait_cycles, mark_crc, prev_EOT, use_us, dicKeys, dicKeysCount); if (kbd_enter_pressed()) { + PrintAndLogEx(INFO, "User interrupted detected. Aborting"); break; } } diff --git a/client/src/emv/cmdemv.c b/client/src/emv/cmdemv.c index 1babce4d2..8c8c85d0b 100644 --- a/client/src/emv/cmdemv.c +++ b/client/src/emv/cmdemv.c @@ -2144,9 +2144,9 @@ static int CmdEMVScan(const char *Cmd) { uint8_t psenum = (channel == CC_CONTACT) ? 1 : 2; - uint8_t filename[FILE_PATH_SIZE] = {0}; - int filenamelen = sizeof(filename) - 1; // CLIGetStrWithReturn does not guarantee string to be null-terminated - CLIGetStrWithReturn(ctx, 12, filename, &filenamelen); + char filename[FILE_PATH_SIZE] = {0}; + int fnlen = 0; + CLIParamStrToBuf(arg_get_str(ctx, 12), (uint8_t *)filename, FILE_PATH_SIZE, &fnlen); CLIParserFree(ctx); diff --git a/client/src/fileutils.c b/client/src/fileutils.c index 481c872cc..8422efd79 100644 --- a/client/src/fileutils.c +++ b/client/src/fileutils.c @@ -3085,7 +3085,7 @@ int pm3_load_dump(const char *fn, void **pdump, size_t *dumplen, size_t maxdumpl case FLIPPER: { nfc_df_e dumptype; res = detect_nfc_dump_format(fn, &dumptype, true); - if (res != SUCCESS) { + if (res != PM3_SUCCESS) { break; } diff --git a/client/src/loclass/ikeys.c b/client/src/loclass/ikeys.c index 9458f13ce..44b9234ee 100644 --- a/client/src/loclass/ikeys.c +++ b/client/src/loclass/ikeys.c @@ -562,7 +562,15 @@ void invert_hash0(uint8_t k[8]) { // Create new forks by duplicating existing uint64_t values int new_head = heads_count * 2; - hydra_heads = (uint64_t *)realloc(hydra_heads, new_head * sizeof(uint64_t)); + + // proper realloc pattern + uint64_t *ptmp = (uint64_t *)realloc(hydra_heads, new_head * sizeof(uint64_t)); + if (ptmp == NULL) { + PrintAndLogEx(FAILED, "failed to allocate memory"); + free(hydra_heads); + return; + } + hydra_heads = ptmp; // Duplicate all current values and add the value to both original and new ones for (int i = 0; i < heads_count; i++) { diff --git a/client/src/mifare/mifarehost.c b/client/src/mifare/mifarehost.c index ebe119a78..7048173b4 100644 --- a/client/src/mifare/mifarehost.c +++ b/client/src/mifare/mifarehost.c @@ -44,7 +44,7 @@ #include "gen4.h" #include "parity.h" -int mfDarkside(uint8_t blockno, uint8_t key_type, uint64_t *key) { +int mf_dark_side(uint8_t blockno, uint8_t key_type, uint64_t *key) { uint32_t uid = 0; uint32_t nt = 0, nr = 0, ar = 0; uint64_t par_list = 0, ks_list = 0; @@ -189,7 +189,7 @@ int mfDarkside(uint8_t blockno, uint8_t key_type, uint64_t *key) { } } - if (mfCheckKeys(blockno, key_type - 0x60, false, size, keyBlock, key) == PM3_SUCCESS) { + if (mf_check_keys(blockno, key_type - 0x60, false, size, keyBlock, key) == PM3_SUCCESS) { break; } } @@ -208,7 +208,7 @@ int mfDarkside(uint8_t blockno, uint8_t key_type, uint64_t *key) { return PM3_SUCCESS; } -int mfCheckKeys(uint8_t blockNo, uint8_t keyType, bool clear_trace, uint8_t keycnt, uint8_t *keyBlock, uint64_t *key) { +int mf_check_keys(uint8_t blockNo, uint8_t keyType, bool clear_trace, uint8_t keycnt, uint8_t *keyBlock, uint64_t *key) { if (key) { *key = -1; } @@ -249,9 +249,9 @@ int mfCheckKeys(uint8_t blockNo, uint8_t keyType, bool clear_trace, uint8_t keyc // 0 == ok all keys found // 1 == // 2 == Time-out, aborting -int mfCheckKeys_fast_ex(uint8_t sectorsCnt, uint8_t firstChunk, uint8_t lastChunk, uint8_t strategy, - uint32_t size, uint8_t *keyBlock, sector_t *e_sector, bool use_flashmemory, - bool verbose, bool quiet, uint16_t singleSectorParams) { +int mf_check_keys_fast_ex(uint8_t sectorsCnt, uint8_t firstChunk, uint8_t lastChunk, uint8_t strategy, + uint32_t size, uint8_t *keyBlock, sector_t *e_sector, bool use_flashmemory, + bool verbose, bool quiet, uint16_t singleSectorParams) { uint64_t t2 = msclock(); @@ -357,15 +357,15 @@ int mfCheckKeys_fast_ex(uint8_t sectorsCnt, uint8_t firstChunk, uint8_t lastChun return PM3_ESOFT; } -int mfCheckKeys_fast(uint8_t sectorsCnt, uint8_t firstChunk, uint8_t lastChunk, uint8_t strategy, - uint32_t size, uint8_t *keyBlock, sector_t *e_sector, bool use_flashmemory, bool verbose) { - return mfCheckKeys_fast_ex(sectorsCnt, firstChunk, lastChunk, strategy, size, keyBlock, e_sector, use_flashmemory, verbose, false, 0); +int mf_check_keys_fast(uint8_t sectorsCnt, uint8_t firstChunk, uint8_t lastChunk, uint8_t strategy, + uint32_t size, uint8_t *keyBlock, sector_t *e_sector, bool use_flashmemory, bool verbose) { + return mf_check_keys_fast_ex(sectorsCnt, firstChunk, lastChunk, strategy, size, keyBlock, e_sector, use_flashmemory, verbose, false, 0); } // Trigger device to use a binary file on flash mem as keylist for mfCheckKeys. // As of now, 255 keys possible in the file // 6 * 255 = 1500 bytes -int mfCheckKeys_file(uint8_t *destfn, uint64_t *key) { +int mf_check_keys_file(uint8_t *destfn, uint64_t *key) { *key = -1; clearCommandBuffer(); @@ -412,7 +412,7 @@ int mfCheckKeys_file(uint8_t *destfn, uint64_t *key) { // PM3 imp of J-Run mf_key_brute (part 2) // ref: https://github.com/J-Run/mf_key_brute -int mfKeyBrute(uint8_t blockNo, uint8_t keyType, const uint8_t *key, uint64_t *resultkey) { +int mf_key_brute(uint8_t blockNo, uint8_t keyType, const uint8_t *key, uint64_t *resultkey) { uint64_t key64; uint8_t found = false; @@ -441,7 +441,7 @@ int mfKeyBrute(uint8_t blockNo, uint8_t keyType, const uint8_t *key, uint64_t *r memcpy(keyBlock, candidates + i, KEYBLOCK_SIZE); // check a block of generated key candidates. - if (mfCheckKeys(blockNo, keyType, true, KEYS_IN_BLOCK, keyBlock, &key64) == PM3_SUCCESS) { + if (mf_check_keys(blockNo, keyType, true, KEYS_IN_BLOCK, keyBlock, &key64) == PM3_SUCCESS) { *resultkey = key64; found = true; break; @@ -483,7 +483,7 @@ __attribute__((force_align_arg_pointer)) return statelist->head.slhead; } -int mfnested(uint8_t blockNo, uint8_t keyType, uint8_t *key, uint8_t trgBlockNo, uint8_t trgKeyType, uint8_t *resultKey, bool calibrate) { +int mf_nested(uint8_t blockNo, uint8_t keyType, uint8_t *key, uint8_t trgBlockNo, uint8_t trgKeyType, uint8_t *resultKey, bool calibrate) { uint32_t uid; StateList_t statelists[2]; @@ -625,7 +625,7 @@ int mfnested(uint8_t blockNo, uint8_t keyType, uint8_t *key, uint8_t trgBlockNo, num_to_bytes(key64, 6, keyBlock + j * MIFARE_KEY_SIZE); } - if (mfCheckKeys(statelists[0].blockNo, statelists[0].keyType, false, size, keyBlock, &key64) == PM3_SUCCESS) { + if (mf_check_keys(statelists[0].blockNo, statelists[0].keyType, false, size, keyBlock, &key64) == PM3_SUCCESS) { free(statelists[0].head.slhead); free(statelists[1].head.slhead); num_to_bytes(key64, 6, resultKey); @@ -669,7 +669,7 @@ out: return PM3_ESOFT; } -int mfStaticNested(uint8_t blockNo, uint8_t keyType, uint8_t *key, uint8_t trgBlockNo, uint8_t trgKeyType, uint8_t *resultKey) { +int mf_static_nested(uint8_t blockNo, uint8_t keyType, uint8_t *key, uint8_t trgBlockNo, uint8_t trgKeyType, uint8_t *resultKey) { uint32_t uid; StateList_t statelists[2]; @@ -902,9 +902,9 @@ int mfStaticNested(uint8_t blockNo, uint8_t keyType, uint8_t *key, uint8_t trgBl free(mem); return res; } - res = mfCheckKeys_file(fn, &key64); + res = mf_check_keys_file(fn, &key64); } else { - res = mfCheckKeys(statelists[0].blockNo, statelists[0].keyType, true, chunk, mem, &key64); + res = mf_check_keys(statelists[0].blockNo, statelists[0].keyType, true, chunk, mem, &key64); } if (res == PM3_SUCCESS) { @@ -949,7 +949,7 @@ out: } // MIFARE -int mfReadSector(uint8_t sectorNo, uint8_t keyType, const uint8_t *key, uint8_t *data) { +int mf_read_sector(uint8_t sectorNo, uint8_t keyType, const uint8_t *key, uint8_t *data) { clearCommandBuffer(); SendCommandMIX(CMD_HF_MIFARE_READSC, sectorNo, keyType, 0, (uint8_t *)key, MIFARE_KEY_SIZE); @@ -970,7 +970,7 @@ int mfReadSector(uint8_t sectorNo, uint8_t keyType, const uint8_t *key, uint8_t return PM3_SUCCESS; } -int mfReadBlock(uint8_t blockNo, uint8_t keyType, const uint8_t *key, uint8_t *data) { +int mf_read_block(uint8_t blockNo, uint8_t keyType, const uint8_t *key, uint8_t *data) { mf_readblock_t payload = { .blockno = blockNo, .keytype = keyType @@ -994,8 +994,39 @@ int mfReadBlock(uint8_t blockNo, uint8_t keyType, const uint8_t *key, uint8_t *d return PM3_SUCCESS; } +int mf_write_block(uint8_t blockno, uint8_t keyType, const uint8_t *key, uint8_t *block) { + + uint8_t data[26]; + memcpy(data, key, MIFARE_KEY_SIZE); + memcpy(data + 10, block, MFBLOCK_SIZE); + + clearCommandBuffer(); + SendCommandMIX(CMD_HF_MIFARE_WRITEBL, blockno, keyType, 0, data, sizeof(data)); + PacketResponseNG resp; + if (WaitForResponseTimeout(CMD_ACK, &resp, 1500) == false) { + PrintAndLogEx(FAILED, "mfWriteBlock execution time out"); + return PM3_ETIMEOUT; + } + int res = PM3_SUCCESS; + if ((resp.oldarg[0] & 0xff) != 1) { + res = PM3_EFAILED; + } + return res; +} + +int mf_write_sector(uint8_t sectorNo, uint8_t keyType, const uint8_t *key, uint8_t *sector) { + int res; + for (int i = 0; i < mfNumBlocksPerSector(sectorNo); i++) { + res = mf_write_block((mfFirstBlockOfSector(sectorNo)) + i, keyType, key, sector + (i * MFBLOCK_SIZE)); + if (res != PM3_SUCCESS) { + return (i == 0) ? PM3_EFAILED : PM3_EPARTIAL; + } + } + return PM3_SUCCESS; +} + // EMULATOR -int mfEmlGetMem(uint8_t *data, int blockNum, int blocksCount) { +int mf_eml_get_mem(uint8_t *data, int blockNum, int blocksCount) { size_t size = blocksCount * MFBLOCK_SIZE; if (size > PM3_CMD_DATA_SIZE) { @@ -1025,11 +1056,11 @@ int mfEmlGetMem(uint8_t *data, int blockNum, int blocksCount) { return resp.status; } -int mfEmlSetMem(uint8_t *data, int blockNum, int blocksCount) { - return mfEmlSetMem_xt(data, blockNum, blocksCount, MFBLOCK_SIZE); +int mf_elm_set_mem(uint8_t *data, int blockNum, int blocksCount) { + return mf_eml_set_mem_xt(data, blockNum, blocksCount, MFBLOCK_SIZE); } -int mfEmlSetMem_xt(uint8_t *data, int blockNum, int blocksCount, int blockBtWidth) { +int mf_eml_set_mem_xt(uint8_t *data, int blockNum, int blocksCount, int blockBtWidth) { struct p { uint8_t blockno; @@ -1058,13 +1089,13 @@ int mfEmlSetMem_xt(uint8_t *data, int blockNum, int blocksCount, int blockBtWidt } // "MAGIC" CARD -int mfCSetUID(uint8_t *uid, uint8_t uidlen, const uint8_t *atqa, const uint8_t *sak, uint8_t *old_uid, uint8_t *verifed_uid, uint8_t wipecard) { +int mf_chinese_set_uid(uint8_t *uid, uint8_t uidlen, const uint8_t *atqa, const uint8_t *sak, uint8_t *old_uid, uint8_t *verifed_uid, uint8_t wipecard, uint8_t gdm) { - uint8_t params = MAGIC_SINGLE; + uint8_t params = MAGIC_SINGLE | (gdm ? MAGIC_GDM_ALT_WUPC : MAGIC_WUPC); uint8_t block0[MFBLOCK_SIZE]; memset(block0, 0x00, sizeof(block0)); - int res = mfCGetBlock(0, block0, params); + int res = mf_chinese_get_block(0, block0, params); if (res == 0) { PrintAndLogEx(SUCCESS, "old block 0... %s", sprint_hex_inrow(block0, sizeof(block0))); if (old_uid) { @@ -1109,11 +1140,11 @@ int mfCSetUID(uint8_t *uid, uint8_t uidlen, const uint8_t *atqa, const uint8_t * params |= MAGIC_WIPE; } - res = mfCSetBlock(0, block0, NULL, params); + res = mf_chinese_set_block(0, block0, NULL, params); if (res == PM3_SUCCESS) { - params = MAGIC_SINGLE; + params = MAGIC_SINGLE | MAGIC_WUPC; memset(block0, 0, sizeof(block0)); - res = mfCGetBlock(0, block0, params); + res = mf_chinese_get_block(0, block0, params); if (res == 0) { if (verifed_uid) { memcpy(verifed_uid, block0, uidlen); @@ -1123,13 +1154,13 @@ int mfCSetUID(uint8_t *uid, uint8_t uidlen, const uint8_t *atqa, const uint8_t * return res; } -int mfCWipe(uint8_t *uid, const uint8_t *atqa, const uint8_t *sak) { +int mf_chinese_wipe(uint8_t *uid, const uint8_t *atqa, const uint8_t *sak, uint8_t gdm) { uint8_t block0[MFBLOCK_SIZE] = {0x00, 0x56, 0x78, 0xBB, 0x95, 0x08, 0x04, 0x00, 0x02, 0xB2, 0x1E, 0x24, 0x23, 0x27, 0x1E, 0x1D}; // uint8_t block0[MFBLOCK_SIZE] = {0x04, 0x03, 0x02, 0x01, 0x04, 0x08, 0x04, 0x00, 0x64, 0xB9, 0x95, 0x11, 0x4D, 0x20, 0x42, 0x09}; uint8_t blockD[MFBLOCK_SIZE] = {0x00}; // default transport ACL uint8_t blockK[MFBLOCK_SIZE] = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x07, 0x80, 0x69, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}; - uint8_t params = MAGIC_SINGLE; + uint8_t params = MAGIC_SINGLE | (gdm ? MAGIC_GDM_ALT_WUPC : MAGIC_WUPC); if (uid != NULL) { memcpy(block0, uid, 4); @@ -1149,12 +1180,12 @@ int mfCWipe(uint8_t *uid, const uint8_t *atqa, const uint8_t *sak) { PrintAndLogEx(INPLACE, "wipe block %d", blockNo); if (blockNo == 0) { - res = mfCSetBlock(blockNo, block0, NULL, params); + res = mf_chinese_set_block(blockNo, block0, NULL, params); } else { if (mfIsSectorTrailer(blockNo)) - res = mfCSetBlock(blockNo, blockK, NULL, params); + res = mf_chinese_set_block(blockNo, blockK, NULL, params); else - res = mfCSetBlock(blockNo, blockD, NULL, params); + res = mf_chinese_set_block(blockNo, blockD, NULL, params); } if (res == PM3_SUCCESS) @@ -1173,7 +1204,7 @@ int mfCWipe(uint8_t *uid, const uint8_t *atqa, const uint8_t *sak) { return PM3_SUCCESS; } -int mfCSetBlock(uint8_t blockNo, uint8_t *data, uint8_t *uid, uint8_t params) { +int mf_chinese_set_block(uint8_t blockNo, uint8_t *data, uint8_t *uid, uint8_t params) { clearCommandBuffer(); SendCommandMIX(CMD_HF_MIFARE_CSETBL, params, blockNo, 0, data, MFBLOCK_SIZE); PacketResponseNG resp; @@ -1193,7 +1224,7 @@ int mfCSetBlock(uint8_t blockNo, uint8_t *data, uint8_t *uid, uint8_t params) { return PM3_SUCCESS; } -int mfCGetBlock(uint8_t blockNo, uint8_t *data, uint8_t params) { +int mf_chinese_get_block(uint8_t blockNo, uint8_t *data, uint8_t params) { clearCommandBuffer(); SendCommandMIX(CMD_HF_MIFARE_CGETBL, params, blockNo, 0, NULL, 0); PacketResponseNG resp; @@ -1210,7 +1241,7 @@ int mfCGetBlock(uint8_t blockNo, uint8_t *data, uint8_t params) { return PM3_SUCCESS; } -int mfGen3UID(uint8_t *uid, uint8_t uidlen, uint8_t *oldUid) { +int mf_chinese_gen_3_uid(uint8_t *uid, uint8_t uidlen, uint8_t *oldUid) { clearCommandBuffer(); SendCommandMIX(CMD_HF_MIFARE_GEN3UID, uidlen, 0, 0, uid, uidlen); PacketResponseNG resp; @@ -1225,7 +1256,7 @@ int mfGen3UID(uint8_t *uid, uint8_t uidlen, uint8_t *oldUid) { } } -int mfGen3Block(uint8_t *block, int blockLen, uint8_t *newBlock) { +int mf_chinese_gen_3_block(uint8_t *block, int blockLen, uint8_t *newBlock) { clearCommandBuffer(); SendCommandMIX(CMD_HF_MIFARE_GEN3BLK, blockLen, 0, 0, block, MFBLOCK_SIZE); PacketResponseNG resp; @@ -1240,7 +1271,7 @@ int mfGen3Block(uint8_t *block, int blockLen, uint8_t *newBlock) { } } -int mfGen3Freeze(void) { +int mf_chinese_gen_3_freeze(void) { clearCommandBuffer(); SendCommandNG(CMD_HF_MIFARE_GEN3FREEZ, NULL, 0); PacketResponseNG resp; @@ -1270,7 +1301,7 @@ void mf_crypto1_decrypt(struct Crypto1State *pcs, uint8_t *data, int len, bool i } } -int tryDecryptWord(uint32_t nt, uint32_t ar_enc, uint32_t at_enc, uint8_t *data, int len) { +int try_decrypt_word(uint32_t nt, uint32_t ar_enc, uint32_t at_enc, uint8_t *data, int len) { PrintAndLogEx(SUCCESS, "encrypted data... %s", sprint_hex(data, len)); uint32_t ks2 = ar_enc ^ prng_successor(nt, 64); @@ -1605,7 +1636,7 @@ uint16_t detect_mf_magic(bool is_mfc, uint8_t key_type, uint64_t key) { bool detect_mfc_ev1_signature(void) { uint64_t key = 0; - int res = mfCheckKeys(69, MF_KEY_B, false, 1, (uint8_t *)g_mifare_signature_key_b, &key); + int res = mf_check_keys(69, MF_KEY_B, false, 1, (uint8_t *)g_mifare_signature_key_b, &key); return (res == PM3_SUCCESS); } @@ -1614,17 +1645,17 @@ int read_mfc_ev1_signature(uint8_t *signature) { return PM3_EINVARG; } uint8_t sign[32] = {0}; - int res = mfReadBlock(69, MF_KEY_B, g_mifare_signature_key_b, sign); + int res = mf_read_block(69, MF_KEY_B, g_mifare_signature_key_b, sign); if (res == PM3_SUCCESS) { - res = mfReadBlock(70, MF_KEY_B, g_mifare_signature_key_b, sign + 16); + res = mf_read_block(70, MF_KEY_B, g_mifare_signature_key_b, sign + 16); if (res == PM3_SUCCESS) { memcpy(signature, sign, sizeof(sign)); } } else { // try QL88 - res = mfReadBlock(69, MF_KEY_B, g_mifare_ql88_signature_key_b, sign); + res = mf_read_block(69, MF_KEY_B, g_mifare_ql88_signature_key_b, sign); if (res == PM3_SUCCESS) { - res = mfReadBlock(70, MF_KEY_B, g_mifare_ql88_signature_key_b, sign + 16); + res = mf_read_block(70, MF_KEY_B, g_mifare_ql88_signature_key_b, sign + 16); if (res == PM3_SUCCESS) { memcpy(signature, sign, sizeof(sign)); } diff --git a/client/src/mifare/mifarehost.h b/client/src/mifare/mifarehost.h index 22460b170..6dc12da52 100644 --- a/client/src/mifare/mifarehost.h +++ b/client/src/mifare/mifarehost.h @@ -70,38 +70,41 @@ typedef struct { #define KEYBLOCK_SIZE (KEYS_IN_BLOCK * MIFARE_KEY_SIZE) #define CANDIDATE_SIZE (0xFFFF * MIFARE_KEY_SIZE) -int mfDarkside(uint8_t blockno, uint8_t key_type, uint64_t *key); -int mfnested(uint8_t blockNo, uint8_t keyType, uint8_t *key, uint8_t trgBlockNo, uint8_t trgKeyType, uint8_t *resultKey, bool calibrate); -int mfStaticNested(uint8_t blockNo, uint8_t keyType, uint8_t *key, uint8_t trgBlockNo, uint8_t trgKeyType, uint8_t *resultKey); -int mfCheckKeys(uint8_t blockNo, uint8_t keyType, bool clear_trace, uint8_t keycnt, uint8_t *keyBlock, uint64_t *key); -int mfCheckKeys_fast(uint8_t sectorsCnt, uint8_t firstChunk, uint8_t lastChunk, - uint8_t strategy, uint32_t size, uint8_t *keyBlock, sector_t *e_sector, - bool use_flashmemory, bool verbose); -int mfCheckKeys_fast_ex(uint8_t sectorsCnt, uint8_t firstChunk, uint8_t lastChunk, uint8_t strategy, - uint32_t size, uint8_t *keyBlock, sector_t *e_sector, bool use_flashmemory, - bool verbose, bool quiet, uint16_t singleSectorParams); +int mf_dark_side(uint8_t blockno, uint8_t key_type, uint64_t *key); +int mf_nested(uint8_t blockNo, uint8_t keyType, uint8_t *key, uint8_t trgBlockNo, uint8_t trgKeyType, uint8_t *resultKey, bool calibrate); +int mf_static_nested(uint8_t blockNo, uint8_t keyType, uint8_t *key, uint8_t trgBlockNo, uint8_t trgKeyType, uint8_t *resultKey); +int mf_check_keys(uint8_t blockNo, uint8_t keyType, bool clear_trace, uint8_t keycnt, uint8_t *keyBlock, uint64_t *key); +int mf_check_keys_fast(uint8_t sectorsCnt, uint8_t firstChunk, uint8_t lastChunk, + uint8_t strategy, uint32_t size, uint8_t *keyBlock, sector_t *e_sector, + bool use_flashmemory, bool verbose); +int mf_check_keys_fast_ex(uint8_t sectorsCnt, uint8_t firstChunk, uint8_t lastChunk, uint8_t strategy, + uint32_t size, uint8_t *keyBlock, sector_t *e_sector, bool use_flashmemory, + bool verbose, bool quiet, uint16_t singleSectorParams); -int mfCheckKeys_file(uint8_t *destfn, uint64_t *key); +int mf_check_keys_file(uint8_t *destfn, uint64_t *key); -int mfKeyBrute(uint8_t blockNo, uint8_t keyType, const uint8_t *key, uint64_t *resultkey); +int mf_key_brute(uint8_t blockNo, uint8_t keyType, const uint8_t *key, uint64_t *resultkey); -int mfReadSector(uint8_t sectorNo, uint8_t keyType, const uint8_t *key, uint8_t *data); -int mfReadBlock(uint8_t blockNo, uint8_t keyType, const uint8_t *key, uint8_t *data); +int mf_read_sector(uint8_t sectorNo, uint8_t keyType, const uint8_t *key, uint8_t *data); +int mf_read_block(uint8_t blockNo, uint8_t keyType, const uint8_t *key, uint8_t *data); -int mfEmlGetMem(uint8_t *data, int blockNum, int blocksCount); -int mfEmlSetMem(uint8_t *data, int blockNum, int blocksCount); -int mfEmlSetMem_xt(uint8_t *data, int blockNum, int blocksCount, int blockBtWidth); +int mf_write_block(uint8_t blockno, uint8_t keyType, const uint8_t *key, uint8_t *block); +int mf_write_sector(uint8_t sectorNo, uint8_t keyType, const uint8_t *key, uint8_t *sector); -int mfCSetUID(uint8_t *uid, uint8_t uidlen, const uint8_t *atqa, const uint8_t *sak, uint8_t *old_uid, uint8_t *verifed_uid, uint8_t wipecard); -int mfCWipe(uint8_t *uid, const uint8_t *atqa, const uint8_t *sak); -int mfCSetBlock(uint8_t blockNo, uint8_t *data, uint8_t *uid, uint8_t params); -int mfCGetBlock(uint8_t blockNo, uint8_t *data, uint8_t params); +int mf_eml_get_mem(uint8_t *data, int blockNum, int blocksCount); +int mf_elm_set_mem(uint8_t *data, int blockNum, int blocksCount); +int mf_eml_set_mem_xt(uint8_t *data, int blockNum, int blocksCount, int blockBtWidth); -int mfGen3UID(uint8_t *uid, uint8_t uidlen, uint8_t *oldUid); -int mfGen3Block(uint8_t *block, int blockLen, uint8_t *newBlock); -int mfGen3Freeze(void); +int mf_chinese_set_uid(uint8_t *uid, uint8_t uidlen, const uint8_t *atqa, const uint8_t *sak, uint8_t *old_uid, uint8_t *verifed_uid, uint8_t wipecard, uint8_t gdm); +int mf_chinese_wipe(uint8_t *uid, const uint8_t *atqa, const uint8_t *sak, uint8_t gdm); +int mf_chinese_set_block(uint8_t blockNo, uint8_t *data, uint8_t *uid, uint8_t params); +int mf_chinese_get_block(uint8_t blockNo, uint8_t *data, uint8_t params); -int tryDecryptWord(uint32_t nt, uint32_t ar_enc, uint32_t at_enc, uint8_t *data, int len); +int mf_chinese_gen_3_uid(uint8_t *uid, uint8_t uidlen, uint8_t *oldUid); +int mf_chinese_gen_3_block(uint8_t *block, int blockLen, uint8_t *newBlock); +int mf_chinese_gen_3_freeze(void); + +int try_decrypt_word(uint32_t nt, uint32_t ar_enc, uint32_t at_enc, uint8_t *data, int len); int detect_classic_prng(void); int detect_classic_nackbug(bool verbose); diff --git a/client/src/pm3.c b/client/src/pm3.c index 600fd07ca..ff08c8b94 100644 --- a/client/src/pm3.c +++ b/client/src/pm3.c @@ -58,12 +58,14 @@ void pm3_close(pm3_device_t *dev) { free_grabber(); } -int pm3_console(pm3_device_t *dev, const char *cmd, bool passthru) { +int pm3_console(pm3_device_t *dev, const char *cmd, bool capture, bool quiet) { // For now, there is no real device context: (void) dev; uint8_t prev_printAndLog = g_printAndLog; - if (! passthru) { + if (capture) { g_printAndLog |= PRINTANDLOG_GRAB; + } + if (quiet) { g_printAndLog &= ~PRINTANDLOG_PRINT; } int ret = CommandReceived(cmd); diff --git a/client/src/pm3.i b/client/src/pm3.i index 810f5eb22..f7923323c 100644 --- a/client/src/pm3.i +++ b/client/src/pm3.i @@ -11,8 +11,11 @@ #ifdef PYWRAP #include - %typemap(default) bool passthru { - $1 = Py_False; + %typemap(default) bool capture { + $1 = Py_True; + } + %typemap(default) bool quiet { + $1 = Py_True; } #endif typedef struct { @@ -37,7 +40,7 @@ typedef struct { pm3_close($self); } } - int console(char *cmd, bool passthru = false); + int console(char *cmd, bool capture = true, bool quiet = true); char const * const name; char const * const grabbed_output; } diff --git a/client/src/pm3_luawrap.c b/client/src/pm3_luawrap.c index c880d09fd..65798a2e5 100644 --- a/client/src/pm3_luawrap.c +++ b/client/src/pm3_luawrap.c @@ -2768,13 +2768,15 @@ static int _wrap_pm3_console(lua_State *L) { int SWIG_arg = 0; pm3 *arg1 = (pm3 *) 0 ; char *arg2 = (char *) 0 ; - bool arg3 = (bool) false ; + bool arg3 = (bool) true ; + bool arg4 = (bool) true ; int result; - SWIG_check_num_args("pm3::console", 2, 3) + SWIG_check_num_args("pm3::console", 2, 4) if (!SWIG_isptrtype(L, 1)) SWIG_fail_arg("pm3::console", 1, "pm3 *"); if (!SWIG_lua_isnilstring(L, 2)) SWIG_fail_arg("pm3::console", 2, "char *"); if (lua_gettop(L) >= 3 && !lua_isboolean(L, 3)) SWIG_fail_arg("pm3::console", 3, "bool"); + if (lua_gettop(L) >= 4 && !lua_isboolean(L, 4)) SWIG_fail_arg("pm3::console", 4, "bool"); if (!SWIG_IsOK(SWIG_ConvertPtr(L, 1, (void **)&arg1, SWIGTYPE_p_pm3, 0))) { SWIG_fail_ptr("pm3_console", 1, SWIGTYPE_p_pm3); @@ -2784,7 +2786,10 @@ static int _wrap_pm3_console(lua_State *L) { if (lua_gettop(L) >= 3) { arg3 = (lua_toboolean(L, 3) != 0); } - result = (int)pm3_console(arg1, arg2, arg3); + if (lua_gettop(L) >= 4) { + arg4 = (lua_toboolean(L, 4) != 0); + } + result = (int)pm3_console(arg1, arg2, arg3, arg4); lua_pushnumber(L, (lua_Number) result); SWIG_arg++; return SWIG_arg; diff --git a/client/src/pm3_pywrap.c b/client/src/pm3_pywrap.c index 93a26e2d0..02186d257 100644 --- a/client/src/pm3_pywrap.c +++ b/client/src/pm3_pywrap.c @@ -3546,7 +3546,8 @@ SWIGINTERN PyObject *_wrap_pm3_console(PyObject *self, PyObject *args) { PyObject *resultobj = 0; pm3 *arg1 = (pm3 *) 0 ; char *arg2 = (char *) 0 ; - bool arg3 = (bool) false ; + bool arg3 = (bool) true ; + bool arg4 = (bool) true ; void *argp1 = 0 ; int res1 = 0 ; int res2 ; @@ -3554,11 +3555,13 @@ SWIGINTERN PyObject *_wrap_pm3_console(PyObject *self, PyObject *args) { int alloc2 = 0 ; bool val3 ; int ecode3 = 0 ; - PyObject *swig_obj[3] ; + bool val4 ; + int ecode4 = 0 ; + PyObject *swig_obj[4] ; int result; (void)self; - if (!SWIG_Python_UnpackTuple(args, "pm3_console", 2, 3, swig_obj)) SWIG_fail; + if (!SWIG_Python_UnpackTuple(args, "pm3_console", 2, 4, swig_obj)) SWIG_fail; res1 = SWIG_ConvertPtr(swig_obj[0], &argp1, SWIGTYPE_p_pm3, 0 | 0); if (!SWIG_IsOK(res1)) { SWIG_exception_fail(SWIG_ArgError(res1), "in method '" "pm3_console" "', argument " "1"" of type '" "pm3 *""'"); @@ -3576,7 +3579,14 @@ SWIGINTERN PyObject *_wrap_pm3_console(PyObject *self, PyObject *args) { } arg3 = (bool)(val3); } - result = (int)pm3_console(arg1, arg2, arg3); + if (swig_obj[3]) { + ecode4 = SWIG_AsVal_bool(swig_obj[3], &val4); + if (!SWIG_IsOK(ecode4)) { + SWIG_exception_fail(SWIG_ArgError(ecode4), "in method '" "pm3_console" "', argument " "4"" of type '" "bool""'"); + } + arg4 = (bool)(val4); + } + result = (int)pm3_console(arg1, arg2, arg3, arg4); resultobj = SWIG_From_int((int)(result)); if (alloc2 == SWIG_NEWOBJ) free((char *)buf2); return resultobj; diff --git a/client/src/pm3line_vocabulary.h b/client/src/pm3line_vocabulary.h index 4c67baf01..bd7fdbc45 100644 --- a/client/src/pm3line_vocabulary.h +++ b/client/src/pm3line_vocabulary.h @@ -63,7 +63,7 @@ const static vocabulary_t vocabulary[] = { { 1, "prefs set output" }, { 1, "prefs set plotsliders" }, { 1, "analyse help" }, - { 1, "analyse lcr" }, + { 1, "analyse lrc" }, { 1, "analyse crc" }, { 1, "analyse chksum" }, { 1, "analyse dates" }, @@ -267,6 +267,7 @@ const static vocabulary_t vocabulary[] = { { 0, "hf gallagher delete" }, { 1, "hf gallagher diversifykey" }, { 1, "hf gallagher decode" }, + { 1, "hf gallagher encode" }, { 1, "hf iclass help" }, { 1, "hf iclass list" }, { 0, "hf iclass dump" }, diff --git a/client/src/proxmark3.c b/client/src/proxmark3.c index f098eaf6c..1beb93c2a 100644 --- a/client/src/proxmark3.c +++ b/client/src/proxmark3.c @@ -278,7 +278,7 @@ static void prompt_compose(char *buf, size_t buflen, const char *promptctx, cons if (no_newline) { snprintf(buf, buflen - 1, PROXPROMPT_COMPOSE, promptdev, promptnet, promptctx); } else { - snprintf(buf, buflen - 1, "\r \r" PROXPROMPT_COMPOSE, promptdev, promptnet, promptctx); + snprintf(buf, buflen - 1, "\33[2K\r" PROXPROMPT_COMPOSE, promptdev, promptnet, promptctx); } } diff --git a/client/src/scripting.c b/client/src/scripting.c index 9b1542d54..0657ae8d6 100644 --- a/client/src/scripting.c +++ b/client/src/scripting.c @@ -423,7 +423,7 @@ static int l_mfDarkside(lua_State *L) { break; } - int retval = mfDarkside(blockno & 0xFF, keytype & 0xFF, &key); + int retval = mf_dark_side(blockno & 0xFF, keytype & 0xFF, &key); uint8_t dest_key[8]; num_to_bytes(key, sizeof(dest_key), dest_key); diff --git a/common/commonutil.c b/common/commonutil.c index 4c203a332..15c79d248 100644 --- a/common/commonutil.c +++ b/common/commonutil.c @@ -544,17 +544,26 @@ void reverse_arraybytes_copy(uint8_t *arr, uint8_t *dest, size_t len) { } } -size_t concatbits(uint8_t *dst, size_t dstskip, const uint8_t *src, size_t srcstart, size_t srclen) { - // erase dstbuf bits that will be overriden - dst[dstskip / 8] &= 0xFF - ((1 << (7 - (dstskip % 8) + 1)) - 1); - for (size_t i = (dstskip / 8) + 1; i <= (dstskip + srclen) / 8; i++) { - dst[i] = 0; +// TODO: Boost performance by copying in chunks of 1, 2, or 4 bytes when feasible. +size_t concatbits(uint8_t *dest, int dest_offset, const uint8_t *src, int src_offset, size_t nbits) { + int i, end, step; + + // overlap + if ((src - dest) * 8 + src_offset - dest_offset > 0) { + i = 0; + end = nbits; + step = 1; + } else { + i = nbits; + end = 0; + step = -1; } - for (size_t i = 0; i < srclen; i++) { - // equiv of dstbufbits[dstbufskip + i] = srcbufbits[srcbufstart + i] - dst[(dstskip + i) / 8] |= ((src[(srcstart + i) / 8] >> (7 - ((srcstart + i) % 8))) & 1) << (7 - ((dstskip + i) % 8)); + for (; i != end; i += step) { + // equiv of dest_bits[dest_offset + i] = src_bits[src_offset + i] + CLEAR_BIT(dest, dest_offset + i); + if (TEST_BIT(src, src_offset + i)) SET_BIT(dest, dest_offset + i); } - return dstskip + srclen; + return dest_offset + nbits; } diff --git a/common/commonutil.h b/common/commonutil.h index a3476a284..b2d560f3d 100644 --- a/common/commonutil.h +++ b/common/commonutil.h @@ -135,5 +135,5 @@ bool hexstr_to_byte_array(const char *hexstr, uint8_t *d, size_t *n); void reverse_arraybytes(uint8_t *arr, size_t len); void reverse_arraybytes_copy(uint8_t *arr, uint8_t *dest, size_t len); -size_t concatbits(uint8_t *dst, size_t dstskip, const uint8_t *src, size_t srcstart, size_t srclen); +size_t concatbits(uint8_t *dest, int dest_offset, const uint8_t *src, int src_offset, size_t nbits); #endif diff --git a/common_arm/flashmem.c b/common_arm/flashmem.c index 33d7db2ad..d316fa945 100644 --- a/common_arm/flashmem.c +++ b/common_arm/flashmem.c @@ -43,6 +43,8 @@ static uint32_t FLASHMEM_SPIBAUDRATE = FLASH_BAUD; #ifndef AS_BOOTROM +uint8_t spi_flash_p64k = 0; + void FlashmemSetSpiBaudrate(uint32_t baudrate) { FLASHMEM_SPIBAUDRATE = baudrate; Dbprintf("Spi Baudrate : %dMHz", FLASHMEM_SPIBAUDRATE / 1000000); @@ -58,9 +60,9 @@ bool Flash_ReadID(flash_device_type_t *result, bool read_jedec) { // 0x9F JEDEC FlashSendByte(JEDECID); - result->manufacturer_id = FlashSendByte(0xFF); - result->device_id = FlashSendByte(0xFF); - result->device_id2 = FlashSendLastByte(0xFF); + result->manufacturer_id = (FlashSendByte(0xFF) & 0xFF); + result->device_id = (FlashSendByte(0xFF) & 0xFF); + result->device_id2 = (FlashSendLastByte(0xFF) & 0xFF); } else { // 0x90 Manufacture ID / device ID FlashSendByte(ID); @@ -68,8 +70,8 @@ bool Flash_ReadID(flash_device_type_t *result, bool read_jedec) { FlashSendByte(0x00); FlashSendByte(0x00); - result->manufacturer_id = FlashSendByte(0xFF); - result->device_id = FlashSendLastByte(0xFF); + result->manufacturer_id = (FlashSendByte(0xFF) & 0xFF); + result->device_id = (FlashSendLastByte(0xFF) & 0xFF); } return true; @@ -92,10 +94,10 @@ uint16_t Flash_ReadData(uint32_t address, uint8_t *out, uint16_t len) { } uint16_t i = 0; - for (; i < (len - 1); i++) - out[i] = FlashSendByte(0xFF); - - out[i] = FlashSendLastByte(0xFF); + for (; i < (len - 1); i++) { + out[i] = (FlashSendByte(0xFF) & 0xFF); + } + out[i] = (FlashSendLastByte(0xFF) & 0xFF); FlashStop(); return len; } @@ -122,10 +124,10 @@ uint16_t Flash_ReadDataCont(uint32_t address, uint8_t *out, uint16_t len) { } uint16_t i = 0; - for (; i < (len - 1); i++) - out[i] = FlashSendByte(0xFF); - - out[i] = FlashSendLastByte(0xFF); + for (; i < (len - 1); i++) { + out[i] = ( FlashSendByte(0xFF) & 0xFF); + } + out[i] = (FlashSendLastByte(0xFF) & 0xFF); return len; } @@ -144,14 +146,15 @@ uint16_t Flash_WriteData(uint32_t address, uint8_t *in, uint16_t len) { return 0; } - // out-of-range - if (((address >> 16) & 0xFF) > MAX_BLOCKS) { - Dbprintf("Flash_WriteData, block out-of-range"); + if (!FlashInit()) { + if (g_dbglevel > 3) Dbprintf("Flash_WriteData init fail"); return 0; } - if (!FlashInit()) { - if (g_dbglevel > 3) Dbprintf("Flash_WriteData init fail"); + // out-of-range + if (((address >> 16) & 0xFF) > spi_flash_p64k) { + Dbprintf("Flash_WriteData, block out-of-range %02x > %02x", (address >> 16) & 0xFF, spi_flash_p64k); + FlashStop(); return 0; } @@ -187,8 +190,8 @@ uint16_t Flash_WriteDataCont(uint32_t address, uint8_t *in, uint16_t len) { return 0; } - if (((address >> 16) & 0xFF) > MAX_BLOCKS) { - Dbprintf("Flash_WriteDataCont, block out-of-range"); + if (((address >> 16) & 0xFF) > spi_flash_p64k) { + Dbprintf("Flash_WriteDataCont, block out-of-range %02x > %02x", (address >> 16) & 0xFF, spi_flash_p64k); return 0; } @@ -266,18 +269,11 @@ bool Flash_WipeMemory(void) { // Each block is 64Kb. Four blocks // one block erase takes 1s ( 1000ms ) - Flash_WriteEnable(); - Flash_Erase64k(0); - Flash_CheckBusy(BUSY_TIMEOUT); - Flash_WriteEnable(); - Flash_Erase64k(1); - Flash_CheckBusy(BUSY_TIMEOUT); - Flash_WriteEnable(); - Flash_Erase64k(2); - Flash_CheckBusy(BUSY_TIMEOUT); - Flash_WriteEnable(); - Flash_Erase64k(3); - Flash_CheckBusy(BUSY_TIMEOUT); + for (uint8_t i=0; i < spi_flash_p64k; i++) { + Flash_WriteEnable(); + Flash_Erase64k(i); + Flash_CheckBusy(BUSY_TIMEOUT); + } FlashStop(); return true; @@ -293,7 +289,7 @@ void Flash_WriteEnable(void) { // execution time: 0.8ms / 800us bool Flash_Erase4k(uint8_t block, uint8_t sector) { - if (block > MAX_BLOCKS || sector > MAX_SECTORS) return false; + if (block > spi_flash_p64k || sector > MAX_SECTORS) return false; FlashSendByte(SECTORERASE); FlashSendByte(block); @@ -328,7 +324,7 @@ bool Flash_Erase32k(uint32_t address) { // 0x03 00 00 -- 0x 03 FF FF == block 3 bool Flash_Erase64k(uint8_t block) { - if (block > MAX_BLOCKS) return false; + if (block > spi_flash_p64k) return false; FlashSendByte(BLOCK64ERASE); FlashSendByte(block); @@ -404,6 +400,7 @@ void Flashmem_print_status(void) { ); } } + Dbprintf(" Flash pages (64k)....... " _YELLOW_("0x%02x (%u)"), spi_flash_p64k, spi_flash_p64k); uint8_t uid[8] = {0, 0, 0, 0, 0, 0, 0, 0}; Flash_UniqueID(uid); @@ -431,7 +428,7 @@ void Flashmem_print_info(void) { uint16_t num; Flash_CheckBusy(BUSY_TIMEOUT); - uint16_t isok = Flash_ReadDataCont(DEFAULT_MF_KEYS_OFFSET, keysum, 2); + uint16_t isok = Flash_ReadDataCont(DEFAULT_MF_KEYS_OFFSET_P(spi_flash_p64k), keysum, 2); if (isok == 2) { num = ((keysum[1] << 8) | keysum[0]); if (num != 0xFFFF && num != 0x0) @@ -439,7 +436,7 @@ void Flashmem_print_info(void) { } Flash_CheckBusy(BUSY_TIMEOUT); - isok = Flash_ReadDataCont(DEFAULT_T55XX_KEYS_OFFSET, keysum, 2); + isok = Flash_ReadDataCont(DEFAULT_T55XX_KEYS_OFFSET_P(spi_flash_p64k), keysum, 2); if (isok == 2) { num = ((keysum[1] << 8) | keysum[0]); if (num != 0xFFFF && num != 0x0) @@ -447,7 +444,7 @@ void Flashmem_print_info(void) { } Flash_CheckBusy(BUSY_TIMEOUT); - isok = Flash_ReadDataCont(DEFAULT_ICLASS_KEYS_OFFSET, keysum, 2); + isok = Flash_ReadDataCont(DEFAULT_ICLASS_KEYS_OFFSET_P(spi_flash_p64k), keysum, 2); if (isok == 2) { num = ((keysum[1] << 8) | keysum[0]); if (num != 0xFFFF && num != 0x0) @@ -457,6 +454,28 @@ void Flashmem_print_info(void) { FlashStop(); } +//read spi flash JEDEC ID and fill the global variable spi_flash_p64k +bool FlashDetect(void) { + flash_device_type_t flash_device = {0}; + + if (!Flash_ReadID(&flash_device, true)) { + if (g_dbglevel > 3) Dbprintf("Flash_ReadID failed"); + return false; + } + + uint32_t identifier = (flash_device.manufacturer_id << 16) + (flash_device.device_id <<8 ) + flash_device.device_id2; + int i = 0; + for (; i < ARRAYLEN(SpiFlashTable)-1; i++) { + if (SpiFlashTable[i].identifier == identifier) { + break; + } + } + + spi_flash_p64k = SpiFlashTable[i].pages64; + + return true; +} + #endif // #ifndef AS_BOOTROM @@ -471,6 +490,14 @@ bool FlashInit(void) { return false; } +#ifndef AS_BOOTROM + if (spi_flash_p64k == 0) { + if (!FlashDetect()) { + return false; + } + } +#endif // #ifndef AS_BOOTROM + return true; } @@ -486,14 +513,14 @@ void Flash_UniqueID(uint8_t *uid) { FlashSendByte(0xFF); FlashSendByte(0xFF); - uid[7] = FlashSendByte(0xFF); - uid[6] = FlashSendByte(0xFF); - uid[5] = FlashSendByte(0xFF); - uid[4] = FlashSendByte(0xFF); - uid[3] = FlashSendByte(0xFF); - uid[2] = FlashSendByte(0xFF); - uid[1] = FlashSendByte(0xFF); - uid[0] = FlashSendLastByte(0xFF); + uid[7] = (FlashSendByte(0xFF) & 0xFF); + uid[6] = (FlashSendByte(0xFF) & 0xFF); + uid[5] = (FlashSendByte(0xFF) & 0xFF); + uid[4] = (FlashSendByte(0xFF) & 0xFF); + uid[3] = (FlashSendByte(0xFF) & 0xFF); + uid[2] = (FlashSendByte(0xFF) & 0xFF); + uid[1] = (FlashSendByte(0xFF) & 0xFF); + uid[0] = (FlashSendLastByte(0xFF) & 0xFF); } void FlashStop(void) { diff --git a/common_arm/flashmem.h b/common_arm/flashmem.h index 127ea43b0..fd407299c 100644 --- a/common_arm/flashmem.h +++ b/common_arm/flashmem.h @@ -145,6 +145,37 @@ uint16_t Flash_WriteDataCont(uint32_t address, uint8_t *in, uint16_t len); void Flashmem_print_status(void); void Flashmem_print_info(void); +typedef struct spi_flash_s { + const uint32_t identifier; + const uint8_t pages64; + const char *desc; +} spi_flash_t; + +const static spi_flash_t SpiFlashTable[] = { + // Manufacturer: Puya + { 0x856015, 32, "P25Q16H" }, + // Manufacturer: Winbond + { 0xEF3012, 4, "W25X20BV" }, + { 0xEF3013, 8, "W25X40BV" }, + + { 0xEF4013, 8, "W25Q40BV" }, + { 0xEF4014, 16, "W25Q80BV" }, + { 0xEF4015, 32, "W25Q16BV" }, + { 0xEF4016, 64, "W25Q32BV" }, + + { 0xEF7022, 4, "W25Q02JV" }, + // (default) last record + { 0x000000, 4, "Unknown!" } +}; + +#ifndef ARRAYLEN +# define ARRAYLEN(x) (sizeof(x)/sizeof((x)[0])) +#endif + +extern uint8_t spi_flash_p64k; + +bool FlashDetect(void); + #endif // #ifndef AS_BOOTROM diff --git a/common_arm/ticks.c b/common_arm/ticks.c index 019c3b10c..10c3dcb72 100644 --- a/common_arm/ticks.c +++ b/common_arm/ticks.c @@ -149,7 +149,8 @@ void StartCountSspClk(void) { | AT91C_TC_WAVE // Waveform Mode | AT91C_TC_WAVESEL_UP // just count | AT91C_TC_ACPA_CLEAR // Clear TIOA0 on RA Compare - | AT91C_TC_ACPC_SET; // Set TIOA0 on RC Compare + | AT91C_TC_ACPC_SET // Set TIOA0 on RC Compare + | AT91C_TC_ASWTRG_SET; // Set TIOA0 on software trigger to trigger instant reset of TC2 AT91C_BASE_TC0->TC_RA = 1; // RA Compare value = 1; pulse width to TC2 AT91C_BASE_TC0->TC_RC = 0; // RC Compare value = 0; increment TC2 on overflow @@ -191,8 +192,8 @@ void StartCountSspClk(void) { // whenever the last three bits of our counter go 0, we can be sure to be in the middle of a frame transfer. // (just started with the transfer of the 4th Bit). - // The high word of the counter (TC2) will not reset until the low word (TC0) overflows. - // Therefore need to wait quite some time before we can use the counter. + // The high word of the counter (TC2) will not reset until the low word (TC0) clocks to process the external trigger. + // Therefore may need to wait a little bit before we can use the counter. while (AT91C_BASE_TC2->TC_CV > 0); } void ResetSspClk(void) { @@ -336,4 +337,3 @@ void StopTicks(void) { AT91C_BASE_TC0->TC_CCR = AT91C_TC_CLKDIS; AT91C_BASE_TC1->TC_CCR = AT91C_TC_CLKDIS; } - diff --git a/common_arm/usb_cdc.c b/common_arm/usb_cdc.c index 2447330c8..ce7bda325 100644 --- a/common_arm/usb_cdc.c +++ b/common_arm/usb_cdc.c @@ -716,7 +716,11 @@ uint32_t usb_read(uint8_t *data, size_t len) { static uint8_t usb_read_ng_buffer[64] = {0}; static uint8_t usb_read_ng_bufoffset = 0; -static uint8_t usb_read_ng_buflen = 0; +static size_t usb_read_ng_buflen = 0; + +bool usb_read_ng_has_buffered_data(void) { + return usb_read_ng_buflen > 0; +} uint32_t usb_read_ng(uint8_t *data, size_t len) { @@ -733,7 +737,7 @@ uint32_t usb_read_ng(uint8_t *data, size_t len) { // if local buffer has all data - for (uint8_t i = 0; i < len; i++) { + for (size_t i = 0; i < len; i++) { data[nbBytesRcv++] = usb_read_ng_buffer[usb_read_ng_bufoffset + i]; } @@ -751,7 +755,7 @@ uint32_t usb_read_ng(uint8_t *data, size_t len) { // take all data from local buffer, then read from usb - for (uint8_t i = 0; i < usb_read_ng_buflen; i++) { + for (size_t i = 0; i < usb_read_ng_buflen; i++) { data[nbBytesRcv++] = usb_read_ng_buffer[usb_read_ng_bufoffset + i]; } diff --git a/common_arm/usb_cdc.h b/common_arm/usb_cdc.h index 9c7cb7299..2df1acc1e 100644 --- a/common_arm/usb_cdc.h +++ b/common_arm/usb_cdc.h @@ -39,6 +39,7 @@ int async_usb_write_start(void); void async_usb_write_pushByte(uint8_t data); bool async_usb_write_requestWrite(void); int async_usb_write_stop(void); +bool usb_read_ng_has_buffered_data(void); uint32_t usb_read_ng(uint8_t *data, size_t len); void usb_update_serial(uint64_t newSerialNumber); diff --git a/doc/commands.json b/doc/commands.json index f28a7e93b..d4c4bcd5b 100644 --- a/doc/commands.json +++ b/doc/commands.json @@ -97,16 +97,16 @@ }, "analyse help": { "command": "analyse help", - "description": "help This help lcr Generate final byte for XOR LRC crc Stub method for CRC evaluations chksum Checksum with adding, masking and one's complement dates Look for datestamps in a given array of bytes lfsr LFSR tests a num bits test nuid create NUID from 7byte UID demodbuff Load binary string to DemodBuffer freq Calc wave lengths foo muxer units convert ETU <> US <> SSP_CLK (3.39MHz) --------------------------------------------------------------------------------------- analyse lcr available offline: yes Specifying the bytes of a UID with a known LRC will find the last byte value needed to generate that LRC with a rolling XOR. All bytes should be specified in HEX.", + "description": "help This help lrc Generate final byte for XOR LRC crc Stub method for CRC evaluations chksum Checksum with adding, masking and one's complement dates Look for datestamps in a given array of bytes lfsr LFSR tests a num bits test nuid create NUID from 7byte UID demodbuff Load binary string to DemodBuffer freq Calc wave lengths foo muxer units convert ETU <> US <> SSP_CLK (3.39MHz) --------------------------------------------------------------------------------------- analyse lrc available offline: yes Specifying the bytes of a UID with a known LRC will find the last byte value needed to generate that LRC with a rolling XOR. All bytes should be specified in HEX.", "notes": [ - "analyse lcr -d 04008064BA -> Target (BA) requires final LRC XOR byte value: 5A" + "analyse lrc -d 04008064BA -> Target (BA) requires final LRC XOR byte value: 5A" ], "offline": true, "options": [ "-h, --help This help", - "-d, --data bytes to calc missing XOR in a LCR" + "-d, --data bytes to calc missing XOR in a LRC" ], - "usage": "analyse lcr [-h] -d " + "usage": "analyse lrc [-h] -d " }, "analyse lfsr": { "command": "analyse lfsr", @@ -380,15 +380,15 @@ }, "data envelope": { "command": "data envelope", - "description": "Create an square envelop of the samples", + "description": "Create an square envelope of the samples", "notes": [ - "data envelop" + "data envelope" ], "offline": true, "options": [ "-h, --help This help" ], - "usage": "data envelop [-h]" + "usage": "data envelope [-h]" }, "data fsktonrz": { "command": "data fsktonrz", @@ -582,7 +582,7 @@ "description": "Function takes a decimal or hexdecimal number and print it in decimal/hex/binary Will print message if number is a prime number", "notes": [ "data num --dec 2023", - "data num --hex 0x1000" + "data num --hex 2A" ], "offline": true, "options": [ @@ -3141,9 +3141,25 @@ ], "usage": "hf gallagher diversify [-h] --aid [--keynum ] [--uid ] [--sitekey ] [--apdu]" }, + "hf gallagher encode": { + "command": "hf gallagher encode", + "description": "Encode a Gallagher credential block Credential block can be specified with or without the bitwise inverse.", + "notes": [ + "hf gallagher encode --rc 1 --fc 22153 --cn 1253518 --il 1" + ], + "offline": true, + "options": [ + "-h, --help This help", + "-r, --rc Region code. 4 bits max", + "-f, --fc Facility code. 2 bytes max", + "-c, --cn Card number. 3 bytes max", + "-i, --il Issue level. 4 bits max" + ], + "usage": "hf gallagher encode [-h] -r -f -c -i " + }, "hf gallagher help": { "command": "hf gallagher help", - "description": "help This help diversifykey Diversify Gallagher key decode Decode Gallagher credential block --------------------------------------------------------------------------------------- hf gallagher reader available offline: no Read a Gallagher DESFire tag from the Card Application Directory, CAD Specify site key is required if using non-default key", + "description": "help This help diversifykey Diversify Gallagher key decode Decode Gallagher credential block encode Encode Gallagher credential block --------------------------------------------------------------------------------------- hf gallagher reader available offline: no Read a Gallagher DESFire tag from the Card Application Directory, CAD Specify site key is required if using non-default key", "notes": [ "hf gallagher reader -@ -> continuous reader mode", "hf gallagher reader --aid 2081f4 --sitekey 00112233445566778899aabbccddeeff -> skip CAD" @@ -3226,23 +3242,20 @@ "command": "hf iclass configcard", "description": "Manage reader configuration card via Cardhelper or internal database, The generated config card will be uploaded to device emulator memory. You can start simulating `hf iclass sim -t 3` or use the emul commands", "notes": [ - "hf iclass configcard -l -> download config card settings from cardhelper", "hf iclass configcard -p -> print all config cards in the database", - "hf iclass configcard --ci 1 -> view config card setting in slot 1", - "hf iclass configcard -g --ci 0 -> generate config file from slot 0" + "hf iclass configcard --g 0 -> generate config file with option 0" ], "offline": false, "options": [ "-h, --help This help", - "--ci use config slot at index", + "--g use config option", "--ki Card Key - index to select key from memory 'hf iclass managekeys'", - "--krki Elite Keyroll Key - index to select key from memory 'hf iclass managekeys'", + "--eki Elite Key - index to select key from memory 'hf iclass managekeys'", + "--mrki Standard Master Key - index to select key from memory 'hf iclass managekeys'", "--elite Use elite key for the the Card Key ki", - "-g generate card dump file", - "-l load available cards", "-p print available cards" ], - "usage": "hf iclass configcard [-hglp] [--ci ] [--ki ] [--krki ] [--elite]" + "usage": "hf iclass configcard [-hp] [--g ] [--ki ] [--eki ] [--mrki ] [--elite]" }, "hf iclass creditepurse": { "command": "hf iclass creditepurse", @@ -4367,9 +4380,10 @@ "options": [ "-h, --help This help", "-b, --blk block number", - "-v, --verbose verbose output" + "-v, --verbose verbose output", + "--gdm use gdm alt (20/23) magic wakeup" ], - "usage": "hf mf cgetblk [-hv] -b " + "usage": "hf mf cgetblk [-hv] -b [--gdm]" }, "hf mf cgetsc": { "command": "hf mf cgetsc", @@ -4381,9 +4395,10 @@ "options": [ "-h, --help This help", "-s, --sec sector number", - "-v, --verbose verbose output" + "-v, --verbose verbose output", + "--gdm use gdm alt (20/23) magic wakeup" ], - "usage": "hf mf cgetsc [-hv] -s " + "usage": "hf mf cgetsc [-hv] -s [--gdm]" }, "hf mf chk": { "command": "hf mf chk", @@ -4431,9 +4446,10 @@ "--1k MIFARE Classic 1k / S50 (def)", "--2k MIFARE Classic/Plus 2k", "--4k MIFARE Classic 4k / S70", - "--emu from emulator memory" + "--emu from emulator memory", + "--gdm use gdm alt (20/23) magic wakeup" ], - "usage": "hf mf cload [-h] [-f ] [--mini] [--1k] [--2k] [--4k] [--emu]" + "usage": "hf mf cload [-h] [-f ] [--mini] [--1k] [--2k] [--4k] [--emu] [--gdm]" }, "hf mf csave": { "command": "hf mf csave", @@ -4450,9 +4466,10 @@ "--1k MIFARE Classic 1k / S50 (def)", "--2k MIFARE Classic/Plus 2k", "--4k MIFARE Classic 4k / S70", - "--emu to emulator memory" + "--emu to emulator memory", + "--gdm to emulator memory" ], - "usage": "hf mf csave [-h] [-f ] [--mini] [--1k] [--2k] [--4k] [--emu]" + "usage": "hf mf csave [-h] [-f ] [--mini] [--1k] [--2k] [--4k] [--emu] [--gdm]" }, "hf mf csetblk": { "command": "hf mf csetblk", @@ -4465,9 +4482,10 @@ "-h, --help This help", "-b, --blk block number", "-d, --data bytes to write, 16 hex bytes", - "-w, --wipe wipes card with backdoor cmd before writing" + "-w, --wipe wipes card with backdoor cmd before writing", + "--gdm use gdm alt (20/23) magic wakeup" ], - "usage": "hf mf csetblk [-hw] -b [-d ]" + "usage": "hf mf csetblk [-hw] -b [-d ] [--gdm]" }, "hf mf csetuid": { "command": "hf mf csetuid", @@ -4482,9 +4500,10 @@ "-w, --wipe wipes card with backdoor cmd`", "-u, --uid UID, 4/7 hex bytes", "-a, --atqa ATQA, 2 hex bytes", - "-s, --sak SAK, 1 hex byte" + "-s, --sak SAK, 1 hex byte", + "--gdm use gdm alt (20/23) magic wakeup" ], - "usage": "hf mf csetuid [-hw] [-u ] [-a ] [-s ]" + "usage": "hf mf csetuid [-hw] [-u ] [-a ] [-s ] [--gdm]" }, "hf mf cview": { "command": "hf mf cview", @@ -4500,9 +4519,10 @@ "--1k MIFARE Classic 1k / S50 (def)", "--2k MIFARE Classic/Plus 2k", "--4k MIFARE Classic 4k / S70", - "-v, --verbose verbose output" + "-v, --verbose verbose output", + "--gdm use gdm alt (20/23) magic wakeup" ], - "usage": "hf mf cview [-hv] [--mini] [--1k] [--2k] [--4k]" + "usage": "hf mf cview [-hv] [--mini] [--1k] [--2k] [--4k] [--gdm]" }, "hf mf cwipe": { "command": "hf mf cwipe", @@ -4516,9 +4536,10 @@ "-h, --help This help", "-u, --uid UID, 4 hex bytes", "-a, --atqa ATQA, 2 hex bytes", - "-s, --sak SAK, 1 hex byte" + "-s, --sak SAK, 1 hex byte", + "--gdm use gdm alt (20/23) magic wakeup" ], - "usage": "hf mf cwipe [-h] [-u ] [-a ] [-s ]" + "usage": "hf mf cwipe [-h] [-u ] [-a ] [-s ] [--gdm]" }, "hf mf darkside": { "command": "hf mf darkside", @@ -5151,9 +5172,10 @@ "-b, --keyb use key B for access printing sectors (by default: key A)", "--be (optional, BigEndian)", "--dch decode Card Holder information", - "-f, --file load dump file and decode MAD" + "-f, --file load dump file and decode MAD", + "--force force decode (skip key check)" ], - "usage": "hf mf mad [-hvb] [--aid ] [-k ] [--be] [--dch] [-f ]" + "usage": "hf mf mad [-hvb] [--aid ] [-k ] [--be] [--dch] [-f ] [--force]" }, "hf mf nack": { "command": "hf mf nack", @@ -5383,17 +5405,18 @@ "--1k MIFARE Classic 1k / S50", "--2k MIFARE Classic/Plus 2k", "--4k MIFARE Classic 4k / S70", - "--atqa Provide explicit ATQA (2 bytes, overrides option t)", - "--sak Provide explicit SAK (1 bytes, overrides option t)", + "--atqa Provide explicit ATQA (2 bytes)", + "--sak Provide explicit SAK (1 bytes)", "-n, --num Automatically exit simulation after blocks have been read by reader. 0 = infinite", "-i, --interactive Console will not be returned until simulation finishes or is aborted", "-x Performs the 'reader attack', nr/ar attack against a reader.", "-y Performs the nested 'reader attack'. This requires preloading nt & nt_enc in emulator memory. Implies -x.", "-e, --emukeys Fill simulator keys from found keys. Requires -x or -y. Implies -i. Simulation will restart automatically.", - "-v, --verbose verbose output", - "--cve trigger CVE 2021_0430" + "--allowkeyb Allow key B even if readable", + "-v, --verbose Verbose output", + "--cve Trigger CVE 2021_0430" ], - "usage": "hf mf sim [-hixyev] [-u ] [--mini] [--1k] [--2k] [--4k] [--atqa ] [--sak ] [-n ] [--cve]" + "usage": "hf mf sim [-hixyev] [-u ] [--mini] [--1k] [--2k] [--4k] [--atqa ] [--sak ] [-n ] [--allowkeyb] [--cve]" }, "hf mf staticnested": { "command": "hf mf staticnested", @@ -9813,10 +9836,11 @@ "--nrar nonce / answer writer, 8 hex bytes", "--crypto crypto mode", "-k, --key pwd or key, 4 or 6 hex bytes", + "-m, --mode response protocol mode. 0 (Standard 00110), 1 (Advanced 11000), 2 (Advanced 11001), 3 (Fast Advanced 11010) (def: 3)", "-p, --page page address to read from", - "-c, --count how many pages to read. '0' reads all pages up to the end page (default: 1)" + "-c, --count how many pages to read. '0' reads all pages up to the end page (def: 1)" ], - "usage": "lf hitag hts rdbl [-h8] [--nrar ] [--crypto] [-k ] [-p ] [-c ]" + "usage": "lf hitag hts rdbl [-h8] [--nrar ] [--crypto] [-k ] [-m ] [-p ] [-c ]" }, "lf hitag hts reader": { "command": "lf hitag hts reader", @@ -9863,10 +9887,11 @@ "--nrar nonce / answer writer, 8 hex bytes", "--crypto crypto mode", "-k, --key pwd or key, 4 or 6 hex bytes", + "-m, --mode response protocol mode. 0 (Standard 00110), 1 (Advanced 11000), 2 (Advanced 11001), 3 (Fast Advanced 11010) (def: 3)", "-p, --page page address to write to", "-d, --data data, 4 hex bytes" ], - "usage": "lf hitag hts wrbl [-h8] [--nrar ] [--crypto] [-k ] -p -d " + "usage": "lf hitag hts wrbl [-h8] [--nrar ] [--crypto] [-k ] [-m ] -p -d " }, "lf hitag info": { "command": "lf hitag info", @@ -12217,7 +12242,7 @@ "--aid Applet ID to select. By default A0000003080000100 will be used", "--nonce Nonce to sign.", "--slot Slot number. Default will be 0x9E (card auth cert).", - "--alg Algorithm to use to sign. Example values: 06=RSA-1024, 07=RSA-2048, 11=ECC-P256 (default), 14=ECC-P384" + "--alg Algorithm to use to sign. Example values: 06=RSA-1024, 07=RSA-2048, 17=ECC-P256 (default), 20=ECC-P384" ], "usage": "piv sign [-hskatw] [--aid ] --nonce [--slot ] [--alg ]" }, @@ -12976,8 +13001,8 @@ } }, "metadata": { - "commands_extracted": 748, + "commands_extracted": 749, "extracted_by": "PM3Help2JSON v1.00", - "extracted_on": "2024-10-18T15:36:53" + "extracted_on": "2024-11-15T13:26:34" } } diff --git a/doc/commands.md b/doc/commands.md index 717b659aa..bf9b82edc 100644 --- a/doc/commands.md +++ b/doc/commands.md @@ -74,7 +74,7 @@ Check column "offline" for their availability. |command |offline |description |------- |------- |----------- |`analyse help `|Y |`This help` -|`analyse lcr `|Y |`Generate final byte for XOR LRC` +|`analyse lrc `|Y |`Generate final byte for XOR LRC` |`analyse crc `|Y |`Stub method for CRC evaluations` |`analyse chksum `|Y |`Checksum with adding, masking and one's complement` |`analyse dates `|Y |`Look for datestamps in a given array of bytes` @@ -382,6 +382,7 @@ Check column "offline" for their availability. |`hf gallagher delete `|N |`Delete Gallagher credentials from a DESFire card` |`hf gallagher diversifykey`|Y |`Diversify Gallagher key` |`hf gallagher decode `|Y |`Decode Gallagher credential block` +|`hf gallagher encode `|Y |`Encode Gallagher credential block` ### hf iclass @@ -412,7 +413,7 @@ Check column "offline" for their availability. |`hf iclass esave `|N |`Save emulator memory to file` |`hf iclass esetblk `|N |`Set emulator memory block data` |`hf iclass eview `|N |`View emulator memory` -|`hf iclass configcard `|N |`Reader configuration card` +|`hf iclass configcard `|N |`Reader configuration card generator` |`hf iclass calcnewkey `|Y |`Calc diversified keys (blocks 3 & 4) to write new keys` |`hf iclass encode `|Y |`Encode binary wiegand to block 7` |`hf iclass encrypt `|Y |`Encrypt given block data` diff --git a/doc/magic_cards_notes.md b/doc/magic_cards_notes.md index df7452259..fb838d4db 100644 --- a/doc/magic_cards_notes.md +++ b/doc/magic_cards_notes.md @@ -374,7 +374,7 @@ UID 4b: (actually NUID as there are no more "unique" IDs on 4b) ``` -Computing BCC on UID 11223344: `analyse lcr -d 11223344` = `44` +Computing BCC on UID 11223344: `analyse lrc -d 11223344` = `44` UID 7b: @@ -1043,7 +1043,7 @@ No implemented commands today | 7AFF000000000000BAFA000000000008 | UFUID | | 7AFF0000000000000000000000000008 | ZUID | -*Not all tags are the same!* UFUID, ZUID and PFUID* are not full implementations of Magic85 - they only acknowledge the first 8 (except wakeup command) and last config byte(s). +*Not all tags are the same!* UFUID, ZUID and PFUID* are not full implementations of USCUID - they only acknowledge the first 8 (except wakeup command) and last config byte(s). *Read and write config commands are flipped @@ -1053,9 +1053,9 @@ Well-known variations are described below. ^[Top](#top) -Known as "write only once", which is only partially true. +Known as "write only once", which is only partially true. Please note that some newer FUIDs have had ton configration blocks locked down and are truly a write-once tag. -Allows direct write to block 0 only when UID is default `AA55C396`. But always could be rewritten multiple times with backdoors commands. +Allows direct write to block 0 only when UID is default `AA55C396`. If your tag responds to a gen4 magic wakeup, the UID could always be rewritten multiple times with backdoors commands. Backdoor commands are available even after the personalization and makes that tag detectable. @@ -1074,6 +1074,8 @@ That's a key difference from [OTP](#mifare-classic-direct-write-otp)/[OTP 2.0](# ^[Top](#top) +Unlocked tag type: + ``` hf mf info ... @@ -1082,6 +1084,15 @@ hf mf info ``` +or locked down tag type: + +``` +hf mf info +... +[+] Magic capabilities... Write Once / FUID + +``` + ### Parsed configuration ^[Top](#top) @@ -1089,20 +1100,21 @@ hf mf info ``` [usb] pm3 --> hf mf gdmcfg --gdm [+] Config... 7A FF 85 00 00 00 00 00 00 FF 00 00 00 00 00 08 -[+] 7A FF .......................................... Magic wakeup enabled with GDM config block access -[+] 85 ....................................... Magic wakeup style GDM 20(7)/23 -[+] 00 00 00 .............................. Unknown -[+] 00 ........................... Key B use allowed when readable by ACL -[+] 00 ........................ Block 0 Direct Write Disabled (CUID) -[+] 00 ..................... Unknown -[+] FF .................. MFC EV1 personalization: 4B UID from Block 0 -[+] 00 ............... Shadow mode disabled -[+] 00 ............. Magic auth disabled -[+] 00 ........... Static encrypted nonce disabled -[+] 00 ......... MFC EV1 signature disabled -[+] 00 ...... Unknown -[+] 08 ... SAK +[+] 7A FF ......................................... Magic wakeup enabled with GDM config block access +[+] 85 ...................................... Magic wakeup style GDM 20(7)/23 +[+] 00 00 00 ............................. Unknown +[+] 00 .......................... Key B use allowed when readable by ACL +[+] 00 ....................... Block 0 Direct Write Disabled (CUID) +[+] 00 .................... Unknown +[+] FF ................. MFC EV1 personalization: 4B UID from Block 0 +[+] 00 .............. Shadow mode disabled +[+] 00 ........... Magic auth disabled +[+] 00 ........ Static encrypted nonce disabled +[+] 00 ..... MFC EV1 signature disabled +[+] 00 .. Unknown +[+] 08 SAK ``` +**Note: this is only possile on the FUID style that has not been locked down. ### Commands @@ -1115,7 +1127,7 @@ hf mf info * Write hidden block: `A8xx+crc`, `[16 bytes data]+crc` * Read configuration: `E000+crc` * Write configuration: `E100+crc` -* Example of changing block 0 after the personalization: +* Example of changing block 0 after the personalization (only possible on tags that have not been locked down): ``` [usb] pm3 --> hf 14a raw -k -a -b 7 20 @@ -1181,19 +1193,19 @@ Before the sealing could be detected from the config block value. ``` [usb] pm3 --> hf mf gdmcfg --gen1a [+] Config... 7A FF 00 00 00 00 00 00 BA FA 00 00 00 00 00 08 -[+] 7A FF .......................................... Magic wakeup enabled with GDM config block access -[+] 00 ....................................... Magic wakeup style Gen1a 40(7)/43 -[+] 00 00 00 .............................. Unknown -[+] 00 ........................... Key B use allowed when readable by ACL -[+] 00 ........................ Block 0 Direct Write Disabled (CUID) -[+] BA ..................... Unknown -[+] FA .................. MFC EV1 personalization: 4B UID from Block 0 -[+] 00 ............... Shadow mode disabled -[+] 00 ............. Magic auth disabled -[+] 00 ........... Static encrypted nonce disabled -[+] 00 ......... MFC EV1 signature disabled -[+] 00 ...... Unknown -[+] 08 ... SAK +[+] 7A FF ......................................... Magic wakeup enabled with GDM config block access +[+] 00 ...................................... Magic wakeup style Gen1a 40(7)/43 +[+] 00 00 00 ............................. Unknown +[+] 00 .......................... Key B use allowed when readable by ACL +[+] 00 ....................... Block 0 Direct Write Disabled (CUID) +[+] BA .................... Unknown +[+] FA ................. MFC EV1 personalization: 4B UID from Block 0 +[+] 00 .............. Shadow mode disabled +[+] 00 ........... Magic auth disabled +[+] 00 ........ Static encrypted nonce disabled +[+] 00 ..... MFC EV1 signature disabled +[+] 00 .. Unknown +[+] 08 SAK ``` ### Commands @@ -1271,19 +1283,19 @@ Could be detected from the config block value. ``` [usb] pm3 --> hf mf gdmcfg --gen1a [+] Config... 7A FF 00 00 00 00 00 00 00 00 00 00 00 00 00 08 -[+] 7A FF .......................................... Magic wakeup enabled with GDM config block access -[+] 00 ....................................... Magic wakeup style Gen1a 40(7)/43 -[+] 00 00 00 .............................. Unknown -[+] 00 ........................... Key B use allowed when readable by ACL -[+] 00 ........................ Block 0 Direct Write Disabled (CUID) -[+] 00 ..................... Unknown -[+] 00 .................. MFC EV1 personalization: 4B UID from Block 0 -[+] 00 ............... Shadow mode disabled -[+] 00 ............. Magic auth disabled -[+] 00 ........... Static encrypted nonce disabled -[+] 00 ......... MFC EV1 signature disabled -[+] 00 ...... Unknown -[+] 08 ... SAK +[+] 7A FF ......................................... Magic wakeup enabled with GDM config block access +[+] 00 ...................................... Magic wakeup style Gen1a 40(7)/43 +[+] 00 00 00 ............................. Unknown +[+] 00 .......................... Key B use allowed when readable by ACL +[+] 00 ....................... Block 0 Direct Write Disabled (CUID) +[+] 00 .................... Unknown +[+] 00 ................. MFC EV1 personalization: 4B UID from Block 0 +[+] 00 .............. Shadow mode disabled +[+] 00 ........... Magic auth disabled +[+] 00 ........ Static encrypted nonce disabled +[+] 00 ..... MFC EV1 signature disabled +[+] 00 .. Unknown +[+] 08 SAK ``` ### Commands @@ -1351,19 +1363,19 @@ Could be manually validated with the configuration block value. ``` [usb] pm3 --> hf mf gdmcfg [+] Config... 85 00 00 00 00 00 00 00 00 00 5A 5A 00 00 00 08 -[+] 85 00 .......................................... Magic wakeup disabled -[+] 00 ....................................... Magic wakeup style Gen1a 40(7)/43 -[+] 00 00 00 .............................. Unknown -[+] 00 ........................... Key B use allowed when readable by ACL -[+] 00 ........................ Block 0 Direct Write Disabled (CUID) -[+] 00 ..................... Unknown -[+] 00 .................. MFC EV1 personalization: 4B UID from Block 0 -[+] 5A ............... Shadow mode enabled -[+] 5A ............. Magic auth enabled -[+] 00 ........... Static encrypted nonce disabled -[+] 00 ......... MFC EV1 signature disabled -[+] 00 ...... Unknown -[+] 08 ... SAK +[+] 85 00 ......................................... Magic wakeup disabled +[+] 00 ...................................... Magic wakeup style Gen1a 40(7)/43 +[+] 00 00 00 ............................. Unknown +[+] 00 .......................... Key B use allowed when readable by ACL +[+] 00 ....................... Block 0 Direct Write Disabled (CUID) +[+] 00 .................... Unknown +[+] 00 ................. MFC EV1 personalization: 4B UID from Block 0 +[+] 5A .............. Shadow mode enabled +[+] 5A ........... Magic auth enabled +[+] 00 ........ Static encrypted nonce disabled +[+] 00 ..... MFC EV1 signature disabled +[+] 00 .. Unknown +[+] 08 SAK ``` ### Commands @@ -1420,19 +1432,19 @@ Could be manually validated with the configuration block value. ``` [usb] pm3 --> hf mf gdmcfg [+] Config... 85 00 00 00 00 00 00 5A 00 FF 00 5A 00 00 00 08 -[+] 85 00 .......................................... Magic wakeup disabled -[+] 00 ....................................... Magic wakeup style Gen1a 40(7)/43 -[+] 00 00 00 .............................. Unknown -[+] 00 ........................... Key B use allowed when readable by ACL -[+] 5A ........................ Block 0 Direct Write Enabled (CUID) -[+] 00 ..................... Unknown -[+] FF .................. MFC EV1 personalization: 4B UID from Block 0 -[+] 00 ............... Shadow mode disabled -[+] 5A ............. Magic auth enabled -[+] 00 ........... Static encrypted nonce disabled -[+] 00 ......... MFC EV1 signature disabled -[+] 00 ...... Unknown -[+] 08 ... SAK +[+] 85 00 ......................................... Magic wakeup disabled +[+] 00 ...................................... Magic wakeup style Gen1a 40(7)/43 +[+] 00 00 00 ............................. Unknown +[+] 00 .......................... Key B use allowed when readable by ACL +[+] 5A ....................... Block 0 Direct Write Enabled (CUID) +[+] 00 .................... Unknown +[+] FF ................. MFC EV1 personalization: 4B UID from Block 0 +[+] 00 .............. Shadow mode disabled +[+] 5A ........... Magic auth enabled +[+] 00 ........ Static encrypted nonce disabled +[+] 00 ..... MFC EV1 signature disabled +[+] 00 .. Unknown +[+] 08 SAK ``` ### Commands @@ -1607,9 +1619,9 @@ BCC1 Int LCK0 LCK1 UID is made of SN0..SN6 bytes -Computing BCC0 on UID 04112233445566: `analyse lcr -d 88041122` = `bf` +Computing BCC0 on UID 04112233445566: `analyse lrc -d 88041122` = `bf` -Computing BCC1 on UID 04112233445566: `analyse lcr -d 33445566` = `44` +Computing BCC1 on UID 04112233445566: `analyse lrc -d 33445566` = `44` Int is internal, typically 0x48 @@ -2113,9 +2125,9 @@ Possible tag wakeup mechanisms are: ^^^^ >> Gen1a mode (works with bitflip) ^^ >> Magic wakeup command (00 for 40-43; 85 for 20-23) ^^ >> Config available using regular mode (ON: A0) - ^^ >> Do not reply to 1B, making auth impossible - ^^ >> Do not enforce OTP properties (ON: A0) - ^^ >> Maximum memory configuration* + ^^ >> Auth type (00 = PWD mode, 0A = 2TDEA mode for UL-C) + ^^ >> CUID mode, allows writing to blocks 0-3 (ON: 0A) + ^^ >> Maximum memory configuration, please see below * ^^^^^^^^ ^^^^^^^^ >> Version info * This isn't a customizable value - it's a preset. So far: @@ -2150,24 +2162,24 @@ F1: 00000000 ^^^^^^^^ >> Unknown, usually always 00 F2: 000000BD - ^^^^^^ >> Unknown, usually always 00 - ^^ >> Unknown, usually always BD, possible tearing counter value? + ^^^^^^ >> Counter 0 + ^^ >> Tearing 0 F3: 000000BD - ^^^^^^ >> Unknown, usually always 00 - ^^ >> Unknown, usually always BD, possible tearing counter value? + ^^^^^^ >> Counter 1 + ^^ >> Tearing 1 F4: 000000BD - ^^^^^^ >> Unknown, usually always 00 - ^^ >> Unknown, usually always BD, possible tearing counter value? + ^^^^^^ >> Counter 2 + ^^ >> Tearing 2 F5: 00000000 ^^^^^^^^ >> Unknown, usually always 00 F6: 44000400 ^^^^ >> ATQA in byte reverse order. 4400 = ATQA of 0044 - ^^ >> Unknown, usually always set to 04. Changing this value also has something to do with the SAK value in the next byte - ^^ >> SAK, if previous byte set to 04 + ^^ >> SAK1, usually set to 04 to call for CL2 + ^^ >> SAK2, card uses this as SAK F7: 88AF0000 ^^ >> First byte of UID BCC calculation, for Ultralight family is always 88 per the datasheet diff --git a/doc/md/Development/Makefile-vs-CMake.md b/doc/md/Development/Makefile-vs-CMake.md index 934e4b138..2d1094970 100644 --- a/doc/md/Development/Makefile-vs-CMake.md +++ b/doc/md/Development/Makefile-vs-CMake.md @@ -65,6 +65,7 @@ At the moment both are maintained because they don't perfectly overlap yet. | dep mbedtls | in_common | in_common | no sys lib: missing support for CMAC in def conf (btw no .pc available) | | dep python3 | opt, sys, < 3.8 & 3.8 | opt, sys, < 3.8 & 3.8 | | | python3 detection | pc | pkg_search_module | | +| force python3 version | `PYTHON3_PKGCONFIG=python-3.12` | `-DPYTHON3_PKGCONFIG=python-3.12` | | | `SKIPPYTHON` | yes | yes | | | dep pthread | sys | sys | | | pthread detection | **none** | **none** (1) | (1) cf https://stackoverflow.com/questions/1620918/cmake-and-libpthread ? | diff --git a/doc/md/Use_of_Proxmark/0_Compilation-Instructions.md b/doc/md/Use_of_Proxmark/0_Compilation-Instructions.md index d8dd7d5e0..63076e720 100644 --- a/doc/md/Use_of_Proxmark/0_Compilation-Instructions.md +++ b/doc/md/Use_of_Proxmark/0_Compilation-Instructions.md @@ -10,6 +10,7 @@ - [Compile for generic Proxmark3 platforms](#compile-for-generic-proxmark3-platforms) - [Get the latest commits](#get-the-latest-commits) - [Clean and compile everything](#clean-and-compile-everything) + - [if there are different Python 3 packages installed](#if-there-are-different-python-3-packages-installed) - [if you got an error](#if-you-got-an-error) - [Install](#install) - [Flash the BOOTROM & FULLIMAGE](#flash-the-bootrom--fullimage) @@ -56,6 +57,15 @@ git pull make clean && make -j ``` +### if there are different Python 3 packages installed +^[Top](#top) + +It is possible to point to a different Python 3 package. For example, to build against Python 3.11: + +```sh +make clean && make -j PYTHON3_PKGCONFIG=python-3.11 +``` + ### if you got an error ^[Top](#top) diff --git a/include/hitag.h b/include/hitag.h index 334ab773d..451952ce4 100644 --- a/include/hitag.h +++ b/include/hitag.h @@ -65,30 +65,6 @@ typedef enum { HT2_LAST_CMD = HT2F_UID_ONLY, } PACKED hitag_function; -typedef struct { - hitag_function cmd; - uint8_t page; - uint8_t page_count; - uint8_t data[HITAGS_PAGE_SIZE]; - uint8_t NrAr[HITAG_NRAR_SIZE]; - // unaligned access to key as uint64_t will abort. - // todo: Why does the compiler without -munaligned-access generate unaligned-access code in the first place? - uint8_t key[HITAG_CRYPTOKEY_SIZE] __attribute__((aligned(4))); - uint8_t pwd[HITAG_PASSWORD_SIZE]; - - // Hitag 1 section. - // will reuse pwd or key field. - uint8_t key_no; - uint8_t logdata_0[4]; - uint8_t logdata_1[4]; - uint8_t nonce[4]; -} PACKED lf_hitag_data_t; - -typedef struct { - int status; - uint8_t data[256]; -} PACKED lf_hitag_crack_response_t; - //--------------------------------------------------------- // Hitag S //--------------------------------------------------------- @@ -111,15 +87,6 @@ typedef enum TAG_STATE { HT_WRITING_BLOCK_DATA } TSATE; -//number of start-of-frame bits -typedef enum SOF_TYPE { - HT_STANDARD = 0, - HT_ADVANCED, - HT_FAST_ADVANCED, - HT_ONE, - HT_NO_BITS -} stype; - typedef struct { // con0 uint8_t MEMT : 2; @@ -156,7 +123,6 @@ struct hitagS_tag { TSATE tstate; // tag-state int max_page; - stype mode; union { uint8_t pages[64][4]; @@ -177,6 +143,33 @@ struct hitagS_tag { } PACKED; +typedef struct { + hitag_function cmd; + uint8_t page; + uint8_t page_count; + uint8_t data[HITAGS_PAGE_SIZE]; + uint8_t NrAr[HITAG_NRAR_SIZE]; + // unaligned access to key as uint64_t will abort. + // todo: Why does the compiler without -munaligned-access generate unaligned-access code in the first place? + uint8_t key[HITAG_CRYPTOKEY_SIZE] __attribute__((aligned(4))); + uint8_t pwd[HITAG_PASSWORD_SIZE]; + + // Hitag 1 section. + // will reuse pwd or key field. + uint8_t key_no; + uint8_t logdata_0[4]; + uint8_t logdata_1[4]; + uint8_t nonce[4]; + + //Hitag s section + uint8_t mode; +} PACKED lf_hitag_data_t; + +typedef struct { + int status; + uint8_t data[256]; +} PACKED lf_hitag_crack_response_t; + typedef struct { union { uint8_t asBytes[HITAGS_PAGE_SIZE]; diff --git a/include/mifare.h b/include/mifare.h index 4082b174e..69665cdc8 100644 --- a/include/mifare.h +++ b/include/mifare.h @@ -21,11 +21,10 @@ #include "common.h" +// These are also used to construct AUTH commands (60+x) #define MF_KEY_A 0 #define MF_KEY_B 1 -#define MF_KEY_BD08S 2 -#define MF_KEY_BD08 3 -#define MF_KEY_BD32 4 +#define MF_KEY_BD 4 #define MF_MAD1_SECTOR 0x00 #define MF_MAD2_SECTOR 0x10 diff --git a/include/pm3_cmd.h b/include/pm3_cmd.h index e3eb64978..0cfc9bfaa 100644 --- a/include/pm3_cmd.h +++ b/include/pm3_cmd.h @@ -433,6 +433,7 @@ typedef struct { #define CMD_FLASHMEM_DOWNLOADED 0x0124 #define CMD_FLASHMEM_INFO 0x0125 #define CMD_FLASHMEM_SET_SPIBAUDRATE 0x0126 +#define CMD_FLASHMEM_PAGES64K 0x0127 // RDV40, High level flashmem SPIFFS Manipulation // ALL function will have a lazy or Safe version @@ -766,21 +767,72 @@ typedef struct { #define CMD_UNKNOWN 0xFFFF //Mifare simulation flags -#define FLAG_INTERACTIVE 0x01 -#define FLAG_4B_UID_IN_DATA 0x02 -#define FLAG_7B_UID_IN_DATA 0x04 -#define FLAG_10B_UID_IN_DATA 0x08 -#define FLAG_UID_IN_EMUL 0x10 -#define FLAG_NR_AR_ATTACK 0x20 -#define FLAG_MF_MINI 0x80 -#define FLAG_MF_1K 0x100 -#define FLAG_MF_2K 0x200 -#define FLAG_MF_4K 0x400 -#define FLAG_FORCED_ATQA 0x800 -#define FLAG_FORCED_SAK 0x1000 -#define FLAG_CVE21_0430 0x2000 -#define FLAG_RATS_IN_DATA 0x4000 -#define FLAG_NESTED_AUTH_ATTACK 0x8000 +// In interactive mode, we are expected to finish the operation with an ACK +#define FLAG_INTERACTIVE 0x0001 +#define FLAG_ATQA_IN_DATA 0x0002 +#define FLAG_SAK_IN_DATA 0x0004 +#define FLAG_RATS_IN_DATA 0x0008 + +// internal constants, use the function macros instead +#define FLAG_MASK_UID 0x0030 +#define FLAG_UID_IN_EMUL 0x0000 +#define FLAG_4B_UID_IN_DATA 0x0010 +#define FLAG_7B_UID_IN_DATA 0x0020 +#define FLAG_10B_UID_IN_DATA 0x0030 +// if there is a UID in the data-section to be used: +// note: if UIDLEN is wrong, we default to FLAG_UID_IN_EMUL +#define FLAG_SET_UID_IN_DATA(flags, len) {\ + flags = (flags & (~FLAG_MASK_UID))|\ + (len == 4 ? FLAG_4B_UID_IN_DATA : \ + (len == 7 ? FLAG_7B_UID_IN_DATA : \ + (len == 10 ? FLAG_10B_UID_IN_DATA : \ + FLAG_UID_IN_EMUL)));\ + } +// else we tell to take UID from block 0: +#define FLAG_SET_UID_IN_EMUL(flags) {flags = (flags & (~FLAG_MASK_UID))|FLAG_UID_IN_EMUL;} +#define IS_FLAG_UID_IN_DATA(flags, len) (\ + (flags & FLAG_MASK_UID) == \ + (len == 4 ? FLAG_4B_UID_IN_DATA : \ + (len == 7 ? FLAG_7B_UID_IN_DATA : \ + (len == 10 ? FLAG_10B_UID_IN_DATA : \ + FLAG_UID_IN_EMUL)))\ + ) +#define IS_FLAG_UID_IN_EMUL(flags) ((flags & FLAG_MASK_UID) == FLAG_UID_IN_EMUL) + +// internal constants, use the function macros instead +#define MIFARE_4K_MAX_BYTES 4096 +#define MIFARE_2K_MAX_BYTES 2048 +#define MIFARE_1K_MAX_BYTES 1024 +#define MIFARE_MINI_MAX_BYTES 320 +#define FLAG_MASK_MF_SIZE 0x00C0 +#define FLAG_MF_MINI 0x0000 +#define FLAG_MF_1K 0x0040 +#define FLAG_MF_2K 0x0080 +#define FLAG_MF_4K 0x00C0 +#define FLAG_SET_MF_SIZE(flags, size) {\ + flags = (flags & (~FLAG_MASK_MF_SIZE))|\ + (size == MIFARE_MINI_MAX_BYTES ? FLAG_MF_MINI : \ + (size == MIFARE_1K_MAX_BYTES ? FLAG_MF_1K : \ + (size == MIFARE_2K_MAX_BYTES ? FLAG_MF_2K : \ + (size == MIFARE_4K_MAX_BYTES ? FLAG_MF_4K : \ + 0))));\ + } +// else we tell to take UID from block 0: +#define IS_FLAG_MF_SIZE(flags, size) (\ + (flags & FLAG_MASK_MF_SIZE) == \ + (size == MIFARE_MINI_MAX_BYTES ? FLAG_MF_MINI : \ + (size == MIFARE_1K_MAX_BYTES ? FLAG_MF_1K : \ + (size == MIFARE_2K_MAX_BYTES ? FLAG_MF_2K : \ + (size == MIFARE_4K_MAX_BYTES ? FLAG_MF_4K : \ + 0))))\ + ) + +#define FLAG_MF_USE_READ_KEYB 0x0100 +#define FLAG_CVE21_0430 0x0200 +// collect NR_AR responses for bruteforcing later +#define FLAG_NR_AR_ATTACK 0x0400 +// support nested authentication attack +#define FLAG_NESTED_AUTH_ATTACK 0x0800 #define MODE_SIM_CSN 0 @@ -788,10 +840,11 @@ typedef struct { #define MODE_FULLSIM 2 // Static Nonce detection -#define NONCE_FAIL 0x01 -#define NONCE_NORMAL 0x02 -#define NONCE_STATIC 0x03 -#define NONCE_STATIC_ENC 0x04 +#define NONCE_FAIL 0x01 +#define NONCE_NORMAL 0x02 +#define NONCE_STATIC 0x03 +#define NONCE_STATIC_ENC 0x04 +#define NONCE_SUPERSTATIC 0x05 // Dbprintf flags #define FLAG_RAWPRINT 0x00 diff --git a/include/pmflash.h b/include/pmflash.h index 65e373f8f..bbed4b12e 100644 --- a/include/pmflash.h +++ b/include/pmflash.h @@ -37,10 +37,16 @@ #ifndef FLASH_MEM_MAX_SIZE # define FLASH_MEM_MAX_SIZE 0x40000 // (262144) #endif +#ifndef FLASH_MEM_MAX_SIZE_P +# define FLASH_MEM_MAX_SIZE_P(p64k) (1024 * 64 * (p64k)) +#endif #ifndef FLASH_MEM_MAX_4K_SECTOR # define FLASH_MEM_MAX_4K_SECTOR 0x3F000 #endif +#ifndef FLASH_MEM_MAX_4K_SECTOR_P +# define FLASH_MEM_MAX_4K_SECTOR_P(p64k) (FLASH_MEM_MAX_SIZE_P(p64k) - 4096) +#endif #ifndef FLASH_MEM_ID_LEN # define FLASH_MEM_ID_LEN 8 @@ -54,6 +60,9 @@ // -1 for historical compatibility with already released Proxmark3 RDV4.0 devices # define FLASH_MEM_SIGNATURE_OFFSET (FLASH_MEM_MAX_SIZE - FLASH_MEM_SIGNATURE_LEN - 1) #endif +#ifndef FLASH_MEM_SIGNATURE_OFFSET_P +# define FLASH_MEM_SIGNATURE_OFFSET_P(p64k) (FLASH_MEM_MAX_SIZE_P(p64k) - FLASH_MEM_SIGNATURE_LEN - 1) +#endif #ifndef T55XX_CONFIG_LEN # define T55XX_CONFIG_LEN sizeof( t55xx_configurations_t ) @@ -62,6 +71,9 @@ #ifndef T55XX_CONFIG_OFFSET # define T55XX_CONFIG_OFFSET (FLASH_MEM_MAX_4K_SECTOR - 0x2000) #endif +#ifndef T55XX_CONFIG_OFFSET_P +# define T55XX_CONFIG_OFFSET_P(p64k) (FLASH_MEM_MAX_4K_SECTOR_P(p64k) - 0x2000) +#endif // Reserved space for T55XX PWD = 4 kb #ifndef DEFAULT_T55XX_KEYS_OFFSET @@ -69,6 +81,9 @@ # define DEFAULT_T55XX_KEYS_OFFSET (T55XX_CONFIG_OFFSET - DEFAULT_T55XX_KEYS_LEN) # define DEFAULT_T55XX_KEYS_MAX ((DEFAULT_T55XX_KEYS_LEN - 2) / 4) #endif +#ifndef DEFAULT_T55XX_KEYS_OFFSET_P +# define DEFAULT_T55XX_KEYS_OFFSET_P(p64k) (T55XX_CONFIG_OFFSET_P(p64k) - DEFAULT_T55XX_KEYS_LEN) +#endif // Reserved space for iClass keys = 4 kb #ifndef DEFAULT_ICLASS_KEYS_OFFSET @@ -76,6 +91,9 @@ # define DEFAULT_ICLASS_KEYS_OFFSET (DEFAULT_T55XX_KEYS_OFFSET - DEFAULT_ICLASS_KEYS_LEN) # define DEFAULT_ICLASS_KEYS_MAX ((DEFAULT_ICLASS_KEYS_LEN - 2) / 8) #endif +#ifndef DEFAULT_ICLASS_KEYS_OFFSET_P +# define DEFAULT_ICLASS_KEYS_OFFSET_P(p64k) (DEFAULT_T55XX_KEYS_OFFSET_P(p64k) - DEFAULT_ICLASS_KEYS_LEN) +#endif // Reserved space for MIFARE Keys = 12 kb #ifndef DEFAULT_MF_KEYS_OFFSET @@ -83,6 +101,9 @@ # define DEFAULT_MF_KEYS_OFFSET (DEFAULT_ICLASS_KEYS_OFFSET - DEFAULT_MF_KEYS_LEN) # define DEFAULT_MF_KEYS_MAX ((DEFAULT_MF_KEYS_LEN - 2) / 6) #endif +#ifndef DEFAULT_MF_KEYS_OFFSET_P +# define DEFAULT_MF_KEYS_OFFSET_P(p64k) (DEFAULT_ICLASS_KEYS_OFFSET_P(p64k) - DEFAULT_MF_KEYS_LEN) +#endif // RDV40, validation structure to help identifying that client/firmware is talking with RDV40 typedef struct { @@ -91,9 +112,5 @@ typedef struct { uint8_t signature[FLASH_MEM_SIGNATURE_LEN]; } PACKED rdv40_validation_t; -// SPIFFS current allocates 192KB of the 256KB available. -#ifndef FLASH_SPIFFS_ALLOCATED_SIZE -# define FLASH_SPIFFS_ALLOCATED_SIZE (1024 * 192) -#endif #endif // __PMFLASH_H diff --git a/include/protocols.h b/include/protocols.h index 58c857a6f..3aa40ea79 100644 --- a/include/protocols.h +++ b/include/protocols.h @@ -250,6 +250,8 @@ ISO 7816-4 Basic interindustry commands. For command APDU's. // bit 3 - turn on FPGA // bit 4 - turn off FPGA // bit 5 - set datain instead of issuing USB reply (called via ARM for StandAloneMode14a) +// bit 6 - wipe tag. +// bit 7 - use USCUID/GDM (20/23) magic wakeup #define MAGIC_UID 0x01 #define MAGIC_WUPC 0x02 #define MAGIC_HALT 0x04 @@ -257,7 +259,8 @@ ISO 7816-4 Basic interindustry commands. For command APDU's. #define MAGIC_OFF 0x10 #define MAGIC_DATAIN 0x20 #define MAGIC_WIPE 0x40 -#define MAGIC_SINGLE (MAGIC_WUPC | MAGIC_HALT | MAGIC_INIT | MAGIC_OFF) //0x1E +#define MAGIC_GDM_ALT_WUPC 0x80 +#define MAGIC_SINGLE (MAGIC_HALT | MAGIC_INIT | MAGIC_OFF) //0x1E // by CMD_HF_MIFARE_CIDENT / Flags #define MAGIC_FLAG_NONE 0x0000 diff --git a/tools/mfd_aes_brute/Makefile b/tools/mfd_aes_brute/Makefile index 8080b67b5..6f8fa44a9 100644 --- a/tools/mfd_aes_brute/Makefile +++ b/tools/mfd_aes_brute/Makefile @@ -1,7 +1,7 @@ MYSRCPATHS = ../../common ../../common/mbedtls MYSRCS = util_posix.c randoms.c MYINCLUDES = -I../../include -I../../common -I../../common/mbedtls -MYCFLAGS = -Ofast +MYCFLAGS = -O3 -ffast-math MYDEFS = MYLDLIBS = -lcrypto @@ -43,7 +43,8 @@ ifeq ($(USE_MACPORTS),1) MYLDFLAGS += -L$(MACPORTS_PREFIX)/lib/openssl-3 -L$(MACPORTS_PREFIX)/lib/openssl-1.1 endif -showinfo: $(info c flags: $(MYCFLAGS)) +showinfo: + @echo "C flags: $(MYCFLAGS)" brute_key : $(OBJDIR)/brute_key.o $(MYOBJS)