Release v4.19552 - Orca
[RRG-proxmark3.git] / client / src / proxmark3.c
blobf0d52cb8af8b3db2f90d971596ab74d1b05547c3
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_SUCCESS;
49 #ifndef LIBPM3
50 #define BANNERMSG1 ""
51 #define BANNERMSG2 " [ :coffee: ]"
52 #define BANNERMSG3 "Release v4.19552 - Orca"
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, "");
230 // PrintAndLogEx(NORMAL, " Monero");
231 // PrintAndLogEx(NORMAL, " 43mNJLpgBVaTvyZmX9ajcohpvVkaRy1kbZPm8tqAb7itZgfuYecgkRF36rXrKFUkwEGeZedPsASRxgv4HPBHvJwyJdyvQuP");
232 PrintAndLogEx(NORMAL, "");
233 fflush(stdout);
234 g_printAndLog = old_printAndLog;
236 #endif //LIBPM3
238 static const char *prompt_dev = "";
239 static const char *prompt_ctx = "";
240 static const char *prompt_net = "";
243 static void prompt_set(void) {
244 if (g_session.pm3_present) {
246 switch (g_conn.send_via_ip) {
247 case PM3_TCPv4:
248 prompt_net = PROXPROMPT_NET_TCPV4;
249 break;
250 case PM3_TCPv6:
251 prompt_net = PROXPROMPT_NET_TCPV6;
252 break;
253 case PM3_UDPv4:
254 prompt_net = PROXPROMPT_NET_UDPV4;
255 break;
256 case PM3_UDPv6:
257 prompt_net = PROXPROMPT_NET_UDPV6;
258 break;
259 case PM3_NONE:
260 prompt_net = PROXPROMPT_NET_NONE;
261 break;
262 default:
263 break;
266 if (g_conn.send_via_fpc_usart)
267 prompt_dev = PROXPROMPT_DEV_FPC;
268 else
269 prompt_dev = PROXPROMPT_DEV_USB;
271 } else {
272 prompt_dev = PROXPROMPT_DEV_OFFLINE;
276 static void prompt_compose(char *buf, size_t buflen, const char *promptctx, const char *promptdev, const char *promptnet, bool no_newline) {
277 if (no_newline) {
278 snprintf(buf, buflen - 1, PROXPROMPT_COMPOSE, promptdev, promptnet, promptctx);
279 } else {
280 snprintf(buf, buflen - 1, "\33[2K\r" PROXPROMPT_COMPOSE, promptdev, promptnet, promptctx);
284 static bool c_update_reconnect_prompt = false;
286 // This function is hooked via RL_EVENT_HOOK.
287 static int check_comm(void) {
288 // If communications thread goes down. Device disconnected then this should hook up PM3 again.
289 if (IsCommunicationThreadDead() && g_session.pm3_present) {
291 #ifndef HAVE_READLINE
292 PrintAndLogEx(INFO, _YELLOW_("OFFLINE") " mode. Use "_YELLOW_("\"hw connect\"") " to reconnect\n");
293 #endif
294 prompt_dev = PROXPROMPT_DEV_OFFLINE;
295 char prompt[PROXPROMPT_MAX_SIZE] = {0};
296 prompt_compose(prompt, sizeof(prompt), prompt_ctx, prompt_dev, prompt_net, false);
297 char prompt_filtered[PROXPROMPT_MAX_SIZE] = {0};
298 memcpy_filter_ansi(prompt_filtered, prompt, sizeof(prompt_filtered), !g_session.supports_colors);
299 pm3line_update_prompt(prompt_filtered);
300 CloseProxmark(g_session.current_device);
301 StartReconnectProxmark();
302 c_update_reconnect_prompt = true;
304 // its alive again
305 if (c_update_reconnect_prompt && IsReconnectedOk() && g_session.pm3_present) {
307 prompt_set();
309 char prompt[PROXPROMPT_MAX_SIZE] = {0};
310 prompt_compose(prompt, sizeof(prompt), prompt_ctx, prompt_dev, prompt_net, false);
311 char prompt_filtered[PROXPROMPT_MAX_SIZE] = {0};
312 memcpy_filter_ansi(prompt_filtered, prompt, sizeof(prompt_filtered), !g_session.supports_colors);
313 pm3line_update_prompt(prompt_filtered);
314 c_update_reconnect_prompt = false;
317 msleep(50);
318 return 0;
321 #if defined(_WIN32)
322 static bool DetectWindowsAnsiSupport(void) {
323 #ifndef ENABLE_VIRTUAL_TERMINAL_PROCESSING
324 #define ENABLE_VIRTUAL_TERMINAL_PROCESSING 0x0004
325 #endif
327 // disable colors if stdin or stdout are redirected
328 if ((! g_session.stdinOnTTY) || (! g_session.stdoutOnTTY))
329 return false;
331 HANDLE hOut = GetStdHandle(STD_OUTPUT_HANDLE);
332 DWORD dwMode = 0;
333 GetConsoleMode(hOut, &dwMode);
335 //ENABLE_VIRTUAL_TERMINAL_PROCESSING is already set
336 if ((dwMode & ENABLE_VIRTUAL_TERMINAL_PROCESSING))
337 return true;
339 dwMode |= ENABLE_VIRTUAL_TERMINAL_PROCESSING;
341 return SetConsoleMode(hOut, dwMode) ? true : false;
343 #endif //_WIN32
345 // first slot is always NULL, indicating absence of script when idx=0
346 static FILE *cmdscriptfile[MAX_NESTED_CMDSCRIPT + 1] = {0};
347 static uint8_t cmdscriptfile_idx = 0;
348 static bool cmdscriptfile_stayafter = false;
350 int push_cmdscriptfile(char *path, bool stayafter) {
351 if (cmdscriptfile_idx == MAX_NESTED_CMDSCRIPT) {
352 PrintAndLogEx(ERR, "Too many nested scripts, skipping %s\n", path);
353 return PM3_EMALLOC;
356 FILE *f = fopen(path, "r");
357 if (f == NULL)
358 return PM3_EFILE;
360 if (cmdscriptfile_idx == 0)
361 cmdscriptfile_stayafter = stayafter;
363 cmdscriptfile[++cmdscriptfile_idx] = f;
364 return PM3_SUCCESS;
367 static FILE *current_cmdscriptfile(void) {
368 return cmdscriptfile[cmdscriptfile_idx];
371 static bool pop_cmdscriptfile(void) {
372 fclose(cmdscriptfile[cmdscriptfile_idx]);
373 cmdscriptfile[cmdscriptfile_idx--] = NULL;
374 if (cmdscriptfile_idx == 0)
375 return cmdscriptfile_stayafter;
376 else
377 return true;
380 // Main thread of PM3 Client
381 void
382 #ifdef __has_attribute
383 #if __has_attribute(force_align_arg_pointer)
384 __attribute__((force_align_arg_pointer))
385 #endif
386 #endif
387 main_loop(const char *script_cmds_file, char *script_cmd, bool stayInCommandLoop) {
389 char *cmd = NULL;
390 bool execCommand = (script_cmd != NULL);
391 bool fromInteractive = false;
392 uint16_t script_cmd_len = 0;
393 if (execCommand) {
394 script_cmd_len = strlen(script_cmd);
395 str_creplace(script_cmd, script_cmd_len, ';', '\0');
397 bool stdinOnPipe = !isatty(STDIN_FILENO);
398 char script_cmd_buf[256] = {0x00}; // iceman, needs lua script the same file_path_buffer as the rest
400 // cache Version information now:
401 if (execCommand || script_cmds_file || stdinOnPipe)
402 pm3_version(false, false);
403 else
404 pm3_version_short();
406 if (script_cmds_file) {
408 char *path;
409 int res = searchFile(&path, CMD_SCRIPTS_SUBDIR, script_cmds_file, ".cmd", false);
410 if (res == PM3_SUCCESS) {
411 if (push_cmdscriptfile(path, stayInCommandLoop) == PM3_SUCCESS)
412 PrintAndLogEx(SUCCESS, "executing commands from file: %s\n", path);
413 else
414 PrintAndLogEx(ERR, "could not open " _YELLOW_("%s") "...", path);
415 free(path);
419 g_session.history_path = NULL;
420 if (g_session.incognito) {
421 PrintAndLogEx(INFO, "No history will be recorded");
422 } else {
423 if (searchHomeFilePath(&g_session.history_path, NULL, PROXHISTORY, true) != PM3_SUCCESS) {
424 g_session.history_path = NULL;
425 PrintAndLogEx(ERR, "No history will be recorded");
426 } else {
427 if (pm3line_load_history(g_session.history_path) != PM3_SUCCESS) {
428 PrintAndLogEx(INFO, "No previous history could be loaded");
433 // loops every time enter is pressed...
434 while (1) {
436 bool printprompt = false;
438 prompt_set();
440 check_script:
441 // If there is a script file
442 if (current_cmdscriptfile()) {
444 // clear array
445 memset(script_cmd_buf, 0, sizeof(script_cmd_buf));
447 // read script file
448 if (fgets(script_cmd_buf, sizeof(script_cmd_buf), current_cmdscriptfile()) == NULL) {
449 if (pop_cmdscriptfile() == false) {
450 break;
452 goto check_script;
455 prompt_ctx = PROXPROMPT_CTX_SCRIPTFILE;
457 // remove linebreaks
458 str_cleanrn(script_cmd_buf, sizeof(script_cmd_buf));
460 cmd = str_dup(script_cmd_buf);
461 if (cmd != NULL) {
462 printprompt = true;
465 } else {
466 // If there is a script command
467 if (execCommand) {
468 prompt_ctx = stdinOnPipe ? PROXPROMPT_CTX_STDIN : PROXPROMPT_CTX_SCRIPTCMD;
470 cmd = str_dup(script_cmd);
471 if ((cmd != NULL) && (! fromInteractive))
472 printprompt = true;
474 uint16_t len = strlen(script_cmd) + 1;
475 script_cmd += len;
477 if (script_cmd_len == len - 1)
478 execCommand = false;
480 script_cmd_len -= len;
481 } else {
482 // exit after exec command
483 if (script_cmd && !stayInCommandLoop)
484 break;
486 // if there is a pipe from stdin
487 if (stdinOnPipe) {
488 // clear array
489 memset(script_cmd_buf, 0, sizeof(script_cmd_buf));
490 // get
491 if (fgets(script_cmd_buf, sizeof(script_cmd_buf), stdin) == NULL) {
492 PrintAndLogEx(ERR, "STDIN unexpected end, exit...");
493 break;
495 execCommand = true;
496 stayInCommandLoop = true;
497 fromInteractive = false;
498 script_cmd = script_cmd_buf;
499 script_cmd_len = strlen(script_cmd);
500 str_creplace(script_cmd, script_cmd_len, ';', '\0');
501 // remove linebreaks
502 str_cleanrn(script_cmd, script_cmd_len);
503 goto check_script;
504 } else {
505 pm3line_check(check_comm);
506 prompt_ctx = PROXPROMPT_CTX_INTERACTIVE;
507 char prompt[PROXPROMPT_MAX_SIZE] = {0};
508 prompt_compose(prompt, sizeof(prompt), prompt_ctx, prompt_dev, prompt_net, true);
509 char prompt_filtered[PROXPROMPT_MAX_SIZE] = {0};
510 memcpy_filter_ansi(prompt_filtered, prompt, sizeof(prompt_filtered), !g_session.supports_colors);
511 g_pendingPrompt = true;
512 script_cmd = pm3line_read(prompt_filtered);
513 #if defined(_WIN32)
514 //Check if color support needs to be enabled again in case the window buffer did change
515 g_session.supports_colors = DetectWindowsAnsiSupport();
516 #endif
517 if (script_cmd != NULL) {
518 execCommand = true;
519 stayInCommandLoop = true;
520 fromInteractive = true;
521 script_cmd_len = strlen(script_cmd);
522 str_creplace(script_cmd, script_cmd_len, ';', '\0');
523 // remove linebreaks
524 str_cleanrn(script_cmd, script_cmd_len);
525 goto check_script;
527 fflush(NULL);
532 // execute command
533 if (cmd) {
535 // rtrim
536 size_t l = strlen(cmd);
537 while (l > 0 && isspace(cmd[l - 1])) {
538 cmd[--l] = '\0';
540 // ltrim
541 size_t off = 0;
542 while ((cmd[off] != '\0') && isspace(cmd[off])) {
543 off++;
546 for (size_t i = 0; i < strlen(cmd) - off; i++) {
547 cmd[i] = cmd[i + off];
550 cmd[strlen(cmd) - off] = '\0';
552 if (cmd[0] != '\0') {
553 uint8_t old_printAndLog = g_printAndLog;
554 if (!printprompt) {
555 g_printAndLog &= ~PRINTANDLOG_PRINT;
557 char prompt[PROXPROMPT_MAX_SIZE] = {0};
558 prompt_compose(prompt, sizeof(prompt), prompt_ctx, prompt_dev, prompt_net, true);
559 // always filter RL magic separators if not using readline
560 char prompt_filtered[PROXPROMPT_MAX_SIZE] = {0};
561 memcpy_filter_rlmarkers(prompt_filtered, prompt, sizeof(prompt_filtered));
562 PrintAndLogEx(NORMAL, "%s%s", prompt_filtered, cmd);
563 g_printAndLog = old_printAndLog;
565 // add to history if not from a script
566 if (!current_cmdscriptfile()) {
567 pm3line_add_history(cmd);
569 // process cmd
570 g_pendingPrompt = false;
571 mainret = CommandReceived(cmd);
573 // exit or quit
574 if (mainret == PM3_EFATAL)
575 break;
576 if (mainret == PM3_SQUIT) {
577 // Normal quit, map to 0
578 mainret = PM3_SUCCESS;
579 break;
582 free(cmd);
583 cmd = NULL;
584 } else {
585 PrintAndLogEx(NORMAL, "\n");
586 if (script_cmds_file && stayInCommandLoop)
587 stayInCommandLoop = false;
588 else
589 break;
591 } // end while
593 if (g_session.pm3_present) {
594 clearCommandBuffer();
595 SendCommandNG(CMD_QUIT_SESSION, NULL, 0);
596 msleep(100); // Make sure command is sent before killing client
599 while (current_cmdscriptfile()) {
600 pop_cmdscriptfile();
603 pm3line_flush_history();
605 if (cmd) {
606 free(cmd);
607 cmd = NULL;
611 #ifndef LIBPM3
612 static void dumpAllHelp(int markdown, bool full_help) {
613 g_session.help_dump_mode = true;
614 PrintAndLogEx(NORMAL, "\n%sProxmark3 command dump%s\n\n", markdown ? "# " : "", markdown ? "" : "\n======================");
615 PrintAndLogEx(NORMAL, "Some commands are available only if a Proxmark3 is actually connected.%s\n", markdown ? " " : "");
616 PrintAndLogEx(NORMAL, "Check column \"offline\" for their availability.\n");
617 PrintAndLogEx(NORMAL, "\n");
618 command_t *cmds = getTopLevelCommandTable();
619 dumpCommandsRecursive(cmds, markdown, full_help);
620 g_session.help_dump_mode = false;
621 PrintAndLogEx(NORMAL, "Full help dump done.");
623 #endif //LIBPM3
625 static char *my_executable_path = NULL;
626 static char *my_executable_directory = NULL;
628 const char *get_my_executable_path(void) {
629 return my_executable_path;
632 const char *get_my_executable_directory(void) {
633 return my_executable_directory;
636 static void set_my_executable_path(void) {
637 int path_length = wai_getExecutablePath(NULL, 0, NULL);
638 if (path_length == -1)
639 return;
641 my_executable_path = (char *)calloc(path_length + 1, sizeof(uint8_t));
642 int dirname_length = 0;
643 if (wai_getExecutablePath(my_executable_path, path_length, &dirname_length) != -1) {
644 my_executable_path[path_length] = '\0';
645 my_executable_directory = (char *)calloc(dirname_length + 2, sizeof(uint8_t));
646 strncpy(my_executable_directory, my_executable_path, dirname_length + 1);
647 my_executable_directory[dirname_length + 1] = '\0';
651 static const char *my_user_directory = NULL;
652 // static char _cwd_Buffer [FILENAME_MAX] = {0};
654 const char *get_my_user_directory(void) {
655 return my_user_directory;
658 static void set_my_user_directory(void) {
659 /* my_user_directory = getenv("HOME");
661 // if not found, default to current directory
662 if (my_user_directory == NULL) {
663 my_user_directory = GetCurrentDir(_cwd_Buffer, sizeof(_cwd_Buffer));
664 // change all slashes to / (windows should not care...
665 for (int i = 0; i < strlen(_cwd_Buffer); i++)
666 if (_cwd_Buffer[i] == '\\') _cwd_Buffer[i] = '/';
667 // my_user_directory = ".";
670 my_user_directory = getenv("HOME");
672 // if not found, default to current directory
673 if (my_user_directory == NULL) {
675 uint16_t pathLen = FILENAME_MAX; // should be a good starting point
676 char *cwd_buffer = (char *)calloc(pathLen, sizeof(uint8_t));
677 if (cwd_buffer == NULL) {
678 PrintAndLogEx(WARNING, "failed to allocate memory");
679 return;
682 while (GetCurrentDir(cwd_buffer, pathLen) == NULL) {
683 if (errno == ERANGE) { // Need bigger buffer
684 pathLen += 10; // if buffer was too small add 10 characters and try again
685 char *tmp = realloc(cwd_buffer, pathLen);
686 if (tmp == NULL) {
687 PrintAndLogEx(WARNING, "failed to allocate memory");
688 free(cwd_buffer);
689 return;
691 cwd_buffer = tmp;
692 } else {
693 free(cwd_buffer);
694 return;
698 for (int i = 0; i < strlen(cwd_buffer); i++) {
699 if (cwd_buffer[i] == '\\') {
700 cwd_buffer[i] = '/';
704 my_user_directory = cwd_buffer;
708 #ifndef LIBPM3
709 static void show_help(bool showFullHelp, char *exec_name) {
711 PrintAndLogEx(NORMAL, "\nsyntax: %s [-h|-t|-m|--fulltext]", exec_name);
712 #ifdef HAVE_PYTHON
713 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);
714 #else // HAVE_PYTHON
715 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);
716 #endif // HAVE_PYTHON
717 PrintAndLogEx(NORMAL, " %s [-p] <port> --flash [--unlock-bootloader] [--image <imagefile>]+ [-w] [-f] [-d <0|1|2>]", exec_name);
719 if (showFullHelp) {
721 PrintAndLogEx(NORMAL, "\nCommon options:");
722 PrintAndLogEx(NORMAL, " -h/--help this help");
723 PrintAndLogEx(NORMAL, " -v/--version print client version");
724 PrintAndLogEx(NORMAL, " -p/--port serial port to connect to");
725 PrintAndLogEx(NORMAL, " -w/--wait 20sec waiting the serial port to appear in the OS");
726 PrintAndLogEx(NORMAL, " -f/--flush output will be flushed after every print");
727 PrintAndLogEx(NORMAL, " -d/--debug <0|1|2> set debugmode");
728 PrintAndLogEx(NORMAL, "\nOptions in client mode:");
729 PrintAndLogEx(NORMAL, " -t/--text dump all interactive command list at once");
730 PrintAndLogEx(NORMAL, " --fulltext dump all interactive command's help at once");
731 PrintAndLogEx(NORMAL, " -m/--markdown dump all interactive command list at once in markdown syntax");
732 PrintAndLogEx(NORMAL, " -b/--baud serial port speed (only needed for physical UART, not for USB-CDC or BT)");
733 PrintAndLogEx(NORMAL, " -c/--command <command> execute one Proxmark3 command (or several separated by ';').");
734 PrintAndLogEx(NORMAL, " -l/--lua <lua_script_file> execute Lua script.");
735 #ifdef HAVE_PYTHON
736 // Technically, --lua and --py are identical and interexchangeable
737 PrintAndLogEx(NORMAL, " -y/--py <python_script_file> execute Python script.");
738 #endif // HAVE_PYTHON
739 PrintAndLogEx(NORMAL, " -s/--script-file <cmd_script_file> script file with one Proxmark3 command per line");
740 PrintAndLogEx(NORMAL, " -i/--interactive enter interactive mode after executing the script or the command");
741 PrintAndLogEx(NORMAL, " --incognito do not use history, prefs file nor log files");
742 PrintAndLogEx(NORMAL, " --ncpu <num_cores> override number of CPU cores");
743 PrintAndLogEx(NORMAL, "\nOptions in flasher mode:");
744 PrintAndLogEx(NORMAL, " --flash flash Proxmark3, requires at least one --image");
745 PrintAndLogEx(NORMAL, " --reboot-to-bootloader reboot Proxmark3 into bootloader mode");
746 PrintAndLogEx(NORMAL, " --unlock-bootloader Enable flashing of bootloader area *DANGEROUS* (need --flash)");
747 PrintAndLogEx(NORMAL, " --force Enable flashing even if firmware seems to not match client version");
748 PrintAndLogEx(NORMAL, " --image <imagefile> image to flash. Can be specified several times.");
749 PrintAndLogEx(NORMAL, "\nOptions in memory dump mode:");
750 PrintAndLogEx(NORMAL, " --dumpmem <dumpfile> dumps Proxmark3 flash memory to file");
751 PrintAndLogEx(NORMAL, " --dumpaddr <address> starting address for dump, default 0");
752 PrintAndLogEx(NORMAL, " --dumplen <length> number of bytes to dump, default 512KB");
753 PrintAndLogEx(NORMAL, " --dumpraw raw address mode: dump from anywhere, not just flash");
754 PrintAndLogEx(NORMAL, "\nExamples:");
755 PrintAndLogEx(NORMAL, "\n to run Proxmark3 client:\n");
756 PrintAndLogEx(NORMAL, " %s "SERIAL_PORT_EXAMPLE_H" -- runs the pm3 client", exec_name);
757 PrintAndLogEx(NORMAL, " %s "SERIAL_PORT_EXAMPLE_H" -f -- flush output every time", exec_name);
758 PrintAndLogEx(NORMAL, " %s "SERIAL_PORT_EXAMPLE_H" -w -- wait for serial port", exec_name);
759 PrintAndLogEx(NORMAL, " %s -- runs the pm3 client in OFFLINE mode", exec_name);
760 PrintAndLogEx(NORMAL, "\n to execute different commands from terminal:\n");
761 PrintAndLogEx(NORMAL, " %s "SERIAL_PORT_EXAMPLE_H" -c \"hf mf chk --1k\" -- execute cmd and quit client", exec_name);
762 PrintAndLogEx(NORMAL, " %s "SERIAL_PORT_EXAMPLE_H" -l hf_read -- execute Lua script `hf_read` and quit client", exec_name);
763 PrintAndLogEx(NORMAL, " %s "SERIAL_PORT_EXAMPLE_H" -s mycmds.txt -- execute each pm3 cmd in file and quit client", exec_name);
764 PrintAndLogEx(NORMAL, "\n to flash fullimage and bootloader:\n");
765 PrintAndLogEx(NORMAL, " %s "SERIAL_PORT_EXAMPLE_H" --flash --unlock-bootloader --image bootrom.elf --image fullimage.elf", exec_name);
766 #ifdef __linux__
767 PrintAndLogEx(NORMAL, "\nNote (Linux):\nif the flasher gets stuck in 'Waiting for Proxmark3 to reappear on <DEVICE>',");
768 PrintAndLogEx(NORMAL, "you need to blacklist Proxmark3 for modem-manager - see documentation for more details:");
769 PrintAndLogEx(NORMAL, "* https://github.com/RfidResearchGroup/proxmark3/blob/master/doc/md/Installation_Instructions/ModemManager-Must-Be-Discarded.md");
770 PrintAndLogEx(NORMAL, "\nMore info on flashing procedure from the official Proxmark3 wiki:");
771 PrintAndLogEx(NORMAL, "* https://github.com/Proxmark/proxmark3/wiki/Gentoo%%20Linux");
772 PrintAndLogEx(NORMAL, "* https://github.com/Proxmark/proxmark3/wiki/Ubuntu%%20Linux");
773 PrintAndLogEx(NORMAL, "* https://github.com/Proxmark/proxmark3/wiki/OSX\n");
774 #endif
778 static int dumpmem_to_file(const char *filename, uint32_t addr, uint32_t len, bool raw, bool in_bootloader) {
780 uint8_t *buffer = calloc(len, sizeof(uint8_t));
781 if (buffer == NULL) {
782 PrintAndLogEx(ERR, "error, cannot allocate memory ");
783 return PM3_EMALLOC;
786 int res = PM3_EUNDEF;
787 size_t readlen = 0;
788 DeviceMemType_t type = raw ? MCU_MEM : MCU_FLASH;
789 if (GetFromDevice(type, buffer, len, addr, NULL, 0, NULL, 1000, true)) {
790 res = PM3_SUCCESS;
791 readlen = len; // GetFromDevice does not report the actual number of bytes received.
794 if (res == PM3_SUCCESS) {
795 res = saveFile(filename, ".bin", buffer, readlen);
796 if (res != PM3_SUCCESS) {
797 PrintAndLogEx(ERR, "error writing to file "_YELLOW_("%s"), filename);
801 free(buffer);
802 return res;
805 static int dumpmem_pm3(char *serial_port_name, const char *filename, uint32_t addr, uint32_t len, bool raw) {
806 int ret = PM3_EUNDEF;
807 bool in_bootloader = false;
809 if (serial_port_name == NULL) {
810 PrintAndLogEx(ERR, "You must specify a port.\n");
811 return PM3_EINVARG;
814 if (OpenProxmark(&g_session.current_device, serial_port_name, true, 60, true, FLASHMODE_SPEED)) {
815 PrintAndLogEx(NORMAL, _GREEN_(" found"));
816 msleep(200);
817 } else {
818 PrintAndLogEx(ERR, "Could not find Proxmark3 on " _RED_("%s") ".\n", serial_port_name);
819 ret = PM3_ETIMEOUT;
820 goto finish;
823 // Determine if we're talking to a bootloader or main firmware.
824 SendCommandBL(CMD_DEVICE_INFO, 0, 0, 0, NULL, 0);
825 PacketResponseNG resp;
826 if (WaitForResponseTimeout(CMD_UNKNOWN, &resp, 1000) == false) {
827 PrintAndLogEx(ERR, "Could not get device info.");
828 goto finish2;
830 uint32_t dev_info = resp.oldarg[0];
831 in_bootloader = (dev_info & DEVICE_INFO_FLAG_CURRENT_MODE_BOOTROM) != 0;
832 if (in_bootloader) {
833 if ((dev_info & DEVICE_INFO_FLAG_UNDERSTANDS_READ_MEM) != 0) {
834 PrintAndLogEx(INFO, "Device is running the bootloader.");
835 } else {
836 PrintAndLogEx(ERR, "Device is running the bootloader, but the bootloader"
837 " doesn't understand the READ MEM command.");
838 goto finish2;
842 PrintAndLogEx(SUCCESS, "Dump requested from address "_YELLOW_("%u")", length "_YELLOW_("%u")"%s.",
843 addr, len, raw ? ", in raw address mode" : "");
845 PrintAndLogEx(SUCCESS, _CYAN_("Memory dumping to file..."));
846 ret = dumpmem_to_file(filename, addr, len, raw, in_bootloader);
847 if (ret != PM3_SUCCESS) {
848 goto finish2;
850 PrintAndLogEx(NORMAL, "");
852 finish2:
853 clearCommandBuffer();
854 if (in_bootloader) {
855 g_session.current_device->g_conn->run = false;
856 SendCommandOLD(CMD_PING, 0, 0, 0, NULL, 0);
857 } else {
858 SendCommandNG(CMD_QUIT_SESSION, NULL, 0);
859 msleep(100);
861 CloseProxmark(g_session.current_device);
863 finish:
864 if (ret == PM3_SUCCESS)
865 PrintAndLogEx(SUCCESS, _CYAN_("All done"));
866 else if (ret == PM3_EOPABORTED)
867 PrintAndLogEx(FAILED, "Aborted by user");
868 else
869 PrintAndLogEx(ERR, "Aborted on error %u", ret);
870 return ret;
873 static int flash_pm3(char *serial_port_name, uint8_t num_files, const char *filenames[FLASH_MAX_FILES], bool can_write_bl, bool force) {
875 int ret = PM3_EUNDEF;
876 flash_file_t files[FLASH_MAX_FILES];
877 memset(files, 0, sizeof(files));
879 if (serial_port_name == NULL) {
880 PrintAndLogEx(ERR, "You must specify a port.\n");
881 return PM3_EINVARG;
884 for (int i = 0 ; i < num_files; ++i) {
885 char *path;
886 ret = searchFile(&path, FIRMWARES_SUBDIR, filenames[i], ".elf", true);
887 if (ret != PM3_SUCCESS) {
888 ret = searchFile(&path, BOOTROM_SUBDIR, filenames[i], ".elf", true);
890 if (ret != PM3_SUCCESS) {
891 // Last try, let the error msg be displayed if not found
892 ret = searchFile(&path, FULLIMAGE_SUBDIR, filenames[i], ".elf", false);
894 if (ret != PM3_SUCCESS) {
895 goto finish2;
897 files[i].filename = path;
900 PrintAndLogEx(SUCCESS, "About to use the following file%s:", num_files > 1 ? "s" : "");
901 for (int i = 0 ; i < num_files; ++i) {
902 PrintAndLogEx(SUCCESS, " "_YELLOW_("%s"), files[i].filename);
905 for (int i = 0 ; i < num_files; ++i) {
906 ret = flash_load(&files[i], force);
907 if (ret != PM3_SUCCESS) {
908 goto finish2;
910 PrintAndLogEx(NORMAL, "");
913 if (OpenProxmark(&g_session.current_device, serial_port_name, true, 60, true, FLASHMODE_SPEED)) {
914 PrintAndLogEx(NORMAL, _GREEN_(" found"));
915 msleep(200);
916 } else {
917 PrintAndLogEx(ERR, "Could not find Proxmark3 on " _RED_("%s") ".\n", serial_port_name);
918 ret = PM3_ETIMEOUT;
919 goto finish2;
922 uint32_t max_allowed = 0;
923 ret = flash_start_flashing(can_write_bl, serial_port_name, &max_allowed);
924 if (ret != PM3_SUCCESS) {
925 goto finish;
928 if (num_files == 0)
929 goto finish;
931 for (int i = 0 ; i < num_files; ++i) {
932 ret = flash_prepare(&files[i], can_write_bl, max_allowed * ONE_KB);
933 if (ret != PM3_SUCCESS) {
934 goto finish;
936 PrintAndLogEx(NORMAL, "");
939 PrintAndLogEx(SUCCESS, _CYAN_("Flashing..."));
941 for (int i = 0; i < num_files; i++) {
942 ret = flash_write(&files[i]);
943 if (ret != PM3_SUCCESS) {
944 goto finish;
946 PrintAndLogEx(NORMAL, "");
949 finish:
950 if (ret != PM3_SUCCESS)
951 PrintAndLogEx(WARNING, "The flashing procedure failed, follow the suggested steps!");
952 ret = flash_stop_flashing();
953 CloseProxmark(g_session.current_device);
954 finish2:
955 for (int i = 0 ; i < num_files; ++i) {
956 flash_free(&files[i]);
958 if (ret == PM3_SUCCESS)
959 PrintAndLogEx(SUCCESS, _CYAN_("All done"));
960 else if (ret == PM3_EOPABORTED)
961 PrintAndLogEx(FAILED, "Aborted by user");
962 else
963 PrintAndLogEx(ERR, "Aborted on error");
964 PrintAndLogEx(INFO, "\nHave a nice day!");
965 return ret;
968 static int reboot_bootloader_pm3(char *serial_port_name) {
969 if (serial_port_name == NULL) {
970 PrintAndLogEx(ERR, "You must specify a port.\n");
971 return PM3_EINVARG;
974 if (OpenProxmark(&g_session.current_device, serial_port_name, true, 60, true, FLASHMODE_SPEED) == false) {
975 PrintAndLogEx(ERR, "Could not find Proxmark3 on " _RED_("%s") ".\n", serial_port_name);
976 return PM3_ETIMEOUT;
979 PrintAndLogEx(NORMAL, _GREEN_(" found"));
980 return flash_reboot_bootloader(serial_port_name, true);
983 #endif //LIBPM3
985 void pm3_init(void) {
986 srand(time(0));
988 g_session.pm3_present = false;
989 g_session.help_dump_mode = false;
990 g_session.incognito = false;
991 g_session.supports_colors = false;
992 g_session.emoji_mode = EMO_ALTTEXT;
993 g_session.stdinOnTTY = false;
994 g_session.stdoutOnTTY = false;
996 // set global variables soon enough to get the log path
997 set_my_executable_path();
998 set_my_user_directory();
1002 #ifndef LIBPM3
1003 int main(int argc, char *argv[]) {
1004 pm3_init();
1005 bool waitCOMPort = false;
1006 bool addScriptExec = false;
1007 bool stayInCommandLoop = false;
1008 char *script_cmds_file = NULL;
1009 char *script_cmd = NULL;
1010 char *port = NULL;
1011 uint32_t speed = 0;
1013 pm3line_init();
1015 char exec_name[100] = {0};
1016 strncpy(exec_name, basename(argv[0]), sizeof(exec_name) - 1);
1018 bool flash_mode = false;
1019 bool reboot_bootloader_mode = false;
1020 bool flash_can_write_bl = false;
1021 bool flash_force = false;
1022 bool debug_mode_forced = false;
1023 int flash_num_files = 0;
1024 const char *flash_filenames[FLASH_MAX_FILES];
1025 bool dumpmem_mode = false;
1026 const char *dumpmem_filename = NULL;
1027 uint32_t dumpmem_addr = 0;
1028 uint32_t dumpmem_len = 512 * 1024;
1029 bool dumpmem_raw = false;
1031 // color management:
1032 // 1. default = no color
1033 // 2. enable colors if OS seems to support colors and if stdin/stdout aren't redirected
1034 // 3. load prefs if available, overwrite colors choice if needed
1035 // 4. disable colors anyway if stdin/stdout are redirected
1037 // For info, grep --color=auto is doing sth like this, plus test getenv("TERM") != "dumb":
1038 // struct stat tmp_stat;
1039 // if ((fstat (STDOUT_FILENO, &tmp_stat) == 0) && (S_ISCHR (tmp_stat.st_mode)) && isatty(STDIN_FILENO))
1040 g_session.stdinOnTTY = isatty(STDIN_FILENO);
1041 g_session.stdoutOnTTY = isatty(STDOUT_FILENO);
1042 g_session.supports_colors = false;
1043 g_session.emoji_mode = EMO_ALTTEXT;
1044 if (g_session.stdinOnTTY && g_session.stdoutOnTTY) {
1045 #if defined(__linux__) || defined(__APPLE__)
1046 g_session.supports_colors = true;
1047 g_session.emoji_mode = EMO_EMOJI;
1048 #elif defined(_WIN32)
1049 g_session.supports_colors = DetectWindowsAnsiSupport();
1050 g_session.emoji_mode = EMO_ALTTEXT;
1051 #endif
1053 for (int i = 1; i < argc; i++) {
1055 if (argv[i][0] != '-') {
1056 // For backward compatibility we accept direct port
1057 if (port != NULL) {
1058 // We got already one
1059 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]);
1060 show_help(false, exec_name);
1061 return 1;
1063 port = argv[i];
1064 continue;
1067 // port
1068 if (strcmp(argv[i], "-p") == 0 || strcmp(argv[i], "--port") == 0) {
1069 if (i + 1 == argc) {
1070 PrintAndLogEx(ERR, _RED_("ERROR:") " missing port specification after -p\n");
1071 show_help(false, exec_name);
1072 return 1;
1074 if (port != NULL) {
1075 // We got already one
1076 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]);
1077 show_help(false, exec_name);
1078 return 1;
1080 port = argv[++i];
1081 continue;
1084 // short help
1085 if (strcmp(argv[i], "-h") == 0 || strcmp(argv[i], "--help") == 0) {
1086 g_printAndLog = PRINTANDLOG_PRINT;
1087 show_help(true, exec_name);
1088 return 0;
1091 // dump help
1092 if (strcmp(argv[i], "-t") == 0 || strcmp(argv[i], "--text") == 0) {
1093 g_printAndLog = PRINTANDLOG_PRINT;
1094 show_help(false, exec_name);
1095 dumpAllHelp(0, false);
1096 return 0;
1099 // dump help
1100 if (strcmp(argv[i], "--fulltext") == 0) {
1101 g_printAndLog = PRINTANDLOG_PRINT;
1102 show_help(false, exec_name);
1103 dumpAllHelp(0, true);
1104 return 0;
1107 // dump markup
1108 if (strcmp(argv[i], "-m") == 0 || strcmp(argv[i], "--markdown") == 0) {
1109 g_printAndLog = PRINTANDLOG_PRINT;
1110 dumpAllHelp(1, false);
1111 return 0;
1113 // print client version
1114 if (strcmp(argv[i], "-v") == 0 || strcmp(argv[i], "--version") == 0) {
1115 pm3_version(true, true);
1116 return 0;
1119 // set debugmode
1120 if (strcmp(argv[i], "-d") == 0 || strcmp(argv[i], "--debug") == 0) {
1121 if (i + 1 == argc) {
1122 PrintAndLogEx(ERR, _RED_("ERROR:") " missing debugmode specification after -d\n");
1123 show_help(false, exec_name);
1124 return 1;
1126 int demod = atoi(argv[i + 1]);
1127 if (demod < 0 || demod > 2) {
1128 PrintAndLogEx(ERR, _RED_("ERROR:") " invalid debugmode: -d " _YELLOW_("%s") "\n", argv[i + 1]);
1129 return 1;
1131 g_debugMode = demod;
1132 debug_mode_forced = true;
1133 i++;
1134 continue;
1137 // flush output
1138 if (strcmp(argv[i], "-f") == 0 || strcmp(argv[i], "--flush") == 0) {
1139 SetFlushAfterWrite(true);
1140 PrintAndLogEx(INFO, "Output will be flushed after every print.\n");
1141 continue;
1144 // set baudrate
1145 if (strcmp(argv[i], "-b") == 0 || strcmp(argv[i], "--baud") == 0) {
1146 if (i + 1 == argc) {
1147 PrintAndLogEx(ERR, _RED_("ERROR:") " missing baud specification after -b\n");
1148 show_help(false, exec_name);
1149 return 1;
1151 uint64_t tmpspeed = strtoul(argv[i + 1], NULL, 10);
1152 if ((tmpspeed == ULONG_MAX) || (tmpspeed == 0)) {
1153 PrintAndLogEx(ERR, _RED_("ERROR:") " invalid baudrate: -b " _YELLOW_("%s") "\n", argv[i + 1]);
1154 return 1;
1156 speed = tmpspeed;
1157 i++;
1158 continue;
1161 // wait for comport
1162 if (strcmp(argv[i], "-w") == 0 || strcmp(argv[i], "--wait") == 0) {
1163 waitCOMPort = true;
1164 continue;
1167 // execute pm3 command
1168 if (strcmp(argv[i], "-c") == 0 || strcmp(argv[i], "--command") == 0) {
1169 if (i + 1 == argc) {
1170 PrintAndLogEx(ERR, _RED_("ERROR:") " missing command specification after -c\n");
1171 show_help(false, exec_name);
1172 return 1;
1174 script_cmd = argv[++i];
1175 continue;
1178 // execute pm3 command file
1179 if (strcmp(argv[i], "-s") == 0 || strcmp(argv[i], "--script-file") == 0) {
1180 if (i + 1 == argc || strlen(argv[i + 1]) == 0) {
1181 PrintAndLogEx(ERR, _RED_("ERROR:") " missing script file specification after -s\n");
1182 show_help(false, exec_name);
1183 return 1;
1185 script_cmds_file = argv[++i];
1186 continue;
1189 // execute Lua script
1190 if (strcmp(argv[i], "-l") == 0 || strcmp(argv[i], "--lua") == 0) {
1191 if (i + 1 == argc || strlen(argv[i + 1]) == 0) {
1192 PrintAndLogEx(ERR, _RED_("ERROR:") " missing Lua script specification after --lua\n");
1193 show_help(false, exec_name);
1194 return 1;
1196 script_cmd = argv[++i];
1197 if (script_cmd == NULL || strlen(script_cmd) == 0) {
1198 return 1;
1200 addScriptExec = true;
1201 continue;
1203 #ifdef HAVE_PYTHON
1204 // execute Python script
1205 if (strcmp(argv[i], "-y") == 0 || strcmp(argv[i], "--py") == 0) {
1206 if (i + 1 == argc || strlen(argv[i + 1]) == 0) {
1207 PrintAndLogEx(ERR, _RED_("ERROR:") " missing Python script specification after --py\n");
1208 show_help(false, exec_name);
1209 return 1;
1211 script_cmd = argv[++i];
1212 if (script_cmd == NULL || strlen(script_cmd) == 0) {
1213 return 1;
1215 addScriptExec = true;
1216 continue;
1218 #endif // HAVE_PYTHON
1219 // go to interactive instead of quitting after a script/command
1220 if (strcmp(argv[i], "-i") == 0 || strcmp(argv[i], "--interactive") == 0) {
1221 stayInCommandLoop = true;
1222 continue;
1225 // do not use history nor log files
1226 if (strcmp(argv[i], "--incognito") == 0) {
1227 g_session.incognito = true;
1228 continue;
1231 // go to dump mode
1232 if (strcmp(argv[i], "--dumpmem") == 0) {
1233 dumpmem_mode = true;
1234 if (i + 1 == argc) {
1235 PrintAndLogEx(ERR, _RED_("ERROR:") " missing file specification after --dumpmem\n");
1236 show_help(false, exec_name);
1237 return 1;
1239 dumpmem_filename = argv[++i];
1240 continue;
1242 if (strcmp(argv[i], "--dumpaddr") == 0) {
1243 if (i + 1 == argc) {
1244 PrintAndLogEx(ERR, _RED_("ERROR:") " missing address specification after -dumpaddr\n");
1245 show_help(false, exec_name);
1246 return 1;
1248 uint32_t tmpaddr = strtoul(argv[i + 1], NULL, 0);
1249 dumpmem_addr = tmpaddr;
1250 i++;
1251 continue;
1253 if (strcmp(argv[i], "--dumplen") == 0) {
1254 if (i + 1 == argc) {
1255 PrintAndLogEx(ERR, _RED_("ERROR:") " missing address specification after -dumplen\n");
1256 show_help(false, exec_name);
1257 return 1;
1259 uint32_t tmplen = strtoul(argv[i + 1], NULL, 0);
1260 dumpmem_len = tmplen;
1261 i++;
1262 continue;
1264 if (strcmp(argv[i], "--dumpraw") == 0) {
1265 dumpmem_raw = true;
1266 continue;
1269 // go to flash mode
1270 if (strcmp(argv[i], "--flash") == 0) {
1271 flash_mode = true;
1272 continue;
1275 // go to flash mode
1276 if (strcmp(argv[i], "--reboot-to-bootloader") == 0) {
1277 reboot_bootloader_mode = true;
1278 continue;
1281 // unlock bootloader area
1282 if (strcmp(argv[i], "--unlock-bootloader") == 0) {
1283 flash_can_write_bl = true;
1284 continue;
1287 // force flash even if firmware seems to not match client version
1288 if (strcmp(argv[i], "--force") == 0) {
1289 flash_force = true;
1290 continue;
1293 // flash file
1294 if (strcmp(argv[i], "--image") == 0) {
1295 if (flash_num_files == FLASH_MAX_FILES) {
1296 PrintAndLogEx(ERR, _RED_("ERROR:") " too many --image, please use it max %i times\n", FLASH_MAX_FILES);
1297 return 1;
1299 if (i + 1 == argc) {
1300 PrintAndLogEx(ERR, _RED_("ERROR:") " missing image specification after --image\n");
1301 show_help(false, exec_name);
1302 return 1;
1304 flash_filenames[flash_num_files++] = argv[++i];
1305 continue;
1308 if (strcmp(argv[i], "--ncpu") == 0) {
1309 if (i + 1 == argc) {
1310 PrintAndLogEx(ERR, _RED_("ERROR:") " missing CPU number specification after --ncpu\n");
1311 show_help(false, exec_name);
1312 return 1;
1314 long int ncpus = strtol(argv[i + 1], NULL, 10);
1315 const int detected_cpus = detect_num_CPUs();
1316 if (ncpus < 0 || ncpus >= detected_cpus) {
1317 PrintAndLogEx(ERR, _RED_("ERROR:") " invalid number of CPU cores: --ncpu " _YELLOW_("%s") " (available: %d)\n", argv[i + 1], detected_cpus);
1318 return 1;
1320 g_numCPUs = ncpus;
1321 i++;
1322 continue;
1325 // We got an unknown parameter
1326 PrintAndLogEx(ERR, _RED_("ERROR:") " invalid parameter: " _YELLOW_("%s") "\n", argv[i]);
1327 show_help(false, exec_name);
1328 return 1;
1331 // Load Settings and assign
1332 // This will allow the command line to override the settings.json values
1333 preferences_load();
1334 // quick patch for debug level
1335 if (! debug_mode_forced)
1336 g_debugMode = g_session.client_debug_level;
1337 // settings_save ();
1338 // End Settings
1340 // even if prefs, we disable colors if stdin or stdout is not a TTY
1341 if ((! g_session.stdinOnTTY) || (! g_session.stdoutOnTTY)) {
1342 g_session.supports_colors = false;
1343 g_session.emoji_mode = EMO_ALTTEXT;
1346 // Let's take a baudrate ok for real UART, USB-CDC & BT don't use that info anyway
1347 if (speed == 0)
1348 speed = USART_BAUD_RATE;
1350 if (dumpmem_mode) {
1351 dumpmem_pm3(port, dumpmem_filename, dumpmem_addr, dumpmem_len, dumpmem_raw);
1352 exit(EXIT_SUCCESS);
1355 if (flash_mode) {
1356 flash_pm3(port, flash_num_files, flash_filenames, flash_can_write_bl, flash_force);
1357 exit(EXIT_SUCCESS);
1360 if (reboot_bootloader_mode) {
1361 reboot_bootloader_pm3(port);
1362 exit(EXIT_SUCCESS);
1365 if (script_cmd) {
1366 while (script_cmd[strlen(script_cmd) - 1] == ' ')
1367 script_cmd[strlen(script_cmd) - 1] = 0x00;
1369 if (strlen(script_cmd) == 0) {
1370 script_cmd = NULL;
1371 PrintAndLogEx(ERR, _RED_("ERROR:") " execute command: " _YELLOW_("command not found") ".\n");
1372 return 2;
1373 } else {
1374 if (addScriptExec) {
1375 // add "script run " to command
1376 int len = strlen(script_cmd) + 11 + 1;
1377 char *ctmp = (char *) calloc(len, sizeof(uint8_t));
1378 if (ctmp != NULL) {
1379 memset(ctmp, 0, len);
1380 strcpy(ctmp, "script run ");
1381 strcpy(&ctmp[11], script_cmd);
1382 script_cmd = ctmp;
1386 PrintAndLogEx(SUCCESS, "execute command from commandline: " _YELLOW_("%s") "\n", script_cmd);
1390 // try to open USB connection to Proxmark
1391 if (port != NULL) {
1392 OpenProxmark(&g_session.current_device, port, waitCOMPort, 20, false, speed);
1395 if (g_session.pm3_present && (TestProxmark(g_session.current_device) != PM3_SUCCESS)) {
1396 PrintAndLogEx(ERR, _RED_("ERROR:") " cannot communicate with the Proxmark3\n");
1397 CloseProxmark(g_session.current_device);
1400 if ((port != NULL) && (!g_session.pm3_present)) {
1401 exit(EXIT_FAILURE);
1404 if (!g_session.pm3_present) {
1405 PrintAndLogEx(INFO, _YELLOW_("OFFLINE") " mode. Check " _YELLOW_("\"%s -h\"") " if it's not what you want.\n", exec_name);
1408 // ascii art only in interactive client
1409 if (!script_cmds_file && !script_cmd && g_session.stdinOnTTY && g_session.stdoutOnTTY && !dumpmem_mode && !flash_mode && !reboot_bootloader_mode) {
1410 showBanner();
1413 // Save settings if not loaded from settings json file.
1414 // Doing this here will ensure other checks and updates are saved to over rule default
1415 // e.g. Linux color use check
1416 if ((!g_session.preferences_loaded) && (!g_session.incognito)) {
1417 PrintAndLogEx(INFO, "Creating initial preferences file"); // json save reports file name, so just info msg here
1418 preferences_save(); // Save defaults
1419 g_session.preferences_loaded = true;
1420 } /* else {
1421 // Set device debug level
1422 PrintAndLogEx(INFO,"setting device debug loglevel");
1423 if (g_session.pm3_present) {
1424 SendCommandNG(CMD_SET_DBGMODE, &g_session.device_debug_level, 1);
1425 PacketResponseNG resp;
1426 if (WaitForResponseTimeout(CMD_SET_DBGMODE, &resp, 2000) == false)
1427 PrintAndLogEx (INFO,"failed to set device debug loglevel");
1429 else
1430 PrintAndLogEx(WARNING,"Proxmark3 not ready to set debug level");
1434 #ifdef HAVE_GUI
1436 # if defined(_WIN32)
1437 InitGraphics(argc, argv, script_cmds_file, script_cmd, stayInCommandLoop);
1438 MainGraphics();
1439 # else
1440 // for *nix distro's, check environment variable to verify a display
1441 const char *display = getenv("DISPLAY");
1442 if (display && strlen(display) > 1) {
1443 InitGraphics(argc, argv, script_cmds_file, script_cmd, stayInCommandLoop);
1444 MainGraphics();
1445 } else {
1446 main_loop(script_cmds_file, script_cmd, stayInCommandLoop);
1448 # endif
1450 #else
1451 main_loop(script_cmds_file, script_cmd, stayInCommandLoop);
1452 #endif
1454 // Clean up the port
1455 if (g_session.pm3_present) {
1456 CloseProxmark(g_session.current_device);
1459 // Plot/Overlay moved or resized
1460 if (g_session.window_changed) {
1461 preferences_save();
1464 free_grabber();
1466 return mainret;
1468 #endif //LIBPM3