1 //-----------------------------------------------------------------------------
2 // Copyright (C) 2009 Michael Gernoth <michael at gernoth.net>
3 // Copyright (C) 2010 iZsh <izsh at fail0verflow.com>
5 // This code is licensed to you under the terms of the GNU GPL, version 2 or,
6 // at your option, any later version. See the LICENSE.txt file for the text of
8 //-----------------------------------------------------------------------------
10 //-----------------------------------------------------------------------------
12 /* Ensure strtok_r is available even with -std=c99; must be included before
15 #define _POSIX_C_SOURCE 200112L
18 #include "commonutil.h" // ARRAYLEN
20 #include <stdio.h> // for Mingw readline
25 //Load readline after stdio.h
26 #include <readline/readline.h>
31 #include "proxmark3.h" // PROXLOG
32 #include "fileutils.h"
36 # include <direct.h> // _mkdir
41 #include "emojis_alt.h"
42 session_arg_t session
;
44 double CursorScaleFactor
= 1;
45 char CursorScaleFactorUnit
[11] = {0};
46 double PlotGridX
= 0, PlotGridY
= 0, PlotGridXdefault
= 64, PlotGridYdefault
= 64;
47 uint32_t CursorCPos
= 0, CursorDPos
= 0;
48 double GraphPixelsPerPoint
= 1.f
; // How many visual pixels are between each sample point (x axis)
49 static bool flushAfterWrite
= 0;
50 double GridOffset
= 0;
51 bool GridLocked
= false;
52 bool showDemod
= true;
54 pthread_mutex_t print_lock
= PTHREAD_MUTEX_INITIALIZER
;
56 static void fPrintAndLog(FILE *stream
, const char *fmt
, ...);
58 // needed by flasher, so let's put it here instead of fileutils.c
59 int searchHomeFilePath(char **foundpath
, const char *subdir
, const char *filename
, bool create_home
) {
60 if (foundpath
== NULL
)
63 const char *user_path
= get_my_user_directory();
64 if (user_path
== NULL
) {
65 fprintf(stderr
, "Could not retrieve $HOME from the environment\n");
69 size_t pathlen
= strlen(user_path
) + strlen(PM3_USER_DIRECTORY
) + 1;
70 char *path
= calloc(pathlen
, sizeof(char));
74 strcpy(path
, user_path
);
75 strcat(path
, PM3_USER_DIRECTORY
);
80 // Mingw _stat fails if path ends with /, so let's use a stripped path
81 if (path
[strlen(path
) - 1] == '/') {
82 path
[strlen(path
) - 1] = '\0';
83 result
= _stat(path
, &st
);
84 path
[strlen(path
)] = '/';
86 result
= _stat(path
, &st
);
90 result
= stat(path
, &st
);
92 if ((result
!= 0) && create_home
) {
97 if (mkdir(path
, 0700))
100 fprintf(stderr
, "Could not create user directory %s\n", path
);
105 if (subdir
!= NULL
) {
106 pathlen
+= strlen(subdir
);
107 char *tmp
= realloc(path
, pathlen
* sizeof(char));
113 strcat(path
, subdir
);
116 // Mingw _stat fails if path ends with /, so let's use a stripped path
117 if (path
[strlen(path
) - 1] == '/') {
118 path
[strlen(path
) - 1] = '\0';
119 result
= _stat(path
, &st
);
120 path
[strlen(path
)] = '/';
122 result
= _stat(path
, &st
);
125 result
= stat(path
, &st
);
127 if ((result
!= 0) && create_home
) {
132 if (mkdir(path
, 0700))
135 fprintf(stderr
, "Could not create user directory %s\n", path
);
142 if (filename
== NULL
) {
146 pathlen
+= strlen(filename
);
147 char *tmp
= realloc(path
, pathlen
* sizeof(char));
153 strcat(path
, filename
);
158 void PrintAndLogOptions(const char *str
[][2], size_t size
, size_t space
) {
159 char buff
[2000] = "Options:\n";
160 char format
[2000] = "";
161 size_t counts
[2] = {0, 0};
162 for (size_t i
= 0; i
< size
; i
++)
163 for (size_t j
= 0 ; j
< 2 ; j
++)
164 if (counts
[j
] < strlen(str
[i
][j
])) {
165 counts
[j
] = strlen(str
[i
][j
]);
167 for (size_t i
= 0; i
< size
; i
++) {
168 for (size_t j
= 0; j
< 2; j
++) {
170 snprintf(format
, sizeof(format
), "%%%zus%%%zus", space
, counts
[j
]);
172 snprintf(format
, sizeof(format
), "%%%zus%%-%zus", space
, counts
[j
]);
173 snprintf(buff
+ strlen(buff
), sizeof(buff
) - strlen(buff
), format
, " ", str
[i
][j
]);
176 strncat(buff
, "\n", sizeof(buff
) - strlen(buff
) - 1);
178 PrintAndLogEx(NORMAL
, "%s", buff
);
181 static uint8_t PrintAndLogEx_spinidx
= 0;
183 void PrintAndLogEx(logLevel_t level
, const char *fmt
, ...) {
185 // skip debug messages if client debugging is turned off i.e. 'DATA SETDEBUG -0'
186 if (g_debugMode
== 0 && level
== DEBUG
)
189 // skip HINT messages if client has hints turned off i.e. 'HINT 0'
190 if (session
.show_hints
== false && level
== HINT
)
193 char prefix
[40] = {0};
194 char buffer
[MAX_PRINT_BUFFER
] = {0};
195 char buffer2
[MAX_PRINT_BUFFER
+ sizeof(prefix
)] = {0};
197 char *tmp_ptr
= NULL
;
198 FILE *stream
= stdout
;
199 const char *spinner
[] = {_YELLOW_("[\\]"), _YELLOW_("[|]"), _YELLOW_("[/]"), _YELLOW_("[-]")};
200 const char *spinner_emoji
[] = {" :clock1: ", " :clock2: ", " :clock3: ", " :clock4: ", " :clock5: ", " :clock6: ",
201 " :clock7: ", " :clock8: ", " :clock9: ", " :clock10: ", " :clock11: ", " :clock12: "
205 if (session
.emoji_mode
== EMO_EMOJI
)
206 strncpy(prefix
, "[" _RED_("!!") "] :rotating_light: ", sizeof(prefix
) - 1);
208 strncpy(prefix
, "[" _RED_("!!") "] ", sizeof(prefix
) - 1);
212 if (session
.emoji_mode
== EMO_EMOJI
)
213 strncpy(prefix
, "[" _RED_("-") "] :no_entry: ", sizeof(prefix
) - 1);
215 strncpy(prefix
, "[" _RED_("-") "] ", sizeof(prefix
) - 1);
218 strncpy(prefix
, "[" _BLUE_("#") "] ", sizeof(prefix
) - 1);
221 strncpy(prefix
, "[" _YELLOW_("?") "] ", sizeof(prefix
) - 1);
224 strncpy(prefix
, "[" _GREEN_("+") "] ", sizeof(prefix
) - 1);
227 if (session
.emoji_mode
== EMO_EMOJI
)
228 strncpy(prefix
, "[" _CYAN_("!") "] :warning: ", sizeof(prefix
) - 1);
230 strncpy(prefix
, "[" _CYAN_("!") "] ", sizeof(prefix
) - 1);
233 strncpy(prefix
, "[" _YELLOW_("=") "] ", sizeof(prefix
) - 1);
236 if (session
.emoji_mode
== EMO_EMOJI
) {
237 strncpy(prefix
, spinner_emoji
[PrintAndLogEx_spinidx
], sizeof(prefix
) - 1);
238 PrintAndLogEx_spinidx
++;
239 if (PrintAndLogEx_spinidx
>= ARRAYLEN(spinner_emoji
))
240 PrintAndLogEx_spinidx
= 0;
242 strncpy(prefix
, spinner
[PrintAndLogEx_spinidx
], sizeof(prefix
) - 1);
243 PrintAndLogEx_spinidx
++;
244 if (PrintAndLogEx_spinidx
>= ARRAYLEN(spinner
))
245 PrintAndLogEx_spinidx
= 0;
249 // no prefixes for normal
255 vsnprintf(buffer
, sizeof(buffer
), fmt
, args
);
258 // no prefixes for normal & inplace
259 if (level
== NORMAL
) {
260 fPrintAndLog(stream
, "%s", buffer
);
264 if (strchr(buffer
, '\n')) {
266 const char delim
[2] = "\n";
268 // line starts with newline
269 if (buffer
[0] == '\n')
270 fPrintAndLog(stream
, "");
272 token
= strtok_r(buffer
, delim
, &tmp_ptr
);
274 while (token
!= NULL
) {
276 size_t size
= strlen(buffer2
);
279 snprintf(buffer2
+ size
, sizeof(buffer2
) - size
, "%s%s\n", prefix
, token
);
281 snprintf(buffer2
+ size
, sizeof(buffer2
) - size
, "\n");
283 token
= strtok_r(NULL
, delim
, &tmp_ptr
);
285 fPrintAndLog(stream
, "%s", buffer2
);
287 snprintf(buffer2
, sizeof(buffer2
), "%s%s", prefix
, buffer
);
288 if (level
== INPLACE
) {
289 char buffer3
[sizeof(buffer2
)] = {0};
290 char buffer4
[sizeof(buffer2
)] = {0};
291 memcpy_filter_ansi(buffer3
, buffer2
, sizeof(buffer2
), !session
.supports_colors
);
292 memcpy_filter_emoji(buffer4
, buffer3
, sizeof(buffer3
), session
.emoji_mode
);
293 fprintf(stream
, "\r%s", buffer4
);
296 fPrintAndLog(stream
, "%s", buffer2
);
301 static void fPrintAndLog(FILE *stream
, const char *fmt
, ...) {
303 static FILE *logfile
= NULL
;
304 static int logging
= 1;
305 char buffer
[MAX_PRINT_BUFFER
] = {0};
306 char buffer2
[MAX_PRINT_BUFFER
] = {0};
307 char buffer3
[MAX_PRINT_BUFFER
] = {0};
308 // lock this section to avoid interlacing prints from different threads
309 pthread_mutex_lock(&print_lock
);
310 bool linefeed
= true;
312 if (logging
&& session
.incognito
) {
315 if ((g_printAndLog
& PRINTANDLOG_LOG
) && logging
&& !logfile
) {
316 char *my_logfile_path
= NULL
;
319 time_t now
= time(NULL
);
320 timenow
= gmtime(&now
);
321 strftime(filename
, sizeof(filename
), PROXLOG
, timenow
);
322 if (searchHomeFilePath(&my_logfile_path
, LOGS_SUBDIR
, filename
, true) != PM3_SUCCESS
) {
323 printf(_YELLOW_("[-]") " Logging disabled!\n");
324 my_logfile_path
= NULL
;
327 logfile
= fopen(my_logfile_path
, "a");
328 if (logfile
== NULL
) {
329 printf(_YELLOW_("[-]") " Can't open logfile %s, logging disabled!\n", my_logfile_path
);
333 if (session
.supports_colors
) {
334 printf("["_YELLOW_("=")"] Session log " _YELLOW_("%s") "\n", my_logfile_path
);
336 printf("[=] Session log %s\n", my_logfile_path
);
340 free(my_logfile_path
);
345 // If there is an incoming message from the hardware (eg: lf hid read) in
346 // the background (while the prompt is displayed and accepting user input),
347 // stash the prompt and bring it back later.
348 #ifdef RL_STATE_READCMD
349 // We are using GNU readline. libedit (OSX) doesn't support this flag.
350 int need_hack
= (rl_readline_state
& RL_STATE_READCMD
) > 0;
355 saved_point
= rl_point
;
356 saved_line
= rl_copy_text(0, rl_end
);
358 rl_replace_line("", 0);
363 va_start(argptr
, fmt
);
364 vsnprintf(buffer
, sizeof(buffer
), fmt
, argptr
);
366 if (strlen(buffer
) > 0 && buffer
[strlen(buffer
) - 1] == NOLF
[0]) {
368 buffer
[strlen(buffer
) - 1] = 0;
370 bool filter_ansi
= !session
.supports_colors
;
371 memcpy_filter_ansi(buffer2
, buffer
, sizeof(buffer
), filter_ansi
);
372 if (g_printAndLog
& PRINTANDLOG_PRINT
) {
373 memcpy_filter_emoji(buffer3
, buffer2
, sizeof(buffer2
), session
.emoji_mode
);
374 fprintf(stream
, "%s", buffer3
);
376 fprintf(stream
, "\n");
379 #ifdef RL_STATE_READCMD
380 // We are using GNU readline. libedit (OSX) doesn't support this flag.
383 rl_replace_line(saved_line
, 0);
384 rl_point
= saved_point
;
390 if ((g_printAndLog
& PRINTANDLOG_LOG
) && logging
&& logfile
) {
391 memcpy_filter_emoji(buffer3
, buffer2
, sizeof(buffer2
), EMO_ALTTEXT
);
392 if (filter_ansi
) { // already done
393 fprintf(logfile
, "%s", buffer3
);
395 memcpy_filter_ansi(buffer
, buffer3
, sizeof(buffer3
), true);
396 fprintf(logfile
, "%s", buffer
);
399 fprintf(logfile
, "\n");
407 pthread_mutex_unlock(&print_lock
);
410 void SetFlushAfterWrite(bool value
) {
411 flushAfterWrite
= value
;
414 void memcpy_filter_rlmarkers(void *dest
, const void *src
, size_t n
) {
415 uint8_t *rdest
= (uint8_t *)dest
;
416 uint8_t *rsrc
= (uint8_t *)src
;
418 for (uint16_t i
= 0; i
< n
; i
++) {
419 if ((rsrc
[i
] == '\001') || (rsrc
[i
] == '\002'))
420 // skip readline special markers
422 rdest
[si
++] = rsrc
[i
];
426 void memcpy_filter_ansi(void *dest
, const void *src
, size_t n
, bool filter
) {
428 // Filter out ANSI sequences on these OS
429 uint8_t *rdest
= (uint8_t *)dest
;
430 uint8_t *rsrc
= (uint8_t *)src
;
432 for (uint16_t i
= 0; i
< n
; i
++) {
434 && (rsrc
[i
] == '\x1b')
435 && (rsrc
[i
+ 1] >= 0x40)
436 && (rsrc
[i
+ 1] <= 0x5F)) { // entering ANSI sequence
439 if ((i
< n
- 1) && (rsrc
[i
] == '[')) { // entering CSI sequence
442 while ((i
< n
- 1) && (rsrc
[i
] >= 0x30) && (rsrc
[i
] <= 0x3F)) { // parameter bytes
446 while ((i
< n
- 1) && (rsrc
[i
] >= 0x20) && (rsrc
[i
] <= 0x2F)) { // intermediate bytes
450 if ((rsrc
[i
] >= 0x40) && (rsrc
[i
] <= 0x7F)) { // final byte
457 rdest
[si
++] = rsrc
[i
];
460 memcpy(dest
, src
, n
);
464 static bool emojify_token(const char *token
, uint8_t token_length
, const char **emojified_token
, uint8_t *emojified_token_length
, emojiMode_t mode
) {
466 while (EmojiTable
[i
].alias
&& EmojiTable
[i
].emoji
) {
467 if ((strlen(EmojiTable
[i
].alias
) == token_length
) && (0 == memcmp(EmojiTable
[i
].alias
, token
, token_length
))) {
470 *emojified_token
= EmojiTable
[i
].emoji
;
471 *emojified_token_length
= strlen(EmojiTable
[i
].emoji
);
476 *emojified_token_length
= 0;
477 while (EmojiAltTable
[j
].alias
&& EmojiAltTable
[j
].alttext
) {
478 if ((strlen(EmojiAltTable
[j
].alias
) == token_length
) && (0 == memcmp(EmojiAltTable
[j
].alias
, token
, token_length
))) {
479 *emojified_token
= EmojiAltTable
[j
].alttext
;
480 *emojified_token_length
= strlen(EmojiAltTable
[j
].alttext
);
488 *emojified_token_length
= 0;
491 case EMO_ALIAS
: { // should never happen
502 static bool token_charset(uint8_t c
) {
503 if ((c
>= '0') && (c
<= '9')) return true;
504 if ((c
>= 'a') && (c
<= 'z')) return true;
505 if ((c
>= 'A') && (c
<= 'Z')) return true;
506 if ((c
== '_') || (c
== '+') || (c
== '-')) return true;
510 void memcpy_filter_emoji(void *dest
, const void *src
, size_t n
, emojiMode_t mode
) {
511 if (mode
== EMO_ALIAS
) {
512 memcpy(dest
, src
, n
);
515 const char *emojified_token
= NULL
;
516 uint8_t emojified_token_length
= 0;
517 char *current_token
= NULL
;
518 uint8_t current_token_length
= 0;
520 char *rdest
= (char *)dest
;
521 char *rsrc
= (char *)src
;
523 for (uint16_t i
= 0; i
< n
; i
++) {
524 current_char
= rsrc
[i
];
526 if (current_token_length
== 0) {
527 // starting a new token.
528 if (current_char
== ':') {
529 current_token
= rsrc
+ i
;
530 current_token_length
= 1;
531 } else { // not starting a new token.
532 rdest
[si
++] = current_char
;
535 // finishing the current token.
536 if (current_char
== ':') {
537 // nothing changed? we still need the ending ':' as it might serve for an upcoming emoji
538 if (! emojify_token(current_token
, current_token_length
+ 1, &emojified_token
, &emojified_token_length
, mode
)) {
539 memcpy(rdest
+ si
, current_token
, current_token_length
);
540 si
+= current_token_length
;
541 current_token
= rsrc
+ i
;
542 current_token_length
= 1;
544 memcpy(rdest
+ si
, emojified_token
, emojified_token_length
);
545 si
+= emojified_token_length
;
546 current_token_length
= 0;
548 } else if (token_charset(current_char
)) { // continuing the current token.
549 current_token_length
++;
550 } else { // dropping the current token.
551 current_token_length
++;
552 memcpy(rdest
+ si
, current_token
, current_token_length
);
553 si
+= current_token_length
;
554 current_token_length
= 0;
558 memcpy(rdest
+ si
, current_token
, current_token_length
);
563 // If reactivated, beware it doesn't compile on Android (DXL)
564 void iceIIR_Butterworth(int *data, const size_t len) {
566 int *output = (int *) calloc(sizeof(int) * len, sizeof(uint8_t));
570 memset(output, 0x00, len);
572 size_t adjustedLen = len;
573 float fc = 0.1125f; // center frequency
575 // create very simple low-pass filter to remove images (2nd-order Butterworth)
576 float complex iir_buf[3] = {0, 0, 0};
577 float b[3] = {0.003621681514929, 0.007243363029857, 0.003621681514929};
578 float a[3] = {1.000000000000000, -1.822694925196308, 0.837181651256023};
580 for (size_t i = 0; i < adjustedLen; ++i) {
582 float sample = data[i]; // input sample read from array
583 float complex x_prime = 1.0f; // save sample for estimating frequency
586 // remove DC offset and mix to complex baseband
587 x = (sample - 127.5f) * cexpf(_Complex_I * 2 * M_PI * fc * i);
589 // apply low-pass filter, removing spectral image (IIR using direct-form II)
590 iir_buf[2] = iir_buf[1];
591 iir_buf[1] = iir_buf[0];
592 iir_buf[0] = x - a[1] * iir_buf[1] - a[2] * iir_buf[2];
593 x = b[0] * iir_buf[0] +
597 // compute instantaneous frequency by looking at phase difference
598 // between adjacent samples
599 float freq = cargf(x * conjf(x_prime));
600 x_prime = x; // retain this sample for next iteration
602 output[i] = (freq > 0) ? 127 : -127;
606 //memcpy(data, output, adjustedLen);
607 for (size_t j = 0; j < adjustedLen; ++j)
614 void iceSimple_Filter(int *data
, const size_t len
, uint8_t k
) {
615 // ref: http://www.edn.com/design/systems-design/4320010/A-simple-software-lowpass-filter-suits-embedded-system-applications
617 #define FILTER_SHIFT 4
619 int32_t filter_reg
= 0;
620 int8_t shift
= (k
<= 8) ? k
: FILTER_SHIFT
;
622 for (size_t i
= 0; i
< len
; ++i
) {
623 // Update filter with current sample
624 filter_reg
= filter_reg
- (filter_reg
>> shift
) + data
[i
];
626 // Scale output for unity gain
627 data
[i
] = filter_reg
>> shift
;
631 void print_progress(size_t count
, uint64_t max
, barMode_t style
) {
634 static int prev_cols
= 0;
636 rl_reset_screen_size(); // refresh Readline idea of the actual screen width
637 rl_get_screen_size(&rows
, &cols
);
639 if (prev_cols
> cols
) {
640 PrintAndLogEx(NORMAL
, _CLEAR_ _TOP_
"");
644 int width
= cols
- 35;
648 #define PERCENTAGE(V, T) ((V * width) / T)
649 // x/8 fractional part of the percentage
650 #define PERCENTAGEFRAC(V, T) ((uint8_t)(((((float)V * width) / T) - ((V * width) / T)) * 8))
652 const char *smoothtable
[] = {
664 int mode
= (session
.emoji_mode
== EMO_EMOJI
);
666 const char *block
[] = {"#", "\xe2\x96\x88"};
667 // use a 3-byte space in emoji mode to ease computations
668 const char *space
[] = {" ", "\xe2\x80\x80"};
670 size_t unit
= strlen(block
[mode
]);
672 char *bar
= (char *)calloc(unit
* width
+ 1, sizeof(uint8_t));
674 uint8_t value
= PERCENTAGE(count
, max
);
677 // prefix is added already.
678 for (; i
< unit
* value
; i
+= unit
) {
679 memcpy(bar
+ i
, block
[mode
], unit
);
683 memcpy(bar
+ i
, smoothtable
[PERCENTAGEFRAC(count
, max
)], unit
);
685 memcpy(bar
+ i
, space
[mode
], unit
);
689 for (; i
< unit
* width
; i
+= unit
) {
690 memcpy(bar
+ i
, space
[mode
], unit
);
693 size_t collen
= strlen(bar
) + 40;
694 char *cbar
= (char *)calloc(collen
, sizeof(uint8_t));
697 if (session
.supports_colors
) {
698 int p60
= unit
* (width
* 60 / 100);
699 int p20
= unit
* (width
* 20 / 100);
700 snprintf(cbar
, collen
, _GREEN_("%.*s"), p60
, bar
);
701 snprintf(cbar
+ strlen(cbar
), collen
- strlen(cbar
), _CYAN_("%.*s"), p20
, bar
+ p60
);
702 snprintf(cbar
+ strlen(cbar
), collen
- strlen(cbar
), _YELLOW_("%.*s"), (int)(unit
* width
- p60
- p20
), bar
+ p60
+ p20
);
704 snprintf(cbar
, collen
, "%s", bar
);
707 size_t olen
= strlen(cbar
) + 40;
708 char *out
= (char *)calloc(olen
, sizeof(uint8_t));
712 sprintf(out
, "%s", cbar
);
713 printf("\b%c[2K\r[" _YELLOW_("=")"] %s", 27, out
);
717 sprintf(out
, "%s [ %zu mV / %2u V / %2u Vmax ]", cbar
, count
, (uint32_t)(count
/ 1000), (uint32_t)(max
/ 1000));
718 printf("\b%c[2K\r[" _YELLOW_("=")"] %s", 27, out
);
722 printf("[" _YELLOW_("=")"] %zu mV / %2u V / %2u Vmax \r", count
, (uint32_t)(count
/ 1000), (uint32_t)(max
/ 1000));