mirror of
https://github.com/RfidResearchGroup/proxmark3.git
synced 2026-09-02 06:53:46 +00:00
Bug triggered with: lf read -s 50000 hw ping ReadLF_realtime() in lfsampling.c never knew how many samples the client wanted — for the realtime path, lf_sample_payload_t.samples was never populated on the client side, and the ARM handler in appmain.c (CMD_LF_ACQ_RAW_ADC/CMD_LF_SNIFF_RAW_ADC) ignored it anyway. So the device just streamed samples indefinitely, only checking for a stop request roughly once per 64-sample USB burst, and even after noticing it, flushed one more trailing partial burst via async_usb_write_stop(). Meanwhile the client (WaitForRawDataTimeout in comms.c) stops listening in raw mode the instant it has collected exactly the number of bytes it asked for (e.g. 50000), then switches back to normal framed-packet parsing. Since the device kept sending more bytes past that point, those extra bytes sat unread in the pipe and got misinterpreted as a PacketResponseNGPreamble/OLD-frame header, corrupting every subsequent USB exchange (hw ping, etc.) — reproducible even with a plain lf read -s 50000, no trigger/COTAG needed. Moreover, we make sure the client doesn't stop listening, even briefly, during stream reception.
2452 lines
85 KiB
C
2452 lines
85 KiB
C
// //-----------------------------------------------------------------------------
|
|
// Copyright (C) Proxmark3 contributors. See AUTHORS.md for details.
|
|
//
|
|
// This program is free software: you can redistribute it and/or modify
|
|
// it under the terms of the GNU General Public License as published by
|
|
// the Free Software Foundation, either version 3 of the License, or
|
|
// (at your option) any later version.
|
|
//
|
|
// This program is distributed in the hope that it will be useful,
|
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
// GNU General Public License for more details.
|
|
//
|
|
// See LICENSE.txt for the text of the license.
|
|
//-----------------------------------------------------------------------------
|
|
// Low frequency commands
|
|
//-----------------------------------------------------------------------------
|
|
#include "cmdlf.h"
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <limits.h>
|
|
#include <ctype.h>
|
|
#include <math.h>
|
|
#include <unistd.h>
|
|
|
|
#include "relay/relay.h"
|
|
|
|
#include "cmdparser.h" // command_t
|
|
#include "comms.h"
|
|
#include "util.h" // set_rgb
|
|
#include "commonutil.h" // ARRAYLEN
|
|
#include "lfdemod.h" // device/client demods of LF signals
|
|
#include "ui.h" // for show graph controls
|
|
#include "proxgui.h"
|
|
#include "cliparser.h" // args parsing
|
|
#include "graph.h" // for graph data
|
|
#include "cmddata.h" // for `lf search`
|
|
#include "cmdhw.h" // for setting FPGA image
|
|
#include "cmdlfawid.h" // for awid menu
|
|
#include "cmdlfem.h" // for em menu
|
|
#include "cmdlfem410x.h" // for em4x menu
|
|
#include "cmdlfem4x05.h" // for em4x05 / 4x69
|
|
#include "cmdlfem4x50.h" // for em4x50
|
|
#include "cmdlfem4x70.h" // for em4x70
|
|
#include "cmdlfhid.h" // for hid menu
|
|
#include "cmdlfhitag.h" // for hitag menu
|
|
#include "cmdlfhitaghts.h" // for hitag S sub commands
|
|
#include "cmdlfhitagu.h" // for hitag µ sub commands
|
|
#include "cmdlfidteck.h" // for idteck menu
|
|
#include "cmdlfio.h" // for ioprox menu
|
|
#include "cmdlfcotag.h" // for COTAG menu
|
|
#include "pm3_dsp.h" // pm3_extract, pm3_is_switched_carrier
|
|
#include "cmdlfdestron.h" // for FDX-A FECAVA Destron menu
|
|
#include "cmdlffdxb.h" // for FDX-B menu
|
|
#include "cmdlfgallagher.h" // for GALLAGHER menu
|
|
#include "cmdlfguard.h" // for gproxii menu
|
|
#include "cmdlfindala.h" // for indala menu
|
|
#include "cmdlfjablotron.h" // for JABLOTRON menu
|
|
#include "cmdlfkeri.h" // for keri menu
|
|
#include "cmdlfmotorola.h" // for Motorola menu
|
|
#include "cmdlfnedap.h" // for NEDAP menu
|
|
#include "cmdlfnexwatch.h" // for nexwatch menu
|
|
#include "cmdlfnoralsy.h" // for NORALSY menu
|
|
#include "cmdlfpac.h" // for pac menu
|
|
#include "cmdlfparadox.h" // for paradox menu
|
|
#include "cmdlfpcf7931.h" // for pcf7931 menu
|
|
#include "cmdlfpresco.h" // for presco menu
|
|
#include "cmdlfpyramid.h" // for pyramid menu
|
|
#include "cmdlfsecurakey.h" // for securakey menu
|
|
#include "cmdlft55xx.h" // for t55xx menu
|
|
#include "cmdlfti.h" // for ti menu
|
|
#include "cmdlftrovan.h" // for trovan menu
|
|
#include "cmdlfviking.h" // for viking menu
|
|
#include "cmdlfvisa2000.h" // for VISA2000 menu
|
|
#include "cmdlfzx8211.h" // for ZX8211 menu
|
|
#include "crc.h"
|
|
#include "pm3_cmd.h" // for LF_CMDREAD_MAX_EXTRA_SYMBOLS
|
|
#include "fpga.h" // for set_fpga_mode
|
|
#include "util_posix.h" // msleep
|
|
|
|
|
|
static int CmdHelp(const char *Cmd);
|
|
|
|
// Informative user function.
|
|
// loop and wait for either keyboard press or pm3 button to exit
|
|
// if key event, send break loop cmd to Pm3
|
|
int lfsim_wait_check(uint32_t cmd) {
|
|
PrintAndLogEx(NORMAL, "");
|
|
PrintAndLogEx(INFO, "Press " _GREEN_("pm3 button") " or " _GREEN_("<Enter>") " to abort simulation");
|
|
|
|
for (;;) {
|
|
if (kbd_enter_pressed()) {
|
|
SendCommandNG(CMD_BREAK_LOOP, NULL, 0);
|
|
PrintAndLogEx(DEBUG, "\naborted via keyboard!");
|
|
break;
|
|
}
|
|
|
|
PacketResponseNG resp;
|
|
if (WaitForResponseTimeout(cmd, &resp, 1000)) {
|
|
if (resp.status == PM3_EOPABORTED) {
|
|
PrintAndLogEx(DEBUG, "Button pressed, user aborted");
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
PrintAndLogEx(INFO, "Done!");
|
|
return PM3_SUCCESS;
|
|
}
|
|
|
|
// Mirror an antenna tuning level on the PM5 antenna RGB LED. `volt` is scaled
|
|
// relative to the running peak (`v_max`), so the colour tracks the on-screen bar:
|
|
// blue = low, green = mid, red = high.
|
|
static void tune_rgb_update(uint32_t volt, uint32_t v_max) {
|
|
uint32_t t = (v_max > 0) ? (volt * 510 / v_max) : 0;
|
|
if (t > 510) {
|
|
t = 510;
|
|
}
|
|
if (t < 255) { // blue -> green
|
|
set_rgb(0, (uint8_t)t, (uint8_t)(255 - t));
|
|
} else { // green -> red
|
|
t -= 255;
|
|
set_rgb((uint8_t)t, (uint8_t)(255 - t), 0);
|
|
}
|
|
}
|
|
|
|
static int CmdLFTune(const char *Cmd) {
|
|
|
|
CLIParserContext *ctx;
|
|
CLIParserInit(&ctx, "lf tune",
|
|
"Continuously measure LF antenna tuning.\n"
|
|
"Press button or <Enter> to interrupt.",
|
|
"lf tune\n"
|
|
"lf tune --mix"
|
|
);
|
|
|
|
char q_str[60];
|
|
snprintf(q_str, sizeof(q_str), "Frequency divisor. %d -> 134 kHz, %d -> 125 kHz", LF_DIVISOR_134, LF_DIVISOR_125);
|
|
void *argtable[] = {
|
|
arg_param_begin,
|
|
arg_u64_0("n", "iter", "<dec>", "number of iterations (default: 0=infinite)"),
|
|
arg_u64_0("q", "divisor", "<dec>", q_str),
|
|
arg_dbl0("f", "freq", "<float>", "Frequency in kHz"),
|
|
arg_lit0(NULL, "bar", "bar style"),
|
|
arg_lit0(NULL, "mix", "mixed style"),
|
|
arg_lit0(NULL, "value", "values style"),
|
|
arg_lit0("v", "verbose", "verbose output"),
|
|
arg_lit0(NULL, "rgb", "(PM5) mirror the tuning level on the antenna RGB LED"),
|
|
arg_param_end
|
|
};
|
|
CLIExecWithReturn(ctx, Cmd, argtable, true);
|
|
uint32_t iter = arg_get_u32_def(ctx, 1, 0);
|
|
uint8_t divisor = arg_get_u32_def(ctx, 2, LF_DIVISOR_125);
|
|
double freq = arg_get_dbl_def(ctx, 3, 125);
|
|
|
|
bool is_bar = arg_get_lit(ctx, 4);
|
|
bool is_mix = arg_get_lit(ctx, 5);
|
|
bool is_value = arg_get_lit(ctx, 6);
|
|
bool verbose = arg_get_lit(ctx, 7);
|
|
bool use_rgb = arg_get_lit(ctx, 8);
|
|
CLIParserFree(ctx);
|
|
|
|
if (use_rgb && (IfPm5() == false)) {
|
|
PrintAndLogEx(WARNING, "`--rgb` is only supported on Proxmark5; ignoring");
|
|
use_rgb = false;
|
|
}
|
|
|
|
if (divisor < 19) {
|
|
PrintAndLogEx(ERR, "divisor must be between 19 and 255");
|
|
return PM3_EINVARG;
|
|
}
|
|
|
|
if ((freq < 47) || (freq > 600)) {
|
|
PrintAndLogEx(ERR, "freq must be between 47 and 600");
|
|
return PM3_EINVARG;
|
|
}
|
|
|
|
if (divisor != LF_DIVISOR_125 && freq != 125) {
|
|
PrintAndLogEx(ERR, "Select either `divisor` or `frequency`");
|
|
return PM3_EINVARG;
|
|
}
|
|
|
|
if (freq != 125) {
|
|
divisor = LF_FREQ2DIV(freq);
|
|
}
|
|
|
|
if ((is_bar + is_mix + is_value) > 1) {
|
|
PrintAndLogEx(ERR, "Select only one output style");
|
|
return PM3_EINVARG;
|
|
}
|
|
|
|
barMode_t style = g_session.bar_mode;
|
|
if (is_bar) {
|
|
style = STYLE_BAR;
|
|
}
|
|
if (is_mix) {
|
|
style = STYLE_MIXED;
|
|
}
|
|
if (is_value) {
|
|
style = STYLE_VALUE;
|
|
}
|
|
|
|
PrintAndLogEx(INFO, "Measuring LF antenna at " _YELLOW_("%.2f") " kHz", LF_DIV2FREQ(divisor));
|
|
PrintAndLogEx(INFO, "Press " _GREEN_("pm3 button") " or " _GREEN_("<Enter>") " to exit");
|
|
|
|
uint8_t params[] = {1, 0};
|
|
params[1] = divisor;
|
|
PacketResponseNG resp;
|
|
clearCommandBuffer();
|
|
|
|
SendCommandNG(CMD_MEASURE_ANTENNA_TUNING_LF, params, sizeof(params));
|
|
if (WaitForResponseTimeout(CMD_MEASURE_ANTENNA_TUNING_LF, &resp, 1000) == false) {
|
|
PrintAndLogEx(WARNING, "timeout while waiting for Proxmark LF initialization, aborting");
|
|
return PM3_ETIMEOUT;
|
|
}
|
|
|
|
params[0] = 2;
|
|
|
|
// #define MAX_ADC_LF_VOLTAGE 140800
|
|
uint32_t v_max = 71000;
|
|
uint32_t v_min = 71000;
|
|
uint64_t v_sum = 0;
|
|
uint64_t v_count = 0;
|
|
bool first = true;
|
|
|
|
print_progress(0, v_max, style);
|
|
|
|
// loop forever (till button pressed) if iter = 0 (default)
|
|
for (uint32_t i = 0; iter == 0 || i < iter; i++) {
|
|
if (kbd_enter_pressed()) {
|
|
break;
|
|
}
|
|
|
|
SendCommandNG(CMD_MEASURE_ANTENNA_TUNING_LF, params, sizeof(params));
|
|
if (WaitForResponseTimeout(CMD_MEASURE_ANTENNA_TUNING_LF, &resp, 1000) == false) {
|
|
PrintAndLogEx(NORMAL, "");
|
|
PrintAndLogEx(WARNING, "timeout while waiting for Proxmark LF measure, aborting");
|
|
break;
|
|
}
|
|
|
|
if ((resp.status == PM3_EOPABORTED) || (resp.length != sizeof(uint32_t))) {
|
|
PrintAndLogEx(NORMAL, "");
|
|
break;
|
|
}
|
|
|
|
uint32_t volt = resp.data.asDwords[0];
|
|
if (first) {
|
|
v_max = volt;
|
|
v_min = volt;
|
|
first = false;
|
|
}
|
|
v_max = (volt > v_max) ? volt : v_max;
|
|
if (verbose) {
|
|
v_min = (volt < v_min) ? volt : v_min;
|
|
v_sum += volt;
|
|
v_count++;
|
|
}
|
|
print_progress(volt, v_max, style);
|
|
if (use_rgb) {
|
|
tune_rgb_update(volt, v_max);
|
|
}
|
|
}
|
|
if (use_rgb) {
|
|
set_rgb(0, 0, 0); // turn the LED off on exit
|
|
}
|
|
|
|
params[0] = 3;
|
|
SendCommandNG(CMD_MEASURE_ANTENNA_TUNING_LF, params, sizeof(params));
|
|
if (WaitForResponseTimeout(CMD_MEASURE_ANTENNA_TUNING_LF, &resp, 1000) == false) {
|
|
PrintAndLogEx(WARNING, "timeout while waiting for Proxmark LF shutdown, aborting");
|
|
return PM3_ETIMEOUT;
|
|
}
|
|
|
|
PrintAndLogEx(NORMAL, _CLR_LINE_ "\r");
|
|
if (verbose) {
|
|
PrintAndLogEx(INFO, "Min....... %u mV", v_min);
|
|
PrintAndLogEx(INFO, "Max....... %u mV", v_max);
|
|
PrintAndLogEx(INFO, "Average... %.3lf mV", v_sum / (double)v_count);
|
|
}
|
|
PrintAndLogEx(INFO, "Done!");
|
|
return PM3_SUCCESS;
|
|
}
|
|
|
|
#define PAYLOAD_HEADER_SIZE (12 + (3 * LF_CMDREAD_MAX_EXTRA_SYMBOLS))
|
|
|
|
/* send a LF command before reading */
|
|
int CmdLFCommandRead(const char *Cmd) {
|
|
CLIParserContext *ctx;
|
|
CLIParserInit(&ctx, "lf cmdread",
|
|
"Modulate LF reader field to send command before read. All periods in microseconds.\n"
|
|
" - use " _YELLOW_("`lf config`") _CYAN_(" to set parameters"),
|
|
"lf cmdread -d 50 -z 116 -o 166 -e W3000 -c W00110 --> probing for Hitag 1/S\n"
|
|
"lf cmdread -d 50 -z 116 -o 166 -e W3000 -c W11000 --> probing for Hitag 2/S\n"
|
|
"lf cmdread -d 50 -z 116 -o 166 -e W3000 -c W11010 --> probing for Hitag S\n"
|
|
"lf cmdread -d 50 -z 116 -o 166 -e W3000 -c W11000 -s 2000 -@ --> probing for Hitag 2/S, oscilloscope style\n"
|
|
"lf cmdread -d 48 -z 112 -o 176 -e W3000 -e S240 -e E336 -c W0S00000010000E --> probing for Hitag µ(micro)\n"
|
|
);
|
|
|
|
char div_str[70] = {0};
|
|
snprintf(div_str, sizeof(div_str), "Extra symbol definition and duration (up to %i)", LF_CMDREAD_MAX_EXTRA_SYMBOLS);
|
|
|
|
void *argtable[] = {
|
|
arg_param_begin,
|
|
arg_u64_0("d", "duration", "<us>", "delay OFF period, (0 for bitbang mode)"),
|
|
arg_str0("c", "cmd", "<0|1|...>", "command symbols"),
|
|
arg_strx0("e", "extra", "<us>", div_str),
|
|
arg_u64_0("o", "one", "<us>", "ONE time period"),
|
|
arg_u64_0("z", "zero", "<us>", "ZERO time period"),
|
|
arg_u64_0("s", "samples", "<dec>", "number of samples to collect"),
|
|
arg_lit0("v", "verbose", "verbose output"),
|
|
arg_lit0("k", "keep", "keep signal field ON after receive"),
|
|
arg_lit0(NULL, "crc-ht", "calculate and append CRC-8/HITAG (also for ZX8211)"),
|
|
arg_lit0("@", NULL, "continuous mode"),
|
|
arg_param_end
|
|
};
|
|
CLIExecWithReturn(ctx, Cmd, argtable, false);
|
|
uint32_t delay = arg_get_u32_def(ctx, 1, 0);
|
|
|
|
char cmd[PM3_CMD_DATA_SIZE - PAYLOAD_HEADER_SIZE] = {0};
|
|
int cmd_len = sizeof(cmd) - 1; // CLIGetStrWithReturn does not guarantee string to be null-terminated
|
|
CLIGetStrWithReturn(ctx, 2, (uint8_t *)cmd, &cmd_len);
|
|
|
|
char extra_arg[250] = {0};
|
|
int extra_arg_len = sizeof(extra_arg) - 1; // CLIGetStrWithReturn does not guarantee string to be null-terminated
|
|
CLIGetStrWithReturn(ctx, 3, (uint8_t *)extra_arg, &extra_arg_len);
|
|
|
|
uint16_t period_1 = arg_get_u32_def(ctx, 4, 0);
|
|
uint16_t period_0 = arg_get_u32_def(ctx, 5, 0);
|
|
uint32_t samples = arg_get_u32_def(ctx, 6, 0);
|
|
|
|
bool verbose = arg_get_lit(ctx, 7);
|
|
bool keep_field_on = arg_get_lit(ctx, 8);
|
|
bool add_crc_ht = arg_get_lit(ctx, 9);
|
|
bool cm = arg_get_lit(ctx, 10);
|
|
|
|
CLIParserFree(ctx);
|
|
|
|
if (g_session.pm3_present == false) {
|
|
return PM3_ENOTTY;
|
|
}
|
|
|
|
struct p {
|
|
uint32_t delay;
|
|
uint16_t period_0;
|
|
uint16_t period_1;
|
|
uint8_t symbol_extra[LF_CMDREAD_MAX_EXTRA_SYMBOLS];
|
|
uint16_t period_extra[LF_CMDREAD_MAX_EXTRA_SYMBOLS];
|
|
uint32_t samples : 30;
|
|
bool keep_field_on : 1;
|
|
bool verbose : 1;
|
|
uint8_t data[PM3_CMD_DATA_SIZE - PAYLOAD_HEADER_SIZE];
|
|
} PACKED payload;
|
|
payload.delay = delay;
|
|
payload.period_1 = period_1;
|
|
payload.period_0 = period_0;
|
|
payload.samples = samples;
|
|
payload.keep_field_on = keep_field_on;
|
|
payload.verbose = verbose;
|
|
memset(payload.symbol_extra, 0, sizeof(payload.symbol_extra));
|
|
memset(payload.period_extra, 0, sizeof(payload.period_extra));
|
|
|
|
if (cmd_len > sizeof(payload.data) - 8 * add_crc_ht - 1) {
|
|
PrintAndLogEx(ERR, "cmd too long, max length is %zu", sizeof(cmd) - 1);
|
|
return PM3_EINVARG;
|
|
}
|
|
|
|
if (add_crc_ht) {
|
|
|
|
// Hitag 1, Hitag S, ZX8211
|
|
// width=8 poly=0x1d init=0xff refin=false refout=false xorout=0x00 check=0xb4 residue=0x00 name="CRC-8/HITAG"
|
|
crc_t crc;
|
|
crc_init_ref(&crc, 8, 0x1d, 0xff, 0, false, false);
|
|
|
|
uint8_t data = 0;
|
|
uint8_t n = 0;
|
|
|
|
for (int i = 0; i < cmd_len; i++) {
|
|
|
|
if ((cmd[i] != '0') && (cmd[i] != '1')) {
|
|
// avoid include 'W0S' in crc
|
|
crc_init_ref(&crc, 8, 0x1d, 0xff, 0, false, false);
|
|
n = 0;
|
|
data = 0;
|
|
continue;
|
|
}
|
|
|
|
data <<= 1;
|
|
data += cmd[i] - '0';
|
|
n++;
|
|
|
|
if (n == 8) {
|
|
crc_update2(&crc, data, n);
|
|
n = 0;
|
|
data = 0;
|
|
}
|
|
}
|
|
|
|
if (n > 0) {
|
|
crc_update2(&crc, data, n);
|
|
}
|
|
|
|
uint8_t crc_final = crc_finish(&crc);
|
|
for (int j = 7; j >= 0; j--) {
|
|
cmd[cmd_len] = ((crc_final >> j) & 1) ? '1' : '0';
|
|
cmd_len++;
|
|
}
|
|
}
|
|
|
|
memcpy(payload.data, cmd, cmd_len + 1);
|
|
|
|
// extra symbol definition
|
|
uint8_t index_extra = 0;
|
|
int i = 0;
|
|
for (; i < extra_arg_len;) {
|
|
|
|
if (index_extra < LF_CMDREAD_MAX_EXTRA_SYMBOLS) {
|
|
payload.symbol_extra[index_extra] = extra_arg[i];
|
|
int tmp = atoi(extra_arg + (i + 1));
|
|
payload.period_extra[index_extra] = tmp;
|
|
index_extra++;
|
|
i++;
|
|
while (extra_arg[i] >= 0x30 && extra_arg[i] <= 0x39)
|
|
i++;
|
|
|
|
} else {
|
|
PrintAndLogEx(ERR, "Too many extra symbols, please define up to %i symbols", LF_CMDREAD_MAX_EXTRA_SYMBOLS);
|
|
return PM3_EINVARG;
|
|
}
|
|
}
|
|
|
|
// bitbang mode
|
|
if (payload.delay == 0) {
|
|
if (payload.period_0 < 7 || payload.period_1 < 7) {
|
|
PrintAndLogEx(ERR, "periods cannot be less than 7us in bit bang mode");
|
|
return PM3_EINVARG;
|
|
}
|
|
}
|
|
|
|
PrintAndLogEx(DEBUG, _CYAN_("Cmd read - settings"));
|
|
PrintAndLogEx(DEBUG, "--------------------");
|
|
PrintAndLogEx(DEBUG, "Delay..... " _YELLOW_("%u"), payload.delay);
|
|
PrintAndLogEx(DEBUG, "Zero...... " _YELLOW_("%u"), payload.period_0);
|
|
PrintAndLogEx(DEBUG, "One....... " _YELLOW_("%u"), payload.period_1);
|
|
PrintAndLogEx(DEBUG, "Samples... " _YELLOW_("%u"), payload.samples);
|
|
PrintAndLogEx(DEBUG, "");
|
|
PrintAndLogEx(DEBUG, _CYAN_("Extra symbols"));
|
|
for (i = 0; i < LF_CMDREAD_MAX_EXTRA_SYMBOLS; i++) {
|
|
if (payload.symbol_extra[i] == 0x00)
|
|
continue;
|
|
|
|
PrintAndLogEx(DEBUG, "%c......... " _YELLOW_("%u"), payload.symbol_extra[i], payload.period_extra[i]);
|
|
}
|
|
PrintAndLogEx(DEBUG, "");
|
|
PrintAndLogEx(DEBUG, "Cmd....... " _YELLOW_("%s"), payload.data);
|
|
PrintAndLogEx(DEBUG, "");
|
|
|
|
if (cm) {
|
|
PrintAndLogEx(INFO, "Press " _GREEN_("<Enter>") " to exit");
|
|
}
|
|
|
|
if (verbose) {
|
|
PrintAndLogEx(SUCCESS, "Sending command...");
|
|
}
|
|
|
|
int ret = PM3_SUCCESS;
|
|
do {
|
|
clearCommandBuffer();
|
|
SendCommandNG(CMD_LF_MOD_THEN_ACQ_RAW_ADC, (uint8_t *)&payload, PAYLOAD_HEADER_SIZE + cmd_len + 1);
|
|
|
|
// init to ZERO
|
|
PacketResponseNG resp;
|
|
memset(&resp, 0, sizeof(resp));
|
|
|
|
i = 10;
|
|
// 20sec wait loop
|
|
while (WaitForResponseTimeout(CMD_LF_MOD_THEN_ACQ_RAW_ADC, &resp, 2000) == false && i != 0) {
|
|
if (verbose) {
|
|
PrintAndLogEx(NORMAL, "." NOLF);
|
|
}
|
|
i--;
|
|
}
|
|
|
|
if (verbose) {
|
|
PrintAndLogEx(NORMAL, "");
|
|
}
|
|
|
|
if (resp.status != PM3_SUCCESS) {
|
|
PrintAndLogEx(WARNING, "command failed.");
|
|
return PM3_ESOFT;
|
|
}
|
|
|
|
if (i) {
|
|
if (verbose) {
|
|
PrintAndLogEx(SUCCESS, "downloading response signal data");
|
|
}
|
|
getSamples(samples, false);
|
|
ret = PM3_SUCCESS;
|
|
} else {
|
|
PrintAndLogEx(WARNING, "timeout while waiting for reply");
|
|
return PM3_ETIMEOUT;
|
|
}
|
|
|
|
} while (cm && (kbd_enter_pressed() == false));
|
|
return ret;
|
|
}
|
|
|
|
int CmdFlexdemod(const char *Cmd) {
|
|
(void)Cmd; // Cmd is not used so far
|
|
|
|
#ifndef LONG_WAIT
|
|
#define LONG_WAIT 100
|
|
#endif
|
|
int i, j, start, bit, sum;
|
|
|
|
int *data = calloc(g_GraphTraceLen, sizeof(int));
|
|
if (data == NULL) {
|
|
PrintAndLogEx(WARNING, "Failed to allocate memory");
|
|
return PM3_EMALLOC;
|
|
}
|
|
memcpy(data, g_GraphBuffer, g_GraphTraceLen);
|
|
|
|
size_t size = g_GraphTraceLen;
|
|
|
|
for (i = 0; i < g_GraphTraceLen; ++i)
|
|
data[i] = (data[i] < 0) ? -1 : 1;
|
|
|
|
for (start = 0; start < size - LONG_WAIT; start++) {
|
|
int first = data[start];
|
|
for (i = start; i < start + LONG_WAIT; i++) {
|
|
if (data[i] != first) {
|
|
break;
|
|
}
|
|
}
|
|
if (i == (start + LONG_WAIT))
|
|
break;
|
|
}
|
|
|
|
if (start == size - LONG_WAIT) {
|
|
PrintAndLogEx(WARNING, "nothing to wait for");
|
|
free(data);
|
|
return PM3_ENODATA;
|
|
}
|
|
|
|
data[start] = 4;
|
|
data[start + 1] = 0;
|
|
|
|
uint8_t bits[64] = {0x00};
|
|
|
|
i = start;
|
|
for (bit = 0; bit < 64; bit++) {
|
|
sum = 0;
|
|
for (j = 0; j < 16; j++) {
|
|
sum += data[i++];
|
|
}
|
|
bits[bit] = (sum > 0) ? 1 : 0;
|
|
// PrintAndLogEx(NORMAL, "bit %d sum %d", bit, sum);
|
|
}
|
|
|
|
for (bit = 0; bit < 64; bit++) {
|
|
sum = 0;
|
|
for (j = 0; j < 16; j++)
|
|
sum += data[i++];
|
|
|
|
if (sum > 0 && bits[bit] != 1) PrintAndLogEx(WARNING, "oops1 at %d", bit);
|
|
|
|
if (sum < 0 && bits[bit] != 0) PrintAndLogEx(WARNING, "oops2 at %d", bit);
|
|
|
|
}
|
|
|
|
// iceman, use g_DemodBuffer? blue line?
|
|
// HACK writing back to graphbuffer.
|
|
g_GraphTraceLen = 32 * 64;
|
|
i = 0;
|
|
for (bit = 0; bit < 64; bit++) {
|
|
|
|
int phase = (bits[bit] == 0) ? 0 : 1;
|
|
|
|
for (j = 0; j < 32; j++) {
|
|
g_GraphBuffer[i++] = phase;
|
|
phase = !phase;
|
|
}
|
|
}
|
|
RepaintGraphWindow();
|
|
free(data);
|
|
return PM3_SUCCESS;
|
|
}
|
|
|
|
/*
|
|
* this function will save a copy of the current lf config value, and set config to default values.
|
|
*
|
|
*/
|
|
int lf_resetconfig(sample_config *config) {
|
|
|
|
if (config == NULL) {
|
|
return PM3_EINVARG;
|
|
}
|
|
|
|
memset(config, 0, sizeof(sample_config));
|
|
|
|
int res = lf_getconfig(config);
|
|
if (res != PM3_SUCCESS) {
|
|
PrintAndLogEx(ERR, "failed to get current device LF config");
|
|
return res;
|
|
}
|
|
|
|
sample_config def_config = {
|
|
.decimation = 1,
|
|
.bits_per_sample = 8,
|
|
.averaging = 1,
|
|
.divisor = LF_DIVISOR_125,
|
|
.trigger_threshold = 0,
|
|
.samples_to_skip = 0,
|
|
.verbose = false,
|
|
};
|
|
|
|
res = lf_setconfig(&def_config);
|
|
if (res != PM3_SUCCESS) {
|
|
PrintAndLogEx(ERR, "failed to reset LF configuration to default values");
|
|
return res;
|
|
}
|
|
|
|
// disable output on save config object
|
|
config->verbose = false;
|
|
|
|
return PM3_SUCCESS;
|
|
}
|
|
|
|
int lf_getconfig(sample_config *config) {
|
|
if (!g_session.pm3_present) return PM3_ENOTTY;
|
|
|
|
if (config == NULL)
|
|
return PM3_EINVARG;
|
|
|
|
clearCommandBuffer();
|
|
|
|
SendCommandNG(CMD_LF_SAMPLING_GET_CONFIG, NULL, 0);
|
|
PacketResponseNG resp;
|
|
if (WaitForResponseTimeout(CMD_LF_SAMPLING_GET_CONFIG, &resp, 2000) == false) {
|
|
PrintAndLogEx(WARNING, "command execution time out");
|
|
return PM3_ETIMEOUT;
|
|
}
|
|
memcpy(config, resp.data.asBytes, sizeof(sample_config));
|
|
return PM3_SUCCESS;
|
|
}
|
|
|
|
int lf_setconfig(sample_config *config) {
|
|
if (!g_session.pm3_present) return PM3_ENOTTY;
|
|
|
|
clearCommandBuffer();
|
|
if (config != NULL)
|
|
SendCommandNG(CMD_LF_SAMPLING_SET_CONFIG, (uint8_t *)config, sizeof(sample_config));
|
|
else
|
|
SendCommandNG(CMD_LF_SAMPLING_PRINT_CONFIG, NULL, 0);
|
|
|
|
return PM3_SUCCESS;
|
|
}
|
|
|
|
int CmdLFConfig(const char *Cmd) {
|
|
CLIParserContext *ctx;
|
|
CLIParserInit(&ctx, "lf config",
|
|
"Get/Set config for LF sampling, bit/sample, decimation, frequency\n"
|
|
"These changes are temporary, will be reset after a power cycle.\n\n"
|
|
" - use " _YELLOW_("`lf read`") _CYAN_(" performs a read (active field)\n")
|
|
_CYAN_(" - use ") _YELLOW_("`lf sniff`") _CYAN_(" performs a sniff (no active field)"),
|
|
"lf config --> shows current config\n"
|
|
"lf config -b 8 --125 --> samples at 125 kHz, 8 bps\n"
|
|
"lf config -b 4 --134 --dec 3 --> samples at 134 kHz, averages three samples into one, stored with a resolution of 4 bits per sample\n"
|
|
"lf config --trig 20 -s 10000 --> trigger sampling when above 20, skip 10 000 first samples after triggered\n"
|
|
"lf config --reset --> reset back to default values\n"
|
|
);
|
|
|
|
char div_str[70] = {0};
|
|
snprintf(div_str, sizeof(div_str), "Manually set freq divisor. %d -> 134 kHz, %d -> 125 kHz", LF_DIVISOR_134, LF_DIVISOR_125);
|
|
|
|
void *argtable[] = {
|
|
arg_param_begin,
|
|
arg_lit0(NULL, "125", "125 kHz frequency"),
|
|
arg_lit0(NULL, "134", "134 kHz frequency"),
|
|
arg_int0("a", "avg", "<0|1>", "averaging - if set, will average the stored sample value when decimating (default 1)"),
|
|
arg_int0("b", "bps", "<1-8>", "sets resolution of bits per sample (default 8)"),
|
|
arg_int0(NULL, "dec", "<1-8>", "sets decimation. A value of N saves only 1 in N samples (default 1)"),
|
|
arg_int0(NULL, "divisor", "<19-255>", div_str),
|
|
arg_int0("f", "freq", "<47-600>", "manually set frequency in kHz"),
|
|
arg_lit0("r", "reset", "reset values to defaults"),
|
|
arg_int0("s", "skip", "<dec>", "sets a number of samples to skip before capture (default 0)"),
|
|
arg_int0("t", "trig", "<0-128>", "sets trigger threshold. 0 means no threshold"),
|
|
arg_param_end
|
|
};
|
|
CLIExecWithReturn(ctx, Cmd, argtable, true);
|
|
bool use_125 = arg_get_lit(ctx, 1);
|
|
bool use_134 = arg_get_lit(ctx, 2);
|
|
int8_t avg = arg_get_int_def(ctx, 3, 0);
|
|
int8_t bps = arg_get_int_def(ctx, 4, -1);
|
|
int8_t dec = arg_get_int_def(ctx, 5, -1);
|
|
int16_t divisor = arg_get_int_def(ctx, 6, -1);
|
|
int16_t freq = arg_get_int_def(ctx, 7, -1);
|
|
bool reset = arg_get_lit(ctx, 8);
|
|
int32_t skip = arg_get_int_def(ctx, 9, -1);
|
|
int16_t trigg = arg_get_int_def(ctx, 10, -1);
|
|
CLIParserFree(ctx);
|
|
|
|
if (g_session.pm3_present == false)
|
|
return PM3_ENOTTY;
|
|
|
|
// if called with no params, just print the device config
|
|
if (strlen(Cmd) == 0) {
|
|
return lf_setconfig(NULL);
|
|
}
|
|
|
|
if (use_125 + use_134 > 1) {
|
|
PrintAndLogEx(ERR, "use only one of 125 or 134 params");
|
|
return PM3_EINVARG;
|
|
}
|
|
|
|
sample_config config = {
|
|
.decimation = -1,
|
|
.bits_per_sample = -1,
|
|
.averaging = -1,
|
|
.divisor = -1,
|
|
.trigger_threshold = -1,
|
|
.samples_to_skip = -1,
|
|
.verbose = true
|
|
};
|
|
|
|
if (use_125)
|
|
config.divisor = LF_DIVISOR_125;
|
|
|
|
if (use_134)
|
|
config.divisor = LF_DIVISOR_134;
|
|
|
|
// check if the config.averaging is not set by if(reset){...}
|
|
if (config.averaging == -1)
|
|
config.averaging = (avg == 1);
|
|
|
|
if (bps > -1) {
|
|
// bps is limited to 8
|
|
config.bits_per_sample = (bps & 0x0F);
|
|
if (config.bits_per_sample > 8)
|
|
config.bits_per_sample = 8;
|
|
}
|
|
|
|
if (dec > -1) {
|
|
// decimation is limited to 8
|
|
config.decimation = (dec & 0x0F);
|
|
if (config.decimation > 8)
|
|
config.decimation = 8;
|
|
}
|
|
|
|
if (divisor > -1) {
|
|
config.divisor = divisor;
|
|
if (config.divisor < 19) {
|
|
PrintAndLogEx(ERR, "divisor must be between 19 and 255");
|
|
return PM3_EINVARG;
|
|
}
|
|
}
|
|
|
|
if (freq > -1) {
|
|
config.divisor = LF_FREQ2DIV(freq);
|
|
if (config.divisor < 19) {
|
|
PrintAndLogEx(ERR, "freq must be between 47 and 600");
|
|
return PM3_EINVARG;
|
|
}
|
|
}
|
|
|
|
if (trigg > -1) {
|
|
config.trigger_threshold = trigg;
|
|
}
|
|
|
|
config.samples_to_skip = skip;
|
|
|
|
if (reset) {
|
|
config.decimation = 1;
|
|
config.bits_per_sample = 8;
|
|
config.averaging = 1,
|
|
config.divisor = LF_DIVISOR_125;
|
|
config.samples_to_skip = 0;
|
|
config.trigger_threshold = 0;
|
|
}
|
|
|
|
return lf_setconfig(&config);
|
|
}
|
|
|
|
static int lf_read_internal(bool realtime, bool verbose, uint64_t samples, bool cotag) {
|
|
if (!g_session.pm3_present) return PM3_ENOTTY;
|
|
|
|
lf_sample_payload_t payload = {0};
|
|
payload.realtime = realtime;
|
|
payload.verbose = verbose;
|
|
payload.cotag = cotag;
|
|
payload.samples = (samples > MAX_LF_SAMPLES) ? MAX_LF_SAMPLES : samples;
|
|
|
|
sample_config current_config;
|
|
int retval = lf_getconfig(¤t_config);
|
|
if (retval != PM3_SUCCESS) {
|
|
PrintAndLogEx(ERR, "failed to get current device config");
|
|
return retval;
|
|
}
|
|
clearCommandBuffer();
|
|
const uint8_t bits_per_sample = current_config.bits_per_sample;
|
|
const bool is_trigger_threshold_set = (current_config.trigger_threshold > 0);
|
|
|
|
if (realtime) {
|
|
uint8_t *realtimeBuf = calloc(samples, sizeof(uint8_t));
|
|
if (realtimeBuf == NULL) {
|
|
PrintAndLogEx(WARNING, "Failed to allocate memory");
|
|
return PM3_EMALLOC;
|
|
}
|
|
|
|
size_t sample_bytes = samples * bits_per_sample;
|
|
sample_bytes = (sample_bytes / 8) + (sample_bytes % 8 != 0);
|
|
|
|
// In real-time mode, the LF bitstream should be loaded before receiving raw data.
|
|
// Otherwise, the first batch of raw data might contain the response of CMD_WTX.
|
|
int result = set_fpga_mode(FPGA_BITSTREAM_LF);
|
|
if (result != PM3_SUCCESS) {
|
|
PrintAndLogEx(FAILED, "failed to load LF bitstream to FPGA");
|
|
free(realtimeBuf);
|
|
return result;
|
|
}
|
|
|
|
SendCommandNG(CMD_LF_ACQ_RAW_ADC, (uint8_t *)&payload, sizeof(payload));
|
|
if (is_trigger_threshold_set) {
|
|
size_t first_receive_len = 32;
|
|
// Wait until a bunch of data arrives. Keep raw mode on across both
|
|
// calls so the comm thread doesn't briefly fall back to parsing
|
|
// framed packets out of the still-arriving sample stream.
|
|
first_receive_len = WaitForRawDataTimeout(realtimeBuf, first_receive_len, -1, false, true);
|
|
sample_bytes = WaitForRawDataTimeout(realtimeBuf + first_receive_len, sample_bytes - first_receive_len, 1000, true, false);
|
|
sample_bytes += first_receive_len;
|
|
} else {
|
|
sample_bytes = WaitForRawDataTimeout(realtimeBuf, sample_bytes, 1000, true, false);
|
|
}
|
|
samples = sample_bytes * 8 / bits_per_sample;
|
|
PrintAndLogEx(INFO, "Done: %" PRIu64 " samples (%zu bytes)", samples, sample_bytes);
|
|
if (samples != 0) {
|
|
getSamplesFromBufEx(realtimeBuf, samples, bits_per_sample, verbose);
|
|
}
|
|
|
|
free(realtimeBuf);
|
|
} else {
|
|
SendCommandNG(CMD_LF_ACQ_RAW_ADC, (uint8_t *)&payload, sizeof(payload));
|
|
PacketResponseNG resp;
|
|
|
|
if (is_trigger_threshold_set) {
|
|
WaitForResponse(CMD_LF_ACQ_RAW_ADC, &resp);
|
|
} else {
|
|
|
|
if (WaitForResponseTimeout(CMD_LF_ACQ_RAW_ADC, &resp, 2500) == false) {
|
|
PrintAndLogEx(WARNING, "(lf_read) command execution time out");
|
|
return PM3_ETIMEOUT;
|
|
}
|
|
}
|
|
// response is number of bits read
|
|
uint32_t size = (resp.data.asDwords[0] / bits_per_sample);
|
|
getSamples(size, verbose);
|
|
}
|
|
|
|
return PM3_SUCCESS;
|
|
}
|
|
|
|
int lf_read(bool verbose, uint64_t samples) {
|
|
return lf_read_internal(false, verbose, samples, false);
|
|
}
|
|
|
|
int lf_read_cotag(bool realtime, bool verbose, uint64_t samples) {
|
|
return lf_read_internal(realtime, verbose, samples, true);
|
|
}
|
|
|
|
int CmdLFRead(const char *Cmd) {
|
|
CLIParserContext *ctx;
|
|
CLIParserInit(&ctx, "lf read",
|
|
"Sniff low frequency signal.\n"
|
|
" - use " _YELLOW_("`lf config`") _CYAN_(" to set parameters.\n")
|
|
_CYAN_(" - use ") _YELLOW_("`data plot`") _CYAN_(" to look at it.\n")
|
|
_CYAN_("If the number of samples is more than the device memory limit (40000 now), ")
|
|
_CYAN_("it will try to use the real-time sampling mode."),
|
|
"lf read -v -s 12000 --> collect 12000 samples\n"
|
|
"lf read -s 3000 -@ --> oscilloscope style \n"
|
|
);
|
|
|
|
void *argtable[] = {
|
|
arg_param_begin,
|
|
arg_u64_0("s", "samples", "<dec>", "number of samples to collect"),
|
|
arg_lit0("v", "verbose", "verbose output"),
|
|
arg_lit0("@", NULL, "continuous reading mode"),
|
|
arg_param_end
|
|
};
|
|
CLIExecWithReturn(ctx, Cmd, argtable, true);
|
|
uint64_t samples = arg_get_u64_def(ctx, 1, 0);
|
|
bool verbose = arg_get_lit(ctx, 2);
|
|
bool cm = arg_get_lit(ctx, 3);
|
|
CLIParserFree(ctx);
|
|
|
|
// the 40000 there should be the result of BigBuf_max_traceLen(),
|
|
// but IDK how to get it.
|
|
bool realtime = samples > 40000;
|
|
|
|
if (g_session.pm3_present == false)
|
|
return PM3_ENOTTY;
|
|
|
|
if (cm || realtime) {
|
|
PrintAndLogEx(INFO, "Press " _GREEN_("<Enter>") " to exit");
|
|
}
|
|
int ret = PM3_SUCCESS;
|
|
do {
|
|
ret = lf_read_internal(realtime, verbose, samples, false);
|
|
} while (cm && (kbd_enter_pressed() == false));
|
|
|
|
if (ret == PM3_SUCCESS) {
|
|
PrintAndLogEx(SUCCESS, "Got " _YELLOW_("%zu") " samples", g_GraphTraceLen);
|
|
|
|
if (getSignalProperties()->isnoise) {
|
|
PrintAndLogEx(INFO, "signal looks like noise");
|
|
}
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
int lf_sniff(bool realtime, bool verbose, uint64_t samples) {
|
|
if (!g_session.pm3_present) return PM3_ENOTTY;
|
|
|
|
lf_sample_payload_t payload = {0};
|
|
payload.realtime = realtime;
|
|
payload.verbose = verbose;
|
|
payload.samples = (samples > MAX_LF_SAMPLES) ? MAX_LF_SAMPLES : samples;
|
|
|
|
sample_config current_config;
|
|
int retval = lf_getconfig(¤t_config);
|
|
if (retval != PM3_SUCCESS) {
|
|
PrintAndLogEx(ERR, "failed to get current device config");
|
|
return retval;
|
|
}
|
|
clearCommandBuffer();
|
|
const uint8_t bits_per_sample = current_config.bits_per_sample;
|
|
const bool is_trigger_threshold_set = (current_config.trigger_threshold > 0);
|
|
|
|
if (realtime) {
|
|
uint8_t *realtimeBuf = calloc(samples, sizeof(uint8_t));
|
|
if (realtimeBuf == NULL) {
|
|
PrintAndLogEx(WARNING, "Failed to allocate memory");
|
|
return PM3_EMALLOC;
|
|
}
|
|
|
|
size_t sample_bytes = samples * bits_per_sample;
|
|
sample_bytes = (sample_bytes / 8) + (sample_bytes % 8 != 0);
|
|
|
|
// In real-time mode, the LF bitstream should be loaded before receiving raw data.
|
|
// Otherwise, the first batch of raw data might contain the response of CMD_WTX.
|
|
int result = set_fpga_mode(FPGA_BITSTREAM_LF);
|
|
if (result != PM3_SUCCESS) {
|
|
PrintAndLogEx(FAILED, "failed to load LF bitstream to FPGA");
|
|
free(realtimeBuf);
|
|
return result;
|
|
}
|
|
|
|
SendCommandNG(CMD_LF_SNIFF_RAW_ADC, (uint8_t *)&payload, sizeof(payload));
|
|
if (is_trigger_threshold_set) {
|
|
size_t first_receive_len = 32;
|
|
// Wait until a bunch of data arrives. Keep raw mode on across both
|
|
// calls so the comm thread doesn't briefly fall back to parsing
|
|
// framed packets out of the still-arriving sample stream.
|
|
first_receive_len = WaitForRawDataTimeout(realtimeBuf, first_receive_len, -1, false, true);
|
|
sample_bytes = WaitForRawDataTimeout(realtimeBuf + first_receive_len, sample_bytes - first_receive_len, 1000, true, false);
|
|
sample_bytes += first_receive_len;
|
|
} else {
|
|
sample_bytes = WaitForRawDataTimeout(realtimeBuf, sample_bytes, 1000, true, false);
|
|
}
|
|
samples = sample_bytes * 8 / bits_per_sample;
|
|
PrintAndLogEx(INFO, "Done: %" PRIu64 " samples (%zu bytes)", samples, sample_bytes);
|
|
if (samples != 0) {
|
|
getSamplesFromBufEx(realtimeBuf, samples, bits_per_sample, verbose);
|
|
}
|
|
|
|
free(realtimeBuf);
|
|
} else {
|
|
SendCommandNG(CMD_LF_SNIFF_RAW_ADC, (uint8_t *)&payload, sizeof(payload));
|
|
PacketResponseNG resp;
|
|
if (is_trigger_threshold_set) {
|
|
WaitForResponse(CMD_LF_SNIFF_RAW_ADC, &resp);
|
|
} else {
|
|
if (WaitForResponseTimeout(CMD_LF_SNIFF_RAW_ADC, &resp, 2500) == false) {
|
|
PrintAndLogEx(WARNING, "(lf_read) command execution time out");
|
|
return PM3_ETIMEOUT;
|
|
}
|
|
}
|
|
// response is number of bits read
|
|
uint32_t size = (resp.data.asDwords[0] / bits_per_sample);
|
|
getSamples(size, verbose);
|
|
}
|
|
|
|
return PM3_SUCCESS;
|
|
}
|
|
|
|
int CmdLFSniff(const char *Cmd) {
|
|
CLIParserContext *ctx;
|
|
CLIParserInit(&ctx, "lf sniff",
|
|
"Sniff low frequency signal. You need to configure the LF part on the Proxmark3 device manually.\n"
|
|
"Usually a trigger and skip samples is a good thing to set before doing a low frequency sniff.\n"
|
|
"\n"
|
|
" - use " _YELLOW_("`lf config`") _CYAN_(" to set parameters.\n")
|
|
_CYAN_(" - use ") _YELLOW_("`data plot`") _CYAN_(" to look at sniff signal.\n")
|
|
_CYAN_(" - use ") _YELLOW_("`lf search -1`") _CYAN_(" to see if signal can be automatic decoded.\n")
|
|
_CYAN_("If the number of samples is more than the device memory limit (40000 now), ")
|
|
_CYAN_("it will try to use the real-time sampling mode."),
|
|
"lf sniff -v\n"
|
|
"lf sniff -s 3000 -@ --> oscilloscope style \n"
|
|
);
|
|
|
|
void *argtable[] = {
|
|
arg_param_begin,
|
|
arg_u64_0("s", "samples", "<dec>", "number of samples to collect"),
|
|
arg_lit0("v", "verbose", "verbose output"),
|
|
arg_lit0("@", NULL, "continuous sniffing mode"),
|
|
arg_param_end
|
|
};
|
|
CLIExecWithReturn(ctx, Cmd, argtable, true);
|
|
uint64_t samples = arg_get_u64_def(ctx, 1, 0);
|
|
bool verbose = arg_get_lit(ctx, 2);
|
|
bool cm = arg_get_lit(ctx, 3);
|
|
CLIParserFree(ctx);
|
|
|
|
// the 40000 there should be the result of BigBuf_max_traceLen(),
|
|
// but IDK how to get it.
|
|
bool realtime = samples > 40000;
|
|
|
|
if (g_session.pm3_present == false)
|
|
return PM3_ENOTTY;
|
|
|
|
if (cm || realtime) {
|
|
PrintAndLogEx(INFO, "Press " _GREEN_("<Enter>") " to exit");
|
|
}
|
|
int ret = PM3_SUCCESS;
|
|
do {
|
|
ret = lf_sniff(realtime, verbose, samples);
|
|
} while (cm && (kbd_enter_pressed() == false));
|
|
return ret;
|
|
}
|
|
|
|
static void lf_chk_bitstream(void) {
|
|
// convert to bitstream if necessary
|
|
for (int i = 0; i < (int)(g_GraphTraceLen / 2); i++) {
|
|
if (g_GraphBuffer[i] > 1 || g_GraphBuffer[i] < 0) {
|
|
CmdGetBitStream("");
|
|
PrintAndLogEx(INFO, "converted Graphbuffer to bitstream values (0|1)");
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Uploads g_GraphBuffer to device, in order to be used for LF SIM.
|
|
int lfsim_upload_gb(void) {
|
|
PrintAndLogEx(DEBUG, "DEBUG: Uploading %zu bytes", g_GraphTraceLen);
|
|
|
|
struct pupload {
|
|
uint8_t flag;
|
|
uint16_t offset;
|
|
uint8_t data[PM3_CMD_DATA_SIZE - 3];
|
|
} PACKED payload_up;
|
|
|
|
// flag =
|
|
// b0 0
|
|
// 1 clear bigbuff
|
|
payload_up.flag = 0x1;
|
|
|
|
// fast push mode
|
|
g_conn.block_after_ACK = true;
|
|
|
|
PacketResponseNG resp;
|
|
|
|
//can send only 512 bits at a time (1 byte sent per bit...)
|
|
PrintAndLogEx(INFO, "." NOLF);
|
|
for (size_t i = 0; i < g_GraphTraceLen; i += PM3_CMD_DATA_SIZE - 3) {
|
|
|
|
size_t len = MIN((g_GraphTraceLen - i), PM3_CMD_DATA_SIZE - 3);
|
|
clearCommandBuffer();
|
|
payload_up.offset = i;
|
|
|
|
for (size_t j = 0; j < len; j++)
|
|
payload_up.data[j] = g_GraphBuffer[i + j];
|
|
|
|
SendCommandNG(CMD_LF_UPLOAD_SIM_SAMPLES, (uint8_t *)&payload_up, sizeof(struct pupload));
|
|
WaitForResponse(CMD_LF_UPLOAD_SIM_SAMPLES, &resp);
|
|
if (resp.status != PM3_SUCCESS) {
|
|
PrintAndLogEx(INFO, "Bigbuf is full");
|
|
break;
|
|
}
|
|
PrintAndLogEx(NORMAL, "." NOLF);
|
|
fflush(stdout);
|
|
payload_up.flag = 0;
|
|
}
|
|
PrintAndLogEx(NORMAL, "");
|
|
|
|
// Disable fast mode before last command
|
|
g_conn.block_after_ACK = false;
|
|
return PM3_SUCCESS;
|
|
}
|
|
|
|
//Attempt to simulate any wave in buffer (one bit per output sample)
|
|
// converts g_GraphBuffer to bitstream (based on zero crossings) if needed.
|
|
int CmdLFSim(const char *Cmd) {
|
|
CLIParserContext *ctx;
|
|
CLIParserInit(&ctx, "lf sim",
|
|
"Simulate low frequency tag from graphbuffer\n"
|
|
"Use " _YELLOW_("`lf config`") _CYAN_(" to set parameters"),
|
|
"lf sim\n"
|
|
"lf sim --gap 240 --> start simulating with 240ms gap"
|
|
);
|
|
|
|
void *argtable[] = {
|
|
arg_param_begin,
|
|
arg_u64_0("g", "gap", "<ms>", "start gap in microseconds"),
|
|
arg_param_end
|
|
};
|
|
CLIExecWithReturn(ctx, Cmd, argtable, true);
|
|
uint16_t gap = arg_get_u32_def(ctx, 1, 0);
|
|
CLIParserFree(ctx);
|
|
|
|
if (g_session.pm3_present == false) {
|
|
PrintAndLogEx(DEBUG, "DEBUG: no proxmark present");
|
|
return PM3_ENOTTY;
|
|
}
|
|
|
|
// sanity check
|
|
if (g_GraphTraceLen < 20) {
|
|
PrintAndLogEx(ERR, "No data in Graphbuffer");
|
|
return PM3_ENODATA;
|
|
}
|
|
|
|
// convert to bitstream if necessary
|
|
lf_chk_bitstream();
|
|
|
|
lfsim_upload_gb();
|
|
|
|
struct p {
|
|
uint16_t len;
|
|
uint16_t gap;
|
|
} PACKED payload;
|
|
payload.len = g_GraphTraceLen;
|
|
payload.gap = gap;
|
|
|
|
clearCommandBuffer();
|
|
SendCommandNG(CMD_LF_SIMULATE, (uint8_t *)&payload, sizeof(payload));
|
|
return lfsim_wait_check(CMD_LF_SIMULATE);
|
|
}
|
|
|
|
// sim fsk data given clock, fcHigh, fcLow, invert
|
|
// - allow pull data from g_DemodBuffer
|
|
int CmdLFfskSim(const char *Cmd) {
|
|
CLIParserContext *ctx;
|
|
CLIParserInit(&ctx, "lf simfsk",
|
|
"Simulate FSK tag from DemodBuffer or input. There are about four FSK modulations to know of.\n"
|
|
"FSK1 - where fc/8 = high and fc/5 = low\n"
|
|
"FSK1a - is inverted FSK1, ie: fc/5 = high and fc/8 = low\n"
|
|
"FSK2 - where fc/10 = high and fc/8 = low\n"
|
|
"FSK2a - is inverted FSK2, ie: fc/10 = high and fc/8 = low\n\n"
|
|
"NOTE: if you set one clock manually set them all manually",
|
|
"lf simfsk -c 40 --high 8 --low 5 -d 010203 --> FSK1 rf/40 data 010203\n"
|
|
"lf simfsk -c 40 --high 5 --low 8 -d 010203 --> FSK1a rf/40 data 010203\n"
|
|
"lf simfsk -c 64 --high 10 --low 8 -d 010203 --> FSK2 rf/64 data 010203\n"
|
|
"lf simfsk -c 64 --high 8 --low 10 -d 010203 --> FSK2a rf/64 data 010203\n\n"
|
|
"lf simfsk -c 50 --high 10 --low 8 -d 1D5559555569A9A555A59569 --> simulate HID Prox tag manually\n"
|
|
"lf simfsk -c 50 --high 10 --low 8 --stt -d 011DB2487E8D811111111111 --> simulate AWID tag manually"
|
|
);
|
|
|
|
void *argtable[] = {
|
|
arg_param_begin,
|
|
arg_u64_0("c", "clk", "<dec>", "manually set clock - can autodetect if using DemodBuffer (default 64)"),
|
|
arg_u64_0(NULL, "low", "<dec>", "manually set larger Field Clock"),
|
|
arg_u64_0(NULL, "high", "<dec>", "manually set smaller Field Clock"),
|
|
arg_lit0(NULL, "stt", "TBD! - STT to enable a gap between playback repetitions (default: no gap)"),
|
|
arg_str0("d", "data", "<hex>", "data to sim - omit to use DemodBuffer"),
|
|
arg_lit0("v", "verbose", "verbose output"),
|
|
arg_param_end
|
|
};
|
|
CLIExecWithReturn(ctx, Cmd, argtable, true);
|
|
uint8_t clk = arg_get_u32_def(ctx, 1, 0);
|
|
uint8_t fclow = arg_get_u32_def(ctx, 2, 0);
|
|
uint8_t fchigh = arg_get_u32_def(ctx, 3, 0);
|
|
bool separator = arg_get_lit(ctx, 4);
|
|
|
|
char raw[65] = {0};
|
|
int raw_len = sizeof(raw) - 1; // CLIGetStrWithReturn does not guarantee string to be null-terminated
|
|
CLIGetStrWithReturn(ctx, 5, (uint8_t *)raw, &raw_len);
|
|
|
|
bool verbose = arg_get_lit(ctx, 6);
|
|
CLIParserFree(ctx);
|
|
|
|
// No args
|
|
if (raw_len == 0 && g_DemodBufferLen == 0) {
|
|
PrintAndLogEx(ERR, "No user supplied data nor inside DemodBuffer");
|
|
return PM3_EINVARG;
|
|
}
|
|
|
|
if (verbose && separator) {
|
|
PrintAndLogEx(INFO, "STT gap isn't implemented yet. Skipping...");
|
|
separator = 0;
|
|
}
|
|
|
|
uint8_t bs[256] = {0x00};
|
|
int bs_len = hextobinarray((char *)bs, raw);
|
|
if (bs_len == 0) {
|
|
// Using data from g_DemodBuffer
|
|
// might be able to autodetect FC and clock from Graphbuffer if using g_DemodBuffer
|
|
// will need clock, fchigh, fclow and bitstream
|
|
PrintAndLogEx(INFO, "No user supplied data, using DemodBuffer...");
|
|
|
|
if (clk == 0 || fchigh == 0 || fclow == 0) {
|
|
int firstClockEdge = 0;
|
|
bool res = fskClocks(&fchigh, &fclow, &clk, &firstClockEdge);
|
|
if (res == false) {
|
|
clk = 0;
|
|
fchigh = 0;
|
|
fclow = 0;
|
|
}
|
|
}
|
|
PrintAndLogEx(DEBUG, "Detected rf/%u, High fc/%u, Low fc/%u, n %zu ", clk, fchigh, fclow, g_DemodBufferLen);
|
|
|
|
} else {
|
|
setDemodBuff(bs, bs_len, 0);
|
|
}
|
|
|
|
//default if not found
|
|
if (clk == 0) {
|
|
clk = 50;
|
|
PrintAndLogEx(DEBUG, "Autodetection of clock failed, falling back to rf/%u", clk);
|
|
}
|
|
|
|
if (fchigh == 0) {
|
|
fchigh = 10;
|
|
PrintAndLogEx(DEBUG, "Autodetection of larger clock failed, falling back to fc/%u", fchigh);
|
|
}
|
|
|
|
if (fclow == 0) {
|
|
fclow = 8;
|
|
PrintAndLogEx(DEBUG, "Autodetection of smaller clock failed, falling back to fc/%u", fclow);
|
|
}
|
|
|
|
size_t size = g_DemodBufferLen;
|
|
if (size > (PM3_CMD_DATA_SIZE - sizeof(lf_fsksim_t))) {
|
|
PrintAndLogEx(WARNING, "DemodBuffer too long for current implementation - length: %zu - max: %zu", size, PM3_CMD_DATA_SIZE - sizeof(lf_fsksim_t));
|
|
PrintAndLogEx(INFO, "Continuing with trimmed down data");
|
|
size = PM3_CMD_DATA_SIZE - sizeof(lf_fsksim_t);
|
|
}
|
|
|
|
lf_fsksim_t *payload = calloc(1, sizeof(lf_fsksim_t) + size);
|
|
if (payload == NULL) {
|
|
PrintAndLogEx(WARNING, "Failed to allocate memory");
|
|
return PM3_EMALLOC;
|
|
}
|
|
|
|
payload->fchigh = fchigh;
|
|
payload->fclow = fclow;
|
|
payload->separator = separator;
|
|
payload->clock = clk;
|
|
memcpy(payload->data, g_DemodBuffer, size);
|
|
|
|
clearCommandBuffer();
|
|
SendCommandNG(CMD_LF_FSK_SIMULATE, (uint8_t *)payload, sizeof(lf_fsksim_t) + size);
|
|
free(payload);
|
|
setClockGrid(clk, 0);
|
|
|
|
return lfsim_wait_check(CMD_LF_FSK_SIMULATE);
|
|
}
|
|
|
|
// sim ask data given clock, invert, manchester or raw, separator
|
|
// - allow pull data from g_DemodBuffer
|
|
int CmdLFaskSim(const char *Cmd) {
|
|
CLIParserContext *ctx;
|
|
CLIParserInit(&ctx, "lf simask",
|
|
"Simulate ASK tag from DemodBuffer or input",
|
|
"lf simask --clk 32 --am -d 0102030405 --> simulate ASK/MAN rf/32\n"
|
|
"lf simask --clk 32 --bi -d 0102030405 --> simulate ASK/BIPHASE rf/32\n\n"
|
|
"lf simask --clk 64 --am -d ffbd8001686f1924 --> simulate a EM410x tag\n"
|
|
"lf simask --clk 64 --am --stt -d 5649533200003F340000001B --> simulate a VISA2K tag"
|
|
);
|
|
|
|
void *argtable[] = {
|
|
arg_param_begin,
|
|
arg_lit0("i", "inv", "invert data"),
|
|
arg_u64_0("c", "clk", "<dec>", "manually set clock - can autodetect if using DemodBuffer (default 64)"),
|
|
arg_lit0(NULL, "bi", "ask/biphase encoding"),
|
|
arg_lit0(NULL, "am", "ask/manchester encoding (default)"),
|
|
arg_lit0(NULL, "ar", "ask/raw encoding"),
|
|
arg_lit0(NULL, "stt", "add t55xx Sequence Terminator gap - default: no gaps (only manchester)"),
|
|
arg_str0("d", "data", "<hex>", "data to sim - omit to use DemodBuffer"),
|
|
arg_lit0("v", "verbose", "verbose output"),
|
|
arg_param_end
|
|
};
|
|
CLIExecWithReturn(ctx, Cmd, argtable, true);
|
|
bool invert = arg_get_lit(ctx, 1);
|
|
uint8_t clk = arg_get_u32_def(ctx, 2, 0);
|
|
bool use_bi = arg_get_lit(ctx, 3);
|
|
bool use_am = arg_get_lit(ctx, 4);
|
|
bool use_ar = arg_get_lit(ctx, 5);
|
|
bool separator = arg_get_lit(ctx, 6);
|
|
|
|
char raw[65] = {0};
|
|
int raw_len = sizeof(raw) - 1; // CLIGetStrWithReturn does not guarantee string to be null-terminated
|
|
CLIGetStrWithReturn(ctx, 7, (uint8_t *)raw, &raw_len);
|
|
|
|
bool verbose = arg_get_lit(ctx, 8);
|
|
CLIParserFree(ctx);
|
|
|
|
if ((use_bi + use_am + use_ar) > 1) {
|
|
PrintAndLogEx(ERR, "only one encoding can be set");
|
|
return PM3_EINVARG;
|
|
}
|
|
|
|
uint8_t encoding = 1;
|
|
if (use_bi)
|
|
encoding = 2;
|
|
else if (use_ar)
|
|
encoding = 0;
|
|
|
|
// No args
|
|
if (raw_len == 0 && g_DemodBufferLen == 0) {
|
|
PrintAndLogEx(ERR, "No user supplied data nor any inside DemodBuffer");
|
|
return PM3_EINVARG;
|
|
}
|
|
|
|
uint8_t bs[256] = {0x00};
|
|
int bs_len = hextobinarray((char *)bs, raw);
|
|
if (bs_len == 0) {
|
|
// Using data from g_DemodBuffer
|
|
// might be able to autodetect FC and clock from Graphbuffer if using g_DemodBuffer
|
|
// will need carrier, clock, and bitstream
|
|
PrintAndLogEx(INFO, "No user supplied data, using DemodBuffer...");
|
|
|
|
if (clk == 0) {
|
|
int res = GetAskClock("0", verbose);
|
|
if (res < 1) {
|
|
clk = 64;
|
|
} else {
|
|
clk = (uint8_t)res;
|
|
}
|
|
}
|
|
|
|
PrintAndLogEx(DEBUG, "Detected rf/%u, n %zu ", clk, g_DemodBufferLen);
|
|
|
|
} else {
|
|
setDemodBuff(bs, bs_len, 0);
|
|
}
|
|
|
|
|
|
if (clk == 0) {
|
|
clk = 32;
|
|
PrintAndLogEx(DEBUG, "Autodetection of clock failed, falling back to rf/%u", clk);
|
|
}
|
|
|
|
if (encoding == 0) {
|
|
clk /= 2; // askraw needs to double the clock speed
|
|
PrintAndLogEx(DEBUG, "ASK/RAW needs half rf. Using rf/%u", clk);
|
|
}
|
|
|
|
size_t size = g_DemodBufferLen;
|
|
if (size > (PM3_CMD_DATA_SIZE - sizeof(lf_asksim_t))) {
|
|
PrintAndLogEx(WARNING, "DemodBuffer too long for current implementation - length: %zu - max: %zu", size, PM3_CMD_DATA_SIZE - sizeof(lf_asksim_t));
|
|
PrintAndLogEx(INFO, "Continuing with trimmed down data");
|
|
size = PM3_CMD_DATA_SIZE - sizeof(lf_asksim_t);
|
|
}
|
|
|
|
lf_asksim_t *payload = calloc(1, sizeof(lf_asksim_t) + size);
|
|
if (payload == NULL) {
|
|
PrintAndLogEx(WARNING, "Failed to allocate memory");
|
|
return PM3_EMALLOC;
|
|
}
|
|
|
|
payload->encoding = encoding;
|
|
payload->invert = invert;
|
|
payload->separator = separator;
|
|
payload->clock = clk;
|
|
memcpy(payload->data, g_DemodBuffer, size);
|
|
|
|
clearCommandBuffer();
|
|
SendCommandNG(CMD_LF_ASK_SIMULATE, (uint8_t *)payload, sizeof(lf_asksim_t) + size);
|
|
free(payload);
|
|
setClockGrid(clk, 0);
|
|
|
|
return lfsim_wait_check(CMD_LF_ASK_SIMULATE);
|
|
}
|
|
|
|
// sim psk data given carrier, clock, invert
|
|
// - allow pull data from g_DemodBuffer or parameters
|
|
int CmdLFpskSim(const char *Cmd) {
|
|
|
|
CLIParserContext *ctx;
|
|
CLIParserInit(&ctx, "lf simpsk",
|
|
"Simulate PSK tag from DemodBuffer or input",
|
|
"lf simpsk -1 --clk 40 --fc 4 -d 01020304 --> simulate PSK1 rf/40 psksub fc/4, data 01020304\n\n"
|
|
"lf simpsk -1 --clk 32 --fc 2 -d a0000000bd989a11 --> simulate a indala tag manually"
|
|
);
|
|
|
|
void *argtable[] = {
|
|
arg_param_begin,
|
|
arg_lit0("1", "psk1", "set PSK1 (default)"),
|
|
arg_lit0("2", "psk2", "set PSK2"),
|
|
arg_lit0("3", "psk3", "set PSK3"),
|
|
arg_lit0("i", "inv", "invert data"),
|
|
arg_u64_0("c", "clk", "<dec>", "manually set clock - can autodetect if using DemodBuffer (default 32)"),
|
|
arg_u64_0(NULL, "fc", "<dec>", "2|4|8 are valid carriers (default 2)"),
|
|
arg_str0("d", "data", "<hex>", "data to sim - omit to use DemodBuffer"),
|
|
arg_lit0("v", "verbose", "verbose output"),
|
|
arg_param_end
|
|
};
|
|
|
|
CLIExecWithReturn(ctx, Cmd, argtable, true);
|
|
|
|
bool use_psk1 = arg_get_lit(ctx, 1);
|
|
bool use_psk2 = arg_get_lit(ctx, 2);
|
|
bool use_psk3 = arg_get_lit(ctx, 3);
|
|
bool invert = arg_get_lit(ctx, 4);
|
|
|
|
uint8_t clk = arg_get_u32_def(ctx, 5, 0);
|
|
uint8_t carrier = arg_get_u32_def(ctx, 6, 2);
|
|
|
|
char raw[65] = {0};
|
|
int raw_len = sizeof(raw) - 1; // CLIGetStrWithReturn does not guarantee string to be null-terminated
|
|
CLIGetStrWithReturn(ctx, 7, (uint8_t *)raw, &raw_len);
|
|
bool verbose = arg_get_lit(ctx, 8);
|
|
|
|
CLIParserFree(ctx);
|
|
|
|
if ((use_psk1 + use_psk2 + use_psk3) > 1) {
|
|
PrintAndLogEx(ERR, "only one PSK mode can be set");
|
|
return PM3_EINVARG;
|
|
}
|
|
|
|
if (carrier != 2 && carrier != 4 && carrier != 8) {
|
|
PrintAndLogEx(ERR, "Wrong carrier given, expected <2|4|8>");
|
|
return PM3_EINVARG;
|
|
}
|
|
|
|
uint8_t psk_type = 1;
|
|
if (use_psk2)
|
|
psk_type = 2;
|
|
if (use_psk3)
|
|
psk_type = 3;
|
|
|
|
// No args
|
|
if (raw_len == 0 && g_DemodBufferLen == 0) {
|
|
PrintAndLogEx(ERR, "No user supplied data nor any inside DemodBuffer");
|
|
return PM3_EINVARG;
|
|
}
|
|
|
|
uint8_t bs[256] = {0x00};
|
|
int bs_len = hextobinarray((char *)bs, raw);
|
|
|
|
if (bs_len == 0) {
|
|
// Using data from g_DemodBuffer
|
|
// might be able to autodetect FC and clock from Graphbuffer if using g_DemodBuffer
|
|
// will need carrier, clock, and bitstream
|
|
PrintAndLogEx(INFO, "No user supplied data, using DemodBuffer...");
|
|
|
|
int res;
|
|
if (clk == 0) {
|
|
res = GetPskClock("", verbose);
|
|
if (res < 1) {
|
|
clk = 32;
|
|
} else {
|
|
clk = (uint8_t)res;
|
|
}
|
|
}
|
|
|
|
if (carrier == 0) {
|
|
res = GetPskCarrier(verbose);
|
|
if (res < 1) {
|
|
carrier = 2;
|
|
} else {
|
|
carrier = (uint8_t)res;
|
|
}
|
|
}
|
|
|
|
PrintAndLogEx(DEBUG, "Detected rf/%u, fc/%u, n %zu ", clk, carrier, g_DemodBufferLen);
|
|
|
|
} else {
|
|
setDemodBuff(bs, bs_len, 0);
|
|
}
|
|
|
|
if (clk == 0) {
|
|
clk = 32;
|
|
PrintAndLogEx(DEBUG, "Autodetection of clock failed, falling back to rf/%u", clk);
|
|
}
|
|
|
|
if (psk_type == 2) {
|
|
//need to convert psk2 to psk1 data before sim
|
|
psk2TOpsk1(g_DemodBuffer, g_DemodBufferLen);
|
|
} else if (psk_type == 3) {
|
|
PrintAndLogEx(INFO, "PSK3 not yet available. Falling back to PSK1");
|
|
}
|
|
|
|
size_t size = g_DemodBufferLen;
|
|
if (size > (PM3_CMD_DATA_SIZE - sizeof(lf_psksim_t))) {
|
|
PrintAndLogEx(WARNING, "DemodBuffer too long for current implementation - length: %zu - max: %zu", size, PM3_CMD_DATA_SIZE - sizeof(lf_psksim_t));
|
|
PrintAndLogEx(INFO, "Continuing with trimmed down data");
|
|
size = PM3_CMD_DATA_SIZE - sizeof(lf_psksim_t);
|
|
}
|
|
|
|
lf_psksim_t *payload = calloc(1, sizeof(lf_psksim_t) + size);
|
|
if (payload == NULL) {
|
|
PrintAndLogEx(WARNING, "Failed to allocate memory");
|
|
return PM3_EMALLOC;
|
|
}
|
|
|
|
payload->carrier = carrier;
|
|
payload->invert = invert;
|
|
payload->clock = clk;
|
|
memcpy(payload->data, g_DemodBuffer, size);
|
|
clearCommandBuffer();
|
|
SendCommandNG(CMD_LF_PSK_SIMULATE, (uint8_t *)payload, sizeof(lf_psksim_t) + size);
|
|
free(payload);
|
|
setClockGrid(clk, 0);
|
|
|
|
return lfsim_wait_check(CMD_LF_PSK_SIMULATE);
|
|
}
|
|
|
|
int CmdLFSimBidir(const char *Cmd) {
|
|
|
|
CLIParserContext *ctx;
|
|
CLIParserInit(&ctx, "lf simbidir",
|
|
"Simulate LF tag with bidirectional data transmission between reader and tag",
|
|
"lf simbidir"
|
|
);
|
|
|
|
void *argtable[] = {
|
|
arg_param_begin,
|
|
arg_param_end
|
|
};
|
|
CLIExecWithReturn(ctx, Cmd, argtable, true);
|
|
CLIParserFree(ctx);
|
|
|
|
// Set ADC to twice the carrier for a slight supersampling
|
|
// HACK: not implemented in ARMSRC.
|
|
PrintAndLogEx(INFO, "Not implemented yet.");
|
|
// SendCommandMIX(CMD_LF_SIMULATE_BIDIR, 47, 384, 0, NULL, 0);
|
|
return PM3_SUCCESS;
|
|
}
|
|
|
|
// ICEMAN, Verichip is Animal tag. Tested against correct reader
|
|
/*
|
|
|
|
int CmdVchDemod(const char *Cmd) {
|
|
|
|
if (g_GraphTraceLen < 4096) {
|
|
PrintAndLogEx(DEBUG, "debug; VchDemod - too few samples");
|
|
return PM3_EINVARG;
|
|
}
|
|
|
|
// Is this the entire sync pattern, or does this also include some
|
|
// data bits that happen to be the same everywhere? That would be
|
|
// lovely to know.
|
|
static const int SyncPattern[] = {
|
|
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
|
1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
|
|
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
|
1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
|
|
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
|
1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
|
|
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
|
1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
|
|
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
|
1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
|
|
};
|
|
|
|
// iceman, using correlate as preamble detect seems way better than our current memcompare
|
|
|
|
// So first, we correlate for the sync pattern, and mark that.
|
|
int bestCorrel = 0, bestPos = 0;
|
|
int i, j, sum = 0;
|
|
|
|
// It does us no good to find the sync pattern, with fewer than 2048 samples after it.
|
|
|
|
for (i = 0; i < (g_GraphTraceLen - 2048); i++) {
|
|
for (j = 0; j < ARRAYLEN(SyncPattern); j++) {
|
|
sum += g_GraphBuffer[i + j] * SyncPattern[j];
|
|
}
|
|
if (sum > bestCorrel) {
|
|
bestCorrel = sum;
|
|
bestPos = i;
|
|
}
|
|
}
|
|
PrintAndLogEx(NORMAL, "best sync at %d [metric %d]", bestPos, bestCorrel);
|
|
|
|
char bits[257];
|
|
bits[256] = '\0';
|
|
|
|
int worst = INT_MAX, worstPos = 0;
|
|
|
|
for (i = 0; i < 2048; i += 8) {
|
|
sum = 0;
|
|
for (j = 0; j < 8; j++)
|
|
sum += g_GraphBuffer[bestPos + i + j];
|
|
|
|
if (sum < 0)
|
|
bits[i / 8] = '.';
|
|
else
|
|
bits[i / 8] = '1';
|
|
|
|
if (abs(sum) < worst) {
|
|
worst = abs(sum);
|
|
worstPos = i;
|
|
}
|
|
}
|
|
PrintAndLogEx(NORMAL, "bits:");
|
|
PrintAndLogEx(NORMAL, "%s", bits);
|
|
PrintAndLogEx(NORMAL, "worst metric: %d at pos %d", worst, worstPos);
|
|
|
|
// clone
|
|
if (strcmp(Cmd, "clone") == 0) {
|
|
g_GraphTraceLen = 0;
|
|
char *s;
|
|
for (s = bits; *s; s++) {
|
|
for (j = 0; j < 16; j++) {
|
|
g_GraphBuffer[g_GraphTraceLen++] = (*s == '1') ? 1 : 0;
|
|
}
|
|
}
|
|
RepaintGraphWindow();
|
|
}
|
|
return PM3_SUCCESS;
|
|
}
|
|
*/
|
|
|
|
static bool check_chiptype(bool getDeviceData) {
|
|
|
|
bool retval = false;
|
|
|
|
if (getDeviceData == false) {
|
|
return retval;
|
|
}
|
|
|
|
//Save the state of the Graph and Demod Buffers
|
|
buffer_savestate_t saveState_gb = save_bufferS32(g_GraphBuffer, g_GraphTraceLen);
|
|
saveState_gb.offset = g_GridOffset;
|
|
buffer_savestate_t saveState_db = save_buffer8(g_DemodBuffer, g_DemodBufferLen);
|
|
saveState_db.clock = g_DemodClock;
|
|
saveState_db.offset = g_DemodStartIdx;
|
|
|
|
PrintAndLogEx(INFO, "Searching for auth LF and special cases...");
|
|
|
|
// check for em4x05/em4x69 chips first
|
|
uint32_t word = 0;
|
|
if (IfPm3EM4x50() && em4x05_isblock0(&word)) {
|
|
PrintAndLogEx(SUCCESS, "Chipset... " _GREEN_("EM4x05 / EM4x69"));
|
|
PrintAndLogEx(HINT, "Hint: Try `" _YELLOW_("lf em 4x05") "` commands");
|
|
retval = true;
|
|
goto out;
|
|
}
|
|
|
|
// check for t55xx chip...
|
|
if (tryDetectP1(true)) {
|
|
PrintAndLogEx(SUCCESS, "Chipset... " _GREEN_("T55xx"));
|
|
PrintAndLogEx(HINT, "Hint: Try `" _YELLOW_("lf t55xx") "` commands");
|
|
retval = true;
|
|
goto out;
|
|
}
|
|
|
|
|
|
if (IfPm3Hitag()) {
|
|
|
|
// Hitag 2
|
|
if (ht2_read_uid() == PM3_SUCCESS) {
|
|
PrintAndLogEx(SUCCESS, "Chipset... " _GREEN_("Hitag 2"));
|
|
PrintAndLogEx(HINT, "Hint: Try `" _YELLOW_("lf hitag") "` commands");
|
|
retval = true;
|
|
goto out;
|
|
}
|
|
|
|
// Hitag S
|
|
if (read_hts_uid() == PM3_SUCCESS) {
|
|
PrintAndLogEx(SUCCESS, "Chipset... " _GREEN_("Hitag 1/S / 82xx"));
|
|
PrintAndLogEx(HINT, "Hint: Try `" _YELLOW_("lf hitag hts") "` commands");
|
|
retval = true;
|
|
goto out;
|
|
}
|
|
|
|
// Hitag µ
|
|
if (read_htu_uid() == PM3_SUCCESS) {
|
|
PrintAndLogEx(SUCCESS, "Chipset... " _GREEN_("Hitag µ / 8265"));
|
|
PrintAndLogEx(HINT, "Hint: Try `" _YELLOW_("lf hitag htu") "` commands");
|
|
retval = true;
|
|
goto out;
|
|
}
|
|
}
|
|
|
|
|
|
#if !defined ICOPYX
|
|
// check for em4x50 chips
|
|
if (IfPm3EM4x50() && detect_4x50_block()) {
|
|
PrintAndLogEx(SUCCESS, "Chipset... " _GREEN_("EM4x50"));
|
|
PrintAndLogEx(HINT, "Hint: Try `" _YELLOW_("lf em 4x50") "` commands");
|
|
retval = true;
|
|
goto out;
|
|
}
|
|
|
|
// check for em4x70 chips
|
|
if (IfPm3EM4x70() && detect_4x70_block()) {
|
|
PrintAndLogEx(SUCCESS, "Chipset... " _GREEN_("EM4x70"));
|
|
PrintAndLogEx(HINT, "Hint: Try `" _YELLOW_("lf em 4x70") "` commands");
|
|
retval = true;
|
|
goto out;
|
|
}
|
|
#endif
|
|
|
|
PrintAndLogEx(INFO, "Couldn't identify a chipset");
|
|
out:
|
|
restore_buffer8(saveState_db, g_DemodBuffer);
|
|
g_DemodClock = saveState_db.clock;
|
|
g_DemodStartIdx = saveState_db.offset;
|
|
|
|
restore_bufferS32(saveState_gb, g_GraphBuffer);
|
|
g_GridOffset = saveState_gb.offset;
|
|
|
|
return retval;
|
|
}
|
|
|
|
static int check_autocorrelate(const char *prefix, int clock) {
|
|
|
|
PrintAndLogEx(NORMAL, "");
|
|
PrintAndLogEx(INFO, _CYAN_("%s - auto correlations"), prefix);
|
|
for (int win = 2000; win < 30000; win += 2000) {
|
|
int samples = AutoCorrelate(g_GraphBuffer, g_GraphBuffer, g_GraphTraceLen, win, false, false);
|
|
if (samples == -1) {
|
|
continue;
|
|
}
|
|
|
|
if ((samples > 1) && (clock > 0)) {
|
|
|
|
// If we got a field clock / bit rate from before
|
|
// we can use it for predict number of repeating bytes within
|
|
|
|
int bytes = (samples / (8 * clock));
|
|
int mod = (bytes % 4);
|
|
if (mod)
|
|
bytes++;
|
|
|
|
int blocks = (bytes / 4);
|
|
PrintAndLogEx(SUCCESS, " " _YELLOW_("%d") " samples / 8 bits / " _YELLOW_("%d") " clock", samples, clock);
|
|
PrintAndLogEx(SUCCESS, " " _YELLOW_("%d") " bytes repeating", bytes);
|
|
|
|
if (blocks < 7) {
|
|
PrintAndLogEx(SUCCESS, " " _YELLOW_("%d") " block%c", blocks, (blocks == 1) ? ' ' : 's');
|
|
PrintAndLogEx(NORMAL, "");
|
|
return PM3_SUCCESS;
|
|
} else {
|
|
PrintAndLogEx(INFO, " " _YELLOW_("%d") " blocks ( too large for T5577 )", blocks);
|
|
PrintAndLogEx(NORMAL, "");
|
|
return PM3_EFAILED;
|
|
}
|
|
}
|
|
}
|
|
PrintAndLogEx(NORMAL, "");
|
|
return PM3_EFAILED;
|
|
}
|
|
|
|
static int lf_relay_tag(uint64_t samples, uint16_t port) {
|
|
|
|
relay_socket_t listen_sock = RELAY_SOCKET_INVALID;
|
|
relay_socket_t client = relay_listen_accept(port, &listen_sock);
|
|
if (client == RELAY_SOCKET_INVALID) {
|
|
return PM3_EFAILED;
|
|
}
|
|
|
|
while (true) {
|
|
|
|
if (kbd_enter_pressed()) {
|
|
SendCommandNG(CMD_BREAK_LOOP, NULL, 0);
|
|
PrintAndLogEx(DEBUG, "\naborted via keyboard!");
|
|
msleep(300);
|
|
break;
|
|
}
|
|
|
|
lf_read_internal(false, false, samples, false);
|
|
|
|
if ((g_GraphTraceLen > 1000) && (getSignalProperties()->isnoise == false)) {
|
|
|
|
PrintAndLogEx(INFO, "Tag detected! Sending %zu samples to Client...", g_GraphTraceLen);
|
|
|
|
uint32_t len = (uint32_t)g_GraphTraceLen;
|
|
if (relay_send_all(client, &len, sizeof(len)) != 0) {
|
|
break;
|
|
}
|
|
|
|
if (relay_send_all(client, g_GraphBuffer, len * sizeof(int32_t)) != 0) {
|
|
break;
|
|
}
|
|
|
|
msleep(500);
|
|
}
|
|
}
|
|
|
|
relay_close(client);
|
|
relay_close(listen_sock);
|
|
return PM3_SUCCESS;
|
|
}
|
|
|
|
static int lf_relay_rdr(const char *ip, uint16_t port) {
|
|
|
|
relay_socket_t sock = relay_connect(ip, port);
|
|
if (sock == RELAY_SOCKET_INVALID) {
|
|
return PM3_EFAILED;
|
|
}
|
|
|
|
PrintAndLogEx(INFO, "Relay connected to %s:%u", ip, port);
|
|
PrintAndLogEx(INFO, "Waiting for signal...");
|
|
PrintAndLogEx(INFO, "Press " _GREEN_("<Enter>") " to exit");
|
|
PrintAndLogEx(NORMAL, "");
|
|
|
|
bool running = true;
|
|
while (running) {
|
|
|
|
if (kbd_enter_pressed()) {
|
|
SendCommandNG(CMD_BREAK_LOOP, NULL, 0);
|
|
PrintAndLogEx(DEBUG, "\naborted via keyboard!");
|
|
msleep(300);
|
|
break;
|
|
}
|
|
|
|
uint32_t incoming_len = 0;
|
|
int n = relay_recv_all(sock, &incoming_len, sizeof(incoming_len));
|
|
if (n < 0) {
|
|
break;
|
|
}
|
|
|
|
if (incoming_len == 0) {
|
|
continue;
|
|
}
|
|
|
|
if (incoming_len > MAX_GRAPH_TRACE_LEN) {
|
|
PrintAndLogEx(ERR, "Received length " _RED_("%u") " exceeds buffer size %u, dropping", incoming_len, (uint32_t)MAX_GRAPH_TRACE_LEN);
|
|
break;
|
|
}
|
|
|
|
PrintAndLogEx(INFO, "Received " _YELLOW_("%u") " samples. Processing...", incoming_len);
|
|
|
|
int rx = relay_recv_all(sock, g_GraphBuffer, incoming_len * sizeof(int32_t));
|
|
if (rx < 0) {
|
|
PrintAndLogEx(ERR, "Short read receiving sample data");
|
|
break;
|
|
}
|
|
|
|
// if previous simulation running, we need to break it.
|
|
SendCommandNG(CMD_BREAK_LOOP, NULL, 0);
|
|
msleep(300);
|
|
|
|
g_GraphTraceLen = incoming_len;
|
|
lf_chk_bitstream();
|
|
lfsim_upload_gb();
|
|
|
|
struct {
|
|
uint16_t len;
|
|
uint16_t gap;
|
|
} PACKED payload;
|
|
payload.len = (g_GraphTraceLen > UINT16_MAX) ? UINT16_MAX : (uint16_t)g_GraphTraceLen;
|
|
payload.gap = 0;
|
|
|
|
clearCommandBuffer();
|
|
SendCommandNG(CMD_LF_SIMULATE, (uint8_t *)&payload, sizeof(payload));
|
|
|
|
PrintAndLogEx(SUCCESS, "Simulation active.");
|
|
}
|
|
|
|
relay_close(sock);
|
|
return PM3_SUCCESS;
|
|
}
|
|
|
|
int CmdLFRelay(const char *Cmd) {
|
|
CLIParserContext *ctx;
|
|
CLIParserInit(&ctx, "lf relay",
|
|
"Relay LF signal between two Proxmark3 devices over TCP.\n"
|
|
"By default it uses PORT 8000 and uses 40000 samples from Graphbuffer\n"
|
|
" --rdr : Reading device, act as IP client and reads LF tag and sends data\n"
|
|
" --tag : Simulation device, act as IP server and simulates relayed data\n",
|
|
_WHITE_("Device A, reading LF tag, client") "\n"
|
|
"lf relay --rdr --ip 192.168.1.141 -> Client, connect to IP 192.168.1.141:8000\n"
|
|
"lf relay --rdr --ip 192.168.1.141 -p 18111 -> Client, connect to IP 192.168.1.141:18111 \n\n"
|
|
_WHITE_("Device B, simulate LF tag, server") "\n"
|
|
"lf relay --tag -p 8111 -> Server listening port 8111, recv 40000 samples\n"
|
|
"lf relay --tag -s 10000 -> Server listening port 8000, recv 10000 samples\n"
|
|
);
|
|
|
|
void *argtable[] = {
|
|
arg_param_begin,
|
|
arg_lit0(NULL, "tag", "Simulation device, act as Server"),
|
|
arg_lit0(NULL, "rdr", "Sniffing device, act as client"),
|
|
arg_str0("i", "ip", "<ipaddr>", "Target IPv4 address to send data to. Used with `--rdr`"),
|
|
arg_u64_0("s", "samples", "<dec>", "Number of samples to collect (def: 40000)"),
|
|
arg_u64_0("p", "port", "<dec>", "Port number (def: 8000)"),
|
|
arg_param_end
|
|
};
|
|
|
|
CLIExecWithReturn(ctx, Cmd, argtable, true);
|
|
|
|
bool is_tag = arg_get_lit(ctx, 1);
|
|
bool is_rdr = arg_get_lit(ctx, 2);
|
|
|
|
int iplen = 0;
|
|
char ip[256] = { 0 };
|
|
CLIParamStrToBuf(arg_get_str(ctx, 3), (uint8_t *)ip, sizeof(ip), &iplen);
|
|
|
|
uint64_t samples = arg_get_u64_def(ctx, 4, 40000);
|
|
uint16_t port = arg_get_u32_def(ctx, 5, 8000) & 0xFFFF;
|
|
|
|
CLIParserFree(ctx);
|
|
|
|
// sanitize
|
|
if ((is_tag + is_rdr) == 0) {
|
|
PrintAndLogEx(ERR, "Specify either --tag or --rdr");
|
|
return PM3_EINVARG;
|
|
}
|
|
|
|
if (is_rdr && (iplen == 0)) {
|
|
PrintAndLogEx(ERR, "IP address is empty");
|
|
PrintAndLogEx(HINT, "try `" _YELLOW_("lf relay --rdr --ip <ip>") "`");
|
|
return PM3_EINVARG;
|
|
}
|
|
|
|
// main
|
|
if (is_tag) {
|
|
return lf_relay_tag(samples, port);
|
|
}
|
|
|
|
if (is_rdr) {
|
|
return lf_relay_rdr(ip, port);
|
|
}
|
|
|
|
return PM3_SUCCESS;
|
|
}
|
|
|
|
int CmdLFfind(const char *Cmd) {
|
|
|
|
CLIParserContext *ctx;
|
|
CLIParserInit(&ctx, "lf search",
|
|
"Read and search for valid known tag. For offline mode, you can `data load` first then search.",
|
|
"lf search -> try reading data from tag & search for known tag\n"
|
|
"lf search -u -> try reading data from tag & search for known and unknown tag\n"
|
|
"lf search -1 -> use data from the GraphBuffer & search for known tag\n"
|
|
"lf search -1uc -> use data from the GraphBuffer & search for known and unknown tag\n"
|
|
);
|
|
|
|
void *argtable[] = {
|
|
arg_param_begin,
|
|
arg_lit0("1", NULL, "Use data from Graphbuffer to search (offline mode)"),
|
|
arg_lit0("c", NULL, "Continue searching after successful match"),
|
|
arg_lit0("u", NULL, "Search for unknown tags"),
|
|
arg_param_end
|
|
};
|
|
CLIExecWithReturn(ctx, Cmd, argtable, true);
|
|
bool use_gb = arg_get_lit(ctx, 1);
|
|
bool search_cont = arg_get_lit(ctx, 2);
|
|
bool search_unk = arg_get_lit(ctx, 3);
|
|
CLIParserFree(ctx);
|
|
int found = 0;
|
|
bool is_online = (g_session.pm3_present && (use_gb == false));
|
|
if (is_online) {
|
|
lf_read(false, 30000);
|
|
}
|
|
|
|
size_t min_length = 2000;
|
|
if (g_GraphTraceLen < min_length) {
|
|
PrintAndLogEx(FAILED, "Data in Graphbuffer was too small.");
|
|
return PM3_ESOFT;
|
|
}
|
|
|
|
if (search_cont) {
|
|
PrintAndLogEx(INFO, "Continue searching after successful match");
|
|
}
|
|
|
|
PrintAndLogEx(NORMAL, "");
|
|
PrintAndLogEx(INFO, "Note: False Positives " _YELLOW_("ARE") " possible");
|
|
PrintAndLogEx(INFO, "");
|
|
PrintAndLogEx(INFO, _CYAN_("Checking for known tags..."));
|
|
PrintAndLogEx(INFO, "");
|
|
|
|
int retval = PM3_SUCCESS;
|
|
|
|
// only run these tests if device is online
|
|
if (is_online) {
|
|
|
|
if (IfPm3Hitag()) {
|
|
if (ht2_read_paxton() == PM3_SUCCESS) {
|
|
PrintAndLogEx(SUCCESS, "\nValid " _GREEN_("Paxton ID") " found!");
|
|
if (search_cont) {
|
|
found++;
|
|
} else {
|
|
goto out;
|
|
}
|
|
}
|
|
}
|
|
|
|
#if !defined ICOPYX
|
|
if (IfPm3EM4x50()) {
|
|
if (read_em4x50_uid() == PM3_SUCCESS) {
|
|
PrintAndLogEx(SUCCESS, "\nValid " _GREEN_("EM4x50 ID") " found!");
|
|
if (search_cont) {
|
|
found++;
|
|
} else {
|
|
return PM3_SUCCESS;
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
|
|
// only run if graphbuffer is just noise as it should be for hitag
|
|
// The improved noise detection will find Cotag.
|
|
if (getSignalProperties()->isnoise) {
|
|
|
|
PrintAndLogEx(INPLACE, "Searching for MOTOROLA tag...");
|
|
if (readMotorolaUid()) {
|
|
PrintAndLogEx(SUCCESS, "\nValid " _GREEN_("Motorola FlexPass ID") " found!");
|
|
if (search_cont) {
|
|
found++;
|
|
} else {
|
|
return PM3_SUCCESS;
|
|
}
|
|
}
|
|
|
|
PrintAndLogEx(INPLACE, "Searching for COTAG tag...");
|
|
if (readCOTAGUid()) {
|
|
PrintAndLogEx(SUCCESS, "\nValid " _GREEN_("COTAG ID") " found!");
|
|
if (search_cont) {
|
|
found++;
|
|
} else {
|
|
return PM3_SUCCESS;
|
|
}
|
|
}
|
|
|
|
PrintAndLogEx(NORMAL, "");
|
|
|
|
// identify chipset
|
|
bool lf_special_search = check_chiptype(is_online);
|
|
if (lf_special_search) {
|
|
found++;
|
|
} else {
|
|
PrintAndLogEx(DEBUG, "Automatic chip type detection " _RED_("failed"));
|
|
}
|
|
|
|
if (found == 0) {
|
|
PrintAndLogEx(HINT, "Hint: try `" _YELLOW_("hf search") "` - since tag might not be LF");
|
|
}
|
|
|
|
PrintAndLogEx(NORMAL, "");
|
|
if (search_cont == 0) {
|
|
return PM3_ESOFT;
|
|
}
|
|
}
|
|
}
|
|
|
|
// ask / man
|
|
if (demodEM410x(true) == PM3_SUCCESS) {
|
|
PrintAndLogEx(SUCCESS, "\nValid " _GREEN_("EM410x ID") " found!");
|
|
if (search_cont) {
|
|
found++;
|
|
} else {
|
|
goto out;
|
|
}
|
|
}
|
|
if (demodDestron(true) == PM3_SUCCESS) { // to do before HID
|
|
PrintAndLogEx(SUCCESS, "\nValid " _GREEN_("FDX-A FECAVA Destron ID") " found!");
|
|
if (search_cont) {
|
|
found++;
|
|
} else {
|
|
goto out;
|
|
}
|
|
}
|
|
if (demodGallagher(true) == PM3_SUCCESS) {
|
|
PrintAndLogEx(SUCCESS, "\nValid " _GREEN_("GALLAGHER ID") " found!");
|
|
if (search_cont) {
|
|
found++;
|
|
} else {
|
|
goto out;
|
|
}
|
|
}
|
|
if (demodNoralsy(true) == PM3_SUCCESS) {
|
|
PrintAndLogEx(SUCCESS, "\nValid " _GREEN_("Noralsy ID") " found!");
|
|
if (search_cont) {
|
|
found++;
|
|
} else {
|
|
goto out;
|
|
}
|
|
}
|
|
if (demodPresco(true) == PM3_SUCCESS) {
|
|
PrintAndLogEx(SUCCESS, "\nValid " _GREEN_("Presco ID") " found!");
|
|
if (search_cont) {
|
|
found++;
|
|
} else {
|
|
goto out;
|
|
}
|
|
}
|
|
if (demodSecurakey(true) == PM3_SUCCESS) {
|
|
PrintAndLogEx(SUCCESS, "\nValid " _GREEN_("Securakey ID") " found!");
|
|
if (search_cont) {
|
|
found++;
|
|
} else {
|
|
goto out;
|
|
}
|
|
}
|
|
if (demodViking(true) == PM3_SUCCESS) {
|
|
PrintAndLogEx(SUCCESS, "\nValid " _GREEN_("Viking ID") " found!");
|
|
if (search_cont) {
|
|
found++;
|
|
} else {
|
|
goto out;
|
|
}
|
|
}
|
|
if (demodVisa2k(true) == PM3_SUCCESS) {
|
|
PrintAndLogEx(SUCCESS, "\nValid " _GREEN_("Visa2000 ID") " found!");
|
|
if (search_cont) {
|
|
found++;
|
|
} else {
|
|
goto out;
|
|
}
|
|
}
|
|
|
|
// ask / bi
|
|
if (demodFDXB(true) == PM3_SUCCESS) {
|
|
PrintAndLogEx(SUCCESS, "\nValid " _GREEN_("FDX-B ID") " found!");
|
|
if (search_cont) {
|
|
found++;
|
|
} else {
|
|
goto out;
|
|
}
|
|
}
|
|
if (demodJablotron(true) == PM3_SUCCESS) {
|
|
PrintAndLogEx(SUCCESS, "\nValid " _GREEN_("Jablotron ID") " found!");
|
|
if (search_cont) {
|
|
found++;
|
|
} else {
|
|
goto out;
|
|
}
|
|
}
|
|
if (demodGuard(true) == PM3_SUCCESS) {
|
|
PrintAndLogEx(SUCCESS, "\nValid " _GREEN_("Guardall G-Prox II ID") " found!");
|
|
if (search_cont) {
|
|
found++;
|
|
} else {
|
|
goto out;
|
|
}
|
|
}
|
|
if (demodNedap(true) == PM3_SUCCESS) {
|
|
PrintAndLogEx(SUCCESS, "\nValid " _GREEN_("NEDAP ID") " found!");
|
|
if (search_cont) {
|
|
found++;
|
|
} else {
|
|
goto out;
|
|
}
|
|
}
|
|
|
|
// nrz
|
|
if (demodPac(true) == PM3_SUCCESS) {
|
|
PrintAndLogEx(SUCCESS, "\nValid " _GREEN_("PAC/Stanley ID") " found!");
|
|
if (search_cont) {
|
|
found++;
|
|
} else {
|
|
goto out;
|
|
}
|
|
}
|
|
|
|
// fsk
|
|
if (demodHID(true) == PM3_SUCCESS) {
|
|
PrintAndLogEx(SUCCESS, "\nValid " _GREEN_("HID Prox ID") " found!");
|
|
if (search_cont) {
|
|
found++;
|
|
} else {
|
|
goto out;
|
|
}
|
|
}
|
|
if (demodAWID(true) == PM3_SUCCESS) {
|
|
PrintAndLogEx(SUCCESS, "\nValid " _GREEN_("AWID ID") " found!");
|
|
if (search_cont) {
|
|
found++;
|
|
} else {
|
|
goto out;
|
|
}
|
|
}
|
|
if (demodIOProx(true) == PM3_SUCCESS) {
|
|
PrintAndLogEx(SUCCESS, "\nValid " _GREEN_("IO Prox ID") " found!");
|
|
if (search_cont) {
|
|
found++;
|
|
} else {
|
|
goto out;
|
|
}
|
|
}
|
|
if (demodPyramid(true) == PM3_SUCCESS) {
|
|
PrintAndLogEx(SUCCESS, "\nValid " _GREEN_("Pyramid ID") " found!");
|
|
if (search_cont) {
|
|
found++;
|
|
} else {
|
|
goto out;
|
|
}
|
|
}
|
|
if (demodParadox(true, false) == PM3_SUCCESS) {
|
|
PrintAndLogEx(SUCCESS, "\nValid " _GREEN_("Paradox ID") " found!");
|
|
if (search_cont) {
|
|
found++;
|
|
} else {
|
|
goto out;
|
|
}
|
|
}
|
|
|
|
// psk
|
|
if (demodIdteck(NULL, true) == PM3_SUCCESS) {
|
|
PrintAndLogEx(SUCCESS, "\nValid " _GREEN_("Idteck ID") " found!");
|
|
if (search_cont) {
|
|
found++;
|
|
} else {
|
|
goto out;
|
|
}
|
|
}
|
|
if (demodKeri(true) == PM3_SUCCESS) {
|
|
PrintAndLogEx(SUCCESS, "\nValid " _GREEN_("KERI ID") " found!");
|
|
if (search_cont) {
|
|
found++;
|
|
} else {
|
|
goto out;
|
|
}
|
|
}
|
|
if (demodNexWatch(true) == PM3_SUCCESS) {
|
|
PrintAndLogEx(SUCCESS, "\nValid " _GREEN_("NexWatch ID") " found!");
|
|
if (search_cont) {
|
|
found++;
|
|
} else {
|
|
goto out;
|
|
}
|
|
}
|
|
if (demodIndala(true) == PM3_SUCCESS) {
|
|
PrintAndLogEx(SUCCESS, "\nValid " _GREEN_("Indala ID") " found!");
|
|
if (search_cont) {
|
|
found++;
|
|
} else {
|
|
goto out;
|
|
}
|
|
}
|
|
/*
|
|
if (demodTI() == PM3_SUCCESS) {
|
|
PrintAndLogEx(SUCCESS, "\nValid " _GREEN_("Texas Instrument ID") " found!");
|
|
if (search_cont) {
|
|
found++;
|
|
} else {
|
|
goto out;
|
|
}
|
|
}
|
|
if (demodFermax() == PM3_SUCCESS) {
|
|
PrintAndLogEx(SUCCESS, "\nValid " _GREEN_("Fermax ID") " found!");
|
|
if (search_cont) {
|
|
found++;
|
|
} else {
|
|
goto out;
|
|
}
|
|
}
|
|
*/
|
|
if (demodTrovan(false) == PM3_SUCCESS) {
|
|
PrintAndLogEx(SUCCESS, "\nValid " _GREEN_("Trovan ID") " found!");
|
|
if (search_cont) {
|
|
found++;
|
|
} else {
|
|
goto out;
|
|
}
|
|
}
|
|
|
|
// COTAG last, and only when the capture actually looks like one.
|
|
if (found == 0) {
|
|
|
|
double *sig = pm3_extract(g_GraphBuffer, g_GraphTraceLen, 0, g_GraphTraceLen);
|
|
if (sig != NULL) {
|
|
|
|
const bool switched = pm3_is_switched_carrier(sig, g_GraphTraceLen);
|
|
free(sig);
|
|
|
|
if (switched && demodCOTAG(false, -1, -1) == PM3_SUCCESS) {
|
|
PrintAndLogEx(SUCCESS, "\nValid " _GREEN_("COTAG ID") " found!");
|
|
if (search_cont) {
|
|
found++;
|
|
} else {
|
|
goto out;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (found == 0) {
|
|
PrintAndLogEx(FAILED, _RED_("No known 125/134 kHz tags found!"));
|
|
}
|
|
|
|
if (search_unk) {
|
|
|
|
// test unknown tag formats (raw mode)
|
|
PrintAndLogEx(INFO, _CYAN_("Checking for unknown tags...") "\n");
|
|
|
|
uint8_t ones[] = {
|
|
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
|
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
|
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
|
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
|
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
|
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
|
};
|
|
|
|
// FSK
|
|
PrintAndLogEx(INFO, "FSK clock.......... " NOLF);
|
|
int clock = GetFskClock("", false);
|
|
if (clock) {
|
|
PrintAndLogEx(NORMAL, _GREEN_("detected"));
|
|
if (FSKrawDemod(0, 0, 0, 0, true) == PM3_SUCCESS) {
|
|
check_autocorrelate("FSK", clock);
|
|
found++;
|
|
} else {
|
|
PrintAndLogEx(INFO, "FSK demodulation... " _RED_("failed"));
|
|
}
|
|
} else {
|
|
PrintAndLogEx(NORMAL, _RED_("no"));
|
|
}
|
|
|
|
// ASK
|
|
PrintAndLogEx(INFO, "ASK clock.......... " NOLF);
|
|
clock = GetAskClock("", false);
|
|
if (clock && clock > 8) {
|
|
PrintAndLogEx(NORMAL, _GREEN_("detected"));
|
|
bool st = true;
|
|
if (ASKDemod_ext(0, 0, 0, 0, false, true, false, 1, &st) == PM3_SUCCESS) {
|
|
PrintAndLogEx(NORMAL, "");
|
|
PrintAndLogEx(INFO, _GREEN_("ASK") " modulation / Manchester encoding detected!");
|
|
PrintAndLogEx(INFO, " could also be ASK/Biphase - try " _YELLOW_("'data rawdemod --ab'"));
|
|
check_autocorrelate("ASK", clock);
|
|
found++;
|
|
} else {
|
|
PrintAndLogEx(INFO, "ASK demodulation... " _RED_("failed"));
|
|
}
|
|
} else {
|
|
PrintAndLogEx(NORMAL, _RED_("no"));
|
|
}
|
|
|
|
// NZR
|
|
PrintAndLogEx(INFO, "NRZ clock.......... " NOLF);
|
|
clock = GetNrzClock("", false);
|
|
if (clock && clock > 8) {
|
|
PrintAndLogEx(NORMAL, _GREEN_("detected"));
|
|
if (NRZrawDemod(0, 0, 0, true) == PM3_SUCCESS) {
|
|
|
|
int min = MIN(g_DemodBufferLen, sizeof(ones));
|
|
// if demodulated binary is only 1, skip autocorrect
|
|
if (memcmp(g_DemodBuffer, ones, min) != 0) {
|
|
check_autocorrelate("NRZ", clock);
|
|
found++;
|
|
} else {
|
|
PrintAndLogEx(INFO, "NRZ ............... " _RED_("false positive"));
|
|
PrintAndLogEx(NORMAL, "");
|
|
}
|
|
} else {
|
|
PrintAndLogEx(INFO, "NRZ demodulation... " _RED_("failed"));
|
|
}
|
|
} else {
|
|
PrintAndLogEx(NORMAL, _RED_("no"));
|
|
}
|
|
|
|
// PSK
|
|
PrintAndLogEx(INFO, "PSK clock.......... " NOLF);
|
|
clock = GetPskClock("", false);
|
|
if (clock) {
|
|
PrintAndLogEx(NORMAL, _GREEN_("detected"));
|
|
if (CmdPSK1rawDemod("") == PM3_SUCCESS) {
|
|
PrintAndLogEx(INFO, "Possible " _GREEN_("PSK1") " modulation detected!");
|
|
PrintAndLogEx(INFO, " Could also be PSK2 - try " _YELLOW_("'data rawdemod --p2'"));
|
|
PrintAndLogEx(INFO, " Could also be PSK3 - [currently not supported]");
|
|
PrintAndLogEx(INFO, " Could also be NRZ - try " _YELLOW_("'data rawdemod --nr"));
|
|
check_autocorrelate("PSK", clock);
|
|
found++;
|
|
} else {
|
|
PrintAndLogEx(INFO, "PSK demodulation... " _RED_("failed"));
|
|
}
|
|
} else {
|
|
PrintAndLogEx(NORMAL, _RED_("no"));
|
|
}
|
|
|
|
if (found == 0) {
|
|
PrintAndLogEx(FAILED, _RED_("Failed to demodulated signal"));
|
|
}
|
|
}
|
|
|
|
if (found == 0) {
|
|
retval = PM3_ESOFT;
|
|
}
|
|
|
|
out:
|
|
// identify chipset
|
|
if (check_chiptype(is_online) == false) {
|
|
PrintAndLogEx(DEBUG, "Automatic chip type detection " _RED_("failed"));
|
|
}
|
|
return retval;
|
|
}
|
|
|
|
static command_t CommandTable[] = {
|
|
{"help", CmdHelp, AlwaysAvailable, "This help"},
|
|
{"-----------", CmdHelp, AlwaysAvailable, "-------------- " _CYAN_("Low Frequency") " --------------"},
|
|
{"awid", CmdLFAWID, AlwaysAvailable, "{ AWID RFIDs... }"},
|
|
{"cotag", CmdLFCOTAG, AlwaysAvailable, "{ COTAG CHIPs... }"},
|
|
{"destron", CmdLFDestron, AlwaysAvailable, "{ FDX-A Destron RFIDs... }"},
|
|
{"em", CmdLFEM, AlwaysAvailable, "{ EM CHIPs & RFIDs... }"},
|
|
{"fdxb", CmdLFFdxB, AlwaysAvailable, "{ FDX-B RFIDs... }"},
|
|
{"gallagher", CmdLFGallagher, AlwaysAvailable, "{ GALLAGHER RFIDs... }"},
|
|
{"gproxii", CmdLFGuard, AlwaysAvailable, "{ Guardall Prox II RFIDs... }"},
|
|
{"hid", CmdLFHID, AlwaysAvailable, "{ HID Prox RFIDs... }"},
|
|
{"hitag", CmdLFHitag, AlwaysAvailable, "{ Hitag CHIPs... }"},
|
|
{"idteck", CmdLFIdteck, AlwaysAvailable, "{ Idteck RFIDs... }"},
|
|
{"indala", CmdLFINDALA, AlwaysAvailable, "{ Indala RFIDs... }"},
|
|
{"io", CmdLFIO, AlwaysAvailable, "{ ioProx RFIDs... }"},
|
|
{"jablotron", CmdLFJablotron, AlwaysAvailable, "{ Jablotron RFIDs... }"},
|
|
{"keri", CmdLFKeri, AlwaysAvailable, "{ KERI RFIDs... }"},
|
|
{"motorola", CmdLFMotorola, AlwaysAvailable, "{ Motorola Flexpass RFIDs... }"},
|
|
{"nedap", CmdLFNedap, AlwaysAvailable, "{ Nedap RFIDs... }"},
|
|
{"nexwatch", CmdLFNEXWATCH, AlwaysAvailable, "{ NexWatch RFIDs... }"},
|
|
{"noralsy", CmdLFNoralsy, AlwaysAvailable, "{ Noralsy RFIDs... }"},
|
|
{"pac", CmdLFPac, AlwaysAvailable, "{ PAC/Stanley RFIDs... }"},
|
|
{"paradox", CmdLFParadox, AlwaysAvailable, "{ Paradox RFIDs... }"},
|
|
{"pcf7931", CmdLFPCF7931, AlwaysAvailable, "{ PCF7931 CHIPs... }"},
|
|
{"presco", CmdLFPresco, AlwaysAvailable, "{ Presco RFIDs... }"},
|
|
{"pyramid", CmdLFPyramid, AlwaysAvailable, "{ Farpointe/Pyramid RFIDs... }"},
|
|
{"securakey", CmdLFSecurakey, AlwaysAvailable, "{ Securakey RFIDs... }"},
|
|
{"ti", CmdLFTI, AlwaysAvailable, "{ TI CHIPs... }"},
|
|
{"t55xx", CmdLFT55XX, AlwaysAvailable, "{ T55xx CHIPs... }"},
|
|
{"trovan", CmdLFTrovan, AlwaysAvailable, "{ Trovan animal IDs... }"},
|
|
{"viking", CmdLFViking, AlwaysAvailable, "{ Viking RFIDs... }"},
|
|
{"visa2000", CmdLFVisa2k, AlwaysAvailable, "{ Visa2000 RFIDs... }"},
|
|
// {"zx", CmdLFZx8211, AlwaysAvailable, "{ ZX8211 RFIDs... }"},
|
|
{"-----------", CmdHelp, AlwaysAvailable, "--------------------- " _CYAN_("General") " ---------------------"},
|
|
{"config", CmdLFConfig, IfPm3Lf, "Get/Set config for LF sampling, bit/sample, decimation, frequency"},
|
|
{"cmdread", CmdLFCommandRead, IfPm3Lf, "Modulate LF reader field to send command before read"},
|
|
{"read", CmdLFRead, IfPm3Lf, "Read LF tag"},
|
|
{"relay", CmdLFRelay, IfPm3Lf, "LF relay between two pm3 devices (tag/rdr mode)"},
|
|
{"search", CmdLFfind, AlwaysAvailable, "Read and Search for valid known tag"},
|
|
{"sim", CmdLFSim, IfPm3Lf, "Simulate LF tag from buffer"},
|
|
{"simask", CmdLFaskSim, IfPm3Lf, "Simulate " _YELLOW_("ASK") " tag"},
|
|
{"simfsk", CmdLFfskSim, IfPm3Lf, "Simulate " _YELLOW_("FSK") " tag"},
|
|
{"simpsk", CmdLFpskSim, IfPm3Lf, "Simulate " _YELLOW_("PSK") " tag"},
|
|
// {"simnrz", CmdLFnrzSim, IfPm3Lf, "Simulate " _YELLOW_("NRZ") " tag"},
|
|
{"simbidir", CmdLFSimBidir, IfPm3Lf, "Simulate LF tag (with bidirectional data transmission between reader and tag)"},
|
|
{"sniff", CmdLFSniff, IfPm3Lf, "Sniff LF traffic between reader and tag"},
|
|
{"tune", CmdLFTune, IfPm3Lf, "Continuously measure LF antenna tuning"},
|
|
// {"vchdemod", CmdVchDemod, AlwaysAvailable, "Demodulate samples for VeriChip"},
|
|
// {"flexdemod", CmdFlexdemod, AlwaysAvailable, "Demodulate samples for Motorola FlexPass"},
|
|
{NULL, NULL, NULL, NULL}
|
|
};
|
|
|
|
int CmdLF(const char *Cmd) {
|
|
clearCommandBuffer();
|
|
return CmdsParse(CommandTable, Cmd);
|
|
}
|
|
|
|
int CmdHelp(const char *Cmd) {
|
|
(void)Cmd; // Cmd is not used so far
|
|
CmdsHelp(CommandTable);
|
|
return PM3_SUCCESS;
|
|
}
|