mirror of
https://github.com/RfidResearchGroup/proxmark3.git
synced 2025-08-20 21:33:47 -07:00
Merge branch 'master' into patch-1
Signed-off-by: Jarek Barwinski <116510448+jareckib@users.noreply.github.com>
This commit is contained in:
commit
ddd148329e
23 changed files with 3342 additions and 420 deletions
|
@ -3,6 +3,7 @@ All notable changes to this project will be documented in this file.
|
||||||
This project uses the changelog in accordance with [keepchangelog](http://keepachangelog.com/). Please use this to write notable changes, which is not the same as git commit log...
|
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]
|
||||||
|
- Major update to `lf em 4x70` internals on ARM side; Enabling improved debugging and reliability (@henrygab)
|
||||||
- Improved `pcf7931` generic readability of the code. Unified datatypes and added documentation/explainations (@tinooo)
|
- Improved `pcf7931` generic readability of the code. Unified datatypes and added documentation/explainations (@tinooo)
|
||||||
- Improved `lf pcf7931` read code - fixed some checks for more stability (@tinooo)
|
- Improved `lf pcf7931` read code - fixed some checks for more stability (@tinooo)
|
||||||
- Changed `trace list -t seos` - improved annotation (@iceman1001)
|
- Changed `trace list -t seos` - improved annotation (@iceman1001)
|
||||||
|
|
1168
armsrc/em4x70.c
1168
armsrc/em4x70.c
File diff suppressed because it is too large
Load diff
|
@ -19,12 +19,11 @@
|
||||||
#ifndef EM4x70_H
|
#ifndef EM4x70_H
|
||||||
#define EM4x70_H
|
#define EM4x70_H
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <assert.h>
|
||||||
#include "../include/em4x70.h"
|
#include "../include/em4x70.h"
|
||||||
|
|
||||||
typedef struct {
|
|
||||||
uint8_t data[32];
|
|
||||||
} em4x70_tag_t;
|
|
||||||
|
|
||||||
typedef enum {
|
typedef enum {
|
||||||
RISING_EDGE,
|
RISING_EDGE,
|
||||||
FALLING_EDGE
|
FALLING_EDGE
|
||||||
|
|
|
@ -320,7 +320,7 @@ 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 hitag2_reader_send_bit(int bit) {
|
||||||
// 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
|
||||||
|
|
||||||
|
@ -349,13 +349,13 @@ 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 hitag2_reader_send_frame(const uint8_t *frame, size_t frame_len) {
|
||||||
WDT_HIT();
|
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 / 8] >> (7 - (i % 8))) & 1);
|
wait += hitag2_reader_send_bit((frame[i / 8] >> (7 - (i % 8))) & 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Send EOF
|
// Send EOF
|
||||||
|
@ -378,14 +378,14 @@ static uint32_t hitag_reader_send_frame(const uint8_t *frame, size_t frame_len)
|
||||||
|
|
||||||
// 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_framebits(const uint8_t *frame, size_t frame_len) {
|
static uint32_t hitag2_reader_send_framebits(const uint8_t *frame, size_t frame_len) {
|
||||||
|
|
||||||
WDT_HIT();
|
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 += hitag2_reader_send_bit(frame[i]);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Send EOF
|
// Send EOF
|
||||||
|
@ -1863,7 +1863,7 @@ void ReaderHitag(const lf_hitag_data_t *payload, bool ledcontrol) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Transmit the reader frame
|
// Transmit the reader frame
|
||||||
command_duration = hitag_reader_send_frame(tx, txlen);
|
command_duration = hitag2_reader_send_frame(tx, txlen);
|
||||||
response_start = command_start + command_duration;
|
response_start = command_start + command_duration;
|
||||||
|
|
||||||
// Let the antenna and ADC values settle
|
// Let the antenna and ADC values settle
|
||||||
|
@ -2214,7 +2214,7 @@ void WriterHitag(const lf_hitag_data_t *payload, bool ledcontrol) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Transmit the reader frame
|
// Transmit the reader frame
|
||||||
command_duration = hitag_reader_send_frame(tx, txlen);
|
command_duration = hitag2_reader_send_frame(tx, txlen);
|
||||||
|
|
||||||
// global write state variable used
|
// global write state variable used
|
||||||
// tearoff occurred
|
// tearoff occurred
|
||||||
|
@ -2434,9 +2434,9 @@ static void ht2_send(bool turn_on, uint32_t *cmd_start
|
||||||
|
|
||||||
// Transmit the reader frame
|
// Transmit the reader frame
|
||||||
if (send_bits) {
|
if (send_bits) {
|
||||||
*cmd_duration = hitag_reader_send_framebits(tx, txlen);
|
*cmd_duration = hitag2_reader_send_framebits(tx, txlen);
|
||||||
} else {
|
} else {
|
||||||
*cmd_duration = hitag_reader_send_frame(tx, txlen);
|
*cmd_duration = hitag2_reader_send_frame(tx, txlen);
|
||||||
}
|
}
|
||||||
|
|
||||||
*resp_start = (*cmd_start + *cmd_duration);
|
*resp_start = (*cmd_start + *cmd_duration);
|
||||||
|
|
|
@ -419,7 +419,7 @@ static void hts_init_clock(void) {
|
||||||
static int check_select(const uint8_t *rx, uint32_t uid) {
|
static int check_select(const uint8_t *rx, uint32_t uid) {
|
||||||
|
|
||||||
// global var?
|
// global var?
|
||||||
concatbits((uint8_t *)&reader_selected_uid, 0, rx, 5, 32);
|
concatbits((uint8_t *)&reader_selected_uid, 0, rx, 5, 32, false);
|
||||||
reader_selected_uid = BSWAP_32(reader_selected_uid);
|
reader_selected_uid = BSWAP_32(reader_selected_uid);
|
||||||
|
|
||||||
if (reader_selected_uid == uid) {
|
if (reader_selected_uid == uid) {
|
||||||
|
@ -1090,7 +1090,7 @@ static int hts_select_tag(const lf_hitag_data_t *packet, uint8_t *tx, size_t siz
|
||||||
|
|
||||||
protocol_mode = packet->mode;
|
protocol_mode = packet->mode;
|
||||||
uint8_t cmd = protocol_mode;
|
uint8_t cmd = protocol_mode;
|
||||||
txlen = concatbits(tx, txlen, &cmd, 0, 5);
|
txlen = concatbits(tx, txlen, &cmd, 0, 5, false);
|
||||||
hts_send_receive(tx, txlen, rx, sizeofrx, &rxlen, t_wait, ledcontrol, true);
|
hts_send_receive(tx, txlen, rx, sizeofrx, &rxlen, t_wait, ledcontrol, true);
|
||||||
|
|
||||||
if (rxlen != 32) {
|
if (rxlen != 32) {
|
||||||
|
@ -1105,10 +1105,10 @@ static int hts_select_tag(const lf_hitag_data_t *packet, uint8_t *tx, size_t siz
|
||||||
// select uid
|
// select uid
|
||||||
txlen = 0;
|
txlen = 0;
|
||||||
cmd = HITAGS_SELECT;
|
cmd = HITAGS_SELECT;
|
||||||
txlen = concatbits(tx, txlen, &cmd, 0, 5);
|
txlen = concatbits(tx, txlen, &cmd, 0, 5, false);
|
||||||
txlen = concatbits(tx, txlen, rx, 0, 32);
|
txlen = concatbits(tx, txlen, rx, 0, 32, false);
|
||||||
uint8_t crc = CRC8Hitag1Bits(tx, txlen);
|
uint8_t crc = CRC8Hitag1Bits(tx, txlen);
|
||||||
txlen = concatbits(tx, txlen, &crc, 0, 8);
|
txlen = concatbits(tx, txlen, &crc, 0, 8, false);
|
||||||
|
|
||||||
hts_send_receive(tx, txlen, rx, sizeofrx, &rxlen, HITAG_T_WAIT_SC, ledcontrol, false);
|
hts_send_receive(tx, txlen, rx, sizeofrx, &rxlen, HITAG_T_WAIT_SC, ledcontrol, false);
|
||||||
|
|
||||||
|
@ -1140,8 +1140,8 @@ static int hts_select_tag(const lf_hitag_data_t *packet, uint8_t *tx, size_t siz
|
||||||
}
|
}
|
||||||
|
|
||||||
txlen = 0;
|
txlen = 0;
|
||||||
txlen = concatbits(tx, txlen, rnd, 0, 32);
|
txlen = concatbits(tx, txlen, rnd, 0, 32, false);
|
||||||
txlen = concatbits(tx, txlen, auth_ks, 0, 32);
|
txlen = concatbits(tx, txlen, auth_ks, 0, 32, false);
|
||||||
|
|
||||||
DBG DbpString("Authenticating using key:");
|
DBG DbpString("Authenticating using key:");
|
||||||
DBG Dbhexdump(6, packet->key, false);
|
DBG Dbhexdump(6, packet->key, false);
|
||||||
|
@ -1173,13 +1173,13 @@ static int hts_select_tag(const lf_hitag_data_t *packet, uint8_t *tx, size_t siz
|
||||||
// send write page request
|
// send write page request
|
||||||
txlen = 0;
|
txlen = 0;
|
||||||
cmd = HITAGS_WRITE_PAGE;
|
cmd = HITAGS_WRITE_PAGE;
|
||||||
txlen = concatbits(tx, txlen, &cmd, 0, 4);
|
txlen = concatbits(tx, txlen, &cmd, 0, 4, false);
|
||||||
|
|
||||||
uint8_t addr = 64;
|
uint8_t addr = 64;
|
||||||
txlen = concatbits(tx, txlen, &addr, 0, 8);
|
txlen = concatbits(tx, txlen, &addr, 0, 8, false);
|
||||||
|
|
||||||
crc = CRC8Hitag1Bits(tx, txlen);
|
crc = CRC8Hitag1Bits(tx, txlen);
|
||||||
txlen = concatbits(tx, txlen, &crc, 0, 8);
|
txlen = concatbits(tx, txlen, &crc, 0, 8, false);
|
||||||
|
|
||||||
hts_send_receive(tx, txlen, rx, sizeofrx, &rxlen, HITAG_T_WAIT_SC, ledcontrol, false);
|
hts_send_receive(tx, txlen, rx, sizeofrx, &rxlen, HITAG_T_WAIT_SC, ledcontrol, false);
|
||||||
|
|
||||||
|
@ -1189,9 +1189,9 @@ static int hts_select_tag(const lf_hitag_data_t *packet, uint8_t *tx, size_t siz
|
||||||
}
|
}
|
||||||
|
|
||||||
txlen = 0;
|
txlen = 0;
|
||||||
txlen = concatbits(tx, txlen, packet->pwd, 0, 32);
|
txlen = concatbits(tx, txlen, packet->pwd, 0, 32, false);
|
||||||
crc = CRC8Hitag1Bits(tx, txlen);
|
crc = CRC8Hitag1Bits(tx, txlen);
|
||||||
txlen = concatbits(tx, txlen, &crc, 0, 8);
|
txlen = concatbits(tx, txlen, &crc, 0, 8, false);
|
||||||
|
|
||||||
hts_send_receive(tx, txlen, rx, sizeofrx, &rxlen, HITAG_T_WAIT_SC, ledcontrol, false);
|
hts_send_receive(tx, txlen, rx, sizeofrx, &rxlen, HITAG_T_WAIT_SC, ledcontrol, false);
|
||||||
|
|
||||||
|
@ -1287,11 +1287,11 @@ void hts_read(const lf_hitag_data_t *payload, bool ledcontrol) {
|
||||||
//send read request
|
//send read request
|
||||||
size_t txlen = 0;
|
size_t txlen = 0;
|
||||||
uint8_t cmd = HITAGS_READ_PAGE;
|
uint8_t cmd = HITAGS_READ_PAGE;
|
||||||
txlen = concatbits(tx, txlen, &cmd, 0, 4);
|
txlen = concatbits(tx, txlen, &cmd, 0, 4, false);
|
||||||
uint8_t addr = page_addr;
|
uint8_t addr = page_addr;
|
||||||
txlen = concatbits(tx, txlen, &addr, 0, 8);
|
txlen = concatbits(tx, txlen, &addr, 0, 8, false);
|
||||||
uint8_t crc = CRC8Hitag1Bits(tx, txlen);
|
uint8_t crc = CRC8Hitag1Bits(tx, txlen);
|
||||||
txlen = concatbits(tx, txlen, &crc, 0, 8);
|
txlen = concatbits(tx, txlen, &crc, 0, 8, false);
|
||||||
|
|
||||||
hts_send_receive(tx, txlen, rx, ARRAYLEN(rx), &rxlen, HITAG_T_WAIT_SC, ledcontrol, false);
|
hts_send_receive(tx, txlen, rx, ARRAYLEN(rx), &rxlen, HITAG_T_WAIT_SC, ledcontrol, false);
|
||||||
|
|
||||||
|
@ -1396,13 +1396,13 @@ void hts_write_page(const lf_hitag_data_t *payload, bool ledcontrol) {
|
||||||
txlen = 0;
|
txlen = 0;
|
||||||
|
|
||||||
uint8_t cmd = HITAGS_WRITE_PAGE;
|
uint8_t cmd = HITAGS_WRITE_PAGE;
|
||||||
txlen = concatbits(tx, txlen, &cmd, 0, 4);
|
txlen = concatbits(tx, txlen, &cmd, 0, 4, false);
|
||||||
|
|
||||||
uint8_t addr = payload->page;
|
uint8_t addr = payload->page;
|
||||||
txlen = concatbits(tx, txlen, &addr, 0, 8);
|
txlen = concatbits(tx, txlen, &addr, 0, 8, false);
|
||||||
|
|
||||||
uint8_t crc = CRC8Hitag1Bits(tx, txlen);
|
uint8_t crc = CRC8Hitag1Bits(tx, txlen);
|
||||||
txlen = concatbits(tx, txlen, &crc, 0, 8);
|
txlen = concatbits(tx, txlen, &crc, 0, 8, false);
|
||||||
|
|
||||||
hts_send_receive(tx, txlen, rx, ARRAYLEN(rx), &rxlen, HITAG_T_WAIT_SC, ledcontrol, false);
|
hts_send_receive(tx, txlen, rx, ARRAYLEN(rx), &rxlen, HITAG_T_WAIT_SC, ledcontrol, false);
|
||||||
|
|
||||||
|
@ -1430,9 +1430,9 @@ void hts_write_page(const lf_hitag_data_t *payload, bool ledcontrol) {
|
||||||
// }
|
// }
|
||||||
|
|
||||||
txlen = 0;
|
txlen = 0;
|
||||||
txlen = concatbits(tx, txlen, payload->data, 0, 32);
|
txlen = concatbits(tx, txlen, payload->data, 0, 32, false);
|
||||||
crc = CRC8Hitag1Bits(tx, txlen);
|
crc = CRC8Hitag1Bits(tx, txlen);
|
||||||
txlen = concatbits(tx, txlen, &crc, 0, 8);
|
txlen = concatbits(tx, txlen, &crc, 0, 8, false);
|
||||||
|
|
||||||
enable_page_tearoff = g_tearoff_enabled;
|
enable_page_tearoff = g_tearoff_enabled;
|
||||||
|
|
||||||
|
@ -1493,7 +1493,7 @@ int hts_read_uid(uint32_t *uid, bool ledcontrol, bool send_answer) {
|
||||||
size_t txlen = 0;
|
size_t txlen = 0;
|
||||||
uint8_t tx[HITAG_FRAME_LEN] = { 0x00 };
|
uint8_t tx[HITAG_FRAME_LEN] = { 0x00 };
|
||||||
|
|
||||||
txlen = concatbits(tx, txlen, &cmd, 0, 5);
|
txlen = concatbits(tx, txlen, &cmd, 0, 5, false);
|
||||||
|
|
||||||
hts_send_receive(tx, txlen, rx, ARRAYLEN(rx), &rxlen, HITAG_T_WAIT_FIRST, ledcontrol, true);
|
hts_send_receive(tx, txlen, rx, ARRAYLEN(rx), &rxlen, HITAG_T_WAIT_FIRST, ledcontrol, true);
|
||||||
|
|
||||||
|
|
|
@ -87,15 +87,15 @@ size_t DemodPCF7931(uint8_t **outBlocks, bool ledcontrol) {
|
||||||
samplePosLastEdge = 0;
|
samplePosLastEdge = 0;
|
||||||
block_done = 0;
|
block_done = 0;
|
||||||
bitPos = 0;
|
bitPos = 0;
|
||||||
lastClockDuration=0;
|
lastClockDuration = 0;
|
||||||
|
|
||||||
for (sample = 1 ; sample < g_GraphTraceLen-4; sample++) {
|
for (sample = 1 ; sample < g_GraphTraceLen - 4; sample++) {
|
||||||
// condition is searching for the next edge, in the expected diretion.
|
// condition is searching for the next edge, in the expected diretion.
|
||||||
//todo: without flouz
|
//todo: without flouz
|
||||||
dest[sample] = (uint8_t)(dest[sample-1] * IIR_CONST1 + dest[sample] * IIR_CONST2); // apply IIR filter
|
dest[sample] = (uint8_t)(dest[sample - 1] * IIR_CONST1 + dest[sample] * IIR_CONST2); // apply IIR filter
|
||||||
|
|
||||||
if ( ((dest[sample] + THRESHOLD) < dest[sample-1] && expectedNextEdge == FALLING ) ||
|
if (((dest[sample] + THRESHOLD) < dest[sample - 1] && expectedNextEdge == FALLING) ||
|
||||||
((dest[sample] - THRESHOLD) > dest[sample-1] && expectedNextEdge == RISING )) {
|
((dest[sample] - THRESHOLD) > dest[sample - 1] && expectedNextEdge == RISING)) {
|
||||||
//okay, next falling/rising edge found
|
//okay, next falling/rising edge found
|
||||||
|
|
||||||
expectedNextEdge = (expectedNextEdge == FALLING) ? RISING : FALLING; //toggle the next expected edge
|
expectedNextEdge = (expectedNextEdge == FALLING) ? RISING : FALLING; //toggle the next expected edge
|
||||||
|
@ -104,7 +104,7 @@ size_t DemodPCF7931(uint8_t **outBlocks, bool ledcontrol) {
|
||||||
lastClockDuration = samplePosCurrentEdge - samplePosLastEdge;
|
lastClockDuration = samplePosCurrentEdge - samplePosLastEdge;
|
||||||
samplePosLastEdge = sample;
|
samplePosLastEdge = sample;
|
||||||
|
|
||||||
// Dbprintf("%d, %d, edge found, len: %d, nextEdge: %d", sample, dest[sample], lastClockDuration*DECIMATION, expectedNextEdge);
|
// Dbprintf("%d, %d, edge found, len: %d, nextEdge: %d", sample, dest[sample], lastClockDuration*DECIMATION, expectedNextEdge);
|
||||||
|
|
||||||
// Switch depending on lastClockDuration length:
|
// Switch depending on lastClockDuration length:
|
||||||
// 16T0
|
// 16T0
|
||||||
|
@ -121,7 +121,7 @@ size_t DemodPCF7931(uint8_t **outBlocks, bool ledcontrol) {
|
||||||
block_done = 1;
|
block_done = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 32TO
|
// 32TO
|
||||||
} else if (ABS(lastClockDuration - _32T0) < TOLERANCE) {
|
} else if (ABS(lastClockDuration - _32T0) < TOLERANCE) {
|
||||||
// if the clock before also was 16T0, it is a PMC!
|
// if the clock before also was 16T0, it is a PMC!
|
||||||
if (ABS(beforeLastClockDuration - _16T0) < TOLERANCE) {
|
if (ABS(beforeLastClockDuration - _16T0) < TOLERANCE) {
|
||||||
|
@ -134,26 +134,26 @@ size_t DemodPCF7931(uint8_t **outBlocks, bool ledcontrol) {
|
||||||
samplePosLastEdge = sample;
|
samplePosLastEdge = sample;
|
||||||
block_done = 1;
|
block_done = 1;
|
||||||
|
|
||||||
// if no pmc, then its a normal bit.
|
// if no pmc, then its a normal bit.
|
||||||
// Check if its the second time, the edge changed if yes, then the bit is 0
|
// Check if its the second time, the edge changed if yes, then the bit is 0
|
||||||
} else if (half_switch == 1) {
|
} else if (half_switch == 1) {
|
||||||
bits[bitPos] = 0;
|
bits[bitPos] = 0;
|
||||||
// reset the edge counter to 0
|
// reset the edge counter to 0
|
||||||
half_switch = 0;
|
half_switch = 0;
|
||||||
bitPos++;
|
bitPos++;
|
||||||
|
|
||||||
// if it is the first time the edge changed. No bit value will be set here, bit if the
|
// if it is the first time the edge changed. No bit value will be set here, bit if the
|
||||||
// edge changes again, it will be. see case above.
|
// edge changes again, it will be. see case above.
|
||||||
} else
|
} else
|
||||||
half_switch++;
|
half_switch++;
|
||||||
|
|
||||||
// 64T0
|
// 64T0
|
||||||
} else if (ABS(lastClockDuration - _64T0) < TOLERANCE) {
|
} else if (ABS(lastClockDuration - _64T0) < TOLERANCE) {
|
||||||
// this means, bit here is 1
|
// this means, bit here is 1
|
||||||
bits[bitPos] = 1;
|
bits[bitPos] = 1;
|
||||||
bitPos++;
|
bitPos++;
|
||||||
|
|
||||||
// Error
|
// Error
|
||||||
} else {
|
} else {
|
||||||
// some Error. maybe check tolerances.
|
// some Error. maybe check tolerances.
|
||||||
// likeley to happen in the first block.
|
// likeley to happen in the first block.
|
||||||
|
@ -193,8 +193,8 @@ size_t DemodPCF7931(uint8_t **outBlocks, bool ledcontrol) {
|
||||||
half_switch = 0;
|
half_switch = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
}else {
|
} else {
|
||||||
// Dbprintf("%d, %d", sample, dest[sample]);
|
// Dbprintf("%d, %d", sample, dest[sample]);
|
||||||
}
|
}
|
||||||
|
|
||||||
// one block only holds 16byte (=128 bit) and then comes the PMC. so if more bit are found than 129, there must be an issue and PMC has not been identfied...
|
// one block only holds 16byte (=128 bit) and then comes the PMC. so if more bit are found than 129, there must be an issue and PMC has not been identfied...
|
||||||
|
@ -403,28 +403,28 @@ void ReadPCF7931(bool ledcontrol) {
|
||||||
|
|
||||||
|
|
||||||
end:
|
end:
|
||||||
/*
|
/*
|
||||||
Dbprintf("-----------------------------------------");
|
Dbprintf("-----------------------------------------");
|
||||||
Dbprintf("Memory content:");
|
Dbprintf("Memory content:");
|
||||||
Dbprintf("-----------------------------------------");
|
Dbprintf("-----------------------------------------");
|
||||||
for (i = 0; i < maxBlocks; ++i) {
|
for (i = 0; i < maxBlocks; ++i) {
|
||||||
if (memory_blocks[i][ALLOC])
|
if (memory_blocks[i][ALLOC])
|
||||||
print_result("Block", memory_blocks[i], 16);
|
print_result("Block", memory_blocks[i], 16);
|
||||||
else
|
else
|
||||||
Dbprintf("<missing block %d>", i);
|
Dbprintf("<missing block %d>", i);
|
||||||
}
|
}
|
||||||
Dbprintf("-----------------------------------------");
|
Dbprintf("-----------------------------------------");
|
||||||
|
|
||||||
if (found_blocks < maxBlocks) {
|
if (found_blocks < maxBlocks) {
|
||||||
Dbprintf("-----------------------------------------");
|
Dbprintf("-----------------------------------------");
|
||||||
Dbprintf("Blocks with unknown position:");
|
Dbprintf("Blocks with unknown position:");
|
||||||
Dbprintf("-----------------------------------------");
|
Dbprintf("-----------------------------------------");
|
||||||
for (i = 0; i < single_blocks_cnt; ++i)
|
for (i = 0; i < single_blocks_cnt; ++i)
|
||||||
print_result("Block", single_blocks[i], 16);
|
print_result("Block", single_blocks[i], 16);
|
||||||
|
|
||||||
Dbprintf("-----------------------------------------");
|
Dbprintf("-----------------------------------------");
|
||||||
}
|
}
|
||||||
*/
|
*/
|
||||||
|
|
||||||
reply_mix(CMD_ACK, 0, 0, 0, 0, 0);
|
reply_mix(CMD_ACK, 0, 0, 0, 0, 0);
|
||||||
}
|
}
|
||||||
|
@ -434,7 +434,7 @@ static void RealWritePCF7931(
|
||||||
uint16_t init_delay,
|
uint16_t init_delay,
|
||||||
int8_t offsetPulseWidth, int8_t offsetPulsePosition,
|
int8_t offsetPulseWidth, int8_t offsetPulsePosition,
|
||||||
uint8_t address, uint8_t byte, uint8_t data,
|
uint8_t address, uint8_t byte, uint8_t data,
|
||||||
bool ledcontrol){
|
bool ledcontrol) {
|
||||||
|
|
||||||
uint32_t tab[1024] = {0}; // data times frame
|
uint32_t tab[1024] = {0}; // data times frame
|
||||||
uint32_t u = 0;
|
uint32_t u = 0;
|
||||||
|
|
|
@ -19,7 +19,7 @@
|
||||||
#include "common.h"
|
#include "common.h"
|
||||||
|
|
||||||
|
|
||||||
typedef enum{
|
typedef enum {
|
||||||
FALLING,
|
FALLING,
|
||||||
RISING
|
RISING
|
||||||
} EdgeType;
|
} EdgeType;
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
|
|
||||||
local getopt = require('getopt')
|
local getopt = require('getopt')
|
||||||
local utils = require('utils')
|
local utils = require('utils')
|
||||||
local ac = require('ansicolors')
|
local ac = require('ansicolors')
|
||||||
|
@ -8,11 +7,14 @@ local dir = os.getenv('HOME') .. '/.proxmark3/logs/'
|
||||||
local logfile = (io.popen('dir /a-d /o-d /tw /b/s "' .. dir .. '" 2>nul:'):read("*a"):match("%C+"))
|
local logfile = (io.popen('dir /a-d /o-d /tw /b/s "' .. dir .. '" 2>nul:'):read("*a"):match("%C+"))
|
||||||
local log_file_path = dir .. "Paxton_log.txt"
|
local log_file_path = dir .. "Paxton_log.txt"
|
||||||
local nam = ""
|
local nam = ""
|
||||||
|
local pm3 = require('pm3')
|
||||||
|
p = pm3.pm3()
|
||||||
local command = core.console
|
local command = core.console
|
||||||
|
command('clear')
|
||||||
|
|
||||||
author = ' Author: jareckib - 30.01.2025'
|
author = ' Author: jareckib - 30.01.2025'
|
||||||
tutorial = ' Based on Equipter tutorial - Downgrade Paxton to EM4102'
|
tutorial = ' Based on Equipter tutorial - Downgrade Paxton to EM4102'
|
||||||
version = ' version v1.18'
|
version = ' version v1.19'
|
||||||
desc = [[
|
desc = [[
|
||||||
The script automates the copying of Paxton fobs read - write.
|
The script automates the copying of Paxton fobs read - write.
|
||||||
It also allows manual input of data for blocks 4-7.
|
It also allows manual input of data for blocks 4-7.
|
||||||
|
@ -228,13 +230,10 @@ end
|
||||||
|
|
||||||
local function handle_cloning(decimal_id, padded_hex_id, blocks, was_option_3)
|
local function handle_cloning(decimal_id, padded_hex_id, blocks, was_option_3)
|
||||||
while true do
|
while true do
|
||||||
print(" Create Paxton choose " .. ac.cyan .. "1" .. ac.reset .. " or EM4102 choose " .. ac.cyan .. "2" .. ac.reset)
|
io.write(" Create Paxton choose " .. ac.cyan .. "1" .. ac.reset .. " or EM4102 choose " .. ac.cyan .. "2 " .. ac.reset)
|
||||||
print(dash)
|
|
||||||
io.write(" Your choice "..ac.cyan.."(1/2): "..ac.reset)
|
|
||||||
local choice = io.read()
|
local choice = io.read()
|
||||||
if choice == "1" then
|
if choice == "1" then
|
||||||
print(dash)
|
io.write(" Place the" .. ac.cyan .. " Paxton " .. ac.reset .. "Fob on the coil to write.." .. ac.green .. " ENTER " .. ac.reset .. "to continue..")
|
||||||
print(" Place the" .. ac.cyan .. " Paxton " .. ac.reset .. "Fob on the coil to write.." .. ac.green .. " ENTER " .. ac.reset .. "to continue..")
|
|
||||||
io.read()
|
io.read()
|
||||||
print(dash)
|
print(dash)
|
||||||
command("lf hitag wrbl --ht2 -p 4 -d " .. blocks[4] .. " -k BDF5E846")
|
command("lf hitag wrbl --ht2 -p 4 -d " .. blocks[4] .. " -k BDF5E846")
|
||||||
|
@ -242,20 +241,18 @@ local function handle_cloning(decimal_id, padded_hex_id, blocks, was_option_3)
|
||||||
command("lf hitag wrbl --ht2 -p 6 -d " .. blocks[6] .. " -k BDF5E846")
|
command("lf hitag wrbl --ht2 -p 6 -d " .. blocks[6] .. " -k BDF5E846")
|
||||||
command("lf hitag wrbl --ht2 -p 7 -d " .. blocks[7] .. " -k BDF5E846")
|
command("lf hitag wrbl --ht2 -p 7 -d " .. blocks[7] .. " -k BDF5E846")
|
||||||
elseif choice == "2" then
|
elseif choice == "2" then
|
||||||
print(dash)
|
io.write(" Place the" .. ac.cyan .. " T5577 " .. ac.reset .. "tag on the coil and press" .. ac.green .. " ENTER " .. ac.reset .. "to continue..")
|
||||||
print(" Place the" .. ac.cyan .. " T5577 " .. ac.reset .. "tag on the coil and press" .. ac.green .. " ENTER " .. ac.reset .. "to continue..")
|
|
||||||
io.read()
|
io.read()
|
||||||
print(dash)
|
p:console("lf em 410x clone --id " .. padded_hex_id)
|
||||||
command("lf em 410x clone --id " .. padded_hex_id)
|
print(' Cloned EM4102 to T5577 with ID ' ..ac.green.. padded_hex_id ..ac.reset)
|
||||||
else
|
else
|
||||||
print(ac.yellow .. " Invalid choice." .. ac.reset .. " Please enter " .. ac.cyan .. "1" .. ac.reset .. " or " .. ac.cyan .. "2" .. ac.reset)
|
print(ac.yellow .. " Invalid choice." .. ac.reset .. " Please enter " .. ac.cyan .. "1" .. ac.reset .. " or " .. ac.cyan .. "2" .. ac.reset)
|
||||||
goto ask_again
|
goto ask_again
|
||||||
end
|
end
|
||||||
while true do
|
while true do
|
||||||
print(dash)
|
print(dash)
|
||||||
io.write(" Make next RFID Fob"..ac.cyan.." (y/n)"..ac.reset.." > "..ac.yellow)
|
io.write(" Make next RFID Fob"..ac.cyan.." (y/n) "..ac.reset)
|
||||||
local another = io.read()
|
local another = io.read()
|
||||||
io.write(ac.reset..'')
|
|
||||||
if another:lower() == "n" then
|
if another:lower() == "n" then
|
||||||
if was_option_3 then
|
if was_option_3 then
|
||||||
print(" No writing to Paxton_log.txt - Name: " ..ac.green.. nam .. ac.reset.. " exist")
|
print(" No writing to Paxton_log.txt - Name: " ..ac.green.. nam .. ac.reset.. " exist")
|
||||||
|
@ -303,7 +300,6 @@ local function main(args)
|
||||||
if o == 'h' then return help() end
|
if o == 'h' then return help() end
|
||||||
end
|
end
|
||||||
command('clear')
|
command('clear')
|
||||||
print()
|
|
||||||
print(dash)
|
print(dash)
|
||||||
print(ac.green .. ' Select option: ' .. ac.reset)
|
print(ac.green .. ' Select option: ' .. ac.reset)
|
||||||
print(ac.cyan .. ' 1' .. ac.reset .. ' - Read Paxton blocks 4-7 to make a copy')
|
print(ac.cyan .. ' 1' .. ac.reset .. ' - Read Paxton blocks 4-7 to make a copy')
|
||||||
|
@ -324,12 +320,11 @@ local function main(args)
|
||||||
local show_place_message = true
|
local show_place_message = true
|
||||||
while true do
|
while true do
|
||||||
if show_place_message then
|
if show_place_message then
|
||||||
print(' Place the' .. ac.cyan .. ' Paxton' .. ac.reset .. ' Fob on the coil to read..' .. ac.green .. 'ENTER' .. ac.reset .. ' to continue..')
|
io.write(' Place the' .. ac.cyan .. ' Paxton' .. ac.reset .. ' Fob on the coil to read..' .. ac.green .. 'ENTER' .. ac.reset .. ' to continue..')
|
||||||
print(dash)
|
|
||||||
end
|
end
|
||||||
io.read()
|
io.read()
|
||||||
command('lf hitag read --ht2 -k BDF5E846')
|
print(dash)
|
||||||
command('clear')
|
p:console('lf hitag read --ht2 -k BDF5E846')
|
||||||
if not logfile then
|
if not logfile then
|
||||||
error(" No files in this directory")
|
error(" No files in this directory")
|
||||||
end
|
end
|
||||||
|
@ -343,12 +338,11 @@ local function main(args)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
if empty_block then
|
if empty_block then
|
||||||
print(dash)
|
io.write(ac.yellow .. ' Adjust the Fob position on the coil.' .. ac.reset .. ' Press' .. ac.green .. ' ENTER' .. ac.reset .. ' to continue..')
|
||||||
print(ac.yellow .. ' Adjust the Fob position on the coil.' .. ac.reset .. ' Press' .. ac.green .. ' ENTER' .. ac.reset .. ' to continue..')
|
|
||||||
print(dash)
|
|
||||||
show_place_message = false
|
show_place_message = false
|
||||||
else
|
else
|
||||||
print(dash)
|
print(' Readed blocks:')
|
||||||
|
print()
|
||||||
for i = 4, 7 do
|
for i = 4, 7 do
|
||||||
if blocks[i] then
|
if blocks[i] then
|
||||||
print(string.format(" Block %d: %s%s%s", i, ac.yellow, blocks[i], ac.reset))
|
print(string.format(" Block %d: %s%s%s", i, ac.yellow, blocks[i], ac.reset))
|
||||||
|
@ -364,7 +358,6 @@ local function main(args)
|
||||||
print(' Identified Paxton ' .. ac.cyan .. 'Switch2' .. ac.reset)
|
print(' Identified Paxton ' .. ac.cyan .. 'Switch2' .. ac.reset)
|
||||||
decimal_id, padded_hex_id = calculate_id_switch({blocks[4], blocks[5], blocks[6], blocks[7]})
|
decimal_id, padded_hex_id = calculate_id_switch({blocks[4], blocks[5], blocks[6], blocks[7]})
|
||||||
end
|
end
|
||||||
print(dash)
|
|
||||||
print(string.format(" ID for EM4102 is: %s", ac.green .. padded_hex_id .. ac.reset))
|
print(string.format(" ID for EM4102 is: %s", ac.green .. padded_hex_id .. ac.reset))
|
||||||
print(dash)
|
print(dash)
|
||||||
handle_cloning(decimal_id, padded_hex_id, blocks, was_option_3)
|
handle_cloning(decimal_id, padded_hex_id, blocks, was_option_3)
|
||||||
|
|
|
@ -11,7 +11,7 @@ local command = core.console
|
||||||
command('clear')
|
command('clear')
|
||||||
|
|
||||||
author = ' Author: jareckib - 15.02.2025'
|
author = ' Author: jareckib - 15.02.2025'
|
||||||
version = ' version v1.01'
|
version = ' version v1.02'
|
||||||
desc = [[
|
desc = [[
|
||||||
This simple script first checks if a password has been set for the T5577.
|
This simple script first checks if a password has been set for the T5577.
|
||||||
It uses the dictionary t55xx_default_pwds.dic for this purpose. If a password
|
It uses the dictionary t55xx_default_pwds.dic for this purpose. If a password
|
||||||
|
@ -111,20 +111,23 @@ local function main(args)
|
||||||
if o == 'h' then return help() end
|
if o == 'h' then return help() end
|
||||||
end
|
end
|
||||||
p:console('clear')
|
p:console('clear')
|
||||||
print(' I am initiating the repair process for '..ac.cyan..'T5577'..ac.reset)
|
print(dash)
|
||||||
print(dash)
|
print('I am initiating the repair process for '..ac.cyan..'T5577'..ac.reset)
|
||||||
|
io.write("Place the" .. ac.cyan .. " T5577 " .. ac.reset .. "tag on the coil and press" .. ac.green .. " ENTER " .. ac.reset .. "to continue..")
|
||||||
|
io.read()
|
||||||
|
print(dash)
|
||||||
print("::: "..ac.cyan.."Hold on, I'm searching for a password in the dictionary"..ac.reset.." :::")
|
print("::: "..ac.cyan.."Hold on, I'm searching for a password in the dictionary"..ac.reset.." :::")
|
||||||
print(dash)
|
print(dash)
|
||||||
p:console('lf t55 chk')
|
p:console('lf t55 chk')
|
||||||
timer(5)
|
|
||||||
local log_content = read_log_file(logfile)
|
local log_content = read_log_file(logfile)
|
||||||
local password = log_content and extract_password(log_content) or nil
|
local password = log_content and extract_password(log_content) or nil
|
||||||
reanimate_t5577(password)
|
reanimate_t5577(password)
|
||||||
p:console('lf t55 detect')
|
p:console('lf t55 detect')
|
||||||
|
p:console('lf t55 read -b 0')
|
||||||
timer(5)
|
timer(5)
|
||||||
local success = false
|
local success = false
|
||||||
for line in p.grabbed_output:gmatch("[^\r\n]+") do
|
for line in p.grabbed_output:gmatch("[^\r\n]+") do
|
||||||
if line:find("000880E0") then
|
if line:find("00 | 000880E0 |") then
|
||||||
success = true
|
success = true
|
||||||
break
|
break
|
||||||
end
|
end
|
||||||
|
|
462
client/luascripts/paxton_clone.lua
Normal file
462
client/luascripts/paxton_clone.lua
Normal file
|
@ -0,0 +1,462 @@
|
||||||
|
local getopt = require('getopt')
|
||||||
|
local utils = require('utils')
|
||||||
|
local ac = require('ansicolors')
|
||||||
|
local os = require('os')
|
||||||
|
local dash = string.rep('--', 32)
|
||||||
|
local dir = os.getenv('HOME') .. '/.proxmark3/logs/'
|
||||||
|
local logfile = (io.popen('dir /a-d /o-d /tw /b/s "' .. dir .. '" 2>nul:'):read("*a"):match("%C+"))
|
||||||
|
local log_file_path = dir .. "Paxton_log.txt"
|
||||||
|
local nam = ""
|
||||||
|
local pm3 = require('pm3')
|
||||||
|
p = pm3.pm3()
|
||||||
|
local command = core.console
|
||||||
|
command('clear')
|
||||||
|
|
||||||
|
author = ' Author: jareckib - 30.01.2025'
|
||||||
|
tutorial = ' Based on Equipter tutorial - Downgrade Paxton to EM4102'
|
||||||
|
version = ' version v1.18'
|
||||||
|
desc = [[
|
||||||
|
The script automates the copying of Paxton fobs read - write.
|
||||||
|
It also allows manual input of data for blocks 4-7.
|
||||||
|
The third option is reading data stored in the log file and create new fob.
|
||||||
|
Additionally, the script calculates the ID for downgrading Paxton to EM4102.
|
||||||
|
|
||||||
|
]]
|
||||||
|
usage = [[
|
||||||
|
script run paxton_clone
|
||||||
|
]]
|
||||||
|
arguments = [[
|
||||||
|
script run paxton_clone -h : this help
|
||||||
|
]]
|
||||||
|
|
||||||
|
local debug = true
|
||||||
|
|
||||||
|
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
|
||||||
|
|
||||||
|
local function help()
|
||||||
|
print()
|
||||||
|
print(author)
|
||||||
|
print(tutorial)
|
||||||
|
print(version)
|
||||||
|
print(desc)
|
||||||
|
print(ac.cyan..' Usage'..ac.reset)
|
||||||
|
print(usage)
|
||||||
|
print(ac.cyan..' Arguments'..ac.reset)
|
||||||
|
print(arguments)
|
||||||
|
end
|
||||||
|
|
||||||
|
local function read_log_file(logfile)
|
||||||
|
local file = io.open(logfile, "r")
|
||||||
|
if not file then
|
||||||
|
error(" Could not open the file")
|
||||||
|
end
|
||||||
|
local content = file:read("*all")
|
||||||
|
file:close()
|
||||||
|
return content
|
||||||
|
end
|
||||||
|
|
||||||
|
local function parse_blocks(result)
|
||||||
|
local blocks = {}
|
||||||
|
for line in result:gmatch("[^\r\n]+") do
|
||||||
|
local block_num, block_data = line:match("%[%=%]%s+%d/0x0([4-7])%s+%|%s+([0-9A-F ]+)")
|
||||||
|
if block_num and block_data then
|
||||||
|
block_num = tonumber(block_num)
|
||||||
|
block_data = block_data:gsub("%s+", "")
|
||||||
|
blocks[block_num] = block_data
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return blocks
|
||||||
|
end
|
||||||
|
|
||||||
|
local function hex_to_bin(hex_string)
|
||||||
|
local bin_string = ""
|
||||||
|
local hex_to_bin_map = {
|
||||||
|
['0'] = "0000", ['1'] = "0001", ['2'] = "0010", ['3'] = "0011",
|
||||||
|
['4'] = "0100", ['5'] = "0101", ['6'] = "0110", ['7'] = "0111",
|
||||||
|
['8'] = "1000", ['9'] = "1001", ['A'] = "1010", ['B'] = "1011",
|
||||||
|
['C'] = "1100", ['D'] = "1101", ['E'] = "1110", ['F'] = "1111"
|
||||||
|
}
|
||||||
|
for i = 1, #hex_string do
|
||||||
|
bin_string = bin_string .. hex_to_bin_map[hex_string:sub(i, i)]
|
||||||
|
end
|
||||||
|
return bin_string
|
||||||
|
end
|
||||||
|
|
||||||
|
local function remove_last_two_bits(binary_str)
|
||||||
|
return binary_str:sub(1, #binary_str - 2)
|
||||||
|
end
|
||||||
|
|
||||||
|
local function split_into_5bit_chunks(binary_str)
|
||||||
|
local chunks = {}
|
||||||
|
for i = 1, #binary_str, 5 do
|
||||||
|
table.insert(chunks, binary_str:sub(i, i + 4))
|
||||||
|
end
|
||||||
|
return chunks
|
||||||
|
end
|
||||||
|
|
||||||
|
local function remove_parity_bit(chunks)
|
||||||
|
local no_parity_chunks = {}
|
||||||
|
for _, chunk in ipairs(chunks) do
|
||||||
|
if #chunk == 5 then
|
||||||
|
table.insert(no_parity_chunks, chunk:sub(2))
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return no_parity_chunks
|
||||||
|
end
|
||||||
|
|
||||||
|
local function convert_to_hex(chunks)
|
||||||
|
local hex_values = {}
|
||||||
|
for _, chunk in ipairs(chunks) do
|
||||||
|
if #chunk > 0 then
|
||||||
|
table.insert(hex_values, string.format("%X", tonumber(chunk, 2)))
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return hex_values
|
||||||
|
end
|
||||||
|
|
||||||
|
local function convert_to_decimal(chunks)
|
||||||
|
local decimal_values = {}
|
||||||
|
for _, chunk in ipairs(chunks) do
|
||||||
|
table.insert(decimal_values, tonumber(chunk, 2))
|
||||||
|
end
|
||||||
|
return decimal_values
|
||||||
|
end
|
||||||
|
|
||||||
|
local function find_until_before_f(hex_values)
|
||||||
|
local result = {}
|
||||||
|
for _, value in ipairs(hex_values) do
|
||||||
|
if value == 'F' then
|
||||||
|
break
|
||||||
|
end
|
||||||
|
table.insert(result, value)
|
||||||
|
end
|
||||||
|
return result
|
||||||
|
end
|
||||||
|
|
||||||
|
local function process_block(block)
|
||||||
|
local binary_str = hex_to_bin(block)
|
||||||
|
binary_str = remove_last_two_bits(binary_str)
|
||||||
|
local chunks = split_into_5bit_chunks(binary_str)
|
||||||
|
local no_parity_chunks = remove_parity_bit(chunks)
|
||||||
|
return no_parity_chunks
|
||||||
|
end
|
||||||
|
|
||||||
|
local function calculate_id_net(blocks)
|
||||||
|
local all_hex_values = {}
|
||||||
|
for _, block in ipairs(blocks) do
|
||||||
|
local hex_values = convert_to_hex(process_block(block))
|
||||||
|
for _, hex in ipairs(hex_values) do
|
||||||
|
table.insert(all_hex_values, hex)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
local selected_hex_values = find_until_before_f(all_hex_values)
|
||||||
|
if #selected_hex_values == 0 then
|
||||||
|
error(ac.red..' Error: '..ac.reset..'No valid data found in blocks 4 and 5')
|
||||||
|
end
|
||||||
|
local combined_hex = table.concat(selected_hex_values)
|
||||||
|
if not combined_hex:match("^%x+$") then
|
||||||
|
error(ac.red..' Error: '..ac.reset..'Invalid data in blocks 4 and 5')
|
||||||
|
end
|
||||||
|
local decimal_id = tonumber(combined_hex)
|
||||||
|
local stripped_hex_id = string.format("%X", decimal_id)
|
||||||
|
local padded_hex_id = string.format("%010X", decimal_id)
|
||||||
|
return decimal_id, padded_hex_id
|
||||||
|
end
|
||||||
|
|
||||||
|
local function calculate_id_switch(blocks)
|
||||||
|
local all_decimal_values = {}
|
||||||
|
for _, block in ipairs(blocks) do
|
||||||
|
local decimal_values = convert_to_decimal(process_block(block))
|
||||||
|
for _, dec in ipairs(decimal_values) do
|
||||||
|
table.insert(all_decimal_values, dec)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
if #all_decimal_values < 15 then
|
||||||
|
error(ac.red..' Error:'..ac.reset..' Not enough data after processing blocks 4, 5, 6, and 7')
|
||||||
|
end
|
||||||
|
local id_positions = {9, 11, 13, 15, 2, 4, 6, 8}
|
||||||
|
local id_numbers = {}
|
||||||
|
for _, pos in ipairs(id_positions) do
|
||||||
|
table.insert(id_numbers, all_decimal_values[pos])
|
||||||
|
end
|
||||||
|
local decimal_id = tonumber(table.concat(id_numbers))
|
||||||
|
local padded_hex_id = string.format("%010X", decimal_id)
|
||||||
|
return decimal_id, padded_hex_id
|
||||||
|
end
|
||||||
|
|
||||||
|
local function name_exists_in_log(name)
|
||||||
|
local file = io.open(log_file_path, "r")
|
||||||
|
if not file then
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
local pattern = "^Name:%s*" .. name .. "%s*$"
|
||||||
|
for line in file:lines() do
|
||||||
|
if line:match(pattern) then
|
||||||
|
file:close()
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
end
|
||||||
|
file:close()
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
|
||||||
|
local function log_result(blocks, em410_id, name)
|
||||||
|
local log_file = io.open(log_file_path, "a")
|
||||||
|
if log_file then
|
||||||
|
log_file:write("Name: " .. name .. "\n")
|
||||||
|
log_file:write("Date: ", os.date("%Y-%m-%d %H:%M:%S"), "\n")
|
||||||
|
for i = 4, 7 do
|
||||||
|
log_file:write(string.format("Block %d: %s\n", i, blocks[i] or "nil"))
|
||||||
|
end
|
||||||
|
log_file:write(string.format('EM4102 ID: %s\n', em410_id or "nil"))
|
||||||
|
log_file:write('--------------------------\n')
|
||||||
|
log_file:close()
|
||||||
|
print(' Log saved as: pm3/.proxmark3/logs/' ..ac.yellow..' Paxton_log.txt'..ac.reset)
|
||||||
|
else
|
||||||
|
print(" Failed to open log file for writing.")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
local function handle_cloning(decimal_id, padded_hex_id, blocks, was_option_3)
|
||||||
|
while true do
|
||||||
|
io.write(" Create Paxton choose " .. ac.cyan .. "1" .. ac.reset .. " or EM4102 choose " .. ac.cyan .. "2 " .. ac.reset)
|
||||||
|
local choice = io.read()
|
||||||
|
if choice == "1" then
|
||||||
|
io.write(" Place the" .. ac.cyan .. " Paxton " .. ac.reset .. "Fob on the coil to write.." .. ac.green .. " ENTER " .. ac.reset .. "to continue..")
|
||||||
|
io.read()
|
||||||
|
print(dash)
|
||||||
|
command("lf hitag wrbl --ht2 -p 4 -d " .. blocks[4] .. " -k BDF5E846")
|
||||||
|
command("lf hitag wrbl --ht2 -p 5 -d " .. blocks[5] .. " -k BDF5E846")
|
||||||
|
command("lf hitag wrbl --ht2 -p 6 -d " .. blocks[6] .. " -k BDF5E846")
|
||||||
|
command("lf hitag wrbl --ht2 -p 7 -d " .. blocks[7] .. " -k BDF5E846")
|
||||||
|
elseif choice == "2" then
|
||||||
|
io.write(" Place the" .. ac.cyan .. " T5577 " .. ac.reset .. "tag on the coil and press" .. ac.green .. " ENTER " .. ac.reset .. "to continue..")
|
||||||
|
io.read()
|
||||||
|
p:console("lf em 410x clone --id " .. padded_hex_id)
|
||||||
|
print(' Cloned EM4102 to T5577 with ID ' ..ac.green.. padded_hex_id ..ac.reset)
|
||||||
|
else
|
||||||
|
print(ac.yellow .. " Invalid choice." .. ac.reset .. " Please enter " .. ac.cyan .. "1" .. ac.reset .. " or " .. ac.cyan .. "2" .. ac.reset)
|
||||||
|
goto ask_again
|
||||||
|
end
|
||||||
|
while true do
|
||||||
|
print(dash)
|
||||||
|
io.write(" Make next RFID Fob"..ac.cyan.." (y/n) "..ac.reset)
|
||||||
|
local another = io.read()
|
||||||
|
if another:lower() == "n" then
|
||||||
|
if was_option_3 then
|
||||||
|
print(" No writing to Paxton_log.txt - Name: " ..ac.green.. nam .. ac.reset.. " exist")
|
||||||
|
return
|
||||||
|
end
|
||||||
|
print()
|
||||||
|
print(ac.green .. " Saving Paxton_log file..." .. ac.reset)
|
||||||
|
while true do
|
||||||
|
io.write(" Enter a name for database (cannot be empty/duplicate): "..ac.yellow)
|
||||||
|
name = io.read()
|
||||||
|
io.write(ac.reset..'')
|
||||||
|
if name == nil or name:match("^%s*$") then
|
||||||
|
print(ac.red .. ' ERROR:'..ac.reset..' Name cannot be empty.')
|
||||||
|
else
|
||||||
|
if name_exists_in_log(name) then
|
||||||
|
print(ac.yellow .. ' Name exists!!! '..ac.reset.. 'Please choose a different name.')
|
||||||
|
else
|
||||||
|
break
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
log_result(blocks, padded_hex_id, name)
|
||||||
|
print(ac.green .. " Log saved successfully!" .. ac.reset)
|
||||||
|
local file = io.open(logfile, "w+")
|
||||||
|
file:write("")
|
||||||
|
file:close()
|
||||||
|
return
|
||||||
|
elseif another:lower() == "y" then
|
||||||
|
goto ask_again
|
||||||
|
else
|
||||||
|
print(ac.yellow.." Invalid response."..ac.reset.." Please enter"..ac.cyan.." y"..ac.reset.." or"..ac.cyan.." n"..ac.reset)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
::ask_again::
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
local function is_valid_hex(input)
|
||||||
|
return #input == 8 and input:match("^[0-9A-Fa-f]+$")
|
||||||
|
end
|
||||||
|
|
||||||
|
local function main(args)
|
||||||
|
while true do
|
||||||
|
for o, a in getopt.getopt(args, 'h') do
|
||||||
|
if o == 'h' then return help() end
|
||||||
|
end
|
||||||
|
command('clear')
|
||||||
|
print(dash)
|
||||||
|
print(ac.green .. ' Select option: ' .. ac.reset)
|
||||||
|
print(ac.cyan .. ' 1' .. ac.reset .. ' - Read Paxton blocks 4-7 to make a copy')
|
||||||
|
print(ac.cyan .. ' 2' .. ac.reset .. ' - Manually input data for Paxton blocks 4-7')
|
||||||
|
print(ac.cyan .. " 3" .. ac.reset .. " - Search in Paxton_log by name and use the data")
|
||||||
|
print(dash)
|
||||||
|
while true do
|
||||||
|
io.write(' Your choice '..ac.cyan..'(1/2/3): ' .. ac.reset)
|
||||||
|
input_option = io.read()
|
||||||
|
if input_option == "1" or input_option == "2" or input_option == "3" then
|
||||||
|
break
|
||||||
|
else
|
||||||
|
print(ac.yellow .. ' Invalid choice.' .. ac.reset .. ' Please enter ' .. ac.cyan .. '1' .. ac.reset .. ' or ' .. ac.cyan .. '2' .. ac.reset..' or'..ac.cyan..' 3'..ac.reset)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
local was_option_3 = false
|
||||||
|
if input_option == "1" then
|
||||||
|
local show_place_message = true
|
||||||
|
while true do
|
||||||
|
if show_place_message then
|
||||||
|
io.write(' Place the' .. ac.cyan .. ' Paxton' .. ac.reset .. ' Fob on the coil to read..' .. ac.green .. 'ENTER' .. ac.reset .. ' to continue..')
|
||||||
|
end
|
||||||
|
io.read()
|
||||||
|
print(dash)
|
||||||
|
p:console('lf hitag read --ht2 -k BDF5E846')
|
||||||
|
if not logfile then
|
||||||
|
error(" No files in this directory")
|
||||||
|
end
|
||||||
|
local result = read_log_file(logfile)
|
||||||
|
local blocks = parse_blocks(result)
|
||||||
|
local empty_block = false
|
||||||
|
for i = 4, 7 do
|
||||||
|
if not blocks[i] then
|
||||||
|
empty_block = true
|
||||||
|
break
|
||||||
|
end
|
||||||
|
end
|
||||||
|
if empty_block then
|
||||||
|
io.write(ac.yellow .. ' Adjust the Fob position on the coil.' .. ac.reset .. ' Press' .. ac.green .. ' ENTER' .. ac.reset .. ' to continue..')
|
||||||
|
show_place_message = false
|
||||||
|
else
|
||||||
|
print(' Readed blocks:')
|
||||||
|
print()
|
||||||
|
for i = 4, 7 do
|
||||||
|
if blocks[i] then
|
||||||
|
print(string.format(" Block %d: %s%s%s", i, ac.yellow, blocks[i], ac.reset))
|
||||||
|
end
|
||||||
|
end
|
||||||
|
local decimal_id, padded_hex_id
|
||||||
|
if blocks[5] and (blocks[5]:sub(4, 4) == 'F' or blocks[5]:sub(4, 4) == 'f') then
|
||||||
|
print(dash)
|
||||||
|
print(' Identified Paxton ' .. ac.cyan .. 'Net2' .. ac.reset)
|
||||||
|
decimal_id, padded_hex_id = calculate_id_net({blocks[4], blocks[5]})
|
||||||
|
else
|
||||||
|
print(dash)
|
||||||
|
print(' Identified Paxton ' .. ac.cyan .. 'Switch2' .. ac.reset)
|
||||||
|
decimal_id, padded_hex_id = calculate_id_switch({blocks[4], blocks[5], blocks[6], blocks[7]})
|
||||||
|
end
|
||||||
|
print(string.format(" ID for EM4102 is: %s", ac.green .. padded_hex_id .. ac.reset))
|
||||||
|
print(dash)
|
||||||
|
handle_cloning(decimal_id, padded_hex_id, blocks, was_option_3)
|
||||||
|
break
|
||||||
|
end
|
||||||
|
end
|
||||||
|
elseif input_option == "2" then
|
||||||
|
local blocks = {}
|
||||||
|
for i = 4, 7 do
|
||||||
|
while true do
|
||||||
|
io.write(ac.reset..' Enter data for block ' .. i .. ': ' .. ac.yellow)
|
||||||
|
local input = io.read()
|
||||||
|
input = input:upper()
|
||||||
|
if is_valid_hex(input) then
|
||||||
|
blocks[i] = input
|
||||||
|
break
|
||||||
|
else
|
||||||
|
print(ac.yellow .. ' Invalid input.' .. ac.reset .. ' Each block must be 4 bytes (8 hex characters).')
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
local decimal_id, padded_hex_id
|
||||||
|
if blocks[5] and (blocks[5]:sub(4, 4) == 'F' or blocks[5]:sub(4, 4) == 'f') then
|
||||||
|
print(ac.reset.. dash)
|
||||||
|
print(' Identified Paxton ' .. ac.cyan .. 'Net2' .. ac.reset)
|
||||||
|
decimal_id, padded_hex_id = calculate_id_net({blocks[4], blocks[5]})
|
||||||
|
else
|
||||||
|
print(ac.reset.. dash)
|
||||||
|
print(' Identified Paxton ' .. ac.cyan .. 'Switch2' .. ac.reset)
|
||||||
|
decimal_id, padded_hex_id = calculate_id_switch({blocks[4], blocks[5], blocks[6], blocks[7]})
|
||||||
|
end
|
||||||
|
print(dash)
|
||||||
|
print(string.format(" ID for EM4102 is: %s", ac.green .. padded_hex_id .. ac.reset))
|
||||||
|
print(dash)
|
||||||
|
if not padded_hex_id then
|
||||||
|
print(ac.red..' ERROR: '..ac.reset.. 'Invalid block data provided')
|
||||||
|
return
|
||||||
|
end
|
||||||
|
handle_cloning(decimal_id, padded_hex_id, blocks, was_option_3)
|
||||||
|
break
|
||||||
|
elseif input_option == "3" then
|
||||||
|
was_option_3 = true
|
||||||
|
local retries = 3
|
||||||
|
while retries > 0 do
|
||||||
|
io.write(' Enter the name to search ('..retries..' attempts) : '..ac.yellow)
|
||||||
|
local user_input = io.read()
|
||||||
|
io.write(ac.reset..'')
|
||||||
|
if user_input == nil or user_input:match("^%s*$") then
|
||||||
|
print(ac.yellow..' Error: '..ac.reset.. 'Empty name !!!')
|
||||||
|
end
|
||||||
|
local name_clean = "^Name:%s*" .. user_input:gsub("%s", "%%s") .. "%s*$"
|
||||||
|
local file = io.open(log_file_path, "r")
|
||||||
|
if not file then
|
||||||
|
print(ac.red .. ' Error:'..ac.reset.. 'Could not open log file.')
|
||||||
|
return
|
||||||
|
end
|
||||||
|
local lines = {}
|
||||||
|
for line in file:lines() do
|
||||||
|
table.insert(lines, line)
|
||||||
|
end
|
||||||
|
file:close()
|
||||||
|
local found = false
|
||||||
|
for i = 1, #lines do
|
||||||
|
if lines[i]:match(name_clean) then
|
||||||
|
nam = user_input
|
||||||
|
local blocks = {
|
||||||
|
[4] = lines[i + 2]:match("Block 4: (.+)"),
|
||||||
|
[5] = lines[i + 3]:match("Block 5: (.+)"),
|
||||||
|
[6] = lines[i + 4]:match("Block 6: (.+)"),
|
||||||
|
[7] = lines[i + 5]:match("Block 7: (.+)")
|
||||||
|
}
|
||||||
|
local em4102_id = lines[i + 6]:match("EM4102 ID: (.+)")
|
||||||
|
print(dash)
|
||||||
|
print(' I found the data under the name: '..ac.yellow ..nam.. ac.reset)
|
||||||
|
for j = 4, 7 do
|
||||||
|
print(string.format(" Block %d: %s%s%s", j, ac.yellow, blocks[j] or "N/A", ac.reset))
|
||||||
|
end
|
||||||
|
print(" EM4102 ID: " .. ac.green .. (em4102_id or "N/A") .. ac.reset)
|
||||||
|
print(dash)
|
||||||
|
local decimal_id, padded_hex_id = em4102_id, em4102_id
|
||||||
|
handle_cloning(decimal_id, padded_hex_id, blocks, was_option_3, nam)
|
||||||
|
found = true
|
||||||
|
break
|
||||||
|
end
|
||||||
|
end
|
||||||
|
if not found then
|
||||||
|
retries = retries - 1
|
||||||
|
else
|
||||||
|
break
|
||||||
|
end
|
||||||
|
end
|
||||||
|
if retries == 0 then
|
||||||
|
print(ac.yellow .. " Name not found after 3 attempts." .. ac.reset)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
print(dash)
|
||||||
|
print(' Exiting script Lua...')
|
||||||
|
return
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
main(args)
|
|
@ -688,11 +688,11 @@ static int CmdEM410xClone(const char *Cmd) {
|
||||||
uint8_t r_parity = 0;
|
uint8_t r_parity = 0;
|
||||||
uint8_t nibble = id >> i & 0xF;
|
uint8_t nibble = id >> i & 0xF;
|
||||||
|
|
||||||
databits = concatbits(data, databits, &nibble, 4, 4);
|
databits = concatbits(data, databits, &nibble, 4, 4, false);
|
||||||
for (size_t j = 0; j < 4; j++) {
|
for (size_t j = 0; j < 4; j++) {
|
||||||
r_parity ^= nibble >> j & 1;
|
r_parity ^= nibble >> j & 1;
|
||||||
}
|
}
|
||||||
databits = concatbits(data, databits, &r_parity, 7, 1);
|
databits = concatbits(data, databits, &r_parity, 7, 1, false);
|
||||||
c_parity ^= nibble;
|
c_parity ^= nibble;
|
||||||
}
|
}
|
||||||
data[7] |= c_parity << 1;
|
data[7] |= c_parity << 1;
|
||||||
|
|
|
@ -34,9 +34,15 @@
|
||||||
|
|
||||||
// TODO: Optional: use those unique structures in a union, call it em4x70_data_t, but add a first
|
// TODO: Optional: use those unique structures in a union, call it em4x70_data_t, but add a first
|
||||||
// common header field that includes the command itself (to improve debugging / validation).
|
// common header field that includes the command itself (to improve debugging / validation).
|
||||||
|
|
||||||
typedef struct _em4x70_tag_info_t {
|
typedef struct _em4x70_tag_info_t {
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The full data on an em4x70 the tag.
|
/// The full data on an em4170 tag.
|
||||||
|
/// For V4070 tags:
|
||||||
|
/// * UM2 does not exist on the tag
|
||||||
|
/// * Pin does not exist on the tag
|
||||||
|
/// * UM1 (including the lock bits) might be one-time programmable (OTP)
|
||||||
|
///
|
||||||
/// [31] == Block 15 MSB == UM2₆₃..UM2₅₆
|
/// [31] == Block 15 MSB == UM2₆₃..UM2₅₆
|
||||||
/// [30] == Block 15 LSB == UM2₅₅..UM2₄₈
|
/// [30] == Block 15 LSB == UM2₅₅..UM2₄₈
|
||||||
/// [29] == Block 14 MSB == UM2₄₇..UM2₄₀
|
/// [29] == Block 14 MSB == UM2₄₇..UM2₄₀
|
||||||
|
@ -168,6 +174,7 @@ typedef struct _em4x70_cmd_input_calculate_t {
|
||||||
ID48LIB_KEY key;
|
ID48LIB_KEY key;
|
||||||
ID48LIB_NONCE rn;
|
ID48LIB_NONCE rn;
|
||||||
} em4x70_cmd_input_calculate_t;
|
} em4x70_cmd_input_calculate_t;
|
||||||
|
|
||||||
typedef struct _em4x70_cmd_output_calculate_t {
|
typedef struct _em4x70_cmd_output_calculate_t {
|
||||||
ID48LIB_FRN frn;
|
ID48LIB_FRN frn;
|
||||||
ID48LIB_GRN grn;
|
ID48LIB_GRN grn;
|
||||||
|
|
|
@ -832,7 +832,7 @@ static bool ht2_get_uid(uint32_t *uid) {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (resp.status != PM3_SUCCESS) {
|
if (resp.status != PM3_SUCCESS) {
|
||||||
PrintAndLogEx(DEBUG, "DEBUG: Error - failed getting UID");
|
PrintAndLogEx(DEBUG, "DEBUG: Error - failed getting Hitag 2 UID");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -186,6 +186,7 @@ static void fill_grabber(const char *string) {
|
||||||
g_grabbed_output.ptr = tmp;
|
g_grabbed_output.ptr = tmp;
|
||||||
g_grabbed_output.size += MAX_PRINT_BUFFER;
|
g_grabbed_output.size += MAX_PRINT_BUFFER;
|
||||||
}
|
}
|
||||||
|
|
||||||
int len = snprintf(g_grabbed_output.ptr + g_grabbed_output.idx, MAX_PRINT_BUFFER, "%s", string);
|
int len = snprintf(g_grabbed_output.ptr + g_grabbed_output.idx, MAX_PRINT_BUFFER, "%s", string);
|
||||||
if (len < 0 || len > MAX_PRINT_BUFFER) {
|
if (len < 0 || len > MAX_PRINT_BUFFER) {
|
||||||
// We leave current g_grabbed_output_len untouched
|
// We leave current g_grabbed_output_len untouched
|
||||||
|
@ -196,24 +197,34 @@ static void fill_grabber(const char *string) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void PrintAndLogOptions(const char *str[][2], size_t size, size_t space) {
|
void PrintAndLogOptions(const char *str[][2], size_t size, size_t space) {
|
||||||
|
|
||||||
char buff[2000] = "Options:\n";
|
char buff[2000] = "Options:\n";
|
||||||
char format[2000] = "";
|
char format[2000] = "";
|
||||||
size_t counts[2] = {0, 0};
|
size_t counts[2] = {0, 0};
|
||||||
for (size_t i = 0; i < size; i++)
|
|
||||||
for (size_t j = 0 ; j < 2 ; j++)
|
for (size_t i = 0; i < size; i++) {
|
||||||
|
for (size_t j = 0 ; j < 2 ; j++) {
|
||||||
if (counts[j] < strlen(str[i][j])) {
|
if (counts[j] < strlen(str[i][j])) {
|
||||||
counts[j] = strlen(str[i][j]);
|
counts[j] = strlen(str[i][j]);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
for (size_t i = 0; i < size; i++) {
|
for (size_t i = 0; i < size; i++) {
|
||||||
|
|
||||||
for (size_t j = 0; j < 2; j++) {
|
for (size_t j = 0; j < 2; j++) {
|
||||||
if (j == 0)
|
if (j == 0) {
|
||||||
snprintf(format, sizeof(format), "%%%zus%%%zus", space, counts[j]);
|
snprintf(format, sizeof(format), "%%%zus%%%zus", space, counts[j]);
|
||||||
else
|
} else {
|
||||||
snprintf(format, sizeof(format), "%%%zus%%-%zus", space, counts[j]);
|
snprintf(format, sizeof(format), "%%%zus%%-%zus", space, counts[j]);
|
||||||
|
}
|
||||||
|
|
||||||
snprintf(buff + strlen(buff), sizeof(buff) - strlen(buff), format, " ", str[i][j]);
|
snprintf(buff + strlen(buff), sizeof(buff) - strlen(buff), format, " ", str[i][j]);
|
||||||
}
|
}
|
||||||
if (i < size - 1)
|
|
||||||
|
if (i < size - 1) {
|
||||||
strncat(buff, "\n", sizeof(buff) - strlen(buff) - 1);
|
strncat(buff, "\n", sizeof(buff) - strlen(buff) - 1);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
PrintAndLogEx(NORMAL, "%s", buff);
|
PrintAndLogEx(NORMAL, "%s", buff);
|
||||||
}
|
}
|
||||||
|
@ -223,12 +234,14 @@ static uint8_t PrintAndLogEx_spinidx = 0;
|
||||||
void PrintAndLogEx(logLevel_t level, const char *fmt, ...) {
|
void PrintAndLogEx(logLevel_t level, const char *fmt, ...) {
|
||||||
|
|
||||||
// skip debug messages if client debugging is turned off i.e. 'DATA SETDEBUG -0'
|
// skip debug messages if client debugging is turned off i.e. 'DATA SETDEBUG -0'
|
||||||
if (g_debugMode == 0 && level == DEBUG)
|
if (g_debugMode == 0 && level == DEBUG) {
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// skip HINT messages if client has hints turned off i.e. 'HINT 0'
|
// skip HINT messages if client has hints turned off i.e. 'HINT 0'
|
||||||
if (g_session.show_hints == false && level == HINT)
|
if (g_session.show_hints == false && level == HINT) {
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
char prefix[40] = {0};
|
char prefix[40] = {0};
|
||||||
char buffer[MAX_PRINT_BUFFER] = {0};
|
char buffer[MAX_PRINT_BUFFER] = {0};
|
||||||
|
@ -242,17 +255,19 @@ void PrintAndLogEx(logLevel_t level, const char *fmt, ...) {
|
||||||
};
|
};
|
||||||
switch (level) {
|
switch (level) {
|
||||||
case ERR:
|
case ERR:
|
||||||
if (g_session.emoji_mode == EMO_EMOJI)
|
if (g_session.emoji_mode == EMO_EMOJI) {
|
||||||
strncpy(prefix, "[" _RED_("!!") "] :rotating_light: ", sizeof(prefix) - 1);
|
strncpy(prefix, "[" _RED_("!!") "] :rotating_light: ", sizeof(prefix) - 1);
|
||||||
else
|
} else {
|
||||||
strncpy(prefix, "[" _RED_("!!") "] ", sizeof(prefix) - 1);
|
strncpy(prefix, "[" _RED_("!!") "] ", sizeof(prefix) - 1);
|
||||||
|
}
|
||||||
stream = stderr;
|
stream = stderr;
|
||||||
break;
|
break;
|
||||||
case FAILED:
|
case FAILED:
|
||||||
if (g_session.emoji_mode == EMO_EMOJI)
|
if (g_session.emoji_mode == EMO_EMOJI) {
|
||||||
strncpy(prefix, "[" _RED_("-") "] :no_entry: ", sizeof(prefix) - 1);
|
strncpy(prefix, "[" _RED_("-") "] :no_entry: ", sizeof(prefix) - 1);
|
||||||
else
|
} else {
|
||||||
strncpy(prefix, "[" _RED_("-") "] ", sizeof(prefix) - 1);
|
strncpy(prefix, "[" _RED_("-") "] ", sizeof(prefix) - 1);
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
case DEBUG:
|
case DEBUG:
|
||||||
strncpy(prefix, "[" _BLUE_("#") "] ", sizeof(prefix) - 1);
|
strncpy(prefix, "[" _BLUE_("#") "] ", sizeof(prefix) - 1);
|
||||||
|
@ -264,10 +279,11 @@ void PrintAndLogEx(logLevel_t level, const char *fmt, ...) {
|
||||||
strncpy(prefix, "[" _GREEN_("+") "] ", sizeof(prefix) - 1);
|
strncpy(prefix, "[" _GREEN_("+") "] ", sizeof(prefix) - 1);
|
||||||
break;
|
break;
|
||||||
case WARNING:
|
case WARNING:
|
||||||
if (g_session.emoji_mode == EMO_EMOJI)
|
if (g_session.emoji_mode == EMO_EMOJI) {
|
||||||
strncpy(prefix, "[" _CYAN_("!") "] :warning: ", sizeof(prefix) - 1);
|
strncpy(prefix, "[" _CYAN_("!") "] :warning: ", sizeof(prefix) - 1);
|
||||||
else
|
} else {
|
||||||
strncpy(prefix, "[" _CYAN_("!") "] ", sizeof(prefix) - 1);
|
strncpy(prefix, "[" _CYAN_("!") "] ", sizeof(prefix) - 1);
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
case INFO:
|
case INFO:
|
||||||
strncpy(prefix, "[" _YELLOW_("=") "] ", sizeof(prefix) - 1);
|
strncpy(prefix, "[" _YELLOW_("=") "] ", sizeof(prefix) - 1);
|
||||||
|
@ -276,13 +292,15 @@ void PrintAndLogEx(logLevel_t level, const char *fmt, ...) {
|
||||||
if (g_session.emoji_mode == EMO_EMOJI) {
|
if (g_session.emoji_mode == EMO_EMOJI) {
|
||||||
strncpy(prefix, spinner_emoji[PrintAndLogEx_spinidx], sizeof(prefix) - 1);
|
strncpy(prefix, spinner_emoji[PrintAndLogEx_spinidx], sizeof(prefix) - 1);
|
||||||
PrintAndLogEx_spinidx++;
|
PrintAndLogEx_spinidx++;
|
||||||
if (PrintAndLogEx_spinidx >= ARRAYLEN(spinner_emoji))
|
if (PrintAndLogEx_spinidx >= ARRAYLEN(spinner_emoji)) {
|
||||||
PrintAndLogEx_spinidx = 0;
|
PrintAndLogEx_spinidx = 0;
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
strncpy(prefix, spinner[PrintAndLogEx_spinidx], sizeof(prefix) - 1);
|
strncpy(prefix, spinner[PrintAndLogEx_spinidx], sizeof(prefix) - 1);
|
||||||
PrintAndLogEx_spinidx++;
|
PrintAndLogEx_spinidx++;
|
||||||
if (PrintAndLogEx_spinidx >= ARRAYLEN(spinner))
|
if (PrintAndLogEx_spinidx >= ARRAYLEN(spinner)) {
|
||||||
PrintAndLogEx_spinidx = 0;
|
PrintAndLogEx_spinidx = 0;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case NORMAL:
|
case NORMAL:
|
||||||
|
@ -306,8 +324,9 @@ void PrintAndLogEx(logLevel_t level, const char *fmt, ...) {
|
||||||
const char delim[2] = "\n";
|
const char delim[2] = "\n";
|
||||||
|
|
||||||
// line starts with newline
|
// line starts with newline
|
||||||
if (buffer[0] == '\n')
|
if (buffer[0] == '\n') {
|
||||||
fPrintAndLog(stream, "");
|
fPrintAndLog(stream, "");
|
||||||
|
}
|
||||||
|
|
||||||
token = strtok_r(buffer, delim, &tmp_ptr);
|
token = strtok_r(buffer, delim, &tmp_ptr);
|
||||||
|
|
||||||
|
@ -315,16 +334,21 @@ void PrintAndLogEx(logLevel_t level, const char *fmt, ...) {
|
||||||
|
|
||||||
size_t size = strlen(buffer2);
|
size_t size = strlen(buffer2);
|
||||||
|
|
||||||
if (strlen(token))
|
if (strlen(token)) {
|
||||||
snprintf(buffer2 + size, sizeof(buffer2) - size, "%s%s\n", prefix, token);
|
snprintf(buffer2 + size, sizeof(buffer2) - size, "%s%s\n", prefix, token);
|
||||||
else
|
} else {
|
||||||
snprintf(buffer2 + size, sizeof(buffer2) - size, "\n");
|
snprintf(buffer2 + size, sizeof(buffer2) - size, "\n");
|
||||||
|
}
|
||||||
|
|
||||||
token = strtok_r(NULL, delim, &tmp_ptr);
|
token = strtok_r(NULL, delim, &tmp_ptr);
|
||||||
}
|
}
|
||||||
|
|
||||||
fPrintAndLog(stream, "%s", buffer2);
|
fPrintAndLog(stream, "%s", buffer2);
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
|
|
||||||
snprintf(buffer2, sizeof(buffer2), "%s%s", prefix, buffer);
|
snprintf(buffer2, sizeof(buffer2), "%s%s", prefix, buffer);
|
||||||
|
|
||||||
if (level == INPLACE) {
|
if (level == INPLACE) {
|
||||||
// ignore INPLACE if rest of output is grabbed
|
// ignore INPLACE if rest of output is grabbed
|
||||||
if (!(g_printAndLog & PRINTANDLOG_GRAB)) {
|
if (!(g_printAndLog & PRINTANDLOG_GRAB)) {
|
||||||
|
@ -354,6 +378,7 @@ static void fPrintAndLog(FILE *stream, const char *fmt, ...) {
|
||||||
if (logging && g_session.incognito) {
|
if (logging && g_session.incognito) {
|
||||||
logging = 0;
|
logging = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((g_printAndLog & PRINTANDLOG_LOG) && logging && !logfile) {
|
if ((g_printAndLog & PRINTANDLOG_LOG) && logging && !logfile) {
|
||||||
char *my_logfile_path = NULL;
|
char *my_logfile_path = NULL;
|
||||||
char filename[40];
|
char filename[40];
|
||||||
|
@ -361,11 +386,15 @@ static void fPrintAndLog(FILE *stream, const char *fmt, ...) {
|
||||||
time_t now = time(NULL);
|
time_t now = time(NULL);
|
||||||
timenow = gmtime(&now);
|
timenow = gmtime(&now);
|
||||||
strftime(filename, sizeof(filename), PROXLOG, timenow);
|
strftime(filename, sizeof(filename), PROXLOG, timenow);
|
||||||
|
|
||||||
if (searchHomeFilePath(&my_logfile_path, LOGS_SUBDIR, filename, true) != PM3_SUCCESS) {
|
if (searchHomeFilePath(&my_logfile_path, LOGS_SUBDIR, filename, true) != PM3_SUCCESS) {
|
||||||
|
|
||||||
printf(_YELLOW_("[-]") " Logging disabled!\n");
|
printf(_YELLOW_("[-]") " Logging disabled!\n");
|
||||||
my_logfile_path = NULL;
|
my_logfile_path = NULL;
|
||||||
logging = 0;
|
logging = 0;
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
|
|
||||||
logfile = fopen(my_logfile_path, "a");
|
logfile = fopen(my_logfile_path, "a");
|
||||||
if (logfile == NULL) {
|
if (logfile == NULL) {
|
||||||
printf(_YELLOW_("[-]") " Can't open logfile %s, logging disabled!\n", my_logfile_path);
|
printf(_YELLOW_("[-]") " Can't open logfile %s, logging disabled!\n", my_logfile_path);
|
||||||
|
@ -411,13 +440,16 @@ static void fPrintAndLog(FILE *stream, const char *fmt, ...) {
|
||||||
linefeed = false;
|
linefeed = false;
|
||||||
buffer[strlen(buffer) - 1] = 0;
|
buffer[strlen(buffer) - 1] = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool filter_ansi = !g_session.supports_colors;
|
bool filter_ansi = !g_session.supports_colors;
|
||||||
memcpy_filter_ansi(buffer2, buffer, sizeof(buffer), filter_ansi);
|
memcpy_filter_ansi(buffer2, buffer, sizeof(buffer), filter_ansi);
|
||||||
if (g_printAndLog & PRINTANDLOG_PRINT) {
|
|
||||||
|
if ((g_printAndLog & PRINTANDLOG_PRINT) == PRINTANDLOG_PRINT) {
|
||||||
memcpy_filter_emoji(buffer3, buffer2, sizeof(buffer2), g_session.emoji_mode);
|
memcpy_filter_emoji(buffer3, buffer2, sizeof(buffer2), g_session.emoji_mode);
|
||||||
fprintf(stream, "%s", buffer3);
|
fprintf(stream, "%s", buffer3);
|
||||||
if (linefeed)
|
if (linefeed) {
|
||||||
fprintf(stream, "\n");
|
fprintf(stream, "\n");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef RL_STATE_READCMD
|
#ifdef RL_STATE_READCMD
|
||||||
|
@ -433,33 +465,44 @@ static void fPrintAndLog(FILE *stream, const char *fmt, ...) {
|
||||||
|
|
||||||
if (((g_printAndLog & PRINTANDLOG_LOG) && logging && logfile) ||
|
if (((g_printAndLog & PRINTANDLOG_LOG) && logging && logfile) ||
|
||||||
(g_printAndLog & PRINTANDLOG_GRAB)) {
|
(g_printAndLog & PRINTANDLOG_GRAB)) {
|
||||||
|
|
||||||
memcpy_filter_emoji(buffer3, buffer2, sizeof(buffer2), EMO_ALTTEXT);
|
memcpy_filter_emoji(buffer3, buffer2, sizeof(buffer2), EMO_ALTTEXT);
|
||||||
|
|
||||||
if (filter_ansi == false) {
|
if (filter_ansi == false) {
|
||||||
memcpy_filter_ansi(buffer, buffer3, sizeof(buffer3), true);
|
memcpy_filter_ansi(buffer, buffer3, sizeof(buffer3), true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((g_printAndLog & PRINTANDLOG_LOG) && logging && logfile) {
|
if ((g_printAndLog & PRINTANDLOG_LOG) && logging && logfile) {
|
||||||
|
|
||||||
if (filter_ansi) {
|
if (filter_ansi) {
|
||||||
fprintf(logfile, "%s", buffer3);
|
fprintf(logfile, "%s", buffer3);
|
||||||
} else {
|
} else {
|
||||||
fprintf(logfile, "%s", buffer);
|
fprintf(logfile, "%s", buffer);
|
||||||
}
|
}
|
||||||
if (linefeed)
|
|
||||||
|
if (linefeed) {
|
||||||
fprintf(logfile, "\n");
|
fprintf(logfile, "\n");
|
||||||
|
}
|
||||||
fflush(logfile);
|
fflush(logfile);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (g_printAndLog & PRINTANDLOG_GRAB) {
|
if (g_printAndLog & PRINTANDLOG_GRAB) {
|
||||||
|
|
||||||
if (filter_ansi) {
|
if (filter_ansi) {
|
||||||
fill_grabber(buffer3);
|
fill_grabber(buffer3);
|
||||||
} else {
|
} else {
|
||||||
fill_grabber(buffer);
|
fill_grabber(buffer);
|
||||||
}
|
}
|
||||||
if (linefeed)
|
|
||||||
|
if (linefeed) {
|
||||||
fill_grabber("\n");
|
fill_grabber("\n");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (flushAfterWrite)
|
if (flushAfterWrite) {
|
||||||
fflush(stdout);
|
fflush(stdout);
|
||||||
|
}
|
||||||
|
|
||||||
//release lock
|
//release lock
|
||||||
pthread_mutex_unlock(&g_print_lock);
|
pthread_mutex_unlock(&g_print_lock);
|
||||||
|
@ -478,9 +521,10 @@ void memcpy_filter_rlmarkers(void *dest, const void *src, size_t n) {
|
||||||
uint8_t *rsrc = (uint8_t *)src;
|
uint8_t *rsrc = (uint8_t *)src;
|
||||||
uint16_t si = 0;
|
uint16_t si = 0;
|
||||||
for (size_t i = 0; i < n; i++) {
|
for (size_t i = 0; i < n; i++) {
|
||||||
if ((rsrc[i] == '\001') || (rsrc[i] == '\002'))
|
if ((rsrc[i] == '\001') || (rsrc[i] == '\002')) {
|
||||||
// skip readline special markers
|
// skip readline special markers
|
||||||
continue;
|
continue;
|
||||||
|
}
|
||||||
rdest[si++] = rsrc[i];
|
rdest[si++] = rsrc[i];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -555,9 +555,13 @@ void reverse_arraybytes_copy(uint8_t *arr, uint8_t *dest, size_t len) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Boost performance by copying in chunks of 1, 2, or 4 bytes when feasible.
|
// TODO: Boost performance by copying in chunks of 1, 2, or 4 bytes when feasible.
|
||||||
size_t concatbits(uint8_t *dest, int dest_offset, const uint8_t *src, int src_offset, size_t nbits) {
|
/**
|
||||||
|
* @brief Concatenate bits from src to dest, bitstream is stored MSB first
|
||||||
|
* which means that the dest_offset=0 is the MSB of the dest[0]
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
size_t concatbits(uint8_t *dest, int dest_offset, const uint8_t *src, int src_offset, size_t nbits, bool src_lsb) {
|
||||||
int i, end, step;
|
int i, end, step;
|
||||||
|
|
||||||
// overlap
|
// overlap
|
||||||
if ((src - dest) * 8 + src_offset - dest_offset > 0) {
|
if ((src - dest) * 8 + src_offset - dest_offset > 0) {
|
||||||
i = 0;
|
i = 0;
|
||||||
|
@ -571,8 +575,8 @@ size_t concatbits(uint8_t *dest, int dest_offset, const uint8_t *src, int src_of
|
||||||
|
|
||||||
for (; i != end; i += step) {
|
for (; i != end; i += step) {
|
||||||
// equiv of dest_bits[dest_offset + i] = src_bits[src_offset + i]
|
// equiv of dest_bits[dest_offset + i] = src_bits[src_offset + i]
|
||||||
CLEAR_BIT(dest, dest_offset + i);
|
CLEAR_BIT_MSB(dest, dest_offset + i);
|
||||||
if (TEST_BIT(src, src_offset + i)) SET_BIT(dest, dest_offset + i);
|
if (src_lsb ? TEST_BIT_LSB(src, src_offset + i) : TEST_BIT_MSB(src, src_offset + i)) SET_BIT_MSB(dest, dest_offset + i);
|
||||||
}
|
}
|
||||||
|
|
||||||
return dest_offset + nbits;
|
return dest_offset + nbits;
|
||||||
|
|
|
@ -150,7 +150,7 @@ bool hexstr_to_byte_array(const char *hexstr, uint8_t *d, size_t *n);
|
||||||
void reverse_arraybytes(uint8_t *arr, size_t len);
|
void reverse_arraybytes(uint8_t *arr, size_t len);
|
||||||
void reverse_arraybytes_copy(uint8_t *arr, uint8_t *dest, size_t len);
|
void reverse_arraybytes_copy(uint8_t *arr, uint8_t *dest, size_t len);
|
||||||
|
|
||||||
size_t concatbits(uint8_t *dest, int dest_offset, const uint8_t *src, int src_offset, size_t nbits);
|
size_t concatbits(uint8_t *dest, int dest_offset, const uint8_t *src, int src_offset, size_t nbits, bool src_lsb);
|
||||||
int char2int(char c);
|
int char2int(char c);
|
||||||
int hexstr2ByteArr(const char *hexstr, unsigned char *array, size_t asize);
|
int hexstr2ByteArr(const char *hexstr, unsigned char *array, size_t asize);
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -5047,7 +5047,7 @@
|
||||||
"-v, --verbose verbose output",
|
"-v, --verbose verbose output",
|
||||||
"-f, --file <fn> Specify a filename for dump file",
|
"-f, --file <fn> Specify a filename for dump file",
|
||||||
"--emu from emulator memory",
|
"--emu from emulator memory",
|
||||||
"--start <dec> index of block to start writing (default 0)",
|
"--start <dec> index of block to start writing (def 0)",
|
||||||
"--end <dec> index of block to end writing (default last block)"
|
"--end <dec> index of block to end writing (default last block)"
|
||||||
],
|
],
|
||||||
"usage": "hf mf gload [-hv] [--mini] [--1k] [--1k+] [--2k] [--4k] [-p <hex>] [-f <fn>] [--emu] [--start <dec>] [--end <dec>]"
|
"usage": "hf mf gload [-hv] [--mini] [--1k] [--1k+] [--2k] [--4k] [-p <hex>] [-f <fn>] [--emu] [--start <dec>] [--end <dec>]"
|
||||||
|
@ -10992,8 +10992,8 @@
|
||||||
"-r, --reset Reset configuration to default values",
|
"-r, --reset Reset configuration to default values",
|
||||||
"-p, --pwd <hex> Password, 7bytes, LSB-order",
|
"-p, --pwd <hex> Password, 7bytes, LSB-order",
|
||||||
"-d, --delay <dec> Tag initialization delay (in us)",
|
"-d, --delay <dec> Tag initialization delay (in us)",
|
||||||
"--lw <dec> offset, low pulses width (in us)",
|
"--lw <dec> offset, low pulses width (in us), optional!",
|
||||||
"--lp <dec> offset, low pulses position (in us)"
|
"--lp <dec> offset, low pulses position (in us), optional!"
|
||||||
],
|
],
|
||||||
"usage": "lf pcf7931 config [-hr] [-p <hex>] [-d <dec>] [--lw <dec>] [--lp <dec>]"
|
"usage": "lf pcf7931 config [-hr] [-p <hex>] [-d <dec>] [--lw <dec>] [--lp <dec>]"
|
||||||
},
|
},
|
||||||
|
@ -13232,6 +13232,6 @@
|
||||||
"metadata": {
|
"metadata": {
|
||||||
"commands_extracted": 760,
|
"commands_extracted": 760,
|
||||||
"extracted_by": "PM3Help2JSON v1.00",
|
"extracted_by": "PM3Help2JSON v1.00",
|
||||||
"extracted_on": "2025-03-12T15:46:33"
|
"extracted_on": "2025-03-18T06:54:58"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
147
doc/md/em4x70/arbitrary_lf_em_commands.md
Normal file
147
doc/md/em4x70/arbitrary_lf_em_commands.md
Normal file
|
@ -0,0 +1,147 @@
|
||||||
|
# arbitrary lf em commands
|
||||||
|
|
||||||
|
Goals:
|
||||||
|
1. Improved logging of `lf em` commands and responses
|
||||||
|
2. Greater certainty in command sequences
|
||||||
|
3. Easier testing of new commands
|
||||||
|
|
||||||
|
## Methodology
|
||||||
|
|
||||||
|
This is documenting the actual commands used by existing code. Phases include:
|
||||||
|
* Document the existing command sequences
|
||||||
|
* Document the existing logging APIs
|
||||||
|
* Define small set of timing-sensitive functions as abstractions
|
||||||
|
* Implement the abstractions
|
||||||
|
* Add logging
|
||||||
|
|
||||||
|
The goal is to improve logging and debugging, and allow easily testing new LF commands.
|
||||||
|
|
||||||
|
## EM4x70 (aka ID48, aka Megamos)
|
||||||
|
|
||||||
|
Only six command sequences currently used:
|
||||||
|
|
||||||
|
#define EM4X70_COMMAND_ID 0x01
|
||||||
|
#define EM4X70_COMMAND_UM1 0x02
|
||||||
|
#define EM4X70_COMMAND_AUTH 0x03
|
||||||
|
#define EM4X70_COMMAND_PIN 0x04
|
||||||
|
#define EM4X70_COMMAND_WRITE 0x05
|
||||||
|
#define EM4X70_COMMAND_UM2 0x07
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
### ID Command
|
||||||
|
|
||||||
|
Wait for `LIW` (listen window), and start transmission at next `LIW`:
|
||||||
|
|
||||||
|
source | bits | comment
|
||||||
|
----------|---------|---------
|
||||||
|
tag | LIW | listen window sync
|
||||||
|
reader | `0b00` | RM
|
||||||
|
reader | `0b001` | CMD
|
||||||
|
reader | `0b1` | command parity bit
|
||||||
|
tag | HEADER | HEADER (0b1111'1111'1111'0000)
|
||||||
|
tag | 32-bits | ID (D31..D0)
|
||||||
|
tag | LIW | tag reverts to be ready for next command
|
||||||
|
|
||||||
|
### UM1 Command
|
||||||
|
|
||||||
|
source | bits | comment
|
||||||
|
----------|---------|---------
|
||||||
|
tag | LIW | listen window
|
||||||
|
reader | `0b00` | RM
|
||||||
|
reader | `0b010` | CMD
|
||||||
|
reader | `0b1` | command parity bit
|
||||||
|
tag | 16-bits | HEADER
|
||||||
|
tag | 32-bits | UM1 data
|
||||||
|
tag | LIW | tag reverts to be ready for next command
|
||||||
|
|
||||||
|
### UM2 Command
|
||||||
|
|
||||||
|
source | bits | comment
|
||||||
|
----------|---------|---------
|
||||||
|
tag | LIW | listen window
|
||||||
|
reader | `0b00` | RM
|
||||||
|
reader | `0b111` | CMD
|
||||||
|
reader | `0b1` | command parity bit
|
||||||
|
tag | 16-bits | HEADER
|
||||||
|
tag | 64-bits | UM2 data
|
||||||
|
tag | LIW | tag reverts to be ready for next command
|
||||||
|
|
||||||
|
|
||||||
|
### Auth Command
|
||||||
|
|
||||||
|
source | bits | comment
|
||||||
|
----------|---------|---------
|
||||||
|
tag | LIW | listen window
|
||||||
|
reader | `0b00` | RM
|
||||||
|
reader | `0b011` | CMD
|
||||||
|
reader | `0b0` | command parity bit
|
||||||
|
reader | 56-bits | RN
|
||||||
|
reader | 7-bits | Tdiv == 0b0000000 (always zero)
|
||||||
|
reader | 28-bits | f(RN)
|
||||||
|
tag | 16-bits | HEADER
|
||||||
|
tag | 20-bits | g(RN)
|
||||||
|
tag | LIW | tag reverts to be ready for next command
|
||||||
|
|
||||||
|
### Write Word
|
||||||
|
|
||||||
|
source | bits | comment
|
||||||
|
----------|---------|---------
|
||||||
|
tag | LIW | listen window
|
||||||
|
reader | `0b00` | RM
|
||||||
|
reader | `0b101` | CMD
|
||||||
|
reader | `0b0` | command parity bit
|
||||||
|
reader | 4-bits | address/block to write
|
||||||
|
reader | 1-bit | address/block parity bit
|
||||||
|
reader | 25-bits | 5x5 data w/ row and column parity
|
||||||
|
tag | ACK | Wait (TWA) for ACK ... time to wait before searching for ACK
|
||||||
|
tag | ACK | Wait (WEE) for ACK ... time to wait before searching for ACK
|
||||||
|
tag | LIW | tag reverts to be ready for next command
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
### PIN Command
|
||||||
|
|
||||||
|
source | bits | comment
|
||||||
|
----------|---------|---------
|
||||||
|
tag | LIW | listen window
|
||||||
|
reader | `0b00` | RM
|
||||||
|
reader | `0b100` | CMD
|
||||||
|
reader | `0b1` | command parity bit
|
||||||
|
reader | 32-bits | ID of the tag
|
||||||
|
reader | 32-bits | PIN
|
||||||
|
tag | ACK | Wait (TWALB) for ACK ... time to wait before searching for ACK
|
||||||
|
tag | HEADER | DELAYED (TWEE) header ... time to wait before searching for header
|
||||||
|
tag | 32-bits | ID of the tag
|
||||||
|
tag | LIW | tag reverts to be ready for next command
|
||||||
|
|
||||||
|
|
||||||
|
### Abstraction required
|
||||||
|
|
||||||
|
Possible items to abstract:
|
||||||
|
* bits to send: quantity of bits to be sent + storage containing those bits
|
||||||
|
* bits to receive: expected bits to receive + storage to receive those bits
|
||||||
|
* LIW: special-case handling to synchronize next command
|
||||||
|
* ACK: special-case handling to wait for ACK
|
||||||
|
* HEADER: special-case handling to wait for HEADER
|
||||||
|
* DELAY: ticks to delay before processing next item
|
||||||
|
|
||||||
|
Special handling required for:
|
||||||
|
* `HEADER` --> 12-bits of zero, 4-bits of one. Consider a timeout: if tag disappears, no pulse found, while sometimes expect long time before HEADER appears (as in SEND_PIN). Read of header may miss the first few bits during transition, so need to special-case handling of this detection.
|
||||||
|
* `LIW` --> Timing-sensitive, syncs reader with tag ... reader must send during 32 period where chip's modulator is ON.
|
||||||
|
* `ACK` --> This is currently a time-to-delay.
|
||||||
|
Should this be a maximum time to wait for ACK?
|
||||||
|
Currently, could sit waiting for long time
|
||||||
|
if no tag present, as `check_ack()` has no timeout.
|
||||||
|
|
||||||
|
```C
|
||||||
|
WaitTicks(EM4X70_T_TAG_TWA);
|
||||||
|
if (check_ack())
|
||||||
|
WaitTicks(EM4X70_T_TAG_WEE);
|
||||||
|
if (check_ack())
|
||||||
|
return PM3_SUCCESS;
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
|
1517
doc/md/em4x70/lf_em4x70_trace_notes.md
Normal file
1517
doc/md/em4x70/lf_em4x70_trace_notes.md
Normal file
File diff suppressed because it is too large
Load diff
|
@ -202,10 +202,15 @@ extern bool g_tearoff_enabled;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// bit stream operations
|
// bit stream operations
|
||||||
#define TEST_BIT(data, i) (*((data) + ((i) / 8)) >> (7 - ((i) % 8))) & 1
|
#define TEST_BIT_MSB(data, i) ((*((data) + ((i) / 8)) >> (7 - ((i) % 8))) & 1)
|
||||||
#define SET_BIT(data, i) *((data) + ((i) / 8)) |= (1 << (7 - ((i) % 8)))
|
#define SET_BIT_MSB(data, i) (*((data) + ((i) / 8)) |= (1 << (7 - ((i) % 8))))
|
||||||
#define CLEAR_BIT(data, i) *((data) + ((i) / 8)) &= ~(1 << (7 - ((i) % 8)))
|
#define CLEAR_BIT_MSB(data, i) (*((data) + ((i) / 8)) &= ~(1 << (7 - ((i) % 8))))
|
||||||
#define FLIP_BIT(data, i) *((data) + ((i) / 8)) ^= (1 << (7 - ((i) % 8)))
|
#define FLIP_BIT_MSB(data, i) (*((data) + ((i) / 8)) ^= (1 << (7 - ((i) % 8))))
|
||||||
|
|
||||||
|
#define TEST_BIT_LSB(data, i) ((*((data) + ((i) / 8)) >> ((i) % 8)) & 1)
|
||||||
|
#define SET_BIT_LSB(data, i) (*((data) + ((i) / 8)) |= (1 << ((i) % 8)))
|
||||||
|
#define CLEAR_BIT_LSB(data, i) (*((data) + ((i) / 8)) &= ~(1 << ((i) % 8)))
|
||||||
|
#define FLIP_BIT_LSB(data, i) (*((data) + ((i) / 8)) ^= (1 << ((i) % 8)))
|
||||||
|
|
||||||
// time for decompressing and loading the image to the FPGA
|
// time for decompressing and loading the image to the FPGA
|
||||||
#define FPGA_LOAD_WAIT_TIME (1500)
|
#define FPGA_LOAD_WAIT_TIME (1500)
|
||||||
|
|
|
@ -21,6 +21,7 @@
|
||||||
|
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
|
#include <assert.h>
|
||||||
|
|
||||||
#define EM4X70_NUM_BLOCKS 16
|
#define EM4X70_NUM_BLOCKS 16
|
||||||
|
|
||||||
|
@ -28,24 +29,36 @@
|
||||||
#define EM4X70_PIN_WORD_LOWER 10
|
#define EM4X70_PIN_WORD_LOWER 10
|
||||||
#define EM4X70_PIN_WORD_UPPER 11
|
#define EM4X70_PIN_WORD_UPPER 11
|
||||||
|
|
||||||
|
/// @brief Command transport structure for EM4x70 commands.
|
||||||
|
/// @details
|
||||||
|
/// This structure is used to transport data from the PC
|
||||||
|
/// to the proxmark3, and contain all data needed for
|
||||||
|
/// a given `lf em 4x70 ...` command to be processed
|
||||||
|
/// on the proxmark3.
|
||||||
|
/// The only requirement is that this structure remain
|
||||||
|
/// smaller than the NG buffer size (256 bytes).
|
||||||
typedef struct {
|
typedef struct {
|
||||||
// ISSUE: `bool` type does not have a standard-defined size.
|
|
||||||
// therefore, compatibility between architectures /
|
|
||||||
// compilers is not guaranteed.
|
|
||||||
// ISSUE: C99 has no _Static_assert() ... was added in C11
|
|
||||||
// TODO: add _Static_assert(sizeof(bool)==1);
|
|
||||||
// TODO: add _Static_assert(sizeof(em4x70_data_t)==36);
|
|
||||||
bool parity;
|
bool parity;
|
||||||
|
|
||||||
// Used for writing address
|
// Used for writing address
|
||||||
uint8_t address;
|
uint8_t address;
|
||||||
// ISSUE: Presumes target is little-endian
|
// BUGBUG: Non-portable ... presumes stored in little-endian form!
|
||||||
uint16_t word;
|
uint16_t word;
|
||||||
|
|
||||||
// PIN to unlock
|
// PIN to unlock
|
||||||
|
// BUGBUG: Non-portable ... presumes stored in little-endian form!
|
||||||
uint32_t pin;
|
uint32_t pin;
|
||||||
|
|
||||||
// Used for authentication
|
// Used for authentication
|
||||||
|
//
|
||||||
|
// IoT safe subset of C++ would be helpful here,
|
||||||
|
// to support variable-bit-length integer types
|
||||||
|
// as integral integer types.
|
||||||
|
//
|
||||||
|
// Even C23 would work for this (GCC14+, Clang15+):
|
||||||
|
// _BitInt(56) rnd;
|
||||||
|
// _BitInt(28) frnd;
|
||||||
|
// _BitInt(20) grnd;
|
||||||
uint8_t frnd[4];
|
uint8_t frnd[4];
|
||||||
uint8_t grnd[3];
|
uint8_t grnd[3];
|
||||||
uint8_t rnd[7];
|
uint8_t rnd[7];
|
||||||
|
@ -54,9 +67,20 @@ typedef struct {
|
||||||
uint8_t crypt_key[12];
|
uint8_t crypt_key[12];
|
||||||
|
|
||||||
// used for bruteforce the partial key
|
// used for bruteforce the partial key
|
||||||
// ISSUE: Presumes target is little-endian
|
// BUGBUG: Non-portable ... presumes stored in little-endian form!
|
||||||
uint16_t start_key;
|
uint16_t start_key;
|
||||||
|
|
||||||
} em4x70_data_t;
|
} em4x70_data_t;
|
||||||
|
//_Static_assert(sizeof(em4x70_data_t) == 36);
|
||||||
|
|
||||||
|
// ISSUE: `bool` type does not have a standard-defined size.
|
||||||
|
// therefore, compatibility between architectures /
|
||||||
|
// compilers is not guaranteed.
|
||||||
|
// TODO: verify alignof(bool) == 1
|
||||||
|
//_Static_assert(sizeof(bool) == 1, "bool size mismatch");
|
||||||
|
typedef union {
|
||||||
|
uint8_t data[32];
|
||||||
|
} em4x70_tag_t;
|
||||||
|
//_Static_assert(sizeof(em4x70_tag_t) == 32, "em4x70_tag_t size mismatch");
|
||||||
|
|
||||||
#endif /* EM4X70_H__ */
|
#endif /* EM4X70_H__ */
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue