fix one too small
[RRG-proxmark3.git] / client / src / proxmark3.c
blobf5da41dc7520e1646e2f3db41ef7be87314d1a63
1 //-----------------------------------------------------------------------------
2 // Copyright (C) Proxmark3 contributors. See AUTHORS.md for details.
3 //
4 // This program is free software: you can redistribute it and/or modify
5 // it under the terms of the GNU General Public License as published by
6 // the Free Software Foundation, either version 3 of the License, or
7 // (at your option) any later version.
8 //
9 // This program is distributed in the hope that it will be useful,
10 // but WITHOUT ANY WARRANTY; without even the implied warranty of
11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 // GNU General Public License for more details.
14 // See LICENSE.txt for the text of the license.
15 //-----------------------------------------------------------------------------
16 // Main binary
17 //-----------------------------------------------------------------------------
19 #include "proxmark3.h"
21 #include <stdlib.h>
22 #include <limits.h>
23 #include <unistd.h>
24 #include <ctype.h>
25 #include <libgen.h> // basename
26 #include <time.h>
28 #include "pm3line.h"
29 #include "usart_defs.h"
30 #include "util_posix.h"
31 #include "proxgui.h"
32 #include "cmdmain.h"
33 #include "ui.h"
34 #include "cmdhw.h"
35 #include "whereami.h"
36 #include "comms.h"
37 #include "fileutils.h"
38 #include "flash.h"
39 #include "preferences.h"
40 #include "commonutil.h"
42 #ifndef _WIN32
43 #include <locale.h>
44 #endif
47 static int mainret = PM3_ESOFT;
49 #ifndef LIBPM3
50 #define BANNERMSG1 ""
51 #define BANNERMSG2 " [ :coffee: ]"
52 #define BANNERMSG3 ""
54 typedef enum LogoMode { UTF8, ANSI, ASCII } LogoMode;
56 static void showBanner_logo(LogoMode mode) {
57 switch (mode) {
58 case UTF8: {
59 const char *sq = "\xE2\x96\x88"; // square block
60 const char *tr = "\xE2\x95\x97"; // top right corner
61 const char *tl = "\xE2\x95\x94"; // top left corner
62 const char *br = "\xE2\x95\x9D"; // bottom right corner
63 const char *bl = "\xE2\x95\x9A"; // bottom left corner
64 const char *hl = "\xE2\x95\x90"; // horiz line
65 const char *vl = "\xE2\x95\x91"; // vert line
66 const char *__ = " ";
68 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"),
69 sq, sq, sq, sq, sq, sq, tr, __, sq, sq, sq, tr, __, __, __, sq, sq, sq, tr, sq, sq, sq, sq, sq, tr, __);
70 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"),
71 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);
72 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"),
73 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);
74 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"),
75 sq, sq, tl, hl, hl, hl, br, __, sq, sq, vl, bl, sq, sq, tl, br, sq, sq, vl, __, bl, hl, hl, sq, sq, tr);
76 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,
77 sq, sq, vl, __, __, __, __, __, sq, sq, vl, __, bl, hl, br, __, sq, sq, vl, sq, sq, sq, sq, sq, tl, br);
78 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,
79 bl, hl, br, __, __, __, __, __, bl, hl, br, __, __, __, __, __, bl, hl, br, bl, hl, hl, hl, hl, br, __);
80 break;
82 case ANSI: {
83 PrintAndLogEx(NORMAL, " " _CYAN_("8888888b. 888b d888 .d8888b. "));
84 PrintAndLogEx(NORMAL, " " _CYAN_("888 Y88b 8888b d8888 d88P Y88b "));
85 PrintAndLogEx(NORMAL, " " _CYAN_("888 888 88888b.d88888 .d88P "));
86 PrintAndLogEx(NORMAL, " " _CYAN_("888 d88P 888Y88888P888 8888\" "));
87 PrintAndLogEx(NORMAL, " " _CYAN_("8888888P\" 888 Y888P 888 \"Y8b. "));
88 PrintAndLogEx(NORMAL, " " _CYAN_("888 888 Y8P 888 888 888 "));
89 PrintAndLogEx(NORMAL, " " _CYAN_("888 888 \" 888 Y88b d88P") " " BANNERMSG1);
90 PrintAndLogEx(NORMAL, " " _CYAN_("888 888 888 \"Y8888P\"") " " BANNERMSG2);
91 break;
93 case ASCII: {
94 PrintAndLogEx(NORMAL, " 8888888b. 888b d888 .d8888b. ");
95 PrintAndLogEx(NORMAL, " 888 Y88b 8888b d8888 d88P Y88b ");
96 PrintAndLogEx(NORMAL, " 888 888 88888b.d88888 .d88P ");
97 PrintAndLogEx(NORMAL, " 888 d88P 888Y88888P888 8888\" ");
98 PrintAndLogEx(NORMAL, " 8888888P\" 888 Y888P 888 \"Y8b. ");
99 PrintAndLogEx(NORMAL, " 888 888 Y8P 888 888 888 ");
100 PrintAndLogEx(NORMAL, " 888 888 \" 888 Y88b d88P " BANNERMSG1);
101 PrintAndLogEx(NORMAL, " 888 888 888 \"Y8888P\" " BANNERMSG2);
102 break;
105 PrintAndLogEx(NORMAL, "");
106 PrintAndLogEx(NORMAL, BANNERMSG3);
109 static uint8_t detect_current_lang(void) {
110 #ifndef _WIN32
111 const char *lang = setlocale(LC_ALL, "");
112 if (lang == NULL) {
113 return 1;
115 if (memcmp(lang, "fr", 2) == 0) {
116 return 2;
118 if (memcmp(lang, "es", 2) == 0) {
119 return 3;
121 #endif
122 return 1;
125 static const char *get_quote(void) {
127 const char *quotes_en[] = {
128 "Fund creativity, empower dreams",
129 "Invest in open innovation",
130 "Donate, empower, grow, sustain",
131 "Back global innovation today",
132 "Fuel open source revolution",
133 "Contribute funds, drive progress",
134 "Sponsor innovation, build tomorrow",
135 "Consider supporting: fund innovation",
136 "Your donation fuels progress",
137 "Empower dreams with your support",
138 "Join us: finance creative freedom",
139 "Make an impact: donate today",
140 "Help us drive open innovation",
141 "Your support, our future",
142 "Invest in a better tomorrow",
143 "Every contribution powers change",
144 "Support us, shape the future",
145 "Ignite change: support open-source creativity",
146 "Together, we can innovate without limits",
149 const char *quotes_fr[] = {
150 "Financez la créativité, donnez pouvoir aux rêves",
151 "Investissez dans l'innovation ouverte",
152 "Donnez, habilitez, croissez, soutenez",
153 "Soutenez l'innovation mondiale aujourd'hui",
154 "Alimentez la révolution open source",
155 "Contribuez financièrement, poussez le progrès",
156 "Parrainez l'innovation, construisez demain",
157 "Envisagez de soutenir : financez l'innovation",
158 "Votre don alimente le progrès",
159 "Donnez pouvoir aux rêves avec votre soutien",
160 "Rejoignez-nous : financez la liberté créative",
161 "Faites une différence : donnez aujourd'hui",
162 "Aidez-nous à stimuler l'innovation ouverte",
163 "Votre soutien, notre avenir",
164 "Investissez dans un meilleur demain",
165 "Chaque contribution favorise le changement",
166 "Soutenez-nous, façonnez l'avenir",
167 "Allumez le changement : soutenez la créativité open-source",
168 "Ensemble, nous pouvons innover sans limites",
171 const char *quotes_es[] = {
172 "Financia la creatividad, empodera sueños",
173 "Invierte en innovación abierta",
174 "Dona, empodera, crece, sostén",
175 "Apoya la innovación global hoy",
176 "Impulsa la revolución de código abierto",
177 "Contribuye fondos, impulsa el progreso",
178 "Patrocina la innovación, construye el mañana",
179 "Considera apoyar: financia la innovación",
180 "Tu donación impulsa el progreso",
181 "Empodera sueños con tu apoyo",
182 "Únete a nosotros: financia la libertad creativa",
183 "Haz un impacto: dona hoy",
184 "Ayúdanos a impulsar la innovación abierta",
185 "Tu apoyo, nuestro futuro",
186 "Invierte en un mejor mañana",
187 "Cada contribución impulsa el cambio",
188 "Apóyanos, forma el futuro",
189 "Enciende el cambio: apoya la creatividad de código abierto",
190 "Juntos, podemos innovar sin límites",
193 srand((uint32_t)time(NULL));
194 int r = rand() % ARRAYLEN(quotes_en);
196 uint8_t lang = detect_current_lang();
197 switch (lang) {
198 case 2:
199 return quotes_fr[r];
200 case 3:
201 return quotes_es[r];
202 case 1:
203 default:
204 return quotes_en[r];
208 static void showBanner(void) {
209 uint8_t old_printAndLog = g_printAndLog;
210 g_printAndLog &= PRINTANDLOG_PRINT;
211 PrintAndLogEx(NORMAL, "\n");
213 #if defined(_WIN32)
214 if (GetConsoleCP() == 65001) {
215 // If on Windows and using UTF-8 then we need utf-8 ascii art for banner.
216 showBanner_logo(UTF8);
217 } else {
218 showBanner_logo(ANSI);
220 #elif defined(__linux__) || defined(__APPLE__)
221 showBanner_logo(ANSI);
222 #else
223 showBanner_logo(ASCII);
224 #endif
226 PrintAndLogEx(NORMAL, "");
227 PrintAndLogEx(NORMAL, " [ " _YELLOW_("%s!")" ]", get_quote());
228 PrintAndLogEx(NORMAL, " Patreon - https://www.patreon.com/iceman1001/");
229 PrintAndLogEx(NORMAL, " Paypal - https://www.paypal.me/iceman1001/");
230 PrintAndLogEx(NORMAL, "");
231 // PrintAndLogEx(NORMAL, " Monero");
232 // PrintAndLogEx(NORMAL, " 43mNJLpgBVaTvyZmX9ajcohpvVkaRy1kbZPm8tqAb7itZgfuYecgkRF36rXrKFUkwEGeZedPsASRxgv4HPBHvJwyJdyvQuP");
233 PrintAndLogEx(NORMAL, "");
234 fflush(stdout);
235 g_printAndLog = old_printAndLog;
237 #endif //LIBPM3
239 static const char *prompt_dev = "";
240 static const char *prompt_ctx = "";
241 static const char *prompt_net = "";
244 static void prompt_set(void) {
245 if (g_session.pm3_present) {
247 switch (g_conn.send_via_ip) {
248 case PM3_TCPv4:
249 prompt_net = PROXPROMPT_NET_TCPV4;
250 break;
251 case PM3_TCPv6:
252 prompt_net = PROXPROMPT_NET_TCPV6;
253 break;
254 case PM3_UDPv4:
255 prompt_net = PROXPROMPT_NET_UDPV4;
256 break;
257 case PM3_UDPv6:
258 prompt_net = PROXPROMPT_NET_UDPV6;
259 break;
260 case PM3_NONE:
261 prompt_net = PROXPROMPT_NET_NONE;
262 break;
263 default:
264 break;
267 if (g_conn.send_via_fpc_usart)
268 prompt_dev = PROXPROMPT_DEV_FPC;
269 else
270 prompt_dev = PROXPROMPT_DEV_USB;
272 } else {
273 prompt_dev = PROXPROMPT_DEV_OFFLINE;
277 static void prompt_compose(char *buf, size_t buflen, const char *promptctx, const char *promptdev, const char *promptnet, bool no_newline) {
278 if (no_newline) {
279 snprintf(buf, buflen - 1, PROXPROMPT_COMPOSE, promptdev, promptnet, promptctx);
280 } else {
281 snprintf(buf, buflen - 1, "\r \r" PROXPROMPT_COMPOSE, promptdev, promptnet, promptctx);
285 static bool c_update_reconnect_prompt = false;
287 // This function is hooked via RL_EVENT_HOOK.
288 static int check_comm(void) {
289 // If communications thread goes down. Device disconnected then this should hook up PM3 again.
290 if (IsCommunicationThreadDead() && g_session.pm3_present) {
292 #ifndef HAVE_READLINE
293 PrintAndLogEx(INFO, _YELLOW_("OFFLINE") " mode. Use "_YELLOW_("\"hw connect\"") " to reconnect\n");
294 #endif
295 prompt_dev = PROXPROMPT_DEV_OFFLINE;
296 char prompt[PROXPROMPT_MAX_SIZE] = {0};
297 prompt_compose(prompt, sizeof(prompt), prompt_ctx, prompt_dev, prompt_net, false);
298 char prompt_filtered[PROXPROMPT_MAX_SIZE] = {0};
299 memcpy_filter_ansi(prompt_filtered, prompt, sizeof(prompt_filtered), !g_session.supports_colors);
300 pm3line_update_prompt(prompt_filtered);
301 CloseProxmark(g_session.current_device);
302 StartReconnectProxmark();
303 c_update_reconnect_prompt = true;
305 // its alive again
306 if (c_update_reconnect_prompt && IsReconnectedOk() && g_session.pm3_present) {
308 prompt_set();
310 char prompt[PROXPROMPT_MAX_SIZE] = {0};
311 prompt_compose(prompt, sizeof(prompt), prompt_ctx, prompt_dev, prompt_net, false);
312 char prompt_filtered[PROXPROMPT_MAX_SIZE] = {0};
313 memcpy_filter_ansi(prompt_filtered, prompt, sizeof(prompt_filtered), !g_session.supports_colors);
314 pm3line_update_prompt(prompt_filtered);
315 c_update_reconnect_prompt = false;
318 msleep(50);
319 return 0;
322 #if defined(_WIN32)
323 static bool DetectWindowsAnsiSupport(void) {
324 #ifndef ENABLE_VIRTUAL_TERMINAL_PROCESSING
325 #define ENABLE_VIRTUAL_TERMINAL_PROCESSING 0x0004
326 #endif
328 // disable colors if stdin or stdout are redirected
329 if ((! g_session.stdinOnTTY) || (! g_session.stdoutOnTTY))
330 return false;
332 HANDLE hOut = GetStdHandle(STD_OUTPUT_HANDLE);
333 DWORD dwMode = 0;
334 GetConsoleMode(hOut, &dwMode);
336 //ENABLE_VIRTUAL_TERMINAL_PROCESSING is already set
337 if ((dwMode & ENABLE_VIRTUAL_TERMINAL_PROCESSING))
338 return true;
340 dwMode |= ENABLE_VIRTUAL_TERMINAL_PROCESSING;
342 return SetConsoleMode(hOut, dwMode) ? true : false;
344 #endif //_WIN32
346 // first slot is always NULL, indicating absence of script when idx=0
347 static FILE *cmdscriptfile[MAX_NESTED_CMDSCRIPT + 1] = {0};
348 static uint8_t cmdscriptfile_idx = 0;
349 static bool cmdscriptfile_stayafter = false;
351 int push_cmdscriptfile(char *path, bool stayafter) {
352 if (cmdscriptfile_idx == MAX_NESTED_CMDSCRIPT) {
353 PrintAndLogEx(ERR, "Too many nested scripts, skipping %s\n", path);
354 return PM3_EMALLOC;
357 FILE *f = fopen(path, "r");
358 if (f == NULL)
359 return PM3_EFILE;
361 if (cmdscriptfile_idx == 0)
362 cmdscriptfile_stayafter = stayafter;
364 cmdscriptfile[++cmdscriptfile_idx] = f;
365 return PM3_SUCCESS;
368 static FILE *current_cmdscriptfile(void) {
369 return cmdscriptfile[cmdscriptfile_idx];
372 static bool pop_cmdscriptfile(void) {
373 fclose(cmdscriptfile[cmdscriptfile_idx]);
374 cmdscriptfile[cmdscriptfile_idx--] = NULL;
375 if (cmdscriptfile_idx == 0)
376 return cmdscriptfile_stayafter;
377 else
378 return true;
381 // Main thread of PM3 Client
382 void
383 #ifdef __has_attribute
384 #if __has_attribute(force_align_arg_pointer)
385 __attribute__((force_align_arg_pointer))
386 #endif
387 #endif
388 main_loop(const char *script_cmds_file, char *script_cmd, bool stayInCommandLoop) {
390 char *cmd = NULL;
391 bool execCommand = (script_cmd != NULL);
392 bool fromInteractive = false;
393 uint16_t script_cmd_len = 0;
394 if (execCommand) {
395 script_cmd_len = strlen(script_cmd);
396 str_creplace(script_cmd, script_cmd_len, ';', '\0');
398 bool stdinOnPipe = !isatty(STDIN_FILENO);
399 char script_cmd_buf[256] = {0x00}; // iceman, needs lua script the same file_path_buffer as the rest
401 // cache Version information now:
402 if (execCommand || script_cmds_file || stdinOnPipe)
403 pm3_version(false, false);
404 else
405 pm3_version_short();
407 if (script_cmds_file) {
409 char *path;
410 int res = searchFile(&path, CMD_SCRIPTS_SUBDIR, script_cmds_file, ".cmd", false);
411 if (res == PM3_SUCCESS) {
412 if (push_cmdscriptfile(path, stayInCommandLoop) == PM3_SUCCESS)
413 PrintAndLogEx(SUCCESS, "executing commands from file: %s\n", path);
414 else
415 PrintAndLogEx(ERR, "could not open " _YELLOW_("%s") "...", path);
416 free(path);
420 g_session.history_path = NULL;
421 if (g_session.incognito) {
422 PrintAndLogEx(INFO, "No history will be recorded");
423 } else {
424 if (searchHomeFilePath(&g_session.history_path, NULL, PROXHISTORY, true) != PM3_SUCCESS) {
425 g_session.history_path = NULL;
426 PrintAndLogEx(ERR, "No history will be recorded");
427 } else {
428 if (pm3line_load_history(g_session.history_path) != PM3_SUCCESS) {
429 PrintAndLogEx(INFO, "No previous history could be loaded");
434 // loops every time enter is pressed...
435 while (1) {
437 bool printprompt = false;
439 prompt_set();
441 check_script:
442 // If there is a script file
443 if (current_cmdscriptfile()) {
445 // clear array
446 memset(script_cmd_buf, 0, sizeof(script_cmd_buf));
448 // read script file
449 if (fgets(script_cmd_buf, sizeof(script_cmd_buf), current_cmdscriptfile()) == NULL) {
450 if (pop_cmdscriptfile() == false) {
451 break;
453 goto check_script;
456 prompt_ctx = PROXPROMPT_CTX_SCRIPTFILE;
458 // remove linebreaks
459 str_cleanrn(script_cmd_buf, sizeof(script_cmd_buf));
461 cmd = str_dup(script_cmd_buf);
462 if (cmd != NULL) {
463 printprompt = true;
466 } else {
467 // If there is a script command
468 if (execCommand) {
469 prompt_ctx = stdinOnPipe ? PROXPROMPT_CTX_STDIN : PROXPROMPT_CTX_SCRIPTCMD;
471 cmd = str_dup(script_cmd);
472 if ((cmd != NULL) && (! fromInteractive))
473 printprompt = true;
475 uint16_t len = strlen(script_cmd) + 1;
476 script_cmd += len;
478 if (script_cmd_len == len - 1)
479 execCommand = false;
481 script_cmd_len -= len;
482 } else {
483 // exit after exec command
484 if (script_cmd && !stayInCommandLoop)
485 break;
487 // if there is a pipe from stdin
488 if (stdinOnPipe) {
489 // clear array
490 memset(script_cmd_buf, 0, sizeof(script_cmd_buf));
491 // get
492 if (fgets(script_cmd_buf, sizeof(script_cmd_buf), stdin) == NULL) {
493 PrintAndLogEx(ERR, "STDIN unexpected end, exit...");
494 break;
496 execCommand = true;
497 stayInCommandLoop = true;
498 fromInteractive = false;
499 script_cmd = script_cmd_buf;
500 script_cmd_len = strlen(script_cmd);
501 str_creplace(script_cmd, script_cmd_len, ';', '\0');
502 // remove linebreaks
503 str_cleanrn(script_cmd, script_cmd_len);
504 goto check_script;
505 } else {
506 pm3line_check(check_comm);
507 prompt_ctx = PROXPROMPT_CTX_INTERACTIVE;
508 char prompt[PROXPROMPT_MAX_SIZE] = {0};
509 prompt_compose(prompt, sizeof(prompt), prompt_ctx, prompt_dev, prompt_net, true);
510 char prompt_filtered[PROXPROMPT_MAX_SIZE] = {0};
511 memcpy_filter_ansi(prompt_filtered, prompt, sizeof(prompt_filtered), !g_session.supports_colors);
512 g_pendingPrompt = true;
513 script_cmd = pm3line_read(prompt_filtered);
514 #if defined(_WIN32)
515 //Check if color support needs to be enabled again in case the window buffer did change
516 g_session.supports_colors = DetectWindowsAnsiSupport();
517 #endif
518 if (script_cmd != NULL) {
519 execCommand = true;
520 stayInCommandLoop = true;
521 fromInteractive = true;
522 script_cmd_len = strlen(script_cmd);
523 str_creplace(script_cmd, script_cmd_len, ';', '\0');
524 // remove linebreaks
525 str_cleanrn(script_cmd, script_cmd_len);
526 goto check_script;
528 fflush(NULL);
533 // execute command
534 if (cmd) {
536 // rtrim
537 size_t l = strlen(cmd);
538 while (l > 0 && isspace(cmd[l - 1])) {
539 cmd[--l] = '\0';
541 // ltrim
542 size_t off = 0;
543 while ((cmd[off] != '\0') && isspace(cmd[off])) {
544 off++;
547 for (size_t i = 0; i < strlen(cmd) - off; i++) {
548 cmd[i] = cmd[i + off];
551 cmd[strlen(cmd) - off] = '\0';
553 if (cmd[0] != '\0') {
554 uint8_t old_printAndLog = g_printAndLog;
555 if (!printprompt) {
556 g_printAndLog &= ~PRINTANDLOG_PRINT;
558 char prompt[PROXPROMPT_MAX_SIZE] = {0};
559 prompt_compose(prompt, sizeof(prompt), prompt_ctx, prompt_dev, prompt_net, true);
560 // always filter RL magic separators if not using readline
561 char prompt_filtered[PROXPROMPT_MAX_SIZE] = {0};
562 memcpy_filter_rlmarkers(prompt_filtered, prompt, sizeof(prompt_filtered));
563 PrintAndLogEx(NORMAL, "%s%s", prompt_filtered, cmd);
564 g_printAndLog = old_printAndLog;
566 // add to history if not from a script
567 if (!current_cmdscriptfile()) {
568 pm3line_add_history(cmd);
570 // process cmd
571 g_pendingPrompt = false;
572 mainret = CommandReceived(cmd);
574 // exit or quit
575 if (mainret == PM3_EFATAL)
576 break;
578 free(cmd);
579 cmd = NULL;
580 } else {
581 PrintAndLogEx(NORMAL, "\n");
582 if (script_cmds_file && stayInCommandLoop)
583 stayInCommandLoop = false;
584 else
585 break;
587 } // end while
589 if (g_session.pm3_present) {
590 clearCommandBuffer();
591 SendCommandNG(CMD_QUIT_SESSION, NULL, 0);
592 msleep(100); // Make sure command is sent before killing client
595 while (current_cmdscriptfile()) {
596 pop_cmdscriptfile();
599 pm3line_flush_history();
601 if (cmd) {
602 free(cmd);
603 cmd = NULL;
607 #ifndef LIBPM3
608 static void dumpAllHelp(int markdown, bool full_help) {
609 g_session.help_dump_mode = true;
610 PrintAndLogEx(NORMAL, "\n%sProxmark3 command dump%s\n\n", markdown ? "# " : "", markdown ? "" : "\n======================");
611 PrintAndLogEx(NORMAL, "Some commands are available only if a Proxmark3 is actually connected.%s\n", markdown ? " " : "");
612 PrintAndLogEx(NORMAL, "Check column \"offline\" for their availability.\n");
613 PrintAndLogEx(NORMAL, "\n");
614 command_t *cmds = getTopLevelCommandTable();
615 dumpCommandsRecursive(cmds, markdown, full_help);
616 g_session.help_dump_mode = false;
617 PrintAndLogEx(NORMAL, "Full help dump done.");
619 #endif //LIBPM3
621 static char *my_executable_path = NULL;
622 static char *my_executable_directory = NULL;
624 const char *get_my_executable_path(void) {
625 return my_executable_path;
628 const char *get_my_executable_directory(void) {
629 return my_executable_directory;
632 static void set_my_executable_path(void) {
633 int path_length = wai_getExecutablePath(NULL, 0, NULL);
634 if (path_length == -1)
635 return;
637 my_executable_path = (char *)calloc(path_length + 1, sizeof(uint8_t));
638 int dirname_length = 0;
639 if (wai_getExecutablePath(my_executable_path, path_length, &dirname_length) != -1) {
640 my_executable_path[path_length] = '\0';
641 my_executable_directory = (char *)calloc(dirname_length + 2, sizeof(uint8_t));
642 strncpy(my_executable_directory, my_executable_path, dirname_length + 1);
643 my_executable_directory[dirname_length + 1] = '\0';
647 static const char *my_user_directory = NULL;
648 // static char _cwd_Buffer [FILENAME_MAX] = {0};
650 const char *get_my_user_directory(void) {
651 return my_user_directory;
654 static void set_my_user_directory(void) {
655 /* my_user_directory = getenv("HOME");
657 // if not found, default to current directory
658 if (my_user_directory == NULL) {
659 my_user_directory = GetCurrentDir(_cwd_Buffer, sizeof(_cwd_Buffer));
660 // change all slashes to / (windows should not care...
661 for (int i = 0; i < strlen(_cwd_Buffer); i++)
662 if (_cwd_Buffer[i] == '\\') _cwd_Buffer[i] = '/';
663 // my_user_directory = ".";
666 my_user_directory = getenv("HOME");
668 // if not found, default to current directory
669 if (my_user_directory == NULL) {
671 uint16_t pathLen = FILENAME_MAX; // should be a good starting point
672 char *cwd_buffer = (char *)calloc(pathLen, sizeof(uint8_t));
673 if (cwd_buffer == NULL) {
674 PrintAndLogEx(WARNING, "failed to allocate memory");
675 return;
678 while (GetCurrentDir(cwd_buffer, pathLen) == NULL) {
679 if (errno == ERANGE) { // Need bigger buffer
680 pathLen += 10; // if buffer was too small add 10 characters and try again
681 char *tmp = realloc(cwd_buffer, pathLen);
682 if (tmp == NULL) {
683 PrintAndLogEx(WARNING, "failed to allocate memory");
684 free(cwd_buffer);
685 return;
687 cwd_buffer = tmp;
688 } else {
689 free(cwd_buffer);
690 return;
694 for (int i = 0; i < strlen(cwd_buffer); i++) {
695 if (cwd_buffer[i] == '\\') {
696 cwd_buffer[i] = '/';
700 my_user_directory = cwd_buffer;
704 #ifndef LIBPM3
705 static void show_help(bool showFullHelp, char *exec_name) {
707 PrintAndLogEx(NORMAL, "\nsyntax: %s [-h|-t|-m|--fulltext]", exec_name);
708 #ifdef HAVE_PYTHON
709 PrintAndLogEx(NORMAL, " %s [[-p] <port>] [-b] [-w] [-f] [-c <command>]|[-l <lua_script_file>]|[-y <python_script_file>]|[-s <cmd_script_file>] [-i] [-d <0|1|2>]", exec_name);
710 #else // HAVE_PYTHON
711 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);
712 #endif // HAVE_PYTHON
713 PrintAndLogEx(NORMAL, " %s [-p] <port> --flash [--unlock-bootloader] [--image <imagefile>]+ [-w] [-f] [-d <0|1|2>]", exec_name);
715 if (showFullHelp) {
717 PrintAndLogEx(NORMAL, "\nCommon options:");
718 PrintAndLogEx(NORMAL, " -h/--help this help");
719 PrintAndLogEx(NORMAL, " -v/--version print client version");
720 PrintAndLogEx(NORMAL, " -p/--port serial port to connect to");
721 PrintAndLogEx(NORMAL, " -w/--wait 20sec waiting the serial port to appear in the OS");
722 PrintAndLogEx(NORMAL, " -f/--flush output will be flushed after every print");
723 PrintAndLogEx(NORMAL, " -d/--debug <0|1|2> set debugmode");
724 PrintAndLogEx(NORMAL, "\nOptions in client mode:");
725 PrintAndLogEx(NORMAL, " -t/--text dump all interactive command list at once");
726 PrintAndLogEx(NORMAL, " --fulltext dump all interactive command's help at once");
727 PrintAndLogEx(NORMAL, " -m/--markdown dump all interactive command list at once in markdown syntax");
728 PrintAndLogEx(NORMAL, " -b/--baud serial port speed (only needed for physical UART, not for USB-CDC or BT)");
729 PrintAndLogEx(NORMAL, " -c/--command <command> execute one Proxmark3 command (or several separated by ';').");
730 PrintAndLogEx(NORMAL, " -l/--lua <lua_script_file> execute Lua script.");
731 #ifdef HAVE_PYTHON
732 // Technically, --lua and --py are identical and interexchangeable
733 PrintAndLogEx(NORMAL, " -y/--py <python_script_file> execute Python script.");
734 #endif // HAVE_PYTHON
735 PrintAndLogEx(NORMAL, " -s/--script-file <cmd_script_file> script file with one Proxmark3 command per line");
736 PrintAndLogEx(NORMAL, " -i/--interactive enter interactive mode after executing the script or the command");
737 PrintAndLogEx(NORMAL, " --incognito do not use history, prefs file nor log files");
738 PrintAndLogEx(NORMAL, " --ncpu <num_cores> override number of CPU cores");
739 PrintAndLogEx(NORMAL, "\nOptions in flasher mode:");
740 PrintAndLogEx(NORMAL, " --flash flash Proxmark3, requires at least one --image");
741 PrintAndLogEx(NORMAL, " --reboot-to-bootloader reboot Proxmark3 into bootloader mode");
742 PrintAndLogEx(NORMAL, " --unlock-bootloader Enable flashing of bootloader area *DANGEROUS* (need --flash)");
743 PrintAndLogEx(NORMAL, " --force Enable flashing even if firmware seems to not match client version");
744 PrintAndLogEx(NORMAL, " --image <imagefile> image to flash. Can be specified several times.");
745 PrintAndLogEx(NORMAL, "\nOptions in memory dump mode:");
746 PrintAndLogEx(NORMAL, " --dumpmem <dumpfile> dumps Proxmark3 flash memory to file");
747 PrintAndLogEx(NORMAL, " --dumpaddr <address> starting address for dump, default 0");
748 PrintAndLogEx(NORMAL, " --dumplen <length> number of bytes to dump, default 512KB");
749 PrintAndLogEx(NORMAL, " --dumpraw raw address mode: dump from anywhere, not just flash");
750 PrintAndLogEx(NORMAL, "\nExamples:");
751 PrintAndLogEx(NORMAL, "\n to run Proxmark3 client:\n");
752 PrintAndLogEx(NORMAL, " %s "SERIAL_PORT_EXAMPLE_H" -- runs the pm3 client", exec_name);
753 PrintAndLogEx(NORMAL, " %s "SERIAL_PORT_EXAMPLE_H" -f -- flush output every time", exec_name);
754 PrintAndLogEx(NORMAL, " %s "SERIAL_PORT_EXAMPLE_H" -w -- wait for serial port", exec_name);
755 PrintAndLogEx(NORMAL, " %s -- runs the pm3 client in OFFLINE mode", exec_name);
756 PrintAndLogEx(NORMAL, "\n to execute different commands from terminal:\n");
757 PrintAndLogEx(NORMAL, " %s "SERIAL_PORT_EXAMPLE_H" -c \"hf mf chk --1k\" -- execute cmd and quit client", exec_name);
758 PrintAndLogEx(NORMAL, " %s "SERIAL_PORT_EXAMPLE_H" -l hf_read -- execute Lua script `hf_read` and quit client", exec_name);
759 PrintAndLogEx(NORMAL, " %s "SERIAL_PORT_EXAMPLE_H" -s mycmds.txt -- execute each pm3 cmd in file and quit client", exec_name);
760 PrintAndLogEx(NORMAL, "\n to flash fullimage and bootloader:\n");
761 PrintAndLogEx(NORMAL, " %s "SERIAL_PORT_EXAMPLE_H" --flash --unlock-bootloader --image bootrom.elf --image fullimage.elf", exec_name);
762 #ifdef __linux__
763 PrintAndLogEx(NORMAL, "\nNote (Linux):\nif the flasher gets stuck in 'Waiting for Proxmark3 to reappear on <DEVICE>',");
764 PrintAndLogEx(NORMAL, "you need to blacklist Proxmark3 for modem-manager - see documentation for more details:");
765 PrintAndLogEx(NORMAL, "* https://github.com/RfidResearchGroup/proxmark3/blob/master/doc/md/Installation_Instructions/ModemManager-Must-Be-Discarded.md");
766 PrintAndLogEx(NORMAL, "\nMore info on flashing procedure from the official Proxmark3 wiki:");
767 PrintAndLogEx(NORMAL, "* https://github.com/Proxmark/proxmark3/wiki/Gentoo%%20Linux");
768 PrintAndLogEx(NORMAL, "* https://github.com/Proxmark/proxmark3/wiki/Ubuntu%%20Linux");
769 PrintAndLogEx(NORMAL, "* https://github.com/Proxmark/proxmark3/wiki/OSX\n");
770 #endif
774 static int dumpmem_to_file(const char *filename, uint32_t addr, uint32_t len, bool raw, bool in_bootloader) {
776 uint8_t *buffer = calloc(len, sizeof(uint8_t));
777 if (buffer == NULL) {
778 PrintAndLogEx(ERR, "error, cannot allocate memory ");
779 return PM3_EMALLOC;
782 int res = PM3_EUNDEF;
783 size_t readlen = 0;
784 DeviceMemType_t type = raw ? MCU_MEM : MCU_FLASH;
785 if (GetFromDevice(type, buffer, len, addr, NULL, 0, NULL, 1000, true)) {
786 res = PM3_SUCCESS;
787 readlen = len; // GetFromDevice does not report the actual number of bytes received.
790 if (res == PM3_SUCCESS) {
791 res = saveFile(filename, ".bin", buffer, readlen);
792 if (res != PM3_SUCCESS) {
793 PrintAndLogEx(ERR, "error writing to file "_YELLOW_("%s"), filename);
797 free(buffer);
798 return res;
801 static int dumpmem_pm3(char *serial_port_name, const char *filename, uint32_t addr, uint32_t len, bool raw) {
802 int ret = PM3_EUNDEF;
803 bool in_bootloader = false;
805 if (serial_port_name == NULL) {
806 PrintAndLogEx(ERR, "You must specify a port.\n");
807 return PM3_EINVARG;
810 if (OpenProxmark(&g_session.current_device, serial_port_name, true, 60, true, FLASHMODE_SPEED)) {
811 PrintAndLogEx(NORMAL, _GREEN_(" found"));
812 msleep(200);
813 } else {
814 PrintAndLogEx(ERR, "Could not find Proxmark3 on " _RED_("%s") ".\n", serial_port_name);
815 ret = PM3_ETIMEOUT;
816 goto finish;
819 // Determine if we're talking to a bootloader or main firmware.
820 SendCommandBL(CMD_DEVICE_INFO, 0, 0, 0, NULL, 0);
821 PacketResponseNG resp;
822 if (WaitForResponseTimeout(CMD_UNKNOWN, &resp, 1000) == false) {
823 PrintAndLogEx(ERR, "Could not get device info.");
824 goto finish2;
826 uint32_t dev_info = resp.oldarg[0];
827 in_bootloader = (dev_info & DEVICE_INFO_FLAG_CURRENT_MODE_BOOTROM) != 0;
828 if (in_bootloader) {
829 if ((dev_info & DEVICE_INFO_FLAG_UNDERSTANDS_READ_MEM) != 0) {
830 PrintAndLogEx(INFO, "Device is running the bootloader.");
831 } else {
832 PrintAndLogEx(ERR, "Device is running the bootloader, but the bootloader"
833 " doesn't understand the READ MEM command.");
834 goto finish2;
838 PrintAndLogEx(SUCCESS, "Dump requested from address "_YELLOW_("%u")", length "_YELLOW_("%u")"%s.",
839 addr, len, raw ? ", in raw address mode" : "");
841 PrintAndLogEx(SUCCESS, _CYAN_("Memory dumping to file..."));
842 ret = dumpmem_to_file(filename, addr, len, raw, in_bootloader);
843 if (ret != PM3_SUCCESS) {
844 goto finish2;
846 PrintAndLogEx(NORMAL, "");
848 finish2:
849 clearCommandBuffer();
850 if (in_bootloader) {
851 g_session.current_device->g_conn->run = false;
852 SendCommandOLD(CMD_PING, 0, 0, 0, NULL, 0);
853 } else {
854 SendCommandNG(CMD_QUIT_SESSION, NULL, 0);
855 msleep(100);
857 CloseProxmark(g_session.current_device);
859 finish:
860 if (ret == PM3_SUCCESS)
861 PrintAndLogEx(SUCCESS, _CYAN_("All done"));
862 else if (ret == PM3_EOPABORTED)
863 PrintAndLogEx(FAILED, "Aborted by user");
864 else
865 PrintAndLogEx(ERR, "Aborted on error %u", ret);
866 return ret;
869 static int flash_pm3(char *serial_port_name, uint8_t num_files, const char *filenames[FLASH_MAX_FILES], bool can_write_bl, bool force) {
871 int ret = PM3_EUNDEF;
872 flash_file_t files[FLASH_MAX_FILES];
873 memset(files, 0, sizeof(files));
875 if (serial_port_name == NULL) {
876 PrintAndLogEx(ERR, "You must specify a port.\n");
877 return PM3_EINVARG;
880 for (int i = 0 ; i < num_files; ++i) {
881 char *path;
882 ret = searchFile(&path, FIRMWARES_SUBDIR, filenames[i], ".elf", true);
883 if (ret != PM3_SUCCESS) {
884 ret = searchFile(&path, BOOTROM_SUBDIR, filenames[i], ".elf", true);
886 if (ret != PM3_SUCCESS) {
887 // Last try, let the error msg be displayed if not found
888 ret = searchFile(&path, FULLIMAGE_SUBDIR, filenames[i], ".elf", false);
890 if (ret != PM3_SUCCESS) {
891 goto finish2;
893 files[i].filename = path;
896 PrintAndLogEx(SUCCESS, "About to use the following file%s:", num_files > 1 ? "s" : "");
897 for (int i = 0 ; i < num_files; ++i) {
898 PrintAndLogEx(SUCCESS, " "_YELLOW_("%s"), files[i].filename);
901 for (int i = 0 ; i < num_files; ++i) {
902 ret = flash_load(&files[i], force);
903 if (ret != PM3_SUCCESS) {
904 goto finish2;
906 PrintAndLogEx(NORMAL, "");
909 if (OpenProxmark(&g_session.current_device, serial_port_name, true, 60, true, FLASHMODE_SPEED)) {
910 PrintAndLogEx(NORMAL, _GREEN_(" found"));
911 msleep(200);
912 } else {
913 PrintAndLogEx(ERR, "Could not find Proxmark3 on " _RED_("%s") ".\n", serial_port_name);
914 ret = PM3_ETIMEOUT;
915 goto finish2;
918 uint32_t max_allowed = 0;
919 ret = flash_start_flashing(can_write_bl, serial_port_name, &max_allowed);
920 if (ret != PM3_SUCCESS) {
921 goto finish;
924 if (num_files == 0)
925 goto finish;
927 for (int i = 0 ; i < num_files; ++i) {
928 ret = flash_prepare(&files[i], can_write_bl, max_allowed * ONE_KB);
929 if (ret != PM3_SUCCESS) {
930 goto finish;
932 PrintAndLogEx(NORMAL, "");
935 PrintAndLogEx(SUCCESS, _CYAN_("Flashing..."));
937 for (int i = 0; i < num_files; i++) {
938 ret = flash_write(&files[i]);
939 if (ret != PM3_SUCCESS) {
940 goto finish;
942 PrintAndLogEx(NORMAL, "");
945 finish:
946 if (ret != PM3_SUCCESS)
947 PrintAndLogEx(WARNING, "The flashing procedure failed, follow the suggested steps!");
948 ret = flash_stop_flashing();
949 CloseProxmark(g_session.current_device);
950 finish2:
951 for (int i = 0 ; i < num_files; ++i) {
952 flash_free(&files[i]);
954 if (ret == PM3_SUCCESS)
955 PrintAndLogEx(SUCCESS, _CYAN_("All done"));
956 else if (ret == PM3_EOPABORTED)
957 PrintAndLogEx(FAILED, "Aborted by user");
958 else
959 PrintAndLogEx(ERR, "Aborted on error");
960 PrintAndLogEx(INFO, "\nHave a nice day!");
961 return ret;
964 static int reboot_bootloader_pm3(char *serial_port_name) {
965 if (serial_port_name == NULL) {
966 PrintAndLogEx(ERR, "You must specify a port.\n");
967 return PM3_EINVARG;
970 if (OpenProxmark(&g_session.current_device, serial_port_name, true, 60, true, FLASHMODE_SPEED) == false) {
971 PrintAndLogEx(ERR, "Could not find Proxmark3 on " _RED_("%s") ".\n", serial_port_name);
972 return PM3_ETIMEOUT;
975 PrintAndLogEx(NORMAL, _GREEN_(" found"));
976 return flash_reboot_bootloader(serial_port_name, true);
979 #endif //LIBPM3
981 void pm3_init(void) {
982 srand(time(0));
984 g_session.pm3_present = false;
985 g_session.help_dump_mode = false;
986 g_session.incognito = false;
987 g_session.supports_colors = false;
988 g_session.emoji_mode = EMO_ALTTEXT;
989 g_session.stdinOnTTY = false;
990 g_session.stdoutOnTTY = false;
992 // set global variables soon enough to get the log path
993 set_my_executable_path();
994 set_my_user_directory();
998 #ifndef LIBPM3
999 int main(int argc, char *argv[]) {
1000 pm3_init();
1001 bool waitCOMPort = false;
1002 bool addScriptExec = false;
1003 bool stayInCommandLoop = false;
1004 char *script_cmds_file = NULL;
1005 char *script_cmd = NULL;
1006 char *port = NULL;
1007 uint32_t speed = 0;
1009 pm3line_init();
1011 char exec_name[100] = {0};
1012 strncpy(exec_name, basename(argv[0]), sizeof(exec_name) - 1);
1014 bool flash_mode = false;
1015 bool reboot_bootloader_mode = false;
1016 bool flash_can_write_bl = false;
1017 bool flash_force = false;
1018 bool debug_mode_forced = false;
1019 int flash_num_files = 0;
1020 const char *flash_filenames[FLASH_MAX_FILES];
1021 bool dumpmem_mode = false;
1022 const char *dumpmem_filename = NULL;
1023 uint32_t dumpmem_addr = 0;
1024 uint32_t dumpmem_len = 512 * 1024;
1025 bool dumpmem_raw = false;
1027 // color management:
1028 // 1. default = no color
1029 // 2. enable colors if OS seems to support colors and if stdin/stdout aren't redirected
1030 // 3. load prefs if available, overwrite colors choice if needed
1031 // 4. disable colors anyway if stdin/stdout are redirected
1033 // For info, grep --color=auto is doing sth like this, plus test getenv("TERM") != "dumb":
1034 // struct stat tmp_stat;
1035 // if ((fstat (STDOUT_FILENO, &tmp_stat) == 0) && (S_ISCHR (tmp_stat.st_mode)) && isatty(STDIN_FILENO))
1036 g_session.stdinOnTTY = isatty(STDIN_FILENO);
1037 g_session.stdoutOnTTY = isatty(STDOUT_FILENO);
1038 g_session.supports_colors = false;
1039 g_session.emoji_mode = EMO_ALTTEXT;
1040 if (g_session.stdinOnTTY && g_session.stdoutOnTTY) {
1041 #if defined(__linux__) || defined(__APPLE__)
1042 g_session.supports_colors = true;
1043 g_session.emoji_mode = EMO_EMOJI;
1044 #elif defined(_WIN32)
1045 g_session.supports_colors = DetectWindowsAnsiSupport();
1046 g_session.emoji_mode = EMO_ALTTEXT;
1047 #endif
1049 for (int i = 1; i < argc; i++) {
1051 if (argv[i][0] != '-') {
1052 // For backward compatibility we accept direct port
1053 if (port != NULL) {
1054 // We got already one
1055 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]);
1056 show_help(false, exec_name);
1057 return 1;
1059 port = argv[i];
1060 continue;
1063 // port
1064 if (strcmp(argv[i], "-p") == 0 || strcmp(argv[i], "--port") == 0) {
1065 if (i + 1 == argc) {
1066 PrintAndLogEx(ERR, _RED_("ERROR:") " missing port specification after -p\n");
1067 show_help(false, exec_name);
1068 return 1;
1070 if (port != NULL) {
1071 // We got already one
1072 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]);
1073 show_help(false, exec_name);
1074 return 1;
1076 port = argv[++i];
1077 continue;
1080 // short help
1081 if (strcmp(argv[i], "-h") == 0 || strcmp(argv[i], "--help") == 0) {
1082 g_printAndLog = PRINTANDLOG_PRINT;
1083 show_help(true, exec_name);
1084 return 0;
1087 // dump help
1088 if (strcmp(argv[i], "-t") == 0 || strcmp(argv[i], "--text") == 0) {
1089 g_printAndLog = PRINTANDLOG_PRINT;
1090 show_help(false, exec_name);
1091 dumpAllHelp(0, false);
1092 return 0;
1095 // dump help
1096 if (strcmp(argv[i], "--fulltext") == 0) {
1097 g_printAndLog = PRINTANDLOG_PRINT;
1098 show_help(false, exec_name);
1099 dumpAllHelp(0, true);
1100 return 0;
1103 // dump markup
1104 if (strcmp(argv[i], "-m") == 0 || strcmp(argv[i], "--markdown") == 0) {
1105 g_printAndLog = PRINTANDLOG_PRINT;
1106 dumpAllHelp(1, false);
1107 return 0;
1109 // print client version
1110 if (strcmp(argv[i], "-v") == 0 || strcmp(argv[i], "--version") == 0) {
1111 pm3_version(true, true);
1112 return 0;
1115 // set debugmode
1116 if (strcmp(argv[i], "-d") == 0 || strcmp(argv[i], "--debug") == 0) {
1117 if (i + 1 == argc) {
1118 PrintAndLogEx(ERR, _RED_("ERROR:") " missing debugmode specification after -d\n");
1119 show_help(false, exec_name);
1120 return 1;
1122 int demod = atoi(argv[i + 1]);
1123 if (demod < 0 || demod > 2) {
1124 PrintAndLogEx(ERR, _RED_("ERROR:") " invalid debugmode: -d " _YELLOW_("%s") "\n", argv[i + 1]);
1125 return 1;
1127 g_debugMode = demod;
1128 debug_mode_forced = true;
1129 i++;
1130 continue;
1133 // flush output
1134 if (strcmp(argv[i], "-f") == 0 || strcmp(argv[i], "--flush") == 0) {
1135 SetFlushAfterWrite(true);
1136 PrintAndLogEx(INFO, "Output will be flushed after every print.\n");
1137 continue;
1140 // set baudrate
1141 if (strcmp(argv[i], "-b") == 0 || strcmp(argv[i], "--baud") == 0) {
1142 if (i + 1 == argc) {
1143 PrintAndLogEx(ERR, _RED_("ERROR:") " missing baud specification after -b\n");
1144 show_help(false, exec_name);
1145 return 1;
1147 uint64_t tmpspeed = strtoul(argv[i + 1], NULL, 10);
1148 if ((tmpspeed == ULONG_MAX) || (tmpspeed == 0)) {
1149 PrintAndLogEx(ERR, _RED_("ERROR:") " invalid baudrate: -b " _YELLOW_("%s") "\n", argv[i + 1]);
1150 return 1;
1152 speed = tmpspeed;
1153 i++;
1154 continue;
1157 // wait for comport
1158 if (strcmp(argv[i], "-w") == 0 || strcmp(argv[i], "--wait") == 0) {
1159 waitCOMPort = true;
1160 continue;
1163 // execute pm3 command
1164 if (strcmp(argv[i], "-c") == 0 || strcmp(argv[i], "--command") == 0) {
1165 if (i + 1 == argc) {
1166 PrintAndLogEx(ERR, _RED_("ERROR:") " missing command specification after -c\n");
1167 show_help(false, exec_name);
1168 return 1;
1170 script_cmd = argv[++i];
1171 continue;
1174 // execute pm3 command file
1175 if (strcmp(argv[i], "-s") == 0 || strcmp(argv[i], "--script-file") == 0) {
1176 if (i + 1 == argc || strlen(argv[i + 1]) == 0) {
1177 PrintAndLogEx(ERR, _RED_("ERROR:") " missing script file specification after -s\n");
1178 show_help(false, exec_name);
1179 return 1;
1181 script_cmds_file = argv[++i];
1182 continue;
1185 // execute Lua script
1186 if (strcmp(argv[i], "-l") == 0 || strcmp(argv[i], "--lua") == 0) {
1187 if (i + 1 == argc || strlen(argv[i + 1]) == 0) {
1188 PrintAndLogEx(ERR, _RED_("ERROR:") " missing Lua script specification after --lua\n");
1189 show_help(false, exec_name);
1190 return 1;
1192 script_cmd = argv[++i];
1193 if (script_cmd == NULL || strlen(script_cmd) == 0) {
1194 return 1;
1196 addScriptExec = true;
1197 continue;
1199 #ifdef HAVE_PYTHON
1200 // execute Python script
1201 if (strcmp(argv[i], "-y") == 0 || strcmp(argv[i], "--py") == 0) {
1202 if (i + 1 == argc || strlen(argv[i + 1]) == 0) {
1203 PrintAndLogEx(ERR, _RED_("ERROR:") " missing Python script specification after --py\n");
1204 show_help(false, exec_name);
1205 return 1;
1207 script_cmd = argv[++i];
1208 if (script_cmd == NULL || strlen(script_cmd) == 0) {
1209 return 1;
1211 addScriptExec = true;
1212 continue;
1214 #endif // HAVE_PYTHON
1215 // go to interactive instead of quitting after a script/command
1216 if (strcmp(argv[i], "-i") == 0 || strcmp(argv[i], "--interactive") == 0) {
1217 stayInCommandLoop = true;
1218 continue;
1221 // do not use history nor log files
1222 if (strcmp(argv[i], "--incognito") == 0) {
1223 g_session.incognito = true;
1224 continue;
1227 // go to dump mode
1228 if (strcmp(argv[i], "--dumpmem") == 0) {
1229 dumpmem_mode = true;
1230 if (i + 1 == argc) {
1231 PrintAndLogEx(ERR, _RED_("ERROR:") " missing file specification after --dumpmem\n");
1232 show_help(false, exec_name);
1233 return 1;
1235 dumpmem_filename = argv[++i];
1236 continue;
1238 if (strcmp(argv[i], "--dumpaddr") == 0) {
1239 if (i + 1 == argc) {
1240 PrintAndLogEx(ERR, _RED_("ERROR:") " missing address specification after -dumpaddr\n");
1241 show_help(false, exec_name);
1242 return 1;
1244 uint32_t tmpaddr = strtoul(argv[i + 1], NULL, 0);
1245 dumpmem_addr = tmpaddr;
1246 i++;
1247 continue;
1249 if (strcmp(argv[i], "--dumplen") == 0) {
1250 if (i + 1 == argc) {
1251 PrintAndLogEx(ERR, _RED_("ERROR:") " missing address specification after -dumplen\n");
1252 show_help(false, exec_name);
1253 return 1;
1255 uint32_t tmplen = strtoul(argv[i + 1], NULL, 0);
1256 dumpmem_len = tmplen;
1257 i++;
1258 continue;
1260 if (strcmp(argv[i], "--dumpraw") == 0) {
1261 dumpmem_raw = true;
1262 continue;
1265 // go to flash mode
1266 if (strcmp(argv[i], "--flash") == 0) {
1267 flash_mode = true;
1268 continue;
1271 // go to flash mode
1272 if (strcmp(argv[i], "--reboot-to-bootloader") == 0) {
1273 reboot_bootloader_mode = true;
1274 continue;
1277 // unlock bootloader area
1278 if (strcmp(argv[i], "--unlock-bootloader") == 0) {
1279 flash_can_write_bl = true;
1280 continue;
1283 // force flash even if firmware seems to not match client version
1284 if (strcmp(argv[i], "--force") == 0) {
1285 flash_force = true;
1286 continue;
1289 // flash file
1290 if (strcmp(argv[i], "--image") == 0) {
1291 if (flash_num_files == FLASH_MAX_FILES) {
1292 PrintAndLogEx(ERR, _RED_("ERROR:") " too many --image, please use it max %i times\n", FLASH_MAX_FILES);
1293 return 1;
1295 if (i + 1 == argc) {
1296 PrintAndLogEx(ERR, _RED_("ERROR:") " missing image specification after --image\n");
1297 show_help(false, exec_name);
1298 return 1;
1300 flash_filenames[flash_num_files++] = argv[++i];
1301 continue;
1304 if (strcmp(argv[i], "--ncpu") == 0) {
1305 if (i + 1 == argc) {
1306 PrintAndLogEx(ERR, _RED_("ERROR:") " missing CPU number specification after --ncpu\n");
1307 show_help(false, exec_name);
1308 return 1;
1310 long int ncpus = strtol(argv[i + 1], NULL, 10);
1311 const int detected_cpus = detect_num_CPUs();
1312 if (ncpus < 0 || ncpus >= detected_cpus) {
1313 PrintAndLogEx(ERR, _RED_("ERROR:") " invalid number of CPU cores: --ncpu " _YELLOW_("%s") " (available: %d)\n", argv[i + 1], detected_cpus);
1314 return 1;
1316 g_numCPUs = ncpus;
1317 i++;
1318 continue;
1321 // We got an unknown parameter
1322 PrintAndLogEx(ERR, _RED_("ERROR:") " invalid parameter: " _YELLOW_("%s") "\n", argv[i]);
1323 show_help(false, exec_name);
1324 return 1;
1327 // Load Settings and assign
1328 // This will allow the command line to override the settings.json values
1329 preferences_load();
1330 // quick patch for debug level
1331 if (! debug_mode_forced)
1332 g_debugMode = g_session.client_debug_level;
1333 // settings_save ();
1334 // End Settings
1336 // even if prefs, we disable colors if stdin or stdout is not a TTY
1337 if ((! g_session.stdinOnTTY) || (! g_session.stdoutOnTTY)) {
1338 g_session.supports_colors = false;
1339 g_session.emoji_mode = EMO_ALTTEXT;
1342 // Let's take a baudrate ok for real UART, USB-CDC & BT don't use that info anyway
1343 if (speed == 0)
1344 speed = USART_BAUD_RATE;
1346 if (dumpmem_mode) {
1347 dumpmem_pm3(port, dumpmem_filename, dumpmem_addr, dumpmem_len, dumpmem_raw);
1348 exit(EXIT_SUCCESS);
1351 if (flash_mode) {
1352 flash_pm3(port, flash_num_files, flash_filenames, flash_can_write_bl, flash_force);
1353 exit(EXIT_SUCCESS);
1356 if (reboot_bootloader_mode) {
1357 reboot_bootloader_pm3(port);
1358 exit(EXIT_SUCCESS);
1361 if (script_cmd) {
1362 while (script_cmd[strlen(script_cmd) - 1] == ' ')
1363 script_cmd[strlen(script_cmd) - 1] = 0x00;
1365 if (strlen(script_cmd) == 0) {
1366 script_cmd = NULL;
1367 PrintAndLogEx(ERR, _RED_("ERROR:") " execute command: " _YELLOW_("command not found") ".\n");
1368 return 2;
1369 } else {
1370 if (addScriptExec) {
1371 // add "script run " to command
1372 int len = strlen(script_cmd) + 11 + 1;
1373 char *ctmp = (char *) calloc(len, sizeof(uint8_t));
1374 if (ctmp != NULL) {
1375 memset(ctmp, 0, len);
1376 strcpy(ctmp, "script run ");
1377 strcpy(&ctmp[11], script_cmd);
1378 script_cmd = ctmp;
1382 PrintAndLogEx(SUCCESS, "execute command from commandline: " _YELLOW_("%s") "\n", script_cmd);
1386 // try to open USB connection to Proxmark
1387 if (port != NULL) {
1388 OpenProxmark(&g_session.current_device, port, waitCOMPort, 20, false, speed);
1391 if (g_session.pm3_present && (TestProxmark(g_session.current_device) != PM3_SUCCESS)) {
1392 PrintAndLogEx(ERR, _RED_("ERROR:") " cannot communicate with the Proxmark3\n");
1393 CloseProxmark(g_session.current_device);
1396 if ((port != NULL) && (!g_session.pm3_present)) {
1397 exit(EXIT_FAILURE);
1400 if (!g_session.pm3_present) {
1401 PrintAndLogEx(INFO, _YELLOW_("OFFLINE") " mode. Check " _YELLOW_("\"%s -h\"") " if it's not what you want.\n", exec_name);
1404 // ascii art only in interactive client
1405 if (!script_cmds_file && !script_cmd && g_session.stdinOnTTY && g_session.stdoutOnTTY && !dumpmem_mode && !flash_mode && !reboot_bootloader_mode) {
1406 showBanner();
1409 // Save settings if not loaded from settings json file.
1410 // Doing this here will ensure other checks and updates are saved to over rule default
1411 // e.g. Linux color use check
1412 if ((!g_session.preferences_loaded) && (!g_session.incognito)) {
1413 PrintAndLogEx(INFO, "Creating initial preferences file"); // json save reports file name, so just info msg here
1414 preferences_save(); // Save defaults
1415 g_session.preferences_loaded = true;
1416 } /* else {
1417 // Set device debug level
1418 PrintAndLogEx(INFO,"setting device debug loglevel");
1419 if (g_session.pm3_present) {
1420 SendCommandNG(CMD_SET_DBGMODE, &g_session.device_debug_level, 1);
1421 PacketResponseNG resp;
1422 if (WaitForResponseTimeout(CMD_SET_DBGMODE, &resp, 2000) == false)
1423 PrintAndLogEx (INFO,"failed to set device debug loglevel");
1425 else
1426 PrintAndLogEx(WARNING,"Proxmark3 not ready to set debug level");
1430 #ifdef HAVE_GUI
1432 # if defined(_WIN32)
1433 InitGraphics(argc, argv, script_cmds_file, script_cmd, stayInCommandLoop);
1434 MainGraphics();
1435 # else
1436 // for *nix distro's, check environment variable to verify a display
1437 const char *display = getenv("DISPLAY");
1438 if (display && strlen(display) > 1) {
1439 InitGraphics(argc, argv, script_cmds_file, script_cmd, stayInCommandLoop);
1440 MainGraphics();
1441 } else {
1442 main_loop(script_cmds_file, script_cmd, stayInCommandLoop);
1444 # endif
1446 #else
1447 main_loop(script_cmds_file, script_cmd, stayInCommandLoop);
1448 #endif
1450 // Clean up the port
1451 if (g_session.pm3_present) {
1452 CloseProxmark(g_session.current_device);
1455 // Plot/Overlay moved or resized
1456 if (g_session.window_changed) {
1457 preferences_save();
1460 free_grabber();
1462 return mainret;
1464 #endif //LIBPM3