mirror of
https://github.com/RfidResearchGroup/proxmark3.git
synced 2025-08-20 13:23:51 -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...
|
||||
|
||||
## [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 `lf pcf7931` read code - fixed some checks for more stability (@tinooo)
|
||||
- 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
|
||||
#define EM4x70_H
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include <assert.h>
|
||||
#include "../include/em4x70.h"
|
||||
|
||||
typedef struct {
|
||||
uint8_t data[32];
|
||||
} em4x70_tag_t;
|
||||
|
||||
typedef enum {
|
||||
RISING_EDGE,
|
||||
FALLING_EDGE
|
||||
|
|
|
@ -320,7 +320,7 @@ static void hitag2_handle_reader_command(uint8_t *rx, const size_t rxlen, uint8_
|
|||
|
||||
// reader/writer
|
||||
// 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
|
||||
// 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
|
||||
// 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();
|
||||
|
||||
uint32_t wait = 0;
|
||||
// Send the content of the frame
|
||||
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
|
||||
|
@ -378,14 +378,14 @@ static uint32_t hitag_reader_send_frame(const uint8_t *frame, size_t frame_len)
|
|||
|
||||
// reader / writer commands
|
||||
// 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();
|
||||
|
||||
uint32_t wait = 0;
|
||||
// Send the content of the frame
|
||||
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
|
||||
|
@ -1863,7 +1863,7 @@ void ReaderHitag(const lf_hitag_data_t *payload, bool ledcontrol) {
|
|||
}
|
||||
|
||||
// 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;
|
||||
|
||||
// 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
|
||||
command_duration = hitag_reader_send_frame(tx, txlen);
|
||||
command_duration = hitag2_reader_send_frame(tx, txlen);
|
||||
|
||||
// global write state variable used
|
||||
// tearoff occurred
|
||||
|
@ -2434,9 +2434,9 @@ static void ht2_send(bool turn_on, uint32_t *cmd_start
|
|||
|
||||
// Transmit the reader frame
|
||||
if (send_bits) {
|
||||
*cmd_duration = hitag_reader_send_framebits(tx, txlen);
|
||||
*cmd_duration = hitag2_reader_send_framebits(tx, txlen);
|
||||
} else {
|
||||
*cmd_duration = hitag_reader_send_frame(tx, txlen);
|
||||
*cmd_duration = hitag2_reader_send_frame(tx, txlen);
|
||||
}
|
||||
|
||||
*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) {
|
||||
|
||||
// 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);
|
||||
|
||||
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;
|
||||
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);
|
||||
|
||||
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
|
||||
txlen = 0;
|
||||
cmd = HITAGS_SELECT;
|
||||
txlen = concatbits(tx, txlen, &cmd, 0, 5);
|
||||
txlen = concatbits(tx, txlen, rx, 0, 32);
|
||||
txlen = concatbits(tx, txlen, &cmd, 0, 5, false);
|
||||
txlen = concatbits(tx, txlen, rx, 0, 32, false);
|
||||
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);
|
||||
|
||||
|
@ -1140,8 +1140,8 @@ static int hts_select_tag(const lf_hitag_data_t *packet, uint8_t *tx, size_t siz
|
|||
}
|
||||
|
||||
txlen = 0;
|
||||
txlen = concatbits(tx, txlen, rnd, 0, 32);
|
||||
txlen = concatbits(tx, txlen, auth_ks, 0, 32);
|
||||
txlen = concatbits(tx, txlen, rnd, 0, 32, false);
|
||||
txlen = concatbits(tx, txlen, auth_ks, 0, 32, false);
|
||||
|
||||
DBG DbpString("Authenticating using key:");
|
||||
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
|
||||
txlen = 0;
|
||||
cmd = HITAGS_WRITE_PAGE;
|
||||
txlen = concatbits(tx, txlen, &cmd, 0, 4);
|
||||
txlen = concatbits(tx, txlen, &cmd, 0, 4, false);
|
||||
|
||||
uint8_t addr = 64;
|
||||
txlen = concatbits(tx, txlen, &addr, 0, 8);
|
||||
txlen = concatbits(tx, txlen, &addr, 0, 8, false);
|
||||
|
||||
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);
|
||||
|
||||
|
@ -1189,9 +1189,9 @@ static int hts_select_tag(const lf_hitag_data_t *packet, uint8_t *tx, size_t siz
|
|||
}
|
||||
|
||||
txlen = 0;
|
||||
txlen = concatbits(tx, txlen, packet->pwd, 0, 32);
|
||||
txlen = concatbits(tx, txlen, packet->pwd, 0, 32, false);
|
||||
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);
|
||||
|
||||
|
@ -1287,11 +1287,11 @@ void hts_read(const lf_hitag_data_t *payload, bool ledcontrol) {
|
|||
//send read request
|
||||
size_t txlen = 0;
|
||||
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;
|
||||
txlen = concatbits(tx, txlen, &addr, 0, 8);
|
||||
txlen = concatbits(tx, txlen, &addr, 0, 8, false);
|
||||
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);
|
||||
|
||||
|
@ -1396,13 +1396,13 @@ void hts_write_page(const lf_hitag_data_t *payload, bool ledcontrol) {
|
|||
txlen = 0;
|
||||
|
||||
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;
|
||||
txlen = concatbits(tx, txlen, &addr, 0, 8);
|
||||
txlen = concatbits(tx, txlen, &addr, 0, 8, false);
|
||||
|
||||
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);
|
||||
|
||||
|
@ -1430,9 +1430,9 @@ void hts_write_page(const lf_hitag_data_t *payload, bool ledcontrol) {
|
|||
// }
|
||||
|
||||
txlen = 0;
|
||||
txlen = concatbits(tx, txlen, payload->data, 0, 32);
|
||||
txlen = concatbits(tx, txlen, payload->data, 0, 32, false);
|
||||
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;
|
||||
|
||||
|
@ -1493,7 +1493,7 @@ int hts_read_uid(uint32_t *uid, bool ledcontrol, bool send_answer) {
|
|||
size_t txlen = 0;
|
||||
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);
|
||||
|
||||
|
|
130
armsrc/pcf7931.c
130
armsrc/pcf7931.c
|
@ -43,7 +43,7 @@
|
|||
#define _64T0 (CLOCK)
|
||||
|
||||
// calculating the two possible pmc lengths, based on the clock. -4 at the end is to make sure not to increment too far
|
||||
#define PMC_16T0_LEN ((128 + 127 + 16 + 32 + 33 + 16) * CLOCK/64);
|
||||
#define PMC_16T0_LEN ((128 + 127 + 16 + 32 + 33 + 16) * CLOCK/64);
|
||||
#define PMC_32T0_LEN ((128 + 127 + 16 + 32 + 33 ) * CLOCK/64);
|
||||
|
||||
// theshold for recognition of positive/negative slope
|
||||
|
@ -54,13 +54,13 @@ size_t DemodPCF7931(uint8_t **outBlocks, bool ledcontrol) {
|
|||
uint8_t blocks[8][16];
|
||||
uint8_t *dest = BigBuf_get_addr();
|
||||
uint16_t g_GraphTraceLen = BigBuf_max_traceLen();
|
||||
// limit g_GraphTraceLen to a little more than 2 data frames.
|
||||
// limit g_GraphTraceLen to a little more than 2 data frames.
|
||||
// To make sure a complete dataframe is in the dataset.
|
||||
// 1 Frame is 16 Byte -> 128byte. at a T0 of 64 -> 8129 Samples per frame.
|
||||
// + PMC -> 384T0 --> 8576 samples required for one block
|
||||
// to make sure that one complete block is definitely being sampled, we need 2 times that
|
||||
// which is ~17.xxx samples. round up. and clamp to this value.
|
||||
|
||||
|
||||
// TODO: Doublecheck why this is being limited? - seems not to be needed.
|
||||
// g_GraphTraceLen = (g_GraphTraceLen > 18000) ? 18000 : g_GraphTraceLen;
|
||||
|
||||
|
@ -87,15 +87,15 @@ size_t DemodPCF7931(uint8_t **outBlocks, bool ledcontrol) {
|
|||
samplePosLastEdge = 0;
|
||||
block_done = 0;
|
||||
bitPos = 0;
|
||||
lastClockDuration=0;
|
||||
|
||||
for (sample = 1 ; sample < g_GraphTraceLen-4; sample++) {
|
||||
// condition is searching for the next edge, in the expected diretion.
|
||||
//todo: without flouz
|
||||
dest[sample] = (uint8_t)(dest[sample-1] * IIR_CONST1 + dest[sample] * IIR_CONST2); // apply IIR filter
|
||||
lastClockDuration = 0;
|
||||
|
||||
if ( ((dest[sample] + THRESHOLD) < dest[sample-1] && expectedNextEdge == FALLING ) ||
|
||||
((dest[sample] - THRESHOLD) > dest[sample-1] && expectedNextEdge == RISING )) {
|
||||
for (sample = 1 ; sample < g_GraphTraceLen - 4; sample++) {
|
||||
// condition is searching for the next edge, in the expected diretion.
|
||||
//todo: without flouz
|
||||
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) ||
|
||||
((dest[sample] - THRESHOLD) > dest[sample - 1] && expectedNextEdge == RISING)) {
|
||||
//okay, next falling/rising edge found
|
||||
|
||||
expectedNextEdge = (expectedNextEdge == FALLING) ? RISING : FALLING; //toggle the next expected edge
|
||||
|
@ -104,14 +104,14 @@ size_t DemodPCF7931(uint8_t **outBlocks, bool ledcontrol) {
|
|||
lastClockDuration = samplePosCurrentEdge - samplePosLastEdge;
|
||||
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:
|
||||
// 16T0
|
||||
// 16T0
|
||||
if (ABS(lastClockDuration - _16T0) < TOLERANCE) {
|
||||
|
||||
// if the clock before also was 16T0, it is a PMC!
|
||||
if (ABS(beforeLastClockDuration - _16T0) < TOLERANCE) {
|
||||
if (ABS(beforeLastClockDuration - _16T0) < TOLERANCE) {
|
||||
// It's a PMC
|
||||
Dbprintf(_GREEN_("PMC 16T0 FOUND:") " bitPos: %d, sample: %d", bitPos, sample);
|
||||
sample += PMC_16T0_LEN; // move to the sample after PMC
|
||||
|
@ -120,44 +120,44 @@ size_t DemodPCF7931(uint8_t **outBlocks, bool ledcontrol) {
|
|||
samplePosLastEdge = sample;
|
||||
block_done = 1;
|
||||
}
|
||||
|
||||
// 32TO
|
||||
|
||||
// 32TO
|
||||
} else if (ABS(lastClockDuration - _32T0) < TOLERANCE) {
|
||||
// if the clock before also was 16T0, it is a PMC!
|
||||
if (ABS(beforeLastClockDuration - _16T0) < TOLERANCE) {
|
||||
// It's a PMC !
|
||||
Dbprintf(_GREEN_("PMC 32T0 FOUND:") " bitPos: %d, sample: %d", bitPos, sample);
|
||||
|
||||
|
||||
sample += PMC_32T0_LEN; // move to the sample after PMC
|
||||
|
||||
expectedNextEdge = FALLING;
|
||||
samplePosLastEdge = sample;
|
||||
block_done = 1;
|
||||
|
||||
// if no pmc, then its a normal bit.
|
||||
// Check if its the second time, the edge changed if yes, then the bit is 0
|
||||
// if no pmc, then its a normal bit.
|
||||
// Check if its the second time, the edge changed if yes, then the bit is 0
|
||||
} else if (half_switch == 1) {
|
||||
bits[bitPos] = 0;
|
||||
// reset the edge counter to 0
|
||||
half_switch = 0;
|
||||
bitPos++;
|
||||
|
||||
// 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.
|
||||
// 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.
|
||||
} else
|
||||
half_switch++;
|
||||
|
||||
// 64T0
|
||||
// 64T0
|
||||
} else if (ABS(lastClockDuration - _64T0) < TOLERANCE) {
|
||||
// this means, bit here is 1
|
||||
bits[bitPos] = 1;
|
||||
bitPos++;
|
||||
|
||||
// Error
|
||||
|
||||
// Error
|
||||
} else {
|
||||
// some Error. maybe check tolerances.
|
||||
// some Error. maybe check tolerances.
|
||||
// likeley to happen in the first block.
|
||||
|
||||
|
||||
// In an Ideal world, this can be enabled. However, if only bad antenna field, this print will flood the output
|
||||
// and one might miss some "good" frames.
|
||||
//Dbprintf(_RED_("ERROR in demodulation") " Length last clock: %d - check threshold/tolerance/signal. Toss block", lastClockDuration*DECIMATION);
|
||||
|
@ -168,7 +168,7 @@ size_t DemodPCF7931(uint8_t **outBlocks, bool ledcontrol) {
|
|||
|
||||
if (block_done == 1) {
|
||||
// Dbprintf(_YELLOW_("Block Done") " bitPos: %d, sample: %d", bitPos, sample);
|
||||
|
||||
|
||||
// check if it is a complete block. If bitpos <128, it means that we did not receive
|
||||
// a complete block. E.g. at the first start of a transmission.
|
||||
// only save if a complete block is being received.
|
||||
|
@ -187,14 +187,14 @@ size_t DemodPCF7931(uint8_t **outBlocks, bool ledcontrol) {
|
|||
}
|
||||
num_blocks++;
|
||||
}
|
||||
// now start over for the next block / first complete block.
|
||||
// now start over for the next block / first complete block.
|
||||
bitPos = 0;
|
||||
block_done = 0;
|
||||
half_switch = 0;
|
||||
}
|
||||
|
||||
}else {
|
||||
// Dbprintf("%d, %d", sample, dest[sample]);
|
||||
} else {
|
||||
// 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...
|
||||
|
@ -204,8 +204,8 @@ size_t DemodPCF7931(uint8_t **outBlocks, bool ledcontrol) {
|
|||
bitPos = 0;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
memcpy(outBlocks, blocks, 16 * num_blocks);
|
||||
return num_blocks;
|
||||
}
|
||||
|
@ -251,10 +251,10 @@ bool IsBlock1PCF7931(const uint8_t *block) {
|
|||
}
|
||||
|
||||
void ReadPCF7931(bool ledcontrol) {
|
||||
|
||||
|
||||
uint8_t maxBlocks = 8; // readable blocks
|
||||
int found_blocks = 0; // successfully read blocks
|
||||
|
||||
|
||||
// TODO: Why 17 byte len? 16 should be good.
|
||||
uint8_t memory_blocks[maxBlocks][17]; // PCF content
|
||||
uint8_t single_blocks[maxBlocks][17]; // PFC blocks with unknown position
|
||||
|
@ -263,7 +263,7 @@ void ReadPCF7931(bool ledcontrol) {
|
|||
int single_blocks_cnt = 0;
|
||||
|
||||
size_t n; // transmitted blocks
|
||||
|
||||
|
||||
//uint8_t found_0_1 = 0; // flag: blocks 0 and 1 were found
|
||||
int errors = 0; // error counter
|
||||
int tries = 0; // tries counter
|
||||
|
@ -300,7 +300,7 @@ void ReadPCF7931(bool ledcontrol) {
|
|||
goto end;
|
||||
}
|
||||
|
||||
// This part was not working properly.
|
||||
// This part was not working properly.
|
||||
// So currently the blocks are not being sorted, but at least printed.
|
||||
|
||||
// // our logic breaks if we don't get at least two blocks
|
||||
|
@ -403,28 +403,28 @@ void ReadPCF7931(bool ledcontrol) {
|
|||
|
||||
|
||||
end:
|
||||
/*
|
||||
Dbprintf("-----------------------------------------");
|
||||
Dbprintf("Memory content:");
|
||||
Dbprintf("-----------------------------------------");
|
||||
for (i = 0; i < maxBlocks; ++i) {
|
||||
if (memory_blocks[i][ALLOC])
|
||||
print_result("Block", memory_blocks[i], 16);
|
||||
else
|
||||
Dbprintf("<missing block %d>", i);
|
||||
}
|
||||
Dbprintf("-----------------------------------------");
|
||||
/*
|
||||
Dbprintf("-----------------------------------------");
|
||||
Dbprintf("Memory content:");
|
||||
Dbprintf("-----------------------------------------");
|
||||
for (i = 0; i < maxBlocks; ++i) {
|
||||
if (memory_blocks[i][ALLOC])
|
||||
print_result("Block", memory_blocks[i], 16);
|
||||
else
|
||||
Dbprintf("<missing block %d>", i);
|
||||
}
|
||||
Dbprintf("-----------------------------------------");
|
||||
|
||||
if (found_blocks < maxBlocks) {
|
||||
Dbprintf("-----------------------------------------");
|
||||
Dbprintf("Blocks with unknown position:");
|
||||
Dbprintf("-----------------------------------------");
|
||||
for (i = 0; i < single_blocks_cnt; ++i)
|
||||
print_result("Block", single_blocks[i], 16);
|
||||
if (found_blocks < maxBlocks) {
|
||||
Dbprintf("-----------------------------------------");
|
||||
Dbprintf("Blocks with unknown position:");
|
||||
Dbprintf("-----------------------------------------");
|
||||
for (i = 0; i < single_blocks_cnt; ++i)
|
||||
print_result("Block", single_blocks[i], 16);
|
||||
|
||||
Dbprintf("-----------------------------------------");
|
||||
}
|
||||
*/
|
||||
Dbprintf("-----------------------------------------");
|
||||
}
|
||||
*/
|
||||
|
||||
reply_mix(CMD_ACK, 0, 0, 0, 0, 0);
|
||||
}
|
||||
|
@ -434,7 +434,7 @@ static void RealWritePCF7931(
|
|||
uint16_t init_delay,
|
||||
int8_t offsetPulseWidth, int8_t offsetPulsePosition,
|
||||
uint8_t address, uint8_t byte, uint8_t data,
|
||||
bool ledcontrol){
|
||||
bool ledcontrol) {
|
||||
|
||||
uint32_t tab[1024] = {0}; // data times frame
|
||||
uint32_t u = 0;
|
||||
|
@ -512,10 +512,10 @@ static void RealWritePCF7931(
|
|||
* @param data : data to write
|
||||
*/
|
||||
void WritePCF7931(
|
||||
uint8_t pass1, uint8_t pass2, uint8_t pass3, uint8_t pass4, uint8_t pass5, uint8_t pass6, uint8_t pass7,
|
||||
uint16_t init_delay,
|
||||
int8_t offsetPulseWidth, int8_t offsetPulsePosition,
|
||||
uint8_t address, uint8_t byte, uint8_t data,
|
||||
uint8_t pass1, uint8_t pass2, uint8_t pass3, uint8_t pass4, uint8_t pass5, uint8_t pass6, uint8_t pass7,
|
||||
uint16_t init_delay,
|
||||
int8_t offsetPulseWidth, int8_t offsetPulsePosition,
|
||||
uint8_t address, uint8_t byte, uint8_t data,
|
||||
bool ledcontrol) {
|
||||
|
||||
if (g_dbglevel >= DBG_INFO) {
|
||||
|
@ -550,7 +550,7 @@ void SendCmdPCF7931(uint32_t *tab, bool ledcontrol) {
|
|||
FpgaWriteConfWord(FPGA_MAJOR_MODE_LF_PASSTHRU);
|
||||
|
||||
if (ledcontrol) LED_A_ON();
|
||||
|
||||
|
||||
// rescale the values to match the time of the timer below.
|
||||
for (u = 0; u < 500; ++u) {
|
||||
tab[u] = (tab[u] * 3) / 2;
|
||||
|
@ -651,7 +651,7 @@ bool AddBitPCF7931(bool b, uint32_t *tab, int8_t offsetPulseWidth, int8_t offset
|
|||
|
||||
tab[u + 1] = 6 * T0_PCF + tab[u] + offsetPulseWidth;
|
||||
tab[u + 2] = 88 * T0_PCF + tab[u + 1] - offsetPulseWidth - offsetPulsePosition;
|
||||
|
||||
|
||||
} else { //add a bit 0
|
||||
if (u == 0)
|
||||
tab[u] = 98 * T0_PCF + offsetPulsePosition;
|
||||
|
@ -660,7 +660,7 @@ bool AddBitPCF7931(bool b, uint32_t *tab, int8_t offsetPulseWidth, int8_t offset
|
|||
|
||||
tab[u + 1] = 6 * T0_PCF + tab[u] + offsetPulseWidth;
|
||||
tab[u + 2] = 24 * T0_PCF + tab[u + 1] - offsetPulseWidth - offsetPulsePosition;
|
||||
|
||||
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
|
|
@ -19,9 +19,9 @@
|
|||
#include "common.h"
|
||||
|
||||
|
||||
typedef enum{
|
||||
typedef enum {
|
||||
FALLING,
|
||||
RISING
|
||||
RISING
|
||||
} EdgeType;
|
||||
|
||||
size_t DemodPCF7931(uint8_t **outBlocks, bool ledcontrol);
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
|
||||
local getopt = require('getopt')
|
||||
local utils = require('utils')
|
||||
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 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'
|
||||
version = ' version v1.19'
|
||||
desc = [[
|
||||
The script automates the copying of Paxton fobs read - write.
|
||||
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)
|
||||
while true do
|
||||
print(" 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)
|
||||
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
|
||||
print(dash)
|
||||
print(" Place the" .. ac.cyan .. " Paxton " .. ac.reset .. "Fob on the coil to write.." .. ac.green .. " ENTER " .. ac.reset .. "to continue..")
|
||||
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")
|
||||
|
@ -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 7 -d " .. blocks[7] .. " -k BDF5E846")
|
||||
elseif choice == "2" then
|
||||
print(dash)
|
||||
print(" Place the" .. ac.cyan .. " T5577 " .. ac.reset .. "tag on the coil and press" .. ac.green .. " ENTER " .. ac.reset .. "to continue..")
|
||||
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)
|
||||
command("lf em 410x clone --id " .. padded_hex_id)
|
||||
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.." > "..ac.yellow)
|
||||
io.write(" Make next RFID Fob"..ac.cyan.." (y/n) "..ac.reset)
|
||||
local another = io.read()
|
||||
io.write(ac.reset..'')
|
||||
if another:lower() == "n" then
|
||||
if was_option_3 then
|
||||
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
|
||||
end
|
||||
command('clear')
|
||||
print()
|
||||
print(dash)
|
||||
print(ac.green .. ' Select option: ' .. ac.reset)
|
||||
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
|
||||
while true do
|
||||
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..')
|
||||
print(dash)
|
||||
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()
|
||||
command('lf hitag read --ht2 -k BDF5E846')
|
||||
command('clear')
|
||||
print(dash)
|
||||
p:console('lf hitag read --ht2 -k BDF5E846')
|
||||
if not logfile then
|
||||
error(" No files in this directory")
|
||||
end
|
||||
|
@ -343,12 +338,11 @@ local function main(args)
|
|||
end
|
||||
end
|
||||
if empty_block then
|
||||
print(dash)
|
||||
print(ac.yellow .. ' Adjust the Fob position on the coil.' .. ac.reset .. ' Press' .. ac.green .. ' ENTER' .. ac.reset .. ' to continue..')
|
||||
print(dash)
|
||||
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(dash)
|
||||
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))
|
||||
|
@ -364,7 +358,6 @@ local function main(args)
|
|||
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)
|
||||
handle_cloning(decimal_id, padded_hex_id, blocks, was_option_3)
|
||||
|
|
|
@ -11,7 +11,7 @@ local command = core.console
|
|||
command('clear')
|
||||
|
||||
author = ' Author: jareckib - 15.02.2025'
|
||||
version = ' version v1.01'
|
||||
version = ' version v1.02'
|
||||
desc = [[
|
||||
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
|
||||
|
@ -102,7 +102,7 @@ local function reanimate_t5577(password)
|
|||
p:console('lf t55 write -b 0 -d 000880E0 --pg1 --r0 -p 00000000')
|
||||
p:console('lf t55 write -b 0 -d 000880E0 --pg1 --r1 -p 00000000')
|
||||
p:console('lf t55 write -b 0 -d 000880E0 --pg1 --r2 -p 00000000')
|
||||
p:console('lf t55 write -b 0 -d 000880E0 --pg1 --r3 -p 00000000')
|
||||
p:console('lf t55 write -b 0 -d 000880E0 --pg1 --r3 -p 00000000')
|
||||
reset_log_file()
|
||||
end
|
||||
|
||||
|
@ -111,20 +111,23 @@ local function main(args)
|
|||
if o == 'h' then return help() end
|
||||
end
|
||||
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(dash)
|
||||
p:console('lf t55 chk')
|
||||
timer(5)
|
||||
local log_content = read_log_file(logfile)
|
||||
local password = log_content and extract_password(log_content) or nil
|
||||
reanimate_t5577(password)
|
||||
p:console('lf t55 detect')
|
||||
p:console('lf t55 detect')
|
||||
p:console('lf t55 read -b 0')
|
||||
timer(5)
|
||||
local success = false
|
||||
for line in p.grabbed_output:gmatch("[^\r\n]+") do
|
||||
if line:find("000880E0") then
|
||||
if line:find("00 | 000880E0 |") then
|
||||
success = true
|
||||
break
|
||||
end
|
||||
|
|
|
@ -9,14 +9,14 @@ local command = core.console
|
|||
command('clear')
|
||||
author = ' Author: jareckib - 12.03.2025'
|
||||
version = ' version v1.03'
|
||||
desc = [[
|
||||
desc = [[
|
||||
This simple script stores 1, 2 or 3 different EM4102 on a single T5577.
|
||||
There is an option to enter the number engraved on the fob in decimal form.
|
||||
The script can therefore be useful if the original EM4102 doesn't work but
|
||||
has an engraved ID number. By entering such an ID as a single EM4102, we
|
||||
The script can therefore be useful if the original EM4102 doesn't work but
|
||||
has an engraved ID number. By entering such an ID as a single EM4102, we
|
||||
can create a working copy of our damaged fob.
|
||||
A tag T5577 created in this way works with the following USB readers:
|
||||
|
||||
|
||||
- ACM08Y
|
||||
- ACM26C
|
||||
- Sycreader R60D
|
||||
|
@ -121,7 +121,7 @@ local function get_uid_from_user()
|
|||
while true do
|
||||
print(dash)
|
||||
io.write(ac.cyan .. '(1)' .. ac.reset .. ' Manual entry UID |' .. ac.cyan .. ' (2)' .. ac.reset .. ' Read via Proxmark3 ')
|
||||
|
||||
|
||||
local choice
|
||||
repeat
|
||||
choice = io.read()
|
||||
|
@ -159,7 +159,7 @@ local function get_uid_from_user()
|
|||
io.read()
|
||||
|
||||
while true do
|
||||
reset_log_file()
|
||||
reset_log_file()
|
||||
command('lf em 410x read')
|
||||
local log_content = read_log_file(logfile)
|
||||
local uid = extract_uid(log_content)
|
||||
|
@ -178,7 +178,7 @@ end
|
|||
local function main(args)
|
||||
for o, a in getopt.getopt(args, 'h') do
|
||||
if o == 'h' then return help() end
|
||||
end
|
||||
end
|
||||
local blocks = {}
|
||||
local uid_count = 0
|
||||
|
||||
|
@ -224,4 +224,4 @@ local function main(args)
|
|||
print(ac.green .. "Successfully written " .. uid_count .. " EM4102 UID(s) to T5577" .. ac.reset)
|
||||
end
|
||||
|
||||
main(args)
|
||||
main(args)
|
||||
|
|
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)
|
|
@ -77,14 +77,14 @@ def parse_config(config: str):
|
|||
cfg_auth_type = config[8:10]
|
||||
cfg_cuid = config[12:14]
|
||||
cfg_memory_config = config[14:16]
|
||||
|
||||
|
||||
log_magic_wup = "Magic wakeup " + ("en" if cfg_magic_wup != "8500" else "dis") + "abled" + (" with config access" if cfg_magic_wup == "7AFF" else "")
|
||||
log_wup_style = "Magic wakeup style " + ("Gen1a 40(7)/43" if cfg_wup_style == "00" else ("GDM 20(7)/23" if cfg_wup_style == "85" else "unknown"))
|
||||
log_regular_available = "Config " + ("" if cfg_regular_available == "A0" else "un") + "available in regular mode"
|
||||
log_auth_type = "Auth type " + ("1B - PWD" if cfg_auth_type == "00" else "1A - 3DES")
|
||||
log_cuid = "CUID " + ("dis" if cfg_cuid == "A0" else "en") + "abled"
|
||||
log_memory_config = "Maximum memory configuration: " + (MEMORY_CONFIG[cfg_memory_config] if cfg_memory_config in MEMORY_CONFIG.keys() else "unknown")
|
||||
|
||||
|
||||
print(SUCCESS + "^^^^............................ " + log_magic_wup)
|
||||
print(SUCCESS + "....^^.......................... " + log_wup_style)
|
||||
print(SUCCESS + "......^^........................ " + log_regular_available)
|
||||
|
@ -93,7 +93,7 @@ def parse_config(config: str):
|
|||
print(SUCCESS + "............^^.................. " + log_cuid)
|
||||
print(SUCCESS + "..............^^................ " + log_memory_config)
|
||||
print(SUCCESS + "................^^^^^^^^^^^^^^^^ version info")
|
||||
|
||||
|
||||
def try_auth_magic(enforced = False):
|
||||
if enforced and not (gen1a | alt):
|
||||
print(ERROR + "Magic wakeup required. Please select one.")
|
||||
|
@ -158,7 +158,7 @@ elif write_backdoor != None:
|
|||
if len(data) % 8 != 0:
|
||||
print(ERROR + "Data must be a multiple of 4 bytes.")
|
||||
exit()
|
||||
|
||||
|
||||
try_auth_magic(True)
|
||||
for i in range(len(data) // 8):
|
||||
p.console("hf 14a raw -" + ("k" if i != (len(data) // 8 - 1) else "") + f"c A2{(write_backdoor_num + i):02x}{data[8*i:8*i+8]}", False, False)
|
||||
|
@ -171,13 +171,13 @@ elif uid != None:
|
|||
p.console(f"hf 14a raw -kc" + ("s" if not (gen1a or alt) else "") + " 3002")
|
||||
block_2 = p.grabbed_output.split("\n")[-2][4:-9].replace(" ", "")[:8]
|
||||
uid_bytes = [int(uid[2*x:2*x+2], 16) for x in range(7)]
|
||||
|
||||
|
||||
bcc_0 = 0x88 ^ uid_bytes[0] ^ uid_bytes[1] ^ uid_bytes[2]
|
||||
new_block_0 = ""
|
||||
for i in range(3):
|
||||
new_block_0 += f"{uid_bytes[i]:02x}"
|
||||
new_block_0 += f"{bcc_0:02x}"
|
||||
|
||||
|
||||
bcc_1 = uid_bytes[3] ^ uid_bytes[4] ^ uid_bytes[5] ^ uid_bytes[6]
|
||||
new_block_1 = uid[6:]
|
||||
new_block_2 = f"{bcc_1:02x}" + block_2[2:]
|
||||
|
|
|
@ -688,11 +688,11 @@ static int CmdEM410xClone(const char *Cmd) {
|
|||
uint8_t r_parity = 0;
|
||||
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++) {
|
||||
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;
|
||||
}
|
||||
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
|
||||
// common header field that includes the command itself (to improve debugging / validation).
|
||||
|
||||
typedef struct _em4x70_tag_info_t {
|
||||
/// <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₅₆
|
||||
/// [30] == Block 15 LSB == UM2₅₅..UM2₄₈
|
||||
/// [29] == Block 14 MSB == UM2₄₇..UM2₄₀
|
||||
|
@ -168,6 +174,7 @@ 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;
|
||||
|
|
|
@ -832,7 +832,7 @@ static bool ht2_get_uid(uint32_t *uid) {
|
|||
}
|
||||
|
||||
if (resp.status != PM3_SUCCESS) {
|
||||
PrintAndLogEx(DEBUG, "DEBUG: Error - failed getting UID");
|
||||
PrintAndLogEx(DEBUG, "DEBUG: Error - failed getting Hitag 2 UID");
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
|
@ -186,6 +186,7 @@ static void fill_grabber(const char *string) {
|
|||
g_grabbed_output.ptr = tmp;
|
||||
g_grabbed_output.size += MAX_PRINT_BUFFER;
|
||||
}
|
||||
|
||||
int len = snprintf(g_grabbed_output.ptr + g_grabbed_output.idx, MAX_PRINT_BUFFER, "%s", string);
|
||||
if (len < 0 || len > MAX_PRINT_BUFFER) {
|
||||
// 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) {
|
||||
|
||||
char buff[2000] = "Options:\n";
|
||||
char format[2000] = "";
|
||||
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])) {
|
||||
counts[j] = strlen(str[i][j]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < size; i++) {
|
||||
|
||||
for (size_t j = 0; j < 2; j++) {
|
||||
if (j == 0)
|
||||
if (j == 0) {
|
||||
snprintf(format, sizeof(format), "%%%zus%%%zus", space, counts[j]);
|
||||
else
|
||||
} else {
|
||||
snprintf(format, sizeof(format), "%%%zus%%-%zus", space, counts[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);
|
||||
}
|
||||
}
|
||||
PrintAndLogEx(NORMAL, "%s", buff);
|
||||
}
|
||||
|
@ -223,12 +234,14 @@ static uint8_t PrintAndLogEx_spinidx = 0;
|
|||
void PrintAndLogEx(logLevel_t level, const char *fmt, ...) {
|
||||
|
||||
// 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;
|
||||
}
|
||||
|
||||
// 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;
|
||||
}
|
||||
|
||||
char prefix[40] = {0};
|
||||
char buffer[MAX_PRINT_BUFFER] = {0};
|
||||
|
@ -242,17 +255,19 @@ void PrintAndLogEx(logLevel_t level, const char *fmt, ...) {
|
|||
};
|
||||
switch (level) {
|
||||
case ERR:
|
||||
if (g_session.emoji_mode == EMO_EMOJI)
|
||||
if (g_session.emoji_mode == EMO_EMOJI) {
|
||||
strncpy(prefix, "[" _RED_("!!") "] :rotating_light: ", sizeof(prefix) - 1);
|
||||
else
|
||||
} else {
|
||||
strncpy(prefix, "[" _RED_("!!") "] ", sizeof(prefix) - 1);
|
||||
}
|
||||
stream = stderr;
|
||||
break;
|
||||
case FAILED:
|
||||
if (g_session.emoji_mode == EMO_EMOJI)
|
||||
if (g_session.emoji_mode == EMO_EMOJI) {
|
||||
strncpy(prefix, "[" _RED_("-") "] :no_entry: ", sizeof(prefix) - 1);
|
||||
else
|
||||
} else {
|
||||
strncpy(prefix, "[" _RED_("-") "] ", sizeof(prefix) - 1);
|
||||
}
|
||||
break;
|
||||
case DEBUG:
|
||||
strncpy(prefix, "[" _BLUE_("#") "] ", sizeof(prefix) - 1);
|
||||
|
@ -264,10 +279,11 @@ void PrintAndLogEx(logLevel_t level, const char *fmt, ...) {
|
|||
strncpy(prefix, "[" _GREEN_("+") "] ", sizeof(prefix) - 1);
|
||||
break;
|
||||
case WARNING:
|
||||
if (g_session.emoji_mode == EMO_EMOJI)
|
||||
if (g_session.emoji_mode == EMO_EMOJI) {
|
||||
strncpy(prefix, "[" _CYAN_("!") "] :warning: ", sizeof(prefix) - 1);
|
||||
else
|
||||
} else {
|
||||
strncpy(prefix, "[" _CYAN_("!") "] ", sizeof(prefix) - 1);
|
||||
}
|
||||
break;
|
||||
case INFO:
|
||||
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) {
|
||||
strncpy(prefix, spinner_emoji[PrintAndLogEx_spinidx], sizeof(prefix) - 1);
|
||||
PrintAndLogEx_spinidx++;
|
||||
if (PrintAndLogEx_spinidx >= ARRAYLEN(spinner_emoji))
|
||||
if (PrintAndLogEx_spinidx >= ARRAYLEN(spinner_emoji)) {
|
||||
PrintAndLogEx_spinidx = 0;
|
||||
}
|
||||
} else {
|
||||
strncpy(prefix, spinner[PrintAndLogEx_spinidx], sizeof(prefix) - 1);
|
||||
PrintAndLogEx_spinidx++;
|
||||
if (PrintAndLogEx_spinidx >= ARRAYLEN(spinner))
|
||||
if (PrintAndLogEx_spinidx >= ARRAYLEN(spinner)) {
|
||||
PrintAndLogEx_spinidx = 0;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case NORMAL:
|
||||
|
@ -306,8 +324,9 @@ void PrintAndLogEx(logLevel_t level, const char *fmt, ...) {
|
|||
const char delim[2] = "\n";
|
||||
|
||||
// line starts with newline
|
||||
if (buffer[0] == '\n')
|
||||
if (buffer[0] == '\n') {
|
||||
fPrintAndLog(stream, "");
|
||||
}
|
||||
|
||||
token = strtok_r(buffer, delim, &tmp_ptr);
|
||||
|
||||
|
@ -315,16 +334,21 @@ void PrintAndLogEx(logLevel_t level, const char *fmt, ...) {
|
|||
|
||||
size_t size = strlen(buffer2);
|
||||
|
||||
if (strlen(token))
|
||||
if (strlen(token)) {
|
||||
snprintf(buffer2 + size, sizeof(buffer2) - size, "%s%s\n", prefix, token);
|
||||
else
|
||||
} else {
|
||||
snprintf(buffer2 + size, sizeof(buffer2) - size, "\n");
|
||||
}
|
||||
|
||||
token = strtok_r(NULL, delim, &tmp_ptr);
|
||||
}
|
||||
|
||||
fPrintAndLog(stream, "%s", buffer2);
|
||||
|
||||
} else {
|
||||
|
||||
snprintf(buffer2, sizeof(buffer2), "%s%s", prefix, buffer);
|
||||
|
||||
if (level == INPLACE) {
|
||||
// ignore INPLACE if rest of output is grabbed
|
||||
if (!(g_printAndLog & PRINTANDLOG_GRAB)) {
|
||||
|
@ -354,6 +378,7 @@ static void fPrintAndLog(FILE *stream, const char *fmt, ...) {
|
|||
if (logging && g_session.incognito) {
|
||||
logging = 0;
|
||||
}
|
||||
|
||||
if ((g_printAndLog & PRINTANDLOG_LOG) && logging && !logfile) {
|
||||
char *my_logfile_path = NULL;
|
||||
char filename[40];
|
||||
|
@ -361,11 +386,15 @@ static void fPrintAndLog(FILE *stream, const char *fmt, ...) {
|
|||
time_t now = time(NULL);
|
||||
timenow = gmtime(&now);
|
||||
strftime(filename, sizeof(filename), PROXLOG, timenow);
|
||||
|
||||
if (searchHomeFilePath(&my_logfile_path, LOGS_SUBDIR, filename, true) != PM3_SUCCESS) {
|
||||
|
||||
printf(_YELLOW_("[-]") " Logging disabled!\n");
|
||||
my_logfile_path = NULL;
|
||||
logging = 0;
|
||||
|
||||
} else {
|
||||
|
||||
logfile = fopen(my_logfile_path, "a");
|
||||
if (logfile == NULL) {
|
||||
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;
|
||||
buffer[strlen(buffer) - 1] = 0;
|
||||
}
|
||||
|
||||
bool filter_ansi = !g_session.supports_colors;
|
||||
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);
|
||||
fprintf(stream, "%s", buffer3);
|
||||
if (linefeed)
|
||||
if (linefeed) {
|
||||
fprintf(stream, "\n");
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef RL_STATE_READCMD
|
||||
|
@ -433,33 +465,44 @@ static void fPrintAndLog(FILE *stream, const char *fmt, ...) {
|
|||
|
||||
if (((g_printAndLog & PRINTANDLOG_LOG) && logging && logfile) ||
|
||||
(g_printAndLog & PRINTANDLOG_GRAB)) {
|
||||
|
||||
memcpy_filter_emoji(buffer3, buffer2, sizeof(buffer2), EMO_ALTTEXT);
|
||||
|
||||
if (filter_ansi == false) {
|
||||
memcpy_filter_ansi(buffer, buffer3, sizeof(buffer3), true);
|
||||
}
|
||||
}
|
||||
|
||||
if ((g_printAndLog & PRINTANDLOG_LOG) && logging && logfile) {
|
||||
|
||||
if (filter_ansi) {
|
||||
fprintf(logfile, "%s", buffer3);
|
||||
} else {
|
||||
fprintf(logfile, "%s", buffer);
|
||||
}
|
||||
if (linefeed)
|
||||
|
||||
if (linefeed) {
|
||||
fprintf(logfile, "\n");
|
||||
}
|
||||
fflush(logfile);
|
||||
}
|
||||
|
||||
if (g_printAndLog & PRINTANDLOG_GRAB) {
|
||||
|
||||
if (filter_ansi) {
|
||||
fill_grabber(buffer3);
|
||||
} else {
|
||||
fill_grabber(buffer);
|
||||
}
|
||||
if (linefeed)
|
||||
|
||||
if (linefeed) {
|
||||
fill_grabber("\n");
|
||||
}
|
||||
}
|
||||
|
||||
if (flushAfterWrite)
|
||||
if (flushAfterWrite) {
|
||||
fflush(stdout);
|
||||
}
|
||||
|
||||
//release 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;
|
||||
uint16_t si = 0;
|
||||
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
|
||||
continue;
|
||||
}
|
||||
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.
|
||||
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;
|
||||
|
||||
// overlap
|
||||
if ((src - dest) * 8 + src_offset - dest_offset > 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) {
|
||||
// equiv of dest_bits[dest_offset + i] = src_bits[src_offset + i]
|
||||
CLEAR_BIT(dest, dest_offset + i);
|
||||
if (TEST_BIT(src, src_offset + i)) SET_BIT(dest, dest_offset + i);
|
||||
CLEAR_BIT_MSB(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;
|
||||
|
|
|
@ -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_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 hexstr2ByteArr(const char *hexstr, unsigned char *array, size_t asize);
|
||||
#endif
|
||||
|
|
|
@ -5047,7 +5047,7 @@
|
|||
"-v, --verbose verbose output",
|
||||
"-f, --file <fn> Specify a filename for dump file",
|
||||
"--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)"
|
||||
],
|
||||
"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",
|
||||
"-p, --pwd <hex> Password, 7bytes, LSB-order",
|
||||
"-d, --delay <dec> Tag initialization delay (in us)",
|
||||
"--lw <dec> offset, low pulses width (in us)",
|
||||
"--lp <dec> offset, low pulses position (in us)"
|
||||
"--lw <dec> offset, low pulses width (in us), optional!",
|
||||
"--lp <dec> offset, low pulses position (in us), optional!"
|
||||
],
|
||||
"usage": "lf pcf7931 config [-hr] [-p <hex>] [-d <dec>] [--lw <dec>] [--lp <dec>]"
|
||||
},
|
||||
|
@ -13232,6 +13232,6 @@
|
|||
"metadata": {
|
||||
"commands_extracted": 760,
|
||||
"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
|
||||
|
||||
// bit stream operations
|
||||
#define TEST_BIT(data, i) (*((data) + ((i) / 8)) >> (7 - ((i) % 8))) & 1
|
||||
#define SET_BIT(data, i) *((data) + ((i) / 8)) |= (1 << (7 - ((i) % 8)))
|
||||
#define CLEAR_BIT(data, i) *((data) + ((i) / 8)) &= ~(1 << (7 - ((i) % 8)))
|
||||
#define FLIP_BIT(data, i) *((data) + ((i) / 8)) ^= (1 << (7 - ((i) % 8)))
|
||||
#define TEST_BIT_MSB(data, i) ((*((data) + ((i) / 8)) >> (7 - ((i) % 8))) & 1)
|
||||
#define SET_BIT_MSB(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_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
|
||||
#define FPGA_LOAD_WAIT_TIME (1500)
|
||||
|
|
|
@ -21,6 +21,7 @@
|
|||
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include <assert.h>
|
||||
|
||||
#define EM4X70_NUM_BLOCKS 16
|
||||
|
||||
|
@ -28,24 +29,36 @@
|
|||
#define EM4X70_PIN_WORD_LOWER 10
|
||||
#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 {
|
||||
// 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;
|
||||
|
||||
// Used for writing address
|
||||
uint8_t address;
|
||||
// ISSUE: Presumes target is little-endian
|
||||
// BUGBUG: Non-portable ... presumes stored in little-endian form!
|
||||
uint16_t word;
|
||||
|
||||
// PIN to unlock
|
||||
// BUGBUG: Non-portable ... presumes stored in little-endian form!
|
||||
uint32_t pin;
|
||||
|
||||
// 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 grnd[3];
|
||||
uint8_t rnd[7];
|
||||
|
@ -54,9 +67,20 @@ typedef struct {
|
|||
uint8_t crypt_key[12];
|
||||
|
||||
// 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;
|
||||
|
||||
} 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__ */
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue