1 //-----------------------------------------------------------------------------
2 // Copyright (C) Proxmark3 contributors. See AUTHORS.md for details.
4 // This program is free software: you can redistribute it and/or modify
5 // it under the terms of the GNU General Public License as published by
6 // the Free Software Foundation, either version 3 of the License, or
7 // (at your option) any later version.
9 // This program is distributed in the hope that it will be useful,
10 // but WITHOUT ANY WARRANTY; without even the implied warranty of
11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 // GNU General Public License for more details.
14 // See LICENSE.txt for the text of the license.
15 //-----------------------------------------------------------------------------
16 // Data and Graph commands
17 //-----------------------------------------------------------------------------
21 #include <limits.h> // for CmdNorm INT_MIN && INT_MAX
22 #include <math.h> // pow
23 #include <ctype.h> // tolower
24 #include <locale.h> // number formatter..
25 #include "commonutil.h" // ARRAYLEN
26 #include "cmdparser.h" // for command_t
27 #include "ui.h" // for show graph controls
29 #include "graph.h" // for graph data
31 #include "lfdemod.h" // for demod code
32 #include "cmdlf.h" // for lf_getconfig
33 #include "loclass/cipherutils.h" // for decimating samples in getsamples
34 #include "cmdlfem410x.h" // askem410xdecode
35 #include "fileutils.h" // searchFile
36 #include "cliparser.h"
37 #include "cmdlft55xx.h" // print...
38 #include "crypto/asn1utils.h" // ASN1 decode / print
39 #include "cmdflashmemspiffs.h" // SPIFFS flash memory download
40 #include "mbedtls/bignum.h" // big num
41 #include "mbedtls/entropy.h" //
42 #include "mbedtls/ctr_drbg.h" // random generator
43 #include "atrs.h" // ATR lookup
44 #include "crypto/libpcrypto.h" // Cryptography
47 uint8_t g_DemodBuffer
[MAX_DEMOD_BUF_LEN
] = { 0x00 };
48 size_t g_DemodBufferLen
= 0;
49 int32_t g_DemodStartIdx
= 0;
52 static int CmdHelp(const char *Cmd
);
55 // https://www.eskimo.com/~scs/c-faq.com/stdio/commaprint.html
56 static char *commaprint(size_t n
) {
58 static int comma
= '\0';
59 static char retbuf
[30];
61 char *p
= &retbuf
[sizeof(retbuf
) - 1];
66 struct lconv
*lcp
= localeconv();
69 if (lcp
->thousands_sep
!= NULL
&& *lcp
->thousands_sep
!= '\0') {
70 comma
= *lcp
->thousands_sep
;
80 if (i
% 3 == 0 && i
!= 0) {
95 // set the g_DemodBuffer with given array ofq binary (one bit per byte)
96 void setDemodBuff(const uint8_t *buff
, size_t size
, size_t start_idx
) {
97 if (buff
== NULL
) return;
99 if (size
> MAX_DEMOD_BUF_LEN
- start_idx
)
100 size
= MAX_DEMOD_BUF_LEN
- start_idx
;
102 for (size_t i
= 0; i
< size
; i
++)
103 g_DemodBuffer
[i
] = buff
[start_idx
++];
105 g_DemodBufferLen
= size
;
108 bool getDemodBuff(uint8_t *buff
, size_t *size
) {
109 if (buff
== NULL
) return false;
110 if (size
== NULL
) return false;
111 if (*size
== 0) return false;
113 *size
= (*size
> g_DemodBufferLen
) ? g_DemodBufferLen
: *size
;
115 memcpy(buff
, g_DemodBuffer
, *size
);
122 static double rms(double *v, size_t n) {
124 for (size_t i = 0; i < n; i++)
126 return sqrt(sum / n);
129 static int cmp_int(const void *a, const void *b) {
130 if (*(const int *)a < * (const int *)b)
133 return *(const int *)a > *(const int *)b;
135 static int cmp_uint8(const void *a, const void *b) {
136 if (*(const uint8_t *)a < * (const uint8_t *)b)
139 return *(const uint8_t *)a > *(const uint8_t *)b;
141 // Median of a array of values
143 static double median_int(int *src, size_t size) {
144 qsort(src, size, sizeof(int), cmp_int);
145 return 0.5 * (src[size / 2] + src[(size - 1) / 2]);
147 static double median_uint8(uint8_t *src, size_t size) {
148 qsort(src, size, sizeof(uint8_t), cmp_uint8);
149 return 0.5 * (src[size / 2] + src[(size - 1) / 2]);
152 // function to compute mean for a series
153 static double compute_mean(const int *data
, size_t n
) {
155 for (size_t i
= 0; i
< n
; i
++)
161 // function to compute variance for a series
162 static double compute_variance(const int *data
, size_t n
) {
163 double variance
= 0.0;
164 double mean
= compute_mean(data
, n
);
166 for (size_t i
= 0; i
< n
; i
++)
167 variance
+= pow((data
[i
] - mean
), 2.0);
173 // Function to compute autocorrelation for a series
174 // Author: Kenneth J. Christensen
175 // - Corrected divide by n to divide (n - lag) from Tobias Mueller
177 static double compute_autoc(const int *data, size_t n, int lag) {
178 double autocv = 0.0; // Autocovariance value
179 double ac_value; // Computed autocorrelation value to be returned
180 double variance; // Computed variance
183 mean = compute_mean(data, n);
184 variance = compute_variance(data, n);
186 for (size_t i=0; i < (n - lag); i++)
187 autocv += (data[i] - mean) * (data[i+lag] - mean);
189 autocv = (1.0 / (n - lag)) * autocv;
191 // Autocorrelation is autocovariance divided by variance
192 ac_value = autocv / variance;
197 static int CmdSetDebugMode(const char *Cmd
) {
198 CLIParserContext
*ctx
;
199 CLIParserInit(&ctx
, "data setdebugmode",
200 "Set debugging level on client side",
205 arg_lit0("0", NULL
, "no debug messages"),
206 arg_lit0("1", NULL
, "debug"),
207 arg_lit0("2", NULL
, "verbose debugging"),
210 CLIExecWithReturn(ctx
, Cmd
, argtable
, true);
212 bool dg_0
= arg_get_lit(ctx
, 1);
213 bool dg_1
= arg_get_lit(ctx
, 2);
214 bool dg_2
= arg_get_lit(ctx
, 3);
217 if (dg_0
+ dg_1
+ dg_2
> 1) {
218 PrintAndLogEx(INFO
, "Select only one option");
230 switch (g_debugMode
) {
232 PrintAndLogEx(INFO
, "client debug level... %u ( no debug messages )", g_debugMode
);
235 PrintAndLogEx(INFO
, "client debug level... %u ( debug messages )", g_debugMode
);
238 PrintAndLogEx(INFO
, "client debug level... %u ( verbose debug messages )", g_debugMode
);
246 // max output to MAX_DEMODULATION_BITS bits if we have more
247 // doesn't take inconsideration where the demod offset or bitlen found.
248 int printDemodBuff(uint8_t offset
, bool strip_leading
, bool invert
, bool print_hex
) {
249 size_t len
= g_DemodBufferLen
;
251 PrintAndLogEx(WARNING
, "DemodBuffer is empty");
255 uint8_t *buf
= calloc(len
, sizeof(uint8_t));
257 PrintAndLogEx(WARNING
, "fail, cannot allocate memory");
260 memcpy(buf
, g_DemodBuffer
, len
);
267 if (len
> (g_DemodBufferLen
- offset
)) {
268 len
= (g_DemodBufferLen
- offset
);
272 for (i
= 0; i
< len
; i
++) {
280 if (len
> (g_DemodBufferLen
- offset
)) {
281 len
= (g_DemodBufferLen
- offset
);
284 if (len
> MAX_DEMODULATION_BITS
) {
285 len
= MAX_DEMODULATION_BITS
;
292 for (size_t i
= 0; i
< len
; i
++) {
305 char hex
[MAX_DEMODULATION_BITS
+ 1] = { 0x00 };
306 int num_bits
= binarray_2_hex(hex
, sizeof(hex
), (char *)p
, len
);
314 PrintAndLogEx(SUCCESS
, "DemodBuffer:\n%s", hex
);
316 PrintAndLogEx(SUCCESS
, "DemodBuffer:\n%s", sprint_bytebits_bin_break(buf
+ offset
, len
, 32));
324 int CmdPrintDemodBuff(const char *Cmd
) {
325 CLIParserContext
*ctx
;
326 CLIParserInit(&ctx
, "data print",
327 "Print the data in the DemodBuffer as hex or binary.\n"
328 "Defaults to binary output",
333 arg_lit0("i", "inv", "invert DemodBuffer before printing"),
334 // arg_int0("l","len", "<dec>", "length to print in # of bits or hex characters respectively"),
335 arg_int0("o", "offset", "<dec>", "offset in # of bits"),
336 arg_lit0("s", "strip", "strip leading zeroes, i.e. set offset to first bit equal to one"),
337 arg_lit0("x", "hex", "output in hex (omit for binary output)"),
340 CLIExecWithReturn(ctx
, Cmd
, argtable
, true);
342 bool invert
= arg_get_lit(ctx
, 1);
343 int os
= arg_get_int_def(ctx
, 2, 0);
344 bool lstrip
= arg_get_lit(ctx
, 3);
345 bool print_hex
= arg_get_lit(ctx
, 4);
348 uint8_t offset
= (os
& 0xFF);
350 return printDemodBuff(offset
, lstrip
, invert
, print_hex
);
353 // this function strictly converts >1 to 1 and <1 to 0 for each sample in the graphbuffer
354 int CmdGetBitStream(const char *Cmd
) {
355 CLIParserContext
*ctx
;
356 CLIParserInit(&ctx
, "data getbitstream",
357 "Convert GraphBuffer's value accordingly\n"
358 " - larger or equal to ONE becomes ONE\n"
359 " - less than ONE becomes ZERO",
366 CLIExecWithReturn(ctx
, Cmd
, argtable
, true);
370 for (uint32_t i
= 0; i
< g_GraphTraceLen
; i
++) {
371 g_GraphBuffer
[i
] = (g_GraphBuffer
[i
] >= 1) ? 1 : 0;
373 RepaintGraphWindow();
377 static int CmdConvertBitStream(const char *Cmd
) {
379 CLIParserContext
*ctx
;
380 CLIParserInit(&ctx
, "data convertbitstream",
381 "Convert GraphBuffer's 0|1 values to 127|-127",
382 "data convertbitstream"
388 CLIExecWithReturn(ctx
, Cmd
, argtable
, true);
391 if (isGraphBitstream()) {
392 convertGraphFromBitstream();
395 convertGraphFromBitstreamEx(-126, -127);
400 // Cmd Args: Clock, invert, maxErr, maxLen as integers and amplify as char == 'a'
401 // (amp may not be needed anymore)
402 // verbose will print results and demoding messages
403 // emSearch will auto search for EM410x format in bitstream
404 // askType switches decode: ask/raw = 0, ask/manchester = 1
405 int ASKDemod_ext(int clk
, int invert
, int maxErr
, size_t maxlen
, bool amplify
, bool verbose
, bool emSearch
, uint8_t askType
, bool *stCheck
) {
406 PrintAndLogEx(DEBUG
, "DEBUG: (ASKDemod_ext) clk %i invert %i maxErr %i maxLen %zu amplify %i verbose %i emSearch %i askType %i "
419 maxlen
= g_pm3_capabilities
.bigbuf_size
;
421 uint8_t *bits
= calloc(MAX_GRAPH_TRACE_LEN
, sizeof(uint8_t));
423 PrintAndLogEx(INFO
, "failed to allocate memory");
427 size_t bitlen
= getFromGraphBuffer(bits
);
429 PrintAndLogEx(DEBUG
, "DEBUG: (ASKDemod_ext) #samples from graphbuff: %zu", bitlen
);
436 if (maxlen
< bitlen
&& maxlen
!= 0)
441 //amplify signal before ST check
443 askAmp(bits
, bitlen
);
446 size_t ststart
= 0, stend
= 0;
448 bool st
= DetectST(bits
, &bitlen
, &foundclk
, &ststart
, &stend
);
451 if (foundclk
== 32 || foundclk
== 64) {
458 g_MarkerC
.pos
= ststart
;
459 g_MarkerD
.pos
= stend
;
461 PrintAndLogEx(DEBUG
, "Found Sequence Terminator - First one is shown by orange / blue graph markers");
465 int errCnt
= askdemod_ext(bits
, &bitlen
, &clk
, &invert
, maxErr
, askamp
, askType
, &start_idx
);
466 if (start_idx
>= clk
/ 2) {
467 start_idx
-= clk
/ 2;
469 if (askType
== 0) { // if not Manchester, clock width is halved
472 if (errCnt
< 0 || bitlen
< 16) { //if fatal error (or -1)
473 PrintAndLogEx(DEBUG
, "DEBUG: (ASKDemod_ext) No data found errors:%d, %s bitlen:%zu, clock:%i"
475 , (invert
) ? "inverted," : ""
483 if (errCnt
> maxErr
) {
484 PrintAndLogEx(DEBUG
, "DEBUG: (ASKDemod_ext) Too many errors found, errors:%d, bits:%zu, clock:%i"
494 PrintAndLogEx(DEBUG
, "DEBUG: (ASKDemod_ext) using clock:%i, %sbits found:%zu, start index %d"
496 , (invert
) ? "inverted, " : ""
503 setDemodBuff(bits
, bitlen
, 0);
504 setClockGrid(clk
, start_idx
);
508 PrintAndLogEx(DEBUG
, "# Errors during demoding (shown as 7 in bit stream)... " _RED_("%d"), errCnt
);
511 PrintAndLogEx(SUCCESS
, _YELLOW_("ASK/Manchester") " - clock " _YELLOW_("%i") " - decoded bitstream", clk
);
512 PrintAndLogEx(INFO
, "-----------------------------------------------");
514 PrintAndLogEx(SUCCESS
, _YELLOW_("ASK/Raw") " - clock " _YELLOW_("%i") " - decoded bitstream", clk
);
515 PrintAndLogEx(INFO
, "----------------------------------------");
518 printDemodBuff(0, false, false, false);
523 AskEm410xDecode(true, &hi
, &lo
);
529 int ASKDemod(int clk
, int invert
, int maxErr
, size_t maxlen
, bool amplify
, bool verbose
, bool emSearch
, uint8_t askType
) {
531 return ASKDemod_ext(clk
, invert
, maxErr
, maxlen
, amplify
, verbose
, emSearch
, askType
, &st
);
534 // takes 5 arguments - clock, invert, maxErr, maxLen as integers and amplify as char == 'a'
535 // attempts to demodulate ask while decoding manchester
536 // prints binary found and saves in graphbuffer for further commands
537 static int Cmdaskmandemod(const char *Cmd
) {
538 CLIParserContext
*ctx
;
539 CLIParserInit(&ctx
, "data rawdemod --am",
540 "ASK/MANCHESTER demodulate the data in the GraphBuffer and output binary",
541 "data rawdemod --am --> demod a ask/manchester tag, using autodetect\n"
542 "data rawdemod --am -c 32 --> demod a ask/manchester tag, using a clock of RF/32\n"
543 "data rawdemod --am -i --> demod a ask/manchester tag, using autodetect, invert output\n"
544 "data rawdemod --am -c 32 -i --> demod a ask/manchester tag, using a clock of RF/32, invert output\n"
545 "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"
549 arg_lit0("a", "amp", "try attempt demod with ask amplification (def no amp)"),
550 arg_int0("c", "clk", "<dec>", "set clock manually (def autodetect)"),
551 arg_lit0("i", "inv", "invert output"),
552 arg_lit0("s", "st", "check for sequence terminator"),
553 arg_int0(NULL
, "max", "<dec>", "maximum allowed errors (def 100)"),
554 arg_int0(NULL
, "samples", "<dec>", "maximum samples to read (def 32768) [512 bits at RF/64]"),
557 CLIExecWithReturn(ctx
, Cmd
, argtable
, true);
559 bool amplify
= arg_get_lit(ctx
, 1);
560 uint16_t clk
= (uint16_t)arg_get_int_def(ctx
, 2, 0);
561 bool invert
= arg_get_lit(ctx
, 3);
562 bool st
= arg_get_lit(ctx
, 4);
563 uint8_t max_err
= (uint8_t)arg_get_int_def(ctx
, 5, 100) & 0xFF;
564 size_t max_len
= (size_t)arg_get_int_def(ctx
, 6, 0) & 0xFF;
567 return ASKDemod_ext(clk
, invert
, max_err
, max_len
, amplify
, true, true, 1, &st
);
571 // strictly take 10 and 01 and convert to 0 and 1
572 static int Cmdmandecoderaw(const char *Cmd
) {
573 CLIParserContext
*ctx
;
574 CLIParserInit(&ctx
, "data manrawdecode",
575 "Manchester decode binary stream in DemodBuffer\n"
576 "Converts 10 and 01 and converts to 0 and 1 respectively\n"
577 " - must have binary sequence in DemodBuffer (run `data rawdemod --ar` before)",
582 arg_lit0("i", "inv", "invert output"),
583 arg_int0(NULL
, "err", "<dec>", "set max errors tolerated (def 20)"),
586 CLIExecWithReturn(ctx
, Cmd
, argtable
, true);
587 bool invert
= arg_get_lit(ctx
, 1);
588 int max_err
= arg_get_int_def(ctx
, 2, 20);
591 if (g_DemodBufferLen
== 0) {
592 PrintAndLogEx(WARNING
, "DemodBuffer empty, run " _YELLOW_("`data rawdemod --ar`"));
596 uint8_t *bits
= calloc(MAX_DEMOD_BUF_LEN
, sizeof(uint8_t));
598 PrintAndLogEx(FAILED
, "failed to allocate memory");
602 // make sure its just binary data 0|1|7 in buffer
603 int high
= 0, low
= 0;
605 for (; i
< g_DemodBufferLen
; ++i
) {
606 if (g_DemodBuffer
[i
] > high
)
607 high
= g_DemodBuffer
[i
];
608 else if (g_DemodBuffer
[i
] < low
)
609 low
= g_DemodBuffer
[i
];
610 bits
[i
] = g_DemodBuffer
[i
];
613 if (high
> 7 || low
< 0) {
614 PrintAndLogEx(ERR
, "Error: please first raw demod then manchester raw decode");
621 uint16_t err_cnt
= manrawdecode(bits
, &size
, invert
, &offset
);
622 if (err_cnt
> max_err
) {
623 PrintAndLogEx(ERR
, "Too many errors attempting to decode " _RED_("%i"), err_cnt
);
629 PrintAndLogEx(WARNING
, "# %i errors found during demod (shown as " _YELLOW_(".")" in bit stream) ", err_cnt
);
632 PrintAndLogEx(INFO
, "Manchester decoded %s", (invert
) ? "( inverted )" : "");
633 PrintAndLogEx(INFO
, "%s", sprint_bytebits_bin_break(bits
, size
, 32));
641 int res
= Em410xDecode(bits
, &tmpsize
, &idx
, &hi
, &id
);
643 //need to adjust to set bitstream back to manchester encoded data
644 //setDemodBuff(bits, size, idx);
645 printEM410x(hi
, id
, false, res
);
650 setDemodBuff(bits
, size
, 0);
651 setClockGrid(g_DemodClock
* 2, g_DemodStartIdx
);
657 * @author marshmellow
659 * decodes 01 or 10 -> ZERO
661 * param offset adjust start position
662 * param invert invert output
663 * param masxErr maximum tolerated errors
665 static int CmdBiphaseDecodeRaw(const char *Cmd
) {
666 CLIParserContext
*ctx
;
667 CLIParserInit(&ctx
, "data biphaserawdecode",
668 "Biphase decode binary stream in DemodBuffer\n"
669 "Converts 10 or 01 -> 1 and 11 or 00 -> 0\n"
670 " - must have binary sequence in DemodBuffer (run `data rawdemod --ar` before)\n"
671 " - invert for Conditional Dephase Encoding (CDP) AKA Differential Manchester",
672 "data biphaserawdecode --> decode biphase bitstream from the DemodBuffer\n"
673 "data biphaserawdecode -oi --> decode biphase bitstream from the DemodBuffer, adjust offset, and invert output"
677 arg_lit0("o", "offset", "set to adjust decode start position"),
678 arg_lit0("i", "inv", "invert output"),
679 arg_int0(NULL
, "err", "<dec>", "set max errors tolerated (def 20)"),
682 CLIExecWithReturn(ctx
, Cmd
, argtable
, true);
683 int offset
= arg_get_lit(ctx
, 1);
684 bool invert
= arg_get_lit(ctx
, 2);
685 int max_err
= arg_get_int_def(ctx
, 3, 20);
688 if (g_DemodBufferLen
== 0) {
689 PrintAndLogEx(WARNING
, "DemodBuffer empty, run " _YELLOW_("`data rawdemod --ar`"));
693 uint8_t *bits
= calloc(MAX_DEMOD_BUF_LEN
, sizeof(uint8_t));
695 PrintAndLogEx(FAILED
, "failed to allocate memory");
699 size_t size
= MAX_DEMOD_BUF_LEN
;
700 if (!getDemodBuff(bits
, &size
)) {
705 int err_cnt
= BiphaseRawDecode(bits
, &size
, &offset
, invert
);
707 PrintAndLogEx(ERR
, "Error during decode " _RED_("%i"), err_cnt
);
711 if (err_cnt
> max_err
) {
712 PrintAndLogEx(ERR
, "Too many errors attempting to decode " _RED_("%i"), err_cnt
);
718 PrintAndLogEx(WARNING
, "# %i errors found during demod (shown as " _YELLOW_(".")" in bit stream) ", err_cnt
);
721 PrintAndLogEx(INFO
, "Biphase decoded using offset %d%s", offset
, (invert
) ? "( inverted )" : "");
722 PrintAndLogEx(INFO
, "%s", sprint_bytebits_bin_break(bits
, size
, 32));
724 setDemodBuff(bits
, size
, 0);
725 setClockGrid(g_DemodClock
* 2, g_DemodStartIdx
+ g_DemodClock
* offset
);
730 // ASK Demod then Biphase decode g_GraphBuffer samples
731 int ASKbiphaseDemod(int offset
, int clk
, int invert
, int maxErr
, bool verbose
) {
732 //ask raw demod g_GraphBuffer first
734 uint8_t *bs
= calloc(MAX_DEMOD_BUF_LEN
, sizeof(uint8_t));
736 PrintAndLogEx(FAILED
, "failed to allocate memory");
740 size_t size
= getFromGraphBufferEx(bs
, MAX_DEMOD_BUF_LEN
);
742 PrintAndLogEx(DEBUG
, "DEBUG: no data in graphbuf");
747 //invert here inverts the ask raw demoded bits which has no effect on the demod, but we need the pointer
748 int errCnt
= askdemod_ext(bs
, &size
, &clk
, &invert
, maxErr
, 0, 0, &startIdx
);
749 if (errCnt
< 0 || errCnt
> maxErr
) {
750 PrintAndLogEx(DEBUG
, "DEBUG: no data or error found %d, clock: %d", errCnt
, clk
);
755 //attempt to Biphase decode BitStream
756 errCnt
= BiphaseRawDecode(bs
, &size
, &offset
, invert
);
758 if (g_debugMode
|| verbose
) PrintAndLogEx(DEBUG
, "DEBUG: Error BiphaseRawDecode: %d", errCnt
);
762 if (errCnt
> maxErr
) {
763 if (g_debugMode
|| verbose
) PrintAndLogEx(DEBUG
, "DEBUG: Error BiphaseRawDecode too many errors: %d", errCnt
);
771 //success set g_DemodBuffer and return
772 setDemodBuff(bs
, size
, 0);
773 setClockGrid(clk
, startIdx
+ clk
* offset
/ 2);
774 if (g_debugMode
|| verbose
) {
775 PrintAndLogEx(DEBUG
, "Biphase Decoded using offset %d | clock %d | #errors %d | start index %d\ndata\n", offset
, clk
, errCnt
, (startIdx
+ clk
* offset
/ 2));
776 printDemodBuff(offset
, false, false, false);
782 // see ASKbiphaseDemod
783 static int Cmdaskbiphdemod(const char *Cmd
) {
784 CLIParserContext
*ctx
;
785 CLIParserInit(&ctx
, "data rawdemod --ab",
786 "ASK/BIPHASE demodulate the data in the GraphBuffer and output binary\n"
787 "NOTE, `--invert` for Conditional Dephase Encoding (CDP) AKA Differential Manchester\n",
788 "data rawdemod --ab --> demod a ask/biphase tag, using autodetect\n"
789 "data rawdemod --ab -c 32 --> demod a ask/biphase tag, using a clock of RF/32\n"
790 "data rawdemod --ab -i --> demod a ask/biphase tag, using autodetect, invert output\n"
791 "data rawdemod --ab -c 32 -i --> demod a ask/biphase tag, using a clock of RF/32, invert output\n"
792 "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"
796 arg_int0("c", "clk", "<dec>", "set clock manually (def autodetect)"),
797 arg_lit0("i", "inv", "invert output"),
798 arg_int0("o", "offset", "<dec>", "offset to begin biphase (def 0)"),
799 arg_int0(NULL
, "max", "<dec>", "maximum allowed errors (def 50)"),
802 CLIExecWithReturn(ctx
, Cmd
, argtable
, true);
804 uint16_t clk
= (uint16_t)arg_get_int_def(ctx
, 1, 0);
805 bool invert
= arg_get_lit(ctx
, 2);
806 int offset
= arg_get_int_def(ctx
, 3, 0);
807 uint8_t max_err
= (uint8_t)arg_get_int_def(ctx
, 4, 50) & 0xFF;
810 return ASKbiphaseDemod(offset
, clk
, invert
, max_err
, true);
814 static int Cmdaskrawdemod(const char *Cmd
) {
815 CLIParserContext
*ctx
;
816 CLIParserInit(&ctx
, "data rawdemod --ar",
817 "ASK/RAW demodulate the data in the GraphBuffer and output binary",
818 "data rawdemod --ar -a --> demod a ask tag, using autodetect, amplified\n"
819 "data rawdemod --ar -c 32 --> demod a ask tag, using a clock of RF/32\n"
820 "data rawdemod --ar -i --> demod a ask tag, using autodetect, invert output\n"
821 "data rawdemod --ar -c 32 -i --> demod a ask tag, using a clock of RF/32, invert output\n"
822 "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"
826 arg_lit0("a", "amp", "try attempt demod with ask amplification (def no amp)"),
827 arg_int0("c", "clk", "<dec>", "set clock manually (def autodetect)"),
828 arg_lit0("i", "inv", "invert output"),
829 arg_lit0("s", "st", "check for sequence terminator"),
830 arg_int0(NULL
, "max", "<dec>", "maximum allowed errors (def 100)"),
831 arg_int0(NULL
, "samples", "<dec>", "maximum samples to read (def 32768) [512 bits at RF/64]"),
834 CLIExecWithReturn(ctx
, Cmd
, argtable
, true);
836 bool amplify
= arg_get_lit(ctx
, 1);
837 uint16_t clk
= (uint16_t)arg_get_int_def(ctx
, 2, 0);
838 bool invert
= arg_get_lit(ctx
, 3);
839 bool st
= arg_get_lit(ctx
, 4);
840 uint8_t max_err
= (uint8_t)arg_get_int_def(ctx
, 5, 100) & 0xFF;
841 size_t max_len
= (size_t)arg_get_int_def(ctx
, 6, 0) & 0xFF;
844 return ASKDemod_ext(clk
, invert
, max_err
, max_len
, amplify
, true, false, 0, &st
);
847 int AutoCorrelate(const int *in
, int *out
, size_t len
, size_t window
, bool SaveGrph
, bool verbose
) {
854 double autocv
= 0.0; // Autocovariance value
855 size_t correlation
= 0;
859 double mean
= compute_mean(in
, len
);
861 double variance
= compute_variance(in
, len
);
863 int *correl_buf
= calloc(MAX_GRAPH_TRACE_LEN
, sizeof(int));
865 uint8_t peak_cnt
= 0;
866 size_t peaks
[10] = {0};
868 for (size_t i
= 0; i
< len
- window
; ++i
) {
870 for (size_t j
= 0; j
< (len
- i
); j
++) {
871 autocv
+= (in
[j
] - mean
) * (in
[j
+ i
] - mean
);
873 autocv
= (1.0 / (len
- i
)) * autocv
;
875 correl_buf
[i
] = autocv
;
877 // Computed autocorrelation value to be returned
878 // Autocorrelation is autocovariance divided by variance
879 double ac_value
= autocv
/ variance
;
881 // keep track of which distance is repeating.
882 // A value near 1.0 or more indicates a correlation in the signal
883 if (ac_value
> 0.95f
) {
884 correlation
= i
- lastmax
;
887 if ((correlation
> 1) && peak_cnt
< ARRAYLEN(peaks
)) {
888 peaks
[peak_cnt
++] = correlation
;
893 // Find shorts distance between peaks
895 for (size_t i
= 0; i
< ARRAYLEN(peaks
); ++i
) {
897 PrintAndLogEx(DEBUG
, "%zu | %zu", i
, peaks
[i
]);
898 if (peaks
[i
] < 128) {
902 if (distance
== -1) {
907 if (peaks
[i
] < distance
) {
914 PrintAndLogEx(SUCCESS
, "Possible correlation at "_YELLOW_("%4d") " samples", distance
);
917 PrintAndLogEx(HINT
, "No repeating pattern found, try increasing window size");
918 // return value -1, indication to increase window size
923 //g_GraphTraceLen = g_GraphTraceLen - window;
924 memcpy(out
, correl_buf
, len
* sizeof(int));
925 setClockGrid(distance
, 0);
926 g_DemodBufferLen
= 0;
927 RepaintGraphWindow();
933 static int CmdAutoCorr(const char *Cmd
) {
934 CLIParserContext
*ctx
;
935 CLIParserInit(&ctx
, "data autocorr",
936 "Autocorrelate over window is used to detect repeating sequences.\n"
937 "We use it as detection of how long in bits a message inside the signal is",
938 "data autocorr -w 4000\n"
939 "data autocorr -w 4000 -g"
943 arg_lit0("g", NULL
, "save back to GraphBuffer (overwrite)"),
944 arg_u64_0("w", "win", "<dec>", "window length for correlation. def 4000"),
947 CLIExecWithReturn(ctx
, Cmd
, argtable
, true);
948 bool updateGrph
= arg_get_lit(ctx
, 1);
949 uint32_t window
= arg_get_u32_def(ctx
, 2, 4000);
952 PrintAndLogEx(INFO
, "Using window size " _YELLOW_("%u"), window
);
954 if (g_GraphTraceLen
== 0) {
955 PrintAndLogEx(WARNING
, "GraphBuffer is empty");
956 PrintAndLogEx(HINT
, "Try `" _YELLOW_("lf read") "` to collect samples");
960 if (window
>= g_GraphTraceLen
) {
961 PrintAndLogEx(WARNING
, "window must be smaller than trace ( " _YELLOW_("%zu") " samples )", g_GraphTraceLen
);
965 AutoCorrelate(g_GraphBuffer
, g_GraphBuffer
, g_GraphTraceLen
, window
, updateGrph
, true);
969 static int CmdBitsamples(const char *Cmd
) {
971 CLIParserContext
*ctx
;
972 CLIParserInit(&ctx
, "data bitsamples",
973 "Get raw samples from device as bitstring",
980 CLIExecWithReturn(ctx
, Cmd
, argtable
, true);
986 if (!GetFromDevice(BIG_BUF
, got
, sizeof(got
), 0, NULL
, 0, NULL
, 2500, false)) {
987 PrintAndLogEx(WARNING
, "command execution time out");
991 for (size_t j
= 0; j
< ARRAYLEN(got
); j
++) {
992 for (uint8_t k
= 0; k
< 8; k
++) {
993 if (got
[j
] & (1 << (7 - k
)))
994 g_GraphBuffer
[cnt
++] = 1;
996 g_GraphBuffer
[cnt
++] = 0;
999 g_GraphTraceLen
= cnt
;
1000 RepaintGraphWindow();
1004 static int CmdBuffClear(const char *Cmd
) {
1005 CLIParserContext
*ctx
;
1006 CLIParserInit(&ctx
, "data clear",
1007 "This function clears the BigBuf on device side\n"
1008 "and graph window ( graphbuffer )",
1011 void *argtable
[] = {
1015 CLIExecWithReturn(ctx
, Cmd
, argtable
, true);
1017 clearCommandBuffer();
1018 SendCommandNG(CMD_BUFF_CLEAR
, NULL
, 0);
1020 // iceman: should clear all other new buffers getting introduced
1024 static int CmdDecimate(const char *Cmd
) {
1026 CLIParserContext
*ctx
;
1027 CLIParserInit(&ctx
, "data decimate",
1028 "Performs decimation, by reducing samples N times in the grapbuf. Good for PSK\n",
1030 "data decimate -n 4"
1033 void *argtable
[] = {
1035 arg_int0("n", NULL
, "<dec>", "factor to reduce sample set (default 2)"),
1038 CLIExecWithReturn(ctx
, Cmd
, argtable
, true);
1039 int n
= arg_get_int_def(ctx
, 1, 2);
1042 for (size_t i
= 0; i
< (g_GraphTraceLen
/ n
); ++i
)
1043 g_GraphBuffer
[i
] = g_GraphBuffer
[i
* n
];
1045 g_GraphTraceLen
/= n
;
1046 PrintAndLogEx(SUCCESS
, "decimated by " _GREEN_("%u"), n
);
1047 RepaintGraphWindow();
1051 * Undecimate - I'd call it 'interpolate', but we'll save that
1052 * name until someone does an actual interpolation command, not just
1053 * blindly repeating samples
1057 static int CmdUndecimate(const char *Cmd
) {
1058 CLIParserContext
*ctx
;
1059 CLIParserInit(&ctx
, "data undecimate",
1060 "Performs un-decimation, by repeating each sample N times in the graphbuf",
1062 "data undecimate -n 4\n"
1065 void *argtable
[] = {
1067 arg_int0("n", NULL
, "<dec>", "factor to repeat each sample (default 2)"),
1070 CLIExecWithReturn(ctx
, Cmd
, argtable
, true);
1071 int factor
= arg_get_int_def(ctx
, 1, 2);
1074 //We have memory, don't we?
1075 int *swap
= calloc(MAX_GRAPH_TRACE_LEN
, sizeof(int));
1077 PrintAndLogEx(FAILED
, "failed to allocate memory");
1080 uint32_t g_index
= 0, s_index
= 0;
1081 while (g_index
< g_GraphTraceLen
&& s_index
+ factor
< MAX_GRAPH_TRACE_LEN
) {
1083 for (count
= 0; count
< factor
&& s_index
+ count
< MAX_GRAPH_TRACE_LEN
; count
++) {
1084 swap
[s_index
+ count
] = (
1085 (double)(factor
- count
) / (factor
- 1)) * g_GraphBuffer
[g_index
] +
1086 ((double)count
/ factor
) * g_GraphBuffer
[g_index
+ 1]
1093 memcpy(g_GraphBuffer
, swap
, s_index
* sizeof(int));
1094 g_GraphTraceLen
= s_index
;
1095 RepaintGraphWindow();
1100 // shift graph zero up or down based on input + or -
1101 static int CmdGraphShiftZero(const char *Cmd
) {
1103 CLIParserContext
*ctx
;
1104 CLIParserInit(&ctx
, "data shiftgraphzero",
1105 "Shift 0 for Graphed wave + or - shift value",
1106 "data shiftgraphzero -n 10 --> shift 10 points\n"
1107 "data shiftgraphzero -n -22 --> shift negative 22 points"
1109 void *argtable
[] = {
1111 arg_int1("n", NULL
, "<dec>", "shift + or -"),
1114 CLIExecWithReturn(ctx
, Cmd
, argtable
, false);
1115 int shift
= arg_get_int_def(ctx
, 1, 0);
1118 for (size_t i
= 0; i
< g_GraphTraceLen
; i
++) {
1119 int shiftedVal
= g_GraphBuffer
[i
] + shift
;
1121 if (shiftedVal
> 127)
1123 else if (shiftedVal
< -127)
1125 g_GraphBuffer
[i
] = shiftedVal
;
1131 int AskEdgeDetect(const int *in
, int *out
, int len
, int threshold
) {
1133 for (int i
= 1; i
< len
; i
++) {
1134 if (in
[i
] - in
[i
- 1] >= threshold
) //large jump up
1136 else if (in
[i
] - in
[i
- 1] <= -1 * threshold
) //large jump down
1143 // use large jumps in read samples to identify edges of waves and then amplify that wave to max
1144 // similar to dirtheshold, threshold commands
1145 // takes a threshold length which is the measured length between two samples then determines an edge
1146 static int CmdAskEdgeDetect(const char *Cmd
) {
1148 CLIParserContext
*ctx
;
1149 CLIParserInit(&ctx
, "data askedgedetect",
1150 "Adjust Graph for manual ASK demod using the length of sample differences\n"
1151 "to detect the edge of a wave",
1152 "data askedgedetect -t 20"
1154 void *argtable
[] = {
1156 arg_int0("t", "thres", "<dec>", "threshold, use 20 - 45 (def 25)"),
1159 CLIExecWithReturn(ctx
, Cmd
, argtable
, true);
1160 int threshold
= arg_get_int_def(ctx
, 1, 25);
1163 PrintAndLogEx(INFO
, "using threshold " _YELLOW_("%i"), threshold
);
1164 int res
= AskEdgeDetect(g_GraphBuffer
, g_GraphBuffer
, g_GraphTraceLen
, threshold
);
1165 RepaintGraphWindow();
1169 // Print our clock rate
1170 // uses data from graphbuffer
1171 // adjusted to take char parameter for type of modulation to find the clock - by marshmellow.
1172 static int CmdDetectClockRate(const char *Cmd
) {
1173 CLIParserContext
*ctx
;
1174 CLIParserInit(&ctx
, "data detectclock",
1175 "Detect ASK, FSK, NRZ, PSK clock rate of wave in GraphBuffer",
1176 "data detectclock --ask\n"
1177 "data detectclock --nzr --> detect clock of an nrz/direct wave in GraphBuffer\n"
1179 void *argtable
[] = {
1181 arg_lit0(NULL
, "ask", "specify ASK modulation clock detection"),
1182 arg_lit0(NULL
, "fsk", "specify FSK modulation clock detection"),
1183 arg_lit0(NULL
, "nzr", "specify NZR/DIRECT modulation clock detection"),
1184 arg_lit0(NULL
, "psk", "specify PSK modulation clock detection"),
1187 CLIExecWithReturn(ctx
, Cmd
, argtable
, true);
1188 bool a
= arg_get_lit(ctx
, 1);
1189 bool f
= arg_get_lit(ctx
, 2);
1190 bool n
= arg_get_lit(ctx
, 3);
1191 bool p
= arg_get_lit(ctx
, 4);
1194 int tmp
= (a
+ f
+ n
+ p
);
1196 PrintAndLogEx(WARNING
, "Only specify one modulation");
1198 } else if (tmp
== 0) {
1200 int clock
= GetFskClock("", false);
1202 PrintAndLogEx(SUCCESS
, "FSK Clock... %d", clock
);
1204 clock
= GetAskClock("", false);
1206 PrintAndLogEx(SUCCESS
, "ASK Clock... %d", clock
);
1208 clock
= GetNrzClock("", false);
1210 PrintAndLogEx(SUCCESS
, "NRZ Clock... %d", clock
);
1212 clock
= GetPskClock("", false);
1214 PrintAndLogEx(SUCCESS
, "PSK Clock... %d", clock
);
1220 GetAskClock("", true);
1223 GetFskClock("", true);
1226 GetNrzClock("", true);
1229 GetPskClock("", true);
1231 RepaintGraphWindow();
1235 static char *GetFSKType(uint8_t fchigh
, uint8_t fclow
, uint8_t invert
) {
1236 static char fType
[8];
1237 memset(fType
, 0x00, 8);
1238 char *fskType
= fType
;
1240 if (fchigh
== 10 && fclow
== 8) {
1243 memcpy(fskType
, "FSK2a", 5);
1245 memcpy(fskType
, "FSK2", 4);
1247 } else if (fchigh
== 8 && fclow
== 5) {
1250 memcpy(fskType
, "FSK1", 4);
1252 memcpy(fskType
, "FSK1a", 5);
1255 memcpy(fskType
, "FSK??", 5);
1260 // fsk raw demod and print binary
1261 // takes 4 arguments - Clock, invert, fchigh, fclow
1262 // defaults: clock = 50, invert=1, fchigh=10, fclow=8 (RF/10 RF/8 (fsk2a))
1263 int FSKrawDemod(uint8_t rfLen
, uint8_t invert
, uint8_t fchigh
, uint8_t fclow
, bool verbose
) {
1264 //raw fsk demod no manchester decoding no start bit finding just get binary from wave
1265 if (getSignalProperties()->isnoise
) {
1267 PrintAndLogEx(INFO
, "signal looks like noise");
1272 uint8_t *bits
= calloc(MAX_GRAPH_TRACE_LEN
, sizeof(uint8_t));
1274 PrintAndLogEx(FAILED
, "failed to allocate memory");
1278 size_t bitlen
= getFromGraphBuffer(bits
);
1280 PrintAndLogEx(DEBUG
, "DEBUG: no data in graphbuf");
1285 //get field clock lengths
1286 if (!fchigh
|| !fclow
) {
1287 uint16_t fcs
= countFC(bits
, bitlen
, true);
1292 fchigh
= (fcs
>> 8) & 0x00FF;
1293 fclow
= fcs
& 0x00FF;
1296 //get bit clock length
1298 int firstClockEdge
= 0; //todo - align grid on graph with this...
1299 rfLen
= detectFSKClk(bits
, bitlen
, fchigh
, fclow
, &firstClockEdge
);
1300 if (!rfLen
) rfLen
= 50;
1304 int size
= fskdemod(bits
, bitlen
, rfLen
, invert
, fchigh
, fclow
, &start_idx
);
1306 setDemodBuff(bits
, size
, 0);
1307 setClockGrid(rfLen
, start_idx
);
1309 // Now output the bitstream to the scrollback by line of 16 bits
1310 if (verbose
|| g_debugMode
) {
1311 PrintAndLogEx(DEBUG
, "DEBUG: (FSKrawDemod) using clock:%u, %sfc high:%u, fc low:%u"
1313 , (invert
) ? "inverted, " : ""
1317 PrintAndLogEx(NORMAL
, "");
1318 PrintAndLogEx(SUCCESS
, _YELLOW_("%s") " decoded bitstream", GetFSKType(fchigh
, fclow
, invert
));
1319 PrintAndLogEx(INFO
, "-----------------------");
1320 printDemodBuff(0, false, false, false);
1324 PrintAndLogEx(DEBUG
, "no FSK data found");
1332 // fsk raw demod and print binary
1333 // takes 4 arguments - Clock, invert, fchigh, fclow
1334 // defaults: clock = 50, invert=1, fchigh=10, fclow=8 (RF/10 RF/8 (fsk2a))
1335 static int CmdFSKrawdemod(const char *Cmd
) {
1336 CLIParserContext
*ctx
;
1337 CLIParserInit(&ctx
, "data rawdemod --fs",
1338 "FSK demodulate the data in the GraphBuffer and output binary",
1339 "data rawdemod --fs --> demod an fsk tag, using autodetect\n"
1340 "data rawdemod --fs -c 32 --> demod an fsk tag, using a clock of RF/32, autodetect fc\n"
1341 "data rawdemod --fs -i --> demod an fsk tag, using autodetect, invert output\n"
1342 "data rawdemod --fs -c 32 -i --> demod an fsk tag, using a clock of RF/32, invert output, autodetect fc\n"
1343 "data rawdemod --fs -c 64 --hi 8 --lo 5 --> demod an fsk1 RF/64 tag\n"
1344 "data rawdemod --fs -c 50 --hi 10 --lo 8 --> demod an fsk2 RF/50 tag\n"
1345 "data rawdemod --fs -c 50 -i --hi 10 --lo 8 --> demod an fsk2a RF/50 tag\n"
1347 void *argtable
[] = {
1349 arg_int0("c", "clk", "<dec>", "set clock manually (def: autodetect)"),
1350 arg_lit0("i", "inv", "invert output"),
1351 arg_int0(NULL
, "hi", "<dec>", "larger field clock length (def: autodetect)"),
1352 arg_int0(NULL
, "lo", "<dec>", "small field clock length (def: autodetect)"),
1355 CLIExecWithReturn(ctx
, Cmd
, argtable
, true);
1357 uint8_t clk
= (uint8_t)arg_get_int_def(ctx
, 1, 0) & 0xFF;
1358 bool invert
= arg_get_lit(ctx
, 2);
1359 uint8_t fchigh
= (uint8_t)arg_get_int_def(ctx
, 3, 0) & 0xFF;
1360 uint8_t fclow
= (uint8_t)arg_get_int_def(ctx
, 4, 0) & 0xFF;
1363 return FSKrawDemod(clk
, invert
, fchigh
, fclow
, true);
1366 // attempt to psk1 demod graph buffer
1367 int PSKDemod(int clk
, int invert
, int maxErr
, bool verbose
) {
1368 if (getSignalProperties()->isnoise
) {
1370 PrintAndLogEx(INFO
, "signal looks like noise");
1375 uint8_t *bits
= calloc(MAX_GRAPH_TRACE_LEN
, sizeof(uint8_t));
1377 PrintAndLogEx(FAILED
, "failed to allocate memory");
1380 size_t bitlen
= getFromGraphBuffer(bits
);
1387 int errCnt
= pskRawDemod_ext(bits
, &bitlen
, &clk
, &invert
, &startIdx
);
1388 if (errCnt
> maxErr
) {
1389 if (g_debugMode
|| verbose
) PrintAndLogEx(DEBUG
, "DEBUG: (PSKdemod) Too many errors found, clk: %d, invert: %d, numbits: %zu, errCnt: %d", clk
, invert
, bitlen
, errCnt
);
1393 if (errCnt
< 0 || bitlen
< 16) { //throw away static - allow 1 and -1 (in case of threshold command first)
1394 if (g_debugMode
|| verbose
) PrintAndLogEx(DEBUG
, "DEBUG: (PSKdemod) no data found, clk: %d, invert: %d, numbits: %zu, errCnt: %d", clk
, invert
, bitlen
, errCnt
);
1398 if (verbose
|| g_debugMode
) {
1399 PrintAndLogEx(DEBUG
, "DEBUG: (PSKdemod) Using Clock:%d, invert:%d, Bits Found:%zu", clk
, invert
, bitlen
);
1401 PrintAndLogEx(DEBUG
, "DEBUG: (PSKdemod) errors during Demoding (shown as 7 in bit stream): %d", errCnt
);
1404 //prime g_DemodBuffer for output
1405 setDemodBuff(bits
, bitlen
, 0);
1406 setClockGrid(clk
, startIdx
);
1411 // takes 3 arguments - clock, invert, maxErr as integers
1412 // attempts to demodulate nrz only
1413 // prints binary found and saves in g_DemodBuffer for further commands
1414 int NRZrawDemod(int clk
, int invert
, int maxErr
, bool verbose
) {
1416 int errCnt
= 0, clkStartIdx
= 0;
1418 if (getSignalProperties()->isnoise
) {
1420 PrintAndLogEx(INFO
, "signal looks like noise");
1425 uint8_t *bits
= calloc(MAX_GRAPH_TRACE_LEN
, sizeof(uint8_t));
1427 PrintAndLogEx(FAILED
, "failed to allocate memory");
1431 size_t bitlen
= getFromGraphBuffer(bits
);
1438 errCnt
= nrzRawDemod(bits
, &bitlen
, &clk
, &invert
, &clkStartIdx
);
1439 if (errCnt
> maxErr
) {
1440 PrintAndLogEx(DEBUG
, "DEBUG: (NRZrawDemod) Too many errors found, clk: %d, invert: %d, numbits: %zu, errCnt: %d", clk
, invert
, bitlen
, errCnt
);
1444 if (errCnt
< 0 || bitlen
< 16) { //throw away static - allow 1 and -1 (in case of threshold command first)
1445 PrintAndLogEx(DEBUG
, "DEBUG: (NRZrawDemod) no data found, clk: %d, invert: %d, numbits: %zu, errCnt: %d", clk
, invert
, bitlen
, errCnt
);
1450 if (verbose
|| g_debugMode
) PrintAndLogEx(DEBUG
, "DEBUG: (NRZrawDemod) Tried NRZ Demod using Clock: %d - invert: %d - Bits Found: %zu", clk
, invert
, bitlen
);
1451 //prime g_DemodBuffer for output
1452 setDemodBuff(bits
, bitlen
, 0);
1453 setClockGrid(clk
, clkStartIdx
);
1456 if (errCnt
> 0 && (verbose
|| g_debugMode
)) PrintAndLogEx(DEBUG
, "DEBUG: (NRZrawDemod) Errors during Demoding (shown as 7 in bit stream): %d", errCnt
);
1457 if (verbose
|| g_debugMode
) {
1458 PrintAndLogEx(SUCCESS
, "NRZ demoded bitstream");
1459 PrintAndLogEx(INFO
, "---------------------");
1460 // Now output the bitstream to the scrollback by line of 16 bits
1461 printDemodBuff(0, false, invert
, false);
1468 static int CmdNRZrawDemod(const char *Cmd
) {
1469 CLIParserContext
*ctx
;
1470 CLIParserInit(&ctx
, "data rawdemod --nr",
1471 "NRZ/DIRECT demodulate the data in the GraphBuffer and output binary",
1472 "data rawdemod --nr --> demod a nrz/direct tag, using autodetect\n"
1473 "data rawdemod --nr -c 32 --> demod a nrz/direct tag, using a clock of RF/32\n"
1474 "data rawdemod --nr -i --> demod a nrz/direct tag, using autodetect, invert output\n"
1475 "data rawdemod --nr -c 32 -i --> demod a nrz/direct tag, using a clock of RF/32, invert output\n"
1476 "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"
1478 void *argtable
[] = {
1480 arg_int0("c", "clk", "<dec>", "set clock manually (def autodetect)"),
1481 arg_lit0("i", "inv", "invert output"),
1482 arg_int0(NULL
, "max", "<dec>", "maximum allowed errors (def 100)"),
1485 CLIExecWithReturn(ctx
, Cmd
, argtable
, true);
1487 uint16_t clk
= (uint16_t)arg_get_int_def(ctx
, 1, 0);
1488 bool invert
= arg_get_lit(ctx
, 2);
1489 uint8_t max_err
= (uint8_t)arg_get_int_def(ctx
, 3, 100) & 0xFF;
1492 return NRZrawDemod(clk
, invert
, max_err
, true);
1495 // takes 3 arguments - clock, invert, max_err as integers
1496 // attempts to demodulate psk only
1497 // prints binary found and saves in g_DemodBuffer for further commands
1498 int CmdPSK1rawDemod(const char *Cmd
) {
1499 CLIParserContext
*ctx
;
1500 CLIParserInit(&ctx
, "data rawdemod --p1",
1501 "PSK1 demodulate the data in the GraphBuffer and output binary",
1502 "data rawdemod --p1 --> demod a psk1 tag, using autodetect\n"
1503 "data rawdemod --p1 -c 32 --> demod a psk1 tag, using a clock of RF/32\n"
1504 "data rawdemod --p1 -i --> demod a psk1 tag, using autodetect, invert output\n"
1505 "data rawdemod --p1 -c 32 -i --> demod a psk1 tag, using a clock of RF/32, invert output\n"
1506 "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"
1508 void *argtable
[] = {
1510 arg_int0("c", "clk", "<dec>", "set clock manually (def autodetect)"),
1511 arg_lit0("i", "inv", "invert output"),
1512 arg_int0(NULL
, "max", "<dec>", "maximum allowed errors (def 100)"),
1515 CLIExecWithReturn(ctx
, Cmd
, argtable
, true);
1517 uint16_t clk
= (uint16_t)arg_get_int_def(ctx
, 1, 0);
1518 bool invert
= arg_get_lit(ctx
, 2);
1519 uint8_t max_err
= (uint8_t)arg_get_int_def(ctx
, 3, 100) & 0xFF;
1522 int ans
= PSKDemod(clk
, invert
, max_err
, true);
1524 if (ans
!= PM3_SUCCESS
) {
1525 if (g_debugMode
) PrintAndLogEx(ERR
, "Error demoding: %d", ans
);
1528 PrintAndLogEx(SUCCESS
, _YELLOW_("PSK1") " demoded bitstream");
1529 PrintAndLogEx(INFO
, "----------------------");
1530 // Now output the bitstream to the scrollback by line of 16 bits
1531 printDemodBuff(0, false, invert
, false);
1535 // takes same args as cmdpsk1rawdemod
1536 static int CmdPSK2rawDemod(const char *Cmd
) {
1537 CLIParserContext
*ctx
;
1538 CLIParserInit(&ctx
, "data rawdemod --p2",
1539 "PSK2 demodulate the data in the GraphBuffer and output binary",
1540 "data rawdemod --p2 --> demod a psk2 tag, using autodetect\n"
1541 "data rawdemod --p2 -c 32 --> demod a psk2 tag, using a clock of RF/32\n"
1542 "data rawdemod --p2 -i --> demod a psk2 tag, using autodetect, invert output\n"
1543 "data rawdemod --p2 -c 32 -i --> demod a psk2 tag, using a clock of RF/32, invert output\n"
1544 "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"
1546 void *argtable
[] = {
1548 arg_int0("c", "clk", "<dec>", "set clock manually (def autodetect)"),
1549 arg_lit0("i", "inv", "invert output"),
1550 arg_int0(NULL
, "max", "<dec>", "maximum allowed errors (def 100)"),
1553 CLIExecWithReturn(ctx
, Cmd
, argtable
, true);
1555 uint8_t clk
= (uint8_t)arg_get_int_def(ctx
, 1, 0) & 0xFF;
1556 bool invert
= arg_get_lit(ctx
, 2);
1557 uint8_t max_err
= (uint8_t)arg_get_int_def(ctx
, 3, 100) & 0xFF;
1560 int ans
= PSKDemod(clk
, invert
, max_err
, true);
1561 if (ans
!= PM3_SUCCESS
) {
1562 if (g_debugMode
) PrintAndLogEx(ERR
, "Error demoding: %d", ans
);
1565 psk1TOpsk2(g_DemodBuffer
, g_DemodBufferLen
);
1566 PrintAndLogEx(SUCCESS
, _YELLOW_("PSK2") " demoded bitstream");
1567 PrintAndLogEx(INFO
, "----------------------");
1568 // Now output the bitstream to the scrollback by line of 16 bits
1569 printDemodBuff(0, false, invert
, false);
1573 // combines all raw demod functions into one menu command
1574 static int CmdRawDemod(const char *Cmd
) {
1576 CLIParserContext
*ctx
;
1577 CLIParserInit(&ctx
, "data rawdemod",
1578 "Demodulate the data in the GraphBuffer and output binary",
1579 "data rawdemod --fs --> demod FSK - autodetect\n"
1580 "data rawdemod --ab --> demod ASK/BIPHASE - autodetect\n"
1581 "data rawdemod --am --> demod ASK/MANCHESTER - autodetect\n"
1582 "data rawdemod --ar --> demod ASK/RAW - autodetect\n"
1583 "data rawdemod --nr --> demod NRZ/DIRECT - autodetect\n"
1584 "data rawdemod --p1 --> demod PSK1 - autodetect\n"
1585 "data rawdemod --p2 --> demod PSK2 - autodetect\n"
1587 void *argtable
[] = {
1589 arg_lit0(NULL
, "ab", "ASK/Biphase demodulation"),
1590 arg_lit0(NULL
, "am", "ASK/Manchester demodulation"),
1591 arg_lit0(NULL
, "ar", "ASK/Raw demodulation"),
1592 arg_lit0(NULL
, "fs", "FSK demodulation"),
1593 arg_lit0(NULL
, "nr", "NRZ/Direct demodulation"),
1594 arg_lit0(NULL
, "p1", "PSK 1 demodulation"),
1595 arg_lit0(NULL
, "p2", "PSK 2 demodulation"),
1596 arg_strn(NULL
, NULL
, "<params>", 0, 35, "params for sub command"),
1602 size_t n
= MIN(strlen(Cmd
), sizeof(tmp
) - 1);
1603 memset(tmp
, 0, sizeof(tmp
));
1604 strncpy(tmp
, Cmd
, sizeof(tmp
) - 1);
1606 CLIExecWithReturn(ctx
, tmp
, argtable
, false);
1607 bool ab
= arg_get_lit(ctx
, 1);
1608 bool am
= arg_get_lit(ctx
, 2);
1609 bool ar
= arg_get_lit(ctx
, 3);
1610 bool fs
= arg_get_lit(ctx
, 4);
1611 bool nr
= arg_get_lit(ctx
, 5);
1612 bool p1
= arg_get_lit(ctx
, 6);
1613 bool p2
= arg_get_lit(ctx
, 7);
1616 int foo
= (ab
+ am
+ ar
+ fs
+ nr
+ p1
+ p2
);
1618 PrintAndLogEx(WARNING
, "please, select only one modulation");
1622 PrintAndLogEx(WARNING
, "please, select a modulation");
1627 const char *s
= Cmd
+ n
;
1629 ans
= CmdFSKrawdemod(s
);
1631 ans
= Cmdaskbiphdemod(s
);
1633 ans
= Cmdaskmandemod(s
);
1635 ans
= Cmdaskrawdemod(s
);
1637 ans
= CmdNRZrawDemod(s
);
1639 ans
= CmdPSK1rawDemod(s
);
1641 ans
= CmdPSK2rawDemod(s
);
1646 void setClockGrid(uint32_t clk
, int offset
) {
1647 g_DemodStartIdx
= offset
;
1649 if (clk
== 0 && offset
== 0)
1650 PrintAndLogEx(DEBUG
, "DEBUG: (setClockGrid) clear settings");
1652 PrintAndLogEx(DEBUG
, "DEBUG: (setClockGrid) demodoffset %d, clk %d", offset
, clk
);
1654 if (offset
> clk
) offset
%= clk
;
1655 if (offset
< 0) offset
+= clk
;
1657 if (offset
> g_GraphTraceLen
|| offset
< 0) return;
1658 if (clk
< 8 || clk
> g_GraphTraceLen
) {
1659 g_GridLocked
= false;
1663 RepaintGraphWindow();
1665 g_GridLocked
= true;
1666 g_GridOffset
= offset
;
1668 g_DefaultGridX
= clk
;
1669 RepaintGraphWindow();
1673 int CmdGrid(const char *Cmd
) {
1674 CLIParserContext
*ctx
;
1675 CLIParserInit(&ctx
, "data grid",
1676 "This function overlay grid on graph plot window.\n"
1677 "use zero value to turn off either",
1678 "data grid --> turn off\n"
1679 "data grid -x 64 -y 50"
1681 void *argtable
[] = {
1683 arg_dbl0("x", NULL
, "<dec>", "plot grid X coord"),
1684 arg_dbl0("y", NULL
, "<dec>", "plot grid Y coord"),
1687 CLIExecWithReturn(ctx
, Cmd
, argtable
, true);
1688 g_PlotGridX
= arg_get_dbl_def(ctx
, 1, 0);
1689 g_PlotGridY
= arg_get_dbl_def(ctx
, 2, 0);
1692 PrintAndLogEx(DEBUG
, "Setting X %.0f Y %.0f", g_PlotGridX
, g_PlotGridY
);
1693 g_DefaultGridX
= g_PlotGridX
;
1694 g_DefaultGridY
= g_PlotGridY
;
1695 RepaintGraphWindow();
1699 static int CmdSetGraphMarkers(const char *Cmd
) {
1700 CLIParserContext
*ctx
;
1701 CLIParserInit(&ctx
, "data setgraphmarkers",
1702 "Set the locations of the markers in the graph window",
1703 "data setgraphmarkers --> reset the markers\n"
1704 "data setgraphmarkers -a 64 --> set A, reset the rest\n"
1705 "data setgraphmarkers -d --keep --> set D, keep the rest"
1707 void *argtable
[] = {
1709 arg_lit0(NULL
, "keep", "keep the current values of the markers"),
1710 arg_u64_0("a", NULL
, "<dec>", "yellow marker"),
1711 arg_u64_0("b", NULL
, "<dec>", "purple marker"),
1712 arg_u64_0("c", NULL
, "<dec>", "orange marker"),
1713 arg_u64_0("d", NULL
, "<dec>", "blue marker"),
1716 CLIExecWithReturn(ctx
, Cmd
, argtable
, true);
1717 bool keep
= arg_get_lit(ctx
, 1);
1718 g_MarkerA
.pos
= arg_get_u32_def(ctx
, 2, (keep
? g_MarkerA
.pos
: 0));
1719 g_MarkerB
.pos
= arg_get_u32_def(ctx
, 3, (keep
? g_MarkerB
.pos
: 0));
1720 g_MarkerC
.pos
= arg_get_u32_def(ctx
, 4, (keep
? g_MarkerC
.pos
: 0));
1721 g_MarkerD
.pos
= arg_get_u32_def(ctx
, 5, (keep
? g_MarkerD
.pos
: 0));
1723 PrintAndLogEx(INFO
, "Setting markers " _BRIGHT_YELLOW_("A") "=%u, "_BRIGHT_MAGENTA_("B") "=%u, "_RED_("C") "=%u, "_BLUE_("D") "=%u",
1729 RepaintGraphWindow();
1733 static int CmdHexsamples(const char *Cmd
) {
1735 CLIParserContext
*ctx
;
1736 CLIParserInit(&ctx
, "data hexsamples",
1737 "Dump big buffer as hex bytes",
1738 "data hexsamples -n 128 --> dumps 128 bytes from offset 0"
1740 void *argtable
[] = {
1742 arg_u64_0("b", "breaks", "<dec>", "row break, def 16"),
1743 arg_u64_0("n", NULL
, "<dec>", "num of bytes to download"),
1744 arg_u64_0("o", "offset", "<hex>", "offset in big buffer"),
1747 CLIExecWithReturn(ctx
, Cmd
, argtable
, false);
1748 uint32_t breaks
= arg_get_u32_def(ctx
, 1, 16);
1749 uint32_t requested
= arg_get_u32_def(ctx
, 2, 8);
1750 uint32_t offset
= arg_get_u32_def(ctx
, 3, 0);
1754 if (requested
> g_pm3_capabilities
.bigbuf_size
) {
1755 requested
= g_pm3_capabilities
.bigbuf_size
;
1756 PrintAndLogEx(INFO
, "n is larger than big buffer size, will use %u", requested
);
1759 uint8_t got
[g_pm3_capabilities
.bigbuf_size
];
1760 if (offset
+ requested
> sizeof(got
)) {
1761 PrintAndLogEx(NORMAL
, "Tried to read past end of buffer, <bytes %u> + <offset %u> > %d"
1764 , g_pm3_capabilities
.bigbuf_size
1769 if (!GetFromDevice(BIG_BUF
, got
, requested
, offset
, NULL
, 0, NULL
, 2500, false)) {
1770 PrintAndLogEx(WARNING
, "command execution time out");
1774 print_hex_break(got
, requested
, breaks
);
1778 static int CmdHide(const char *Cmd
) {
1779 CLIParserContext
*ctx
;
1780 CLIParserInit(&ctx
, "data hide",
1781 "Show graph window",
1784 void *argtable
[] = {
1788 CLIExecWithReturn(ctx
, Cmd
, argtable
, true);
1794 // zero mean g_GraphBuffer
1795 int CmdHpf(const char *Cmd
) {
1796 CLIParserContext
*ctx
;
1797 CLIParserInit(&ctx
, "data hpf",
1798 "Remove DC offset from trace. It should centralize around 0",
1801 void *argtable
[] = {
1805 CLIExecWithReturn(ctx
, Cmd
, argtable
, true);
1808 uint8_t *bits
= calloc(g_GraphTraceLen
, sizeof(uint8_t));
1810 PrintAndLogEx(FAILED
, "failed to allocate memory");
1813 size_t size
= getFromGraphBuffer(bits
);
1814 removeSignalOffset(bits
, size
);
1815 // push it back to graph
1816 setGraphBuffer(bits
, size
);
1817 // set signal properties low/high/mean/amplitude and is_noise detection
1818 computeSignalProperties(bits
, size
);
1820 RepaintGraphWindow();
1825 static bool _headBit(BitstreamOut_t
*stream
) {
1826 int bytepos
= stream
->position
>> 3; // divide by 8
1827 int bitpos
= (stream
->position
++) & 7; // mask out 00000111
1828 return (*(stream
->buffer
+ bytepos
) >> (7 - bitpos
)) & 1;
1831 static uint8_t getByte(uint8_t bits_per_sample
, BitstreamOut_t
*b
) {
1833 for (int i
= 0 ; i
< bits_per_sample
; i
++)
1834 val
|= (_headBit(b
) << (7 - i
));
1839 int getSamples(uint32_t n
, bool verbose
) {
1840 return getSamplesEx(0, n
, verbose
, false);
1843 int getSamplesEx(uint32_t start
, uint32_t end
, bool verbose
, bool ignore_lf_config
) {
1846 PrintAndLogEx(WARNING
, "error, end (%u) is smaller than start (%u)", end
, start
);
1850 // If we get all but the last byte in bigbuf,
1851 // we don't have to worry about remaining trash
1852 // in the last byte in case the bits-per-sample
1853 // does not line up on byte boundaries
1854 uint8_t got
[g_pm3_capabilities
.bigbuf_size
- 1];
1855 memset(got
, 0x00, sizeof(got
));
1857 uint32_t n
= end
- start
;
1859 if (n
== 0 || n
> g_pm3_capabilities
.bigbuf_size
- 1) {
1860 n
= g_pm3_capabilities
.bigbuf_size
- 1;
1864 PrintAndLogEx(INFO
, "Reading " _YELLOW_("%u") " bytes from device memory", n
);
1867 PacketResponseNG resp
;
1868 if (GetFromDevice(BIG_BUF
, got
, n
, start
, NULL
, 0, &resp
, 10000, true) == false) {
1869 PrintAndLogEx(WARNING
, "timeout while waiting for reply.");
1870 return PM3_ETIMEOUT
;
1874 PrintAndLogEx(SUCCESS
, "Data fetched");
1877 uint8_t bits_per_sample
= 8;
1879 if (IfPm3Lf() && ignore_lf_config
== false) {
1883 PrintAndLogEx(INFO
, "Samples @ " _YELLOW_("%d") " bits/smpl, decimation 1:%d ", sc
.bits_per_sample
, sc
.decimation
);
1885 bits_per_sample
= sc
.bits_per_sample
;
1888 return getSamplesFromBufEx(got
, n
, bits_per_sample
, verbose
);;
1891 int getSamplesFromBufEx(uint8_t *data
, size_t sample_num
, uint8_t bits_per_sample
, bool verbose
) {
1893 size_t max_num
= MIN(sample_num
, MAX_GRAPH_TRACE_LEN
);
1895 if (bits_per_sample
< 8) {
1897 if (verbose
) PrintAndLogEx(INFO
, "Unpacking...");
1899 BitstreamOut_t bout
= {data
, bits_per_sample
* sample_num
, 0};
1901 for (j
= 0; j
< max_num
; j
++) {
1902 uint8_t sample
= getByte(bits_per_sample
, &bout
);
1903 g_GraphBuffer
[j
] = ((int) sample
) - 127;
1905 g_GraphTraceLen
= j
;
1907 if (verbose
) PrintAndLogEx(INFO
, "Unpacked %zu samples", j
);
1910 for (size_t j
= 0; j
< max_num
; j
++) {
1911 g_GraphBuffer
[j
] = ((int)data
[j
]) - 127;
1913 g_GraphTraceLen
= max_num
;
1916 uint8_t *bits
= calloc(g_GraphTraceLen
, sizeof(uint8_t));
1918 PrintAndLogEx(FAILED
, "failed to allocate memory");
1921 size_t size
= getFromGraphBuffer(bits
);
1922 // set signal properties low/high/mean/amplitude and is_noise detection
1923 computeSignalProperties(bits
, size
);
1927 g_DemodBufferLen
= 0;
1928 RepaintGraphWindow();
1933 static int CmdSamples(const char *Cmd
) {
1935 CLIParserContext
*ctx
;
1936 CLIParserInit(&ctx
, "data samples",
1937 "Get raw samples for graph window (GraphBuffer) from device.\n"
1938 "If 0, then get whole big buffer from device.",
1940 "data samples -n 10000"
1942 void *argtable
[] = {
1944 arg_int0("n", NULL
, "<dec>", "num of samples (512 - 40000)"),
1945 arg_lit0("v", "verbose", "verbose output"),
1948 CLIExecWithReturn(ctx
, Cmd
, argtable
, true);
1949 int n
= arg_get_int_def(ctx
, 1, 0);
1950 bool verbose
= arg_get_lit(ctx
, 2);
1952 return getSamples(n
, verbose
);
1956 static int CmdLoad(const char *Cmd
) {
1958 CLIParserContext
*ctx
;
1959 CLIParserInit(&ctx
, "data load",
1960 "This command loads the contents of a pm3 file into graph window\n",
1961 "data load -f myfilename"
1964 void *argtable
[] = {
1966 arg_str1("f", "file", "<fn>", "file to load"),
1967 arg_lit0("b", "bin", "binary file"),
1968 arg_lit0("n", "no-fix", "Load data from file without any transformations"),
1971 CLIExecWithReturn(ctx
, Cmd
, argtable
, false);
1974 char filename
[FILE_PATH_SIZE
] = {0};
1975 CLIParamStrToBuf(arg_get_str(ctx
, 1), (uint8_t *)filename
, FILE_PATH_SIZE
, &fnlen
);
1976 bool is_bin
= arg_get_lit(ctx
, 2);
1977 bool nofix
= arg_get_lit(ctx
, 3);
1981 if (searchFile(&path
, TRACES_SUBDIR
, filename
, ".pm3", true) != PM3_SUCCESS
) {
1982 if (searchFile(&path
, TRACES_SUBDIR
, filename
, "", false) != PM3_SUCCESS
) {
1989 f
= fopen(path
, "rb");
1991 f
= fopen(path
, "r");
1994 PrintAndLogEx(WARNING
, "couldn't open '%s'", path
);
2000 g_GraphTraceLen
= 0;
2004 while (fread(val
, 1, 1, f
)) {
2005 g_GraphBuffer
[g_GraphTraceLen
] = val
[0] - 127;
2008 if (g_GraphTraceLen
>= MAX_GRAPH_TRACE_LEN
)
2013 while (fgets(line
, sizeof(line
), f
)) {
2014 g_GraphBuffer
[g_GraphTraceLen
] = atoi(line
);
2017 if (g_GraphTraceLen
>= MAX_GRAPH_TRACE_LEN
)
2023 PrintAndLogEx(SUCCESS
, "loaded " _YELLOW_("%s") " samples", commaprint(g_GraphTraceLen
));
2025 if (nofix
== false) {
2026 uint8_t *bits
= calloc(g_GraphTraceLen
, sizeof(uint8_t));
2028 PrintAndLogEx(FAILED
, "failed to allocate memory");
2031 size_t size
= getFromGraphBuffer(bits
);
2033 removeSignalOffset(bits
, size
);
2034 setGraphBuffer(bits
, size
);
2035 computeSignalProperties(bits
, size
);
2040 g_DemodBufferLen
= 0;
2041 RepaintGraphWindow();
2045 // trim graph from the end
2046 int CmdLtrim(const char *Cmd
) {
2047 CLIParserContext
*ctx
;
2048 CLIParserInit(&ctx
, "data ltrim",
2049 "Trim samples from left of trace",
2050 "data ltrim -i 300 --> remove from start 0 to index 300"
2052 void *argtable
[] = {
2054 arg_u64_1("i", "idx", "<dec>", "index in graph buffer"),
2057 CLIExecWithReturn(ctx
, Cmd
, argtable
, false);
2058 uint32_t ds
= arg_get_u32(ctx
, 1);
2062 if (g_GraphTraceLen
<= ds
) {
2063 PrintAndLogEx(WARNING
, "index out of bounds");
2067 for (size_t i
= ds
; i
< g_GraphTraceLen
; ++i
) {
2068 g_GraphBuffer
[i
- ds
] = g_GraphBuffer
[i
];
2070 g_GraphTraceLen
-= ds
;
2071 g_DemodStartIdx
-= ds
;
2072 RepaintGraphWindow();
2076 // trim graph from the beginning
2077 static int CmdRtrim(const char *Cmd
) {
2079 CLIParserContext
*ctx
;
2080 CLIParserInit(&ctx
, "data rtrim",
2081 "Trim samples from right of trace",
2082 "data rtrim -i 4000 --> remove from index 4000 to end of graph buffer"
2084 void *argtable
[] = {
2086 arg_u64_1("i", "idx", "<dec>", "index in graph buffer"),
2089 CLIExecWithReturn(ctx
, Cmd
, argtable
, false);
2090 uint32_t ds
= arg_get_u32(ctx
, 1);
2094 if (g_GraphTraceLen
<= ds
) {
2095 PrintAndLogEx(WARNING
, "index out of bounds");
2099 g_GraphTraceLen
= ds
;
2100 RepaintGraphWindow();
2104 // trim graph (middle) piece
2105 static int CmdMtrim(const char *Cmd
) {
2107 CLIParserContext
*ctx
;
2108 CLIParserInit(&ctx
, "data mtrim",
2109 "Trim out samples from\n"
2110 " start 0 to `-s index`\n"
2112 " from `-e index` to end of graph buffer",
2113 "data mtrim -s 1000 -e 2000 --> keep all between index 1000 and 2000"
2115 void *argtable
[] = {
2117 arg_u64_1("s", "start", "<dec>", "start point"),
2118 arg_u64_1("e", "end", "<dec>", "end point"),
2121 CLIExecWithReturn(ctx
, Cmd
, argtable
, false);
2122 uint32_t start
= arg_get_u32(ctx
, 1);
2123 uint32_t stop
= arg_get_u32(ctx
, 2);
2126 if (start
> g_GraphTraceLen
|| stop
> g_GraphTraceLen
|| start
>= stop
) {
2127 PrintAndLogEx(WARNING
, "start and end points doesn't align");
2131 // leave start position sample
2134 g_GraphTraceLen
= stop
- start
;
2135 for (uint32_t i
= 0; i
< g_GraphTraceLen
; i
++) {
2136 g_GraphBuffer
[i
] = g_GraphBuffer
[start
+ i
];
2139 g_DemodStartIdx
= 0;
2140 RepaintGraphWindow();
2144 int CmdNorm(const char *Cmd
) {
2146 CLIParserContext
*ctx
;
2147 CLIParserInit(&ctx
, "data norm",
2148 "Normalize max/min to +/-128",
2151 void *argtable
[] = {
2155 CLIExecWithReturn(ctx
, Cmd
, argtable
, true);
2158 int max
= INT_MIN
, min
= INT_MAX
;
2160 // Find local min, max
2161 for (uint32_t i
= 10; i
< g_GraphTraceLen
; ++i
) {
2162 if (g_GraphBuffer
[i
] > max
) max
= g_GraphBuffer
[i
];
2163 if (g_GraphBuffer
[i
] < min
) min
= g_GraphBuffer
[i
];
2166 if ((g_GraphTraceLen
> 10) && (max
!= min
)) {
2167 for (uint32_t i
= 0; i
< g_GraphTraceLen
; ++i
) {
2168 g_GraphBuffer
[i
] = ((long)(g_GraphBuffer
[i
] - ((max
+ min
) / 2)) * 256) / (max
- min
);
2169 //marshmelow: adjusted *1000 to *256 to make +/- 128 so demod commands still work
2173 uint8_t *bits
= calloc(g_GraphTraceLen
, sizeof(uint8_t));
2175 PrintAndLogEx(FAILED
, "failed to allocate memory");
2178 size_t size
= getFromGraphBuffer(bits
);
2179 // set signal properties low/high/mean/amplitude and is_noise detection
2180 computeSignalProperties(bits
, size
);
2182 RepaintGraphWindow();
2187 int CmdPlot(const char *Cmd
) {
2188 CLIParserContext
*ctx
;
2189 CLIParserInit(&ctx
, "data plot",
2190 "Show graph window \n"
2191 "hit 'h' in window for detail keystroke help available",
2194 void *argtable
[] = {
2198 CLIExecWithReturn(ctx
, Cmd
, argtable
, true);
2204 int CmdSave(const char *Cmd
) {
2206 CLIParserContext
*ctx
;
2207 CLIParserInit(&ctx
, "data save",
2208 "Save signal trace from graph window , i.e. the GraphBuffer\n"
2209 "This is a text file with number -127 to 127. With the option `w` you can save it as wave file\n"
2210 "Filename should be without file extension",
2211 "data save -f myfilename -> save graph buffer to file\n"
2212 "data save --wave -f myfilename -> save graph buffer to wave file"
2215 void *argtable
[] = {
2217 arg_lit0("w", "wave", "save as wave format (.wav)"),
2218 arg_str1("f", "file", "<fn w/o ext>", "save file name"),
2221 CLIExecWithReturn(ctx
, Cmd
, argtable
, false);
2223 bool as_wave
= arg_get_lit(ctx
, 1);
2226 char filename
[FILE_PATH_SIZE
] = {0};
2227 // CLIGetStrWithReturn(ctx, 2, (uint8_t *)filename, &fnlen);
2228 CLIParamStrToBuf(arg_get_str(ctx
, 2), (uint8_t *)filename
, FILE_PATH_SIZE
, &fnlen
);
2231 if (g_GraphTraceLen
== 0) {
2232 PrintAndLogEx(WARNING
, "Graphbuffer is empty, nothing to save");
2237 return saveFileWAVE(filename
, g_GraphBuffer
, g_GraphTraceLen
);
2239 return saveFilePM3(filename
, g_GraphBuffer
, g_GraphTraceLen
);
2242 static int CmdTimeScale(const char *Cmd
) {
2244 CLIParserContext
*ctx
;
2245 CLIParserInit(&ctx
, "data timescale",
2246 "Set cursor display timescale.\n"
2247 "Setting the timescale makes the differential `dt` reading between the yellow and purple markers meaningful.\n"
2248 "once the timescale is set, the differential reading between brackets can become a time duration.",
2249 "data timescale --sr 125 -u ms -> for LF sampled at 125 kHz. Reading will be in milliseconds\n"
2250 "data timescale --sr 1.695 -u us -> for HF sampled at 16 * fc/128. Reading will be in microseconds\n"
2251 "data timescale --sr 16 -u ETU -> for HF with 16 samples per ETU (fc/128). Reading will be in ETUs"
2253 void *argtable
[] = {
2255 arg_dbl1(NULL
, "sr", "<float>", "sets timescale factor according to sampling rate"),
2256 arg_str0("u", "unit", "<string>", "time unit to display (max 10 chars)"),
2259 CLIExecWithReturn(ctx
, Cmd
, argtable
, false);
2260 g_CursorScaleFactor
= arg_get_dbl_def(ctx
, 1, 1);
2261 if (g_CursorScaleFactor
<= 0) {
2262 PrintAndLogEx(FAILED
, "bad, can't have negative or zero timescale factor");
2263 g_CursorScaleFactor
= 1;
2266 g_CursorScaleFactorUnit
[0] = '\x00';
2267 CLIParamStrToBuf(arg_get_str(ctx
, 2), (uint8_t *)g_CursorScaleFactorUnit
, sizeof(g_CursorScaleFactorUnit
), &len
);
2269 RepaintGraphWindow();
2273 int directionalThreshold(const int *in
, int *out
, size_t len
, int8_t up
, int8_t down
) {
2275 int lastValue
= in
[0];
2277 // Will be changed at the end, but init 0 as we adjust to last samples
2278 // value if no threshold kicks in.
2281 for (size_t i
= 1; i
< len
; ++i
) {
2282 // Apply first threshold to samples heading up
2283 if (in
[i
] >= up
&& in
[i
] > lastValue
) {
2284 lastValue
= out
[i
]; // Buffer last value as we overwrite it.
2287 // Apply second threshold to samples heading down
2288 else if (in
[i
] <= down
&& in
[i
] < lastValue
) {
2289 lastValue
= out
[i
]; // Buffer last value as we overwrite it.
2292 lastValue
= out
[i
]; // Buffer last value as we overwrite it.
2293 out
[i
] = out
[i
- 1];
2297 // Align with first edited sample.
2302 static int CmdDirectionalThreshold(const char *Cmd
) {
2303 CLIParserContext
*ctx
;
2304 CLIParserInit(&ctx
, "data dirthreshold",
2305 "Max rising higher up-thres/ Min falling lower down-thres, keep rest as prev.",
2306 "data dirthreshold -u 10 -d -10"
2308 void *argtable
[] = {
2310 arg_int1("d", "down", "<dec>", "threshold down"),
2311 arg_int1("u", "up", "<dec>", "threshold up"),
2314 CLIExecWithReturn(ctx
, Cmd
, argtable
, false);
2315 int8_t down
= arg_get_int(ctx
, 1);
2316 int8_t up
= arg_get_int(ctx
, 2);
2319 PrintAndLogEx(INFO
, "Applying up threshold: " _YELLOW_("%i") ", down threshold: " _YELLOW_("%i") "\n", up
, down
);
2321 directionalThreshold(g_GraphBuffer
, g_GraphBuffer
, g_GraphTraceLen
, up
, down
);
2323 // set signal properties low/high/mean/amplitude and isnoice detection
2324 uint8_t *bits
= calloc(g_GraphTraceLen
, sizeof(uint8_t));
2326 PrintAndLogEx(FAILED
, "failed to allocate memory");
2329 size_t size
= getFromGraphBuffer(bits
);
2330 // set signal properties low/high/mean/amplitude and is_noice detection
2331 computeSignalProperties(bits
, size
);
2333 RepaintGraphWindow();
2338 static int CmdZerocrossings(const char *Cmd
) {
2339 CLIParserContext
*ctx
;
2340 CLIParserInit(&ctx
, "data zerocrossings",
2341 "Count time between zero-crossings",
2342 "data zerocrossings"
2344 void *argtable
[] = {
2348 CLIExecWithReturn(ctx
, Cmd
, argtable
, true);
2351 // Zero-crossings aren't meaningful unless the signal is zero-mean.
2354 int sign
= 1, zc
= 0, lastZc
= 0;
2356 for (uint32_t i
= 0; i
< g_GraphTraceLen
; ++i
) {
2357 if (g_GraphBuffer
[i
] * sign
>= 0) {
2358 // No change in sign, reproduce the previous sample count.
2360 g_GraphBuffer
[i
] = lastZc
;
2362 // Change in sign, reset the sample count.
2364 g_GraphBuffer
[i
] = lastZc
;
2372 uint8_t *bits
= calloc(g_GraphTraceLen
, sizeof(uint8_t));
2374 PrintAndLogEx(FAILED
, "failed to allocate memory");
2377 size_t size
= getFromGraphBuffer(bits
);
2378 // set signal properties low/high/mean/amplitude and is_noise detection
2379 computeSignalProperties(bits
, size
);
2380 RepaintGraphWindow();
2385 static bool data_verify_hex(uint8_t *d
, size_t n
) {
2389 for (size_t i
= 0; i
< n
; i
++) {
2390 if (isxdigit(d
[i
]) == false) {
2391 PrintAndLogEx(ERR
, "Non hex digit found");
2398 /* // example of FSK2 RF/50 Tones
2399 static const int LowTone[] = {
2400 1, 1, 1, 1, 1, -1, -1, -1, -1, -1,
2401 1, 1, 1, 1, 1, -1, -1, -1, -1, -1,
2402 1, 1, 1, 1, 1, -1, -1, -1, -1, -1,
2403 1, 1, 1, 1, 1, -1, -1, -1, -1, -1,
2404 1, 1, 1, 1, 1, -1, -1, -1, -1, -1
2406 static const int HighTone[] = {
2407 1, 1, 1, 1, 1, -1, -1, -1, -1, // note one extra 1 to padd due to 50/8 remainder (1/2 the remainder)
2408 1, 1, 1, 1, -1, -1, -1, -1,
2409 1, 1, 1, 1, -1, -1, -1, -1,
2410 1, 1, 1, 1, -1, -1, -1, -1,
2411 1, 1, 1, 1, -1, -1, -1, -1,
2412 1, 1, 1, 1, -1, -1, -1, -1, -1, // note one extra -1 to padd due to 50/8 remainder
2415 static void GetHiLoTone(int *LowTone
, int *HighTone
, int clk
, int LowToneFC
, int HighToneFC
) {
2417 int Left_Modifier
= ((clk
% LowToneFC
) % 2) + ((clk
% LowToneFC
) / 2);
2418 int Right_Modifier
= (clk
% LowToneFC
) / 2;
2419 //int HighToneMod = clk mod HighToneFC;
2420 int LeftHalfFCCnt
= (LowToneFC
% 2) + (LowToneFC
/ 2); //truncate
2421 int FCs_per_clk
= clk
/ LowToneFC
;
2423 // need to correctly split up the clock to field clocks.
2424 // First attempt uses modifiers on each end to make up for when FCs don't evenly divide into Clk
2426 // start with LowTone
2427 // set extra 1 modifiers to make up for when FC doesn't divide evenly into Clk
2428 for (i
= 0; i
< Left_Modifier
; i
++) {
2432 // loop # of field clocks inside the main clock
2433 for (i
= 0; i
< (FCs_per_clk
); i
++) {
2434 // loop # of samples per field clock
2435 for (j
= 0; j
< LowToneFC
; j
++) {
2436 LowTone
[(i
* LowToneFC
) + Left_Modifier
+ j
] = (j
< LeftHalfFCCnt
) ? 1 : -1;
2441 // add last -1 modifiers
2442 for (k
= 0; k
< Right_Modifier
; k
++) {
2443 LowTone
[((i
- 1) * LowToneFC
) + Left_Modifier
+ j
+ k
] = -1;
2447 Left_Modifier
= ((clk
% HighToneFC
) % 2) + ((clk
% HighToneFC
) / 2);
2448 Right_Modifier
= (clk
% HighToneFC
) / 2;
2449 LeftHalfFCCnt
= (HighToneFC
% 2) + (HighToneFC
/ 2); //truncate
2450 FCs_per_clk
= clk
/ HighToneFC
;
2452 for (i
= 0; i
< Left_Modifier
; i
++) {
2456 // loop # of field clocks inside the main clock
2457 for (i
= 0; i
< (FCs_per_clk
); i
++) {
2458 // loop # of samples per field clock
2459 for (j
= 0; j
< HighToneFC
; j
++) {
2460 HighTone
[(i
* HighToneFC
) + Left_Modifier
+ j
] = (j
< LeftHalfFCCnt
) ? 1 : -1;
2464 // add last -1 modifiers
2465 for (k
= 0; k
< Right_Modifier
; k
++) {
2466 PrintAndLogEx(NORMAL
, "(i-1)*HighToneFC+lm+j+k %i", ((i
- 1) * HighToneFC
) + Left_Modifier
+ j
+ k
);
2467 HighTone
[((i
- 1) * HighToneFC
) + Left_Modifier
+ j
+ k
] = -1;
2469 if (g_debugMode
== 2) {
2470 for (i
= 0; i
< clk
; i
++) {
2471 PrintAndLogEx(NORMAL
, "Low: %i, High: %i", LowTone
[i
], HighTone
[i
]);
2476 //old CmdFSKdemod adapted by marshmellow
2477 //converts FSK to clear NRZ style wave. (or demodulates)
2478 static int FSKToNRZ(int *data
, size_t *dataLen
, uint8_t clk
, uint8_t LowToneFC
, uint8_t HighToneFC
) {
2480 if (clk
== 0 || LowToneFC
== 0 || HighToneFC
== 0) {
2481 int firstClockEdge
= 0;
2482 ans
= fskClocks((uint8_t *) &LowToneFC
, (uint8_t *) &HighToneFC
, (uint8_t *) &clk
, &firstClockEdge
);
2483 if (g_debugMode
> 1) {
2484 PrintAndLogEx(NORMAL
, "DEBUG FSKtoNRZ: detected clocks: fc_low %i, fc_high %i, clk %i, firstClockEdge %i, ans %u", LowToneFC
, HighToneFC
, clk
, firstClockEdge
, ans
);
2487 // 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...)
2488 if (ans
== 0 || clk
== 0 || LowToneFC
== 0 || HighToneFC
== 0 || LowToneFC
> 10 || HighToneFC
< 4) {
2489 if (g_debugMode
> 1) {
2490 PrintAndLogEx(NORMAL
, "DEBUG FSKtoNRZ: no fsk clocks found");
2497 GetHiLoTone(LowTone
, HighTone
, clk
, LowToneFC
, HighToneFC
);
2499 // loop through ([all samples] - clk)
2500 for (size_t i
= 0; i
< *dataLen
- clk
; ++i
) {
2501 int lowSum
= 0, highSum
= 0;
2503 // sum all samples together starting from this sample for [clk] samples for each tone (multiply tone value with sample data)
2504 for (size_t j
= 0; j
< clk
; ++j
) {
2505 lowSum
+= LowTone
[j
] * data
[i
+ j
];
2506 highSum
+= HighTone
[j
] * data
[i
+ j
];
2508 // get abs( [average sample value per clk] * 100 ) (or a rolling average of sorts)
2509 lowSum
= abs(100 * lowSum
/ clk
);
2510 highSum
= abs(100 * highSum
/ clk
);
2511 // save these back to buffer for later use
2512 data
[i
] = (highSum
<< 16) | lowSum
;
2515 // now we have the abs( [average sample value per clk] * 100 ) for each tone
2516 // loop through again [all samples] - clk - 16
2517 // note why 16??? is 16 the largest FC? changed to LowToneFC as that should be the > fc
2518 for (size_t i
= 0; i
< *dataLen
- clk
- LowToneFC
; ++i
) {
2519 int lowTot
= 0, highTot
= 0;
2521 // sum a field clock width of abs( [average sample values per clk] * 100) for each tone
2522 for (size_t j
= 0; j
< LowToneFC
; ++j
) { //10 for fsk2
2523 lowTot
+= (data
[i
+ j
] & 0xffff);
2525 for (size_t j
= 0; j
< HighToneFC
; j
++) { //8 for fsk2
2526 highTot
+= (data
[i
+ j
] >> 16);
2529 // subtract the sum of lowTone averages by the sum of highTone averages as it
2530 // and write back the new graph value
2531 data
[i
] = lowTot
- highTot
;
2533 // update dataLen to what we put back to the data sample buffer
2534 *dataLen
-= (clk
+ LowToneFC
);
2538 static int CmdFSKToNRZ(const char *Cmd
) {
2540 CLIParserContext
*ctx
;
2541 CLIParserInit(&ctx
, "data fsktonrz",
2542 "Convert fsk2 to nrz wave for alternate fsk demodulating (for weak fsk)\n"
2543 "Omitted values are autodetect instead",
2545 "data fsktonrz -c 32 --low 8 --hi 10");
2547 void *argtable
[] = {
2549 arg_int0("c", "clk", "<dec>", "clock"),
2550 arg_int0(NULL
, "low", "<dec>", "low field clock"),
2551 arg_int0(NULL
, "hi", "<dec>", "high field clock"),
2554 CLIExecWithReturn(ctx
, Cmd
, argtable
, true);
2556 int clk
= arg_get_int_def(ctx
, 1, 0);
2557 int fc_low
= arg_get_int_def(ctx
, 2, 0);
2558 int fc_high
= arg_get_int_def(ctx
, 3, 0);
2562 g_DemodBufferLen
= 0;
2563 int ans
= FSKToNRZ(g_GraphBuffer
, &g_GraphTraceLen
, clk
, fc_low
, fc_high
);
2565 RepaintGraphWindow();
2569 static int CmdDataIIR(const char *Cmd
) {
2571 CLIParserContext
*ctx
;
2572 CLIParserInit(&ctx
, "data iir",
2573 "Apply IIR buttersworth filter on plot data",
2576 void *argtable
[] = {
2578 arg_u64_1("n", NULL
, "<dec>", "factor n"),
2581 CLIExecWithReturn(ctx
, Cmd
, argtable
, false);
2582 uint8_t k
= (arg_get_u32_def(ctx
, 1, 0) & 0xFF);
2585 iceSimple_Filter(g_GraphBuffer
, g_GraphTraceLen
, k
);
2587 uint8_t *bits
= calloc(g_GraphTraceLen
, sizeof(uint8_t));
2589 PrintAndLogEx(FAILED
, "failed to allocate memory");
2592 size_t size
= getFromGraphBuffer(bits
);
2593 // set signal properties low/high/mean/amplitude and is_noise detection
2594 computeSignalProperties(bits
, size
);
2595 RepaintGraphWindow();
2601 t55xx_modulation modulation
;
2608 static int print_modulation(lf_modulation_t b
) {
2609 PrintAndLogEx(INFO
, " Modulation........ " _GREEN_("%s"), GetSelectedModulationStr(b
.modulation
));
2610 PrintAndLogEx(INFO
, " Bit clock......... " _GREEN_("RF/%d"), b
.bitrate
);
2611 PrintAndLogEx(INFO
, " Approx baudrate... " _GREEN_("%.f") " baud", (125000 / (float)b
.bitrate
));
2612 switch (b
.modulation
) {
2616 PrintAndLogEx(SUCCESS
, " Carrier rate...... %d", b
.carrier
);
2623 PrintAndLogEx(SUCCESS
, " Field Clocks...... FC/%u, FC/%u", b
.fc1
, b
.fc2
);
2632 PrintAndLogEx(NORMAL
, "");
2636 static int try_detect_modulation(void) {
2638 #define LF_NUM_OF_TESTS 6
2640 lf_modulation_t tests
[LF_NUM_OF_TESTS
];
2641 for (int i
= 0; i
< ARRAYLEN(tests
); i
++) {
2642 memset(&tests
[i
], 0, sizeof(lf_modulation_t
));
2645 int clk
= 0, firstClockEdge
= 0;
2646 uint8_t hits
= 0, fc1
= 0, fc2
= 0;
2650 uint8_t ans
= fskClocks(&fc1
, &fc2
, (uint8_t *)&clk
, &firstClockEdge
);
2652 if (ans
&& ((fc1
== 10 && fc2
== 8) || (fc1
== 8 && fc2
== 5))) {
2654 if ((FSKrawDemod(0, 0, 0, 0, false) == PM3_SUCCESS
)) {
2655 tests
[hits
].modulation
= DEMOD_FSK
;
2656 if (fc1
== 8 && fc2
== 5) {
2657 tests
[hits
].modulation
= DEMOD_FSK1a
;
2658 } else if (fc1
== 10 && fc2
== 8) {
2659 tests
[hits
].modulation
= DEMOD_FSK2
;
2662 tests
[hits
].bitrate
= clk
;
2663 tests
[hits
].fc1
= fc1
;
2664 tests
[hits
].fc2
= fc2
;
2669 clk
= GetAskClock("", false);
2675 // false = no amplify
2676 // false = no verbose
2677 // false = no emSearch
2680 if ((ASKDemod_ext(0, 0, 1, 0, false, false, false, 1, &st
) == PM3_SUCCESS
)) {
2681 tests
[hits
].modulation
= DEMOD_ASK
;
2682 tests
[hits
].bitrate
= clk
;
2685 // "0 0 1 " == clock auto, invert true, maxError 1.
2686 // false = no verbose
2687 // false = no emSearch
2692 if ((ASKbiphaseDemod(0, 0, 0, 2, false) == PM3_SUCCESS
)) {
2693 tests
[hits
].modulation
= DEMOD_BI
;
2694 tests
[hits
].bitrate
= clk
;
2698 if ((ASKbiphaseDemod(0, 0, 1, 2, false) == PM3_SUCCESS
)) {
2699 tests
[hits
].modulation
= DEMOD_BIa
;
2700 tests
[hits
].bitrate
= clk
;
2704 clk
= GetNrzClock("", false);
2705 if ((NRZrawDemod(0, 0, 1, false) == PM3_SUCCESS
)) {
2706 tests
[hits
].modulation
= DEMOD_NRZ
;
2707 tests
[hits
].bitrate
= clk
;
2711 clk
= GetPskClock("", false);
2714 buffer_savestate_t saveState
= save_bufferS32(g_GraphBuffer
, g_GraphTraceLen
);
2715 saveState
.offset
= g_GridOffset
;
2716 // skip first 160 samples to allow antenna to settle in (psk gets inverted occasionally otherwise)
2718 if ((PSKDemod(0, 0, 6, false) == PM3_SUCCESS
)) {
2719 tests
[hits
].modulation
= DEMOD_PSK1
;
2720 tests
[hits
].bitrate
= clk
;
2724 tests
[hits
].carrier
= GetPskCarrier(false);
2727 restore_bufferS32(saveState
, g_GraphBuffer
);
2728 g_GridOffset
= saveState
.offset
;
2733 PrintAndLogEx(SUCCESS
, "Found [%d] possible matches for modulation.", hits
);
2734 for (int i
= 0; i
< hits
; ++i
) {
2735 PrintAndLogEx(INFO
, "--[%d]---------------", i
+ 1);
2736 print_modulation(tests
[i
]);
2740 PrintAndLogEx(INFO
, "Signal doesn't match");
2745 static int CmdDataModulationSearch(const char *Cmd
) {
2746 CLIParserContext
*ctx
;
2747 CLIParserInit(&ctx
, "data modulation",
2748 "search LF signal after clock and modulation\n",
2752 void *argtable
[] = {
2756 CLIExecWithReturn(ctx
, Cmd
, argtable
, true);
2758 return try_detect_modulation();
2761 static int CmdAsn1Decoder(const char *Cmd
) {
2763 CLIParserContext
*ctx
;
2764 CLIParserInit(&ctx
, "data asn1",
2765 "Decode ASN1 bytearray\n"
2767 "data asn1 -d 303381050186922305a5020500a6088101010403030008a7188516eeee4facacf4fbde5e5c49d95e55bfbca74267b02407a9020500\n"
2770 void *argtable
[] = {
2772 arg_str0("d", NULL
, "<hex>", "ASN1 encoded byte array"),
2773 arg_lit0(NULL
, "test", "perform self tests"),
2776 CLIExecWithReturn(ctx
, Cmd
, argtable
, false);
2779 CLIGetHexWithReturn(ctx
, 1, data
, &dlen
);
2780 bool selftest
= arg_get_lit(ctx
, 2);
2783 return asn1_selftest();
2786 // print ASN1 decoded array in TLV view
2787 PrintAndLogEx(INFO
, "---------------- " _CYAN_("ASN1 TLV") " -----------------");
2788 asn1_print(data
, dlen
, " ");
2789 PrintAndLogEx(NORMAL
, "");
2793 static int CmdDiff(const char *Cmd
) {
2795 CLIParserContext
*ctx
;
2796 CLIParserInit(&ctx
, "data diff",
2797 "Diff takes a multitude of input data and makes a binary compare.\n"
2798 "It accepts filenames (filesystem or RDV4 flashmem SPIFFS), emulator memory, magic gen1",
2799 "data diff -w 4 -a hf-mfu-01020304.bin -b hf-mfu-04030201.bin\n"
2800 "data diff -a fileA -b fileB\n"
2801 "data diff -a fileA --eb\n"
2802 "data diff --fa fileA -b fileB\n"
2803 "data diff --fa fileA --fb fileB\n"
2806 void *argtable
[] = {
2808 arg_str0("a", NULL
, "<fn>", "input file name A"),
2809 arg_str0("b", NULL
, "<fn>", "input file name B"),
2810 arg_lit0(NULL
, "eb", "emulator memory <hf mf esave>"),
2811 arg_str0(NULL
, "fa", "<fn>", "input spiffs file A"),
2812 arg_str0(NULL
, "fb", "<fn>", "input spiffs file B"),
2813 arg_int0("w", NULL
, "<4|8|16>", "Width of data output"),
2816 CLIExecWithReturn(ctx
, Cmd
, argtable
, false);
2819 char filenameA
[FILE_PATH_SIZE
] = {0};
2820 CLIParamStrToBuf(arg_get_str(ctx
, 1), (uint8_t *)filenameA
, FILE_PATH_SIZE
, &fnlenA
);
2823 char filenameB
[FILE_PATH_SIZE
] = {0};
2824 CLIParamStrToBuf(arg_get_str(ctx
, 2), (uint8_t *)filenameB
, FILE_PATH_SIZE
, &fnlenB
);
2826 bool use_e
= arg_get_lit(ctx
, 3);
2828 // SPIFFS filename A
2830 char spnameA
[FILE_PATH_SIZE
] = {0};
2831 CLIParamStrToBuf(arg_get_str(ctx
, 4), (uint8_t *)spnameA
, FILE_PATH_SIZE
, &splenA
);
2833 // SPIFFS filename B
2835 char spnameB
[FILE_PATH_SIZE
] = {0};
2836 CLIParamStrToBuf(arg_get_str(ctx
, 5), (uint8_t *)spnameB
, FILE_PATH_SIZE
, &splenB
);
2838 int width
= arg_get_int_def(ctx
, 6, 16);
2842 if (IfPm3Rdv4Fw() == false && (splenA
> 0 || splenB
> 0)) {
2843 PrintAndLogEx(WARNING
, "No RDV4 Flashmemory available");
2848 PrintAndLogEx(WARNING
, "SPIFFS filname A length is large than 32 bytes, got %d", splenA
);
2852 PrintAndLogEx(WARNING
, "SPIFFS filname B length is large than 32 bytes, got %d", splenB
);
2857 if (width
> 16 || width
< 1) {
2858 PrintAndLogEx(INFO
, "Width out of range, using default 16 bytes width");
2862 // if user supplied dump file, time to load it
2863 int res
= PM3_SUCCESS
;
2864 uint8_t *inA
= NULL
, *inB
= NULL
;
2865 size_t datalenA
= 0, datalenB
= 0;
2872 res
= pm3_load_dump(filenameA
, (void **)&inA
, &datalenA
, MIFARE_4K_MAX_BYTES
);
2873 if (res
!= PM3_SUCCESS
) {
2881 res
= pm3_load_dump(filenameB
, (void **)&inB
, &datalenB
, MIFARE_4K_MAX_BYTES
);
2882 if (res
!= PM3_SUCCESS
) {
2887 // read spiffs file A
2889 res
= flashmem_spiffs_download(spnameA
, splenA
, (void **)&inA
, &datalenA
);
2890 if (res
!= PM3_SUCCESS
) {
2895 // read spiffs file B
2897 res
= flashmem_spiffs_download(spnameB
, splenB
, (void **)&inB
, &datalenB
);
2898 if (res
!= PM3_SUCCESS
) {
2903 // download emulator memory
2906 uint8_t *d
= calloc(MIFARE_4K_MAX_BYTES
, sizeof(uint8_t));
2908 PrintAndLogEx(WARNING
, "Fail, cannot allocate memory");
2912 PrintAndLogEx(INFO
, "downloading from emulator memory");
2913 if (GetFromDevice(BIG_BUF_EML
, d
, MIFARE_4K_MAX_BYTES
, 0, NULL
, 0, NULL
, 2500, false) == false) {
2914 PrintAndLogEx(WARNING
, "Fail, transfer from device time-out");
2918 return PM3_ETIMEOUT
;
2922 datalenB
= MIFARE_4K_MAX_BYTES
;
2925 datalenA
= MIFARE_4K_MAX_BYTES
;
2930 size_t biggest
= (datalenA
> datalenB
) ? datalenA
: datalenB
;
2931 PrintAndLogEx(DEBUG
, "data len: %zu A %zu B %zu", biggest
, datalenA
, datalenB
);
2934 PrintAndLogEx(INFO
, "inA null");
2938 PrintAndLogEx(INFO
, "inB null");
2941 char hdr0
[400] = {0};
2943 int hdr_sln
= (width
* 4) + 2;
2944 int max_fn_space
= (width
* 4);
2946 if (max_fn_space
< fnlenA
) {
2947 truncate_filename(filenameA
, max_fn_space
);
2948 fnlenA
= strlen(filenameA
);
2951 if (max_fn_space
< fnlenB
) {
2952 truncate_filename(filenameB
, max_fn_space
);
2953 fnlenB
= strlen(filenameB
);
2956 if (fnlenA
&& fnlenB
) {
2958 snprintf(hdr0
, sizeof(hdr0
) - 1, " # | " _CYAN_("%.*s"), max_fn_space
, filenameA
);
2960 // add space if needed
2961 int padding_len
= (hdr_sln
- fnlenA
- 1);
2962 if (padding_len
> 0) {
2963 memset(hdr0
+ strlen(hdr0
), ' ', padding_len
);
2965 snprintf(hdr0
+ strlen(hdr0
), sizeof(hdr0
) - 1 - strlen(hdr0
), "| " _CYAN_("%.*s"), max_fn_space
, filenameB
);
2968 strcat(hdr0
, " # | " _CYAN_("a"));
2969 memset(hdr0
+ strlen(hdr0
), ' ', hdr_sln
- 2);
2970 strcat(hdr0
+ strlen(hdr0
), "| " _CYAN_("b"));
2973 char hdr1
[200] = "----+";
2974 memset(hdr1
+ strlen(hdr1
), '-', hdr_sln
);
2975 memset(hdr1
+ strlen(hdr1
), '+', 1);
2976 memset(hdr1
+ strlen(hdr1
), '-', hdr_sln
);
2978 PrintAndLogEx(INFO
, "");
2979 PrintAndLogEx(INFO
, hdr1
);
2980 PrintAndLogEx(INFO
, hdr0
);
2981 PrintAndLogEx(INFO
, hdr1
);
2983 char line
[880] = {0};
2985 // print data diff loop
2986 for (int i
= 0 ; i
< biggest
; i
+= width
) {
2987 char dlnA
[240] = {0};
2988 char dlnB
[240] = {0};
2989 char dlnAii
[180] = {0};
2990 char dlnBii
[180] = {0};
2992 memset(dlnA
, 0, sizeof(dlnA
));
2993 memset(dlnB
, 0, sizeof(dlnB
));
2994 memset(dlnAii
, 0, sizeof(dlnAii
));
2995 memset(dlnBii
, 0, sizeof(dlnBii
));
2997 for (int j
= i
; j
< i
+ width
; j
++) {
2998 int dlnALen
= strlen(dlnA
);
2999 int dlnBLen
= strlen(dlnB
);
3000 int dlnAiiLen
= strlen(dlnAii
);
3001 int dlnBiiLen
= strlen(dlnBii
);
3004 if (j
>= datalenA
&& j
>= datalenB
) {
3005 snprintf(dlnA
+ dlnALen
, sizeof(dlnA
) - dlnALen
, "-- ");
3006 snprintf(dlnAii
+ dlnAiiLen
, sizeof(dlnAii
) - dlnAiiLen
, ".") ;
3007 snprintf(dlnB
+ dlnBLen
, sizeof(dlnB
) - dlnBLen
, "-- ");
3008 snprintf(dlnBii
+ dlnBiiLen
, sizeof(dlnBii
) - dlnBiiLen
, ".") ;
3014 if (j
>= datalenA
) {
3015 // file A ended. print B without colors
3017 snprintf(dlnA
+ dlnALen
, sizeof(dlnA
) - dlnALen
, "-- ");
3018 snprintf(dlnAii
+ dlnAiiLen
, sizeof(dlnAii
) - dlnAiiLen
, ".") ;
3019 snprintf(dlnB
+ dlnBLen
, sizeof(dlnB
) - dlnBLen
, "%02X ", inB
[j
]);
3020 snprintf(dlnBii
+ dlnBiiLen
, sizeof(dlnBii
) - dlnBiiLen
, "%c", ((cb
< 32) || (cb
== 127)) ? '.' : cb
);
3024 if (j
>= datalenB
) {
3025 // file B ended. print A without colors
3026 snprintf(dlnA
+ dlnALen
, sizeof(dlnA
) - dlnALen
, "%02X ", inA
[j
]);
3027 snprintf(dlnAii
+ dlnAiiLen
, sizeof(dlnAii
) - dlnAiiLen
, "%c", ((ca
< 32) || (ca
== 127)) ? '.' : ca
);
3028 snprintf(dlnB
+ dlnBLen
, sizeof(dlnB
) - dlnBLen
, "-- ");
3029 snprintf(dlnBii
+ dlnBiiLen
, sizeof(dlnBii
) - dlnBiiLen
, ".") ;
3033 if (inA
[j
] != inB
[j
]) {
3034 // diff / add colors
3035 snprintf(dlnA
+ dlnALen
, sizeof(dlnA
) - dlnALen
, _GREEN_("%02X "), inA
[j
]);
3036 snprintf(dlnB
+ dlnBLen
, sizeof(dlnB
) - dlnBLen
, _RED_("%02X "), inB
[j
]);
3037 snprintf(dlnAii
+ dlnAiiLen
, sizeof(dlnAii
) - dlnAiiLen
, _GREEN_("%c"), ((ca
< 32) || (ca
== 127)) ? '.' : ca
);
3038 snprintf(dlnBii
+ dlnBiiLen
, sizeof(dlnBii
) - dlnBiiLen
, _RED_("%c"), ((cb
< 32) || (cb
== 127)) ? '.' : cb
);
3041 snprintf(dlnA
+ dlnALen
, sizeof(dlnA
) - dlnALen
, "%02X ", inA
[j
]);
3042 snprintf(dlnB
+ dlnBLen
, sizeof(dlnB
) - dlnBLen
, "%02X ", inB
[j
]);
3043 snprintf(dlnAii
+ dlnAiiLen
, sizeof(dlnAii
) - dlnAiiLen
, "%c", ((ca
< 32) || (ca
== 127)) ? '.' : ca
);
3044 snprintf(dlnBii
+ dlnBiiLen
, sizeof(dlnBii
) - dlnBiiLen
, "%c", ((cb
< 32) || (cb
== 127)) ? '.' : cb
);
3047 snprintf(line
, sizeof(line
), "%s%s | %s%s", dlnA
, dlnAii
, dlnB
, dlnBii
);
3049 PrintAndLogEx(INFO
, "%03X | %s", i
, line
);
3053 PrintAndLogEx(INFO
, hdr1
);
3054 PrintAndLogEx(NORMAL
, "");
3062 * @brief Utility for number conversion via cmdline.
3066 static int CmdNumCon(const char *Cmd
) {
3067 CLIParserContext
*ctx
;
3068 CLIParserInit(&ctx
, "data num",
3069 "Function takes a decimal or hexdecimal number and print it in decimal/hex/binary\n"
3070 "Will print message if number is a prime number\n",
3071 "data num --dec 2023\n"
3072 "data num --hex 2A\n"
3075 void *argtable
[] = {
3077 arg_str0(NULL
, "dec", "<dec>", "decimal value"),
3078 arg_str0(NULL
, "hex", "<hex>", "hexadecimal value"),
3079 arg_str0(NULL
, "bin", "<bin>", "binary value"),
3080 arg_lit0("i", NULL
, "print inverted value"),
3081 arg_lit0("r", NULL
, "print reversed value"),
3084 CLIExecWithReturn(ctx
, Cmd
, argtable
, false);
3088 memset(dec
, 0, sizeof(dec
));
3089 int res
= CLIParamStrToBuf(arg_get_str(ctx
, 1), (uint8_t *)dec
, sizeof(dec
), &dlen
);
3093 memset(hex
, 0, sizeof(hex
));
3094 res
|= CLIParamStrToBuf(arg_get_str(ctx
, 2), (uint8_t *)hex
, sizeof(hex
), &hlen
);
3098 memset(bin
, 0, sizeof(bin
));
3099 res
|= CLIParamStrToBuf(arg_get_str(ctx
, 3), (uint8_t *)bin
, sizeof(bin
), &blen
);
3101 bool shall_invert
= arg_get_lit(ctx
, 4);
3102 bool shall_reverse
= arg_get_lit(ctx
, 5);
3107 PrintAndLogEx(FAILED
, "Error parsing bytes");
3111 // results for MPI actions
3115 // container of big number
3117 mbedtls_mpi_init(&N
);
3121 if (data_verify_hex((uint8_t *)hex
, hlen
) == false) {
3124 MBEDTLS_MPI_CHK(mbedtls_mpi_read_string(&N
, 16, hex
));
3125 PrintAndLogEx(INFO
, "#....... %d", hlen
);
3130 // should have decimal string check here too
3131 MBEDTLS_MPI_CHK(mbedtls_mpi_read_string(&N
, 10, dec
));
3136 // should have bianry string check here too
3137 MBEDTLS_MPI_CHK(mbedtls_mpi_read_string(&N
, 2, bin
));
3138 PrintAndLogEx(INFO
, "#bits... %d", blen
);
3142 mbedtls_mpi_init(&base
);
3143 mbedtls_mpi_add_int(&base
, &base
, 10);
3159 const char pad
[] = "00000000000000000000000000000000000000000000000000000000000000000000000000000000";
3161 for (uint8_t i
= 0; i
< ARRAYLEN(radix
); i
++) {
3162 MBEDTLS_MPI_CHK(mbedtls_mpi_write_string(&N
, radix
[i
].radix
, s
, sizeof(s
), &slen
));
3165 // only pad bin string
3168 if (blen
&& slen
< blen
) {
3169 pn
= blen
- slen
+ 1;
3170 } else if (hlen
&& (slen
< (hlen
* 4))) {
3171 pn
= (hlen
* 4) - slen
+ 1;
3174 PrintAndLogEx(SUCCESS
, "%s%.*s%s", radix
[i
].desc
, pn
, pad
, s
);
3179 MBEDTLS_MPI_CHK(mbedtls_mpi_write_string(&N
, 16, s
, sizeof(s
), &slen
));
3181 size_t n
= (slen
>> 1);
3182 uint8_t *d
= calloc(n
, sizeof(uint8_t));
3184 hexstr_to_byte_array(s
, d
, &n
);
3185 PrintAndLogEx(SUCCESS
, "ascii... " _YELLOW_("%s"), sprint_ascii((const uint8_t *)d
, n
));
3191 if (shall_reverse
) {
3192 PrintAndLogEx(SUCCESS
, _CYAN_("Reversed"));
3193 for (uint8_t i
= 0; i
< ARRAYLEN(radix
); i
++) {
3194 MBEDTLS_MPI_CHK(mbedtls_mpi_write_string(&N
, radix
[i
].radix
, s
, sizeof(s
), &slen
));
3198 // only pad bin string
3199 char scpy
[600] = {0x30};
3200 memset(scpy
, 0x30, sizeof(scpy
));
3203 if (blen
&& slen
< blen
) {
3204 pn
= blen
- slen
+ 1;
3205 } else if (hlen
&& (slen
< (hlen
* 4))) {
3206 pn
= (hlen
* 4) - slen
+ 1;
3209 memcpy(scpy
+ pn
, s
, slen
);
3210 str_reverse(scpy
, strlen(scpy
));
3211 PrintAndLogEx(SUCCESS
, "%s%s", radix
[i
].desc
, scpy
);
3216 MBEDTLS_MPI_CHK(mbedtls_mpi_write_string(&N
, 16, s
, sizeof(s
), &slen
));
3218 str_reverse(s
, strlen(s
));
3219 size_t n
= (slen
>> 1);
3220 uint8_t *d
= calloc(n
, sizeof(uint8_t));
3222 hexstr_to_byte_array(s
, d
, &n
);
3223 PrintAndLogEx(SUCCESS
, "ascii... " _YELLOW_("%s"), sprint_ascii((const uint8_t *)d
, n
));
3231 PrintAndLogEx(SUCCESS
, _CYAN_("Inverted"));
3232 for (uint8_t i
= 0; i
< ARRAYLEN(radix
); i
++) {
3233 MBEDTLS_MPI_CHK(mbedtls_mpi_write_string(&N
, radix
[i
].radix
, s
, sizeof(s
), &slen
));
3240 // MBEDTLS_MPI_CHK(mbedtls_mpi_inv_mod(&N, &N, &base));
3244 str_inverse_hex(s
, strlen(s
));
3245 PrintAndLogEx(SUCCESS
, "%s%s", radix
[i
].desc
, s
);
3250 char scpy
[600] = {0x30};
3251 memset(scpy
, 0x30, sizeof(scpy
));
3253 if (blen
&& slen
< blen
) {
3254 pn
= blen
- slen
+ 1;
3255 } else if (hlen
&& (slen
< (hlen
* 4))) {
3256 pn
= (hlen
* 4) - slen
+ 1;
3259 memcpy(scpy
+ pn
, s
, slen
);
3260 str_inverse_bin(scpy
, strlen(scpy
));
3261 PrintAndLogEx(SUCCESS
, "%s%s", radix
[i
].desc
, scpy
);
3270 MBEDTLS_MPI_CHK(mbedtls_mpi_write_string(&N
, 16, s
, sizeof(s
), &slen
));
3272 str_inverse_hex(s
, strlen(s
));
3273 size_t n
= (slen
>> 1);
3274 uint8_t *d
= calloc(n
, sizeof(uint8_t));
3276 hexstr_to_byte_array(s
, d
, &n
);
3277 PrintAndLogEx(SUCCESS
, "ascii... " _YELLOW_("%s"), sprint_ascii((const uint8_t *)d
, n
));
3284 // check if number is a prime
3285 mbedtls_entropy_context entropy
;
3286 mbedtls_ctr_drbg_context ctr_drbg
;
3287 mbedtls_ctr_drbg_init(&ctr_drbg
);
3288 mbedtls_entropy_init(&entropy
);
3290 MBEDTLS_MPI_CHK(mbedtls_ctr_drbg_seed(&ctr_drbg
, mbedtls_entropy_func
, &entropy
, NULL
, 0));
3292 res
= mbedtls_mpi_is_prime_ext(&N
, 50, mbedtls_ctr_drbg_random
, &ctr_drbg
);
3294 PrintAndLogEx(INFO
, "prime... " _YELLOW_("yes"));
3298 mbedtls_mpi_free(&N
);
3299 mbedtls_mpi_free(&base
);
3300 mbedtls_entropy_free(&entropy
);
3301 mbedtls_ctr_drbg_free(&ctr_drbg
);
3305 int centerThreshold(const int *in
, int *out
, size_t len
, int8_t up
, int8_t down
) {
3310 for (size_t i
= 0; i
< len
; ++i
) {
3311 if ((in
[i
] <= up
) && (in
[i
] >= down
)) {
3316 // clean out spikes.
3317 for (size_t i
= 2; i
< len
- 2; ++i
) {
3319 int a
= out
[i
- 2] + out
[i
- 1];
3320 int b
= out
[i
+ 2] + out
[i
+ 1];
3321 if (a
== 0 && b
== 0) {
3328 static int CmdCenterThreshold(const char *Cmd
) {
3329 CLIParserContext
*ctx
;
3330 CLIParserInit(&ctx
, "data cthreshold",
3331 "Inverse of dirty threshold command, all values between up and down will be average out",
3332 "data cthreshold -u 10 -d -10"
3334 void *argtable
[] = {
3336 arg_int1("d", "down", "<dec>", "threshold down"),
3337 arg_int1("u", "up", "<dec>", "threshold up"),
3340 CLIExecWithReturn(ctx
, Cmd
, argtable
, false);
3341 int8_t down
= arg_get_int(ctx
, 1);
3342 int8_t up
= arg_get_int(ctx
, 2);
3345 PrintAndLogEx(INFO
, "Applying up threshold: " _YELLOW_("%i") ", down threshold: " _YELLOW_("%i") "\n", up
, down
);
3347 centerThreshold(g_GraphBuffer
, g_GraphBuffer
, g_GraphTraceLen
, up
, down
);
3349 // set signal properties low/high/mean/amplitude and isnoice detection
3350 uint8_t *bits
= calloc(g_GraphTraceLen
, sizeof(uint8_t));
3352 PrintAndLogEx(FAILED
, "failed to allocate memory");
3355 size_t size
= getFromGraphBuffer(bits
);
3356 // set signal properties low/high/mean/amplitude and is_noice detection
3357 computeSignalProperties(bits
, size
);
3358 RepaintGraphWindow();
3363 static int envelope_square(const int *in
, int *out
, size_t len
) {
3370 while (i
< len
- 8) {
3372 if (in
[i
] == 0 && in
[i
+ 1] == 0 && in
[i
+ 2] == 0 && in
[i
+ 3] == 0 &&
3373 in
[i
+ 4] == 0 && in
[i
+ 5] == 0 && in
[i
+ 6] == 0 && in
[i
+ 7] == 0) {
3385 static int CmdEnvelope(const char *Cmd
) {
3386 CLIParserContext
*ctx
;
3387 CLIParserInit(&ctx
, "data envelope",
3388 "Create an square envelope of the samples",
3391 void *argtable
[] = {
3395 CLIExecWithReturn(ctx
, Cmd
, argtable
, true);
3398 envelope_square(g_GraphBuffer
, g_GraphBuffer
, g_GraphTraceLen
);
3400 uint8_t *bits
= calloc(g_GraphTraceLen
, sizeof(uint8_t));
3402 PrintAndLogEx(FAILED
, "failed to allocate memory");
3405 size_t size
= getFromGraphBuffer(bits
);
3406 // set signal properties low/high/mean/amplitude and is_noice detection
3407 computeSignalProperties(bits
, size
);
3408 RepaintGraphWindow();
3413 static int CmdAtrLookup(const char *Cmd
) {
3414 CLIParserContext
*ctx
;
3415 CLIParserInit(&ctx
, "data atr",
3416 "look up ATR record from bytearray\n"
3418 "data atr -d 3B6B00000031C064BE1B0100079000\n"
3421 void *argtable
[] = {
3423 arg_str0("d", NULL
, "<hex>", "ASN1 encoded byte array"),
3424 // arg_lit0("t", "test", "perform self test"),
3427 CLIExecWithReturn(ctx
, Cmd
, argtable
, false);
3429 int dlen
= sizeof(data
) - 1; // CLIGetStrWithReturn does not guarantee string to be null-terminated
3430 CLIGetStrWithReturn(ctx
, 1, data
, &dlen
);
3432 // bool selftest = arg_get_lit(ctx, 2);
3435 // return atr_selftest();
3437 PrintAndLogEx(INFO
, "ISO7816-3 ATR... " _YELLOW_("%s"), data
);
3438 PrintAndLogEx(INFO
, "Fingerprint...");
3440 char *copy
= str_dup(getAtrInfo((char *)data
));
3442 char *token
= strtok(copy
, "\n");
3443 while (token
!= NULL
) {
3444 PrintAndLogEx(INFO
, " %s", token
);
3445 token
= strtok(NULL
, "\n");
3451 static int CmdCryptography(const char *Cmd
) {
3452 CLIParserContext
*ctx
;
3453 CLIParserInit(&ctx
, "data crypto",
3454 "Encrypt data, right here, right now. Or decrypt.",
3455 "Supply data, key, IV (needed for des MAC or aes), and cryptography action.\n"
3456 "To calculate a MAC for FMCOS, supply challenge as IV, data as data, and session/line protection key as key.\n"
3457 "To calculate a MAC for FeliCa, supply first RC as IV, BLE+data as data and session key as key.\n"
3458 "data crypto -d 04D6850E06AABB80 -k FFFFFFFFFFFFFFFF --iv 9EA0401A00000000 --des -> Calculate a MAC for FMCOS chip. The result should be ED3A0133\n"
3460 void *argtable
[] = {
3462 arg_str1("d", "data", "<hex>", "Data to process"),
3463 arg_str1("k", "key", "<hex>", "Key to use"),
3464 arg_lit0("r", "rev", "Decrypt, not encrypt"),
3465 arg_lit0(NULL
, "des", "Cipher with DES, not AES"),
3466 arg_lit0(NULL
, "mac", "Calculate AES CMAC/FeliCa Lite MAC"),
3467 arg_str0(NULL
, "iv", "<hex>", "IV value if needed"),
3470 CLIExecWithReturn(ctx
, Cmd
, argtable
, false);
3471 uint8_t dati
[250] = {0};
3472 uint8_t dato
[250] = {0};
3474 CLIGetHexWithReturn(ctx
, 1, dati
, &datilen
);
3475 uint8_t key
[25] = {0};
3477 CLIGetHexWithReturn(ctx
, 2, key
, &keylen
);
3479 if (arg_get_lit(ctx
, 3)) type
^= 8;
3480 if (arg_get_lit(ctx
, 4)) type
^= 4;
3481 if (arg_get_lit(ctx
, 5)) type
^= 2;
3482 uint8_t iv
[250] = {0};
3484 CLIGetHexWithReturn(ctx
, 6, iv
, &ivlen
);
3487 // Do data length check
3488 if ((type
& 0x4) >> 2) { // Use AES(0) or DES(1)?
3490 if (datilen
% 8 != 0) {
3491 PrintAndLogEx(ERR
, "<data> length must be a multiple of 8. Got %d", datilen
);
3495 if (keylen
!= 8 && keylen
!= 16 && keylen
!= 24) {
3496 PrintAndLogEx(ERR
, "<key> must be 8, 16 or 24 bytes. Got %d", keylen
);
3502 if (datilen
% 16 != 0 && ((type
& 0x2) >> 1 == 0)) {
3503 PrintAndLogEx(ERR
, "<data> length must be a multiple of 16. Got %d", datilen
);
3508 PrintAndLogEx(ERR
, "<key> must be 16 bytes. Got %d", keylen
);
3513 // Encrypt(0) or decrypt(1)?
3514 if ((type
& 0x8) >> 3) {
3516 if ((type
& 0x4) >> 2) { // AES or DES?
3520 PrintAndLogEx(INFO
, "Called 3DES decrypt");
3521 des3_decrypt(dato
, dati
, key
, keylen
/ 8);
3525 PrintAndLogEx(INFO
, "Called DES decrypt");
3527 // If there's an IV, use CBC
3528 des_decrypt_ecb(dato
, dati
, datilen
, key
);
3530 des_decrypt_cbc(dato
, dati
, datilen
, key
, iv
);
3534 PrintAndLogEx(INFO
, "Called AES decrypt");
3535 aes_decode(iv
, key
, dati
, dato
, datilen
);
3539 if (type
& 0x4) { // AES or DES?
3540 if (type
& 0x02) { // If we will calculate a MAC
3541 /*PrintAndLogEx(INFO, "Called FeliCa MAC");
3542 // 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.
3543 for (int i = 0; i < datilen; i+=8){ // For all 8 byte sequences
3544 reverser(dati, &dati[i], 8, i);
3545 if (i){ // If IV is processed
3546 for (int n = 0; n < 8; ++n){
3547 dato[n] ^= dati[i+n]; // XOR with Dx
3549 des_encrypt_ecb(dato, dato, 8, key); // Cipher itself
3550 } else { // If we didn't start with IV
3551 for (int n = 0; n < 8; ++n){
3552 dato[n] = iv[n]; // Feed data into output
3553 dato[n] ^= dati[i+n]; // XOR with D1
3555 des_encrypt_ecb(dato, dato, 8, key); // Cipher itself
3558 PrintAndLogEx(SUCCESS, "MAC: %s", sprint_hex_inrow(dato, 8));*/
3559 PrintAndLogEx(INFO
, "Not implemented yet - feel free to contribute!");
3564 PrintAndLogEx(INFO
, "Called 3DES encrypt keysize: %i", keylen
/ 8);
3565 des3_encrypt(dato
, dati
, key
, keylen
/ 8);
3568 PrintAndLogEx(INFO
, "Called DES encrypt");
3571 // If there's an IV, use ECB
3572 des_encrypt_ecb(dato
, dati
, datilen
, key
);
3574 des_encrypt_cbc(dato
, dati
, datilen
, key
, iv
);
3576 memset(pad
, ' ', 4 + 8 + (datilen
- 8) * 3);
3577 pad
[8 + (datilen
- 8) * 3] = 0; // Make a padding to insert FMCOS macing algorithm guide
3578 PrintAndLogEx(INFO
, "%sVV VV VV VV FMCOS MAC", pad
);
3585 PrintAndLogEx(INFO
, "Called AES CMAC");
3586 // If we will calculate a MAC
3587 aes_cmac8(iv
, key
, dati
, dato
, datilen
);
3589 PrintAndLogEx(INFO
, "Called AES encrypt");
3590 aes_encode(iv
, key
, dati
, dato
, datilen
);
3594 PrintAndLogEx(SUCCESS
, "Result: %s", sprint_hex(dato
, datilen
));
3598 static int CmdBinaryMap(const char *Cmd
) {
3599 CLIParserContext
*ctx
;
3600 CLIParserInit(&ctx
, "data bmap",
3601 "Breaks down a hex value to binary according a template\n"
3602 " data bmap -d 16 -m 4,4\n"
3603 "This will give two rows each with four bits",
3605 "data bmap -d 3B -m 2,5,1\n"
3608 void *argtable
[] = {
3610 arg_str0("d", NULL
, "<hex>", "hex string"),
3611 arg_str0("m", NULL
, "<str>", "binary template"),
3614 CLIExecWithReturn(ctx
, Cmd
, argtable
, false);
3617 int hlen
= sizeof(hex
) - 1; // CLIGetStrWithReturn does not guarantee string to be null-terminated
3618 CLIGetStrWithReturn(ctx
, 1, hex
, &hlen
);
3620 uint8_t template[41];
3621 int tlen
= sizeof(template) - 1; // CLIGetStrWithReturn does not guarantee string to be null-terminated
3622 CLIGetStrWithReturn(ctx
, 2, template, &tlen
);
3625 char bits
[(8 * 4) + 1] = {0};
3626 hextobinstring_n(bits
, (char *)hex
, hlen
);
3633 char *token
= strtok((char *)template, ",");
3636 PrintAndLogEx(INFO
, "---+-------------------------");
3637 PrintAndLogEx(INFO
, " | b0 b1 b2 b3 b4 b5 b6 b7");
3638 PrintAndLogEx(INFO
, "---+-------------------------");
3643 while (token
!= NULL
) {
3644 sscanf(token
, "%d", &x
);
3646 PrintAndLogEx(INFO
, " %d | %.*s" NOLF
, cnt
, i
* 3, " ");
3648 // incease with previous offset
3651 for (; i
< (uint8_t)x
; i
++) {
3652 PrintAndLogEx(NORMAL
, "%c " NOLF
, bits
[7 - i
]);
3655 PrintAndLogEx(NORMAL
, "");
3656 token
= strtok(NULL
, ",");
3660 PrintAndLogEx(NORMAL
, "");
3664 static int CmdXor(const char *Cmd
) {
3665 CLIParserContext
*ctx
;
3666 CLIParserInit(&ctx
, "data xor",
3667 "takes input string and xor string. Perform xor on it.\n"
3668 "If no xor string, try the most reoccuring value to xor against",
3669 "data xor -d 99aabbcc8888888888\n"
3670 "data xor -d 99aabbcc --xor 88888888\n"
3673 void *argtable
[] = {
3675 arg_str1("d", "data", "<hex>", "input hex string"),
3676 arg_str0("x", "xor", "<str>", "input xor string"),
3679 CLIExecWithReturn(ctx
, Cmd
, argtable
, false);
3682 uint8_t hex
[128 + 1];
3683 CLIGetHexWithReturn(ctx
, 1, hex
, &hlen
);
3686 uint8_t xor[128 + 1];
3687 CLIGetHexWithReturn(ctx
, 2, xor, &xlen
);
3692 uint8_t x
= get_highest_frequency(hex
, hlen
);
3694 memset(xor, x
, xlen
);
3698 PrintAndLogEx(FAILED
, "Length mismatch, got %i != %i", hlen
, xlen
);
3702 PrintAndLogEx(SUCCESS
, "input... %s", sprint_hex_inrow(hex
, hlen
));
3703 PrintAndLogEx(SUCCESS
, "xor..... %s", sprint_hex_inrow(xor, xlen
));
3704 hex_xor(hex
, xor, hlen
);
3705 PrintAndLogEx(SUCCESS
, "plain... " _YELLOW_("%s"), sprint_hex_inrow(hex
, hlen
));
3709 static int CmdTestSaveState8(const char *Cmd
) {
3710 CLIParserContext
*ctx
;
3711 CLIParserInit(&ctx
, "data test_ss8",
3712 "Tests the implementation of Buffer Save States (8-bit buffer)",
3714 void *argtable
[] = {
3718 CLIExecWithReturn(ctx
, Cmd
, argtable
, true);
3723 size_t length
= (rand() % 256);
3724 PrintAndLogEx(DEBUG
, "Testing with length = %llu", length
);
3725 uint8_t *srcBuffer
= (uint8_t *)calloc(length
+ 1, sizeof(uint8_t));
3727 //Set up the source buffer with random data
3728 for (int i
= 0; i
< length
; i
++) {
3729 srcBuffer
[i
] = (rand() % 256);
3732 buffer_savestate_t test8
= save_buffer8(srcBuffer
, length
);
3733 PrintAndLogEx(DEBUG
, "Save State created, length = %llu, padding = %i, type = %i", test8
.bufferSize
, test8
.padding
, test8
.type
);
3735 test8
.clock
= rand();
3736 test8
.offset
= rand();
3737 PrintAndLogEx(DEBUG
, "Save State clock = %u, offset = %u", test8
.clock
, test8
.offset
);
3739 uint8_t *destBuffer
= (uint8_t *)calloc(length
, sizeof(uint8_t));
3740 size_t returnedLength
= restore_buffer8(test8
, destBuffer
);
3742 if (returnedLength
!= length
) {
3743 PrintAndLogEx(DEBUG
, _YELLOW_("Returned length != expected length!"));
3744 PrintAndLogEx(WARNING
, "Returned Length = %llu Buffer Length = %llu Expected = %llu", returnedLength
, test8
.bufferSize
, length
);
3746 PrintAndLogEx(DEBUG
, _GREEN_("Lengths match!") "\n");
3749 for (size_t i
= 0; i
< returnedLength
; i
++) {
3750 if (srcBuffer
[i
] != destBuffer
[i
]) {
3751 PrintAndLogEx(FAILED
, "Buffers don't match at index %lu!, Expected %i, got %i", i
, srcBuffer
[i
], destBuffer
[i
]);
3756 PrintAndLogEx(DEBUG
, "Index %lu matches: %u", i
, destBuffer
[i
]);
3758 PrintAndLogEx(SUCCESS
, _GREEN_("Save State (8-bit) test success!") "\n");
3765 static int CmdTestSaveState32(const char *Cmd
) {
3766 CLIParserContext
*ctx
;
3767 CLIParserInit(&ctx
, "data test_ss32",
3768 "Tests the implementation of Buffer Save States (32-bit buffer)",
3770 void *argtable
[] = {
3774 CLIExecWithReturn(ctx
, Cmd
, argtable
, true);
3780 uint32_t *srcBuffer
= (uint32_t *)calloc(length
, sizeof(uint32_t));
3782 //Set up the source buffer with random data
3783 for (size_t i
= 0; i
< length
; i
++) {
3784 srcBuffer
[i
] = (rand());
3787 buffer_savestate_t test32
= save_buffer32(srcBuffer
, length
);
3788 PrintAndLogEx(DEBUG
, "Save State created, length=%llu, type=%i", test32
.bufferSize
, test32
.type
);
3790 test32
.clock
= rand();
3791 test32
.offset
= rand();
3792 PrintAndLogEx(DEBUG
, "Save State clock=%u, offset=%u", test32
.clock
, test32
.offset
);
3794 uint32_t *destBuffer
= (uint32_t *)calloc(length
, sizeof(uint32_t));
3795 size_t returnedLength
= restore_buffer32(test32
, destBuffer
);
3797 if (returnedLength
!= length
) {
3798 PrintAndLogEx(FAILED
, "Return Length != Buffer Length! Expected '%llu', got '%llu", g_DemodBufferLen
, returnedLength
);
3803 PrintAndLogEx(DEBUG
, _GREEN_("Lengths match!") "\n");
3805 for (size_t i
= 0; i
< length
; i
++) {
3806 if (srcBuffer
[i
] != destBuffer
[i
]) {
3807 PrintAndLogEx(FAILED
, "Buffers don't match at index %lu!, Expected %i, got %i", i
, srcBuffer
[i
], destBuffer
[i
]);
3812 PrintAndLogEx(DEBUG
, "Index %lu matches: %i", i
, destBuffer
[i
]);
3814 PrintAndLogEx(SUCCESS
, _GREEN_("Save State (32-bit) test success!") "\n");
3821 static int CmdTestSaveState32S(const char *Cmd
) {
3822 CLIParserContext
*ctx
;
3823 CLIParserInit(&ctx
, "data test_ss32s",
3824 "Tests the implementation of Buffer Save States (32-bit signed buffer)",
3826 void *argtable
[] = {
3830 CLIExecWithReturn(ctx
, Cmd
, argtable
, true);
3836 int32_t *srcBuffer
= (int32_t *)calloc(length
, sizeof(int32_t));
3838 //Set up the source buffer with random data
3839 for (int i
= 0; i
< length
; i
++) {
3840 srcBuffer
[i
] = (rand() - 4294967296);
3843 buffer_savestate_t test32
= save_bufferS32(srcBuffer
, length
);
3844 PrintAndLogEx(DEBUG
, "Save State created, length=%llu, type=%i", test32
.bufferSize
, test32
.type
);
3846 test32
.clock
= rand();
3847 test32
.offset
= rand();
3848 PrintAndLogEx(DEBUG
, "Save State clock=%u, offset=%u", test32
.clock
, test32
.offset
);
3850 int32_t *destBuffer
= (int32_t *)calloc(length
, sizeof(int32_t));
3851 size_t returnedLength
= restore_bufferS32(test32
, destBuffer
);
3853 if (returnedLength
!= length
) {
3854 PrintAndLogEx(FAILED
, "Return Length != Buffer Length! Expected '%llu', got '%llu", g_DemodBufferLen
, returnedLength
);
3859 PrintAndLogEx(DEBUG
, _GREEN_("Lengths match!") "\n");
3861 for (int i
= 0; i
< length
; i
++) {
3862 if (srcBuffer
[i
] != destBuffer
[i
]) {
3863 PrintAndLogEx(FAILED
, "Buffers don't match at index %i!, Expected %i, got %i", i
, srcBuffer
[i
], destBuffer
[i
]);
3868 PrintAndLogEx(DEBUG
, "Index %i matches: %i", i
, destBuffer
[i
]);
3870 PrintAndLogEx(SUCCESS
, _GREEN_("Save State (signed 32-bit) test success!") "\n");
3877 static command_t CommandTable
[] = {
3878 {"help", CmdHelp
, AlwaysAvailable
, "This help"},
3879 {"-----------", CmdHelp
, AlwaysAvailable
, "------------------------- " _CYAN_("General") "-------------------------"},
3880 {"clear", CmdBuffClear
, AlwaysAvailable
, "Clears various buffers used by the graph window"},
3881 {"hide", CmdHide
, AlwaysAvailable
, "Hide the graph window"},
3882 {"load", CmdLoad
, AlwaysAvailable
, "Load contents of file into graph window"},
3883 {"num", CmdNumCon
, AlwaysAvailable
, "Converts dec/hex/bin"},
3884 {"plot", CmdPlot
, AlwaysAvailable
, "Show the graph window"},
3885 {"print", CmdPrintDemodBuff
, AlwaysAvailable
, "Print the data in the DemodBuffer"},
3886 {"save", CmdSave
, AlwaysAvailable
, "Save signal trace data"},
3887 {"setdebugmode", CmdSetDebugMode
, AlwaysAvailable
, "Set Debugging Level on client side"},
3888 {"xor", CmdXor
, AlwaysAvailable
, "Xor a input string"},
3890 {"-----------", CmdHelp
, AlwaysAvailable
, "------------------------- " _CYAN_("Modulation") "-------------------------"},
3891 {"biphaserawdecode", CmdBiphaseDecodeRaw
, AlwaysAvailable
, "Biphase decode bin stream in DemodBuffer"},
3892 {"detectclock", CmdDetectClockRate
, AlwaysAvailable
, "Detect ASK, FSK, NRZ, PSK clock rate of wave in GraphBuffer"},
3893 {"fsktonrz", CmdFSKToNRZ
, AlwaysAvailable
, "Convert fsk2 to nrz wave for alternate fsk demodulating (for weak fsk)"},
3894 {"manrawdecode", Cmdmandecoderaw
, AlwaysAvailable
, "Manchester decode binary stream in DemodBuffer"},
3895 {"modulation", CmdDataModulationSearch
, AlwaysAvailable
, "Identify LF signal for clock and modulation"},
3896 {"rawdemod", CmdRawDemod
, AlwaysAvailable
, "Demodulate the data in the GraphBuffer and output binary"},
3898 {"-----------", CmdHelp
, AlwaysAvailable
, "------------------------- " _CYAN_("Graph") "-------------------------"},
3899 {"askedgedetect", CmdAskEdgeDetect
, AlwaysAvailable
, "Adjust Graph for manual ASK demod"},
3900 {"autocorr", CmdAutoCorr
, AlwaysAvailable
, "Autocorrelation over window"},
3901 {"convertbitstream", CmdConvertBitStream
, AlwaysAvailable
, "Convert GraphBuffer's 0/1 values to 127 / -127"},
3902 {"cthreshold", CmdCenterThreshold
, AlwaysAvailable
, "Average out all values between"},
3903 {"dirthreshold", CmdDirectionalThreshold
, AlwaysAvailable
, "Max rising higher up-thres/ Min falling lower down-thres"},
3904 {"decimate", CmdDecimate
, AlwaysAvailable
, "Decimate samples"},
3905 {"envelope", CmdEnvelope
, AlwaysAvailable
, "Generate square envelope of samples"},
3906 {"grid", CmdGrid
, AlwaysAvailable
, "overlay grid on graph window"},
3907 {"getbitstream", CmdGetBitStream
, AlwaysAvailable
, "Convert GraphBuffer's >=1 values to 1 and <1 to 0"},
3908 {"hpf", CmdHpf
, AlwaysAvailable
, "Remove DC offset from trace"},
3909 {"iir", CmdDataIIR
, AlwaysAvailable
, "Apply IIR buttersworth filter on plot data"},
3910 {"ltrim", CmdLtrim
, AlwaysAvailable
, "Trim samples from left of trace"},
3911 {"mtrim", CmdMtrim
, AlwaysAvailable
, "Trim out samples from the specified start to the specified stop"},
3912 {"norm", CmdNorm
, AlwaysAvailable
, "Normalize max/min to +/-128"},
3913 {"rtrim", CmdRtrim
, AlwaysAvailable
, "Trim samples from right of trace"},
3914 {"setgraphmarkers", CmdSetGraphMarkers
, AlwaysAvailable
, "Set the markers in the graph window"},
3915 {"shiftgraphzero", CmdGraphShiftZero
, AlwaysAvailable
, "Shift 0 for Graphed wave + or - shift value"},
3916 {"timescale", CmdTimeScale
, AlwaysAvailable
, "Set cursor display timescale"},
3917 {"undecimate", CmdUndecimate
, AlwaysAvailable
, "Un-decimate samples"},
3918 {"zerocrossings", CmdZerocrossings
, AlwaysAvailable
, "Count time between zero-crossings"},
3920 {"-----------", CmdHelp
, AlwaysAvailable
, "------------------------- " _CYAN_("Operations") "-------------------------"},
3921 {"asn1", CmdAsn1Decoder
, AlwaysAvailable
, "ASN1 decoder"},
3922 {"atr", CmdAtrLookup
, AlwaysAvailable
, "ATR lookup"},
3923 {"bitsamples", CmdBitsamples
, IfPm3Present
, "Get raw samples as bitstring"},
3924 {"bmap", CmdBinaryMap
, AlwaysAvailable
, "Convert hex value according a binary template"},
3925 {"crypto", CmdCryptography
, AlwaysAvailable
, "Encrypt and decrypt data"},
3926 {"diff", CmdDiff
, AlwaysAvailable
, "Diff of input files"},
3927 {"hexsamples", CmdHexsamples
, IfPm3Present
, "Dump big buffer as hex bytes"},
3928 {"samples", CmdSamples
, IfPm3Present
, "Get raw samples for graph window ( GraphBuffer )"},
3930 {"-----------", CmdHelp
, IfClientDebugEnabled
, "------------------------- " _CYAN_("Debug") "-------------------------"},
3931 {"test_ss8", CmdTestSaveState8
, IfClientDebugEnabled
, "Test the implementation of Buffer Save States (8-bit buffer)"},
3932 {"test_ss32", CmdTestSaveState32
, IfClientDebugEnabled
, "Test the implementation of Buffer Save States (32-bit buffer)"},
3933 {"test_ss32s", CmdTestSaveState32S
, IfClientDebugEnabled
, "Test the implementation of Buffer Save States (32-bit signed buffer)"},
3935 {NULL
, NULL
, NULL
, NULL
}
3938 static int CmdHelp(const char *Cmd
) {
3939 (void)Cmd
; // Cmd is not used so far
3940 CmdsHelp(CommandTable
);
3944 int CmdData(const char *Cmd
) {
3945 clearCommandBuffer();
3946 return CmdsParse(CommandTable
, Cmd
);