mirror of
https://github.com/RfidResearchGroup/proxmark3.git
synced 2026-04-25 12:12:08 +00:00
4024 lines
138 KiB
C
4024 lines
138 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.
|
|
//-----------------------------------------------------------------------------
|
|
// Data and Graph commands
|
|
//-----------------------------------------------------------------------------
|
|
#include "cmddata.h"
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
#include <limits.h> // for CmdNorm INT_MIN && INT_MAX
|
|
#include <math.h> // pow
|
|
#include <ctype.h> // tolower
|
|
#include <locale.h> // number formatter..
|
|
#include "commonutil.h" // ARRAYLEN
|
|
#include "cmdparser.h" // for command_t
|
|
#include "ui.h" // for show graph controls
|
|
#include "proxgui.h"
|
|
#include "graph.h" // for graph data
|
|
#include "comms.h"
|
|
#include "lfdemod.h" // for demod code
|
|
#include "cmdlf.h" // for lf_getconfig
|
|
#include "loclass/cipherutils.h" // for decimating samples in getsamples
|
|
#include "cmdlfem410x.h" // askem410xdecode
|
|
#include "fileutils.h" // searchFile
|
|
#include "cliparser.h"
|
|
#include "cmdlft55xx.h" // print...
|
|
#include "crypto/asn1utils.h" // ASN1 decode / print
|
|
#include "cmdflashmemspiffs.h" // SPIFFS flash memory download
|
|
#include "mbedtls/bignum.h" // big num
|
|
#include "mbedtls/entropy.h" //
|
|
#include "mbedtls/ctr_drbg.h" // random generator
|
|
#include "atrs.h" // ATR lookup
|
|
#include "crypto/libpcrypto.h" // Cryptography
|
|
|
|
|
|
uint8_t g_DemodBuffer[MAX_DEMOD_BUF_LEN] = { 0x00 };
|
|
size_t g_DemodBufferLen = 0;
|
|
int32_t g_DemodStartIdx = 0;
|
|
int g_DemodClock = 0;
|
|
|
|
static int CmdHelp(const char *Cmd);
|
|
|
|
|
|
// https://www.eskimo.com/~scs/c-faq.com/stdio/commaprint.html
|
|
static char *commaprint(size_t n) {
|
|
|
|
static int comma = '\0';
|
|
static char retbuf[30];
|
|
|
|
char *p = &retbuf[sizeof(retbuf) - 1];
|
|
int i = 0;
|
|
|
|
if (comma == '\0') {
|
|
|
|
struct lconv *lcp = localeconv();
|
|
if (lcp != NULL) {
|
|
|
|
if (lcp->thousands_sep != NULL && *lcp->thousands_sep != '\0') {
|
|
comma = *lcp->thousands_sep;
|
|
} else {
|
|
comma = ',';
|
|
}
|
|
}
|
|
}
|
|
|
|
*p = '\0';
|
|
|
|
do {
|
|
if (i % 3 == 0 && i != 0) {
|
|
*--p = comma;
|
|
}
|
|
|
|
*--p = '0' + n % 10;
|
|
|
|
n /= 10;
|
|
|
|
i++;
|
|
|
|
} while (n != 0);
|
|
|
|
return p;
|
|
}
|
|
|
|
// set the g_DemodBuffer with given array ofq binary (one bit per byte)
|
|
void setDemodBuff(const uint8_t *buff, size_t size, size_t start_idx) {
|
|
if (buff == NULL) {
|
|
return;
|
|
}
|
|
|
|
if (size > MAX_DEMOD_BUF_LEN - start_idx) {
|
|
size = MAX_DEMOD_BUF_LEN - start_idx;
|
|
}
|
|
|
|
for (size_t i = 0; i < size; i++) {
|
|
g_DemodBuffer[i] = buff[start_idx++];
|
|
}
|
|
|
|
g_DemodBufferLen = size;
|
|
}
|
|
|
|
bool getDemodBuff(uint8_t *buff, size_t *size) {
|
|
if (buff == NULL) return false;
|
|
if (size == NULL) return false;
|
|
if (*size == 0) return false;
|
|
|
|
*size = (*size > g_DemodBufferLen) ? g_DemodBufferLen : *size;
|
|
|
|
memcpy(buff, g_DemodBuffer, *size);
|
|
return true;
|
|
}
|
|
|
|
// include <math.h>
|
|
// Root mean square
|
|
/*
|
|
static double rms(double *v, size_t n) {
|
|
double sum = 0.0;
|
|
for (size_t i = 0; i < n; i++)
|
|
sum += v[i] * v[i];
|
|
return sqrt(sum / n);
|
|
}
|
|
|
|
static int cmp_int(const void *a, const void *b) {
|
|
if (*(const int *)a < * (const int *)b)
|
|
return -1;
|
|
else
|
|
return *(const int *)a > *(const int *)b;
|
|
}
|
|
static int cmp_uint8(const void *a, const void *b) {
|
|
if (*(const uint8_t *)a < * (const uint8_t *)b)
|
|
return -1;
|
|
else
|
|
return *(const uint8_t *)a > *(const uint8_t *)b;
|
|
}
|
|
// Median of a array of values
|
|
|
|
static double median_int(int *src, size_t size) {
|
|
qsort(src, size, sizeof(int), cmp_int);
|
|
return 0.5 * (src[size / 2] + src[(size - 1) / 2]);
|
|
}
|
|
static double median_uint8(uint8_t *src, size_t size) {
|
|
qsort(src, size, sizeof(uint8_t), cmp_uint8);
|
|
return 0.5 * (src[size / 2] + src[(size - 1) / 2]);
|
|
}
|
|
*/
|
|
// function to compute mean for a series
|
|
static double compute_mean(const int *data, size_t n) {
|
|
double mean = 0.0;
|
|
for (size_t i = 0; i < n; i++)
|
|
mean += data[i];
|
|
mean /= n;
|
|
return mean;
|
|
}
|
|
|
|
// function to compute variance for a series
|
|
static double compute_variance(const int *data, size_t n) {
|
|
double variance = 0.0;
|
|
double mean = compute_mean(data, n);
|
|
|
|
for (size_t i = 0; i < n; i++)
|
|
variance += pow((data[i] - mean), 2.0);
|
|
|
|
variance /= n;
|
|
return variance;
|
|
}
|
|
|
|
// Function to compute autocorrelation for a series
|
|
// Author: Kenneth J. Christensen
|
|
// - Corrected divide by n to divide (n - lag) from Tobias Mueller
|
|
/*
|
|
static double compute_autoc(const int *data, size_t n, int lag) {
|
|
double autocv = 0.0; // Autocovariance value
|
|
double ac_value; // Computed autocorrelation value to be returned
|
|
double variance; // Computed variance
|
|
double mean;
|
|
|
|
mean = compute_mean(data, n);
|
|
variance = compute_variance(data, n);
|
|
|
|
for (size_t i=0; i < (n - lag); i++)
|
|
autocv += (data[i] - mean) * (data[i+lag] - mean);
|
|
|
|
autocv = (1.0 / (n - lag)) * autocv;
|
|
|
|
// Autocorrelation is autocovariance divided by variance
|
|
ac_value = autocv / variance;
|
|
return ac_value;
|
|
}
|
|
*/
|
|
|
|
static int CmdSetDebugMode(const char *Cmd) {
|
|
CLIParserContext *ctx;
|
|
CLIParserInit(&ctx, "data setdebugmode",
|
|
"Set debugging level on client side",
|
|
"data setdebugmode"
|
|
);
|
|
void *argtable[] = {
|
|
arg_param_begin,
|
|
arg_lit0("0", NULL, "no debug messages"),
|
|
arg_lit0("1", NULL, "debug"),
|
|
arg_lit0("2", NULL, "verbose debugging"),
|
|
arg_param_end
|
|
};
|
|
CLIExecWithReturn(ctx, Cmd, argtable, true);
|
|
|
|
bool dg_0 = arg_get_lit(ctx, 1);
|
|
bool dg_1 = arg_get_lit(ctx, 2);
|
|
bool dg_2 = arg_get_lit(ctx, 3);
|
|
CLIParserFree(ctx);
|
|
|
|
if (dg_0 + dg_1 + dg_2 > 1) {
|
|
PrintAndLogEx(INFO, "Select only one option");
|
|
return PM3_EINVARG;
|
|
}
|
|
if (dg_0)
|
|
g_debugMode = 0;
|
|
|
|
if (dg_1)
|
|
g_debugMode = 1;
|
|
|
|
if (dg_2)
|
|
g_debugMode = 2;
|
|
|
|
switch (g_debugMode) {
|
|
case 0:
|
|
PrintAndLogEx(INFO, "client debug level... %u ( no debug messages )", g_debugMode);
|
|
break;
|
|
case 1:
|
|
PrintAndLogEx(INFO, "client debug level... %u ( debug messages )", g_debugMode);
|
|
break;
|
|
case 2:
|
|
PrintAndLogEx(INFO, "client debug level... %u ( verbose debug messages )", g_debugMode);
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
return PM3_SUCCESS;
|
|
}
|
|
|
|
// max output to MAX_DEMODULATION_BITS bits if we have more
|
|
// doesn't take inconsideration where the demod offset or bitlen found.
|
|
int printDemodBuff(uint8_t offset, bool strip_leading, bool invert, bool print_hex) {
|
|
size_t len = g_DemodBufferLen;
|
|
if (len == 0) {
|
|
PrintAndLogEx(WARNING, "DemodBuffer is empty");
|
|
return PM3_EINVARG;
|
|
}
|
|
|
|
uint8_t *buf = calloc(len, sizeof(uint8_t));
|
|
if (buf == NULL) {
|
|
PrintAndLogEx(WARNING, "Failed to allocate memory");
|
|
return PM3_EMALLOC;
|
|
}
|
|
memcpy(buf, g_DemodBuffer, len);
|
|
|
|
uint8_t *p = NULL;
|
|
|
|
if (strip_leading) {
|
|
p = (buf + offset);
|
|
|
|
if (len > (g_DemodBufferLen - offset)) {
|
|
len = (g_DemodBufferLen - offset);
|
|
}
|
|
|
|
size_t i;
|
|
for (i = 0; i < len; i++) {
|
|
if (p[i] == 1) {
|
|
break;
|
|
}
|
|
}
|
|
offset += i;
|
|
}
|
|
|
|
if (len > (g_DemodBufferLen - offset)) {
|
|
len = (g_DemodBufferLen - offset);
|
|
}
|
|
|
|
if (len > MAX_DEMODULATION_BITS) {
|
|
len = MAX_DEMODULATION_BITS;
|
|
}
|
|
|
|
if (invert) {
|
|
|
|
p = (buf + offset);
|
|
|
|
for (size_t i = 0; i < len; i++) {
|
|
if (p[i] == 1) {
|
|
p[i] = 0;
|
|
} else {
|
|
if (p[i] == 0) {
|
|
p[i] = 1;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (print_hex) {
|
|
p = (buf + offset);
|
|
char hex[MAX_DEMODULATION_BITS + 1] = { 0x00 };
|
|
int num_bits = binarray_2_hex(hex, sizeof(hex), (char *)p, len);
|
|
|
|
if (num_bits == 0) {
|
|
p = NULL;
|
|
free(buf);
|
|
return PM3_ESOFT;
|
|
}
|
|
|
|
PrintAndLogEx(SUCCESS, "DemodBuffer:\n%s", hex);
|
|
} else {
|
|
PrintAndLogEx(SUCCESS, "DemodBuffer:\n%s", sprint_bytebits_bin_break(buf + offset, len, 32));
|
|
}
|
|
|
|
p = NULL;
|
|
free(buf);
|
|
return PM3_SUCCESS;
|
|
}
|
|
|
|
int CmdPrintDemodBuff(const char *Cmd) {
|
|
CLIParserContext *ctx;
|
|
CLIParserInit(&ctx, "data print",
|
|
"Print the data in the DemodBuffer as hex or binary.\n"
|
|
"Defaults to binary output",
|
|
"data print"
|
|
);
|
|
void *argtable[] = {
|
|
arg_param_begin,
|
|
arg_lit0("i", "inv", "invert DemodBuffer before printing"),
|
|
// arg_int0("l","len", "<dec>", "length to print in # of bits or hex characters respectively"),
|
|
arg_int0("o", "offset", "<dec>", "offset in # of bits"),
|
|
arg_lit0("s", "strip", "strip leading zeroes, i.e. set offset to first bit equal to one"),
|
|
arg_lit0("x", "hex", "output in hex (omit for binary output)"),
|
|
arg_param_end
|
|
};
|
|
CLIExecWithReturn(ctx, Cmd, argtable, true);
|
|
|
|
bool invert = arg_get_lit(ctx, 1);
|
|
int os = arg_get_int_def(ctx, 2, 0);
|
|
bool lstrip = arg_get_lit(ctx, 3);
|
|
bool print_hex = arg_get_lit(ctx, 4);
|
|
CLIParserFree(ctx);
|
|
|
|
uint8_t offset = (os & 0xFF);
|
|
|
|
return printDemodBuff(offset, lstrip, invert, print_hex);
|
|
}
|
|
|
|
// this function strictly converts >1 to 1 and <1 to 0 for each sample in the graphbuffer
|
|
int CmdGetBitStream(const char *Cmd) {
|
|
CLIParserContext *ctx;
|
|
CLIParserInit(&ctx, "data getbitstream",
|
|
"Convert GraphBuffer's value accordingly\n"
|
|
" - larger or equal to ONE becomes ONE\n"
|
|
" - less than ONE becomes ZERO",
|
|
"data getbitstream"
|
|
);
|
|
void *argtable[] = {
|
|
arg_param_begin,
|
|
arg_param_end
|
|
};
|
|
CLIExecWithReturn(ctx, Cmd, argtable, true);
|
|
CLIParserFree(ctx);
|
|
|
|
CmdHpf("");
|
|
for (uint32_t i = 0; i < g_GraphTraceLen; i++) {
|
|
g_GraphBuffer[i] = (g_GraphBuffer[i] >= 1) ? 1 : 0;
|
|
}
|
|
RepaintGraphWindow();
|
|
return PM3_SUCCESS;
|
|
}
|
|
|
|
static int CmdConvertBitStream(const char *Cmd) {
|
|
|
|
CLIParserContext *ctx;
|
|
CLIParserInit(&ctx, "data convertbitstream",
|
|
"Convert GraphBuffer's 0|1 values to 127|-127",
|
|
"data convertbitstream"
|
|
);
|
|
void *argtable[] = {
|
|
arg_param_begin,
|
|
arg_param_end
|
|
};
|
|
CLIExecWithReturn(ctx, Cmd, argtable, true);
|
|
CLIParserFree(ctx);
|
|
|
|
if (isGraphBitstream()) {
|
|
convertGraphFromBitstream();
|
|
} else {
|
|
// get high, low
|
|
convertGraphFromBitstreamEx(-126, -127);
|
|
}
|
|
return PM3_SUCCESS;
|
|
}
|
|
|
|
// Cmd Args: Clock, invert, maxErr, maxLen as integers and amplify as char == 'a'
|
|
// (amp may not be needed anymore)
|
|
// verbose will print results and demoding messages
|
|
// emSearch will auto search for EM410x format in bitstream
|
|
// askType switches decode: ask/raw = 0, ask/manchester = 1
|
|
int ASKDemod_ext(int clk, int invert, int maxErr, size_t maxlen, bool amplify, bool verbose, bool emSearch, uint8_t askType, bool *stCheck) {
|
|
PrintAndLogEx(DEBUG, "DEBUG: (ASKDemod_ext) clk %i invert %i maxErr %i maxLen %zu amplify %i verbose %i emSearch %i askType %i "
|
|
, clk
|
|
, invert
|
|
, maxErr
|
|
, maxlen
|
|
, amplify
|
|
, verbose
|
|
, emSearch
|
|
, askType
|
|
);
|
|
uint8_t askamp = 0;
|
|
|
|
if (maxlen == 0)
|
|
maxlen = g_pm3_capabilities.bigbuf_size;
|
|
|
|
uint8_t *bits = calloc(MAX_GRAPH_TRACE_LEN, sizeof(uint8_t));
|
|
if (bits == NULL) {
|
|
PrintAndLogEx(WARNING, "Failed to allocate memory");
|
|
return PM3_EMALLOC;
|
|
}
|
|
|
|
size_t bitlen = getFromGraphBuffer(bits);
|
|
|
|
PrintAndLogEx(DEBUG, "DEBUG: (ASKDemod_ext) #samples from graphbuff: %zu", bitlen);
|
|
|
|
if (bitlen < 255) {
|
|
free(bits);
|
|
return PM3_ESOFT;
|
|
}
|
|
|
|
if (maxlen < bitlen && maxlen != 0)
|
|
bitlen = maxlen;
|
|
|
|
int foundclk = 0;
|
|
|
|
//amplify signal before ST check
|
|
if (amplify) {
|
|
askAmp(bits, bitlen);
|
|
}
|
|
|
|
size_t ststart = 0, stend = 0;
|
|
// if (*stCheck)
|
|
bool st = DetectST(bits, &bitlen, &foundclk, &ststart, &stend);
|
|
|
|
if (clk == 0) {
|
|
if (foundclk == 32 || foundclk == 64) {
|
|
clk = foundclk;
|
|
}
|
|
}
|
|
|
|
if (st) {
|
|
*stCheck = st;
|
|
g_MarkerC.pos = ststart;
|
|
g_MarkerD.pos = stend;
|
|
if (verbose)
|
|
PrintAndLogEx(DEBUG, "Found Sequence Terminator - First one is shown by orange / blue graph markers");
|
|
}
|
|
|
|
int start_idx = 0;
|
|
int errCnt = askdemod_ext(bits, &bitlen, &clk, &invert, maxErr, askamp, askType, &start_idx);
|
|
if (start_idx >= clk / 2) {
|
|
start_idx -= clk / 2;
|
|
}
|
|
if (askType == 0) { // if not Manchester, clock width is halved
|
|
clk /= 2;
|
|
}
|
|
if (errCnt < 0 || bitlen < 16) { //if fatal error (or -1)
|
|
PrintAndLogEx(DEBUG, "DEBUG: (ASKDemod_ext) No data found errors:%d, %s bitlen:%zu, clock:%i"
|
|
, errCnt
|
|
, (invert) ? "inverted," : ""
|
|
, bitlen
|
|
, clk
|
|
);
|
|
free(bits);
|
|
return PM3_ESOFT;
|
|
}
|
|
|
|
if (errCnt > maxErr) {
|
|
PrintAndLogEx(DEBUG, "DEBUG: (ASKDemod_ext) Too many errors found, errors:%d, bits:%zu, clock:%i"
|
|
, errCnt
|
|
, bitlen
|
|
, clk
|
|
);
|
|
free(bits);
|
|
return PM3_ESOFT;
|
|
}
|
|
|
|
if (verbose) {
|
|
PrintAndLogEx(DEBUG, "DEBUG: (ASKDemod_ext) using clock:%i, %sbits found:%zu, start index %d"
|
|
, clk
|
|
, (invert) ? "inverted, " : ""
|
|
, bitlen
|
|
, start_idx
|
|
);
|
|
}
|
|
|
|
//output
|
|
setDemodBuff(bits, bitlen, 0);
|
|
setClockGrid(clk, start_idx);
|
|
|
|
if (verbose) {
|
|
if (errCnt > 0)
|
|
PrintAndLogEx(DEBUG, "# Errors during demoding (shown as 7 in bit stream)... " _RED_("%d"), errCnt);
|
|
|
|
if (askType) {
|
|
PrintAndLogEx(SUCCESS, _YELLOW_("ASK/Manchester") " - clock " _YELLOW_("%i") " - decoded bitstream", clk);
|
|
PrintAndLogEx(INFO, "-----------------------------------------------");
|
|
} else {
|
|
PrintAndLogEx(SUCCESS, _YELLOW_("ASK/Raw") " - clock " _YELLOW_("%i") " - decoded bitstream", clk);
|
|
PrintAndLogEx(INFO, "----------------------------------------");
|
|
}
|
|
|
|
printDemodBuff(0, false, false, false);
|
|
}
|
|
uint64_t lo = 0;
|
|
uint32_t hi = 0;
|
|
if (emSearch)
|
|
AskEm410xDecode(true, &hi, &lo);
|
|
|
|
free(bits);
|
|
return PM3_SUCCESS;
|
|
}
|
|
|
|
int ASKDemod(int clk, int invert, int maxErr, size_t maxlen, bool amplify, bool verbose, bool emSearch, uint8_t askType) {
|
|
bool st = false;
|
|
return ASKDemod_ext(clk, invert, maxErr, maxlen, amplify, verbose, emSearch, askType, &st);
|
|
}
|
|
|
|
// takes 5 arguments - clock, invert, maxErr, maxLen as integers and amplify as char == 'a'
|
|
// attempts to demodulate ask while decoding manchester
|
|
// prints binary found and saves in graphbuffer for further commands
|
|
static int Cmdaskmandemod(const char *Cmd) {
|
|
CLIParserContext *ctx;
|
|
CLIParserInit(&ctx, "data rawdemod --am",
|
|
"ASK/MANCHESTER demodulate the data in the GraphBuffer and output binary",
|
|
"data rawdemod --am --> demod a ask/manchester tag, using autodetect\n"
|
|
"data rawdemod --am -c 32 --> demod a ask/manchester tag, using a clock of RF/32\n"
|
|
"data rawdemod --am -i --> demod a ask/manchester tag, using autodetect, invert output\n"
|
|
"data rawdemod --am -c 32 -i --> demod a ask/manchester tag, using a clock of RF/32, invert output\n"
|
|
"data rawdemod --am -c 64 -i --max 0 --> demod a ask/manchester tag, using a clock of RF/64, inverting and allowing 0 demod errors\n"
|
|
);
|
|
void *argtable[] = {
|
|
arg_param_begin,
|
|
arg_lit0("a", "amp", "try attempt demod with ask amplification (def no amp)"),
|
|
arg_int0("c", "clk", "<dec>", "set clock manually (def autodetect)"),
|
|
arg_lit0("i", "inv", "invert output"),
|
|
arg_lit0("s", "st", "check for sequence terminator"),
|
|
arg_int0(NULL, "max", "<dec>", "maximum allowed errors (def 100)"),
|
|
arg_int0(NULL, "samples", "<dec>", "maximum samples to read (def 32768) [512 bits at RF/64]"),
|
|
arg_param_end
|
|
};
|
|
CLIExecWithReturn(ctx, Cmd, argtable, true);
|
|
|
|
bool amplify = arg_get_lit(ctx, 1);
|
|
uint16_t clk = (uint16_t)arg_get_int_def(ctx, 2, 0);
|
|
bool invert = arg_get_lit(ctx, 3);
|
|
bool st = arg_get_lit(ctx, 4);
|
|
uint8_t max_err = (uint8_t)arg_get_int_def(ctx, 5, 100) & 0xFF;
|
|
size_t max_len = (size_t)arg_get_int_def(ctx, 6, 0) & 0xFF;
|
|
CLIParserFree(ctx);
|
|
|
|
return ASKDemod_ext(clk, invert, max_err, max_len, amplify, true, true, 1, &st);
|
|
}
|
|
|
|
// manchester decode
|
|
// strictly take 10 and 01 and convert to 0 and 1
|
|
static int Cmdmandecoderaw(const char *Cmd) {
|
|
CLIParserContext *ctx;
|
|
CLIParserInit(&ctx, "data manrawdecode",
|
|
"Manchester decode binary stream in DemodBuffer\n"
|
|
"Converts 10 and 01 and converts to 0 and 1 respectively\n"
|
|
" - must have binary sequence in DemodBuffer (run `data rawdemod --ar` before)",
|
|
"data manrawdecode"
|
|
);
|
|
void *argtable[] = {
|
|
arg_param_begin,
|
|
arg_lit0("i", "inv", "invert output"),
|
|
arg_int0(NULL, "err", "<dec>", "set max errors tolerated (def 20)"),
|
|
arg_param_end
|
|
};
|
|
CLIExecWithReturn(ctx, Cmd, argtable, true);
|
|
bool invert = arg_get_lit(ctx, 1);
|
|
int max_err = arg_get_int_def(ctx, 2, 20);
|
|
CLIParserFree(ctx);
|
|
|
|
if (g_DemodBufferLen == 0) {
|
|
PrintAndLogEx(WARNING, "DemodBuffer empty, run " _YELLOW_("`data rawdemod --ar`"));
|
|
return PM3_ESOFT;
|
|
}
|
|
|
|
uint8_t *bits = calloc(MAX_DEMOD_BUF_LEN, sizeof(uint8_t));
|
|
if (bits == NULL) {
|
|
PrintAndLogEx(WARNING, "Failed to allocate memory");
|
|
return PM3_EMALLOC;
|
|
}
|
|
|
|
// make sure its just binary data 0|1|7 in buffer
|
|
int high = 0, low = 0;
|
|
size_t i = 0;
|
|
for (; i < g_DemodBufferLen; ++i) {
|
|
if (g_DemodBuffer[i] > high)
|
|
high = g_DemodBuffer[i];
|
|
else if (g_DemodBuffer[i] < low)
|
|
low = g_DemodBuffer[i];
|
|
bits[i] = g_DemodBuffer[i];
|
|
}
|
|
|
|
if (high > 7 || low < 0) {
|
|
PrintAndLogEx(ERR, "Error: please first raw demod then manchester raw decode");
|
|
free(bits);
|
|
return PM3_ESOFT;
|
|
}
|
|
|
|
size_t size = i;
|
|
uint8_t offset = 0;
|
|
uint16_t err_cnt = manrawdecode(bits, &size, invert, &offset);
|
|
if (err_cnt > max_err) {
|
|
PrintAndLogEx(ERR, "Too many errors attempting to decode " _RED_("%i"), err_cnt);
|
|
free(bits);
|
|
return PM3_ESOFT;
|
|
}
|
|
|
|
if (err_cnt > 0) {
|
|
PrintAndLogEx(WARNING, "# %i errors found during demod (shown as " _YELLOW_(".")" in bit stream) ", err_cnt);
|
|
}
|
|
|
|
PrintAndLogEx(INFO, "Manchester decoded %s", (invert) ? "( inverted )" : "");
|
|
PrintAndLogEx(INFO, "%s", sprint_bytebits_bin_break(bits, size, 32));
|
|
|
|
// try decode EM410x
|
|
if (err_cnt == 0) {
|
|
uint64_t id = 0;
|
|
uint32_t hi = 0;
|
|
size_t idx = 0;
|
|
size_t tmpsize = 0;
|
|
int res = Em410xDecode(bits, &tmpsize, &idx, &hi, &id);
|
|
if (res > 0) {
|
|
//need to adjust to set bitstream back to manchester encoded data
|
|
//setDemodBuff(bits, size, idx);
|
|
printEM410x(hi, id, false, res);
|
|
|
|
size = tmpsize;
|
|
}
|
|
}
|
|
setDemodBuff(bits, size, 0);
|
|
setClockGrid(g_DemodClock * 2, g_DemodStartIdx);
|
|
free(bits);
|
|
return PM3_SUCCESS;
|
|
}
|
|
|
|
/*
|
|
* @author marshmellow
|
|
* biphase decode
|
|
* decodes 01 or 10 -> ZERO
|
|
* 11 or 00 -> ONE
|
|
* param offset adjust start position
|
|
* param invert invert output
|
|
* param masxErr maximum tolerated errors
|
|
*/
|
|
static int CmdBiphaseDecodeRaw(const char *Cmd) {
|
|
CLIParserContext *ctx;
|
|
CLIParserInit(&ctx, "data biphaserawdecode",
|
|
"Biphase decode binary stream in DemodBuffer\n"
|
|
"Converts 10 or 01 -> 1 and 11 or 00 -> 0\n"
|
|
" - must have binary sequence in DemodBuffer (run `data rawdemod --ar` before)\n"
|
|
" - invert for Conditional Dephase Encoding (CDP) AKA Differential Manchester",
|
|
"data biphaserawdecode --> decode biphase bitstream from the DemodBuffer\n"
|
|
"data biphaserawdecode -oi --> decode biphase bitstream from the DemodBuffer, adjust offset, and invert output"
|
|
);
|
|
void *argtable[] = {
|
|
arg_param_begin,
|
|
arg_lit0("o", "offset", "set to adjust decode start position"),
|
|
arg_lit0("i", "inv", "invert output"),
|
|
arg_int0(NULL, "err", "<dec>", "set max errors tolerated (def 20)"),
|
|
arg_param_end
|
|
};
|
|
CLIExecWithReturn(ctx, Cmd, argtable, true);
|
|
int offset = arg_get_lit(ctx, 1);
|
|
bool invert = arg_get_lit(ctx, 2);
|
|
int max_err = arg_get_int_def(ctx, 3, 20);
|
|
CLIParserFree(ctx);
|
|
|
|
if (g_DemodBufferLen == 0) {
|
|
PrintAndLogEx(WARNING, "DemodBuffer empty, run " _YELLOW_("`data rawdemod --ar`"));
|
|
return PM3_ESOFT;
|
|
}
|
|
|
|
uint8_t *bits = calloc(MAX_DEMOD_BUF_LEN, sizeof(uint8_t));
|
|
if (bits == NULL) {
|
|
PrintAndLogEx(WARNING, "Failed to allocate memory");
|
|
return PM3_EMALLOC;
|
|
}
|
|
|
|
size_t size = MAX_DEMOD_BUF_LEN;
|
|
if (!getDemodBuff(bits, &size)) {
|
|
free(bits);
|
|
return PM3_ESOFT;
|
|
}
|
|
|
|
int err_cnt = BiphaseRawDecode(bits, &size, &offset, invert);
|
|
if (err_cnt < 0) {
|
|
PrintAndLogEx(ERR, "Error during decode " _RED_("%i"), err_cnt);
|
|
free(bits);
|
|
return PM3_ESOFT;
|
|
}
|
|
if (err_cnt > max_err) {
|
|
PrintAndLogEx(ERR, "Too many errors attempting to decode " _RED_("%i"), err_cnt);
|
|
free(bits);
|
|
return PM3_ESOFT;
|
|
}
|
|
|
|
if (err_cnt > 0) {
|
|
PrintAndLogEx(WARNING, "# %i errors found during demod (shown as " _YELLOW_(".")" in bit stream) ", err_cnt);
|
|
}
|
|
|
|
PrintAndLogEx(INFO, "Biphase decoded using offset %d%s", offset, (invert) ? "( inverted )" : "");
|
|
PrintAndLogEx(INFO, "%s", sprint_bytebits_bin_break(bits, size, 32));
|
|
|
|
setDemodBuff(bits, size, 0);
|
|
setClockGrid(g_DemodClock * 2, g_DemodStartIdx + g_DemodClock * offset);
|
|
free(bits);
|
|
return PM3_SUCCESS;
|
|
}
|
|
|
|
// ASK Demod then Biphase decode g_GraphBuffer samples
|
|
int ASKbiphaseDemod(int offset, int clk, int invert, int maxErr, bool verbose) {
|
|
//ask raw demod g_GraphBuffer first
|
|
|
|
uint8_t *bs = calloc(MAX_DEMOD_BUF_LEN, sizeof(uint8_t));
|
|
if (bs == NULL) {
|
|
PrintAndLogEx(WARNING, "Failed to allocate memory");
|
|
return PM3_EMALLOC;
|
|
}
|
|
|
|
size_t size = getFromGraphBufferEx(bs, MAX_DEMOD_BUF_LEN);
|
|
if (size == 0) {
|
|
PrintAndLogEx(DEBUG, "DEBUG: no data in graphbuf");
|
|
free(bs);
|
|
return PM3_ESOFT;
|
|
}
|
|
int startIdx = 0;
|
|
//invert here inverts the ask raw demoded bits which has no effect on the demod, but we need the pointer
|
|
int errCnt = askdemod_ext(bs, &size, &clk, &invert, maxErr, 0, 0, &startIdx);
|
|
if (errCnt < 0 || errCnt > maxErr) {
|
|
PrintAndLogEx(DEBUG, "DEBUG: no data or error found %d, clock: %d", errCnt, clk);
|
|
free(bs);
|
|
return PM3_ESOFT;
|
|
}
|
|
|
|
//attempt to Biphase decode BitStream
|
|
errCnt = BiphaseRawDecode(bs, &size, &offset, invert);
|
|
if (errCnt < 0) {
|
|
if (g_debugMode || verbose) PrintAndLogEx(DEBUG, "DEBUG: Error BiphaseRawDecode: %d", errCnt);
|
|
free(bs);
|
|
return PM3_ESOFT;
|
|
}
|
|
if (errCnt > maxErr) {
|
|
if (g_debugMode || verbose) PrintAndLogEx(DEBUG, "DEBUG: Error BiphaseRawDecode too many errors: %d", errCnt);
|
|
free(bs);
|
|
return PM3_ESOFT;
|
|
}
|
|
|
|
if (offset >= 1) {
|
|
offset -= 1;
|
|
}
|
|
//success set g_DemodBuffer and return
|
|
setDemodBuff(bs, size, 0);
|
|
setClockGrid(clk, startIdx + clk * offset / 2);
|
|
if (g_debugMode || verbose) {
|
|
PrintAndLogEx(DEBUG, "Biphase Decoded using offset %d | clock %d | #errors %d | start index %d\ndata\n", offset, clk, errCnt, (startIdx + clk * offset / 2));
|
|
printDemodBuff(offset, false, false, false);
|
|
}
|
|
free(bs);
|
|
return PM3_SUCCESS;
|
|
}
|
|
|
|
// see ASKbiphaseDemod
|
|
static int Cmdaskbiphdemod(const char *Cmd) {
|
|
CLIParserContext *ctx;
|
|
CLIParserInit(&ctx, "data rawdemod --ab",
|
|
"ASK/BIPHASE demodulate the data in the GraphBuffer and output binary\n"
|
|
"NOTE, `--invert` for Conditional Dephase Encoding (CDP) AKA Differential Manchester\n",
|
|
"data rawdemod --ab --> demod a ask/biphase tag, using autodetect\n"
|
|
"data rawdemod --ab -c 32 --> demod a ask/biphase tag, using a clock of RF/32\n"
|
|
"data rawdemod --ab -i --> demod a ask/biphase tag, using autodetect, invert output\n"
|
|
"data rawdemod --ab -c 32 -i --> demod a ask/biphase tag, using a clock of RF/32, invert output\n"
|
|
"data rawdemod --ab -c 64 -i --max 0 --> demod a ask/biphase tag, using a clock of RF/64, inverting and allowing 0 demod errors\n"
|
|
);
|
|
void *argtable[] = {
|
|
arg_param_begin,
|
|
arg_int0("c", "clk", "<dec>", "set clock manually (def autodetect)"),
|
|
arg_lit0("i", "inv", "invert output"),
|
|
arg_int0("o", "offset", "<dec>", "offset to begin biphase (def 0)"),
|
|
arg_int0(NULL, "max", "<dec>", "maximum allowed errors (def 50)"),
|
|
arg_param_end
|
|
};
|
|
CLIExecWithReturn(ctx, Cmd, argtable, true);
|
|
|
|
uint16_t clk = (uint16_t)arg_get_int_def(ctx, 1, 0);
|
|
bool invert = arg_get_lit(ctx, 2);
|
|
int offset = arg_get_int_def(ctx, 3, 0);
|
|
uint8_t max_err = (uint8_t)arg_get_int_def(ctx, 4, 50) & 0xFF;
|
|
CLIParserFree(ctx);
|
|
|
|
return ASKbiphaseDemod(offset, clk, invert, max_err, true);
|
|
}
|
|
|
|
// see ASKDemod
|
|
static int Cmdaskrawdemod(const char *Cmd) {
|
|
CLIParserContext *ctx;
|
|
CLIParserInit(&ctx, "data rawdemod --ar",
|
|
"ASK/RAW demodulate the data in the GraphBuffer and output binary",
|
|
"data rawdemod --ar -a --> demod a ask tag, using autodetect, amplified\n"
|
|
"data rawdemod --ar -c 32 --> demod a ask tag, using a clock of RF/32\n"
|
|
"data rawdemod --ar -i --> demod a ask tag, using autodetect, invert output\n"
|
|
"data rawdemod --ar -c 32 -i --> demod a ask tag, using a clock of RF/32, invert output\n"
|
|
"data rawdemod --ar -c 64 -i --max 0 --> demod a ask tag, using a clock of RF/64, inverting and allowing 0 demod errors\n"
|
|
);
|
|
void *argtable[] = {
|
|
arg_param_begin,
|
|
arg_lit0("a", "amp", "try attempt demod with ask amplification (def no amp)"),
|
|
arg_int0("c", "clk", "<dec>", "set clock manually (def autodetect)"),
|
|
arg_lit0("i", "inv", "invert output"),
|
|
arg_lit0("s", "st", "check for sequence terminator"),
|
|
arg_int0(NULL, "max", "<dec>", "maximum allowed errors (def 100)"),
|
|
arg_int0(NULL, "samples", "<dec>", "maximum samples to read (def 32768) [512 bits at RF/64]"),
|
|
arg_param_end
|
|
};
|
|
CLIExecWithReturn(ctx, Cmd, argtable, true);
|
|
|
|
bool amplify = arg_get_lit(ctx, 1);
|
|
uint16_t clk = (uint16_t)arg_get_int_def(ctx, 2, 0);
|
|
bool invert = arg_get_lit(ctx, 3);
|
|
bool st = arg_get_lit(ctx, 4);
|
|
uint8_t max_err = (uint8_t)arg_get_int_def(ctx, 5, 100) & 0xFF;
|
|
size_t max_len = (size_t)arg_get_int_def(ctx, 6, 0) & 0xFF;
|
|
CLIParserFree(ctx);
|
|
|
|
return ASKDemod_ext(clk, invert, max_err, max_len, amplify, true, false, 0, &st);
|
|
}
|
|
|
|
int AutoCorrelate(const int *in, int *out, size_t len, size_t window, bool SaveGrph, bool verbose) {
|
|
// sanity check
|
|
if (window > len) {
|
|
window = len;
|
|
}
|
|
|
|
//test
|
|
double autocv = 0.0; // Autocovariance value
|
|
size_t correlation = 0;
|
|
int lastmax = 0;
|
|
|
|
// in, len, 4000
|
|
double mean = compute_mean(in, len);
|
|
// Computed variance
|
|
double variance = compute_variance(in, len);
|
|
|
|
int *correl_buf = calloc(MAX_GRAPH_TRACE_LEN, sizeof(int));
|
|
|
|
uint8_t peak_cnt = 0;
|
|
size_t peaks[10] = {0};
|
|
|
|
for (size_t i = 0; i < len - window; ++i) {
|
|
|
|
for (size_t j = 0; j < (len - i); j++) {
|
|
autocv += (in[j] - mean) * (in[j + i] - mean);
|
|
}
|
|
autocv = (1.0 / (len - i)) * autocv;
|
|
|
|
correl_buf[i] = autocv;
|
|
|
|
// Computed autocorrelation value to be returned
|
|
// Autocorrelation is autocovariance divided by variance
|
|
double ac_value = autocv / variance;
|
|
|
|
// keep track of which distance is repeating.
|
|
// A value near 1.0 or more indicates a correlation in the signal
|
|
if (ac_value > 0.95f) {
|
|
correlation = i - lastmax;
|
|
lastmax = i;
|
|
|
|
if ((correlation > 1) && peak_cnt < ARRAYLEN(peaks)) {
|
|
peaks[peak_cnt++] = correlation;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Find shorts distance between peaks
|
|
int distance = -1;
|
|
for (size_t i = 0; i < ARRAYLEN(peaks); ++i) {
|
|
|
|
PrintAndLogEx(DEBUG, "%zu | %zu", i, peaks[i]);
|
|
if (peaks[i] < 128) {
|
|
continue;
|
|
}
|
|
|
|
if (distance == -1) {
|
|
distance = peaks[i];
|
|
continue;
|
|
}
|
|
|
|
if (peaks[i] < distance) {
|
|
distance = peaks[i];
|
|
}
|
|
}
|
|
|
|
if (distance > -1) {
|
|
if (verbose) {
|
|
PrintAndLogEx(SUCCESS, "Possible correlation at "_YELLOW_("%4d") " samples", distance);
|
|
}
|
|
} else {
|
|
PrintAndLogEx(HINT, "Hint: No repeating pattern found, try increasing window size");
|
|
// return value -1, indication to increase window size
|
|
return -1;
|
|
}
|
|
|
|
if (SaveGrph) {
|
|
//g_GraphTraceLen = g_GraphTraceLen - window;
|
|
memcpy(out, correl_buf, len * sizeof(int));
|
|
setClockGrid(distance, 0);
|
|
g_DemodBufferLen = 0;
|
|
RepaintGraphWindow();
|
|
}
|
|
free(correl_buf);
|
|
return distance;
|
|
}
|
|
|
|
static int CmdAutoCorr(const char *Cmd) {
|
|
CLIParserContext *ctx;
|
|
CLIParserInit(&ctx, "data autocorr",
|
|
"Autocorrelate over window is used to detect repeating sequences.\n"
|
|
"We use it as detection of how long in bits a message inside the signal is",
|
|
"data autocorr -w 4000\n"
|
|
"data autocorr -w 4000 -g"
|
|
);
|
|
void *argtable[] = {
|
|
arg_param_begin,
|
|
arg_lit0("g", NULL, "save back to GraphBuffer (overwrite)"),
|
|
arg_u64_0("w", "win", "<dec>", "window length for correlation. def 4000"),
|
|
arg_param_end
|
|
};
|
|
CLIExecWithReturn(ctx, Cmd, argtable, true);
|
|
bool updateGrph = arg_get_lit(ctx, 1);
|
|
uint32_t window = arg_get_u32_def(ctx, 2, 4000);
|
|
CLIParserFree(ctx);
|
|
|
|
PrintAndLogEx(INFO, "Using window size " _YELLOW_("%u"), window);
|
|
|
|
if (g_GraphTraceLen == 0) {
|
|
PrintAndLogEx(WARNING, "GraphBuffer is empty");
|
|
PrintAndLogEx(HINT, "Hint: Try `" _YELLOW_("lf read") "` to collect samples");
|
|
return PM3_ESOFT;
|
|
}
|
|
|
|
if (window >= g_GraphTraceLen) {
|
|
PrintAndLogEx(WARNING, "window must be smaller than trace ( " _YELLOW_("%zu") " samples )", g_GraphTraceLen);
|
|
return PM3_EINVARG;
|
|
}
|
|
|
|
AutoCorrelate(g_GraphBuffer, g_GraphBuffer, g_GraphTraceLen, window, updateGrph, true);
|
|
return PM3_SUCCESS;
|
|
}
|
|
|
|
static int CmdBitsamples(const char *Cmd) {
|
|
|
|
CLIParserContext *ctx;
|
|
CLIParserInit(&ctx, "data bitsamples",
|
|
"Get raw samples from device as bitstring",
|
|
"data bitsamples"
|
|
);
|
|
void *argtable[] = {
|
|
arg_param_begin,
|
|
arg_param_end
|
|
};
|
|
CLIExecWithReturn(ctx, Cmd, argtable, true);
|
|
CLIParserFree(ctx);
|
|
|
|
int cnt = 0;
|
|
uint8_t got[12288];
|
|
|
|
if (!GetFromDevice(BIG_BUF, got, sizeof(got), 0, NULL, 0, NULL, 2500, false)) {
|
|
PrintAndLogEx(WARNING, "command execution time out");
|
|
return PM3_ETIMEOUT;
|
|
}
|
|
|
|
for (size_t j = 0; j < ARRAYLEN(got); j++) {
|
|
for (uint8_t k = 0; k < 8; k++) {
|
|
if (got[j] & (1 << (7 - k)))
|
|
g_GraphBuffer[cnt++] = 1;
|
|
else
|
|
g_GraphBuffer[cnt++] = 0;
|
|
}
|
|
}
|
|
g_GraphTraceLen = cnt;
|
|
RepaintGraphWindow();
|
|
return PM3_SUCCESS;
|
|
}
|
|
|
|
static int CmdBuffClear(const char *Cmd) {
|
|
CLIParserContext *ctx;
|
|
CLIParserInit(&ctx, "data clear",
|
|
"This function clears the BigBuf on device side\n"
|
|
"and graph window ( graphbuffer )",
|
|
"data clear"
|
|
);
|
|
void *argtable[] = {
|
|
arg_param_begin,
|
|
arg_param_end
|
|
};
|
|
CLIExecWithReturn(ctx, Cmd, argtable, true);
|
|
CLIParserFree(ctx);
|
|
clearCommandBuffer();
|
|
SendCommandNG(CMD_BUFF_CLEAR, NULL, 0);
|
|
ClearGraph(true);
|
|
// iceman: should clear all other new buffers getting introduced
|
|
return PM3_SUCCESS;
|
|
}
|
|
|
|
static int CmdDecimate(const char *Cmd) {
|
|
|
|
CLIParserContext *ctx;
|
|
CLIParserInit(&ctx, "data decimate",
|
|
"Performs decimation, by reducing samples N times in the grapbuf. Good for PSK\n",
|
|
"data decimate\n"
|
|
"data decimate -n 4"
|
|
);
|
|
|
|
void *argtable[] = {
|
|
arg_param_begin,
|
|
arg_int0("n", NULL, "<dec>", "factor to reduce sample set (default 2)"),
|
|
arg_param_end
|
|
};
|
|
CLIExecWithReturn(ctx, Cmd, argtable, true);
|
|
int n = arg_get_int_def(ctx, 1, 2);
|
|
CLIParserFree(ctx);
|
|
|
|
for (size_t i = 0; i < (g_GraphTraceLen / n); ++i)
|
|
g_GraphBuffer[i] = g_GraphBuffer[i * n];
|
|
|
|
g_GraphTraceLen /= n;
|
|
PrintAndLogEx(SUCCESS, "decimated by " _GREEN_("%u"), n);
|
|
RepaintGraphWindow();
|
|
return PM3_SUCCESS;
|
|
}
|
|
/**
|
|
* Undecimate - I'd call it 'interpolate', but we'll save that
|
|
* name until someone does an actual interpolation command, not just
|
|
* blindly repeating samples
|
|
* @param Cmd
|
|
* @return
|
|
*/
|
|
static int CmdUndecimate(const char *Cmd) {
|
|
CLIParserContext *ctx;
|
|
CLIParserInit(&ctx, "data undecimate",
|
|
"Performs un-decimation, by repeating each sample N times in the graphbuf",
|
|
"data undecimate\n"
|
|
"data undecimate -n 4\n"
|
|
);
|
|
|
|
void *argtable[] = {
|
|
arg_param_begin,
|
|
arg_int0("n", NULL, "<dec>", "factor to repeat each sample (default 2)"),
|
|
arg_param_end
|
|
};
|
|
CLIExecWithReturn(ctx, Cmd, argtable, true);
|
|
int factor = arg_get_int_def(ctx, 1, 2);
|
|
CLIParserFree(ctx);
|
|
|
|
//We have memory, don't we?
|
|
int *swap = calloc(MAX_GRAPH_TRACE_LEN, sizeof(int));
|
|
if (swap == NULL) {
|
|
PrintAndLogEx(WARNING, "Failed to allocate memory");
|
|
return PM3_EMALLOC;
|
|
}
|
|
uint32_t g_index = 0, s_index = 0;
|
|
while (g_index < g_GraphTraceLen && s_index + factor < MAX_GRAPH_TRACE_LEN) {
|
|
int count = 0;
|
|
for (count = 0; count < factor && s_index + count < MAX_GRAPH_TRACE_LEN; count++) {
|
|
swap[s_index + count] = (
|
|
(double)(factor - count) / (factor - 1)) * g_GraphBuffer[g_index] +
|
|
((double)count / factor) * g_GraphBuffer[g_index + 1]
|
|
;
|
|
}
|
|
s_index += count;
|
|
g_index++;
|
|
}
|
|
|
|
memcpy(g_GraphBuffer, swap, s_index * sizeof(int));
|
|
g_GraphTraceLen = s_index;
|
|
RepaintGraphWindow();
|
|
free(swap);
|
|
return PM3_SUCCESS;
|
|
}
|
|
|
|
// shift graph zero up or down based on input + or -
|
|
static int CmdGraphShiftZero(const char *Cmd) {
|
|
|
|
CLIParserContext *ctx;
|
|
CLIParserInit(&ctx, "data shiftgraphzero",
|
|
"Shift 0 for Graphed wave + or - shift value",
|
|
"data shiftgraphzero -n 10 --> shift 10 points\n"
|
|
"data shiftgraphzero -n -22 --> shift negative 22 points"
|
|
);
|
|
void *argtable[] = {
|
|
arg_param_begin,
|
|
arg_int1("n", NULL, "<dec>", "shift + or -"),
|
|
arg_param_end
|
|
};
|
|
CLIExecWithReturn(ctx, Cmd, argtable, false);
|
|
int shift = arg_get_int_def(ctx, 1, 0);
|
|
CLIParserFree(ctx);
|
|
|
|
for (size_t i = 0; i < g_GraphTraceLen; i++) {
|
|
int shiftedVal = g_GraphBuffer[i] + shift;
|
|
|
|
if (shiftedVal > 127)
|
|
shiftedVal = 127;
|
|
else if (shiftedVal < -127)
|
|
shiftedVal = -127;
|
|
g_GraphBuffer[i] = shiftedVal;
|
|
}
|
|
CmdNorm("");
|
|
return PM3_SUCCESS;
|
|
}
|
|
|
|
int AskEdgeDetect(const int *in, int *out, int len, int threshold) {
|
|
int last = 0;
|
|
for (int i = 1; i < len; i++) {
|
|
if (in[i] - in[i - 1] >= threshold) //large jump up
|
|
last = 127;
|
|
else if (in[i] - in[i - 1] <= -1 * threshold) //large jump down
|
|
last = -127;
|
|
out[i - 1] = last;
|
|
}
|
|
return PM3_SUCCESS;
|
|
}
|
|
|
|
// use large jumps in read samples to identify edges of waves and then amplify that wave to max
|
|
// similar to dirtheshold, threshold commands
|
|
// takes a threshold length which is the measured length between two samples then determines an edge
|
|
static int CmdAskEdgeDetect(const char *Cmd) {
|
|
|
|
CLIParserContext *ctx;
|
|
CLIParserInit(&ctx, "data askedgedetect",
|
|
"Adjust Graph for manual ASK demod using the length of sample differences\n"
|
|
"to detect the edge of a wave",
|
|
"data askedgedetect -t 20"
|
|
);
|
|
void *argtable[] = {
|
|
arg_param_begin,
|
|
arg_int0("t", "thres", "<dec>", "threshold, use 20 - 45 (def 25)"),
|
|
arg_param_end
|
|
};
|
|
CLIExecWithReturn(ctx, Cmd, argtable, true);
|
|
int threshold = arg_get_int_def(ctx, 1, 25);
|
|
CLIParserFree(ctx);
|
|
|
|
PrintAndLogEx(INFO, "using threshold " _YELLOW_("%i"), threshold);
|
|
int res = AskEdgeDetect(g_GraphBuffer, g_GraphBuffer, g_GraphTraceLen, threshold);
|
|
RepaintGraphWindow();
|
|
return res;
|
|
}
|
|
|
|
// Print our clock rate
|
|
// uses data from graphbuffer
|
|
// adjusted to take char parameter for type of modulation to find the clock - by marshmellow.
|
|
static int CmdDetectClockRate(const char *Cmd) {
|
|
CLIParserContext *ctx;
|
|
CLIParserInit(&ctx, "data detectclock",
|
|
"Detect ASK, FSK, NRZ, PSK clock rate of wave in GraphBuffer",
|
|
"data detectclock --ask\n"
|
|
"data detectclock --nzr --> detect clock of an nrz/direct wave in GraphBuffer\n"
|
|
);
|
|
void *argtable[] = {
|
|
arg_param_begin,
|
|
arg_lit0(NULL, "ask", "specify ASK modulation clock detection"),
|
|
arg_lit0(NULL, "fsk", "specify FSK modulation clock detection"),
|
|
arg_lit0(NULL, "nzr", "specify NZR/DIRECT modulation clock detection"),
|
|
arg_lit0(NULL, "psk", "specify PSK modulation clock detection"),
|
|
arg_param_end
|
|
};
|
|
CLIExecWithReturn(ctx, Cmd, argtable, true);
|
|
bool a = arg_get_lit(ctx, 1);
|
|
bool f = arg_get_lit(ctx, 2);
|
|
bool n = arg_get_lit(ctx, 3);
|
|
bool p = arg_get_lit(ctx, 4);
|
|
CLIParserFree(ctx);
|
|
|
|
int tmp = (a + f + n + p);
|
|
if (tmp > 1) {
|
|
PrintAndLogEx(WARNING, "Only specify one modulation");
|
|
return PM3_EINVARG;
|
|
} else if (tmp == 0) {
|
|
|
|
int clock = GetFskClock("", false);
|
|
if (clock > 0) {
|
|
PrintAndLogEx(SUCCESS, "FSK Clock... %d", clock);
|
|
}
|
|
clock = GetAskClock("", false);
|
|
if (clock > 0) {
|
|
PrintAndLogEx(SUCCESS, "ASK Clock... %d", clock);
|
|
}
|
|
clock = GetNrzClock("", false);
|
|
if (clock > 0) {
|
|
PrintAndLogEx(SUCCESS, "NRZ Clock... %d", clock);
|
|
}
|
|
clock = GetPskClock("", false);
|
|
if (clock > 0) {
|
|
PrintAndLogEx(SUCCESS, "PSK Clock... %d", clock);
|
|
}
|
|
return PM3_SUCCESS;
|
|
}
|
|
|
|
if (a)
|
|
GetAskClock("", true);
|
|
|
|
if (f)
|
|
GetFskClock("", true);
|
|
|
|
if (n)
|
|
GetNrzClock("", true);
|
|
|
|
if (p)
|
|
GetPskClock("", true);
|
|
|
|
RepaintGraphWindow();
|
|
return PM3_SUCCESS;
|
|
}
|
|
|
|
static char *GetFSKType(uint8_t fchigh, uint8_t fclow, uint8_t invert) {
|
|
static char fType[8];
|
|
memset(fType, 0x00, 8);
|
|
char *fskType = fType;
|
|
|
|
if (fchigh == 10 && fclow == 8) {
|
|
|
|
if (invert)
|
|
memcpy(fskType, "FSK2a", 5);
|
|
else
|
|
memcpy(fskType, "FSK2", 4);
|
|
|
|
} else if (fchigh == 8 && fclow == 5) {
|
|
|
|
if (invert)
|
|
memcpy(fskType, "FSK1", 4);
|
|
else
|
|
memcpy(fskType, "FSK1a", 5);
|
|
|
|
} else {
|
|
memcpy(fskType, "FSK??", 5);
|
|
}
|
|
return fskType;
|
|
}
|
|
|
|
// fsk raw demod and print binary
|
|
// takes 4 arguments - Clock, invert, fchigh, fclow
|
|
// defaults: clock = 50, invert=1, fchigh=10, fclow=8 (RF/10 RF/8 (fsk2a))
|
|
int FSKrawDemod(uint8_t rfLen, uint8_t invert, uint8_t fchigh, uint8_t fclow, bool verbose) {
|
|
//raw fsk demod no manchester decoding no start bit finding just get binary from wave
|
|
if (getSignalProperties()->isnoise) {
|
|
if (verbose) {
|
|
PrintAndLogEx(INFO, "signal looks like noise");
|
|
}
|
|
return PM3_ESOFT;
|
|
}
|
|
|
|
uint8_t *bits = calloc(MAX_GRAPH_TRACE_LEN, sizeof(uint8_t));
|
|
if (bits == NULL) {
|
|
PrintAndLogEx(WARNING, "Failed to allocate memory");
|
|
return PM3_EMALLOC;
|
|
}
|
|
|
|
size_t bitlen = getFromGraphBuffer(bits);
|
|
if (bitlen == 0) {
|
|
PrintAndLogEx(DEBUG, "DEBUG: no data in graphbuf");
|
|
free(bits);
|
|
return PM3_ESOFT;
|
|
}
|
|
|
|
//get field clock lengths
|
|
if (!fchigh || !fclow) {
|
|
uint16_t fcs = countFC(bits, bitlen, true);
|
|
if (!fcs) {
|
|
fchigh = 10;
|
|
fclow = 8;
|
|
} else {
|
|
fchigh = (fcs >> 8) & 0x00FF;
|
|
fclow = fcs & 0x00FF;
|
|
}
|
|
}
|
|
//get bit clock length
|
|
if (!rfLen) {
|
|
int firstClockEdge = 0; //todo - align grid on graph with this...
|
|
rfLen = detectFSKClk(bits, bitlen, fchigh, fclow, &firstClockEdge);
|
|
if (!rfLen) rfLen = 50;
|
|
}
|
|
|
|
int start_idx = 0;
|
|
int size = fskdemod(bits, bitlen, rfLen, invert, fchigh, fclow, &start_idx);
|
|
if (size > 0) {
|
|
setDemodBuff(bits, size, 0);
|
|
setClockGrid(rfLen, start_idx);
|
|
|
|
// Now output the bitstream to the scrollback by line of 16 bits
|
|
if (verbose || g_debugMode) {
|
|
PrintAndLogEx(DEBUG, "DEBUG: (FSKrawDemod) using clock:%u, %sfc high:%u, fc low:%u"
|
|
, rfLen
|
|
, (invert) ? "inverted, " : ""
|
|
, fchigh
|
|
, fclow
|
|
);
|
|
PrintAndLogEx(NORMAL, "");
|
|
PrintAndLogEx(SUCCESS, _YELLOW_("%s") " decoded bitstream", GetFSKType(fchigh, fclow, invert));
|
|
PrintAndLogEx(INFO, "-----------------------");
|
|
printDemodBuff(0, false, false, false);
|
|
}
|
|
goto out;
|
|
} else {
|
|
PrintAndLogEx(DEBUG, "no FSK data found");
|
|
}
|
|
|
|
out:
|
|
free(bits);
|
|
return PM3_SUCCESS;
|
|
}
|
|
|
|
// fsk raw demod and print binary
|
|
// takes 4 arguments - Clock, invert, fchigh, fclow
|
|
// defaults: clock = 50, invert=1, fchigh=10, fclow=8 (RF/10 RF/8 (fsk2a))
|
|
static int CmdFSKrawdemod(const char *Cmd) {
|
|
CLIParserContext *ctx;
|
|
CLIParserInit(&ctx, "data rawdemod --fs",
|
|
"FSK demodulate the data in the GraphBuffer and output binary",
|
|
"data rawdemod --fs --> demod an fsk tag, using autodetect\n"
|
|
"data rawdemod --fs -c 32 --> demod an fsk tag, using a clock of RF/32, autodetect fc\n"
|
|
"data rawdemod --fs -i --> demod an fsk tag, using autodetect, invert output\n"
|
|
"data rawdemod --fs -c 32 -i --> demod an fsk tag, using a clock of RF/32, invert output, autodetect fc\n"
|
|
"data rawdemod --fs -c 64 --hi 8 --lo 5 --> demod an fsk1 RF/64 tag\n"
|
|
"data rawdemod --fs -c 50 --hi 10 --lo 8 --> demod an fsk2 RF/50 tag\n"
|
|
"data rawdemod --fs -c 50 -i --hi 10 --lo 8 --> demod an fsk2a RF/50 tag\n"
|
|
);
|
|
void *argtable[] = {
|
|
arg_param_begin,
|
|
arg_int0("c", "clk", "<dec>", "set clock manually (def: autodetect)"),
|
|
arg_lit0("i", "inv", "invert output"),
|
|
arg_int0(NULL, "hi", "<dec>", "larger field clock length (def: autodetect)"),
|
|
arg_int0(NULL, "lo", "<dec>", "small field clock length (def: autodetect)"),
|
|
arg_param_end
|
|
};
|
|
CLIExecWithReturn(ctx, Cmd, argtable, true);
|
|
|
|
uint8_t clk = (uint8_t)arg_get_int_def(ctx, 1, 0) & 0xFF;
|
|
bool invert = arg_get_lit(ctx, 2);
|
|
uint8_t fchigh = (uint8_t)arg_get_int_def(ctx, 3, 0) & 0xFF;
|
|
uint8_t fclow = (uint8_t)arg_get_int_def(ctx, 4, 0) & 0xFF;
|
|
CLIParserFree(ctx);
|
|
|
|
return FSKrawDemod(clk, invert, fchigh, fclow, true);
|
|
}
|
|
|
|
// attempt to psk1 demod graph buffer
|
|
int PSKDemod(int clk, int invert, int maxErr, bool verbose) {
|
|
if (getSignalProperties()->isnoise) {
|
|
if (verbose) {
|
|
PrintAndLogEx(INFO, "signal looks like noise");
|
|
}
|
|
return PM3_ESOFT;
|
|
}
|
|
|
|
uint8_t *bits = calloc(MAX_GRAPH_TRACE_LEN, sizeof(uint8_t));
|
|
if (bits == NULL) {
|
|
PrintAndLogEx(WARNING, "Failed to allocate memory");
|
|
return PM3_EMALLOC;
|
|
}
|
|
size_t bitlen = getFromGraphBuffer(bits);
|
|
if (bitlen == 0) {
|
|
free(bits);
|
|
return PM3_ESOFT;
|
|
}
|
|
|
|
int startIdx = 0;
|
|
int errCnt = pskRawDemod_ext(bits, &bitlen, &clk, &invert, &startIdx);
|
|
if (errCnt > maxErr) {
|
|
if (g_debugMode || verbose) PrintAndLogEx(DEBUG, "DEBUG: (PSKdemod) Too many errors found, clk: %d, invert: %d, numbits: %zu, errCnt: %d", clk, invert, bitlen, errCnt);
|
|
free(bits);
|
|
return PM3_ESOFT;
|
|
}
|
|
if (errCnt < 0 || bitlen < 16) { //throw away static - allow 1 and -1 (in case of threshold command first)
|
|
if (g_debugMode || verbose) PrintAndLogEx(DEBUG, "DEBUG: (PSKdemod) no data found, clk: %d, invert: %d, numbits: %zu, errCnt: %d", clk, invert, bitlen, errCnt);
|
|
free(bits);
|
|
return PM3_ESOFT;
|
|
}
|
|
if (verbose || g_debugMode) {
|
|
PrintAndLogEx(DEBUG, "DEBUG: (PSKdemod) Using Clock:%d, invert:%d, Bits Found:%zu", clk, invert, bitlen);
|
|
if (errCnt > 0) {
|
|
PrintAndLogEx(DEBUG, "DEBUG: (PSKdemod) errors during Demoding (shown as 7 in bit stream): %d", errCnt);
|
|
}
|
|
}
|
|
//prime g_DemodBuffer for output
|
|
setDemodBuff(bits, bitlen, 0);
|
|
setClockGrid(clk, startIdx);
|
|
free(bits);
|
|
return PM3_SUCCESS;
|
|
}
|
|
|
|
// takes 3 arguments - clock, invert, maxErr as integers
|
|
// attempts to demodulate nrz only
|
|
// prints binary found and saves in g_DemodBuffer for further commands
|
|
int NRZrawDemod(int clk, int invert, int maxErr, bool verbose) {
|
|
|
|
int errCnt = 0, clkStartIdx = 0;
|
|
|
|
if (getSignalProperties()->isnoise) {
|
|
if (verbose) {
|
|
PrintAndLogEx(INFO, "signal looks like noise");
|
|
}
|
|
return PM3_ESOFT;
|
|
}
|
|
|
|
uint8_t *bits = calloc(MAX_GRAPH_TRACE_LEN, sizeof(uint8_t));
|
|
if (bits == NULL) {
|
|
PrintAndLogEx(WARNING, "Failed to allocate memory");
|
|
return PM3_EMALLOC;
|
|
}
|
|
|
|
size_t bitlen = getFromGraphBuffer(bits);
|
|
|
|
if (bitlen == 0) {
|
|
free(bits);
|
|
return PM3_ESOFT;
|
|
}
|
|
|
|
errCnt = nrzRawDemod(bits, &bitlen, &clk, &invert, &clkStartIdx);
|
|
if (errCnt > maxErr) {
|
|
PrintAndLogEx(DEBUG, "DEBUG: (NRZrawDemod) Too many errors found, clk: %d, invert: %d, numbits: %zu, errCnt: %d", clk, invert, bitlen, errCnt);
|
|
free(bits);
|
|
return PM3_ESOFT;
|
|
}
|
|
if (errCnt < 0 || bitlen < 16) { //throw away static - allow 1 and -1 (in case of threshold command first)
|
|
PrintAndLogEx(DEBUG, "DEBUG: (NRZrawDemod) no data found, clk: %d, invert: %d, numbits: %zu, errCnt: %d", clk, invert, bitlen, errCnt);
|
|
free(bits);
|
|
return PM3_ESOFT;
|
|
}
|
|
|
|
if (verbose || g_debugMode) PrintAndLogEx(DEBUG, "DEBUG: (NRZrawDemod) Tried NRZ Demod using Clock: %d - invert: %d - Bits Found: %zu", clk, invert, bitlen);
|
|
//prime g_DemodBuffer for output
|
|
setDemodBuff(bits, bitlen, 0);
|
|
setClockGrid(clk, clkStartIdx);
|
|
|
|
|
|
if (errCnt > 0 && (verbose || g_debugMode)) PrintAndLogEx(DEBUG, "DEBUG: (NRZrawDemod) Errors during Demoding (shown as 7 in bit stream): %d", errCnt);
|
|
if (verbose || g_debugMode) {
|
|
PrintAndLogEx(SUCCESS, "NRZ demoded bitstream");
|
|
PrintAndLogEx(INFO, "---------------------");
|
|
// Now output the bitstream to the scrollback by line of 16 bits
|
|
printDemodBuff(0, false, invert, false);
|
|
}
|
|
|
|
free(bits);
|
|
return PM3_SUCCESS;
|
|
}
|
|
|
|
static int CmdNRZrawDemod(const char *Cmd) {
|
|
CLIParserContext *ctx;
|
|
CLIParserInit(&ctx, "data rawdemod --nr",
|
|
"NRZ/DIRECT demodulate the data in the GraphBuffer and output binary",
|
|
"data rawdemod --nr --> demod a nrz/direct tag, using autodetect\n"
|
|
"data rawdemod --nr -c 32 --> demod a nrz/direct tag, using a clock of RF/32\n"
|
|
"data rawdemod --nr -i --> demod a nrz/direct tag, using autodetect, invert output\n"
|
|
"data rawdemod --nr -c 32 -i --> demod a nrz/direct tag, using a clock of RF/32, invert output\n"
|
|
"data rawdemod --nr -c 64 -i --max 0 --> demod a nrz/direct tag, using a clock of RF/64, inverting and allowing 0 demod errors\n"
|
|
);
|
|
void *argtable[] = {
|
|
arg_param_begin,
|
|
arg_int0("c", "clk", "<dec>", "set clock manually (def autodetect)"),
|
|
arg_lit0("i", "inv", "invert output"),
|
|
arg_int0(NULL, "max", "<dec>", "maximum allowed errors (def 100)"),
|
|
arg_param_end
|
|
};
|
|
CLIExecWithReturn(ctx, Cmd, argtable, true);
|
|
|
|
uint16_t clk = (uint16_t)arg_get_int_def(ctx, 1, 0);
|
|
bool invert = arg_get_lit(ctx, 2);
|
|
uint8_t max_err = (uint8_t)arg_get_int_def(ctx, 3, 100) & 0xFF;
|
|
CLIParserFree(ctx);
|
|
|
|
return NRZrawDemod(clk, invert, max_err, true);
|
|
}
|
|
|
|
// takes 3 arguments - clock, invert, max_err as integers
|
|
// attempts to demodulate psk only
|
|
// prints binary found and saves in g_DemodBuffer for further commands
|
|
int CmdPSK1rawDemod(const char *Cmd) {
|
|
CLIParserContext *ctx;
|
|
CLIParserInit(&ctx, "data rawdemod --p1",
|
|
"PSK1 demodulate the data in the GraphBuffer and output binary",
|
|
"data rawdemod --p1 --> demod a psk1 tag, using autodetect\n"
|
|
"data rawdemod --p1 -c 32 --> demod a psk1 tag, using a clock of RF/32\n"
|
|
"data rawdemod --p1 -i --> demod a psk1 tag, using autodetect, invert output\n"
|
|
"data rawdemod --p1 -c 32 -i --> demod a psk1 tag, using a clock of RF/32, invert output\n"
|
|
"data rawdemod --p1 -c 64 -i --max 0 --> demod a psk1 tag, using a clock of RF/64, inverting and allowing 0 demod errors\n"
|
|
);
|
|
void *argtable[] = {
|
|
arg_param_begin,
|
|
arg_int0("c", "clk", "<dec>", "set clock manually (def autodetect)"),
|
|
arg_lit0("i", "inv", "invert output"),
|
|
arg_int0(NULL, "max", "<dec>", "maximum allowed errors (def 100)"),
|
|
arg_param_end
|
|
};
|
|
CLIExecWithReturn(ctx, Cmd, argtable, true);
|
|
|
|
uint16_t clk = (uint16_t)arg_get_int_def(ctx, 1, 0);
|
|
bool invert = arg_get_lit(ctx, 2);
|
|
uint8_t max_err = (uint8_t)arg_get_int_def(ctx, 3, 100) & 0xFF;
|
|
CLIParserFree(ctx);
|
|
|
|
int ans = PSKDemod(clk, invert, max_err, true);
|
|
//output
|
|
if (ans != PM3_SUCCESS) {
|
|
if (g_debugMode) PrintAndLogEx(ERR, "Error demoding: %d", ans);
|
|
return PM3_ESOFT;
|
|
}
|
|
PrintAndLogEx(SUCCESS, _YELLOW_("PSK1") " demoded bitstream");
|
|
PrintAndLogEx(INFO, "----------------------");
|
|
// Now output the bitstream to the scrollback by line of 16 bits
|
|
printDemodBuff(0, false, invert, false);
|
|
return PM3_SUCCESS;
|
|
}
|
|
|
|
// takes same args as cmdpsk1rawdemod
|
|
static int CmdPSK2rawDemod(const char *Cmd) {
|
|
CLIParserContext *ctx;
|
|
CLIParserInit(&ctx, "data rawdemod --p2",
|
|
"PSK2 demodulate the data in the GraphBuffer and output binary",
|
|
"data rawdemod --p2 --> demod a psk2 tag, using autodetect\n"
|
|
"data rawdemod --p2 -c 32 --> demod a psk2 tag, using a clock of RF/32\n"
|
|
"data rawdemod --p2 -i --> demod a psk2 tag, using autodetect, invert output\n"
|
|
"data rawdemod --p2 -c 32 -i --> demod a psk2 tag, using a clock of RF/32, invert output\n"
|
|
"data rawdemod --p2 -c 64 -i --max 0 --> demod a psk2 tag, using a clock of RF/64, inverting and allowing 0 demod errors\n"
|
|
);
|
|
void *argtable[] = {
|
|
arg_param_begin,
|
|
arg_int0("c", "clk", "<dec>", "set clock manually (def autodetect)"),
|
|
arg_lit0("i", "inv", "invert output"),
|
|
arg_int0(NULL, "max", "<dec>", "maximum allowed errors (def 100)"),
|
|
arg_param_end
|
|
};
|
|
CLIExecWithReturn(ctx, Cmd, argtable, true);
|
|
|
|
uint8_t clk = (uint8_t)arg_get_int_def(ctx, 1, 0) & 0xFF;
|
|
bool invert = arg_get_lit(ctx, 2);
|
|
uint8_t max_err = (uint8_t)arg_get_int_def(ctx, 3, 100) & 0xFF;
|
|
CLIParserFree(ctx);
|
|
|
|
int ans = PSKDemod(clk, invert, max_err, true);
|
|
if (ans != PM3_SUCCESS) {
|
|
if (g_debugMode) PrintAndLogEx(ERR, "Error demoding: %d", ans);
|
|
return PM3_ESOFT;
|
|
}
|
|
psk1TOpsk2(g_DemodBuffer, g_DemodBufferLen);
|
|
PrintAndLogEx(SUCCESS, _YELLOW_("PSK2") " demoded bitstream");
|
|
PrintAndLogEx(INFO, "----------------------");
|
|
// Now output the bitstream to the scrollback by line of 16 bits
|
|
printDemodBuff(0, false, invert, false);
|
|
return PM3_SUCCESS;
|
|
}
|
|
|
|
// combines all raw demod functions into one menu command
|
|
static int CmdRawDemod(const char *Cmd) {
|
|
|
|
CLIParserContext *ctx;
|
|
CLIParserInit(&ctx, "data rawdemod",
|
|
"Demodulate the data in the GraphBuffer and output binary",
|
|
"data rawdemod --fs --> demod FSK - autodetect\n"
|
|
"data rawdemod --ab --> demod ASK/BIPHASE - autodetect\n"
|
|
"data rawdemod --am --> demod ASK/MANCHESTER - autodetect\n"
|
|
"data rawdemod --ar --> demod ASK/RAW - autodetect\n"
|
|
"data rawdemod --nr --> demod NRZ/DIRECT - autodetect\n"
|
|
"data rawdemod --p1 --> demod PSK1 - autodetect\n"
|
|
"data rawdemod --p2 --> demod PSK2 - autodetect\n"
|
|
);
|
|
void *argtable[] = {
|
|
arg_param_begin,
|
|
arg_lit0(NULL, "ab", "ASK/Biphase demodulation"),
|
|
arg_lit0(NULL, "am", "ASK/Manchester demodulation"),
|
|
arg_lit0(NULL, "ar", "ASK/Raw demodulation"),
|
|
arg_lit0(NULL, "fs", "FSK demodulation"),
|
|
arg_lit0(NULL, "nr", "NRZ/Direct demodulation"),
|
|
arg_lit0(NULL, "p1", "PSK 1 demodulation"),
|
|
arg_lit0(NULL, "p2", "PSK 2 demodulation"),
|
|
arg_strn(NULL, NULL, "<params>", 0, 35, "params for sub command"),
|
|
arg_param_end
|
|
};
|
|
|
|
//
|
|
char tmp[5];
|
|
size_t n = MIN(strlen(Cmd), sizeof(tmp) - 1);
|
|
memset(tmp, 0, sizeof(tmp));
|
|
strncpy(tmp, Cmd, sizeof(tmp) - 1);
|
|
|
|
CLIExecWithReturn(ctx, tmp, argtable, false);
|
|
bool ab = arg_get_lit(ctx, 1);
|
|
bool am = arg_get_lit(ctx, 2);
|
|
bool ar = arg_get_lit(ctx, 3);
|
|
bool fs = arg_get_lit(ctx, 4);
|
|
bool nr = arg_get_lit(ctx, 5);
|
|
bool p1 = arg_get_lit(ctx, 6);
|
|
bool p2 = arg_get_lit(ctx, 7);
|
|
CLIParserFree(ctx);
|
|
|
|
int foo = (ab + am + ar + fs + nr + p1 + p2);
|
|
if (foo > 1) {
|
|
PrintAndLogEx(WARNING, "please, select only one modulation");
|
|
return PM3_EINVARG;
|
|
}
|
|
if (foo == 0) {
|
|
PrintAndLogEx(WARNING, "please, select a modulation");
|
|
return PM3_EINVARG;
|
|
}
|
|
|
|
int ans = 0;
|
|
const char *s = Cmd + n;
|
|
if (fs)
|
|
ans = CmdFSKrawdemod(s);
|
|
else if (ab)
|
|
ans = Cmdaskbiphdemod(s);
|
|
else if (am)
|
|
ans = Cmdaskmandemod(s);
|
|
else if (ar)
|
|
ans = Cmdaskrawdemod(s);
|
|
else if (nr)
|
|
ans = CmdNRZrawDemod(s);
|
|
else if (p1)
|
|
ans = CmdPSK1rawDemod(s);
|
|
else if (p2)
|
|
ans = CmdPSK2rawDemod(s);
|
|
|
|
return ans;
|
|
}
|
|
|
|
void setClockGrid(uint32_t clk, int offset) {
|
|
g_DemodStartIdx = offset;
|
|
g_DemodClock = clk;
|
|
if (clk == 0 && offset == 0)
|
|
PrintAndLogEx(DEBUG, "DEBUG: (setClockGrid) clear settings");
|
|
else
|
|
PrintAndLogEx(DEBUG, "DEBUG: (setClockGrid) demodoffset %d, clk %d", offset, clk);
|
|
|
|
if (offset > clk) offset %= clk;
|
|
if (offset < 0) offset += clk;
|
|
|
|
if (offset > g_GraphTraceLen || offset < 0) return;
|
|
if (clk < 8 || clk > g_GraphTraceLen) {
|
|
g_GridLocked = false;
|
|
g_GridOffset = 0;
|
|
g_PlotGridX = 0;
|
|
g_DefaultGridX = 0;
|
|
RepaintGraphWindow();
|
|
} else {
|
|
g_GridLocked = true;
|
|
g_GridOffset = offset;
|
|
g_PlotGridX = clk;
|
|
g_DefaultGridX = clk;
|
|
RepaintGraphWindow();
|
|
}
|
|
}
|
|
|
|
int CmdGrid(const char *Cmd) {
|
|
CLIParserContext *ctx;
|
|
CLIParserInit(&ctx, "data grid",
|
|
"This function overlay grid on graph plot window.\n"
|
|
"use zero value to turn off either",
|
|
"data grid --> turn off\n"
|
|
"data grid -x 64 -y 50"
|
|
);
|
|
void *argtable[] = {
|
|
arg_param_begin,
|
|
arg_dbl0("x", NULL, "<dec>", "plot grid X coord"),
|
|
arg_dbl0("y", NULL, "<dec>", "plot grid Y coord"),
|
|
arg_param_end
|
|
};
|
|
CLIExecWithReturn(ctx, Cmd, argtable, true);
|
|
g_PlotGridX = arg_get_dbl_def(ctx, 1, 0);
|
|
g_PlotGridY = arg_get_dbl_def(ctx, 2, 0);
|
|
CLIParserFree(ctx);
|
|
|
|
PrintAndLogEx(DEBUG, "Setting X %.0f Y %.0f", g_PlotGridX, g_PlotGridY);
|
|
g_DefaultGridX = g_PlotGridX;
|
|
g_DefaultGridY = g_PlotGridY;
|
|
RepaintGraphWindow();
|
|
return PM3_SUCCESS;
|
|
}
|
|
|
|
static int CmdSetGraphMarkers(const char *Cmd) {
|
|
CLIParserContext *ctx;
|
|
CLIParserInit(&ctx, "data setgraphmarkers",
|
|
"Set the locations of the markers in the graph window",
|
|
"data setgraphmarkers --> reset the markers\n"
|
|
"data setgraphmarkers -a 64 --> set A, reset the rest\n"
|
|
"data setgraphmarkers -d --keep --> set D, keep the rest"
|
|
);
|
|
void *argtable[] = {
|
|
arg_param_begin,
|
|
arg_lit0(NULL, "keep", "keep the current values of the markers"),
|
|
arg_u64_0("a", NULL, "<dec>", "yellow marker"),
|
|
arg_u64_0("b", NULL, "<dec>", "purple marker"),
|
|
arg_u64_0("c", NULL, "<dec>", "orange marker"),
|
|
arg_u64_0("d", NULL, "<dec>", "blue marker"),
|
|
arg_param_end
|
|
};
|
|
CLIExecWithReturn(ctx, Cmd, argtable, true);
|
|
bool keep = arg_get_lit(ctx, 1);
|
|
g_MarkerA.pos = arg_get_u32_def(ctx, 2, (keep ? g_MarkerA.pos : 0));
|
|
g_MarkerB.pos = arg_get_u32_def(ctx, 3, (keep ? g_MarkerB.pos : 0));
|
|
g_MarkerC.pos = arg_get_u32_def(ctx, 4, (keep ? g_MarkerC.pos : 0));
|
|
g_MarkerD.pos = arg_get_u32_def(ctx, 5, (keep ? g_MarkerD.pos : 0));
|
|
CLIParserFree(ctx);
|
|
PrintAndLogEx(INFO, "Setting markers " _BRIGHT_YELLOW_("A") "=%u, "_BRIGHT_MAGENTA_("B") "=%u, "_RED_("C") "=%u, "_BLUE_("D") "=%u",
|
|
g_MarkerA.pos,
|
|
g_MarkerB.pos,
|
|
g_MarkerC.pos,
|
|
g_MarkerD.pos
|
|
);
|
|
RepaintGraphWindow();
|
|
return PM3_SUCCESS;
|
|
}
|
|
|
|
static int CmdHexsamples(const char *Cmd) {
|
|
|
|
CLIParserContext *ctx;
|
|
CLIParserInit(&ctx, "data hexsamples",
|
|
"Dump big buffer as hex bytes",
|
|
"data hexsamples -n 128 --> dumps 128 bytes from offset 0"
|
|
);
|
|
void *argtable[] = {
|
|
arg_param_begin,
|
|
arg_u64_0("b", "breaks", "<dec>", "row break, def 16"),
|
|
arg_u64_0("n", NULL, "<dec>", "num of bytes to download"),
|
|
arg_u64_0("o", "offset", "<hex>", "offset in big buffer"),
|
|
arg_param_end
|
|
};
|
|
CLIExecWithReturn(ctx, Cmd, argtable, false);
|
|
uint32_t breaks = arg_get_u32_def(ctx, 1, 16);
|
|
uint32_t requested = arg_get_u32_def(ctx, 2, 8);
|
|
uint32_t offset = arg_get_u32_def(ctx, 3, 0);
|
|
CLIParserFree(ctx);
|
|
|
|
// sanity checks
|
|
if (requested > g_pm3_capabilities.bigbuf_size) {
|
|
requested = g_pm3_capabilities.bigbuf_size;
|
|
PrintAndLogEx(INFO, "n is larger than big buffer size, will use %u", requested);
|
|
}
|
|
|
|
uint8_t got[g_pm3_capabilities.bigbuf_size];
|
|
if (offset + requested > sizeof(got)) {
|
|
PrintAndLogEx(NORMAL, "Tried to read past end of buffer, <bytes %u> + <offset %u> > %d"
|
|
, requested
|
|
, offset
|
|
, g_pm3_capabilities.bigbuf_size
|
|
);
|
|
return PM3_EINVARG;
|
|
}
|
|
|
|
if (!GetFromDevice(BIG_BUF, got, requested, offset, NULL, 0, NULL, 2500, false)) {
|
|
PrintAndLogEx(WARNING, "command execution time out");
|
|
return PM3_ESOFT;
|
|
}
|
|
|
|
print_hex_break(got, requested, breaks);
|
|
return PM3_SUCCESS;
|
|
}
|
|
|
|
static int CmdHide(const char *Cmd) {
|
|
CLIParserContext *ctx;
|
|
CLIParserInit(&ctx, "data hide",
|
|
"Show graph window",
|
|
"data hide"
|
|
);
|
|
void *argtable[] = {
|
|
arg_param_begin,
|
|
arg_param_end
|
|
};
|
|
CLIExecWithReturn(ctx, Cmd, argtable, true);
|
|
CLIParserFree(ctx);
|
|
HideGraphWindow();
|
|
return PM3_SUCCESS;
|
|
}
|
|
|
|
// zero mean g_GraphBuffer
|
|
int CmdHpf(const char *Cmd) {
|
|
CLIParserContext *ctx;
|
|
CLIParserInit(&ctx, "data hpf",
|
|
"Remove DC offset from trace. It should centralize around 0",
|
|
"data hpf"
|
|
);
|
|
void *argtable[] = {
|
|
arg_param_begin,
|
|
arg_param_end
|
|
};
|
|
CLIExecWithReturn(ctx, Cmd, argtable, true);
|
|
CLIParserFree(ctx);
|
|
|
|
uint8_t *bits = calloc(g_GraphTraceLen, sizeof(uint8_t));
|
|
if (bits == NULL) {
|
|
PrintAndLogEx(WARNING, "Failed to allocate memory");
|
|
return PM3_EMALLOC;
|
|
}
|
|
size_t size = getFromGraphBuffer(bits);
|
|
removeSignalOffset(bits, size);
|
|
// push it back to graph
|
|
setGraphBuffer(bits, size);
|
|
// set signal properties low/high/mean/amplitude and is_noise detection
|
|
computeSignalProperties(bits, size);
|
|
|
|
RepaintGraphWindow();
|
|
free(bits);
|
|
return PM3_SUCCESS;
|
|
}
|
|
|
|
static bool _headBit(BitstreamOut_t *stream) {
|
|
int bytepos = stream->position >> 3; // divide by 8
|
|
int bitpos = (stream->position++) & 7; // mask out 00000111
|
|
return (*(stream->buffer + bytepos) >> (7 - bitpos)) & 1;
|
|
}
|
|
|
|
static uint8_t getByte(uint8_t bits_per_sample, BitstreamOut_t *b) {
|
|
uint8_t val = 0;
|
|
for (int i = 0 ; i < bits_per_sample; i++)
|
|
val |= (_headBit(b) << (7 - i));
|
|
|
|
return val;
|
|
}
|
|
|
|
int getSamples(uint32_t n, bool verbose) {
|
|
return getSamplesEx(0, n, verbose, false);
|
|
}
|
|
|
|
int getSamplesEx(uint32_t start, uint32_t end, bool verbose, bool ignore_lf_config) {
|
|
|
|
if (end < start) {
|
|
PrintAndLogEx(WARNING, "error, end (%u) is smaller than start (%u)", end, start);
|
|
return PM3_EINVARG;
|
|
}
|
|
|
|
// If we get all but the last byte in bigbuf,
|
|
// we don't have to worry about remaining trash
|
|
// in the last byte in case the bits-per-sample
|
|
// does not line up on byte boundaries
|
|
uint8_t got[g_pm3_capabilities.bigbuf_size - 1];
|
|
memset(got, 0x00, sizeof(got));
|
|
|
|
uint32_t n = end - start;
|
|
|
|
if (n == 0 || n > g_pm3_capabilities.bigbuf_size - 1) {
|
|
n = g_pm3_capabilities.bigbuf_size - 1;
|
|
}
|
|
|
|
if (verbose) {
|
|
PrintAndLogEx(INFO, "Reading " _YELLOW_("%u") " bytes from device memory", n);
|
|
}
|
|
|
|
PacketResponseNG resp;
|
|
if (GetFromDevice(BIG_BUF, got, n, start, NULL, 0, &resp, 10000, true) == false) {
|
|
PrintAndLogEx(WARNING, "timeout while waiting for reply");
|
|
return PM3_ETIMEOUT;
|
|
}
|
|
|
|
if (verbose) {
|
|
PrintAndLogEx(SUCCESS, "Data fetched");
|
|
}
|
|
|
|
uint8_t bits_per_sample = 8;
|
|
|
|
if (IfPm3Lf() && ignore_lf_config == false) {
|
|
sample_config sc;
|
|
lf_getconfig(&sc);
|
|
if (verbose) {
|
|
PrintAndLogEx(INFO, "Samples @ " _YELLOW_("%d") " bits/smpl, decimation 1:%d ", sc.bits_per_sample, sc.decimation);
|
|
}
|
|
bits_per_sample = sc.bits_per_sample;
|
|
}
|
|
|
|
return getSamplesFromBufEx(got, n, bits_per_sample, verbose);;
|
|
}
|
|
|
|
int getSamplesFromBufEx(uint8_t *data, size_t sample_num, uint8_t bits_per_sample, bool verbose) {
|
|
|
|
size_t max_num = MIN(sample_num, MAX_GRAPH_TRACE_LEN);
|
|
|
|
if (bits_per_sample < 8) {
|
|
|
|
if (verbose) PrintAndLogEx(INFO, "Unpacking...");
|
|
|
|
BitstreamOut_t bout = {data, bits_per_sample * sample_num, 0};
|
|
size_t j = 0;
|
|
for (j = 0; j < max_num; j++) {
|
|
uint8_t sample = getByte(bits_per_sample, &bout);
|
|
g_GraphBuffer[j] = ((int) sample) - 127;
|
|
}
|
|
g_GraphTraceLen = j;
|
|
|
|
if (verbose) PrintAndLogEx(INFO, "Unpacked %zu samples", j);
|
|
|
|
} else {
|
|
for (size_t j = 0; j < max_num; j++) {
|
|
g_GraphBuffer[j] = ((int)data[j]) - 127;
|
|
}
|
|
g_GraphTraceLen = max_num;
|
|
}
|
|
|
|
uint8_t *bits = calloc(g_GraphTraceLen, sizeof(uint8_t));
|
|
if (bits == NULL) {
|
|
PrintAndLogEx(WARNING, "Failed to allocate memory");
|
|
return PM3_EMALLOC;
|
|
}
|
|
size_t size = getFromGraphBuffer(bits);
|
|
// set signal properties low/high/mean/amplitude and is_noise detection
|
|
computeSignalProperties(bits, size);
|
|
free(bits);
|
|
|
|
setClockGrid(0, 0);
|
|
g_DemodBufferLen = 0;
|
|
RepaintGraphWindow();
|
|
|
|
return PM3_SUCCESS;
|
|
}
|
|
|
|
static int CmdSamples(const char *Cmd) {
|
|
|
|
CLIParserContext *ctx;
|
|
CLIParserInit(&ctx, "data samples",
|
|
"Get raw samples for graph window (GraphBuffer) from device.\n"
|
|
"If 0, then get whole big buffer from device.",
|
|
"data samples\n"
|
|
"data samples -n 10000"
|
|
);
|
|
void *argtable[] = {
|
|
arg_param_begin,
|
|
arg_int0("n", NULL, "<dec>", "num of samples (512 - 40000)"),
|
|
arg_lit0("v", "verbose", "verbose output"),
|
|
arg_param_end
|
|
};
|
|
CLIExecWithReturn(ctx, Cmd, argtable, true);
|
|
int n = arg_get_int_def(ctx, 1, 0);
|
|
bool verbose = arg_get_lit(ctx, 2);
|
|
CLIParserFree(ctx);
|
|
return getSamples(n, verbose);
|
|
}
|
|
|
|
|
|
static int CmdLoad(const char *Cmd) {
|
|
|
|
CLIParserContext *ctx;
|
|
CLIParserInit(&ctx, "data load",
|
|
"This command loads the contents of a pm3 file into graph window\n",
|
|
"data load -f myfilename"
|
|
);
|
|
|
|
void *argtable[] = {
|
|
arg_param_begin,
|
|
arg_str1("f", "file", "<fn>", "file to load"),
|
|
arg_lit0("b", "bin", "binary file"),
|
|
arg_lit0("n", "no-fix", "Load data from file without any transformations"),
|
|
arg_param_end
|
|
};
|
|
CLIExecWithReturn(ctx, Cmd, argtable, false);
|
|
|
|
int fnlen = 0;
|
|
char filename[FILE_PATH_SIZE] = {0};
|
|
CLIParamStrToBuf(arg_get_str(ctx, 1), (uint8_t *)filename, FILE_PATH_SIZE, &fnlen);
|
|
bool is_bin = arg_get_lit(ctx, 2);
|
|
bool nofix = arg_get_lit(ctx, 3);
|
|
CLIParserFree(ctx);
|
|
|
|
char *path = NULL;
|
|
if (searchFile(&path, TRACES_SUBDIR, filename, ".pm3", true) != PM3_SUCCESS) {
|
|
if (searchFile(&path, TRACES_SUBDIR, filename, "", false) != PM3_SUCCESS) {
|
|
return PM3_EFILE;
|
|
}
|
|
}
|
|
|
|
FILE *f;
|
|
if (is_bin)
|
|
f = fopen(path, "rb");
|
|
else
|
|
f = fopen(path, "r");
|
|
|
|
if (f == NULL) {
|
|
PrintAndLogEx(WARNING, "couldn't open `" _YELLOW_("%s") "`", path);
|
|
free(path);
|
|
return PM3_EFILE;
|
|
}
|
|
free(path);
|
|
|
|
g_GraphTraceLen = 0;
|
|
|
|
if (is_bin) {
|
|
uint8_t val[2];
|
|
while (fread(val, 1, 1, f)) {
|
|
g_GraphBuffer[g_GraphTraceLen] = val[0] - 127;
|
|
g_GraphTraceLen++;
|
|
|
|
if (g_GraphTraceLen >= MAX_GRAPH_TRACE_LEN) {
|
|
break;
|
|
}
|
|
}
|
|
} else {
|
|
char line[80];
|
|
while (fgets(line, sizeof(line), f)) {
|
|
g_GraphBuffer[g_GraphTraceLen] = atoi(line);
|
|
g_GraphTraceLen++;
|
|
|
|
if (g_GraphTraceLen >= MAX_GRAPH_TRACE_LEN)
|
|
break;
|
|
}
|
|
}
|
|
fclose(f);
|
|
|
|
PrintAndLogEx(SUCCESS, "loaded " _YELLOW_("%s") " samples", commaprint(g_GraphTraceLen));
|
|
|
|
if (nofix == false) {
|
|
uint8_t *bits = calloc(g_GraphTraceLen, sizeof(uint8_t));
|
|
if (bits == NULL) {
|
|
PrintAndLogEx(WARNING, "Failed to allocate memory");
|
|
return PM3_EMALLOC;
|
|
}
|
|
size_t size = getFromGraphBuffer(bits);
|
|
|
|
removeSignalOffset(bits, size);
|
|
setGraphBuffer(bits, size);
|
|
computeSignalProperties(bits, size);
|
|
free(bits);
|
|
}
|
|
|
|
setClockGrid(0, 0);
|
|
g_DemodBufferLen = 0;
|
|
RepaintGraphWindow();
|
|
return PM3_SUCCESS;
|
|
}
|
|
|
|
// trim graph from the end
|
|
int CmdLtrim(const char *Cmd) {
|
|
CLIParserContext *ctx;
|
|
CLIParserInit(&ctx, "data ltrim",
|
|
"Trim samples from left of trace",
|
|
"data ltrim -i 300 --> remove from start 0 to index 300"
|
|
);
|
|
void *argtable[] = {
|
|
arg_param_begin,
|
|
arg_u64_1("i", "idx", "<dec>", "index in graph buffer"),
|
|
arg_param_end
|
|
};
|
|
CLIExecWithReturn(ctx, Cmd, argtable, false);
|
|
uint32_t ds = arg_get_u32(ctx, 1);
|
|
CLIParserFree(ctx);
|
|
|
|
// sanitycheck
|
|
if (g_GraphTraceLen <= ds) {
|
|
PrintAndLogEx(WARNING, "index out of bounds");
|
|
return PM3_EINVARG;
|
|
}
|
|
|
|
for (size_t i = ds; i < g_GraphTraceLen; ++i) {
|
|
g_GraphBuffer[i - ds] = g_GraphBuffer[i];
|
|
}
|
|
g_GraphTraceLen -= ds;
|
|
g_DemodStartIdx -= ds;
|
|
RepaintGraphWindow();
|
|
return PM3_SUCCESS;
|
|
}
|
|
|
|
// trim graph from the beginning
|
|
static int CmdRtrim(const char *Cmd) {
|
|
|
|
CLIParserContext *ctx;
|
|
CLIParserInit(&ctx, "data rtrim",
|
|
"Trim samples from right of trace",
|
|
"data rtrim -i 4000 --> remove from index 4000 to end of graph buffer"
|
|
);
|
|
void *argtable[] = {
|
|
arg_param_begin,
|
|
arg_u64_1("i", "idx", "<dec>", "index in graph buffer"),
|
|
arg_param_end
|
|
};
|
|
CLIExecWithReturn(ctx, Cmd, argtable, false);
|
|
uint32_t ds = arg_get_u32(ctx, 1);
|
|
CLIParserFree(ctx);
|
|
|
|
// sanitycheck
|
|
if (g_GraphTraceLen <= ds) {
|
|
PrintAndLogEx(WARNING, "index out of bounds");
|
|
return PM3_EINVARG;
|
|
}
|
|
|
|
g_GraphTraceLen = ds;
|
|
RepaintGraphWindow();
|
|
return PM3_SUCCESS;
|
|
}
|
|
|
|
// trim graph (middle) piece
|
|
static int CmdMtrim(const char *Cmd) {
|
|
|
|
CLIParserContext *ctx;
|
|
CLIParserInit(&ctx, "data mtrim",
|
|
"Trim out samples from\n"
|
|
" start 0 to `-s index`\n"
|
|
"AND\n"
|
|
" from `-e index` to end of graph buffer",
|
|
"data mtrim -s 1000 -e 2000 --> keep all between index 1000 and 2000"
|
|
);
|
|
void *argtable[] = {
|
|
arg_param_begin,
|
|
arg_u64_1("s", "start", "<dec>", "start point"),
|
|
arg_u64_1("e", "end", "<dec>", "end point"),
|
|
arg_param_end
|
|
};
|
|
CLIExecWithReturn(ctx, Cmd, argtable, false);
|
|
uint32_t start = arg_get_u32(ctx, 1);
|
|
uint32_t stop = arg_get_u32(ctx, 2);
|
|
CLIParserFree(ctx);
|
|
|
|
if (start > g_GraphTraceLen || stop > g_GraphTraceLen || start >= stop) {
|
|
PrintAndLogEx(WARNING, "start and end points doesn't align");
|
|
return PM3_EINVARG;
|
|
}
|
|
|
|
// leave start position sample
|
|
start++;
|
|
|
|
g_GraphTraceLen = stop - start;
|
|
for (uint32_t i = 0; i < g_GraphTraceLen; i++) {
|
|
g_GraphBuffer[i] = g_GraphBuffer[start + i];
|
|
}
|
|
|
|
g_DemodStartIdx = 0;
|
|
RepaintGraphWindow();
|
|
return PM3_SUCCESS;
|
|
}
|
|
|
|
int CmdNorm(const char *Cmd) {
|
|
|
|
CLIParserContext *ctx;
|
|
CLIParserInit(&ctx, "data norm",
|
|
"Normalize max/min to +/-128",
|
|
"data norm"
|
|
);
|
|
void *argtable[] = {
|
|
arg_param_begin,
|
|
arg_param_end
|
|
};
|
|
CLIExecWithReturn(ctx, Cmd, argtable, true);
|
|
CLIParserFree(ctx);
|
|
|
|
int max = INT_MIN, min = INT_MAX;
|
|
|
|
// Find local min, max
|
|
for (uint32_t i = 10; i < g_GraphTraceLen; ++i) {
|
|
if (g_GraphBuffer[i] > max) max = g_GraphBuffer[i];
|
|
if (g_GraphBuffer[i] < min) min = g_GraphBuffer[i];
|
|
}
|
|
|
|
if ((g_GraphTraceLen > 10) && (max != min)) {
|
|
for (uint32_t i = 0; i < g_GraphTraceLen; ++i) {
|
|
g_GraphBuffer[i] = ((long)(g_GraphBuffer[i] - ((max + min) / 2)) * 256) / (max - min);
|
|
//marshmelow: adjusted *1000 to *256 to make +/- 128 so demod commands still work
|
|
}
|
|
}
|
|
|
|
uint8_t *bits = calloc(g_GraphTraceLen, sizeof(uint8_t));
|
|
if (bits == NULL) {
|
|
PrintAndLogEx(WARNING, "Failed to allocate memory");
|
|
return PM3_EMALLOC;
|
|
}
|
|
size_t size = getFromGraphBuffer(bits);
|
|
// set signal properties low/high/mean/amplitude and is_noise detection
|
|
computeSignalProperties(bits, size);
|
|
|
|
RepaintGraphWindow();
|
|
free(bits);
|
|
return PM3_SUCCESS;
|
|
}
|
|
|
|
int CmdPlot(const char *Cmd) {
|
|
CLIParserContext *ctx;
|
|
CLIParserInit(&ctx, "data plot",
|
|
"Show graph window \n"
|
|
"hit 'h' in window for detail keystroke help available",
|
|
"data plot"
|
|
);
|
|
void *argtable[] = {
|
|
arg_param_begin,
|
|
arg_param_end
|
|
};
|
|
CLIExecWithReturn(ctx, Cmd, argtable, true);
|
|
CLIParserFree(ctx);
|
|
ShowGraphWindow();
|
|
return PM3_SUCCESS;
|
|
}
|
|
|
|
int CmdSave(const char *Cmd) {
|
|
|
|
CLIParserContext *ctx;
|
|
CLIParserInit(&ctx, "data save",
|
|
"Save signal trace from graph window , i.e. the GraphBuffer\n"
|
|
"This is a text file with number -127 to 127. With the option `w` you can save it as wave file\n"
|
|
"Filename should be without file extension",
|
|
"data save -f myfilename -> save graph buffer to file\n"
|
|
"data save --wave -f myfilename -> save graph buffer to wave file"
|
|
);
|
|
|
|
void *argtable[] = {
|
|
arg_param_begin,
|
|
arg_lit0("w", "wave", "save as wave format (.wav)"),
|
|
arg_str1("f", "file", "<fn w/o ext>", "save file name"),
|
|
arg_param_end
|
|
};
|
|
CLIExecWithReturn(ctx, Cmd, argtable, false);
|
|
|
|
bool as_wave = arg_get_lit(ctx, 1);
|
|
|
|
int fnlen = 0;
|
|
char filename[FILE_PATH_SIZE] = {0};
|
|
// CLIGetStrWithReturn(ctx, 2, (uint8_t *)filename, &fnlen);
|
|
CLIParamStrToBuf(arg_get_str(ctx, 2), (uint8_t *)filename, FILE_PATH_SIZE, &fnlen);
|
|
CLIParserFree(ctx);
|
|
|
|
if (g_GraphTraceLen == 0) {
|
|
PrintAndLogEx(WARNING, "Graphbuffer is empty, nothing to save");
|
|
return PM3_SUCCESS;
|
|
}
|
|
|
|
if (as_wave)
|
|
return saveFileWAVE(filename, g_GraphBuffer, g_GraphTraceLen);
|
|
else
|
|
return saveFilePM3(filename, g_GraphBuffer, g_GraphTraceLen);
|
|
}
|
|
|
|
static int CmdTimeScale(const char *Cmd) {
|
|
|
|
CLIParserContext *ctx;
|
|
CLIParserInit(&ctx, "data timescale",
|
|
"Set cursor display timescale.\n"
|
|
"Setting the timescale makes the differential `dt` reading between the yellow and purple markers meaningful.\n"
|
|
"once the timescale is set, the differential reading between brackets can become a time duration.",
|
|
"data timescale --sr 125 -u ms -> for LF sampled at 125 kHz. Reading will be in milliseconds\n"
|
|
"data timescale --sr 1.695 -u us -> for HF sampled at 16 * fc/128. Reading will be in microseconds\n"
|
|
"data timescale --sr 16 -u ETU -> for HF with 16 samples per ETU (fc/128). Reading will be in ETUs"
|
|
);
|
|
void *argtable[] = {
|
|
arg_param_begin,
|
|
arg_dbl1(NULL, "sr", "<float>", "sets timescale factor according to sampling rate"),
|
|
arg_str0("u", "unit", "<str>", "time unit to display (max 10 chars)"),
|
|
arg_param_end
|
|
};
|
|
CLIExecWithReturn(ctx, Cmd, argtable, false);
|
|
g_CursorScaleFactor = arg_get_dbl_def(ctx, 1, 1);
|
|
if (g_CursorScaleFactor <= 0) {
|
|
PrintAndLogEx(FAILED, "bad, can't have negative or zero timescale factor");
|
|
g_CursorScaleFactor = 1;
|
|
}
|
|
int len = 0;
|
|
g_CursorScaleFactorUnit[0] = '\x00';
|
|
CLIParamStrToBuf(arg_get_str(ctx, 2), (uint8_t *)g_CursorScaleFactorUnit, sizeof(g_CursorScaleFactorUnit), &len);
|
|
CLIParserFree(ctx);
|
|
RepaintGraphWindow();
|
|
return PM3_SUCCESS;
|
|
}
|
|
|
|
int directionalThreshold(const int *in, int *out, size_t len, int8_t up, int8_t down) {
|
|
|
|
int lastValue = in[0];
|
|
|
|
// Will be changed at the end, but init 0 as we adjust to last samples
|
|
// value if no threshold kicks in.
|
|
out[0] = 0;
|
|
|
|
for (size_t i = 1; i < len; ++i) {
|
|
// Apply first threshold to samples heading up
|
|
if (in[i] >= up && in[i] > lastValue) {
|
|
lastValue = out[i]; // Buffer last value as we overwrite it.
|
|
out[i] = 1;
|
|
}
|
|
// Apply second threshold to samples heading down
|
|
else if (in[i] <= down && in[i] < lastValue) {
|
|
lastValue = out[i]; // Buffer last value as we overwrite it.
|
|
out[i] = -1;
|
|
} else {
|
|
lastValue = out[i]; // Buffer last value as we overwrite it.
|
|
out[i] = out[i - 1];
|
|
}
|
|
}
|
|
|
|
// Align with first edited sample.
|
|
out[0] = out[1];
|
|
return PM3_SUCCESS;
|
|
}
|
|
|
|
static int CmdDirectionalThreshold(const char *Cmd) {
|
|
CLIParserContext *ctx;
|
|
CLIParserInit(&ctx, "data dirthreshold",
|
|
"Max rising higher up-thres/ Min falling lower down-thres, keep rest as prev.",
|
|
"data dirthreshold -u 10 -d -10"
|
|
);
|
|
void *argtable[] = {
|
|
arg_param_begin,
|
|
arg_int1("d", "down", "<dec>", "threshold down"),
|
|
arg_int1("u", "up", "<dec>", "threshold up"),
|
|
arg_param_end
|
|
};
|
|
CLIExecWithReturn(ctx, Cmd, argtable, false);
|
|
int8_t down = arg_get_int(ctx, 1);
|
|
int8_t up = arg_get_int(ctx, 2);
|
|
CLIParserFree(ctx);
|
|
|
|
PrintAndLogEx(INFO, "Applying up threshold: " _YELLOW_("%i") ", down threshold: " _YELLOW_("%i") "\n", up, down);
|
|
|
|
directionalThreshold(g_GraphBuffer, g_GraphBuffer, g_GraphTraceLen, up, down);
|
|
|
|
// set signal properties low/high/mean/amplitude and isnoice detection
|
|
uint8_t *bits = calloc(g_GraphTraceLen, sizeof(uint8_t));
|
|
if (bits == NULL) {
|
|
PrintAndLogEx(WARNING, "Failed to allocate memory");
|
|
return PM3_EMALLOC;
|
|
}
|
|
size_t size = getFromGraphBuffer(bits);
|
|
// set signal properties low/high/mean/amplitude and is_noice detection
|
|
computeSignalProperties(bits, size);
|
|
|
|
RepaintGraphWindow();
|
|
free(bits);
|
|
return PM3_SUCCESS;
|
|
}
|
|
|
|
static int CmdZerocrossings(const char *Cmd) {
|
|
CLIParserContext *ctx;
|
|
CLIParserInit(&ctx, "data zerocrossings",
|
|
"Count time between zero-crossings",
|
|
"data zerocrossings"
|
|
);
|
|
void *argtable[] = {
|
|
arg_param_begin,
|
|
arg_param_end
|
|
};
|
|
CLIExecWithReturn(ctx, Cmd, argtable, true);
|
|
CLIParserFree(ctx);
|
|
|
|
// Zero-crossings aren't meaningful unless the signal is zero-mean.
|
|
CmdHpf("");
|
|
|
|
int sign = 1, zc = 0, lastZc = 0;
|
|
|
|
for (uint32_t i = 0; i < g_GraphTraceLen; ++i) {
|
|
if (g_GraphBuffer[i] * sign >= 0) {
|
|
// No change in sign, reproduce the previous sample count.
|
|
zc++;
|
|
g_GraphBuffer[i] = lastZc;
|
|
} else {
|
|
// Change in sign, reset the sample count.
|
|
sign = -sign;
|
|
g_GraphBuffer[i] = lastZc;
|
|
if (sign > 0) {
|
|
lastZc = zc;
|
|
zc = 0;
|
|
}
|
|
}
|
|
}
|
|
|
|
uint8_t *bits = calloc(g_GraphTraceLen, sizeof(uint8_t));
|
|
if (bits == NULL) {
|
|
PrintAndLogEx(WARNING, "Failed to allocate memory");
|
|
return PM3_EMALLOC;
|
|
}
|
|
size_t size = getFromGraphBuffer(bits);
|
|
// set signal properties low/high/mean/amplitude and is_noise detection
|
|
computeSignalProperties(bits, size);
|
|
RepaintGraphWindow();
|
|
free(bits);
|
|
return PM3_SUCCESS;
|
|
}
|
|
|
|
static bool data_verify_hex(uint8_t *d, size_t n) {
|
|
if (d == NULL) {
|
|
return false;
|
|
}
|
|
|
|
for (size_t i = 0; i < n; i++) {
|
|
if (isxdigit(d[i]) == false) {
|
|
PrintAndLogEx(ERR, "Non hex digit found");
|
|
return false;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
/* // example of FSK2 RF/50 Tones
|
|
static const int LowTone[] = {
|
|
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
|
|
};
|
|
static const int HighTone[] = {
|
|
1, 1, 1, 1, 1, -1, -1, -1, -1, // note one extra 1 to padd due to 50/8 remainder (1/2 the remainder)
|
|
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, // note one extra -1 to padd due to 50/8 remainder
|
|
};
|
|
*/
|
|
static void GetHiLoTone(int *LowTone, int *HighTone, int clk, int LowToneFC, int HighToneFC) {
|
|
int i, j = 0;
|
|
int Left_Modifier = ((clk % LowToneFC) % 2) + ((clk % LowToneFC) / 2);
|
|
int Right_Modifier = (clk % LowToneFC) / 2;
|
|
//int HighToneMod = clk mod HighToneFC;
|
|
int LeftHalfFCCnt = (LowToneFC % 2) + (LowToneFC / 2); //truncate
|
|
int FCs_per_clk = clk / LowToneFC;
|
|
|
|
// need to correctly split up the clock to field clocks.
|
|
// First attempt uses modifiers on each end to make up for when FCs don't evenly divide into Clk
|
|
|
|
// start with LowTone
|
|
// set extra 1 modifiers to make up for when FC doesn't divide evenly into Clk
|
|
for (i = 0; i < Left_Modifier; i++) {
|
|
LowTone[i] = 1;
|
|
}
|
|
|
|
// loop # of field clocks inside the main clock
|
|
for (i = 0; i < (FCs_per_clk); i++) {
|
|
// loop # of samples per field clock
|
|
for (j = 0; j < LowToneFC; j++) {
|
|
LowTone[(i * LowToneFC) + Left_Modifier + j] = (j < LeftHalfFCCnt) ? 1 : -1;
|
|
}
|
|
}
|
|
|
|
int k;
|
|
// add last -1 modifiers
|
|
for (k = 0; k < Right_Modifier; k++) {
|
|
LowTone[((i - 1) * LowToneFC) + Left_Modifier + j + k] = -1;
|
|
}
|
|
|
|
// now do hightone
|
|
Left_Modifier = ((clk % HighToneFC) % 2) + ((clk % HighToneFC) / 2);
|
|
Right_Modifier = (clk % HighToneFC) / 2;
|
|
LeftHalfFCCnt = (HighToneFC % 2) + (HighToneFC / 2); //truncate
|
|
FCs_per_clk = clk / HighToneFC;
|
|
|
|
for (i = 0; i < Left_Modifier; i++) {
|
|
HighTone[i] = 1;
|
|
}
|
|
|
|
// loop # of field clocks inside the main clock
|
|
for (i = 0; i < (FCs_per_clk); i++) {
|
|
// loop # of samples per field clock
|
|
for (j = 0; j < HighToneFC; j++) {
|
|
HighTone[(i * HighToneFC) + Left_Modifier + j] = (j < LeftHalfFCCnt) ? 1 : -1;
|
|
}
|
|
}
|
|
|
|
// add last -1 modifiers
|
|
for (k = 0; k < Right_Modifier; k++) {
|
|
PrintAndLogEx(NORMAL, "(i-1)*HighToneFC+lm+j+k %i", ((i - 1) * HighToneFC) + Left_Modifier + j + k);
|
|
HighTone[((i - 1) * HighToneFC) + Left_Modifier + j + k] = -1;
|
|
}
|
|
if (g_debugMode == 2) {
|
|
for (i = 0; i < clk; i++) {
|
|
PrintAndLogEx(NORMAL, "Low: %i, High: %i", LowTone[i], HighTone[i]);
|
|
}
|
|
}
|
|
}
|
|
|
|
//old CmdFSKdemod adapted by marshmellow
|
|
//converts FSK to clear NRZ style wave. (or demodulates)
|
|
static int FSKToNRZ(int *data, size_t *dataLen, uint8_t clk, uint8_t LowToneFC, uint8_t HighToneFC) {
|
|
uint8_t ans = 0;
|
|
if (clk == 0 || LowToneFC == 0 || HighToneFC == 0) {
|
|
int firstClockEdge = 0;
|
|
ans = fskClocks((uint8_t *) &LowToneFC, (uint8_t *) &HighToneFC, (uint8_t *) &clk, &firstClockEdge);
|
|
if (g_debugMode > 1) {
|
|
PrintAndLogEx(NORMAL, "DEBUG FSKtoNRZ: detected clocks: fc_low %i, fc_high %i, clk %i, firstClockEdge %i, ans %u", LowToneFC, HighToneFC, clk, firstClockEdge, ans);
|
|
}
|
|
}
|
|
// currently only know fsk modulations with field clocks < 10 samples and > 4 samples. filter out to remove false positives (and possibly destroying ask/psk modulated waves...)
|
|
if (ans == 0 || clk == 0 || LowToneFC == 0 || HighToneFC == 0 || LowToneFC > 10 || HighToneFC < 4) {
|
|
if (g_debugMode > 1) {
|
|
PrintAndLogEx(NORMAL, "DEBUG FSKtoNRZ: no fsk clocks found");
|
|
}
|
|
return PM3_ESOFT;
|
|
}
|
|
|
|
int LowTone[clk];
|
|
int HighTone[clk];
|
|
GetHiLoTone(LowTone, HighTone, clk, LowToneFC, HighToneFC);
|
|
|
|
// loop through ([all samples] - clk)
|
|
for (size_t i = 0; i < *dataLen - clk; ++i) {
|
|
int lowSum = 0, highSum = 0;
|
|
|
|
// sum all samples together starting from this sample for [clk] samples for each tone (multiply tone value with sample data)
|
|
for (size_t j = 0; j < clk; ++j) {
|
|
lowSum += LowTone[j] * data[i + j];
|
|
highSum += HighTone[j] * data[i + j];
|
|
}
|
|
// get abs( [average sample value per clk] * 100 ) (or a rolling average of sorts)
|
|
lowSum = abs(100 * lowSum / clk);
|
|
highSum = abs(100 * highSum / clk);
|
|
// save these back to buffer for later use
|
|
data[i] = (highSum << 16) | lowSum;
|
|
}
|
|
|
|
// now we have the abs( [average sample value per clk] * 100 ) for each tone
|
|
// loop through again [all samples] - clk - 16
|
|
// note why 16??? is 16 the largest FC? changed to LowToneFC as that should be the > fc
|
|
for (size_t i = 0; i < *dataLen - clk - LowToneFC; ++i) {
|
|
int lowTot = 0, highTot = 0;
|
|
|
|
// sum a field clock width of abs( [average sample values per clk] * 100) for each tone
|
|
for (size_t j = 0; j < LowToneFC; ++j) { //10 for fsk2
|
|
lowTot += (data[i + j] & 0xffff);
|
|
}
|
|
for (size_t j = 0; j < HighToneFC; j++) { //8 for fsk2
|
|
highTot += (data[i + j] >> 16);
|
|
}
|
|
|
|
// subtract the sum of lowTone averages by the sum of highTone averages as it
|
|
// and write back the new graph value
|
|
data[i] = lowTot - highTot;
|
|
}
|
|
// update dataLen to what we put back to the data sample buffer
|
|
*dataLen -= (clk + LowToneFC);
|
|
return PM3_SUCCESS;
|
|
}
|
|
|
|
static int CmdFSKToNRZ(const char *Cmd) {
|
|
|
|
CLIParserContext *ctx;
|
|
CLIParserInit(&ctx, "data fsktonrz",
|
|
"Convert fsk2 to nrz wave for alternate fsk demodulating (for weak fsk)\n"
|
|
"Omitted values are autodetect instead",
|
|
"data fsktonrz\n"
|
|
"data fsktonrz -c 32 --low 8 --hi 10");
|
|
|
|
void *argtable[] = {
|
|
arg_param_begin,
|
|
arg_int0("c", "clk", "<dec>", "clock"),
|
|
arg_int0(NULL, "low", "<dec>", "low field clock"),
|
|
arg_int0(NULL, "hi", "<dec>", "high field clock"),
|
|
arg_param_end
|
|
};
|
|
CLIExecWithReturn(ctx, Cmd, argtable, true);
|
|
|
|
int clk = arg_get_int_def(ctx, 1, 0);
|
|
int fc_low = arg_get_int_def(ctx, 2, 0);
|
|
int fc_high = arg_get_int_def(ctx, 3, 0);
|
|
CLIParserFree(ctx);
|
|
|
|
setClockGrid(0, 0);
|
|
g_DemodBufferLen = 0;
|
|
int ans = FSKToNRZ(g_GraphBuffer, &g_GraphTraceLen, clk, fc_low, fc_high);
|
|
CmdNorm("");
|
|
RepaintGraphWindow();
|
|
return ans;
|
|
}
|
|
|
|
/*
|
|
// If reactivated, beware it doesn't compile on Android (DXL)
|
|
void iceIIR_Butterworth(int *data, const size_t len) {
|
|
|
|
int *output = (int *) calloc(sizeof(int) * len, sizeof(uint8_t));
|
|
if (!output) return;
|
|
|
|
// clear mem
|
|
memset(output, 0x00, len);
|
|
|
|
size_t adjustedLen = len;
|
|
float fc = 0.1125f; // center frequency
|
|
|
|
// create very simple low-pass filter to remove images (2nd-order Butterworth)
|
|
float complex iir_buf[3] = {0, 0, 0};
|
|
float b[3] = {0.003621681514929, 0.007243363029857, 0.003621681514929};
|
|
float a[3] = {1.000000000000000, -1.822694925196308, 0.837181651256023};
|
|
|
|
for (size_t i = 0; i < adjustedLen; ++i) {
|
|
|
|
float sample = data[i]; // input sample read from array
|
|
float complex x_prime = 1.0f; // save sample for estimating frequency
|
|
float complex x;
|
|
|
|
// remove DC offset and mix to complex baseband
|
|
x = (sample - 127.5f) * cexpf(_Complex_I * 2 * M_PI * fc * i);
|
|
|
|
// apply low-pass filter, removing spectral image (IIR using direct-form II)
|
|
iir_buf[2] = iir_buf[1];
|
|
iir_buf[1] = iir_buf[0];
|
|
iir_buf[0] = x - a[1] * iir_buf[1] - a[2] * iir_buf[2];
|
|
x = b[0] * iir_buf[0] +
|
|
b[1] * iir_buf[1] +
|
|
b[2] * iir_buf[2];
|
|
|
|
// compute instantaneous frequency by looking at phase difference
|
|
// between adjacent samples
|
|
float freq = cargf(x * conjf(x_prime));
|
|
x_prime = x; // retain this sample for next iteration
|
|
|
|
output[i] = (freq > 0) ? 127 : -127;
|
|
}
|
|
|
|
// show data
|
|
//memcpy(data, output, adjustedLen);
|
|
for (size_t j = 0; j < adjustedLen; ++j)
|
|
data[j] = output[j];
|
|
|
|
free(output);
|
|
}
|
|
*/
|
|
|
|
static void iceSimple_Filter(int *data, const size_t len, uint8_t k) {
|
|
// ref: http://www.edn.com/design/systems-design/4320010/A-simple-software-lowpass-filter-suits-embedded-system-applications
|
|
// parameter K
|
|
#define FILTER_SHIFT 4
|
|
|
|
int32_t filter_reg = 0;
|
|
int8_t shift = (k <= 8) ? k : FILTER_SHIFT;
|
|
|
|
for (size_t i = 0; i < len; ++i) {
|
|
// Update filter with current sample
|
|
filter_reg = filter_reg - (filter_reg >> shift) + data[i];
|
|
|
|
// Scale output for unity gain
|
|
data[i] = filter_reg >> shift;
|
|
}
|
|
}
|
|
|
|
static int CmdDataIIR(const char *Cmd) {
|
|
|
|
CLIParserContext *ctx;
|
|
CLIParserInit(&ctx, "data iir",
|
|
"Apply IIR buttersworth filter on plot data",
|
|
"data iir -n 2"
|
|
);
|
|
void *argtable[] = {
|
|
arg_param_begin,
|
|
arg_u64_1("n", NULL, "<dec>", "factor n"),
|
|
arg_param_end
|
|
};
|
|
CLIExecWithReturn(ctx, Cmd, argtable, false);
|
|
uint8_t k = (arg_get_u32_def(ctx, 1, 0) & 0xFF);
|
|
CLIParserFree(ctx);
|
|
|
|
iceSimple_Filter(g_GraphBuffer, g_GraphTraceLen, k);
|
|
|
|
uint8_t *bits = calloc(g_GraphTraceLen, sizeof(uint8_t));
|
|
if (bits == NULL) {
|
|
PrintAndLogEx(WARNING, "Failed to allocate memory");
|
|
return PM3_EMALLOC;
|
|
}
|
|
|
|
size_t size = getFromGraphBuffer(bits);
|
|
// set signal properties low/high/mean/amplitude and is_noise detection
|
|
computeSignalProperties(bits, size);
|
|
RepaintGraphWindow();
|
|
free(bits);
|
|
return PM3_SUCCESS;
|
|
}
|
|
|
|
typedef struct {
|
|
t55xx_modulation modulation;
|
|
int bitrate;
|
|
int carrier;
|
|
uint8_t fc1;
|
|
uint8_t fc2;
|
|
} lf_modulation_t;
|
|
|
|
static int print_modulation(lf_modulation_t b) {
|
|
PrintAndLogEx(INFO, " Modulation........ " _GREEN_("%s"), GetSelectedModulationStr(b.modulation));
|
|
PrintAndLogEx(INFO, " Bit clock......... " _GREEN_("RF/%d"), b.bitrate);
|
|
PrintAndLogEx(INFO, " Approx baudrate... " _GREEN_("%.f") " baud", (125000 / (float)b.bitrate));
|
|
switch (b.modulation) {
|
|
case DEMOD_PSK1:
|
|
case DEMOD_PSK2:
|
|
case DEMOD_PSK3:
|
|
PrintAndLogEx(SUCCESS, " Carrier rate...... %d", b.carrier);
|
|
break;
|
|
case DEMOD_FSK:
|
|
case DEMOD_FSK1:
|
|
case DEMOD_FSK1a:
|
|
case DEMOD_FSK2:
|
|
case DEMOD_FSK2a:
|
|
PrintAndLogEx(SUCCESS, " Field Clocks...... FC/%u, FC/%u", b.fc1, b.fc2);
|
|
break;
|
|
case DEMOD_NRZ:
|
|
case DEMOD_ASK:
|
|
case DEMOD_BI:
|
|
case DEMOD_BIa:
|
|
default:
|
|
break;
|
|
}
|
|
PrintAndLogEx(NORMAL, "");
|
|
return PM3_SUCCESS;
|
|
}
|
|
|
|
static int try_detect_modulation(void) {
|
|
|
|
#define LF_NUM_OF_TESTS 6
|
|
|
|
lf_modulation_t tests[LF_NUM_OF_TESTS];
|
|
for (int i = 0; i < ARRAYLEN(tests); i++) {
|
|
memset(&tests[i], 0, sizeof(lf_modulation_t));
|
|
}
|
|
|
|
int clk = 0, firstClockEdge = 0;
|
|
uint8_t hits = 0, fc1 = 0, fc2 = 0;
|
|
bool st = false;
|
|
|
|
|
|
uint8_t ans = fskClocks(&fc1, &fc2, (uint8_t *)&clk, &firstClockEdge);
|
|
|
|
if (ans && ((fc1 == 10 && fc2 == 8) || (fc1 == 8 && fc2 == 5))) {
|
|
|
|
if ((FSKrawDemod(0, 0, 0, 0, false) == PM3_SUCCESS)) {
|
|
tests[hits].modulation = DEMOD_FSK;
|
|
if (fc1 == 8 && fc2 == 5) {
|
|
tests[hits].modulation = DEMOD_FSK1a;
|
|
} else if (fc1 == 10 && fc2 == 8) {
|
|
tests[hits].modulation = DEMOD_FSK2;
|
|
}
|
|
|
|
tests[hits].bitrate = clk;
|
|
tests[hits].fc1 = fc1;
|
|
tests[hits].fc2 = fc2;
|
|
++hits;
|
|
}
|
|
|
|
} else {
|
|
clk = GetAskClock("", false);
|
|
if (clk > 0) {
|
|
// 0 = auto clock
|
|
// 0 = no invert
|
|
// 1 = maxError 1
|
|
// 0 = max len
|
|
// false = no amplify
|
|
// false = no verbose
|
|
// false = no emSearch
|
|
// 1 = Ask/Man
|
|
// st = true
|
|
if ((ASKDemod_ext(0, 0, 1, 0, false, false, false, 1, &st) == PM3_SUCCESS)) {
|
|
tests[hits].modulation = DEMOD_ASK;
|
|
tests[hits].bitrate = clk;
|
|
++hits;
|
|
}
|
|
// "0 0 1 " == clock auto, invert true, maxError 1.
|
|
// false = no verbose
|
|
// false = no emSearch
|
|
// 1 = Ask/Man
|
|
// st = true
|
|
|
|
// ASK / biphase
|
|
if ((ASKbiphaseDemod(0, 0, 0, 2, false) == PM3_SUCCESS)) {
|
|
tests[hits].modulation = DEMOD_BI;
|
|
tests[hits].bitrate = clk;
|
|
++hits;
|
|
}
|
|
// ASK / Diphase
|
|
if ((ASKbiphaseDemod(0, 0, 1, 2, false) == PM3_SUCCESS)) {
|
|
tests[hits].modulation = DEMOD_BIa;
|
|
tests[hits].bitrate = clk;
|
|
++hits;
|
|
}
|
|
}
|
|
clk = GetNrzClock("", false);
|
|
if ((NRZrawDemod(0, 0, 1, false) == PM3_SUCCESS)) {
|
|
tests[hits].modulation = DEMOD_NRZ;
|
|
tests[hits].bitrate = clk;
|
|
++hits;
|
|
}
|
|
|
|
clk = GetPskClock("", false);
|
|
if (clk > 0) {
|
|
// allow undo
|
|
buffer_savestate_t saveState = save_bufferS32(g_GraphBuffer, g_GraphTraceLen);
|
|
saveState.offset = g_GridOffset;
|
|
// skip first 160 samples to allow antenna to settle in (psk gets inverted occasionally otherwise)
|
|
CmdLtrim("-i 160");
|
|
if ((PSKDemod(0, 0, 6, false) == PM3_SUCCESS)) {
|
|
tests[hits].modulation = DEMOD_PSK1;
|
|
tests[hits].bitrate = clk;
|
|
++hits;
|
|
|
|
// get psk carrier
|
|
tests[hits].carrier = GetPskCarrier(false);
|
|
}
|
|
//undo trim samples
|
|
restore_bufferS32(saveState, g_GraphBuffer);
|
|
g_GridOffset = saveState.offset;
|
|
}
|
|
}
|
|
|
|
if (hits) {
|
|
PrintAndLogEx(SUCCESS, "Found [%d] possible matches for modulation.", hits);
|
|
for (int i = 0; i < hits; ++i) {
|
|
PrintAndLogEx(INFO, "--[%d]---------------", i + 1);
|
|
print_modulation(tests[i]);
|
|
}
|
|
return PM3_SUCCESS;
|
|
} else {
|
|
PrintAndLogEx(INFO, "Signal doesn't match");
|
|
return PM3_ESOFT;
|
|
}
|
|
}
|
|
|
|
static int CmdDataModulationSearch(const char *Cmd) {
|
|
CLIParserContext *ctx;
|
|
CLIParserInit(&ctx, "data modulation",
|
|
"search LF signal after clock and modulation\n",
|
|
"data modulation"
|
|
);
|
|
|
|
void *argtable[] = {
|
|
arg_param_begin,
|
|
arg_param_end
|
|
};
|
|
CLIExecWithReturn(ctx, Cmd, argtable, true);
|
|
CLIParserFree(ctx);
|
|
return try_detect_modulation();
|
|
}
|
|
|
|
static int CmdAsn1Decoder(const char *Cmd) {
|
|
|
|
CLIParserContext *ctx;
|
|
CLIParserInit(&ctx, "data asn1",
|
|
"Decode ASN1 bytearray\n"
|
|
"",
|
|
"data asn1 -d 303381050186922305a5020500a6088101010403030008a7188516eeee4facacf4fbde5e5c49d95e55bfbca74267b02407a9020500\n"
|
|
);
|
|
|
|
void *argtable[] = {
|
|
arg_param_begin,
|
|
arg_str0("d", NULL, "<hex>", "ASN1 encoded byte array"),
|
|
arg_lit0(NULL, "test", "perform self tests"),
|
|
arg_param_end
|
|
};
|
|
CLIExecWithReturn(ctx, Cmd, argtable, false);
|
|
int dlen = 2048;
|
|
uint8_t data[2048];
|
|
CLIGetHexWithReturn(ctx, 1, data, &dlen);
|
|
bool selftest = arg_get_lit(ctx, 2);
|
|
CLIParserFree(ctx);
|
|
if (selftest) {
|
|
return asn1_selftest();
|
|
}
|
|
|
|
// print ASN1 decoded array in TLV view
|
|
PrintAndLogEx(INFO, "---------------- " _CYAN_("ASN1 TLV") " -----------------");
|
|
asn1_print(data, dlen, " ");
|
|
PrintAndLogEx(NORMAL, "");
|
|
return PM3_SUCCESS;
|
|
}
|
|
|
|
static int CmdDiff(const char *Cmd) {
|
|
|
|
CLIParserContext *ctx;
|
|
CLIParserInit(&ctx, "data diff",
|
|
"Diff takes a multitude of input data and makes a binary compare.\n"
|
|
"It accepts filenames (filesystem or RDV4 flashmem SPIFFS), emulator memory, magic gen1",
|
|
"data diff -w 4 -a hf-mfu-01020304.bin -b hf-mfu-04030201.bin\n"
|
|
"data diff -a fileA -b fileB\n"
|
|
"data diff -a fileA --eb\n"
|
|
"data diff --fa fileA -b fileB\n"
|
|
"data diff --fa fileA --fb fileB\n"
|
|
);
|
|
|
|
void *argtable[] = {
|
|
arg_param_begin,
|
|
arg_str0("a", NULL, "<fn>", "input file name A"),
|
|
arg_str0("b", NULL, "<fn>", "input file name B"),
|
|
arg_lit0(NULL, "eb", "emulator memory <hf mf esave>"),
|
|
arg_str0(NULL, "fa", "<fn>", "input spiffs file A"),
|
|
arg_str0(NULL, "fb", "<fn>", "input spiffs file B"),
|
|
arg_int0("w", NULL, "<4|8|16>", "Width of data output"),
|
|
arg_param_end
|
|
};
|
|
CLIExecWithReturn(ctx, Cmd, argtable, false);
|
|
|
|
int fnlenA = 0;
|
|
char filenameA[FILE_PATH_SIZE] = {0};
|
|
CLIParamStrToBuf(arg_get_str(ctx, 1), (uint8_t *)filenameA, FILE_PATH_SIZE, &fnlenA);
|
|
|
|
int fnlenB = 0;
|
|
char filenameB[FILE_PATH_SIZE] = {0};
|
|
CLIParamStrToBuf(arg_get_str(ctx, 2), (uint8_t *)filenameB, FILE_PATH_SIZE, &fnlenB);
|
|
|
|
bool use_e = arg_get_lit(ctx, 3);
|
|
|
|
// SPIFFS filename A
|
|
int splenA = 0;
|
|
char spnameA[FILE_PATH_SIZE] = {0};
|
|
CLIParamStrToBuf(arg_get_str(ctx, 4), (uint8_t *)spnameA, FILE_PATH_SIZE, &splenA);
|
|
|
|
// SPIFFS filename B
|
|
int splenB = 0;
|
|
char spnameB[FILE_PATH_SIZE] = {0};
|
|
CLIParamStrToBuf(arg_get_str(ctx, 5), (uint8_t *)spnameB, FILE_PATH_SIZE, &splenB);
|
|
|
|
int width = arg_get_int_def(ctx, 6, 16);
|
|
CLIParserFree(ctx);
|
|
|
|
// sanity check
|
|
if (IfPm3Rdv4Fw() == false && (splenA > 0 || splenB > 0)) {
|
|
PrintAndLogEx(WARNING, "No RDV4 Flashmemory available");
|
|
return PM3_EINVARG;
|
|
}
|
|
|
|
if (splenA > 32) {
|
|
PrintAndLogEx(WARNING, "SPIFFS filname A length is large than 32 bytes, got %d", splenA);
|
|
return PM3_EINVARG;
|
|
}
|
|
if (splenB > 32) {
|
|
PrintAndLogEx(WARNING, "SPIFFS filname B length is large than 32 bytes, got %d", splenB);
|
|
return PM3_EINVARG;
|
|
}
|
|
|
|
//
|
|
if (width > 16 || width < 1) {
|
|
PrintAndLogEx(INFO, "Width out of range, using default 16 bytes width");
|
|
width = 16;
|
|
}
|
|
|
|
// if user supplied dump file, time to load it
|
|
int res = PM3_SUCCESS;
|
|
uint8_t *inA = NULL, *inB = NULL;
|
|
size_t datalenA = 0, datalenB = 0;
|
|
|
|
|
|
|
|
// read file A
|
|
if (fnlenA) {
|
|
// read dump file
|
|
res = pm3_load_dump(filenameA, (void **)&inA, &datalenA, MIFARE_4K_MAX_BYTES);
|
|
if (res != PM3_SUCCESS) {
|
|
return res;
|
|
}
|
|
}
|
|
|
|
// read file B
|
|
if (fnlenB) {
|
|
// read dump file
|
|
res = pm3_load_dump(filenameB, (void **)&inB, &datalenB, MIFARE_4K_MAX_BYTES);
|
|
if (res != PM3_SUCCESS) {
|
|
return res;
|
|
}
|
|
}
|
|
|
|
// read spiffs file A
|
|
if (splenA) {
|
|
res = flashmem_spiffs_download(spnameA, splenA, (void **)&inA, &datalenA);
|
|
if (res != PM3_SUCCESS) {
|
|
return res;
|
|
}
|
|
}
|
|
|
|
// read spiffs file B
|
|
if (splenB) {
|
|
res = flashmem_spiffs_download(spnameB, splenB, (void **)&inB, &datalenB);
|
|
if (res != PM3_SUCCESS) {
|
|
return res;
|
|
}
|
|
}
|
|
|
|
// download emulator memory
|
|
if (use_e) {
|
|
|
|
uint8_t *d = calloc(MIFARE_4K_MAX_BYTES, sizeof(uint8_t));
|
|
if (d == NULL) {
|
|
PrintAndLogEx(WARNING, "Failed to allocate memory");
|
|
return PM3_EMALLOC;
|
|
}
|
|
|
|
PrintAndLogEx(INFO, "downloading from emulator memory");
|
|
if (GetFromDevice(BIG_BUF_EML, d, MIFARE_4K_MAX_BYTES, 0, NULL, 0, NULL, 2500, false) == false) {
|
|
PrintAndLogEx(WARNING, "Fail, transfer from device time-out");
|
|
free(inA);
|
|
free(inB);
|
|
free(d);
|
|
return PM3_ETIMEOUT;
|
|
}
|
|
|
|
if (fnlenA) {
|
|
datalenB = MIFARE_4K_MAX_BYTES;
|
|
inB = d;
|
|
} else {
|
|
datalenA = MIFARE_4K_MAX_BYTES;
|
|
inA = d;
|
|
}
|
|
}
|
|
|
|
size_t biggest = (datalenA > datalenB) ? datalenA : datalenB;
|
|
PrintAndLogEx(DEBUG, "data len: %zu A %zu B %zu", biggest, datalenA, datalenB);
|
|
|
|
if (inA == NULL) {
|
|
PrintAndLogEx(INFO, "inA null");
|
|
}
|
|
|
|
if (inB == NULL) {
|
|
PrintAndLogEx(INFO, "inB null");
|
|
}
|
|
|
|
char hdr0[400] = {0};
|
|
|
|
int hdr_sln = (width * 4) + 2;
|
|
int max_fn_space = (width * 4);
|
|
|
|
if (max_fn_space < fnlenA) {
|
|
truncate_filename(filenameA, max_fn_space);
|
|
fnlenA = strlen(filenameA);
|
|
}
|
|
|
|
if (max_fn_space < fnlenB) {
|
|
truncate_filename(filenameB, max_fn_space);
|
|
fnlenB = strlen(filenameB);
|
|
}
|
|
|
|
if (fnlenA && fnlenB) {
|
|
|
|
snprintf(hdr0, sizeof(hdr0) - 1, " # | " _CYAN_("%.*s"), max_fn_space, filenameA);
|
|
|
|
// add space if needed
|
|
int padding_len = (hdr_sln - fnlenA - 1);
|
|
if (padding_len > 0) {
|
|
memset(hdr0 + strlen(hdr0), ' ', padding_len);
|
|
}
|
|
snprintf(hdr0 + strlen(hdr0), sizeof(hdr0) - 1 - strlen(hdr0), "| " _CYAN_("%.*s"), max_fn_space, filenameB);
|
|
|
|
} else {
|
|
strcat(hdr0, " # | " _CYAN_("a"));
|
|
memset(hdr0 + strlen(hdr0), ' ', hdr_sln - 2);
|
|
strcat(hdr0 + strlen(hdr0), "| " _CYAN_("b"));
|
|
}
|
|
|
|
char hdr1[200] = "----+";
|
|
memset(hdr1 + strlen(hdr1), '-', hdr_sln);
|
|
memset(hdr1 + strlen(hdr1), '+', 1);
|
|
memset(hdr1 + strlen(hdr1), '-', hdr_sln);
|
|
|
|
PrintAndLogEx(INFO, "");
|
|
PrintAndLogEx(INFO, hdr1);
|
|
PrintAndLogEx(INFO, hdr0);
|
|
PrintAndLogEx(INFO, hdr1);
|
|
|
|
char line[880] = {0};
|
|
|
|
// print data diff loop
|
|
for (int i = 0 ; i < biggest ; i += width) {
|
|
char dlnA[240] = {0};
|
|
char dlnB[240] = {0};
|
|
char dlnAii[180] = {0};
|
|
char dlnBii[180] = {0};
|
|
|
|
memset(dlnA, 0, sizeof(dlnA));
|
|
memset(dlnB, 0, sizeof(dlnB));
|
|
memset(dlnAii, 0, sizeof(dlnAii));
|
|
memset(dlnBii, 0, sizeof(dlnBii));
|
|
|
|
for (int j = i; j < i + width; j++) {
|
|
int dlnALen = strlen(dlnA);
|
|
int dlnBLen = strlen(dlnB);
|
|
int dlnAiiLen = strlen(dlnAii);
|
|
int dlnBiiLen = strlen(dlnBii);
|
|
|
|
//both files ended
|
|
if (j >= datalenA && j >= datalenB) {
|
|
snprintf(dlnA + dlnALen, sizeof(dlnA) - dlnALen, "-- ");
|
|
snprintf(dlnAii + dlnAiiLen, sizeof(dlnAii) - dlnAiiLen, ".") ;
|
|
snprintf(dlnB + dlnBLen, sizeof(dlnB) - dlnBLen, "-- ");
|
|
snprintf(dlnBii + dlnBiiLen, sizeof(dlnBii) - dlnBiiLen, ".") ;
|
|
continue ;
|
|
}
|
|
|
|
char ca, cb;
|
|
|
|
if (j >= datalenA) {
|
|
// file A ended. print B without colors
|
|
cb = inB[j];
|
|
snprintf(dlnA + dlnALen, sizeof(dlnA) - dlnALen, "-- ");
|
|
snprintf(dlnAii + dlnAiiLen, sizeof(dlnAii) - dlnAiiLen, ".") ;
|
|
snprintf(dlnB + dlnBLen, sizeof(dlnB) - dlnBLen, "%02X ", inB[j]);
|
|
snprintf(dlnBii + dlnBiiLen, sizeof(dlnBii) - dlnBiiLen, "%c", ((cb < 32) || (cb == 127)) ? '.' : cb);
|
|
continue ;
|
|
}
|
|
ca = inA[j];
|
|
if (j >= datalenB) {
|
|
// file B ended. print A without colors
|
|
snprintf(dlnA + dlnALen, sizeof(dlnA) - dlnALen, "%02X ", inA[j]);
|
|
snprintf(dlnAii + dlnAiiLen, sizeof(dlnAii) - dlnAiiLen, "%c", ((ca < 32) || (ca == 127)) ? '.' : ca);
|
|
snprintf(dlnB + dlnBLen, sizeof(dlnB) - dlnBLen, "-- ");
|
|
snprintf(dlnBii + dlnBiiLen, sizeof(dlnBii) - dlnBiiLen, ".") ;
|
|
continue ;
|
|
}
|
|
cb = inB[j];
|
|
if (inA[j] != inB[j]) {
|
|
// diff / add colors
|
|
snprintf(dlnA + dlnALen, sizeof(dlnA) - dlnALen, _GREEN_("%02X "), inA[j]);
|
|
snprintf(dlnB + dlnBLen, sizeof(dlnB) - dlnBLen, _RED_("%02X "), inB[j]);
|
|
snprintf(dlnAii + dlnAiiLen, sizeof(dlnAii) - dlnAiiLen, _GREEN_("%c"), ((ca < 32) || (ca == 127)) ? '.' : ca);
|
|
snprintf(dlnBii + dlnBiiLen, sizeof(dlnBii) - dlnBiiLen, _RED_("%c"), ((cb < 32) || (cb == 127)) ? '.' : cb);
|
|
} else {
|
|
// normal
|
|
snprintf(dlnA + dlnALen, sizeof(dlnA) - dlnALen, "%02X ", inA[j]);
|
|
snprintf(dlnB + dlnBLen, sizeof(dlnB) - dlnBLen, "%02X ", inB[j]);
|
|
snprintf(dlnAii + dlnAiiLen, sizeof(dlnAii) - dlnAiiLen, "%c", ((ca < 32) || (ca == 127)) ? '.' : ca);
|
|
snprintf(dlnBii + dlnBiiLen, sizeof(dlnBii) - dlnBiiLen, "%c", ((cb < 32) || (cb == 127)) ? '.' : cb);
|
|
}
|
|
}
|
|
snprintf(line, sizeof(line), "%s%s | %s%s", dlnA, dlnAii, dlnB, dlnBii);
|
|
|
|
PrintAndLogEx(INFO, "%03X | %s", i, line);
|
|
}
|
|
|
|
// footer
|
|
PrintAndLogEx(INFO, hdr1);
|
|
PrintAndLogEx(NORMAL, "");
|
|
|
|
free(inB);
|
|
free(inA);
|
|
return PM3_SUCCESS;
|
|
}
|
|
|
|
/**
|
|
* @brief Utility for number conversion via cmdline.
|
|
* @param Cmd
|
|
* @return
|
|
*/
|
|
static int CmdNumCon(const char *Cmd) {
|
|
CLIParserContext *ctx;
|
|
CLIParserInit(&ctx, "data num",
|
|
"Function takes a decimal or hexdecimal number and print it in decimal/hex/binary\n"
|
|
"Will print message if number is a prime number\n",
|
|
"data num --dec 2023\n"
|
|
"data num --hex 2A\n"
|
|
);
|
|
|
|
void *argtable[] = {
|
|
arg_param_begin,
|
|
arg_str0(NULL, "dec", "<dec>", "decimal value"),
|
|
arg_str0(NULL, "hex", "<hex>", "hexadecimal value"),
|
|
arg_str0(NULL, "bin", "<bin>", "binary value"),
|
|
arg_lit0("i", NULL, "print inverted value"),
|
|
arg_lit0("r", NULL, "print reversed value"),
|
|
arg_param_end
|
|
};
|
|
CLIExecWithReturn(ctx, Cmd, argtable, false);
|
|
|
|
int dlen = 256;
|
|
char dec[256];
|
|
memset(dec, 0, sizeof(dec));
|
|
int res = CLIParamStrToBuf(arg_get_str(ctx, 1), (uint8_t *)dec, sizeof(dec), &dlen);
|
|
|
|
int hlen = 256;
|
|
char hex[256];
|
|
memset(hex, 0, sizeof(hex));
|
|
res |= CLIParamStrToBuf(arg_get_str(ctx, 2), (uint8_t *)hex, sizeof(hex), &hlen);
|
|
|
|
int blen = 256;
|
|
char bin[256];
|
|
memset(bin, 0, sizeof(bin));
|
|
res |= CLIParamStrToBuf(arg_get_str(ctx, 3), (uint8_t *)bin, sizeof(bin), &blen);
|
|
|
|
bool shall_invert = arg_get_lit(ctx, 4);
|
|
bool shall_reverse = arg_get_lit(ctx, 5);
|
|
CLIParserFree(ctx);
|
|
|
|
// sanity checks
|
|
if (res) {
|
|
PrintAndLogEx(FAILED, "Error parsing bytes");
|
|
return PM3_EINVARG;
|
|
}
|
|
|
|
// results for MPI actions
|
|
bool ret = false;
|
|
(void) ret;
|
|
|
|
// container of big number
|
|
mbedtls_mpi N;
|
|
mbedtls_mpi_init(&N);
|
|
|
|
// hex
|
|
if (hlen > 0) {
|
|
if (data_verify_hex((uint8_t *)hex, hlen) == false) {
|
|
return PM3_EINVARG;
|
|
}
|
|
MBEDTLS_MPI_CHK(mbedtls_mpi_read_string(&N, 16, hex));
|
|
PrintAndLogEx(INFO, "#....... %d", hlen);
|
|
}
|
|
|
|
// decimal
|
|
if (dlen > 0) {
|
|
// should have decimal string check here too
|
|
MBEDTLS_MPI_CHK(mbedtls_mpi_read_string(&N, 10, dec));
|
|
}
|
|
|
|
// binary
|
|
if (blen > 0) {
|
|
// should have bianry string check here too
|
|
MBEDTLS_MPI_CHK(mbedtls_mpi_read_string(&N, 2, bin));
|
|
PrintAndLogEx(INFO, "#bits... %d", blen);
|
|
}
|
|
|
|
mbedtls_mpi base;
|
|
mbedtls_mpi_init(&base);
|
|
mbedtls_mpi_add_int(&base, &base, 10);
|
|
|
|
// printing
|
|
typedef struct {
|
|
const char *desc;
|
|
uint8_t radix;
|
|
} radix_t;
|
|
|
|
radix_t radix[] = {
|
|
{"dec..... ", 10},
|
|
{"hex..... ", 16},
|
|
{"bin..... ", 2}
|
|
};
|
|
|
|
char s[600] = {0};
|
|
size_t slen = 0;
|
|
const char pad[] = "00000000000000000000000000000000000000000000000000000000000000000000000000000000";
|
|
|
|
for (uint8_t i = 0; i < ARRAYLEN(radix); i++) {
|
|
MBEDTLS_MPI_CHK(mbedtls_mpi_write_string(&N, radix[i].radix, s, sizeof(s), &slen));
|
|
if (slen) {
|
|
|
|
// only pad bin string
|
|
int pn = 0;
|
|
if (i == 2) {
|
|
if (blen && slen < blen) {
|
|
pn = blen - slen + 1;
|
|
} else if (hlen && (slen < (hlen * 4))) {
|
|
pn = (hlen * 4) - slen + 1;
|
|
}
|
|
}
|
|
PrintAndLogEx(SUCCESS, "%s%.*s%s", radix[i].desc, pn, pad, s);
|
|
}
|
|
}
|
|
|
|
// ascii
|
|
MBEDTLS_MPI_CHK(mbedtls_mpi_write_string(&N, 16, s, sizeof(s), &slen));
|
|
if (slen) {
|
|
size_t n = (slen >> 1);
|
|
uint8_t *d = calloc(n, sizeof(uint8_t));
|
|
if (d != NULL) {
|
|
hexstr_to_byte_array(s, d, &n);
|
|
PrintAndLogEx(SUCCESS, "ascii... " _YELLOW_("%s"), sprint_ascii((const uint8_t *)d, n));
|
|
free(d);
|
|
}
|
|
}
|
|
|
|
// reverse
|
|
if (shall_reverse) {
|
|
PrintAndLogEx(SUCCESS, _CYAN_("Reversed"));
|
|
for (uint8_t i = 0; i < ARRAYLEN(radix); i++) {
|
|
MBEDTLS_MPI_CHK(mbedtls_mpi_write_string(&N, radix[i].radix, s, sizeof(s), &slen));
|
|
|
|
if (slen) {
|
|
|
|
// only pad bin string
|
|
char scpy[600] = {0x30};
|
|
memset(scpy, 0x30, sizeof(scpy));
|
|
int pn = 0;
|
|
if (i == 2) {
|
|
if (blen && slen < blen) {
|
|
pn = blen - slen + 1;
|
|
} else if (hlen && (slen < (hlen * 4))) {
|
|
pn = (hlen * 4) - slen + 1;
|
|
}
|
|
}
|
|
memcpy(scpy + pn, s, slen);
|
|
str_reverse(scpy, strlen(scpy));
|
|
PrintAndLogEx(SUCCESS, "%s%s", radix[i].desc, scpy);
|
|
}
|
|
}
|
|
|
|
// ascii
|
|
MBEDTLS_MPI_CHK(mbedtls_mpi_write_string(&N, 16, s, sizeof(s), &slen));
|
|
if (slen) {
|
|
str_reverse(s, strlen(s));
|
|
size_t n = (slen >> 1);
|
|
uint8_t *d = calloc(n, sizeof(uint8_t));
|
|
if (d != NULL) {
|
|
hexstr_to_byte_array(s, d, &n);
|
|
PrintAndLogEx(SUCCESS, "ascii... " _YELLOW_("%s"), sprint_ascii((const uint8_t *)d, n));
|
|
free(d);
|
|
}
|
|
}
|
|
}
|
|
|
|
// invert
|
|
if (shall_invert) {
|
|
PrintAndLogEx(SUCCESS, _CYAN_("Inverted"));
|
|
for (uint8_t i = 0; i < ARRAYLEN(radix); i++) {
|
|
MBEDTLS_MPI_CHK(mbedtls_mpi_write_string(&N, radix[i].radix, s, sizeof(s), &slen));
|
|
if (slen == 0) {
|
|
continue;
|
|
}
|
|
|
|
switch (i) {
|
|
case 0: {
|
|
// MBEDTLS_MPI_CHK(mbedtls_mpi_inv_mod(&N, &N, &base));
|
|
break;
|
|
}
|
|
case 1: {
|
|
str_inverse_hex(s, strlen(s));
|
|
PrintAndLogEx(SUCCESS, "%s%s", radix[i].desc, s);
|
|
break;
|
|
}
|
|
case 2: {
|
|
|
|
char scpy[600] = {0x30};
|
|
memset(scpy, 0x30, sizeof(scpy));
|
|
int pn = 0;
|
|
if (blen && slen < blen) {
|
|
pn = blen - slen + 1;
|
|
} else if (hlen && (slen < (hlen * 4))) {
|
|
pn = (hlen * 4) - slen + 1;
|
|
}
|
|
|
|
memcpy(scpy + pn, s, slen);
|
|
str_inverse_bin(scpy, strlen(scpy));
|
|
PrintAndLogEx(SUCCESS, "%s%s", radix[i].desc, scpy);
|
|
break;
|
|
}
|
|
default: {
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
// ascii
|
|
MBEDTLS_MPI_CHK(mbedtls_mpi_write_string(&N, 16, s, sizeof(s), &slen));
|
|
if (slen) {
|
|
str_inverse_hex(s, strlen(s));
|
|
size_t n = (slen >> 1);
|
|
uint8_t *d = calloc(n, sizeof(uint8_t));
|
|
if (d != NULL) {
|
|
hexstr_to_byte_array(s, d, &n);
|
|
PrintAndLogEx(SUCCESS, "ascii... " _YELLOW_("%s"), sprint_ascii((const uint8_t *)d, n));
|
|
free(d);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
// check if number is a prime
|
|
mbedtls_entropy_context entropy;
|
|
mbedtls_ctr_drbg_context ctr_drbg;
|
|
mbedtls_ctr_drbg_init(&ctr_drbg);
|
|
mbedtls_entropy_init(&entropy);
|
|
|
|
MBEDTLS_MPI_CHK(mbedtls_ctr_drbg_seed(&ctr_drbg, mbedtls_entropy_func, &entropy, NULL, 0));
|
|
|
|
res = mbedtls_mpi_is_prime_ext(&N, 50, mbedtls_ctr_drbg_random, &ctr_drbg);
|
|
if (res == 0) {
|
|
PrintAndLogEx(INFO, "prime... " _YELLOW_("yes"));
|
|
}
|
|
|
|
cleanup:
|
|
mbedtls_mpi_free(&N);
|
|
mbedtls_mpi_free(&base);
|
|
mbedtls_entropy_free(&entropy);
|
|
mbedtls_ctr_drbg_free(&ctr_drbg);
|
|
return PM3_SUCCESS;
|
|
}
|
|
|
|
int centerThreshold(const int *in, int *out, size_t len, int8_t up, int8_t down) {
|
|
if (len < 5) {
|
|
return PM3_EINVARG;
|
|
}
|
|
|
|
for (size_t i = 0; i < len; ++i) {
|
|
if ((in[i] <= up) && (in[i] >= down)) {
|
|
out[i] = 0;
|
|
}
|
|
}
|
|
|
|
// clean out spikes.
|
|
for (size_t i = 2; i < len - 2; ++i) {
|
|
|
|
int a = out[i - 2] + out[i - 1];
|
|
int b = out[i + 2] + out[i + 1];
|
|
if (a == 0 && b == 0) {
|
|
out[i] = 0;
|
|
}
|
|
}
|
|
return PM3_SUCCESS;
|
|
}
|
|
|
|
static int CmdCenterThreshold(const char *Cmd) {
|
|
CLIParserContext *ctx;
|
|
CLIParserInit(&ctx, "data cthreshold",
|
|
"Inverse of dirty threshold command, all values between up and down will be average out",
|
|
"data cthreshold -u 10 -d -10"
|
|
);
|
|
void *argtable[] = {
|
|
arg_param_begin,
|
|
arg_int1("d", "down", "<dec>", "threshold down"),
|
|
arg_int1("u", "up", "<dec>", "threshold up"),
|
|
arg_param_end
|
|
};
|
|
CLIExecWithReturn(ctx, Cmd, argtable, false);
|
|
int8_t down = arg_get_int(ctx, 1);
|
|
int8_t up = arg_get_int(ctx, 2);
|
|
CLIParserFree(ctx);
|
|
|
|
PrintAndLogEx(INFO, "Applying up threshold: " _YELLOW_("%i") ", down threshold: " _YELLOW_("%i") "\n", up, down);
|
|
|
|
centerThreshold(g_GraphBuffer, g_GraphBuffer, g_GraphTraceLen, up, down);
|
|
|
|
// set signal properties low/high/mean/amplitude and isnoice detection
|
|
uint8_t *bits = calloc(g_GraphTraceLen, sizeof(uint8_t));
|
|
if (bits == NULL) {
|
|
PrintAndLogEx(WARNING, "Failed to allocate memory");
|
|
return PM3_EMALLOC;
|
|
}
|
|
size_t size = getFromGraphBuffer(bits);
|
|
// set signal properties low/high/mean/amplitude and is_noice detection
|
|
computeSignalProperties(bits, size);
|
|
RepaintGraphWindow();
|
|
free(bits);
|
|
return PM3_SUCCESS;
|
|
}
|
|
|
|
static int envelope_square(const int *in, int *out, size_t len) {
|
|
if (len < 10) {
|
|
return PM3_EINVARG;
|
|
}
|
|
|
|
|
|
size_t i = 0;
|
|
while (i < len - 8) {
|
|
|
|
if (in[i] == 0 && in[i + 1] == 0 && in[i + 2] == 0 && in[i + 3] == 0 &&
|
|
in[i + 4] == 0 && in[i + 5] == 0 && in[i + 6] == 0 && in[i + 7] == 0) {
|
|
|
|
i += 8;
|
|
continue;
|
|
}
|
|
|
|
out[i] = 255;
|
|
i++;
|
|
}
|
|
return PM3_SUCCESS;
|
|
}
|
|
|
|
static int CmdEnvelope(const char *Cmd) {
|
|
CLIParserContext *ctx;
|
|
CLIParserInit(&ctx, "data envelope",
|
|
"Create an square envelope of the samples",
|
|
"data envelope"
|
|
);
|
|
void *argtable[] = {
|
|
arg_param_begin,
|
|
arg_param_end
|
|
};
|
|
CLIExecWithReturn(ctx, Cmd, argtable, true);
|
|
CLIParserFree(ctx);
|
|
|
|
envelope_square(g_GraphBuffer, g_GraphBuffer, g_GraphTraceLen);
|
|
|
|
uint8_t *bits = calloc(g_GraphTraceLen, sizeof(uint8_t));
|
|
if (bits == NULL) {
|
|
PrintAndLogEx(WARNING, "Failed to allocate memory");
|
|
return PM3_EMALLOC;
|
|
}
|
|
size_t size = getFromGraphBuffer(bits);
|
|
// set signal properties low/high/mean/amplitude and is_noice detection
|
|
computeSignalProperties(bits, size);
|
|
RepaintGraphWindow();
|
|
free(bits);
|
|
return PM3_SUCCESS;
|
|
}
|
|
|
|
static int CmdAtrLookup(const char *Cmd) {
|
|
CLIParserContext *ctx;
|
|
CLIParserInit(&ctx, "data atr",
|
|
"look up ATR record from bytearray\n"
|
|
"",
|
|
"data atr -d 3B6B00000031C064BE1B0100079000\n"
|
|
);
|
|
|
|
void *argtable[] = {
|
|
arg_param_begin,
|
|
arg_str0("d", NULL, "<hex>", "ASN1 encoded byte array"),
|
|
// arg_lit0("t", "test", "perform self test"),
|
|
arg_param_end
|
|
};
|
|
CLIExecWithReturn(ctx, Cmd, argtable, false);
|
|
uint8_t data[129];
|
|
int dlen = sizeof(data) - 1; // CLIGetStrWithReturn does not guarantee string to be null-terminated
|
|
CLIGetStrWithReturn(ctx, 1, data, &dlen);
|
|
|
|
// bool selftest = arg_get_lit(ctx, 2);
|
|
CLIParserFree(ctx);
|
|
// if (selftest) {
|
|
// return atr_selftest();
|
|
// }
|
|
PrintAndLogEx(INFO, "ISO7816-3 ATR... " _YELLOW_("%s"), data);
|
|
PrintAndLogEx(INFO, "Fingerprint...");
|
|
|
|
char *copy = str_dup(getAtrInfo((char *)data));
|
|
|
|
char *token = strtok(copy, "\n");
|
|
while (token != NULL) {
|
|
PrintAndLogEx(INFO, " %s", token);
|
|
token = strtok(NULL, "\n");
|
|
}
|
|
free(copy);
|
|
return PM3_SUCCESS;
|
|
}
|
|
|
|
static int CmdCryptography(const char *Cmd) {
|
|
CLIParserContext *ctx;
|
|
CLIParserInit(&ctx, "data crypto",
|
|
"Encrypt data, right here, right now. Or decrypt.",
|
|
"Supply data, key, IV (needed for des MAC or aes), and cryptography action.\n"
|
|
"To calculate a MAC for FMCOS, supply challenge as IV, data as data, and session/line protection key as key.\n"
|
|
"To calculate a MAC for FeliCa, supply first RC as IV, BLE+data as data and session key as key.\n"
|
|
"data crypto -d 04D6850E06AABB80 -k FFFFFFFFFFFFFFFF --iv 9EA0401A00000000 --des -> Calculate a MAC for FMCOS chip. The result should be ED3A0133\n"
|
|
);
|
|
void *argtable[] = {
|
|
arg_param_begin,
|
|
arg_str1("d", "data", "<hex>", "Data to process"),
|
|
arg_str1("k", "key", "<hex>", "Key to use"),
|
|
arg_lit0("r", "rev", "Decrypt, not encrypt"),
|
|
arg_lit0(NULL, "des", "Cipher with DES, not AES"),
|
|
arg_lit0(NULL, "mac", "Calculate AES CMAC/FeliCa Lite MAC"),
|
|
arg_str0(NULL, "iv", "<hex>", "IV value if needed"),
|
|
arg_param_end
|
|
};
|
|
CLIExecWithReturn(ctx, Cmd, argtable, false);
|
|
uint8_t dati[250] = {0};
|
|
uint8_t dato[250] = {0};
|
|
int datilen = 0;
|
|
CLIGetHexWithReturn(ctx, 1, dati, &datilen);
|
|
uint8_t key[25] = {0};
|
|
int keylen = 0;
|
|
CLIGetHexWithReturn(ctx, 2, key, &keylen);
|
|
int type = 0;
|
|
if (arg_get_lit(ctx, 3)) type ^= 8;
|
|
if (arg_get_lit(ctx, 4)) type ^= 4;
|
|
if (arg_get_lit(ctx, 5)) type ^= 2;
|
|
uint8_t iv[250] = {0};
|
|
int ivlen = 0;
|
|
CLIGetHexWithReturn(ctx, 6, iv, &ivlen);
|
|
CLIParserFree(ctx);
|
|
|
|
// Do data length check
|
|
if ((type & 0x4) >> 2) { // Use AES(0) or DES(1)?
|
|
|
|
if (datilen % 8 != 0) {
|
|
PrintAndLogEx(ERR, "<data> length must be a multiple of 8. Got %d", datilen);
|
|
return PM3_EINVARG;
|
|
}
|
|
|
|
if (keylen != 8 && keylen != 16 && keylen != 24) {
|
|
PrintAndLogEx(ERR, "<key> must be 8, 16 or 24 bytes. Got %d", keylen);
|
|
return PM3_EINVARG;
|
|
}
|
|
|
|
} else {
|
|
|
|
if (datilen % 16 != 0 && ((type & 0x2) >> 1 == 0)) {
|
|
PrintAndLogEx(ERR, "<data> length must be a multiple of 16. Got %d", datilen);
|
|
return PM3_EINVARG;
|
|
}
|
|
|
|
if (keylen != 16) {
|
|
PrintAndLogEx(ERR, "<key> must be 16 bytes. Got %d", keylen);
|
|
return PM3_EINVARG;
|
|
}
|
|
}
|
|
|
|
// Encrypt(0) or decrypt(1)?
|
|
if ((type & 0x8) >> 3) {
|
|
|
|
if ((type & 0x4) >> 2) { // AES or DES?
|
|
|
|
if (keylen > 8) {
|
|
|
|
PrintAndLogEx(INFO, "Called 3DES decrypt");
|
|
des3_decrypt(dato, dati, key, keylen / 8);
|
|
|
|
} else {
|
|
|
|
PrintAndLogEx(INFO, "Called DES decrypt");
|
|
if (ivlen == 0) {
|
|
// If there's an IV, use CBC
|
|
des_decrypt_ecb(dato, dati, datilen, key);
|
|
} else {
|
|
des_decrypt_cbc(dato, dati, datilen, key, iv);
|
|
}
|
|
}
|
|
} else {
|
|
PrintAndLogEx(INFO, "Called AES decrypt");
|
|
aes_decode(iv, key, dati, dato, datilen);
|
|
}
|
|
|
|
} else {
|
|
if (type & 0x4) { // AES or DES?
|
|
if (type & 0x02) { // If we will calculate a MAC
|
|
/*PrintAndLogEx(INFO, "Called FeliCa MAC");
|
|
// For DES all I know useful is the felica and fudan MAC algorithm.This is just des-cbc, but felica needs it in its way.
|
|
for (int i = 0; i < datilen; i+=8){ // For all 8 byte sequences
|
|
reverser(dati, &dati[i], 8, i);
|
|
if (i){ // If IV is processed
|
|
for (int n = 0; n < 8; ++n){
|
|
dato[n] ^= dati[i+n]; // XOR with Dx
|
|
}
|
|
des_encrypt_ecb(dato, dato, 8, key); // Cipher itself
|
|
} else { // If we didn't start with IV
|
|
for (int n = 0; n < 8; ++n){
|
|
dato[n] = iv[n]; // Feed data into output
|
|
dato[n] ^= dati[i+n]; // XOR with D1
|
|
}
|
|
des_encrypt_ecb(dato, dato, 8, key); // Cipher itself
|
|
}
|
|
}
|
|
PrintAndLogEx(SUCCESS, "MAC: %s", sprint_hex_inrow(dato, 8));*/
|
|
PrintAndLogEx(INFO, "Not implemented yet - feel free to contribute!");
|
|
return PM3_SUCCESS;
|
|
} else {
|
|
|
|
if (keylen > 8) {
|
|
PrintAndLogEx(INFO, "Called 3DES encrypt keysize: %i", keylen / 8);
|
|
des3_encrypt(dato, dati, key, keylen / 8);
|
|
} else {
|
|
|
|
PrintAndLogEx(INFO, "Called DES encrypt");
|
|
|
|
if (ivlen == 0) {
|
|
// If there's an IV, use ECB
|
|
des_encrypt_ecb(dato, dati, datilen, key);
|
|
} else {
|
|
des_encrypt_cbc(dato, dati, datilen, key, iv);
|
|
char pad[250];
|
|
memset(pad, ' ', 4 + 8 + (datilen - 8) * 3);
|
|
pad[8 + (datilen - 8) * 3] = 0; // Make a padding to insert FMCOS macing algorithm guide
|
|
PrintAndLogEx(INFO, "%sVV VV VV VV FMCOS MAC", pad);
|
|
}
|
|
}
|
|
}
|
|
} else {
|
|
|
|
if (type & 0x02) {
|
|
PrintAndLogEx(INFO, "Called AES CMAC");
|
|
// If we will calculate a MAC
|
|
aes_cmac8(iv, key, dati, dato, datilen);
|
|
} else {
|
|
PrintAndLogEx(INFO, "Called AES encrypt");
|
|
aes_encode(iv, key, dati, dato, datilen);
|
|
}
|
|
}
|
|
}
|
|
PrintAndLogEx(SUCCESS, "Result: %s", sprint_hex(dato, datilen));
|
|
return PM3_SUCCESS;
|
|
}
|
|
|
|
static int CmdBinaryMap(const char *Cmd) {
|
|
CLIParserContext *ctx;
|
|
CLIParserInit(&ctx, "data bmap",
|
|
"Breaks down a hex value to binary according a template\n"
|
|
" data bmap -d 16 -m 4,4\n"
|
|
"This will give two rows each with four bits",
|
|
"data bmap -d 3B\n"
|
|
"data bmap -d 3B -m 2,5,1\n"
|
|
);
|
|
|
|
void *argtable[] = {
|
|
arg_param_begin,
|
|
arg_str0("d", NULL, "<hex>", "hex string"),
|
|
arg_str0("m", NULL, "<str>", "binary template"),
|
|
arg_param_end
|
|
};
|
|
CLIExecWithReturn(ctx, Cmd, argtable, false);
|
|
|
|
uint8_t hex[6];
|
|
int hlen = sizeof(hex) - 1; // CLIGetStrWithReturn does not guarantee string to be null-terminated
|
|
CLIGetStrWithReturn(ctx, 1, hex, &hlen);
|
|
|
|
uint8_t template[41];
|
|
int tlen = sizeof(template) - 1; // CLIGetStrWithReturn does not guarantee string to be null-terminated
|
|
CLIGetStrWithReturn(ctx, 2, template, &tlen);
|
|
CLIParserFree(ctx);
|
|
|
|
char bits[(8 * 4) + 1] = {0};
|
|
hextobinstring_n(bits, (char *)hex, hlen);
|
|
|
|
if (tlen == 0) {
|
|
template[0] = '8';
|
|
template[1] = 0;
|
|
}
|
|
|
|
char *token = strtok((char *)template, ",");
|
|
|
|
// header
|
|
PrintAndLogEx(INFO, "---+-------------------------");
|
|
PrintAndLogEx(INFO, " | b0 b1 b2 b3 b4 b5 b6 b7");
|
|
PrintAndLogEx(INFO, "---+-------------------------");
|
|
|
|
uint8_t i = 0;
|
|
uint8_t cnt = 1;
|
|
int x = 0;
|
|
while (token != NULL) {
|
|
sscanf(token, "%d", &x);
|
|
|
|
PrintAndLogEx(INFO, " %d | %.*s" NOLF, cnt, i * 3, " ");
|
|
|
|
// incease with previous offset
|
|
x += i;
|
|
|
|
for (; i < (uint8_t)x; i++) {
|
|
PrintAndLogEx(NORMAL, "%c " NOLF, bits[7 - i]);
|
|
}
|
|
|
|
PrintAndLogEx(NORMAL, "");
|
|
token = strtok(NULL, ",");
|
|
cnt++;
|
|
}
|
|
|
|
PrintAndLogEx(NORMAL, "");
|
|
return PM3_SUCCESS;
|
|
}
|
|
|
|
static int CmdXor(const char *Cmd) {
|
|
CLIParserContext *ctx;
|
|
CLIParserInit(&ctx, "data xor",
|
|
"takes input string and xor string. Perform xor on it.\n"
|
|
"If no xor string, try the most reoccuring value to xor against",
|
|
"data xor -d 99aabbcc8888888888\n"
|
|
"data xor -d 99aabbcc --xor 88888888\n"
|
|
);
|
|
|
|
void *argtable[] = {
|
|
arg_param_begin,
|
|
arg_str1("d", "data", "<hex>", "input hex string"),
|
|
arg_str0("x", "xor", "<str>", "input xor string"),
|
|
arg_param_end
|
|
};
|
|
CLIExecWithReturn(ctx, Cmd, argtable, false);
|
|
|
|
int hlen = 128;
|
|
uint8_t hex[128 + 1];
|
|
CLIGetHexWithReturn(ctx, 1, hex, &hlen);
|
|
|
|
int xlen = 128;
|
|
uint8_t xor[128 + 1];
|
|
CLIGetHexWithReturn(ctx, 2, xor, &xlen);
|
|
CLIParserFree(ctx);
|
|
|
|
// find xor value
|
|
if (xlen == 0) {
|
|
uint8_t x = get_highest_frequency(hex, hlen);
|
|
xlen = hlen;
|
|
memset(xor, x, xlen);
|
|
}
|
|
|
|
if (hlen != xlen) {
|
|
PrintAndLogEx(FAILED, "Length mismatch, got %i != %i", hlen, xlen);
|
|
return PM3_EINVARG;
|
|
}
|
|
|
|
PrintAndLogEx(SUCCESS, "input... %s", sprint_hex_inrow(hex, hlen));
|
|
PrintAndLogEx(SUCCESS, "xor..... %s", sprint_hex_inrow(xor, xlen));
|
|
hex_xor(hex, xor, hlen);
|
|
PrintAndLogEx(SUCCESS, "plain... " _YELLOW_("%s"), sprint_hex_inrow(hex, hlen));
|
|
return PM3_SUCCESS;
|
|
}
|
|
|
|
static int CmdTestSaveState8(const char *Cmd) {
|
|
CLIParserContext *ctx;
|
|
CLIParserInit(&ctx, "data test_ss8",
|
|
"Tests the implementation of Buffer Save States (8-bit buffer)",
|
|
"data test_ss8");
|
|
void *argtable[] = {
|
|
arg_param_begin,
|
|
arg_param_end
|
|
};
|
|
CLIExecWithReturn(ctx, Cmd, argtable, true);
|
|
CLIParserFree(ctx);
|
|
|
|
srand(time(NULL));
|
|
|
|
size_t length = (rand() % 256);
|
|
PrintAndLogEx(DEBUG, "Testing with length = %llu", length);
|
|
uint8_t *srcBuffer = (uint8_t *)calloc(length + 1, sizeof(uint8_t));
|
|
|
|
//Set up the source buffer with random data
|
|
for (int i = 0; i < length; i++) {
|
|
srcBuffer[i] = (rand() % 256);
|
|
}
|
|
|
|
buffer_savestate_t test8 = save_buffer8(srcBuffer, length);
|
|
PrintAndLogEx(DEBUG, "Save State created, length = %llu, padding = %i, type = %i", test8.bufferSize, test8.padding, test8.type);
|
|
|
|
test8.clock = rand();
|
|
test8.offset = rand();
|
|
PrintAndLogEx(DEBUG, "Save State clock = %u, offset = %u", test8.clock, test8.offset);
|
|
|
|
uint8_t *destBuffer = (uint8_t *)calloc(length, sizeof(uint8_t));
|
|
size_t returnedLength = restore_buffer8(test8, destBuffer);
|
|
|
|
if (returnedLength != length) {
|
|
PrintAndLogEx(DEBUG, _YELLOW_("Returned length != expected length!"));
|
|
PrintAndLogEx(WARNING, "Returned Length = %llu Buffer Length = %llu Expected = %llu", returnedLength, test8.bufferSize, length);
|
|
} else {
|
|
PrintAndLogEx(DEBUG, _GREEN_("Lengths match!") "\n");
|
|
}
|
|
|
|
for (size_t i = 0; i < returnedLength; i++) {
|
|
if (srcBuffer[i] != destBuffer[i]) {
|
|
PrintAndLogEx(FAILED, "Buffers don't match at index %lu!, Expected %i, got %i", i, srcBuffer[i], destBuffer[i]);
|
|
free(srcBuffer);
|
|
free(destBuffer);
|
|
return PM3_EFAILED;
|
|
}
|
|
PrintAndLogEx(DEBUG, "Index %lu matches: %u", i, destBuffer[i]);
|
|
}
|
|
PrintAndLogEx(SUCCESS, _GREEN_("Save State (8-bit) test success!") "\n");
|
|
|
|
free(srcBuffer);
|
|
free(destBuffer);
|
|
return PM3_SUCCESS;
|
|
}
|
|
|
|
static int CmdTestSaveState32(const char *Cmd) {
|
|
CLIParserContext *ctx;
|
|
CLIParserInit(&ctx, "data test_ss32",
|
|
"Tests the implementation of Buffer Save States (32-bit buffer)",
|
|
"data test_ss32");
|
|
void *argtable[] = {
|
|
arg_param_begin,
|
|
arg_param_end
|
|
};
|
|
CLIExecWithReturn(ctx, Cmd, argtable, true);
|
|
CLIParserFree(ctx);
|
|
|
|
srand(time(NULL));
|
|
|
|
size_t length = 64;
|
|
uint32_t *srcBuffer = (uint32_t *)calloc(length, sizeof(uint32_t));
|
|
|
|
//Set up the source buffer with random data
|
|
for (size_t i = 0; i < length; i++) {
|
|
srcBuffer[i] = (rand());
|
|
}
|
|
|
|
buffer_savestate_t test32 = save_buffer32(srcBuffer, length);
|
|
PrintAndLogEx(DEBUG, "Save State created, length=%llu, type=%i", test32.bufferSize, test32.type);
|
|
|
|
test32.clock = rand();
|
|
test32.offset = rand();
|
|
PrintAndLogEx(DEBUG, "Save State clock=%u, offset=%u", test32.clock, test32.offset);
|
|
|
|
uint32_t *destBuffer = (uint32_t *)calloc(length, sizeof(uint32_t));
|
|
size_t returnedLength = restore_buffer32(test32, destBuffer);
|
|
|
|
if (returnedLength != length) {
|
|
PrintAndLogEx(FAILED, "Return Length != Buffer Length! Expected '%llu', got '%llu", g_DemodBufferLen, returnedLength);
|
|
free(srcBuffer);
|
|
free(destBuffer);
|
|
return PM3_EFAILED;
|
|
}
|
|
PrintAndLogEx(DEBUG, _GREEN_("Lengths match!") "\n");
|
|
|
|
for (size_t i = 0; i < length; i++) {
|
|
if (srcBuffer[i] != destBuffer[i]) {
|
|
PrintAndLogEx(FAILED, "Buffers don't match at index %lu!, Expected %i, got %i", i, srcBuffer[i], destBuffer[i]);
|
|
free(srcBuffer);
|
|
free(destBuffer);
|
|
return PM3_EFAILED;
|
|
}
|
|
PrintAndLogEx(DEBUG, "Index %lu matches: %i", i, destBuffer[i]);
|
|
}
|
|
PrintAndLogEx(SUCCESS, _GREEN_("Save State (32-bit) test success!") "\n");
|
|
|
|
free(srcBuffer);
|
|
free(destBuffer);
|
|
return PM3_SUCCESS;
|
|
}
|
|
|
|
static int CmdTestSaveState32S(const char *Cmd) {
|
|
CLIParserContext *ctx;
|
|
CLIParserInit(&ctx, "data test_ss32s",
|
|
"Tests the implementation of Buffer Save States (32-bit signed buffer)",
|
|
"data test_ss32s");
|
|
void *argtable[] = {
|
|
arg_param_begin,
|
|
arg_param_end
|
|
};
|
|
CLIExecWithReturn(ctx, Cmd, argtable, true);
|
|
CLIParserFree(ctx);
|
|
|
|
srand(time(NULL));
|
|
|
|
size_t length = 64;
|
|
int32_t *srcBuffer = (int32_t *)calloc(length, sizeof(int32_t));
|
|
|
|
//Set up the source buffer with random data
|
|
for (int i = 0; i < length; i++) {
|
|
srcBuffer[i] = (rand() - 4294967296);
|
|
}
|
|
|
|
buffer_savestate_t test32 = save_bufferS32(srcBuffer, length);
|
|
PrintAndLogEx(DEBUG, "Save State created, length=%llu, type=%i", test32.bufferSize, test32.type);
|
|
|
|
test32.clock = rand();
|
|
test32.offset = rand();
|
|
PrintAndLogEx(DEBUG, "Save State clock=%u, offset=%u", test32.clock, test32.offset);
|
|
|
|
int32_t *destBuffer = (int32_t *)calloc(length, sizeof(int32_t));
|
|
size_t returnedLength = restore_bufferS32(test32, destBuffer);
|
|
|
|
if (returnedLength != length) {
|
|
PrintAndLogEx(FAILED, "Return Length != Buffer Length! Expected '%llu', got '%llu", g_DemodBufferLen, returnedLength);
|
|
free(srcBuffer);
|
|
free(destBuffer);
|
|
return PM3_EFAILED;
|
|
}
|
|
PrintAndLogEx(DEBUG, _GREEN_("Lengths match!") "\n");
|
|
|
|
for (int i = 0; i < length; i++) {
|
|
if (srcBuffer[i] != destBuffer[i]) {
|
|
PrintAndLogEx(FAILED, "Buffers don't match at index %i!, Expected %i, got %i", i, srcBuffer[i], destBuffer[i]);
|
|
free(srcBuffer);
|
|
free(destBuffer);
|
|
return PM3_EFAILED;
|
|
}
|
|
PrintAndLogEx(DEBUG, "Index %i matches: %i", i, destBuffer[i]);
|
|
}
|
|
PrintAndLogEx(SUCCESS, _GREEN_("Save State (signed 32-bit) test success!") "\n");
|
|
|
|
free(srcBuffer);
|
|
free(destBuffer);
|
|
return PM3_SUCCESS;
|
|
}
|
|
|
|
static command_t CommandTable[] = {
|
|
{"help", CmdHelp, AlwaysAvailable, "This help"},
|
|
{"-----------", CmdHelp, AlwaysAvailable, "------------------------- " _CYAN_("General") "-------------------------"},
|
|
{"clear", CmdBuffClear, AlwaysAvailable, "Clears various buffers used by the graph window"},
|
|
{"hide", CmdHide, AlwaysAvailable, "Hide the graph window"},
|
|
{"load", CmdLoad, AlwaysAvailable, "Load contents of file into graph window"},
|
|
{"num", CmdNumCon, AlwaysAvailable, "Converts dec/hex/bin"},
|
|
{"plot", CmdPlot, AlwaysAvailable, "Show the graph window"},
|
|
{"print", CmdPrintDemodBuff, AlwaysAvailable, "Print the data in the DemodBuffer"},
|
|
{"save", CmdSave, AlwaysAvailable, "Save signal trace data"},
|
|
{"setdebugmode", CmdSetDebugMode, AlwaysAvailable, "Set Debugging Level on client side"},
|
|
{"xor", CmdXor, AlwaysAvailable, "Xor a input string"},
|
|
|
|
{"-----------", CmdHelp, AlwaysAvailable, "------------------------- " _CYAN_("Modulation") "-------------------------"},
|
|
{"biphaserawdecode", CmdBiphaseDecodeRaw, AlwaysAvailable, "Biphase decode bin stream in DemodBuffer"},
|
|
{"detectclock", CmdDetectClockRate, AlwaysAvailable, "Detect ASK, FSK, NRZ, PSK clock rate of wave in GraphBuffer"},
|
|
{"fsktonrz", CmdFSKToNRZ, AlwaysAvailable, "Convert fsk2 to nrz wave for alternate fsk demodulating (for weak fsk)"},
|
|
{"manrawdecode", Cmdmandecoderaw, AlwaysAvailable, "Manchester decode binary stream in DemodBuffer"},
|
|
{"modulation", CmdDataModulationSearch, AlwaysAvailable, "Identify LF signal for clock and modulation"},
|
|
{"rawdemod", CmdRawDemod, AlwaysAvailable, "Demodulate the data in the GraphBuffer and output binary"},
|
|
|
|
{"-----------", CmdHelp, AlwaysAvailable, "------------------------- " _CYAN_("Graph") "-------------------------"},
|
|
{"askedgedetect", CmdAskEdgeDetect, AlwaysAvailable, "Adjust Graph for manual ASK demod"},
|
|
{"autocorr", CmdAutoCorr, AlwaysAvailable, "Autocorrelation over window"},
|
|
{"convertbitstream", CmdConvertBitStream, AlwaysAvailable, "Convert GraphBuffer's 0/1 values to 127 / -127"},
|
|
{"cthreshold", CmdCenterThreshold, AlwaysAvailable, "Average out all values between"},
|
|
{"dirthreshold", CmdDirectionalThreshold, AlwaysAvailable, "Max rising higher up-thres/ Min falling lower down-thres"},
|
|
{"decimate", CmdDecimate, AlwaysAvailable, "Decimate samples"},
|
|
{"envelope", CmdEnvelope, AlwaysAvailable, "Generate square envelope of samples"},
|
|
{"grid", CmdGrid, AlwaysAvailable, "overlay grid on graph window"},
|
|
{"getbitstream", CmdGetBitStream, AlwaysAvailable, "Convert GraphBuffer's >=1 values to 1 and <1 to 0"},
|
|
{"hpf", CmdHpf, AlwaysAvailable, "Remove DC offset from trace"},
|
|
{"iir", CmdDataIIR, AlwaysAvailable, "Apply IIR buttersworth filter on plot data"},
|
|
{"ltrim", CmdLtrim, AlwaysAvailable, "Trim samples from left of trace"},
|
|
{"mtrim", CmdMtrim, AlwaysAvailable, "Trim out samples from the specified start to the specified stop"},
|
|
{"norm", CmdNorm, AlwaysAvailable, "Normalize max/min to +/-128"},
|
|
{"rtrim", CmdRtrim, AlwaysAvailable, "Trim samples from right of trace"},
|
|
{"setgraphmarkers", CmdSetGraphMarkers, AlwaysAvailable, "Set the markers in the graph window"},
|
|
{"shiftgraphzero", CmdGraphShiftZero, AlwaysAvailable, "Shift 0 for Graphed wave + or - shift value"},
|
|
{"timescale", CmdTimeScale, AlwaysAvailable, "Set cursor display timescale"},
|
|
{"undecimate", CmdUndecimate, AlwaysAvailable, "Un-decimate samples"},
|
|
{"zerocrossings", CmdZerocrossings, AlwaysAvailable, "Count time between zero-crossings"},
|
|
|
|
{"-----------", CmdHelp, AlwaysAvailable, "------------------------- " _CYAN_("Operations") "-------------------------"},
|
|
{"asn1", CmdAsn1Decoder, AlwaysAvailable, "ASN1 decoder"},
|
|
{"atr", CmdAtrLookup, AlwaysAvailable, "ATR lookup"},
|
|
{"bitsamples", CmdBitsamples, IfPm3Present, "Get raw samples as bitstring"},
|
|
{"bmap", CmdBinaryMap, AlwaysAvailable, "Convert hex value according a binary template"},
|
|
{"crypto", CmdCryptography, AlwaysAvailable, "Encrypt and decrypt data"},
|
|
{"diff", CmdDiff, AlwaysAvailable, "Diff of input files"},
|
|
{"hexsamples", CmdHexsamples, IfPm3Present, "Dump big buffer as hex bytes"},
|
|
{"samples", CmdSamples, IfPm3Present, "Get raw samples for graph window ( GraphBuffer )"},
|
|
|
|
{"-----------", CmdHelp, IfClientDebugEnabled, "------------------------- " _CYAN_("Debug") "-------------------------"},
|
|
{"test_ss8", CmdTestSaveState8, IfClientDebugEnabled, "Test the implementation of Buffer Save States (8-bit buffer)"},
|
|
{"test_ss32", CmdTestSaveState32, IfClientDebugEnabled, "Test the implementation of Buffer Save States (32-bit buffer)"},
|
|
{"test_ss32s", CmdTestSaveState32S, IfClientDebugEnabled, "Test the implementation of Buffer Save States (32-bit signed buffer)"},
|
|
|
|
{NULL, NULL, NULL, NULL}
|
|
};
|
|
|
|
static int CmdHelp(const char *Cmd) {
|
|
(void)Cmd; // Cmd is not used so far
|
|
CmdsHelp(CommandTable);
|
|
return PM3_SUCCESS;
|
|
}
|
|
|
|
int CmdData(const char *Cmd) {
|
|
clearCommandBuffer();
|
|
return CmdsParse(CommandTable, Cmd);
|
|
}
|