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 #include "proxmark3.h"
15 #include <stdio.h> // for Mingw readline
19 #include <readline/readline.h>
20 #include <readline/history.h>
24 #include <libgen.h> // basename
26 #include "usart_defs.h"
27 #include "util_posix.h"
34 #include "fileutils.h"
36 #include "preferences.h"
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
) {
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
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
, __
);
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
);
81 PrintAndLogEx(NORMAL
, " ######. ###. ###.#####. ");
82 PrintAndLogEx(NORMAL
, " ##...##.####. ####. ...##.");
83 PrintAndLogEx(NORMAL
, " ######..##.####.##. ####..");
84 PrintAndLogEx(NORMAL
, " ##..... ##..##..##. ..##.");
85 PrintAndLogEx(NORMAL
, " ##. ##. .. ##.#####.. " BANNERMSG1
);
86 PrintAndLogEx(NORMAL
, " .. .. .. ..... " BANNERMSG2
);
90 PrintAndLogEx(NORMAL
, "");
91 PrintAndLogEx(NORMAL
, BANNERMSG3
);
94 static void showBanner(void) {
96 g_printAndLog
= PRINTANDLOG_PRINT
;
97 PrintAndLogEx(NORMAL
, "\n");
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
);
104 showBanner_logo(ANSI
);
106 #elif defined(__linux__) || defined(__APPLE__)
107 showBanner_logo(ANSI
);
109 showBanner_logo(ASCII
);
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
, "");
116 g_printAndLog
= PRINTANDLOG_PRINT
| PRINTANDLOG_LOG
;
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
;
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();
140 CloseProxmark(session
.current_device
);
146 static void flush_history(void) {
147 if (session
.history_path
) {
148 write_history(session
.history_path
);
149 free(session
.history_path
);
155 static bool WINAPI terminate_handler(DWORD t) {
156 if (t == CTRL_C_EVENT) {
164 struct sigaction old_action
;
165 static void terminate_handler(int signum
) {
166 sigaction(SIGINT
, &old_action
, NULL
);
175 static bool DetectWindowsAnsiSupport(void) {
176 #ifndef ENABLE_VIRTUAL_TERMINAL_PROCESSING
177 #define ENABLE_VIRTUAL_TERMINAL_PROCESSING 0x0004
180 // disable colors if stdin or stdout are redirected
181 if ((! session
.stdinOnTTY
) || (! session
.stdoutOnTTY
))
184 HANDLE hOut
= GetStdHandle(STD_OUTPUT_HANDLE
);
186 GetConsoleMode(hOut
, &dwMode
);
188 //ENABLE_VIRTUAL_TERMINAL_PROCESSING is already set
189 if ((dwMode
& ENABLE_VIRTUAL_TERMINAL_PROCESSING
))
192 dwMode
|= ENABLE_VIRTUAL_TERMINAL_PROCESSING
;
194 return SetConsoleMode(hOut
, dwMode
) ? true : false;
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
);
209 FILE *f
= fopen(path
, "r");
213 if (cmdscriptfile_idx
== 0)
214 cmdscriptfile_stayafter
= stayafter
;
216 cmdscriptfile
[++cmdscriptfile_idx
] = f
;
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
;
233 // Main thread of PM3 Client
235 #ifdef __has_attribute
236 #if __has_attribute(force_align_arg_pointer)
237 __attribute__((force_align_arg_pointer
))
240 main_loop(char *script_cmds_file
, char *script_cmd
, bool stayInCommandLoop
) {
243 bool execCommand
= (script_cmd
!= NULL
);
244 bool fromInteractive
= false;
245 uint16_t script_cmd_len
= 0;
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);
258 pm3_version(true, false);
261 if (script_cmds_file
) {
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
);
269 PrintAndLogEx(ERR
, "could not open " _YELLOW_("%s") "...", path
);
275 session
.history_path
= NULL
;
276 if (session
.incognito
) {
277 PrintAndLogEx(INFO
, "No history will be recorded");
279 if (searchHomeFilePath(&session
.history_path
, NULL
, PROXHISTORY
, true) != PM3_SUCCESS
) {
280 PrintAndLogEx(ERR
, "No history will be recorded");
281 session
.history_path
= NULL
;
285 // SetConsoleCtrlHandler((PHANDLER_ROUTINE)terminate_handler, true);
287 struct sigaction action
;
288 memset(&action
, 0, sizeof(action
));
289 action
.sa_handler
= &terminate_handler
;
290 sigaction(SIGINT
, &action
, &old_action
);
292 rl_catch_signals
= 1;
294 read_history(session
.history_path
);
299 // loops every time enter is pressed...
302 bool printprompt
= false;
303 if (session
.pm3_present
) {
304 if (conn
.send_via_fpc_usart
== false)
305 prompt_dev
= PROXPROMPT_DEV_USB
;
307 prompt_dev
= PROXPROMPT_DEV_FPC
;
309 prompt_dev
= PROXPROMPT_DEV_OFFLINE
;
313 // If there is a script file
314 if (current_cmdscriptfile()) {
317 memset(script_cmd_buf
, 0, sizeof(script_cmd_buf
));
320 if (fgets(script_cmd_buf
, sizeof(script_cmd_buf
), current_cmdscriptfile()) == NULL
) {
321 if (!pop_cmdscriptfile())
326 prompt_ctx
= PROXPROMPT_CTX_SCRIPTFILE
;
328 strcleanrn(script_cmd_buf
, sizeof(script_cmd_buf
));
330 cmd
= str_dup(script_cmd_buf
);
335 // If there is a script command
337 prompt_ctx
= stdinOnPipe
? PROXPROMPT_CTX_STDIN
: PROXPROMPT_CTX_SCRIPTCMD
;
339 cmd
= str_dup(script_cmd
);
340 if ((cmd
!= NULL
) && (! fromInteractive
))
343 uint16_t len
= strlen(script_cmd
) + 1;
346 if (script_cmd_len
== len
- 1)
349 script_cmd_len
-= len
;
351 // exit after exec command
352 if (script_cmd
&& !stayInCommandLoop
)
355 // if there is a pipe from stdin
358 memset(script_cmd_buf
, 0, sizeof(script_cmd_buf
));
360 if (fgets(script_cmd_buf
, sizeof(script_cmd_buf
), stdin
) == NULL
) {
361 PrintAndLogEx(ERR
, "STDIN unexpected end, exit...");
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');
371 strcleanrn(script_cmd
, script_cmd_len
);
375 rl_event_hook
= check_comm
;
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;
386 script_cmd
= readline(prompt_filtered
);
388 //Check if color support needs to be enabled again in case the window buffer did change
389 session
.supports_colors
= DetectWindowsAnsiSupport();
391 if (script_cmd
!= NULL
) {
393 stayInCommandLoop
= true;
394 fromInteractive
= true;
395 script_cmd_len
= strlen(script_cmd
);
396 strcreplace(script_cmd
, script_cmd_len
, ';', '\0');
398 strcleanrn(script_cmd
, script_cmd_len
);
402 printf("%s", prompt_filtered
);
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
);
422 size_t l
= strlen(cmd
);
423 while (l
> 0 && isspace(cmd
[l
- 1])) {
428 while ((cmd
[off
] != '\0') && isspace(cmd
[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') {
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
;
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)) {
461 g_pendingPrompt
= false;
462 int ret
= CommandReceived(cmd
);
464 if (ret
== PM3_EFATAL
)
470 PrintAndLogEx(NORMAL
, "\n");
471 if (script_cmds_file
&& stayInCommandLoop
)
472 stayInCommandLoop
= false;
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())
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.");
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)
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");
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
);
573 PrintAndLogEx(WARNING
, "failed to allocate memory");
584 for (int i
= 0; i
< strlen(cwd_buffer
); i
++) {
585 if (cwd_buffer
[i
] == '\\') {
590 my_user_directory
= cwd_buffer
;
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
);
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
);
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");
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");
660 for (int i
= 0 ; i
< num_files
; ++i
) {
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
) {
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"));
684 PrintAndLogEx(ERR
, "Could not find Proxmark3 on " _RED_("%s") ".\n", serial_port_name
);
689 uint32_t max_allowed
= 0;
690 ret
= flash_start_flashing(can_write_bl
, serial_port_name
, &max_allowed
);
691 if (ret
!= PM3_SUCCESS
) {
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
) {
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
) {
713 flash_free(&files
[i
]);
714 PrintAndLogEx(NORMAL
, "");
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
);
723 for (int i
= 0 ; i
< num_files
; ++i
) {
724 if (filepaths
[i
] != NULL
)
727 if (ret
== PM3_SUCCESS
)
728 PrintAndLogEx(SUCCESS
, _CYAN_("All done"));
730 PrintAndLogEx(ERR
, "Aborted on error");
731 PrintAndLogEx(INFO
, "\nHave a nice day!");
736 void pm3_init(void) {
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();
754 int main(int argc
, char *argv
[]) {
756 bool waitCOMPort
= false;
757 bool addLuaExec
= false;
758 bool stayInCommandLoop
= false;
759 char *script_cmds_file
= NULL
;
760 char *script_cmd
= NULL
;
765 /* initialize 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
];
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
;
804 for (int i
= 1; i
< argc
; i
++) {
806 if (argv
[i
][0] != '-') {
807 // For backward compatibility we accept direct port
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
);
819 if (strcmp(argv
[i
], "-p") == 0 || strcmp(argv
[i
], "--port") == 0) {
821 PrintAndLogEx(ERR
, _RED_("ERROR:") " missing port specification after -p\n");
822 show_help(false, exec_name
);
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
);
836 if (strcmp(argv
[i
], "-h") == 0 || strcmp(argv
[i
], "--help") == 0) {
837 g_printAndLog
= PRINTANDLOG_PRINT
;
838 show_help(true, exec_name
);
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);
851 if (strcmp(argv
[i
], "--fulltext") == 0) {
852 g_printAndLog
= PRINTANDLOG_PRINT
;
853 show_help(false, exec_name
);
854 dumpAllHelp(0, true);
859 if (strcmp(argv
[i
], "-m") == 0 || strcmp(argv
[i
], "--markdown") == 0) {
860 g_printAndLog
= PRINTANDLOG_PRINT
;
861 dumpAllHelp(1, false);
864 // print client version
865 if (strcmp(argv
[i
], "-v") == 0 || strcmp(argv
[i
], "--version") == 0) {
866 pm3_version(true, true);
871 if (strcmp(argv
[i
], "-d") == 0 || strcmp(argv
[i
], "--debug") == 0) {
873 PrintAndLogEx(ERR
, _RED_("ERROR:") " missing debugmode specification after -d\n");
874 show_help(false, exec_name
);
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]);
883 debug_mode_forced
= true;
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");
896 if (strcmp(argv
[i
], "-b") == 0 || strcmp(argv
[i
], "--baud") == 0) {
898 PrintAndLogEx(ERR
, _RED_("ERROR:") " missing baud specification after -b\n");
899 show_help(false, exec_name
);
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]);
913 if (strcmp(argv
[i
], "-w") == 0 || strcmp(argv
[i
], "--wait") == 0) {
918 // execute pm3 command
919 if (strcmp(argv
[i
], "-c") == 0 || strcmp(argv
[i
], "--command") == 0) {
921 PrintAndLogEx(ERR
, _RED_("ERROR:") " missing command specification after -c\n");
922 show_help(false, exec_name
);
925 script_cmd
= argv
[++i
];
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
);
936 script_cmds_file
= argv
[++i
];
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
);
947 script_cmd
= argv
[++i
];
948 if (script_cmd
== NULL
|| strlen(script_cmd
) == 0) {
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;
961 // do not use history nor log files
962 if (strcmp(argv
[i
], "--incognito") == 0) {
963 session
.incognito
= true;
968 if (strcmp(argv
[i
], "--flash") == 0) {
973 // unlock bootloader area
974 if (strcmp(argv
[i
], "--unlock-bootloader") == 0) {
975 flash_can_write_bl
= true;
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
);
986 PrintAndLogEx(ERR
, _RED_("ERROR:") " missing image specification after --image\n");
987 show_help(false, exec_name
);
990 flash_filenames
[flash_num_files
++] = argv
[++i
];
994 // We got an unknown parameter
995 PrintAndLogEx(ERR
, _RED_("ERROR:") " invalid parameter: " _YELLOW_("%s") "\n", argv
[i
]);
996 show_help(false, exec_name
);
1000 // Load Settings and assign
1001 // This will allow the command line to override the settings.json values
1003 // quick patch for debug level
1004 if (! debug_mode_forced
)
1005 g_debugMode
= session
.client_debug_level
;
1006 // settings_save ();
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
1017 speed
= USART_BAUD_RATE
;
1020 flash_pm3(port
, flash_num_files
, flash_filenames
, flash_can_write_bl
);
1025 while (script_cmd
[strlen(script_cmd
) - 1] == ' ')
1026 script_cmd
[strlen(script_cmd
) - 1] = 0x00;
1028 if (strlen(script_cmd
) == 0) {
1030 PrintAndLogEx(ERR
, _RED_("ERROR:") " execute command: " _YELLOW_("command not found") ".\n");
1034 // add "script run " to command
1035 int len
= strlen(script_cmd
) + 11 + 1;
1036 char *ctmp
= (char *) calloc(len
, sizeof(uint8_t));
1038 memset(ctmp
, 0, len
);
1039 strcpy(ctmp
, "script run ");
1040 strcpy(&ctmp
[11], script_cmd
);
1045 PrintAndLogEx(SUCCESS
, "execute command from commandline: " _YELLOW_("%s") "\n", script_cmd
);
1049 // try to open USB connection to Proxmark
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
))
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
)
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;
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");
1086 PrintAndLogEx(WARNING,"Proxmark3 not ready to set debug level");
1092 # if defined(_WIN32)
1093 InitGraphics(argc
, argv
, script_cmds_file
, script_cmd
, stayInCommandLoop
);
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
);
1102 main_loop(script_cmds_file
, script_cmd
, stayInCommandLoop
);
1107 main_loop(script_cmds_file
, script_cmd
, stayInCommandLoop
);
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