mirror of
https://github.com/RfidResearchGroup/proxmark3.git
synced 2025-08-22 06:13:51 -07:00
Merge branch 'master' into emv_smart
This commit is contained in:
commit
ce5f24776e
115 changed files with 4295 additions and 2128 deletions
27
CHANGELOG.md
27
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 <blk>` does not coredump reading EM4450 tag (@ANTodorov)
|
||||
- Fixed `lf search` and `lf em 4x50 rdbl -b <blk>` 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)
|
||||
|
|
14
Makefile
14
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
|
||||
|
|
|
@ -53,7 +53,7 @@ USERMOD = usermod -aG
|
|||
ADDUSER = adduser
|
||||
GETENT_BL = getent group bluetooth
|
||||
|
||||
|
||||
PYTHON3_PKGCONFIG ?= python3
|
||||
|
||||
PATHSEP=/
|
||||
PREFIX ?= /usr/local
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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()) {
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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};
|
||||
|
||||
|
|
|
@ -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};
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
384
armsrc/hitagS.c
384
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 };
|
||||
|
|
400
armsrc/iclass.c
400
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);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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)
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -39,3 +39,5 @@ C1B74D7478053AE2
|
|||
#
|
||||
# default iCLASS RFIDeas
|
||||
6B65797374726B72
|
||||
# Retrieved from Custom Keyed Systems
|
||||
E9924C13F4BFA82C
|
||||
|
|
|
@ -354,6 +354,8 @@ AFBECD121004
|
|||
# Onity S1 A/B
|
||||
8A19D40CF2B5
|
||||
#
|
||||
3961EA82C46D
|
||||
#
|
||||
# 24-7
|
||||
D21762B2DE3B
|
||||
0E83A374B513
|
||||
|
@ -2674,4 +2676,25 @@ CE7FCCBBA5D8
|
|||
F8E385E5A2A0
|
||||
B27678B5C4AE
|
||||
D68D7EBB9551
|
||||
7AB63F082328
|
||||
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
|
||||
|
|
|
@ -13,3 +13,4 @@
|
|||
ln -s build/proxmark3 .
|
||||
)
|
||||
ln -s ../pyscripts/pm3.py
|
||||
ln -s ../lualibs/dkjson.lua
|
||||
|
|
|
@ -1,4 +1,8 @@
|
|||
#!/bin/bash
|
||||
|
||||
cd ..
|
||||
make -j
|
||||
(
|
||||
cd ..
|
||||
make -j
|
||||
)
|
||||
ln -s ../pyscripts/pm3.py
|
||||
ln -s ../lualibs/dkjson.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
|
||||
|
|
|
@ -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'])
|
||||
|
|
|
@ -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)
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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 .
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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'])
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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 <uid>] [-t] [-3] [-s <signature>] [-w 1] [-R/-B <blk>] [-S/-E <sec>] [-g/-c/-b/-2/-7/-d/-a/-n/-r <0/1>]
|
||||
script run hf_mf_uscuid_uid_prog [-h] [-u <uid>] [-t] [-3] [-s <signature>] [-w 1] [-R -B <blk>] [-S -E <sec>] [-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)
|
||||
|
|
|
@ -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 <uid> "
|
||||
|
@ -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
|
||||
--
|
|
@ -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')
|
||||
|
||||
|
|
995
client/pyscripts/fm11rf08_full.py
Normal file
995
client/pyscripts/fm11rf08_full.py
Normal file
|
@ -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()
|
File diff suppressed because it is too large
Load diff
|
@ -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()
|
||||
|
||||
|
|
|
@ -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:")
|
||||
|
|
|
@ -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)
|
||||
|
||||
|
|
|
@ -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") +
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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"
|
||||
}
|
||||
]
|
||||
]
|
|
@ -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",
|
||||
|
|
|
@ -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];
|
||||
}
|
||||
|
||||
|
|
|
@ -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", "<hex>", "bytes to calc missing XOR in a LCR"),
|
||||
arg_str1("d", "data", "<hex>", "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"},
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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", "<dec>", "Region code. 4 bits max"),
|
||||
arg_u64_1("f", "fc", "<dec>", "Facility code. 2 bytes max"),
|
||||
arg_u64_1("c", "cn", "<dec>", "Card number. 3 bytes max"),
|
||||
arg_u64_1("i", "il", "<dec>", "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}
|
||||
};
|
||||
|
||||
|
|
|
@ -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", "<dec>", "use config slot at index"),
|
||||
arg_int0(NULL, "g", "<dec>", "use config option"),
|
||||
arg_int0(NULL, "ki", "<dec>", "Card Key - index to select key from memory 'hf iclass managekeys'"),
|
||||
arg_int0(NULL, "krki", "<dec>", "Elite Keyroll Key - index to select key from memory 'hf iclass managekeys'"),
|
||||
arg_int0(NULL, "eki", "<dec>", "Elite Key - index to select key from memory 'hf iclass managekeys'"),
|
||||
arg_int0(NULL, "mrki", "<dec>", "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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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));
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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");
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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", "<hex>", "nonce / answer writer, 8 hex bytes"),
|
||||
arg_lit0(NULL, "crypto", "crypto mode"),
|
||||
arg_str0("k", "key", "<hex>", "pwd or key, 4 or 6 hex bytes"),
|
||||
arg_int0("m", "mode", "<dec>", "response protocol mode. 0 (Standard 00110), 1 (Advanced 11000), 2 (Advanced 11001), 3 (Fast Advanced 11010) (def: 3)"),
|
||||
arg_int0("p", "page", "<dec>", "page address to read from"),
|
||||
arg_int0("c", "count", "<dec>", "how many pages to read. '0' reads all pages up to the end page (default: 1)"),
|
||||
arg_int0("c", "count", "<dec>", "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", "<hex>", "nonce / answer writer, 8 hex bytes"),
|
||||
arg_lit0(NULL, "crypto", "crypto mode"),
|
||||
arg_str0("k", "key", "<hex>", "pwd or key, 4 or 6 hex bytes"),
|
||||
arg_int0("m", "mode", "<dec>", "response protocol mode. 0 (Standard 00110), 1 (Advanced 11000), 2 (Advanced 11001), 3 (Fast Advanced 11010) (def: 3)"),
|
||||
arg_int1("p", "page", "<dec>", "page address to write to"),
|
||||
arg_str1("d", "data", "<hex>", "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);
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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", "<hex>", "Applet ID to select. By default A0000003080000100 will be used"),
|
||||
arg_str1(NULL, "nonce", "<hex>", "Nonce to sign."),
|
||||
arg_int0(NULL, "slot", "<dec id>", "Slot number. Default will be 0x9E (card auth cert)."),
|
||||
arg_int0(NULL, "alg", "<dec>", "Algorithm to use to sign. Example values: 06=RSA-1024, 07=RSA-2048, 11=ECC-P256 (default), 14=ECC-P384"),
|
||||
arg_int0(NULL, "alg", "<dec>", "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);
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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++) {
|
||||
|
|
|
@ -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));
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -11,8 +11,11 @@
|
|||
|
||||
#ifdef PYWRAP
|
||||
#include <Python.h>
|
||||
%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;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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" },
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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) {
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue