fix little endian vs big endian in the macros... again... but this time correct
[RRG-proxmark3.git] / client / src / proxmark3.c
blobd1507923a7f07d27ecfd3139f8e13842593fe6f3
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 " Iceman :coffee:"
40 #define BANNERMSG2 " :snowflake: bleeding edge"
41 #define BANNERMSG3 " https://github.com/rfidresearchgroup/proxmark3/"
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() && 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), !session.supports_colors);
137 rl_set_prompt(prompt_filtered);
138 rl_forced_update_display();
139 #endif
140 CloseProxmark(session.current_device);
142 msleep(10);
143 return 0;
145 #ifdef HAVE_READLINE
146 static void flush_history(void) {
147 if (session.history_path) {
148 write_history(session.history_path);
149 free(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 struct sigaction old_action;
165 static void terminate_handler(int signum) {
166 sigaction(SIGINT, &old_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 ((! session.stdinOnTTY) || (! 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 (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 session.history_path = NULL;
276 if (session.incognito) {
277 PrintAndLogEx(INFO, "No history will be recorded");
278 } else {
279 if (searchHomeFilePath(&session.history_path, NULL, PROXHISTORY, true) != PM3_SUCCESS) {
280 PrintAndLogEx(ERR, "No history will be recorded");
281 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 = &terminate_handler;
290 sigaction(SIGINT, &action, &old_action);
291 # endif
292 rl_catch_signals = 1;
293 rl_set_signals();
294 read_history(session.history_path);
297 #endif
299 // loops every time enter is pressed...
300 while (1) {
302 bool printprompt = false;
303 if (session.pm3_present) {
304 if (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), !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 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 // exit or quit
464 if (ret == PM3_EFATAL)
465 break;
467 free(cmd);
468 cmd = NULL;
469 } else {
470 PrintAndLogEx(NORMAL, "\n");
471 if (script_cmds_file && stayInCommandLoop)
472 stayInCommandLoop = false;
473 else
474 break;
476 } // end while
478 if (session.pm3_present) {
479 clearCommandBuffer();
480 SendCommandNG(CMD_QUIT_SESSION, NULL, 0);
481 msleep(100); // Make sure command is sent before killing client
484 while (current_cmdscriptfile())
485 pop_cmdscriptfile();
487 #ifdef HAVE_READLINE
488 flush_history();
489 #endif
491 if (cmd) {
492 free(cmd);
493 cmd = NULL;
497 #ifndef LIBPM3
498 static void dumpAllHelp(int markdown, bool full_help) {
499 session.help_dump_mode = true;
500 PrintAndLogEx(NORMAL, "\n%sProxmark3 command dump%s\n\n", markdown ? "# " : "", markdown ? "" : "\n======================");
501 PrintAndLogEx(NORMAL, "Some commands are available only if a Proxmark3 is actually connected.%s\n", markdown ? " " : "");
502 PrintAndLogEx(NORMAL, "Check column \"offline\" for their availability.\n");
503 PrintAndLogEx(NORMAL, "\n");
504 command_t *cmds = getTopLevelCommandTable();
505 dumpCommandsRecursive(cmds, markdown, full_help);
506 session.help_dump_mode = false;
507 PrintAndLogEx(NORMAL, "Full help dump done.");
509 #endif //LIBPM3
511 static char *my_executable_path = NULL;
512 static char *my_executable_directory = NULL;
514 const char *get_my_executable_path(void) {
515 return my_executable_path;
518 const char *get_my_executable_directory(void) {
519 return my_executable_directory;
522 static void set_my_executable_path(void) {
523 int path_length = wai_getExecutablePath(NULL, 0, NULL);
524 if (path_length == -1)
525 return;
527 my_executable_path = (char *)calloc(path_length + 1, sizeof(uint8_t));
528 int dirname_length = 0;
529 if (wai_getExecutablePath(my_executable_path, path_length, &dirname_length) != -1) {
530 my_executable_path[path_length] = '\0';
531 my_executable_directory = (char *)calloc(dirname_length + 2, sizeof(uint8_t));
532 strncpy(my_executable_directory, my_executable_path, dirname_length + 1);
533 my_executable_directory[dirname_length + 1] = '\0';
537 static const char *my_user_directory = NULL;
538 // static char _cwd_Buffer [FILENAME_MAX] = {0};
540 const char *get_my_user_directory(void) {
541 return my_user_directory;
544 static void set_my_user_directory(void) {
545 /* my_user_directory = getenv("HOME");
547 // if not found, default to current directory
548 if (my_user_directory == NULL) {
549 my_user_directory = GetCurrentDir(_cwd_Buffer, sizeof(_cwd_Buffer));
550 // change all slashes to / (windows should not care...
551 for (int i = 0; i < strlen(_cwd_Buffer); i++)
552 if (_cwd_Buffer[i] == '\\') _cwd_Buffer[i] = '/';
553 // my_user_directory = ".";
556 my_user_directory = getenv("HOME");
558 // if not found, default to current directory
559 if (my_user_directory == NULL) {
561 uint16_t pathLen = FILENAME_MAX; // should be a good starting point
562 char *cwd_buffer = (char *)calloc(pathLen, sizeof(uint8_t));
563 if (cwd_buffer == NULL) {
564 PrintAndLogEx(WARNING, "failed to allocate memory");
565 return;
568 while (GetCurrentDir(cwd_buffer, pathLen) == NULL) {
569 if (errno == ERANGE) { // Need bigger buffer
570 pathLen += 10; // if buffer was too small add 10 characters and try again
571 char *tmp = realloc(cwd_buffer, pathLen);
572 if (tmp == NULL) {
573 PrintAndLogEx(WARNING, "failed to allocate memory");
574 free(cwd_buffer);
575 return;
577 cwd_buffer = tmp;
578 } else {
579 free(cwd_buffer);
580 return;
584 for (int i = 0; i < strlen(cwd_buffer); i++) {
585 if (cwd_buffer[i] == '\\') {
586 cwd_buffer[i] = '/';
590 my_user_directory = cwd_buffer;
594 #ifndef LIBPM3
595 static void show_help(bool showFullHelp, char *exec_name) {
597 PrintAndLogEx(NORMAL, "\nsyntax: %s [-h|-t|-m|--fulltext]", exec_name);
598 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);
599 PrintAndLogEx(NORMAL, " %s [-p] <port> --flash [--unlock-bootloader] [--image <imagefile>]+ [-w] [-f] [-d <0|1|2>]", exec_name);
601 if (showFullHelp) {
603 PrintAndLogEx(NORMAL, "\nCommon options:");
604 PrintAndLogEx(NORMAL, " -h/--help this help");
605 PrintAndLogEx(NORMAL, " -v/--version print client version");
606 PrintAndLogEx(NORMAL, " -p/--port serial port to connect to");
607 PrintAndLogEx(NORMAL, " -w/--wait 20sec waiting the serial port to appear in the OS");
608 PrintAndLogEx(NORMAL, " -f/--flush output will be flushed after every print");
609 PrintAndLogEx(NORMAL, " -d/--debug <0|1|2> set debugmode");
610 PrintAndLogEx(NORMAL, "\nOptions in client mode:");
611 PrintAndLogEx(NORMAL, " -t/--text dump all interactive command list at once");
612 PrintAndLogEx(NORMAL, " --fulltext dump all interactive command's help at once");
613 PrintAndLogEx(NORMAL, " -m/--markdown dump all interactive command list at once in markdown syntax");
614 PrintAndLogEx(NORMAL, " -b/--baud serial port speed (only needed for physical UART, not for USB-CDC or BT)");
615 PrintAndLogEx(NORMAL, " -c/--command <command> execute one Proxmark3 command (or several separated by ';').");
616 PrintAndLogEx(NORMAL, " -l/--lua <lua script file> execute lua script.");
617 PrintAndLogEx(NORMAL, " -s/--script-file <cmd_script_file> script file with one Proxmark3 command per line");
618 PrintAndLogEx(NORMAL, " -i/--interactive enter interactive mode after executing the script or the command");
619 PrintAndLogEx(NORMAL, " --incognito do not use history, prefs file nor log files");
620 PrintAndLogEx(NORMAL, "\nOptions in flasher mode:");
621 PrintAndLogEx(NORMAL, " --flash flash Proxmark3, requires at least one --image");
622 PrintAndLogEx(NORMAL, " --unlock-bootloader Enable flashing of bootloader area *DANGEROUS* (need --flash or --flash-info)");
623 PrintAndLogEx(NORMAL, " --image <imagefile> image to flash. Can be specified several times.");
624 PrintAndLogEx(NORMAL, "\nExamples:");
625 PrintAndLogEx(NORMAL, "\n to run Proxmark3 client:\n");
626 PrintAndLogEx(NORMAL, " %s "SERIAL_PORT_EXAMPLE_H" -- runs the pm3 client", exec_name);
627 PrintAndLogEx(NORMAL, " %s "SERIAL_PORT_EXAMPLE_H" -f -- flush output everytime", exec_name);
628 PrintAndLogEx(NORMAL, " %s "SERIAL_PORT_EXAMPLE_H" -w -- wait for serial port", exec_name);
629 PrintAndLogEx(NORMAL, " %s -- runs the pm3 client in OFFLINE mode", exec_name);
630 PrintAndLogEx(NORMAL, "\n to execute different commands from terminal:\n");
631 PrintAndLogEx(NORMAL, " %s "SERIAL_PORT_EXAMPLE_H" -c \"hf mf chk --1k\" -- execute cmd and quit client", exec_name);
632 PrintAndLogEx(NORMAL, " %s "SERIAL_PORT_EXAMPLE_H" -l hf_read -- execute lua script `hf_read` and quit client", exec_name);
633 PrintAndLogEx(NORMAL, " %s "SERIAL_PORT_EXAMPLE_H" -s mycmds.txt -- execute each pm3 cmd in file and quit client", exec_name);
634 PrintAndLogEx(NORMAL, "\n to flash fullimage and bootloader:\n");
635 PrintAndLogEx(NORMAL, " %s "SERIAL_PORT_EXAMPLE_H" --flash --unlock-bootloader --image bootrom.elf --image fullimage.elf", exec_name);
636 #ifdef __linux__
637 PrintAndLogEx(NORMAL, "\nNote (Linux):\nif the flasher gets stuck in 'Waiting for Proxmark3 to reappear on <DEVICE>',");
638 PrintAndLogEx(NORMAL, "you need to blacklist Proxmark3 for modem-manager - see documentation for more details:");
639 PrintAndLogEx(NORMAL, "* https://github.com/RfidResearchGroup/proxmark3/blob/master/doc/md/Installation_Instructions/ModemManager-Must-Be-Discarded.md");
640 PrintAndLogEx(NORMAL, "\nMore info on flashing procedure from the official Proxmark3 wiki:");
641 PrintAndLogEx(NORMAL, "* https://github.com/Proxmark/proxmark3/wiki/Gentoo%%20Linux");
642 PrintAndLogEx(NORMAL, "* https://github.com/Proxmark/proxmark3/wiki/Ubuntu%%20Linux");
643 PrintAndLogEx(NORMAL, "* https://github.com/Proxmark/proxmark3/wiki/OSX\n");
644 #endif
648 static int flash_pm3(char *serial_port_name, uint8_t num_files, char *filenames[FLASH_MAX_FILES], bool can_write_bl) {
650 int ret = PM3_EUNDEF;
651 flash_file_t files[FLASH_MAX_FILES];
652 memset(files, 0, sizeof(files));
653 char *filepaths[FLASH_MAX_FILES] = {0};
655 if (serial_port_name == NULL) {
656 PrintAndLogEx(ERR, "You must specify a port.\n");
657 return PM3_EINVARG;
660 for (int i = 0 ; i < num_files; ++i) {
661 char *path;
662 ret = searchFile(&path, FIRMWARES_SUBDIR, filenames[i], ".elf", true);
663 if (ret != PM3_SUCCESS) {
664 ret = searchFile(&path, BOOTROM_SUBDIR, filenames[i], ".elf", true);
666 if (ret != PM3_SUCCESS) {
667 // Last try, let the error msg be displayed if not found
668 ret = searchFile(&path, FULLIMAGE_SUBDIR, filenames[i], ".elf", false);
670 if (ret != PM3_SUCCESS) {
671 goto finish2;
673 filepaths[i] = path;
676 PrintAndLogEx(SUCCESS, "About to use the following file%s:", num_files > 1 ? "s" : "");
677 for (int i = 0 ; i < num_files; ++i) {
678 PrintAndLogEx(SUCCESS, " "_YELLOW_("%s"), filepaths[i]);
681 if (OpenProxmark(&session.current_device, serial_port_name, true, 60, true, FLASHMODE_SPEED)) {
682 PrintAndLogEx(NORMAL, _GREEN_(" found"));
683 } else {
684 PrintAndLogEx(ERR, "Could not find Proxmark3 on " _RED_("%s") ".\n", serial_port_name);
685 ret = PM3_ETIMEOUT;
686 goto finish2;
689 uint32_t max_allowed = 0;
690 ret = flash_start_flashing(can_write_bl, serial_port_name, &max_allowed);
691 if (ret != PM3_SUCCESS) {
692 goto finish;
695 if (num_files == 0)
696 goto finish;
698 for (int i = 0 ; i < num_files; ++i) {
699 ret = flash_load(&files[i], filepaths[i], can_write_bl, max_allowed * ONE_KB);
700 if (ret != PM3_SUCCESS) {
701 goto finish;
703 PrintAndLogEx(NORMAL, "");
706 PrintAndLogEx(SUCCESS, _CYAN_("Flashing..."));
708 for (int i = 0; i < num_files; i++) {
709 ret = flash_write(&files[i]);
710 if (ret != PM3_SUCCESS) {
711 goto finish;
713 flash_free(&files[i]);
714 PrintAndLogEx(NORMAL, "");
717 finish:
718 if (ret != PM3_SUCCESS)
719 PrintAndLogEx(INFO, "The flashing procedure failed, follow the suggested steps!");
720 ret = flash_stop_flashing();
721 CloseProxmark(session.current_device);
722 finish2:
723 for (int i = 0 ; i < num_files; ++i) {
724 if (filepaths[i] != NULL)
725 free(filepaths[i]);
727 if (ret == PM3_SUCCESS)
728 PrintAndLogEx(SUCCESS, _CYAN_("All done"));
729 else
730 PrintAndLogEx(ERR, "Aborted on error");
731 PrintAndLogEx(INFO, "\nHave a nice day!");
732 return ret;
734 #endif //LIBPM3
736 void pm3_init(void) {
737 srand(time(0));
739 session.pm3_present = false;
740 session.help_dump_mode = false;
741 session.incognito = false;
742 session.supports_colors = false;
743 session.emoji_mode = EMO_ALTTEXT;
744 session.stdinOnTTY = false;
745 session.stdoutOnTTY = false;
747 // set global variables soon enough to get the log path
748 set_my_executable_path();
749 set_my_user_directory();
753 #ifndef LIBPM3
754 int main(int argc, char *argv[]) {
755 pm3_init();
756 bool waitCOMPort = false;
757 bool addLuaExec = false;
758 bool stayInCommandLoop = false;
759 char *script_cmds_file = NULL;
760 char *script_cmd = NULL;
761 char *port = NULL;
762 uint32_t speed = 0;
764 #ifdef HAVE_READLINE
765 /* initialize history */
766 using_history();
768 #ifdef RL_STATE_READCMD
769 rl_extend_line_buffer(1024);
770 #endif // RL_STATE_READCMD
771 #endif // HAVE_READLINE
773 char exec_name[100] = {0};
774 strncpy(exec_name, basename(argv[0]), sizeof(exec_name) - 1);
776 bool flash_mode = false;
777 bool flash_can_write_bl = false;
778 bool debug_mode_forced = false;
779 int flash_num_files = 0;
780 char *flash_filenames[FLASH_MAX_FILES];
782 // color management:
783 // 1. default = no color
784 // 2. enable colors if OS seems to support colors and if stdin/stdout aren't redirected
785 // 3. load prefs if available, overwrite colors choice if needed
786 // 4. disable colors anyway if stdin/stdout are redirected
788 // For info, grep --color=auto is doing sth like this, plus test getenv("TERM") != "dumb":
789 // struct stat tmp_stat;
790 // if ((fstat (STDOUT_FILENO, &tmp_stat) == 0) && (S_ISCHR (tmp_stat.st_mode)) && isatty(STDIN_FILENO))
791 session.stdinOnTTY = isatty(STDIN_FILENO);
792 session.stdoutOnTTY = isatty(STDOUT_FILENO);
793 session.supports_colors = false;
794 session.emoji_mode = EMO_ALTTEXT;
795 if (session.stdinOnTTY && session.stdoutOnTTY) {
796 #if defined(__linux__) || defined(__APPLE__)
797 session.supports_colors = true;
798 session.emoji_mode = EMO_EMOJI;
799 #elif defined(_WIN32)
800 session.supports_colors = DetectWindowsAnsiSupport();
801 session.emoji_mode = EMO_ALTTEXT;
802 #endif
804 for (int i = 1; i < argc; i++) {
806 if (argv[i][0] != '-') {
807 // For backward compatibility we accept direct port
808 if (port != NULL) {
809 // We got already one
810 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]);
811 show_help(false, exec_name);
812 return 1;
814 port = argv[i];
815 continue;
818 // port
819 if (strcmp(argv[i], "-p") == 0 || strcmp(argv[i], "--port") == 0) {
820 if (i + 1 == argc) {
821 PrintAndLogEx(ERR, _RED_("ERROR:") " missing port specification after -p\n");
822 show_help(false, exec_name);
823 return 1;
825 if (port != NULL) {
826 // We got already one
827 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]);
828 show_help(false, exec_name);
829 return 1;
831 port = argv[++i];
832 continue;
835 // short help
836 if (strcmp(argv[i], "-h") == 0 || strcmp(argv[i], "--help") == 0) {
837 g_printAndLog = PRINTANDLOG_PRINT;
838 show_help(true, exec_name);
839 return 0;
842 // dump help
843 if (strcmp(argv[i], "-t") == 0 || strcmp(argv[i], "--text") == 0) {
844 g_printAndLog = PRINTANDLOG_PRINT;
845 show_help(false, exec_name);
846 dumpAllHelp(0, false);
847 return 0;
850 // dump help
851 if (strcmp(argv[i], "--fulltext") == 0) {
852 g_printAndLog = PRINTANDLOG_PRINT;
853 show_help(false, exec_name);
854 dumpAllHelp(0, true);
855 return 0;
858 // dump markup
859 if (strcmp(argv[i], "-m") == 0 || strcmp(argv[i], "--markdown") == 0) {
860 g_printAndLog = PRINTANDLOG_PRINT;
861 dumpAllHelp(1, false);
862 return 0;
864 // print client version
865 if (strcmp(argv[i], "-v") == 0 || strcmp(argv[i], "--version") == 0) {
866 pm3_version(true, true);
867 return 0;
870 // set debugmode
871 if (strcmp(argv[i], "-d") == 0 || strcmp(argv[i], "--debug") == 0) {
872 if (i + 1 == argc) {
873 PrintAndLogEx(ERR, _RED_("ERROR:") " missing debugmode specification after -d\n");
874 show_help(false, exec_name);
875 return 1;
877 int demod = atoi(argv[i + 1]);
878 if (demod < 0 || demod > 2) {
879 PrintAndLogEx(ERR, _RED_("ERROR:") " invalid debugmode: -d " _YELLOW_("%s") "\n", argv[i + 1]);
880 return 1;
882 g_debugMode = demod;
883 debug_mode_forced = true;
884 i++;
885 continue;
888 // flush output
889 if (strcmp(argv[i], "-f") == 0 || strcmp(argv[i], "--flush") == 0) {
890 SetFlushAfterWrite(true);
891 PrintAndLogEx(INFO, "Output will be flushed after every print.\n");
892 continue;
895 // set baudrate
896 if (strcmp(argv[i], "-b") == 0 || strcmp(argv[i], "--baud") == 0) {
897 if (i + 1 == argc) {
898 PrintAndLogEx(ERR, _RED_("ERROR:") " missing baud specification after -b\n");
899 show_help(false, exec_name);
900 return 1;
902 uint64_t tmpspeed = strtoul(argv[i + 1], NULL, 10);
903 if ((tmpspeed == ULONG_MAX) || (tmpspeed == 0)) {
904 PrintAndLogEx(ERR, _RED_("ERROR:") " invalid baudrate: -b " _YELLOW_("%s") "\n", argv[i + 1]);
905 return 1;
907 speed = tmpspeed;
908 i++;
909 continue;
912 // wait for comport
913 if (strcmp(argv[i], "-w") == 0 || strcmp(argv[i], "--wait") == 0) {
914 waitCOMPort = true;
915 continue;
918 // execute pm3 command
919 if (strcmp(argv[i], "-c") == 0 || strcmp(argv[i], "--command") == 0) {
920 if (i + 1 == argc) {
921 PrintAndLogEx(ERR, _RED_("ERROR:") " missing command specification after -c\n");
922 show_help(false, exec_name);
923 return 1;
925 script_cmd = argv[++i];
926 continue;
929 // execute pm3 command file
930 if (strcmp(argv[i], "-s") == 0 || strcmp(argv[i], "--script-file") == 0) {
931 if (i + 1 == argc || strlen(argv[i + 1]) == 0) {
932 PrintAndLogEx(ERR, _RED_("ERROR:") " missing script file specification after -s\n");
933 show_help(false, exec_name);
934 return 1;
936 script_cmds_file = argv[++i];
937 continue;
940 // execute lua script
941 if (strcmp(argv[i], "-l") == 0 || strcmp(argv[i], "--lua") == 0) {
942 if (i + 1 == argc || strlen(argv[i + 1]) == 0) {
943 PrintAndLogEx(ERR, _RED_("ERROR:") " missing lua script specification after -l\n");
944 show_help(false, exec_name);
945 return 1;
947 script_cmd = argv[++i];
948 if (script_cmd == NULL || strlen(script_cmd) == 0) {
949 return 1;
951 addLuaExec = true;
952 continue;
955 // go to interactive instead of quitting after a script/command
956 if (strcmp(argv[i], "-i") == 0 || strcmp(argv[i], "--interactive") == 0) {
957 stayInCommandLoop = true;
958 continue;
961 // do not use history nor log files
962 if (strcmp(argv[i], "--incognito") == 0) {
963 session.incognito = true;
964 continue;
967 // go to flash mode
968 if (strcmp(argv[i], "--flash") == 0) {
969 flash_mode = true;
970 continue;
973 // unlock bootloader area
974 if (strcmp(argv[i], "--unlock-bootloader") == 0) {
975 flash_can_write_bl = true;
976 continue;
979 // flash file
980 if (strcmp(argv[i], "--image") == 0) {
981 if (flash_num_files == FLASH_MAX_FILES) {
982 PrintAndLogEx(ERR, _RED_("ERROR:") " too many --image, please use it max %i times\n", FLASH_MAX_FILES);
983 return 1;
985 if (i + 1 == argc) {
986 PrintAndLogEx(ERR, _RED_("ERROR:") " missing image specification after --image\n");
987 show_help(false, exec_name);
988 return 1;
990 flash_filenames[flash_num_files++] = argv[++i];
991 continue;
994 // We got an unknown parameter
995 PrintAndLogEx(ERR, _RED_("ERROR:") " invalid parameter: " _YELLOW_("%s") "\n", argv[i]);
996 show_help(false, exec_name);
997 return 1;
1000 // Load Settings and assign
1001 // This will allow the command line to override the settings.json values
1002 preferences_load();
1003 // quick patch for debug level
1004 if (! debug_mode_forced)
1005 g_debugMode = session.client_debug_level;
1006 // settings_save ();
1007 // End Settings
1009 // even if prefs, we disable colors if stdin or stdout is not a TTY
1010 if ((! session.stdinOnTTY) || (! session.stdoutOnTTY)) {
1011 session.supports_colors = false;
1012 session.emoji_mode = EMO_ALTTEXT;
1015 // Let's take a baudrate ok for real UART, USB-CDC & BT don't use that info anyway
1016 if (speed == 0)
1017 speed = USART_BAUD_RATE;
1019 if (flash_mode) {
1020 flash_pm3(port, flash_num_files, flash_filenames, flash_can_write_bl);
1021 exit(EXIT_SUCCESS);
1024 if (script_cmd) {
1025 while (script_cmd[strlen(script_cmd) - 1] == ' ')
1026 script_cmd[strlen(script_cmd) - 1] = 0x00;
1028 if (strlen(script_cmd) == 0) {
1029 script_cmd = NULL;
1030 PrintAndLogEx(ERR, _RED_("ERROR:") " execute command: " _YELLOW_("command not found") ".\n");
1031 return 2;
1032 } else {
1033 if (addLuaExec) {
1034 // add "script run " to command
1035 int len = strlen(script_cmd) + 11 + 1;
1036 char *ctmp = (char *) calloc(len, sizeof(uint8_t));
1037 if (ctmp != NULL) {
1038 memset(ctmp, 0, len);
1039 strcpy(ctmp, "script run ");
1040 strcpy(&ctmp[11], script_cmd);
1041 script_cmd = ctmp;
1045 PrintAndLogEx(SUCCESS, "execute command from commandline: " _YELLOW_("%s") "\n", script_cmd);
1049 // try to open USB connection to Proxmark
1050 if (port != NULL) {
1051 OpenProxmark(&session.current_device, port, waitCOMPort, 20, false, speed);
1054 if (session.pm3_present && (TestProxmark(session.current_device) != PM3_SUCCESS)) {
1055 PrintAndLogEx(ERR, _RED_("ERROR:") " cannot communicate with the Proxmark\n");
1056 CloseProxmark(session.current_device);
1059 if ((port != NULL) && (!session.pm3_present))
1060 exit(EXIT_FAILURE);
1062 if (!session.pm3_present)
1063 PrintAndLogEx(INFO, "Running in " _YELLOW_("OFFLINE") " mode. Check " _YELLOW_("\"%s -h\"") " if it's not what you want.\n", exec_name);
1065 // ascii art only in interactive client
1066 if (!script_cmds_file && !script_cmd && session.stdinOnTTY && session.stdoutOnTTY && !flash_mode)
1067 showBanner();
1069 // Save settings if not loaded from settings json file.
1070 // Doing this here will ensure other checks and updates are saved to over rule default
1071 // e.g. Linux color use check
1072 if ((!session.preferences_loaded) && (!session.incognito)) {
1073 PrintAndLogEx(INFO, "Creating initial preferences file"); // json save reports file name, so just info msg here
1074 preferences_save(); // Save defaults
1075 session.preferences_loaded = true;
1076 } /* else {
1077 // Set device debug level
1078 PrintAndLogEx(INFO,"setting device debug loglevel");
1079 if (session.pm3_present) {
1080 SendCommandNG(CMD_SET_DBGMODE, &session.device_debug_level, 1);
1081 PacketResponseNG resp;
1082 if (WaitForResponseTimeout(CMD_SET_DBGMODE, &resp, 2000) == false)
1083 PrintAndLogEx (INFO,"failed to set device debug loglevel");
1085 else
1086 PrintAndLogEx(WARNING,"Proxmark3 not ready to set debug level");
1090 #ifdef HAVE_GUI
1092 # if defined(_WIN32)
1093 InitGraphics(argc, argv, script_cmds_file, script_cmd, stayInCommandLoop);
1094 MainGraphics();
1095 # else
1096 // for *nix distro's, check environment variable to verify a display
1097 char *display = getenv("DISPLAY");
1098 if (display && strlen(display) > 1) {
1099 InitGraphics(argc, argv, script_cmds_file, script_cmd, stayInCommandLoop);
1100 MainGraphics();
1101 } else {
1102 main_loop(script_cmds_file, script_cmd, stayInCommandLoop);
1104 # endif
1106 #else
1107 main_loop(script_cmds_file, script_cmd, stayInCommandLoop);
1108 #endif
1110 // Clean up the port
1111 if (session.pm3_present) {
1112 CloseProxmark(session.current_device);
1115 if (session.window_changed) // Plot/Overlay moved or resized
1116 preferences_save();
1117 exit(EXIT_SUCCESS);
1119 #endif //LIBPM3