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"
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
) {
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() && 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
;
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
);
140 CloseProxmark(g_session
.current_device
);
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
);
155 static bool WINAPI terminate_handler(DWORD t) {
156 if (t == CTRL_C_EVENT) {
164 static struct sigaction gs_old_sigint_action
;
165 static void sigint_handler(int signum
) {
166 sigaction(SIGINT
, &gs_old_sigint_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 ((! g_session
.stdinOnTTY
) || (! g_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 (g_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 g_session
.history_path
= NULL
;
276 if (g_session
.incognito
) {
277 PrintAndLogEx(INFO
, "No history will be recorded");
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
;
285 // SetConsoleCtrlHandler((PHANDLER_ROUTINE)terminate_handler, true);
287 struct sigaction action
;
288 memset(&action
, 0, sizeof(action
));
289 action
.sa_handler
= &sigint_handler
;
290 sigaction(SIGINT
, &action
, &gs_old_sigint_action
);
292 rl_catch_signals
= 1;
294 read_history(g_session
.history_path
);
299 // loops every time enter is pressed...
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
;
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
), !g_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 g_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 PrintAndLogEx(NORMAL
, "\nNikola.D: %d", ret
);
467 if (ret
== PM3_EFATAL
)
473 PrintAndLogEx(NORMAL
, "\n");
474 if (script_cmds_file
&& stayInCommandLoop
)
475 stayInCommandLoop
= false;
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())
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.");
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)
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");
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
);
576 PrintAndLogEx(WARNING
, "failed to allocate memory");
587 for (int i
= 0; i
< strlen(cwd_buffer
); i
++) {
588 if (cwd_buffer
[i
] == '\\') {
593 my_user_directory
= cwd_buffer
;
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
);
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
);
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");
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");
663 for (int i
= 0 ; i
< num_files
; ++i
) {
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
) {
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"));
687 PrintAndLogEx(ERR
, "Could not find Proxmark3 on " _RED_("%s") ".\n", serial_port_name
);
692 uint32_t max_allowed
= 0;
693 ret
= flash_start_flashing(can_write_bl
, serial_port_name
, &max_allowed
);
694 if (ret
!= PM3_SUCCESS
) {
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
) {
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
) {
716 flash_free(&files
[i
]);
717 PrintAndLogEx(NORMAL
, "");
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
);
726 for (int i
= 0 ; i
< num_files
; ++i
) {
727 if (filepaths
[i
] != NULL
)
730 if (ret
== PM3_SUCCESS
)
731 PrintAndLogEx(SUCCESS
, _CYAN_("All done"));
733 PrintAndLogEx(ERR
, "Aborted on error");
734 PrintAndLogEx(INFO
, "\nHave a nice day!");
739 void pm3_init(void) {
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();
757 int main(int argc
, char *argv
[]) {
759 bool waitCOMPort
= false;
760 bool addLuaExec
= false;
761 bool stayInCommandLoop
= false;
762 char *script_cmds_file
= NULL
;
763 char *script_cmd
= NULL
;
768 /* initialize 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
];
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
;
807 for (int i
= 1; i
< argc
; i
++) {
809 if (argv
[i
][0] != '-') {
810 // For backward compatibility we accept direct port
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
);
822 if (strcmp(argv
[i
], "-p") == 0 || strcmp(argv
[i
], "--port") == 0) {
824 PrintAndLogEx(ERR
, _RED_("ERROR:") " missing port specification after -p\n");
825 show_help(false, exec_name
);
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
);
839 if (strcmp(argv
[i
], "-h") == 0 || strcmp(argv
[i
], "--help") == 0) {
840 g_printAndLog
= PRINTANDLOG_PRINT
;
841 show_help(true, exec_name
);
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);
854 if (strcmp(argv
[i
], "--fulltext") == 0) {
855 g_printAndLog
= PRINTANDLOG_PRINT
;
856 show_help(false, exec_name
);
857 dumpAllHelp(0, true);
862 if (strcmp(argv
[i
], "-m") == 0 || strcmp(argv
[i
], "--markdown") == 0) {
863 g_printAndLog
= PRINTANDLOG_PRINT
;
864 dumpAllHelp(1, false);
867 // print client version
868 if (strcmp(argv
[i
], "-v") == 0 || strcmp(argv
[i
], "--version") == 0) {
869 pm3_version(true, true);
874 if (strcmp(argv
[i
], "-d") == 0 || strcmp(argv
[i
], "--debug") == 0) {
876 PrintAndLogEx(ERR
, _RED_("ERROR:") " missing debugmode specification after -d\n");
877 show_help(false, exec_name
);
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]);
886 debug_mode_forced
= true;
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");
899 if (strcmp(argv
[i
], "-b") == 0 || strcmp(argv
[i
], "--baud") == 0) {
901 PrintAndLogEx(ERR
, _RED_("ERROR:") " missing baud specification after -b\n");
902 show_help(false, exec_name
);
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]);
916 if (strcmp(argv
[i
], "-w") == 0 || strcmp(argv
[i
], "--wait") == 0) {
921 // execute pm3 command
922 if (strcmp(argv
[i
], "-c") == 0 || strcmp(argv
[i
], "--command") == 0) {
924 PrintAndLogEx(ERR
, _RED_("ERROR:") " missing command specification after -c\n");
925 show_help(false, exec_name
);
928 script_cmd
= argv
[++i
];
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
);
939 script_cmds_file
= argv
[++i
];
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
);
950 script_cmd
= argv
[++i
];
951 if (script_cmd
== NULL
|| strlen(script_cmd
) == 0) {
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;
964 // do not use history nor log files
965 if (strcmp(argv
[i
], "--incognito") == 0) {
966 g_session
.incognito
= true;
971 if (strcmp(argv
[i
], "--flash") == 0) {
976 // unlock bootloader area
977 if (strcmp(argv
[i
], "--unlock-bootloader") == 0) {
978 flash_can_write_bl
= true;
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
);
989 PrintAndLogEx(ERR
, _RED_("ERROR:") " missing image specification after --image\n");
990 show_help(false, exec_name
);
993 flash_filenames
[flash_num_files
++] = argv
[++i
];
997 // We got an unknown parameter
998 PrintAndLogEx(ERR
, _RED_("ERROR:") " invalid parameter: " _YELLOW_("%s") "\n", argv
[i
]);
999 show_help(false, exec_name
);
1003 // Load Settings and assign
1004 // This will allow the command line to override the settings.json values
1006 // quick patch for debug level
1007 if (! debug_mode_forced
)
1008 g_debugMode
= g_session
.client_debug_level
;
1009 // settings_save ();
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
1020 speed
= USART_BAUD_RATE
;
1023 flash_pm3(port
, flash_num_files
, flash_filenames
, flash_can_write_bl
);
1028 while (script_cmd
[strlen(script_cmd
) - 1] == ' ')
1029 script_cmd
[strlen(script_cmd
) - 1] = 0x00;
1031 if (strlen(script_cmd
) == 0) {
1033 PrintAndLogEx(ERR
, _RED_("ERROR:") " execute command: " _YELLOW_("command not found") ".\n");
1037 // add "script run " to command
1038 int len
= strlen(script_cmd
) + 11 + 1;
1039 char *ctmp
= (char *) calloc(len
, sizeof(uint8_t));
1041 memset(ctmp
, 0, len
);
1042 strcpy(ctmp
, "script run ");
1043 strcpy(&ctmp
[11], script_cmd
);
1048 PrintAndLogEx(SUCCESS
, "execute command from commandline: " _YELLOW_("%s") "\n", script_cmd
);
1052 // try to open USB connection to Proxmark
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
))
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
)
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;
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");
1089 PrintAndLogEx(WARNING,"Proxmark3 not ready to set debug level");
1095 # if defined(_WIN32)
1096 InitGraphics(argc
, argv
, script_cmds_file
, script_cmd
, stayInCommandLoop
);
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
);
1105 main_loop(script_cmds_file
, script_cmd
, stayInCommandLoop
);
1110 main_loop(script_cmds_file
, script_cmd
, stayInCommandLoop
);
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