Release v4.14434 - crimson
[RRG-proxmark3.git] / client / src / proxmark3.c
blob5de8f190cfc47c312d8669527cc6d0a43f64af39
1 //-----------------------------------------------------------------------------
2 // Copyright (C) 2009 Michael Gernoth <michael at gernoth.net>
3 // Copyright (C) 2010 iZsh <izsh at fail0verflow.com>
4 //
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
7 // the license.
8 //-----------------------------------------------------------------------------
9 // Main binary
10 //-----------------------------------------------------------------------------
12 #include "proxmark3.h"
14 #include <stdlib.h>
15 #include <stdio.h> // for Mingw readline
16 #include <limits.h>
17 #include <unistd.h>
18 #ifdef HAVE_READLINE
19 #include <readline/readline.h>
20 #include <readline/history.h>
21 #include <signal.h>
22 #endif
23 #include <ctype.h>
24 #include <libgen.h> // basename
26 #include "usart_defs.h"
27 #include "util_posix.h"
28 #include "proxgui.h"
29 #include "cmdmain.h"
30 #include "ui.h"
31 #include "cmdhw.h"
32 #include "whereami.h"
33 #include "comms.h"
34 #include "fileutils.h"
35 #include "flash.h"
36 #include "preferences.h"
38 #ifndef LIBPM3
39 #define BANNERMSG1 ""
40 #define BANNERMSG2 " [ :snowflake: Iceman :snowflake: ]"
41 #define BANNERMSG3 "Release v4.14434 - crimson"
43 typedef enum LogoMode { UTF8, ANSI, ASCII } LogoMode;
45 static void showBanner_logo(LogoMode mode) {
46 switch (mode) {
47 case UTF8: {
48 const char *sq = "\xE2\x96\x88"; // square block
49 const char *tr = "\xE2\x95\x97"; // top right corner
50 const char *tl = "\xE2\x95\x94"; // top left corner
51 const char *br = "\xE2\x95\x9D"; // bottom right corner
52 const char *bl = "\xE2\x95\x9A"; // bottom left corner
53 const char *hl = "\xE2\x95\x90"; // horiz line
54 const char *vl = "\xE2\x95\x91"; // vert line
55 const char *__ = " ";
57 PrintAndLogEx(NORMAL, " " _BLUE_("%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s"),
58 sq, sq, sq, sq, sq, sq, tr, __, sq, sq, sq, tr, __, __, __, sq, sq, sq, tr, sq, sq, sq, sq, sq, tr, __);
59 PrintAndLogEx(NORMAL, " " _BLUE_("%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s"),
60 sq, sq, tl, hl, hl, sq, sq, tr, sq, sq, sq, sq, tr, __, sq, sq, sq, sq, vl, bl, hl, hl, hl, sq, sq, tr);
61 PrintAndLogEx(NORMAL, " " _BLUE_("%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s"),
62 sq, sq, sq, sq, sq, sq, tl, br, sq, sq, tl, sq, sq, sq, sq, tl, sq, sq, vl, __, sq, sq, sq, sq, tl, br);
63 PrintAndLogEx(NORMAL, " " _BLUE_("%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s"),
64 sq, sq, tl, hl, hl, hl, br, __, sq, sq, vl, bl, sq, sq, tl, br, sq, sq, vl, __, bl, hl, hl, sq, sq, tr);
65 PrintAndLogEx(NORMAL, " " _BLUE_("%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s")" " BANNERMSG1,
66 sq, sq, vl, __, __, __, __, __, sq, sq, vl, __, bl, hl, br, __, sq, sq, vl, sq, sq, sq, sq, sq, tl, br);
67 PrintAndLogEx(NORMAL, " " _BLUE_("%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s")" " BANNERMSG2,
68 bl, hl, br, __, __, __, __, __, bl, hl, br, __, __, __, __, __, bl, hl, br, bl, hl, hl, hl, hl, br, __);
69 break;
71 case ANSI: {
72 PrintAndLogEx(NORMAL, " " _BLUE_("██████╗ ███╗ ███╗█████╗ "));
73 PrintAndLogEx(NORMAL, " " _BLUE_("██╔══██╗████╗ ████║╚═══██╗"));
74 PrintAndLogEx(NORMAL, " " _BLUE_("██████╔╝██╔████╔██║ ████╔╝"));
75 PrintAndLogEx(NORMAL, " " _BLUE_("██╔═══╝ ██║╚██╔╝██║ ╚══██╗"));
76 PrintAndLogEx(NORMAL, " " _BLUE_("██║ ██║ ╚═╝ ██║█████╔╝") " " BANNERMSG1);
77 PrintAndLogEx(NORMAL, " " _BLUE_("╚═╝ ╚═╝ ╚═╝╚════╝ ") " " BANNERMSG2);
78 break;
80 case ASCII: {
81 PrintAndLogEx(NORMAL, " ######. ###. ###.#####. ");
82 PrintAndLogEx(NORMAL, " ##...##.####. ####. ...##.");
83 PrintAndLogEx(NORMAL, " ######..##.####.##. ####..");
84 PrintAndLogEx(NORMAL, " ##..... ##..##..##. ..##.");
85 PrintAndLogEx(NORMAL, " ##. ##. .. ##.#####.. " BANNERMSG1);
86 PrintAndLogEx(NORMAL, " .. .. .. ..... " BANNERMSG2);
87 break;
90 PrintAndLogEx(NORMAL, "");
91 PrintAndLogEx(NORMAL, BANNERMSG3);
94 static void showBanner(void) {
96 g_printAndLog = PRINTANDLOG_PRINT;
97 PrintAndLogEx(NORMAL, "\n");
99 #if defined(_WIN32)
100 if (GetConsoleCP() == 65001) {
101 // If on Windows and using UTF-8 then we need utf-8 ascii art for banner.
102 showBanner_logo(UTF8);
103 } else {
104 showBanner_logo(ANSI);
106 #elif defined(__linux__) || defined(__APPLE__)
107 showBanner_logo(ANSI);
108 #else
109 showBanner_logo(ASCII);
110 #endif
111 // PrintAndLogEx(NORMAL, "\nSupport iceman on patreon - https://www.patreon.com/iceman1001/");
112 // PrintAndLogEx(NORMAL, " on paypal - https://www.paypal.me/iceman1001");
113 // PrintAndLogEx(NORMAL, "\nMonero: 43mNJLpgBVaTvyZmX9ajcohpvVkaRy1kbZPm8tqAb7itZgfuYecgkRF36rXrKFUkwEGeZedPsASRxgv4HPBHvJwyJdyvQuP");
114 PrintAndLogEx(NORMAL, "");
115 fflush(stdout);
116 g_printAndLog = PRINTANDLOG_PRINT | PRINTANDLOG_LOG;
118 #endif //LIBPM3
120 static const char *prompt_dev = "";
121 static const char *prompt_ctx = "";
123 static void prompt_compose(char *buf, size_t buflen, const char *promptctx, const char *promptdev) {
124 snprintf(buf, buflen - 1, PROXPROMPT_COMPOSE, promptdev, promptctx);
127 static int check_comm(void) {
128 // If communications thread goes down. Device disconnected then this should hook up PM3 again.
129 if (IsCommunicationThreadDead() && g_session.pm3_present) {
130 PrintAndLogEx(INFO, "Running in " _YELLOW_("OFFLINE") " mode. Use "_YELLOW_("\"hw connect\"") " to reconnect\n");
131 prompt_dev = PROXPROMPT_DEV_OFFLINE;
132 #ifdef HAVE_READLINE
133 char prompt[PROXPROMPT_MAX_SIZE] = {0};
134 prompt_compose(prompt, sizeof(prompt), prompt_ctx, prompt_dev);
135 char prompt_filtered[PROXPROMPT_MAX_SIZE] = {0};
136 memcpy_filter_ansi(prompt_filtered, prompt, sizeof(prompt_filtered), !g_session.supports_colors);
137 rl_set_prompt(prompt_filtered);
138 rl_redisplay();
139 #endif
140 CloseProxmark(g_session.current_device);
142 msleep(10);
143 return 0;
145 #ifdef HAVE_READLINE
146 static void flush_history(void) {
147 if (g_session.history_path) {
148 write_history(g_session.history_path);
149 free(g_session.history_path);
153 # if defined(_WIN32)
155 static bool WINAPI terminate_handler(DWORD t) {
156 if (t == CTRL_C_EVENT) {
157 flush_history();
158 return true;
160 return false;
163 # else
164 static struct sigaction gs_old_sigint_action;
165 static void sigint_handler(int signum) {
166 sigaction(SIGINT, &gs_old_sigint_action, NULL);
167 flush_history();
168 kill(0, SIGINT);
170 #endif
172 #endif
174 #if defined(_WIN32)
175 static bool DetectWindowsAnsiSupport(void) {
176 #ifndef ENABLE_VIRTUAL_TERMINAL_PROCESSING
177 #define ENABLE_VIRTUAL_TERMINAL_PROCESSING 0x0004
178 #endif
180 // disable colors if stdin or stdout are redirected
181 if ((! g_session.stdinOnTTY) || (! g_session.stdoutOnTTY))
182 return false;
184 HANDLE hOut = GetStdHandle(STD_OUTPUT_HANDLE);
185 DWORD dwMode = 0;
186 GetConsoleMode(hOut, &dwMode);
188 //ENABLE_VIRTUAL_TERMINAL_PROCESSING is already set
189 if ((dwMode & ENABLE_VIRTUAL_TERMINAL_PROCESSING))
190 return true;
192 dwMode |= ENABLE_VIRTUAL_TERMINAL_PROCESSING;
194 return SetConsoleMode(hOut, dwMode) ? true : false;
196 #endif //_WIN32
198 // first slot is always NULL, indicating absence of script when idx=0
199 static FILE *cmdscriptfile[MAX_NESTED_CMDSCRIPT + 1] = {0};
200 static uint8_t cmdscriptfile_idx = 0;
201 static bool cmdscriptfile_stayafter = false;
203 int push_cmdscriptfile(char *path, bool stayafter) {
204 if (cmdscriptfile_idx == MAX_NESTED_CMDSCRIPT) {
205 PrintAndLogEx(ERR, "Too many nested scripts, skipping %s\n", path);
206 return PM3_EMALLOC;
209 FILE *f = fopen(path, "r");
210 if (f == NULL)
211 return PM3_EFILE;
213 if (cmdscriptfile_idx == 0)
214 cmdscriptfile_stayafter = stayafter;
216 cmdscriptfile[++cmdscriptfile_idx] = f;
217 return PM3_SUCCESS;
220 static FILE *current_cmdscriptfile(void) {
221 return cmdscriptfile[cmdscriptfile_idx];
224 static bool pop_cmdscriptfile(void) {
225 fclose(cmdscriptfile[cmdscriptfile_idx]);
226 cmdscriptfile[cmdscriptfile_idx--] = NULL;
227 if (cmdscriptfile_idx == 0)
228 return cmdscriptfile_stayafter;
229 else
230 return true;
233 // Main thread of PM3 Client
234 void
235 #ifdef __has_attribute
236 #if __has_attribute(force_align_arg_pointer)
237 __attribute__((force_align_arg_pointer))
238 #endif
239 #endif
240 main_loop(char *script_cmds_file, char *script_cmd, bool stayInCommandLoop) {
242 char *cmd = NULL;
243 bool execCommand = (script_cmd != NULL);
244 bool fromInteractive = false;
245 uint16_t script_cmd_len = 0;
246 if (execCommand) {
247 script_cmd_len = strlen(script_cmd);
248 strcreplace(script_cmd, script_cmd_len, ';', '\0');
250 bool stdinOnPipe = !isatty(STDIN_FILENO);
251 char script_cmd_buf[256] = {0x00}; // iceman, needs lua script the same file_path_buffer as the rest
253 if (g_session.pm3_present) {
254 // cache Version information now:
255 if (execCommand || script_cmds_file || stdinOnPipe)
256 pm3_version(false, false);
257 else
258 pm3_version(true, false);
261 if (script_cmds_file) {
263 char *path;
264 int res = searchFile(&path, CMD_SCRIPTS_SUBDIR, script_cmds_file, ".cmd", false);
265 if (res == PM3_SUCCESS) {
266 if (push_cmdscriptfile(path, stayInCommandLoop) == PM3_SUCCESS)
267 PrintAndLogEx(SUCCESS, "executing commands from file: %s\n", path);
268 else
269 PrintAndLogEx(ERR, "could not open " _YELLOW_("%s") "...", path);
270 free(path);
274 #ifdef HAVE_READLINE
275 g_session.history_path = NULL;
276 if (g_session.incognito) {
277 PrintAndLogEx(INFO, "No history will be recorded");
278 } else {
279 if (searchHomeFilePath(&g_session.history_path, NULL, PROXHISTORY, true) != PM3_SUCCESS) {
280 PrintAndLogEx(ERR, "No history will be recorded");
281 g_session.history_path = NULL;
282 } else {
284 # if defined(_WIN32)
285 // SetConsoleCtrlHandler((PHANDLER_ROUTINE)terminate_handler, true);
286 # else
287 struct sigaction action;
288 memset(&action, 0, sizeof(action));
289 action.sa_handler = &sigint_handler;
290 sigaction(SIGINT, &action, &gs_old_sigint_action);
291 # endif
292 rl_catch_signals = 1;
293 rl_set_signals();
294 read_history(g_session.history_path);
297 #endif
299 // loops every time enter is pressed...
300 while (1) {
302 bool printprompt = false;
303 if (g_session.pm3_present) {
304 if (g_conn.send_via_fpc_usart == false)
305 prompt_dev = PROXPROMPT_DEV_USB;
306 else
307 prompt_dev = PROXPROMPT_DEV_FPC;
308 } else {
309 prompt_dev = PROXPROMPT_DEV_OFFLINE;
312 check_script:
313 // If there is a script file
314 if (current_cmdscriptfile()) {
316 // clear array
317 memset(script_cmd_buf, 0, sizeof(script_cmd_buf));
319 // read script file
320 if (fgets(script_cmd_buf, sizeof(script_cmd_buf), current_cmdscriptfile()) == NULL) {
321 if (!pop_cmdscriptfile())
322 break;
324 goto check_script;
325 } else {
326 prompt_ctx = PROXPROMPT_CTX_SCRIPTFILE;
327 // remove linebreaks
328 strcleanrn(script_cmd_buf, sizeof(script_cmd_buf));
330 cmd = str_dup(script_cmd_buf);
331 if (cmd != NULL)
332 printprompt = true;
334 } else {
335 // If there is a script command
336 if (execCommand) {
337 prompt_ctx = stdinOnPipe ? PROXPROMPT_CTX_STDIN : PROXPROMPT_CTX_SCRIPTCMD;
339 cmd = str_dup(script_cmd);
340 if ((cmd != NULL) && (! fromInteractive))
341 printprompt = true;
343 uint16_t len = strlen(script_cmd) + 1;
344 script_cmd += len;
346 if (script_cmd_len == len - 1)
347 execCommand = false;
349 script_cmd_len -= len;
350 } else {
351 // exit after exec command
352 if (script_cmd && !stayInCommandLoop)
353 break;
355 // if there is a pipe from stdin
356 if (stdinOnPipe) {
357 // clear array
358 memset(script_cmd_buf, 0, sizeof(script_cmd_buf));
359 // get
360 if (fgets(script_cmd_buf, sizeof(script_cmd_buf), stdin) == NULL) {
361 PrintAndLogEx(ERR, "STDIN unexpected end, exit...");
362 break;
364 execCommand = true;
365 stayInCommandLoop = true;
366 fromInteractive = false;
367 script_cmd = script_cmd_buf;
368 script_cmd_len = strlen(script_cmd);
369 strcreplace(script_cmd, script_cmd_len, ';', '\0');
370 // remove linebreaks
371 strcleanrn(script_cmd, script_cmd_len);
372 goto check_script;
373 } else {
374 #ifdef HAVE_READLINE
375 rl_event_hook = check_comm;
376 #else
377 check_comm();
378 #endif
379 prompt_ctx = PROXPROMPT_CTX_INTERACTIVE;
380 char prompt[PROXPROMPT_MAX_SIZE] = {0};
381 prompt_compose(prompt, sizeof(prompt), prompt_ctx, prompt_dev);
382 char prompt_filtered[PROXPROMPT_MAX_SIZE] = {0};
383 memcpy_filter_ansi(prompt_filtered, prompt, sizeof(prompt_filtered), !g_session.supports_colors);
384 g_pendingPrompt = true;
385 #ifdef HAVE_READLINE
386 script_cmd = readline(prompt_filtered);
387 #if defined(_WIN32)
388 //Check if color support needs to be enabled again in case the window buffer did change
389 g_session.supports_colors = DetectWindowsAnsiSupport();
390 #endif
391 if (script_cmd != NULL) {
392 execCommand = true;
393 stayInCommandLoop = true;
394 fromInteractive = true;
395 script_cmd_len = strlen(script_cmd);
396 strcreplace(script_cmd, script_cmd_len, ';', '\0');
397 // remove linebreaks
398 strcleanrn(script_cmd, script_cmd_len);
399 goto check_script;
401 #else
402 printf("%s", prompt_filtered);
403 cmd = NULL;
404 size_t len = 0;
405 int ret;
406 if ((ret = getline(&cmd, &len, stdin)) < 0) {
407 // TODO this happens also when kbd_enter_pressed() is used, with a key pressed or not
408 printf("GETLINE ERR %i", ret);
409 free(cmd);
410 cmd = NULL;
412 #endif
413 fflush(NULL);
418 // execute command
419 if (cmd) {
421 // rtrim
422 size_t l = strlen(cmd);
423 while (l > 0 && isspace(cmd[l - 1])) {
424 cmd[--l] = '\0';
426 // ltrim
427 size_t off = 0;
428 while ((cmd[off] != '\0') && isspace(cmd[off])) {
429 off++;
432 for (size_t i = 0; i < strlen(cmd) - off; i++) {
433 cmd[i] = cmd[i + off];
436 cmd[strlen(cmd) - off] = '\0';
438 if (cmd[0] != '\0') {
439 if (!printprompt) {
440 g_printAndLog = PRINTANDLOG_LOG;
442 char prompt[PROXPROMPT_MAX_SIZE] = {0};
443 prompt_compose(prompt, sizeof(prompt), prompt_ctx, prompt_dev);
444 // always filter RL magic separators if not using readline
445 char prompt_filtered[PROXPROMPT_MAX_SIZE] = {0};
446 memcpy_filter_rlmarkers(prompt_filtered, prompt, sizeof(prompt_filtered));
447 PrintAndLogEx(NORMAL, "%s%s", prompt_filtered, cmd);
448 g_printAndLog = PRINTANDLOG_PRINT | PRINTANDLOG_LOG;
450 #ifdef HAVE_READLINE
451 // add to history if not from a script
452 if (!current_cmdscriptfile()) {
453 HIST_ENTRY *entry = history_get(history_length);
454 // add if not identical to latest recorded cmd
455 if ((!entry) || (strcmp(entry->line, cmd) != 0)) {
456 add_history(cmd);
459 #endif
460 // process cmd
461 g_pendingPrompt = false;
462 int ret = CommandReceived(cmd);
463 #if defined ICOPYX
464 PrintAndLogEx(NORMAL, "\nNikola.D: %d", ret);
465 #endif
466 // exit or quit
467 if (ret == PM3_EFATAL)
468 break;
470 free(cmd);
471 cmd = NULL;
472 } else {
473 PrintAndLogEx(NORMAL, "\n");
474 if (script_cmds_file && stayInCommandLoop)
475 stayInCommandLoop = false;
476 else
477 break;
479 } // end while
481 if (g_session.pm3_present) {
482 clearCommandBuffer();
483 SendCommandNG(CMD_QUIT_SESSION, NULL, 0);
484 msleep(100); // Make sure command is sent before killing client
487 while (current_cmdscriptfile())
488 pop_cmdscriptfile();
490 #ifdef HAVE_READLINE
491 flush_history();
492 #endif
494 if (cmd) {
495 free(cmd);
496 cmd = NULL;
500 #ifndef LIBPM3
501 static void dumpAllHelp(int markdown, bool full_help) {
502 g_session.help_dump_mode = true;
503 PrintAndLogEx(NORMAL, "\n%sProxmark3 command dump%s\n\n", markdown ? "# " : "", markdown ? "" : "\n======================");
504 PrintAndLogEx(NORMAL, "Some commands are available only if a Proxmark3 is actually connected.%s\n", markdown ? " " : "");
505 PrintAndLogEx(NORMAL, "Check column \"offline\" for their availability.\n");
506 PrintAndLogEx(NORMAL, "\n");
507 command_t *cmds = getTopLevelCommandTable();
508 dumpCommandsRecursive(cmds, markdown, full_help);
509 g_session.help_dump_mode = false;
510 PrintAndLogEx(NORMAL, "Full help dump done.");
512 #endif //LIBPM3
514 static char *my_executable_path = NULL;
515 static char *my_executable_directory = NULL;
517 const char *get_my_executable_path(void) {
518 return my_executable_path;
521 const char *get_my_executable_directory(void) {
522 return my_executable_directory;
525 static void set_my_executable_path(void) {
526 int path_length = wai_getExecutablePath(NULL, 0, NULL);
527 if (path_length == -1)
528 return;
530 my_executable_path = (char *)calloc(path_length + 1, sizeof(uint8_t));
531 int dirname_length = 0;
532 if (wai_getExecutablePath(my_executable_path, path_length, &dirname_length) != -1) {
533 my_executable_path[path_length] = '\0';
534 my_executable_directory = (char *)calloc(dirname_length + 2, sizeof(uint8_t));
535 strncpy(my_executable_directory, my_executable_path, dirname_length + 1);
536 my_executable_directory[dirname_length + 1] = '\0';
540 static const char *my_user_directory = NULL;
541 // static char _cwd_Buffer [FILENAME_MAX] = {0};
543 const char *get_my_user_directory(void) {
544 return my_user_directory;
547 static void set_my_user_directory(void) {
548 /* my_user_directory = getenv("HOME");
550 // if not found, default to current directory
551 if (my_user_directory == NULL) {
552 my_user_directory = GetCurrentDir(_cwd_Buffer, sizeof(_cwd_Buffer));
553 // change all slashes to / (windows should not care...
554 for (int i = 0; i < strlen(_cwd_Buffer); i++)
555 if (_cwd_Buffer[i] == '\\') _cwd_Buffer[i] = '/';
556 // my_user_directory = ".";
559 my_user_directory = getenv("HOME");
561 // if not found, default to current directory
562 if (my_user_directory == NULL) {
564 uint16_t pathLen = FILENAME_MAX; // should be a good starting point
565 char *cwd_buffer = (char *)calloc(pathLen, sizeof(uint8_t));
566 if (cwd_buffer == NULL) {
567 PrintAndLogEx(WARNING, "failed to allocate memory");
568 return;
571 while (GetCurrentDir(cwd_buffer, pathLen) == NULL) {
572 if (errno == ERANGE) { // Need bigger buffer
573 pathLen += 10; // if buffer was too small add 10 characters and try again
574 char *tmp = realloc(cwd_buffer, pathLen);
575 if (tmp == NULL) {
576 PrintAndLogEx(WARNING, "failed to allocate memory");
577 free(cwd_buffer);
578 return;
580 cwd_buffer = tmp;
581 } else {
582 free(cwd_buffer);
583 return;
587 for (int i = 0; i < strlen(cwd_buffer); i++) {
588 if (cwd_buffer[i] == '\\') {
589 cwd_buffer[i] = '/';
593 my_user_directory = cwd_buffer;
597 #ifndef LIBPM3
598 static void show_help(bool showFullHelp, char *exec_name) {
600 PrintAndLogEx(NORMAL, "\nsyntax: %s [-h|-t|-m|--fulltext]", exec_name);
601 PrintAndLogEx(NORMAL, " %s [[-p] <port>] [-b] [-w] [-f] [-c <command>]|[-l <lua_script_file>]|[-s <cmd_script_file>] [-i] [-d <0|1|2>]", exec_name);
602 PrintAndLogEx(NORMAL, " %s [-p] <port> --flash [--unlock-bootloader] [--image <imagefile>]+ [-w] [-f] [-d <0|1|2>]", exec_name);
604 if (showFullHelp) {
606 PrintAndLogEx(NORMAL, "\nCommon options:");
607 PrintAndLogEx(NORMAL, " -h/--help this help");
608 PrintAndLogEx(NORMAL, " -v/--version print client version");
609 PrintAndLogEx(NORMAL, " -p/--port serial port to connect to");
610 PrintAndLogEx(NORMAL, " -w/--wait 20sec waiting the serial port to appear in the OS");
611 PrintAndLogEx(NORMAL, " -f/--flush output will be flushed after every print");
612 PrintAndLogEx(NORMAL, " -d/--debug <0|1|2> set debugmode");
613 PrintAndLogEx(NORMAL, "\nOptions in client mode:");
614 PrintAndLogEx(NORMAL, " -t/--text dump all interactive command list at once");
615 PrintAndLogEx(NORMAL, " --fulltext dump all interactive command's help at once");
616 PrintAndLogEx(NORMAL, " -m/--markdown dump all interactive command list at once in markdown syntax");
617 PrintAndLogEx(NORMAL, " -b/--baud serial port speed (only needed for physical UART, not for USB-CDC or BT)");
618 PrintAndLogEx(NORMAL, " -c/--command <command> execute one Proxmark3 command (or several separated by ';').");
619 PrintAndLogEx(NORMAL, " -l/--lua <lua script file> execute lua script.");
620 PrintAndLogEx(NORMAL, " -s/--script-file <cmd_script_file> script file with one Proxmark3 command per line");
621 PrintAndLogEx(NORMAL, " -i/--interactive enter interactive mode after executing the script or the command");
622 PrintAndLogEx(NORMAL, " --incognito do not use history, prefs file nor log files");
623 PrintAndLogEx(NORMAL, "\nOptions in flasher mode:");
624 PrintAndLogEx(NORMAL, " --flash flash Proxmark3, requires at least one --image");
625 PrintAndLogEx(NORMAL, " --unlock-bootloader Enable flashing of bootloader area *DANGEROUS* (need --flash or --flash-info)");
626 PrintAndLogEx(NORMAL, " --image <imagefile> image to flash. Can be specified several times.");
627 PrintAndLogEx(NORMAL, "\nExamples:");
628 PrintAndLogEx(NORMAL, "\n to run Proxmark3 client:\n");
629 PrintAndLogEx(NORMAL, " %s "SERIAL_PORT_EXAMPLE_H" -- runs the pm3 client", exec_name);
630 PrintAndLogEx(NORMAL, " %s "SERIAL_PORT_EXAMPLE_H" -f -- flush output everytime", exec_name);
631 PrintAndLogEx(NORMAL, " %s "SERIAL_PORT_EXAMPLE_H" -w -- wait for serial port", exec_name);
632 PrintAndLogEx(NORMAL, " %s -- runs the pm3 client in OFFLINE mode", exec_name);
633 PrintAndLogEx(NORMAL, "\n to execute different commands from terminal:\n");
634 PrintAndLogEx(NORMAL, " %s "SERIAL_PORT_EXAMPLE_H" -c \"hf mf chk --1k\" -- execute cmd and quit client", exec_name);
635 PrintAndLogEx(NORMAL, " %s "SERIAL_PORT_EXAMPLE_H" -l hf_read -- execute lua script `hf_read` and quit client", exec_name);
636 PrintAndLogEx(NORMAL, " %s "SERIAL_PORT_EXAMPLE_H" -s mycmds.txt -- execute each pm3 cmd in file and quit client", exec_name);
637 PrintAndLogEx(NORMAL, "\n to flash fullimage and bootloader:\n");
638 PrintAndLogEx(NORMAL, " %s "SERIAL_PORT_EXAMPLE_H" --flash --unlock-bootloader --image bootrom.elf --image fullimage.elf", exec_name);
639 #ifdef __linux__
640 PrintAndLogEx(NORMAL, "\nNote (Linux):\nif the flasher gets stuck in 'Waiting for Proxmark3 to reappear on <DEVICE>',");
641 PrintAndLogEx(NORMAL, "you need to blacklist Proxmark3 for modem-manager - see documentation for more details:");
642 PrintAndLogEx(NORMAL, "* https://github.com/RfidResearchGroup/proxmark3/blob/master/doc/md/Installation_Instructions/ModemManager-Must-Be-Discarded.md");
643 PrintAndLogEx(NORMAL, "\nMore info on flashing procedure from the official Proxmark3 wiki:");
644 PrintAndLogEx(NORMAL, "* https://github.com/Proxmark/proxmark3/wiki/Gentoo%%20Linux");
645 PrintAndLogEx(NORMAL, "* https://github.com/Proxmark/proxmark3/wiki/Ubuntu%%20Linux");
646 PrintAndLogEx(NORMAL, "* https://github.com/Proxmark/proxmark3/wiki/OSX\n");
647 #endif
651 static int flash_pm3(char *serial_port_name, uint8_t num_files, char *filenames[FLASH_MAX_FILES], bool can_write_bl) {
653 int ret = PM3_EUNDEF;
654 flash_file_t files[FLASH_MAX_FILES];
655 memset(files, 0, sizeof(files));
656 char *filepaths[FLASH_MAX_FILES] = {0};
658 if (serial_port_name == NULL) {
659 PrintAndLogEx(ERR, "You must specify a port.\n");
660 return PM3_EINVARG;
663 for (int i = 0 ; i < num_files; ++i) {
664 char *path;
665 ret = searchFile(&path, FIRMWARES_SUBDIR, filenames[i], ".elf", true);
666 if (ret != PM3_SUCCESS) {
667 ret = searchFile(&path, BOOTROM_SUBDIR, filenames[i], ".elf", true);
669 if (ret != PM3_SUCCESS) {
670 // Last try, let the error msg be displayed if not found
671 ret = searchFile(&path, FULLIMAGE_SUBDIR, filenames[i], ".elf", false);
673 if (ret != PM3_SUCCESS) {
674 goto finish2;
676 filepaths[i] = path;
679 PrintAndLogEx(SUCCESS, "About to use the following file%s:", num_files > 1 ? "s" : "");
680 for (int i = 0 ; i < num_files; ++i) {
681 PrintAndLogEx(SUCCESS, " "_YELLOW_("%s"), filepaths[i]);
684 if (OpenProxmark(&g_session.current_device, serial_port_name, true, 60, true, FLASHMODE_SPEED)) {
685 PrintAndLogEx(NORMAL, _GREEN_(" found"));
686 } else {
687 PrintAndLogEx(ERR, "Could not find Proxmark3 on " _RED_("%s") ".\n", serial_port_name);
688 ret = PM3_ETIMEOUT;
689 goto finish2;
692 uint32_t max_allowed = 0;
693 ret = flash_start_flashing(can_write_bl, serial_port_name, &max_allowed);
694 if (ret != PM3_SUCCESS) {
695 goto finish;
698 if (num_files == 0)
699 goto finish;
701 for (int i = 0 ; i < num_files; ++i) {
702 ret = flash_load(&files[i], filepaths[i], can_write_bl, max_allowed * ONE_KB);
703 if (ret != PM3_SUCCESS) {
704 goto finish;
706 PrintAndLogEx(NORMAL, "");
709 PrintAndLogEx(SUCCESS, _CYAN_("Flashing..."));
711 for (int i = 0; i < num_files; i++) {
712 ret = flash_write(&files[i]);
713 if (ret != PM3_SUCCESS) {
714 goto finish;
716 flash_free(&files[i]);
717 PrintAndLogEx(NORMAL, "");
720 finish:
721 if (ret != PM3_SUCCESS)
722 PrintAndLogEx(INFO, "The flashing procedure failed, follow the suggested steps!");
723 ret = flash_stop_flashing();
724 CloseProxmark(g_session.current_device);
725 finish2:
726 for (int i = 0 ; i < num_files; ++i) {
727 if (filepaths[i] != NULL)
728 free(filepaths[i]);
730 if (ret == PM3_SUCCESS)
731 PrintAndLogEx(SUCCESS, _CYAN_("All done"));
732 else
733 PrintAndLogEx(ERR, "Aborted on error");
734 PrintAndLogEx(INFO, "\nHave a nice day!");
735 return ret;
737 #endif //LIBPM3
739 void pm3_init(void) {
740 srand(time(0));
742 g_session.pm3_present = false;
743 g_session.help_dump_mode = false;
744 g_session.incognito = false;
745 g_session.supports_colors = false;
746 g_session.emoji_mode = EMO_ALTTEXT;
747 g_session.stdinOnTTY = false;
748 g_session.stdoutOnTTY = false;
750 // set global variables soon enough to get the log path
751 set_my_executable_path();
752 set_my_user_directory();
756 #ifndef LIBPM3
757 int main(int argc, char *argv[]) {
758 pm3_init();
759 bool waitCOMPort = false;
760 bool addLuaExec = false;
761 bool stayInCommandLoop = false;
762 char *script_cmds_file = NULL;
763 char *script_cmd = NULL;
764 char *port = NULL;
765 uint32_t speed = 0;
767 #ifdef HAVE_READLINE
768 /* initialize history */
769 using_history();
771 #ifdef RL_STATE_READCMD
772 rl_extend_line_buffer(1024);
773 #endif // RL_STATE_READCMD
774 #endif // HAVE_READLINE
776 char exec_name[100] = {0};
777 strncpy(exec_name, basename(argv[0]), sizeof(exec_name) - 1);
779 bool flash_mode = false;
780 bool flash_can_write_bl = false;
781 bool debug_mode_forced = false;
782 int flash_num_files = 0;
783 char *flash_filenames[FLASH_MAX_FILES];
785 // color management:
786 // 1. default = no color
787 // 2. enable colors if OS seems to support colors and if stdin/stdout aren't redirected
788 // 3. load prefs if available, overwrite colors choice if needed
789 // 4. disable colors anyway if stdin/stdout are redirected
791 // For info, grep --color=auto is doing sth like this, plus test getenv("TERM") != "dumb":
792 // struct stat tmp_stat;
793 // if ((fstat (STDOUT_FILENO, &tmp_stat) == 0) && (S_ISCHR (tmp_stat.st_mode)) && isatty(STDIN_FILENO))
794 g_session.stdinOnTTY = isatty(STDIN_FILENO);
795 g_session.stdoutOnTTY = isatty(STDOUT_FILENO);
796 g_session.supports_colors = false;
797 g_session.emoji_mode = EMO_ALTTEXT;
798 if (g_session.stdinOnTTY && g_session.stdoutOnTTY) {
799 #if defined(__linux__) || defined(__APPLE__)
800 g_session.supports_colors = true;
801 g_session.emoji_mode = EMO_EMOJI;
802 #elif defined(_WIN32)
803 g_session.supports_colors = DetectWindowsAnsiSupport();
804 g_session.emoji_mode = EMO_ALTTEXT;
805 #endif
807 for (int i = 1; i < argc; i++) {
809 if (argv[i][0] != '-') {
810 // For backward compatibility we accept direct port
811 if (port != NULL) {
812 // We got already one
813 PrintAndLogEx(ERR, _RED_("ERROR:") " cannot parse command line. We got " _YELLOW_("%s") " as port and now we got also: " _YELLOW_("%s") "\n", port, argv[i]);
814 show_help(false, exec_name);
815 return 1;
817 port = argv[i];
818 continue;
821 // port
822 if (strcmp(argv[i], "-p") == 0 || strcmp(argv[i], "--port") == 0) {
823 if (i + 1 == argc) {
824 PrintAndLogEx(ERR, _RED_("ERROR:") " missing port specification after -p\n");
825 show_help(false, exec_name);
826 return 1;
828 if (port != NULL) {
829 // We got already one
830 PrintAndLogEx(ERR, _RED_("ERROR:") " cannot parse command line. We got " _YELLOW_("%s") " as port and now we got also: " _YELLOW_("%s") "\n", port, argv[i + 1]);
831 show_help(false, exec_name);
832 return 1;
834 port = argv[++i];
835 continue;
838 // short help
839 if (strcmp(argv[i], "-h") == 0 || strcmp(argv[i], "--help") == 0) {
840 g_printAndLog = PRINTANDLOG_PRINT;
841 show_help(true, exec_name);
842 return 0;
845 // dump help
846 if (strcmp(argv[i], "-t") == 0 || strcmp(argv[i], "--text") == 0) {
847 g_printAndLog = PRINTANDLOG_PRINT;
848 show_help(false, exec_name);
849 dumpAllHelp(0, false);
850 return 0;
853 // dump help
854 if (strcmp(argv[i], "--fulltext") == 0) {
855 g_printAndLog = PRINTANDLOG_PRINT;
856 show_help(false, exec_name);
857 dumpAllHelp(0, true);
858 return 0;
861 // dump markup
862 if (strcmp(argv[i], "-m") == 0 || strcmp(argv[i], "--markdown") == 0) {
863 g_printAndLog = PRINTANDLOG_PRINT;
864 dumpAllHelp(1, false);
865 return 0;
867 // print client version
868 if (strcmp(argv[i], "-v") == 0 || strcmp(argv[i], "--version") == 0) {
869 pm3_version(true, true);
870 return 0;
873 // set debugmode
874 if (strcmp(argv[i], "-d") == 0 || strcmp(argv[i], "--debug") == 0) {
875 if (i + 1 == argc) {
876 PrintAndLogEx(ERR, _RED_("ERROR:") " missing debugmode specification after -d\n");
877 show_help(false, exec_name);
878 return 1;
880 int demod = atoi(argv[i + 1]);
881 if (demod < 0 || demod > 2) {
882 PrintAndLogEx(ERR, _RED_("ERROR:") " invalid debugmode: -d " _YELLOW_("%s") "\n", argv[i + 1]);
883 return 1;
885 g_debugMode = demod;
886 debug_mode_forced = true;
887 i++;
888 continue;
891 // flush output
892 if (strcmp(argv[i], "-f") == 0 || strcmp(argv[i], "--flush") == 0) {
893 SetFlushAfterWrite(true);
894 PrintAndLogEx(INFO, "Output will be flushed after every print.\n");
895 continue;
898 // set baudrate
899 if (strcmp(argv[i], "-b") == 0 || strcmp(argv[i], "--baud") == 0) {
900 if (i + 1 == argc) {
901 PrintAndLogEx(ERR, _RED_("ERROR:") " missing baud specification after -b\n");
902 show_help(false, exec_name);
903 return 1;
905 uint64_t tmpspeed = strtoul(argv[i + 1], NULL, 10);
906 if ((tmpspeed == ULONG_MAX) || (tmpspeed == 0)) {
907 PrintAndLogEx(ERR, _RED_("ERROR:") " invalid baudrate: -b " _YELLOW_("%s") "\n", argv[i + 1]);
908 return 1;
910 speed = tmpspeed;
911 i++;
912 continue;
915 // wait for comport
916 if (strcmp(argv[i], "-w") == 0 || strcmp(argv[i], "--wait") == 0) {
917 waitCOMPort = true;
918 continue;
921 // execute pm3 command
922 if (strcmp(argv[i], "-c") == 0 || strcmp(argv[i], "--command") == 0) {
923 if (i + 1 == argc) {
924 PrintAndLogEx(ERR, _RED_("ERROR:") " missing command specification after -c\n");
925 show_help(false, exec_name);
926 return 1;
928 script_cmd = argv[++i];
929 continue;
932 // execute pm3 command file
933 if (strcmp(argv[i], "-s") == 0 || strcmp(argv[i], "--script-file") == 0) {
934 if (i + 1 == argc || strlen(argv[i + 1]) == 0) {
935 PrintAndLogEx(ERR, _RED_("ERROR:") " missing script file specification after -s\n");
936 show_help(false, exec_name);
937 return 1;
939 script_cmds_file = argv[++i];
940 continue;
943 // execute lua script
944 if (strcmp(argv[i], "-l") == 0 || strcmp(argv[i], "--lua") == 0) {
945 if (i + 1 == argc || strlen(argv[i + 1]) == 0) {
946 PrintAndLogEx(ERR, _RED_("ERROR:") " missing lua script specification after -l\n");
947 show_help(false, exec_name);
948 return 1;
950 script_cmd = argv[++i];
951 if (script_cmd == NULL || strlen(script_cmd) == 0) {
952 return 1;
954 addLuaExec = true;
955 continue;
958 // go to interactive instead of quitting after a script/command
959 if (strcmp(argv[i], "-i") == 0 || strcmp(argv[i], "--interactive") == 0) {
960 stayInCommandLoop = true;
961 continue;
964 // do not use history nor log files
965 if (strcmp(argv[i], "--incognito") == 0) {
966 g_session.incognito = true;
967 continue;
970 // go to flash mode
971 if (strcmp(argv[i], "--flash") == 0) {
972 flash_mode = true;
973 continue;
976 // unlock bootloader area
977 if (strcmp(argv[i], "--unlock-bootloader") == 0) {
978 flash_can_write_bl = true;
979 continue;
982 // flash file
983 if (strcmp(argv[i], "--image") == 0) {
984 if (flash_num_files == FLASH_MAX_FILES) {
985 PrintAndLogEx(ERR, _RED_("ERROR:") " too many --image, please use it max %i times\n", FLASH_MAX_FILES);
986 return 1;
988 if (i + 1 == argc) {
989 PrintAndLogEx(ERR, _RED_("ERROR:") " missing image specification after --image\n");
990 show_help(false, exec_name);
991 return 1;
993 flash_filenames[flash_num_files++] = argv[++i];
994 continue;
997 // We got an unknown parameter
998 PrintAndLogEx(ERR, _RED_("ERROR:") " invalid parameter: " _YELLOW_("%s") "\n", argv[i]);
999 show_help(false, exec_name);
1000 return 1;
1003 // Load Settings and assign
1004 // This will allow the command line to override the settings.json values
1005 preferences_load();
1006 // quick patch for debug level
1007 if (! debug_mode_forced)
1008 g_debugMode = g_session.client_debug_level;
1009 // settings_save ();
1010 // End Settings
1012 // even if prefs, we disable colors if stdin or stdout is not a TTY
1013 if ((! g_session.stdinOnTTY) || (! g_session.stdoutOnTTY)) {
1014 g_session.supports_colors = false;
1015 g_session.emoji_mode = EMO_ALTTEXT;
1018 // Let's take a baudrate ok for real UART, USB-CDC & BT don't use that info anyway
1019 if (speed == 0)
1020 speed = USART_BAUD_RATE;
1022 if (flash_mode) {
1023 flash_pm3(port, flash_num_files, flash_filenames, flash_can_write_bl);
1024 exit(EXIT_SUCCESS);
1027 if (script_cmd) {
1028 while (script_cmd[strlen(script_cmd) - 1] == ' ')
1029 script_cmd[strlen(script_cmd) - 1] = 0x00;
1031 if (strlen(script_cmd) == 0) {
1032 script_cmd = NULL;
1033 PrintAndLogEx(ERR, _RED_("ERROR:") " execute command: " _YELLOW_("command not found") ".\n");
1034 return 2;
1035 } else {
1036 if (addLuaExec) {
1037 // add "script run " to command
1038 int len = strlen(script_cmd) + 11 + 1;
1039 char *ctmp = (char *) calloc(len, sizeof(uint8_t));
1040 if (ctmp != NULL) {
1041 memset(ctmp, 0, len);
1042 strcpy(ctmp, "script run ");
1043 strcpy(&ctmp[11], script_cmd);
1044 script_cmd = ctmp;
1048 PrintAndLogEx(SUCCESS, "execute command from commandline: " _YELLOW_("%s") "\n", script_cmd);
1052 // try to open USB connection to Proxmark
1053 if (port != NULL) {
1054 OpenProxmark(&g_session.current_device, port, waitCOMPort, 20, false, speed);
1057 if (g_session.pm3_present && (TestProxmark(g_session.current_device) != PM3_SUCCESS)) {
1058 PrintAndLogEx(ERR, _RED_("ERROR:") " cannot communicate with the Proxmark\n");
1059 CloseProxmark(g_session.current_device);
1062 if ((port != NULL) && (!g_session.pm3_present))
1063 exit(EXIT_FAILURE);
1065 if (!g_session.pm3_present)
1066 PrintAndLogEx(INFO, "Running in " _YELLOW_("OFFLINE") " mode. Check " _YELLOW_("\"%s -h\"") " if it's not what you want.\n", exec_name);
1068 // ascii art only in interactive client
1069 if (!script_cmds_file && !script_cmd && g_session.stdinOnTTY && g_session.stdoutOnTTY && !flash_mode)
1070 showBanner();
1072 // Save settings if not loaded from settings json file.
1073 // Doing this here will ensure other checks and updates are saved to over rule default
1074 // e.g. Linux color use check
1075 if ((!g_session.preferences_loaded) && (!g_session.incognito)) {
1076 PrintAndLogEx(INFO, "Creating initial preferences file"); // json save reports file name, so just info msg here
1077 preferences_save(); // Save defaults
1078 g_session.preferences_loaded = true;
1079 } /* else {
1080 // Set device debug level
1081 PrintAndLogEx(INFO,"setting device debug loglevel");
1082 if (g_session.pm3_present) {
1083 SendCommandNG(CMD_SET_DBGMODE, &g_session.device_debug_level, 1);
1084 PacketResponseNG resp;
1085 if (WaitForResponseTimeout(CMD_SET_DBGMODE, &resp, 2000) == false)
1086 PrintAndLogEx (INFO,"failed to set device debug loglevel");
1088 else
1089 PrintAndLogEx(WARNING,"Proxmark3 not ready to set debug level");
1093 #ifdef HAVE_GUI
1095 # if defined(_WIN32)
1096 InitGraphics(argc, argv, script_cmds_file, script_cmd, stayInCommandLoop);
1097 MainGraphics();
1098 # else
1099 // for *nix distro's, check environment variable to verify a display
1100 char *display = getenv("DISPLAY");
1101 if (display && strlen(display) > 1) {
1102 InitGraphics(argc, argv, script_cmds_file, script_cmd, stayInCommandLoop);
1103 MainGraphics();
1104 } else {
1105 main_loop(script_cmds_file, script_cmd, stayInCommandLoop);
1107 # endif
1109 #else
1110 main_loop(script_cmds_file, script_cmd, stayInCommandLoop);
1111 #endif
1113 // Clean up the port
1114 if (g_session.pm3_present) {
1115 CloseProxmark(g_session.current_device);
1118 if (g_session.window_changed) // Plot/Overlay moved or resized
1119 preferences_save();
1120 exit(EXIT_SUCCESS);
1122 #endif //LIBPM3