data num now also properly pad hex converted to binary string operations 02 -> 000000...
[RRG-proxmark3.git] / client / src / cmddata.c
blob35573b88a11ce80348bce588b636809e3bb39637
1 //-----------------------------------------------------------------------------
2 // Copyright (C) Proxmark3 contributors. See AUTHORS.md for details.
3 //
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.
8 //
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 //-----------------------------------------------------------------------------
18 #include "cmddata.h"
19 #include <stdio.h>
20 #include <string.h>
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
28 #include "proxgui.h"
29 #include "graph.h" // for graph data
30 #include "comms.h"
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;
50 int g_DemodClock = 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];
62 int i = 0;
64 if (comma == '\0') {
66 struct lconv *lcp = localeconv();
67 if (lcp != NULL) {
69 if (lcp->thousands_sep != NULL && *lcp->thousands_sep != '\0') {
70 comma = *lcp->thousands_sep;
71 } else {
72 comma = ',';
77 *p = '\0';
79 do {
80 if (i % 3 == 0 && i != 0) {
81 *--p = comma;
84 *--p = '0' + n % 10;
86 n /= 10;
88 i++;
90 } while (n != 0);
92 return p;
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);
116 return true;
119 // include <math.h>
120 // Root mean square
122 static double rms(double *v, size_t n) {
123 double sum = 0.0;
124 for (size_t i = 0; i < n; i++)
125 sum += v[i] * v[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)
131 return -1;
132 else
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)
137 return -1;
138 else
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) {
154 double mean = 0.0;
155 for (size_t i = 0; i < n; i++)
156 mean += data[i];
157 mean /= n;
158 return mean;
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);
169 variance /= n;
170 return variance;
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
181 double mean;
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;
193 return ac_value;
197 static int CmdSetDebugMode(const char *Cmd) {
198 CLIParserContext *ctx;
199 CLIParserInit(&ctx, "data setdebugmode",
200 "Set debugging level on client side",
201 "data setdebugmode"
203 void *argtable[] = {
204 arg_param_begin,
205 arg_lit0("0", NULL, "no debug messages"),
206 arg_lit0("1", NULL, "debug"),
207 arg_lit0("2", NULL, "verbose debugging"),
208 arg_param_end
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);
215 CLIParserFree(ctx);
217 if (dg_0 + dg_1 + dg_2 > 1) {
218 PrintAndLogEx(INFO, "Select only one option");
219 return PM3_EINVARG;
221 if (dg_0)
222 g_debugMode = 0;
224 if (dg_1)
225 g_debugMode = 1;
227 if (dg_2)
228 g_debugMode = 2;
230 switch (g_debugMode) {
231 case 0:
232 PrintAndLogEx(INFO, "client debug level... %u ( no debug messages )", g_debugMode);
233 break;
234 case 1:
235 PrintAndLogEx(INFO, "client debug level... %u ( debug messages )", g_debugMode);
236 break;
237 case 2:
238 PrintAndLogEx(INFO, "client debug level... %u ( verbose debug messages )", g_debugMode);
239 break;
240 default:
241 break;
243 return PM3_SUCCESS;
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;
250 if (len == 0) {
251 PrintAndLogEx(WARNING, "DemodBuffer is empty");
252 return PM3_EINVARG;
255 uint8_t *buf = calloc(len, sizeof(uint8_t));
256 if (buf == NULL) {
257 PrintAndLogEx(WARNING, "fail, cannot allocate memory");
258 return PM3_EMALLOC;
260 memcpy(buf, g_DemodBuffer, len);
262 uint8_t *p = NULL;
264 if (strip_leading) {
265 p = (buf + offset);
267 if (len > (g_DemodBufferLen - offset)) {
268 len = (g_DemodBufferLen - offset);
271 size_t i;
272 for (i = 0; i < len; i++) {
273 if (p[i] == 1) {
274 break;
277 offset += i;
280 if (len > (g_DemodBufferLen - offset)) {
281 len = (g_DemodBufferLen - offset);
284 if (len > MAX_DEMODULATION_BITS) {
285 len = MAX_DEMODULATION_BITS;
288 if (invert) {
290 p = (buf + offset);
292 for (size_t i = 0; i < len; i++) {
293 if (p[i] == 1) {
294 p[i] = 0;
295 } else {
296 if (p[i] == 0) {
297 p[i] = 1;
303 if (print_hex) {
304 p = (buf + offset);
305 char hex[MAX_DEMODULATION_BITS + 1] = { 0x00 };
306 int num_bits = binarray_2_hex(hex, sizeof(hex), (char *)p, len);
308 if (num_bits == 0) {
309 p = NULL;
310 free(buf);
311 return PM3_ESOFT;
314 PrintAndLogEx(SUCCESS, "DemodBuffer:\n%s", hex);
315 } else {
316 PrintAndLogEx(SUCCESS, "DemodBuffer:\n%s", sprint_bytebits_bin_break(buf + offset, len, 32));
319 p = NULL;
320 free(buf);
321 return PM3_SUCCESS;
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",
329 "data print"
331 void *argtable[] = {
332 arg_param_begin,
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)"),
338 arg_param_end
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);
346 CLIParserFree(ctx);
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",
360 "data getbitstream"
362 void *argtable[] = {
363 arg_param_begin,
364 arg_param_end
366 CLIExecWithReturn(ctx, Cmd, argtable, true);
367 CLIParserFree(ctx);
369 CmdHpf("");
370 for (uint32_t i = 0; i < g_GraphTraceLen; i++) {
371 g_GraphBuffer[i] = (g_GraphBuffer[i] >= 1) ? 1 : 0;
373 RepaintGraphWindow();
374 return PM3_SUCCESS;
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"
384 void *argtable[] = {
385 arg_param_begin,
386 arg_param_end
388 CLIExecWithReturn(ctx, Cmd, argtable, true);
389 CLIParserFree(ctx);
391 if (isGraphBitstream()) {
392 convertGraphFromBitstream();
393 } else {
394 // get high, low
395 convertGraphFromBitstreamEx(-126, -127);
397 return PM3_SUCCESS;
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 "
407 , clk
408 , invert
409 , maxErr
410 , maxlen
411 , amplify
412 , verbose
413 , emSearch
414 , askType
416 uint8_t askamp = 0;
418 if (maxlen == 0)
419 maxlen = g_pm3_capabilities.bigbuf_size;
421 uint8_t *bits = calloc(MAX_GRAPH_TRACE_LEN, sizeof(uint8_t));
422 if (bits == NULL) {
423 PrintAndLogEx(INFO, "failed to allocate memory");
424 return PM3_EMALLOC;
427 size_t bitlen = getFromGraphBuffer(bits);
429 PrintAndLogEx(DEBUG, "DEBUG: (ASKDemod_ext) #samples from graphbuff: %zu", bitlen);
431 if (bitlen < 255) {
432 free(bits);
433 return PM3_ESOFT;
436 if (maxlen < bitlen && maxlen != 0)
437 bitlen = maxlen;
439 int foundclk = 0;
441 //amplify signal before ST check
442 if (amplify) {
443 askAmp(bits, bitlen);
446 size_t ststart = 0, stend = 0;
447 // if (*stCheck)
448 bool st = DetectST(bits, &bitlen, &foundclk, &ststart, &stend);
450 if (clk == 0) {
451 if (foundclk == 32 || foundclk == 64) {
452 clk = foundclk;
456 if (st) {
457 *stCheck = st;
458 g_MarkerC.pos = ststart;
459 g_MarkerD.pos = stend;
460 if (verbose)
461 PrintAndLogEx(DEBUG, "Found Sequence Terminator - First one is shown by orange / blue graph markers");
464 int start_idx = 0;
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
470 clk /= 2;
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"
474 , errCnt
475 , (invert) ? "inverted," : ""
476 , bitlen
477 , clk
479 free(bits);
480 return PM3_ESOFT;
483 if (errCnt > maxErr) {
484 PrintAndLogEx(DEBUG, "DEBUG: (ASKDemod_ext) Too many errors found, errors:%d, bits:%zu, clock:%i"
485 , errCnt
486 , bitlen
487 , clk
489 free(bits);
490 return PM3_ESOFT;
493 if (verbose) {
494 PrintAndLogEx(DEBUG, "DEBUG: (ASKDemod_ext) using clock:%i, %sbits found:%zu, start index %d"
495 , clk
496 , (invert) ? "inverted, " : ""
497 , bitlen
498 , start_idx
502 //output
503 setDemodBuff(bits, bitlen, 0);
504 setClockGrid(clk, start_idx);
506 if (verbose) {
507 if (errCnt > 0)
508 PrintAndLogEx(DEBUG, "# Errors during demoding (shown as 7 in bit stream)... " _RED_("%d"), errCnt);
510 if (askType) {
511 PrintAndLogEx(SUCCESS, _YELLOW_("ASK/Manchester") " - clock " _YELLOW_("%i") " - decoded bitstream", clk);
512 PrintAndLogEx(INFO, "-----------------------------------------------");
513 } else {
514 PrintAndLogEx(SUCCESS, _YELLOW_("ASK/Raw") " - clock " _YELLOW_("%i") " - decoded bitstream", clk);
515 PrintAndLogEx(INFO, "----------------------------------------");
518 printDemodBuff(0, false, false, false);
520 uint64_t lo = 0;
521 uint32_t hi = 0;
522 if (emSearch)
523 AskEm410xDecode(true, &hi, &lo);
525 free(bits);
526 return PM3_SUCCESS;
529 int ASKDemod(int clk, int invert, int maxErr, size_t maxlen, bool amplify, bool verbose, bool emSearch, uint8_t askType) {
530 bool st = false;
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"
547 void *argtable[] = {
548 arg_param_begin,
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]"),
555 arg_param_end
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;
565 CLIParserFree(ctx);
567 return ASKDemod_ext(clk, invert, max_err, max_len, amplify, true, true, 1, &st);
570 // manchester decode
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)",
578 "data manrawdecode"
580 void *argtable[] = {
581 arg_param_begin,
582 arg_lit0("i", "inv", "invert output"),
583 arg_int0(NULL, "err", "<dec>", "set max errors tolerated (def 20)"),
584 arg_param_end
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);
589 CLIParserFree(ctx);
591 if (g_DemodBufferLen == 0) {
592 PrintAndLogEx(WARNING, "DemodBuffer empty, run " _YELLOW_("`data rawdemod --ar`"));
593 return PM3_ESOFT;
596 uint8_t *bits = calloc(MAX_DEMOD_BUF_LEN, sizeof(uint8_t));
597 if (bits == NULL) {
598 PrintAndLogEx(FAILED, "failed to allocate memory");
599 return PM3_EMALLOC;
602 // make sure its just binary data 0|1|7 in buffer
603 int high = 0, low = 0;
604 size_t i = 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");
615 free(bits);
616 return PM3_ESOFT;
619 size_t size = i;
620 uint8_t offset = 0;
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);
624 free(bits);
625 return PM3_ESOFT;
628 if (err_cnt > 0) {
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));
635 // try decode EM410x
636 if (err_cnt == 0) {
637 uint64_t id = 0;
638 uint32_t hi = 0;
639 size_t idx = 0;
640 size_t tmpsize = 0;
641 int res = Em410xDecode(bits, &tmpsize, &idx, &hi, &id);
642 if (res > 0) {
643 //need to adjust to set bitstream back to manchester encoded data
644 //setDemodBuff(bits, size, idx);
645 printEM410x(hi, id, false, res);
647 size = tmpsize;
650 setDemodBuff(bits, size, 0);
651 setClockGrid(g_DemodClock * 2, g_DemodStartIdx);
652 free(bits);
653 return PM3_SUCCESS;
657 * @author marshmellow
658 * biphase decode
659 * decodes 01 or 10 -> ZERO
660 * 11 or 00 -> ONE
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"
675 void *argtable[] = {
676 arg_param_begin,
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)"),
680 arg_param_end
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);
686 CLIParserFree(ctx);
688 if (g_DemodBufferLen == 0) {
689 PrintAndLogEx(WARNING, "DemodBuffer empty, run " _YELLOW_("`data rawdemod --ar`"));
690 return PM3_ESOFT;
693 uint8_t *bits = calloc(MAX_DEMOD_BUF_LEN, sizeof(uint8_t));
694 if (bits == NULL) {
695 PrintAndLogEx(FAILED, "failed to allocate memory");
696 return PM3_EMALLOC;
699 size_t size = MAX_DEMOD_BUF_LEN;
700 if (!getDemodBuff(bits, &size)) {
701 free(bits);
702 return PM3_ESOFT;
705 int err_cnt = BiphaseRawDecode(bits, &size, &offset, invert);
706 if (err_cnt < 0) {
707 PrintAndLogEx(ERR, "Error during decode " _RED_("%i"), err_cnt);
708 free(bits);
709 return PM3_ESOFT;
711 if (err_cnt > max_err) {
712 PrintAndLogEx(ERR, "Too many errors attempting to decode " _RED_("%i"), err_cnt);
713 free(bits);
714 return PM3_ESOFT;
717 if (err_cnt > 0) {
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);
726 free(bits);
727 return PM3_SUCCESS;
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));
735 if (bs == NULL) {
736 PrintAndLogEx(FAILED, "failed to allocate memory");
737 return PM3_EMALLOC;
740 size_t size = getFromGraphBufferEx(bs, MAX_DEMOD_BUF_LEN);
741 if (size == 0) {
742 PrintAndLogEx(DEBUG, "DEBUG: no data in graphbuf");
743 free(bs);
744 return PM3_ESOFT;
746 int startIdx = 0;
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);
751 free(bs);
752 return PM3_ESOFT;
755 //attempt to Biphase decode BitStream
756 errCnt = BiphaseRawDecode(bs, &size, &offset, invert);
757 if (errCnt < 0) {
758 if (g_debugMode || verbose) PrintAndLogEx(DEBUG, "DEBUG: Error BiphaseRawDecode: %d", errCnt);
759 free(bs);
760 return PM3_ESOFT;
762 if (errCnt > maxErr) {
763 if (g_debugMode || verbose) PrintAndLogEx(DEBUG, "DEBUG: Error BiphaseRawDecode too many errors: %d", errCnt);
764 free(bs);
765 return PM3_ESOFT;
768 if (offset >= 1) {
769 offset -= 1;
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);
778 free(bs);
779 return PM3_SUCCESS;
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"
794 void *argtable[] = {
795 arg_param_begin,
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)"),
800 arg_param_end
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;
808 CLIParserFree(ctx);
810 return ASKbiphaseDemod(offset, clk, invert, max_err, true);
813 // see ASKDemod
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"
824 void *argtable[] = {
825 arg_param_begin,
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]"),
832 arg_param_end
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;
842 CLIParserFree(ctx);
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) {
848 // sanity check
849 if (window > len) {
850 window = len;
853 //test
854 double autocv = 0.0; // Autocovariance value
855 size_t correlation = 0;
856 int lastmax = 0;
858 // in, len, 4000
859 double mean = compute_mean(in, len);
860 // Computed variance
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;
885 lastmax = i;
887 if ((correlation > 1) && peak_cnt < ARRAYLEN(peaks)) {
888 peaks[peak_cnt++] = correlation;
893 // Find shorts distance between peaks
894 int distance = -1;
895 for (size_t i = 0; i < ARRAYLEN(peaks); ++i) {
897 PrintAndLogEx(DEBUG, "%zu | %zu", i, peaks[i]);
898 if (peaks[i] < 128) {
899 continue;
902 if (distance == -1) {
903 distance = peaks[i];
904 continue;
907 if (peaks[i] < distance) {
908 distance = peaks[i];
912 if (distance > -1) {
913 if (verbose) {
914 PrintAndLogEx(SUCCESS, "Possible correlation at "_YELLOW_("%4d") " samples", distance);
916 } else {
917 PrintAndLogEx(HINT, "No repeating pattern found, try increasing window size");
918 // return value -1, indication to increase window size
919 return -1;
922 if (SaveGrph) {
923 //g_GraphTraceLen = g_GraphTraceLen - window;
924 memcpy(out, correl_buf, len * sizeof(int));
925 setClockGrid(distance, 0);
926 g_DemodBufferLen = 0;
927 RepaintGraphWindow();
929 free(correl_buf);
930 return distance;
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"
941 void *argtable[] = {
942 arg_param_begin,
943 arg_lit0("g", NULL, "save back to GraphBuffer (overwrite)"),
944 arg_u64_0("w", "win", "<dec>", "window length for correlation. def 4000"),
945 arg_param_end
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);
950 CLIParserFree(ctx);
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");
957 return PM3_ESOFT;
960 if (window >= g_GraphTraceLen) {
961 PrintAndLogEx(WARNING, "window must be smaller than trace ( " _YELLOW_("%zu") " samples )", g_GraphTraceLen);
962 return PM3_EINVARG;
965 AutoCorrelate(g_GraphBuffer, g_GraphBuffer, g_GraphTraceLen, window, updateGrph, true);
966 return PM3_SUCCESS;
969 static int CmdBitsamples(const char *Cmd) {
971 CLIParserContext *ctx;
972 CLIParserInit(&ctx, "data bitsamples",
973 "Get raw samples from device as bitstring",
974 "data bitsamples"
976 void *argtable[] = {
977 arg_param_begin,
978 arg_param_end
980 CLIExecWithReturn(ctx, Cmd, argtable, true);
981 CLIParserFree(ctx);
983 int cnt = 0;
984 uint8_t got[12288];
986 if (!GetFromDevice(BIG_BUF, got, sizeof(got), 0, NULL, 0, NULL, 2500, false)) {
987 PrintAndLogEx(WARNING, "command execution time out");
988 return PM3_ETIMEOUT;
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;
995 else
996 g_GraphBuffer[cnt++] = 0;
999 g_GraphTraceLen = cnt;
1000 RepaintGraphWindow();
1001 return PM3_SUCCESS;
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 )",
1009 "data clear"
1011 void *argtable[] = {
1012 arg_param_begin,
1013 arg_param_end
1015 CLIExecWithReturn(ctx, Cmd, argtable, true);
1016 CLIParserFree(ctx);
1017 clearCommandBuffer();
1018 SendCommandNG(CMD_BUFF_CLEAR, NULL, 0);
1019 ClearGraph(true);
1020 // iceman: should clear all other new buffers getting introduced
1021 return PM3_SUCCESS;
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",
1029 "data decimate\n"
1030 "data decimate -n 4"
1033 void *argtable[] = {
1034 arg_param_begin,
1035 arg_int0("n", NULL, "<dec>", "factor to reduce sample set (default 2)"),
1036 arg_param_end
1038 CLIExecWithReturn(ctx, Cmd, argtable, true);
1039 int n = arg_get_int_def(ctx, 1, 2);
1040 CLIParserFree(ctx);
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();
1048 return PM3_SUCCESS;
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
1054 * @param Cmd
1055 * @return
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",
1061 "data undecimate\n"
1062 "data undecimate -n 4\n"
1065 void *argtable[] = {
1066 arg_param_begin,
1067 arg_int0("n", NULL, "<dec>", "factor to repeat each sample (default 2)"),
1068 arg_param_end
1070 CLIExecWithReturn(ctx, Cmd, argtable, true);
1071 int factor = arg_get_int_def(ctx, 1, 2);
1072 CLIParserFree(ctx);
1074 //We have memory, don't we?
1075 int *swap = calloc(MAX_GRAPH_TRACE_LEN, sizeof(int));
1076 if (swap == NULL) {
1077 PrintAndLogEx(FAILED, "failed to allocate memory");
1078 return PM3_EMALLOC;
1080 uint32_t g_index = 0, s_index = 0;
1081 while (g_index < g_GraphTraceLen && s_index + factor < MAX_GRAPH_TRACE_LEN) {
1082 int count = 0;
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]
1089 s_index += count;
1090 g_index++;
1093 memcpy(g_GraphBuffer, swap, s_index * sizeof(int));
1094 g_GraphTraceLen = s_index;
1095 RepaintGraphWindow();
1096 free(swap);
1097 return PM3_SUCCESS;
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[] = {
1110 arg_param_begin,
1111 arg_int1("n", NULL, "<dec>", "shift + or -"),
1112 arg_param_end
1114 CLIExecWithReturn(ctx, Cmd, argtable, false);
1115 int shift = arg_get_int_def(ctx, 1, 0);
1116 CLIParserFree(ctx);
1118 for (size_t i = 0; i < g_GraphTraceLen; i++) {
1119 int shiftedVal = g_GraphBuffer[i] + shift;
1121 if (shiftedVal > 127)
1122 shiftedVal = 127;
1123 else if (shiftedVal < -127)
1124 shiftedVal = -127;
1125 g_GraphBuffer[i] = shiftedVal;
1127 CmdNorm("");
1128 return PM3_SUCCESS;
1131 int AskEdgeDetect(const int *in, int *out, int len, int threshold) {
1132 int last = 0;
1133 for (int i = 1; i < len; i++) {
1134 if (in[i] - in[i - 1] >= threshold) //large jump up
1135 last = 127;
1136 else if (in[i] - in[i - 1] <= -1 * threshold) //large jump down
1137 last = -127;
1138 out[i - 1] = last;
1140 return PM3_SUCCESS;
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[] = {
1155 arg_param_begin,
1156 arg_int0("t", "thres", "<dec>", "threshold, use 20 - 45 (def 25)"),
1157 arg_param_end
1159 CLIExecWithReturn(ctx, Cmd, argtable, true);
1160 int threshold = arg_get_int_def(ctx, 1, 25);
1161 CLIParserFree(ctx);
1163 PrintAndLogEx(INFO, "using threshold " _YELLOW_("%i"), threshold);
1164 int res = AskEdgeDetect(g_GraphBuffer, g_GraphBuffer, g_GraphTraceLen, threshold);
1165 RepaintGraphWindow();
1166 return res;
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[] = {
1180 arg_param_begin,
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"),
1185 arg_param_end
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);
1192 CLIParserFree(ctx);
1194 int tmp = (a + f + n + p);
1195 if (tmp > 1) {
1196 PrintAndLogEx(WARNING, "Only specify one modulation");
1197 return PM3_EINVARG;
1198 } else if (tmp == 0) {
1200 int clock = GetFskClock("", false);
1201 if (clock > 0) {
1202 PrintAndLogEx(SUCCESS, "FSK Clock... %d", clock);
1204 clock = GetAskClock("", false);
1205 if (clock > 0) {
1206 PrintAndLogEx(SUCCESS, "ASK Clock... %d", clock);
1208 clock = GetNrzClock("", false);
1209 if (clock > 0) {
1210 PrintAndLogEx(SUCCESS, "NRZ Clock... %d", clock);
1212 clock = GetPskClock("", false);
1213 if (clock > 0) {
1214 PrintAndLogEx(SUCCESS, "PSK Clock... %d", clock);
1216 return PM3_SUCCESS;
1219 if (a)
1220 GetAskClock("", true);
1222 if (f)
1223 GetFskClock("", true);
1225 if (n)
1226 GetNrzClock("", true);
1228 if (p)
1229 GetPskClock("", true);
1231 RepaintGraphWindow();
1232 return PM3_SUCCESS;
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) {
1242 if (invert)
1243 memcpy(fskType, "FSK2a", 5);
1244 else
1245 memcpy(fskType, "FSK2", 4);
1247 } else if (fchigh == 8 && fclow == 5) {
1249 if (invert)
1250 memcpy(fskType, "FSK1", 4);
1251 else
1252 memcpy(fskType, "FSK1a", 5);
1254 } else {
1255 memcpy(fskType, "FSK??", 5);
1257 return fskType;
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) {
1266 if (verbose) {
1267 PrintAndLogEx(INFO, "signal looks like noise");
1269 return PM3_ESOFT;
1272 uint8_t *bits = calloc(MAX_GRAPH_TRACE_LEN, sizeof(uint8_t));
1273 if (bits == NULL) {
1274 PrintAndLogEx(FAILED, "failed to allocate memory");
1275 return PM3_EMALLOC;
1278 size_t bitlen = getFromGraphBuffer(bits);
1279 if (bitlen == 0) {
1280 PrintAndLogEx(DEBUG, "DEBUG: no data in graphbuf");
1281 free(bits);
1282 return PM3_ESOFT;
1285 //get field clock lengths
1286 if (!fchigh || !fclow) {
1287 uint16_t fcs = countFC(bits, bitlen, true);
1288 if (!fcs) {
1289 fchigh = 10;
1290 fclow = 8;
1291 } else {
1292 fchigh = (fcs >> 8) & 0x00FF;
1293 fclow = fcs & 0x00FF;
1296 //get bit clock length
1297 if (!rfLen) {
1298 int firstClockEdge = 0; //todo - align grid on graph with this...
1299 rfLen = detectFSKClk(bits, bitlen, fchigh, fclow, &firstClockEdge);
1300 if (!rfLen) rfLen = 50;
1303 int start_idx = 0;
1304 int size = fskdemod(bits, bitlen, rfLen, invert, fchigh, fclow, &start_idx);
1305 if (size > 0) {
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"
1312 , rfLen
1313 , (invert) ? "inverted, " : ""
1314 , fchigh
1315 , fclow
1317 PrintAndLogEx(NORMAL, "");
1318 PrintAndLogEx(SUCCESS, _YELLOW_("%s") " decoded bitstream", GetFSKType(fchigh, fclow, invert));
1319 PrintAndLogEx(INFO, "-----------------------");
1320 printDemodBuff(0, false, false, false);
1322 goto out;
1323 } else {
1324 PrintAndLogEx(DEBUG, "no FSK data found");
1327 out:
1328 free(bits);
1329 return PM3_SUCCESS;
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[] = {
1348 arg_param_begin,
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)"),
1353 arg_param_end
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;
1361 CLIParserFree(ctx);
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) {
1369 if (verbose) {
1370 PrintAndLogEx(INFO, "signal looks like noise");
1372 return PM3_ESOFT;
1375 uint8_t *bits = calloc(MAX_GRAPH_TRACE_LEN, sizeof(uint8_t));
1376 if (bits == NULL) {
1377 PrintAndLogEx(FAILED, "failed to allocate memory");
1378 return PM3_EMALLOC;
1380 size_t bitlen = getFromGraphBuffer(bits);
1381 if (bitlen == 0) {
1382 free(bits);
1383 return PM3_ESOFT;
1386 int startIdx = 0;
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);
1390 free(bits);
1391 return PM3_ESOFT;
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);
1395 free(bits);
1396 return PM3_ESOFT;
1398 if (verbose || g_debugMode) {
1399 PrintAndLogEx(DEBUG, "DEBUG: (PSKdemod) Using Clock:%d, invert:%d, Bits Found:%zu", clk, invert, bitlen);
1400 if (errCnt > 0) {
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);
1407 free(bits);
1408 return PM3_SUCCESS;
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) {
1419 if (verbose) {
1420 PrintAndLogEx(INFO, "signal looks like noise");
1422 return PM3_ESOFT;
1425 uint8_t *bits = calloc(MAX_GRAPH_TRACE_LEN, sizeof(uint8_t));
1426 if (bits == NULL) {
1427 PrintAndLogEx(FAILED, "failed to allocate memory");
1428 return PM3_EMALLOC;
1431 size_t bitlen = getFromGraphBuffer(bits);
1433 if (bitlen == 0) {
1434 free(bits);
1435 return PM3_ESOFT;
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);
1441 free(bits);
1442 return PM3_ESOFT;
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);
1446 free(bits);
1447 return PM3_ESOFT;
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);
1464 free(bits);
1465 return PM3_SUCCESS;
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[] = {
1479 arg_param_begin,
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)"),
1483 arg_param_end
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;
1490 CLIParserFree(ctx);
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[] = {
1509 arg_param_begin,
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)"),
1513 arg_param_end
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;
1520 CLIParserFree(ctx);
1522 int ans = PSKDemod(clk, invert, max_err, true);
1523 //output
1524 if (ans != PM3_SUCCESS) {
1525 if (g_debugMode) PrintAndLogEx(ERR, "Error demoding: %d", ans);
1526 return PM3_ESOFT;
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);
1532 return PM3_SUCCESS;
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[] = {
1547 arg_param_begin,
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)"),
1551 arg_param_end
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;
1558 CLIParserFree(ctx);
1560 int ans = PSKDemod(clk, invert, max_err, true);
1561 if (ans != PM3_SUCCESS) {
1562 if (g_debugMode) PrintAndLogEx(ERR, "Error demoding: %d", ans);
1563 return PM3_ESOFT;
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);
1570 return PM3_SUCCESS;
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[] = {
1588 arg_param_begin,
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"),
1597 arg_param_end
1601 char tmp[5];
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);
1614 CLIParserFree(ctx);
1616 int foo = (ab + am + ar + fs + nr + p1 + p2);
1617 if (foo > 1) {
1618 PrintAndLogEx(WARNING, "please, select only one modulation");
1619 return PM3_EINVARG;
1621 if (foo == 0) {
1622 PrintAndLogEx(WARNING, "please, select a modulation");
1623 return PM3_EINVARG;
1626 int ans = 0;
1627 const char *s = Cmd + n;
1628 if (fs)
1629 ans = CmdFSKrawdemod(s);
1630 else if (ab)
1631 ans = Cmdaskbiphdemod(s);
1632 else if (am)
1633 ans = Cmdaskmandemod(s);
1634 else if (ar)
1635 ans = Cmdaskrawdemod(s);
1636 else if (nr)
1637 ans = CmdNRZrawDemod(s);
1638 else if (p1)
1639 ans = CmdPSK1rawDemod(s);
1640 else if (p2)
1641 ans = CmdPSK2rawDemod(s);
1643 return ans;
1646 void setClockGrid(uint32_t clk, int offset) {
1647 g_DemodStartIdx = offset;
1648 g_DemodClock = clk;
1649 if (clk == 0 && offset == 0)
1650 PrintAndLogEx(DEBUG, "DEBUG: (setClockGrid) clear settings");
1651 else
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;
1660 g_GridOffset = 0;
1661 g_PlotGridX = 0;
1662 g_DefaultGridX = 0;
1663 RepaintGraphWindow();
1664 } else {
1665 g_GridLocked = true;
1666 g_GridOffset = offset;
1667 g_PlotGridX = clk;
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[] = {
1682 arg_param_begin,
1683 arg_dbl0("x", NULL, "<dec>", "plot grid X coord"),
1684 arg_dbl0("y", NULL, "<dec>", "plot grid Y coord"),
1685 arg_param_end
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);
1690 CLIParserFree(ctx);
1692 PrintAndLogEx(DEBUG, "Setting X %.0f Y %.0f", g_PlotGridX, g_PlotGridY);
1693 g_DefaultGridX = g_PlotGridX;
1694 g_DefaultGridY = g_PlotGridY;
1695 RepaintGraphWindow();
1696 return PM3_SUCCESS;
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[] = {
1708 arg_param_begin,
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"),
1714 arg_param_end
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));
1722 CLIParserFree(ctx);
1723 PrintAndLogEx(INFO, "Setting markers " _BRIGHT_YELLOW_("A") "=%u, "_BRIGHT_MAGENTA_("B") "=%u, "_RED_("C") "=%u, "_BLUE_("D") "=%u",
1724 g_MarkerA.pos,
1725 g_MarkerB.pos,
1726 g_MarkerC.pos,
1727 g_MarkerD.pos
1729 RepaintGraphWindow();
1730 return PM3_SUCCESS;
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[] = {
1741 arg_param_begin,
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"),
1745 arg_param_end
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);
1751 CLIParserFree(ctx);
1753 // sanity checks
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"
1762 , requested
1763 , offset
1764 , g_pm3_capabilities.bigbuf_size
1766 return PM3_EINVARG;
1769 if (!GetFromDevice(BIG_BUF, got, requested, offset, NULL, 0, NULL, 2500, false)) {
1770 PrintAndLogEx(WARNING, "command execution time out");
1771 return PM3_ESOFT;
1774 print_hex_break(got, requested, breaks);
1775 return PM3_SUCCESS;
1778 static int CmdHide(const char *Cmd) {
1779 CLIParserContext *ctx;
1780 CLIParserInit(&ctx, "data hide",
1781 "Show graph window",
1782 "data hide"
1784 void *argtable[] = {
1785 arg_param_begin,
1786 arg_param_end
1788 CLIExecWithReturn(ctx, Cmd, argtable, true);
1789 CLIParserFree(ctx);
1790 HideGraphWindow();
1791 return PM3_SUCCESS;
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",
1799 "data hpf"
1801 void *argtable[] = {
1802 arg_param_begin,
1803 arg_param_end
1805 CLIExecWithReturn(ctx, Cmd, argtable, true);
1806 CLIParserFree(ctx);
1808 uint8_t *bits = calloc(g_GraphTraceLen, sizeof(uint8_t));
1809 if (bits == NULL) {
1810 PrintAndLogEx(FAILED, "failed to allocate memory");
1811 return PM3_EMALLOC;
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();
1821 free(bits);
1822 return PM3_SUCCESS;
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) {
1832 uint8_t val = 0;
1833 for (int i = 0 ; i < bits_per_sample; i++)
1834 val |= (_headBit(b) << (7 - i));
1836 return val;
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) {
1845 if (end < start) {
1846 PrintAndLogEx(WARNING, "error, end (%u) is smaller than start (%u)", end, start);
1847 return PM3_EINVARG;
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;
1863 if (verbose) {
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;
1873 if (verbose) {
1874 PrintAndLogEx(SUCCESS, "Data fetched");
1877 uint8_t bits_per_sample = 8;
1879 if (IfPm3Lf() && ignore_lf_config == false) {
1880 sample_config sc;
1881 lf_getconfig(&sc);
1882 if (verbose) {
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};
1900 size_t j = 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);
1909 } else {
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));
1917 if (bits == NULL) {
1918 PrintAndLogEx(FAILED, "failed to allocate memory");
1919 return PM3_EMALLOC;
1921 size_t size = getFromGraphBuffer(bits);
1922 // set signal properties low/high/mean/amplitude and is_noise detection
1923 computeSignalProperties(bits, size);
1924 free(bits);
1926 setClockGrid(0, 0);
1927 g_DemodBufferLen = 0;
1928 RepaintGraphWindow();
1930 return PM3_SUCCESS;
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.",
1939 "data samples\n"
1940 "data samples -n 10000"
1942 void *argtable[] = {
1943 arg_param_begin,
1944 arg_int0("n", NULL, "<dec>", "num of samples (512 - 40000)"),
1945 arg_lit0("v", "verbose", "verbose output"),
1946 arg_param_end
1948 CLIExecWithReturn(ctx, Cmd, argtable, true);
1949 int n = arg_get_int_def(ctx, 1, 0);
1950 bool verbose = arg_get_lit(ctx, 2);
1951 CLIParserFree(ctx);
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[] = {
1965 arg_param_begin,
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"),
1969 arg_param_end
1971 CLIExecWithReturn(ctx, Cmd, argtable, false);
1973 int fnlen = 0;
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);
1978 CLIParserFree(ctx);
1980 char *path = NULL;
1981 if (searchFile(&path, TRACES_SUBDIR, filename, ".pm3", true) != PM3_SUCCESS) {
1982 if (searchFile(&path, TRACES_SUBDIR, filename, "", false) != PM3_SUCCESS) {
1983 return PM3_EFILE;
1987 FILE *f;
1988 if (is_bin)
1989 f = fopen(path, "rb");
1990 else
1991 f = fopen(path, "r");
1993 if (f == NULL) {
1994 PrintAndLogEx(WARNING, "couldn't open '%s'", path);
1995 free(path);
1996 return PM3_EFILE;
1998 free(path);
2000 g_GraphTraceLen = 0;
2002 if (is_bin) {
2003 uint8_t val[2];
2004 while (fread(val, 1, 1, f)) {
2005 g_GraphBuffer[g_GraphTraceLen] = val[0] - 127;
2006 g_GraphTraceLen++;
2008 if (g_GraphTraceLen >= MAX_GRAPH_TRACE_LEN)
2009 break;
2011 } else {
2012 char line[80];
2013 while (fgets(line, sizeof(line), f)) {
2014 g_GraphBuffer[g_GraphTraceLen] = atoi(line);
2015 g_GraphTraceLen++;
2017 if (g_GraphTraceLen >= MAX_GRAPH_TRACE_LEN)
2018 break;
2021 fclose(f);
2023 PrintAndLogEx(SUCCESS, "loaded " _YELLOW_("%s") " samples", commaprint(g_GraphTraceLen));
2025 if (nofix == false) {
2026 uint8_t *bits = calloc(g_GraphTraceLen, sizeof(uint8_t));
2027 if (bits == NULL) {
2028 PrintAndLogEx(FAILED, "failed to allocate memory");
2029 return PM3_EMALLOC;
2031 size_t size = getFromGraphBuffer(bits);
2033 removeSignalOffset(bits, size);
2034 setGraphBuffer(bits, size);
2035 computeSignalProperties(bits, size);
2036 free(bits);
2039 setClockGrid(0, 0);
2040 g_DemodBufferLen = 0;
2041 RepaintGraphWindow();
2042 return PM3_SUCCESS;
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[] = {
2053 arg_param_begin,
2054 arg_u64_1("i", "idx", "<dec>", "index in graph buffer"),
2055 arg_param_end
2057 CLIExecWithReturn(ctx, Cmd, argtable, false);
2058 uint32_t ds = arg_get_u32(ctx, 1);
2059 CLIParserFree(ctx);
2061 // sanitycheck
2062 if (g_GraphTraceLen <= ds) {
2063 PrintAndLogEx(WARNING, "index out of bounds");
2064 return PM3_EINVARG;
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();
2073 return PM3_SUCCESS;
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[] = {
2085 arg_param_begin,
2086 arg_u64_1("i", "idx", "<dec>", "index in graph buffer"),
2087 arg_param_end
2089 CLIExecWithReturn(ctx, Cmd, argtable, false);
2090 uint32_t ds = arg_get_u32(ctx, 1);
2091 CLIParserFree(ctx);
2093 // sanitycheck
2094 if (g_GraphTraceLen <= ds) {
2095 PrintAndLogEx(WARNING, "index out of bounds");
2096 return PM3_EINVARG;
2099 g_GraphTraceLen = ds;
2100 RepaintGraphWindow();
2101 return PM3_SUCCESS;
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"
2111 "AND\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[] = {
2116 arg_param_begin,
2117 arg_u64_1("s", "start", "<dec>", "start point"),
2118 arg_u64_1("e", "end", "<dec>", "end point"),
2119 arg_param_end
2121 CLIExecWithReturn(ctx, Cmd, argtable, false);
2122 uint32_t start = arg_get_u32(ctx, 1);
2123 uint32_t stop = arg_get_u32(ctx, 2);
2124 CLIParserFree(ctx);
2126 if (start > g_GraphTraceLen || stop > g_GraphTraceLen || start >= stop) {
2127 PrintAndLogEx(WARNING, "start and end points doesn't align");
2128 return PM3_EINVARG;
2131 // leave start position sample
2132 start++;
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();
2141 return PM3_SUCCESS;
2144 int CmdNorm(const char *Cmd) {
2146 CLIParserContext *ctx;
2147 CLIParserInit(&ctx, "data norm",
2148 "Normalize max/min to +/-128",
2149 "data norm"
2151 void *argtable[] = {
2152 arg_param_begin,
2153 arg_param_end
2155 CLIExecWithReturn(ctx, Cmd, argtable, true);
2156 CLIParserFree(ctx);
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));
2174 if (bits == NULL) {
2175 PrintAndLogEx(FAILED, "failed to allocate memory");
2176 return PM3_EMALLOC;
2178 size_t size = getFromGraphBuffer(bits);
2179 // set signal properties low/high/mean/amplitude and is_noise detection
2180 computeSignalProperties(bits, size);
2182 RepaintGraphWindow();
2183 free(bits);
2184 return PM3_SUCCESS;
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",
2192 "data plot"
2194 void *argtable[] = {
2195 arg_param_begin,
2196 arg_param_end
2198 CLIExecWithReturn(ctx, Cmd, argtable, true);
2199 CLIParserFree(ctx);
2200 ShowGraphWindow();
2201 return PM3_SUCCESS;
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[] = {
2216 arg_param_begin,
2217 arg_lit0("w", "wave", "save as wave format (.wav)"),
2218 arg_str1("f", "file", "<fn w/o ext>", "save file name"),
2219 arg_param_end
2221 CLIExecWithReturn(ctx, Cmd, argtable, false);
2223 bool as_wave = arg_get_lit(ctx, 1);
2225 int fnlen = 0;
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);
2229 CLIParserFree(ctx);
2231 if (g_GraphTraceLen == 0) {
2232 PrintAndLogEx(WARNING, "Graphbuffer is empty, nothing to save");
2233 return PM3_SUCCESS;
2236 if (as_wave)
2237 return saveFileWAVE(filename, g_GraphBuffer, g_GraphTraceLen);
2238 else
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[] = {
2254 arg_param_begin,
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)"),
2257 arg_param_end
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;
2265 int len = 0;
2266 g_CursorScaleFactorUnit[0] = '\x00';
2267 CLIParamStrToBuf(arg_get_str(ctx, 2), (uint8_t *)g_CursorScaleFactorUnit, sizeof(g_CursorScaleFactorUnit), &len);
2268 CLIParserFree(ctx);
2269 RepaintGraphWindow();
2270 return PM3_SUCCESS;
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.
2279 out[0] = 0;
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.
2285 out[i] = 1;
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.
2290 out[i] = -1;
2291 } else {
2292 lastValue = out[i]; // Buffer last value as we overwrite it.
2293 out[i] = out[i - 1];
2297 // Align with first edited sample.
2298 out[0] = out[1];
2299 return PM3_SUCCESS;
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[] = {
2309 arg_param_begin,
2310 arg_int1("d", "down", "<dec>", "threshold down"),
2311 arg_int1("u", "up", "<dec>", "threshold up"),
2312 arg_param_end
2314 CLIExecWithReturn(ctx, Cmd, argtable, false);
2315 int8_t down = arg_get_int(ctx, 1);
2316 int8_t up = arg_get_int(ctx, 2);
2317 CLIParserFree(ctx);
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));
2325 if (bits == NULL) {
2326 PrintAndLogEx(FAILED, "failed to allocate memory");
2327 return PM3_EMALLOC;
2329 size_t size = getFromGraphBuffer(bits);
2330 // set signal properties low/high/mean/amplitude and is_noice detection
2331 computeSignalProperties(bits, size);
2333 RepaintGraphWindow();
2334 free(bits);
2335 return PM3_SUCCESS;
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[] = {
2345 arg_param_begin,
2346 arg_param_end
2348 CLIExecWithReturn(ctx, Cmd, argtable, true);
2349 CLIParserFree(ctx);
2351 // Zero-crossings aren't meaningful unless the signal is zero-mean.
2352 CmdHpf("");
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.
2359 zc++;
2360 g_GraphBuffer[i] = lastZc;
2361 } else {
2362 // Change in sign, reset the sample count.
2363 sign = -sign;
2364 g_GraphBuffer[i] = lastZc;
2365 if (sign > 0) {
2366 lastZc = zc;
2367 zc = 0;
2372 uint8_t *bits = calloc(g_GraphTraceLen, sizeof(uint8_t));
2373 if (bits == NULL) {
2374 PrintAndLogEx(FAILED, "failed to allocate memory");
2375 return PM3_EMALLOC;
2377 size_t size = getFromGraphBuffer(bits);
2378 // set signal properties low/high/mean/amplitude and is_noise detection
2379 computeSignalProperties(bits, size);
2380 RepaintGraphWindow();
2381 free(bits);
2382 return PM3_SUCCESS;
2385 static bool data_verify_hex(uint8_t *d, size_t n) {
2386 if (d == NULL)
2387 return false;
2389 for (size_t i = 0; i < n; i++) {
2390 if (isxdigit(d[i]) == false) {
2391 PrintAndLogEx(ERR, "Non hex digit found");
2392 return false;
2395 return true;
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) {
2416 int i, j = 0;
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++) {
2429 LowTone[i] = 1;
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;
2440 int k;
2441 // add last -1 modifiers
2442 for (k = 0; k < Right_Modifier; k++) {
2443 LowTone[((i - 1) * LowToneFC) + Left_Modifier + j + k] = -1;
2446 // now do hightone
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++) {
2453 HighTone[i] = 1;
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) {
2479 uint8_t ans = 0;
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");
2492 return PM3_ESOFT;
2495 int LowTone[clk];
2496 int HighTone[clk];
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);
2535 return PM3_SUCCESS;
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",
2544 "data fsktonrz\n"
2545 "data fsktonrz -c 32 --low 8 --hi 10");
2547 void *argtable[] = {
2548 arg_param_begin,
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"),
2552 arg_param_end
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);
2559 CLIParserFree(ctx);
2561 setClockGrid(0, 0);
2562 g_DemodBufferLen = 0;
2563 int ans = FSKToNRZ(g_GraphBuffer, &g_GraphTraceLen, clk, fc_low, fc_high);
2564 CmdNorm("");
2565 RepaintGraphWindow();
2566 return ans;
2569 static int CmdDataIIR(const char *Cmd) {
2571 CLIParserContext *ctx;
2572 CLIParserInit(&ctx, "data iir",
2573 "Apply IIR buttersworth filter on plot data",
2574 "data iir -n 2"
2576 void *argtable[] = {
2577 arg_param_begin,
2578 arg_u64_1("n", NULL, "<dec>", "factor n"),
2579 arg_param_end
2581 CLIExecWithReturn(ctx, Cmd, argtable, false);
2582 uint8_t k = (arg_get_u32_def(ctx, 1, 0) & 0xFF);
2583 CLIParserFree(ctx);
2585 iceSimple_Filter(g_GraphBuffer, g_GraphTraceLen, k);
2587 uint8_t *bits = calloc(g_GraphTraceLen, sizeof(uint8_t));
2588 if (bits == NULL) {
2589 PrintAndLogEx(FAILED, "failed to allocate memory");
2590 return PM3_EMALLOC;
2592 size_t size = getFromGraphBuffer(bits);
2593 // set signal properties low/high/mean/amplitude and is_noise detection
2594 computeSignalProperties(bits, size);
2595 RepaintGraphWindow();
2596 free(bits);
2597 return PM3_SUCCESS;
2600 typedef struct {
2601 t55xx_modulation modulation;
2602 int bitrate;
2603 int carrier;
2604 uint8_t fc1;
2605 uint8_t fc2;
2606 } lf_modulation_t;
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) {
2613 case DEMOD_PSK1:
2614 case DEMOD_PSK2:
2615 case DEMOD_PSK3:
2616 PrintAndLogEx(SUCCESS, " Carrier rate...... %d", b.carrier);
2617 break;
2618 case DEMOD_FSK:
2619 case DEMOD_FSK1:
2620 case DEMOD_FSK1a:
2621 case DEMOD_FSK2:
2622 case DEMOD_FSK2a:
2623 PrintAndLogEx(SUCCESS, " Field Clocks...... FC/%u, FC/%u", b.fc1, b.fc2);
2624 break;
2625 case DEMOD_NRZ:
2626 case DEMOD_ASK:
2627 case DEMOD_BI:
2628 case DEMOD_BIa:
2629 default:
2630 break;
2632 PrintAndLogEx(NORMAL, "");
2633 return PM3_SUCCESS;
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;
2647 bool st = false;
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;
2665 ++hits;
2668 } else {
2669 clk = GetAskClock("", false);
2670 if (clk > 0) {
2671 // 0 = auto clock
2672 // 0 = no invert
2673 // 1 = maxError 1
2674 // 0 = max len
2675 // false = no amplify
2676 // false = no verbose
2677 // false = no emSearch
2678 // 1 = Ask/Man
2679 // st = true
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;
2683 ++hits;
2685 // "0 0 1 " == clock auto, invert true, maxError 1.
2686 // false = no verbose
2687 // false = no emSearch
2688 // 1 = Ask/Man
2689 // st = true
2691 // ASK / biphase
2692 if ((ASKbiphaseDemod(0, 0, 0, 2, false) == PM3_SUCCESS)) {
2693 tests[hits].modulation = DEMOD_BI;
2694 tests[hits].bitrate = clk;
2695 ++hits;
2697 // ASK / Diphase
2698 if ((ASKbiphaseDemod(0, 0, 1, 2, false) == PM3_SUCCESS)) {
2699 tests[hits].modulation = DEMOD_BIa;
2700 tests[hits].bitrate = clk;
2701 ++hits;
2704 clk = GetNrzClock("", false);
2705 if ((NRZrawDemod(0, 0, 1, false) == PM3_SUCCESS)) {
2706 tests[hits].modulation = DEMOD_NRZ;
2707 tests[hits].bitrate = clk;
2708 ++hits;
2711 clk = GetPskClock("", false);
2712 if (clk > 0) {
2713 // allow undo
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)
2717 CmdLtrim("-i 160");
2718 if ((PSKDemod(0, 0, 6, false) == PM3_SUCCESS)) {
2719 tests[hits].modulation = DEMOD_PSK1;
2720 tests[hits].bitrate = clk;
2721 ++hits;
2723 // get psk carrier
2724 tests[hits].carrier = GetPskCarrier(false);
2726 //undo trim samples
2727 restore_bufferS32(saveState, g_GraphBuffer);
2728 g_GridOffset = saveState.offset;
2732 if (hits) {
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]);
2738 return PM3_SUCCESS;
2739 } else {
2740 PrintAndLogEx(INFO, "Signal doesn't match");
2741 return PM3_ESOFT;
2745 static int CmdDataModulationSearch(const char *Cmd) {
2746 CLIParserContext *ctx;
2747 CLIParserInit(&ctx, "data modulation",
2748 "search LF signal after clock and modulation\n",
2749 "data modulation"
2752 void *argtable[] = {
2753 arg_param_begin,
2754 arg_param_end
2756 CLIExecWithReturn(ctx, Cmd, argtable, true);
2757 CLIParserFree(ctx);
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[] = {
2771 arg_param_begin,
2772 arg_str0("d", NULL, "<hex>", "ASN1 encoded byte array"),
2773 arg_lit0(NULL, "test", "perform self tests"),
2774 arg_param_end
2776 CLIExecWithReturn(ctx, Cmd, argtable, false);
2777 int dlen = 2048;
2778 uint8_t data[2048];
2779 CLIGetHexWithReturn(ctx, 1, data, &dlen);
2780 bool selftest = arg_get_lit(ctx, 2);
2781 CLIParserFree(ctx);
2782 if (selftest) {
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, "");
2790 return PM3_SUCCESS;
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[] = {
2807 arg_param_begin,
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"),
2814 arg_param_end
2816 CLIExecWithReturn(ctx, Cmd, argtable, false);
2818 int fnlenA = 0;
2819 char filenameA[FILE_PATH_SIZE] = {0};
2820 CLIParamStrToBuf(arg_get_str(ctx, 1), (uint8_t *)filenameA, FILE_PATH_SIZE, &fnlenA);
2822 int fnlenB = 0;
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
2829 int splenA = 0;
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
2834 int splenB = 0;
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);
2839 CLIParserFree(ctx);
2841 // sanity check
2842 if (IfPm3Rdv4Fw() == false && (splenA > 0 || splenB > 0)) {
2843 PrintAndLogEx(WARNING, "No RDV4 Flashmemory available");
2844 return PM3_EINVARG;
2847 if (splenA > 32) {
2848 PrintAndLogEx(WARNING, "SPIFFS filname A length is large than 32 bytes, got %d", splenA);
2849 return PM3_EINVARG;
2851 if (splenB > 32) {
2852 PrintAndLogEx(WARNING, "SPIFFS filname B length is large than 32 bytes, got %d", splenB);
2853 return PM3_EINVARG;
2857 if (width > 16 || width < 1) {
2858 PrintAndLogEx(INFO, "Width out of range, using default 16 bytes width");
2859 width = 16;
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;
2869 // read file A
2870 if (fnlenA) {
2871 // read dump file
2872 res = pm3_load_dump(filenameA, (void **)&inA, &datalenA, MIFARE_4K_MAX_BYTES);
2873 if (res != PM3_SUCCESS) {
2874 return res;
2878 // read file B
2879 if (fnlenB) {
2880 // read dump file
2881 res = pm3_load_dump(filenameB, (void **)&inB, &datalenB, MIFARE_4K_MAX_BYTES);
2882 if (res != PM3_SUCCESS) {
2883 return res;
2887 // read spiffs file A
2888 if (splenA) {
2889 res = flashmem_spiffs_download(spnameA, splenA, (void **)&inA, &datalenA);
2890 if (res != PM3_SUCCESS) {
2891 return res;
2895 // read spiffs file B
2896 if (splenB) {
2897 res = flashmem_spiffs_download(spnameB, splenB, (void **)&inB, &datalenB);
2898 if (res != PM3_SUCCESS) {
2899 return res;
2903 // download emulator memory
2904 if (use_e) {
2906 uint8_t *d = calloc(MIFARE_4K_MAX_BYTES, sizeof(uint8_t));
2907 if (d == NULL) {
2908 PrintAndLogEx(WARNING, "Fail, cannot allocate memory");
2909 return PM3_EMALLOC;
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");
2915 free(inA);
2916 free(inB);
2917 free(d);
2918 return PM3_ETIMEOUT;
2921 if (fnlenA) {
2922 datalenB = MIFARE_4K_MAX_BYTES;
2923 inB = d;
2924 } else {
2925 datalenA = MIFARE_4K_MAX_BYTES;
2926 inA = d;
2930 size_t biggest = (datalenA > datalenB) ? datalenA : datalenB;
2931 PrintAndLogEx(DEBUG, "data len: %zu A %zu B %zu", biggest, datalenA, datalenB);
2933 if (inA == NULL) {
2934 PrintAndLogEx(INFO, "inA null");
2937 if (inB == 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);
2967 } else {
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);
3003 //both files ended
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, ".") ;
3009 continue ;
3012 char ca, cb;
3014 if (j >= datalenA) {
3015 // file A ended. print B without colors
3016 cb = inB[j];
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);
3021 continue ;
3023 ca = inA[j];
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, ".") ;
3030 continue ;
3032 cb = inB[j];
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);
3039 } else {
3040 // normal
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);
3052 // footer
3053 PrintAndLogEx(INFO, hdr1);
3054 PrintAndLogEx(NORMAL, "");
3056 free(inB);
3057 free(inA);
3058 return PM3_SUCCESS;
3062 * @brief Utility for number conversion via cmdline.
3063 * @param Cmd
3064 * @return
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 0x1000\n"
3075 void *argtable[] = {
3076 arg_param_begin,
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"),
3082 arg_param_end
3084 CLIExecWithReturn(ctx, Cmd, argtable, false);
3086 int dlen = 256;
3087 char dec[256];
3088 memset(dec, 0, sizeof(dec));
3089 int res = CLIParamStrToBuf(arg_get_str(ctx, 1), (uint8_t *)dec, sizeof(dec), &dlen);
3091 int hlen = 256;
3092 char hex[256];
3093 memset(hex, 0, sizeof(hex));
3094 res |= CLIParamStrToBuf(arg_get_str(ctx, 2), (uint8_t *)hex, sizeof(hex), &hlen);
3096 int blen = 256;
3097 char bin[256];
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);
3103 CLIParserFree(ctx);
3105 // sanity checks
3106 if (res) {
3107 PrintAndLogEx(FAILED, "Error parsing bytes");
3108 return PM3_EINVARG;
3111 // results for MPI actions
3112 bool ret = false;
3113 (void) ret;
3115 // container of big number
3116 mbedtls_mpi N;
3117 mbedtls_mpi_init(&N);
3119 // hex
3120 if (hlen > 0) {
3121 if (data_verify_hex((uint8_t *)hex, hlen) == false) {
3122 return PM3_EINVARG;
3124 MBEDTLS_MPI_CHK(mbedtls_mpi_read_string(&N, 16, hex));
3125 PrintAndLogEx(INFO, "#....... %d", hlen);
3128 // decimal
3129 if (dlen > 0) {
3130 // should have decimal string check here too
3131 MBEDTLS_MPI_CHK(mbedtls_mpi_read_string(&N, 10, dec));
3134 // binary
3135 if (blen > 0) {
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);
3141 mbedtls_mpi base;
3142 mbedtls_mpi_init(&base);
3143 mbedtls_mpi_add_int(&base, &base, 10);
3145 // printing
3146 typedef struct {
3147 const char *desc;
3148 uint8_t radix;
3149 } radix_t;
3151 radix_t radix[] = {
3152 {"dec..... ", 10},
3153 {"hex..... ", 16},
3154 {"bin..... ", 2}
3157 char s[600] = {0};
3158 size_t slen = 0;
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));
3163 if (slen) {
3165 // only pad bin string
3166 int pn = 0;
3167 if (i == 2) {
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);
3178 // ascii
3179 MBEDTLS_MPI_CHK(mbedtls_mpi_write_string(&N, 16, s, sizeof(s), &slen));
3180 if (slen) {
3181 size_t n = (slen >> 1);
3182 uint8_t *d = calloc(n, sizeof(uint8_t));
3183 if (d != NULL) {
3184 hexstr_to_byte_array(s, d, &n);
3185 PrintAndLogEx(SUCCESS, "ascii... " _YELLOW_("%s"), sprint_ascii((const uint8_t *)d, n));
3186 free(d);
3190 // reverse
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));
3196 if (slen) {
3198 // only pad bin string
3199 char scpy[600] = {0x30};
3200 memset(scpy, 0x30, sizeof(scpy));
3201 int pn = 0;
3202 if (i == 2) {
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);
3215 // ascii
3216 MBEDTLS_MPI_CHK(mbedtls_mpi_write_string(&N, 16, s, sizeof(s), &slen));
3217 if (slen) {
3218 str_reverse(s, strlen(s));
3219 size_t n = (slen >> 1);
3220 uint8_t *d = calloc(n, sizeof(uint8_t));
3221 if (d != NULL) {
3222 hexstr_to_byte_array(s, d, &n);
3223 PrintAndLogEx(SUCCESS, "ascii... " _YELLOW_("%s"), sprint_ascii((const uint8_t *)d, n));
3224 free(d);
3229 // invert
3230 if (shall_invert) {
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));
3234 if (slen == 0) {
3235 continue;
3238 switch (i) {
3239 case 0: {
3240 // MBEDTLS_MPI_CHK(mbedtls_mpi_inv_mod(&N, &N, &base));
3241 break;
3243 case 1: {
3244 str_inverse_hex(s, strlen(s));
3245 PrintAndLogEx(SUCCESS, "%s%s", radix[i].desc, s);
3246 break;
3248 case 2: {
3250 char scpy[600] = {0x30};
3251 memset(scpy, 0x30, sizeof(scpy));
3252 int pn = 0;
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);
3262 break;
3264 default: {
3265 break;
3269 // ascii
3270 MBEDTLS_MPI_CHK(mbedtls_mpi_write_string(&N, 16, s, sizeof(s), &slen));
3271 if (slen) {
3272 str_inverse_hex(s, strlen(s));
3273 size_t n = (slen >> 1);
3274 uint8_t *d = calloc(n, sizeof(uint8_t));
3275 if (d != NULL) {
3276 hexstr_to_byte_array(s, d, &n);
3277 PrintAndLogEx(SUCCESS, "ascii... " _YELLOW_("%s"), sprint_ascii((const uint8_t *)d, n));
3278 free(d);
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);
3293 if (res == 0) {
3294 PrintAndLogEx(INFO, "prime... " _YELLOW_("yes"));
3297 cleanup:
3298 mbedtls_mpi_free(&N);
3299 mbedtls_mpi_free(&base);
3300 mbedtls_entropy_free(&entropy);
3301 mbedtls_ctr_drbg_free(&ctr_drbg);
3302 return PM3_SUCCESS;
3305 int centerThreshold(const int *in, int *out, size_t len, int8_t up, int8_t down) {
3306 if (len < 5) {
3307 return PM3_EINVARG;
3310 for (size_t i = 0; i < len; ++i) {
3311 if ((in[i] <= up) && (in[i] >= down)) {
3312 out[i] = 0;
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) {
3322 out[i] = 0;
3325 return PM3_SUCCESS;
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[] = {
3335 arg_param_begin,
3336 arg_int1("d", "down", "<dec>", "threshold down"),
3337 arg_int1("u", "up", "<dec>", "threshold up"),
3338 arg_param_end
3340 CLIExecWithReturn(ctx, Cmd, argtable, false);
3341 int8_t down = arg_get_int(ctx, 1);
3342 int8_t up = arg_get_int(ctx, 2);
3343 CLIParserFree(ctx);
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));
3351 if (bits == NULL) {
3352 PrintAndLogEx(FAILED, "failed to allocate memory");
3353 return PM3_EMALLOC;
3355 size_t size = getFromGraphBuffer(bits);
3356 // set signal properties low/high/mean/amplitude and is_noice detection
3357 computeSignalProperties(bits, size);
3358 RepaintGraphWindow();
3359 free(bits);
3360 return PM3_SUCCESS;
3363 static int envelope_square(const int *in, int *out, size_t len) {
3364 if (len < 10) {
3365 return PM3_EINVARG;
3369 size_t i = 0;
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) {
3375 i += 8;
3376 continue;
3379 out[i] = 255;
3380 i++;
3382 return PM3_SUCCESS;
3385 static int CmdEnvelope(const char *Cmd) {
3386 CLIParserContext *ctx;
3387 CLIParserInit(&ctx, "data envelop",
3388 "Create an square envelop of the samples",
3389 "data envelop"
3391 void *argtable[] = {
3392 arg_param_begin,
3393 arg_param_end
3395 CLIExecWithReturn(ctx, Cmd, argtable, true);
3396 CLIParserFree(ctx);
3398 envelope_square(g_GraphBuffer, g_GraphBuffer, g_GraphTraceLen);
3400 uint8_t *bits = calloc(g_GraphTraceLen, sizeof(uint8_t));
3401 if (bits == NULL) {
3402 PrintAndLogEx(FAILED, "failed to allocate memory");
3403 return PM3_EMALLOC;
3405 size_t size = getFromGraphBuffer(bits);
3406 // set signal properties low/high/mean/amplitude and is_noice detection
3407 computeSignalProperties(bits, size);
3408 RepaintGraphWindow();
3409 free(bits);
3410 return PM3_SUCCESS;
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[] = {
3422 arg_param_begin,
3423 arg_str0("d", NULL, "<hex>", "ASN1 encoded byte array"),
3424 // arg_lit0("t", "test", "perform self test"),
3425 arg_param_end
3427 CLIExecWithReturn(ctx, Cmd, argtable, false);
3428 uint8_t data[129];
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);
3433 CLIParserFree(ctx);
3434 // if (selftest) {
3435 // return atr_selftest();
3436 // }
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");
3447 free(copy);
3448 return PM3_SUCCESS;
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[] = {
3461 arg_param_begin,
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"),
3468 arg_param_end
3470 CLIExecWithReturn(ctx, Cmd, argtable, false);
3471 uint8_t dati[250] = {0};
3472 uint8_t dato[250] = {0};
3473 int datilen = 0;
3474 CLIGetHexWithReturn(ctx, 1, dati, &datilen);
3475 uint8_t key[25] = {0};
3476 int keylen = 0;
3477 CLIGetHexWithReturn(ctx, 2, key, &keylen);
3478 int type = 0;
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};
3483 int ivlen = 0;
3484 CLIGetHexWithReturn(ctx, 6, iv, &ivlen);
3485 CLIParserFree(ctx);
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);
3492 return PM3_EINVARG;
3495 if (keylen != 8 && keylen != 16 && keylen != 24) {
3496 PrintAndLogEx(ERR, "<key> must be 8, 16 or 24 bytes. Got %d", keylen);
3497 return PM3_EINVARG;
3500 } else {
3502 if (datilen % 16 != 0 && ((type & 0x2) >> 1 == 0)) {
3503 PrintAndLogEx(ERR, "<data> length must be a multiple of 16. Got %d", datilen);
3504 return PM3_EINVARG;
3507 if (keylen != 16) {
3508 PrintAndLogEx(ERR, "<key> must be 16 bytes. Got %d", keylen);
3509 return PM3_EINVARG;
3513 // Encrypt(0) or decrypt(1)?
3514 if ((type & 0x8) >> 3) {
3516 if ((type & 0x4) >> 2) { // AES or DES?
3518 if (keylen > 8) {
3520 PrintAndLogEx(INFO, "Called 3DES decrypt");
3521 des3_decrypt(dato, dati, key, keylen / 8);
3523 } else {
3525 PrintAndLogEx(INFO, "Called DES decrypt");
3526 if (ivlen == 0) {
3527 // If there's an IV, use CBC
3528 des_decrypt_ecb(dato, dati, datilen, key);
3529 } else {
3530 des_decrypt_cbc(dato, dati, datilen, key, iv);
3533 } else {
3534 PrintAndLogEx(INFO, "Called AES decrypt");
3535 aes_decode(iv, key, dati, dato, datilen);
3538 } else {
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!");
3560 return PM3_SUCCESS;
3561 } else {
3563 if (keylen > 8) {
3564 PrintAndLogEx(INFO, "Called 3DES encrypt keysize: %i", keylen / 8);
3565 des3_encrypt(dato, dati, key, keylen / 8);
3566 } else {
3568 PrintAndLogEx(INFO, "Called DES encrypt");
3570 if (ivlen == 0) {
3571 // If there's an IV, use ECB
3572 des_encrypt_ecb(dato, dati, datilen, key);
3573 } else {
3574 des_encrypt_cbc(dato, dati, datilen, key, iv);
3575 char pad[250];
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);
3582 } else {
3584 if (type & 0x02) {
3585 PrintAndLogEx(INFO, "Called AES CMAC");
3586 // If we will calculate a MAC
3587 aes_cmac8(iv, key, dati, dato, datilen);
3588 } else {
3589 PrintAndLogEx(INFO, "Called AES encrypt");
3590 aes_encode(iv, key, dati, dato, datilen);
3594 PrintAndLogEx(SUCCESS, "Result: %s", sprint_hex(dato, datilen));
3595 return PM3_SUCCESS;
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",
3604 "data bmap -d 3B\n"
3605 "data bmap -d 3B -m 2,5,1\n"
3608 void *argtable[] = {
3609 arg_param_begin,
3610 arg_str0("d", NULL, "<hex>", "hex string"),
3611 arg_str0("m", NULL, "<str>", "binary template"),
3612 arg_param_end
3614 CLIExecWithReturn(ctx, Cmd, argtable, false);
3616 uint8_t hex[6];
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);
3623 CLIParserFree(ctx);
3625 char bits[(8 * 4) + 1] = {0};
3626 hextobinstring_n(bits, (char *)hex, hlen);
3628 if (tlen == 0) {
3629 template[0] = '8';
3630 template[1] = 0;
3633 char *token = strtok((char *)template, ",");
3635 // header
3636 PrintAndLogEx(INFO, "---+-------------------------");
3637 PrintAndLogEx(INFO, " | b0 b1 b2 b3 b4 b5 b6 b7");
3638 PrintAndLogEx(INFO, "---+-------------------------");
3640 uint8_t i = 0;
3641 uint8_t cnt = 1;
3642 int x = 0;
3643 while (token != NULL) {
3644 sscanf(token, "%d", &x);
3646 PrintAndLogEx(INFO, " %d | %.*s" NOLF, cnt, i * 3, " ");
3648 // incease with previous offset
3649 x += i;
3651 for (; i < (uint8_t)x; i++) {
3652 PrintAndLogEx(NORMAL, "%c " NOLF, bits[7 - i]);
3655 PrintAndLogEx(NORMAL, "");
3656 token = strtok(NULL, ",");
3657 cnt++;
3660 PrintAndLogEx(NORMAL, "");
3661 return PM3_SUCCESS;
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[] = {
3674 arg_param_begin,
3675 arg_str1("d", "data", "<hex>", "input hex string"),
3676 arg_str0("x", "xor", "<str>", "input xor string"),
3677 arg_param_end
3679 CLIExecWithReturn(ctx, Cmd, argtable, false);
3681 int hlen = 128;
3682 uint8_t hex[128 + 1];
3683 CLIGetHexWithReturn(ctx, 1, hex, &hlen);
3685 int xlen = 128;
3686 uint8_t xor[128 + 1];
3687 CLIGetHexWithReturn(ctx, 2, xor, &xlen);
3688 CLIParserFree(ctx);
3690 // find xor value
3691 if (xlen == 0) {
3692 uint8_t x = get_highest_frequency(hex, hlen);
3693 xlen = hlen;
3694 memset(xor, x, xlen);
3697 if (hlen != xlen) {
3698 PrintAndLogEx(FAILED, "Length mismatch, got %i != %i", hlen, xlen);
3699 return PM3_EINVARG;
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));
3706 return PM3_SUCCESS;
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)",
3713 "data test_ss8");
3714 void *argtable[] = {
3715 arg_param_begin,
3716 arg_param_end
3718 CLIExecWithReturn(ctx, Cmd, argtable, true);
3719 CLIParserFree(ctx);
3721 srand(time(NULL));
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);
3745 } else {
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]);
3752 free(srcBuffer);
3753 free(destBuffer);
3754 return PM3_EFAILED;
3756 PrintAndLogEx(DEBUG, "Index %lu matches: %u", i, destBuffer[i]);
3758 PrintAndLogEx(SUCCESS, _GREEN_("Save State (8-bit) test success!") "\n");
3760 free(srcBuffer);
3761 free(destBuffer);
3762 return PM3_SUCCESS;
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)",
3769 "data test_ss32");
3770 void *argtable[] = {
3771 arg_param_begin,
3772 arg_param_end
3774 CLIExecWithReturn(ctx, Cmd, argtable, true);
3775 CLIParserFree(ctx);
3777 srand(time(NULL));
3779 size_t length = 64;
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);
3799 free(srcBuffer);
3800 free(destBuffer);
3801 return PM3_EFAILED;
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]);
3808 free(srcBuffer);
3809 free(destBuffer);
3810 return PM3_EFAILED;
3812 PrintAndLogEx(DEBUG, "Index %lu matches: %i", i, destBuffer[i]);
3814 PrintAndLogEx(SUCCESS, _GREEN_("Save State (32-bit) test success!") "\n");
3816 free(srcBuffer);
3817 free(destBuffer);
3818 return PM3_SUCCESS;
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)",
3825 "data test_ss32s");
3826 void *argtable[] = {
3827 arg_param_begin,
3828 arg_param_end
3830 CLIExecWithReturn(ctx, Cmd, argtable, true);
3831 CLIParserFree(ctx);
3833 srand(time(NULL));
3835 size_t length = 64;
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);
3855 free(srcBuffer);
3856 free(destBuffer);
3857 return PM3_EFAILED;
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]);
3864 free(srcBuffer);
3865 free(destBuffer);
3866 return PM3_EFAILED;
3868 PrintAndLogEx(DEBUG, "Index %i matches: %i", i, destBuffer[i]);
3870 PrintAndLogEx(SUCCESS, _GREEN_("Save State (signed 32-bit) test success!") "\n");
3872 free(srcBuffer);
3873 free(destBuffer);
3874 return PM3_SUCCESS;
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);
3941 return PM3_SUCCESS;
3944 int CmdData(const char *Cmd) {
3945 clearCommandBuffer();
3946 return CmdsParse(CommandTable, Cmd);