Files
proxmark3/client/src/proxmark3.c
T
iceman1001 6ed1853320 Revert "Release v4.20142 - Blue Ice"
This reverts commit a657dbda9b.
2025-03-25 16:18:49 +01:00

1450 lines
54 KiB
C

//-----------------------------------------------------------------------------
// Copyright (C) Proxmark3 contributors. See AUTHORS.md for details.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// See LICENSE.txt for the text of the license.
//-----------------------------------------------------------------------------
// Main binary
//-----------------------------------------------------------------------------
#include "proxmark3.h"
#include <stdlib.h>
#include <limits.h>
#include <unistd.h>
#include <ctype.h>
#include <libgen.h> // basename
#include <time.h>
#include "pm3line.h"
#include "usart_defs.h"
#include "util_posix.h"
#include "proxgui.h"
#include "cmdmain.h"
#include "ui.h"
#include "cmdhw.h"
#include "whereami.h"
#include "comms.h"
#include "fileutils.h"
#include "flash.h"
#include "preferences.h"
#include "commonutil.h"
#ifndef _WIN32
#include <locale.h>
#endif
static int mainret = PM3_SUCCESS;
#ifndef LIBPM3
#define BANNERMSG1 ""
#define BANNERMSG2 ""
#define BANNERMSG3 ""
typedef enum LogoMode { UTF8, ANSI, ASCII } LogoMode;
static void showBanner_logo(LogoMode mode) {
switch (mode) {
case UTF8: {
const char *sq = "\xE2\x96\x88"; // square block
const char *tr = "\xE2\x95\x97"; // top right corner
const char *tl = "\xE2\x95\x94"; // top left corner
const char *br = "\xE2\x95\x9D"; // bottom right corner
const char *bl = "\xE2\x95\x9A"; // bottom left corner
const char *hl = "\xE2\x95\x90"; // horiz line
const char *vl = "\xE2\x95\x91"; // vert line
const char *__ = " ";
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"),
sq, sq, sq, sq, sq, sq, tr, __, sq, sq, sq, tr, __, __, __, sq, sq, sq, tr, sq, sq, sq, sq, sq, tr, __);
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"),
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);
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"),
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);
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"),
sq, sq, tl, hl, hl, hl, br, __, sq, sq, vl, bl, sq, sq, tl, br, sq, sq, vl, __, bl, hl, hl, sq, sq, tr);
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,
sq, sq, vl, __, __, __, __, __, sq, sq, vl, __, bl, hl, br, __, sq, sq, vl, sq, sq, sq, sq, sq, tl, br);
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,
bl, hl, br, __, __, __, __, __, bl, hl, br, __, __, __, __, __, bl, hl, br, bl, hl, hl, hl, hl, br, __);
break;
}
case ANSI: {
PrintAndLogEx(NORMAL, " " _CYAN_("8888888b. 888b d888 .d8888b. "));
PrintAndLogEx(NORMAL, " " _CYAN_("888 Y88b 8888b d8888 d88P Y88b "));
PrintAndLogEx(NORMAL, " " _CYAN_("888 888 88888b.d88888 .d88P "));
PrintAndLogEx(NORMAL, " " _CYAN_("888 d88P 888Y88888P888 8888\" "));
PrintAndLogEx(NORMAL, " " _CYAN_("8888888P\" 888 Y888P 888 \"Y8b. "));
PrintAndLogEx(NORMAL, " " _CYAN_("888 888 Y8P 888 888 888 "));
PrintAndLogEx(NORMAL, " " _CYAN_("888 888 \" 888 Y88b d88P") " " BANNERMSG1);
PrintAndLogEx(NORMAL, " " _CYAN_("888 888 888 \"Y8888P\"") " " BANNERMSG2);
break;
}
case ASCII: {
PrintAndLogEx(NORMAL, " 8888888b. 888b d888 .d8888b. ");
PrintAndLogEx(NORMAL, " 888 Y88b 8888b d8888 d88P Y88b ");
PrintAndLogEx(NORMAL, " 888 888 88888b.d88888 .d88P ");
PrintAndLogEx(NORMAL, " 888 d88P 888Y88888P888 8888\" ");
PrintAndLogEx(NORMAL, " 8888888P\" 888 Y888P 888 \"Y8b. ");
PrintAndLogEx(NORMAL, " 888 888 Y8P 888 888 888 ");
PrintAndLogEx(NORMAL, " 888 888 \" 888 Y88b d88P " BANNERMSG1);
PrintAndLogEx(NORMAL, " 888 888 888 \"Y8888P\" " BANNERMSG2);
break;
}
}
PrintAndLogEx(NORMAL, "");
PrintAndLogEx(NORMAL, BANNERMSG3);
}
static uint8_t detect_current_lang(void) {
#ifndef _WIN32
const char *lang = setlocale(LC_ALL, "");
if (lang == NULL) {
return 1;
}
if (memcmp(lang, "fr", 2) == 0) {
return 2;
}
if (memcmp(lang, "es", 2) == 0) {
return 3;
}
#endif
return 1;
}
static const char *get_quote(void) {
const char *quotes_en[] = {
"E Pluribus Unum",
"Carpe Diem",
"Ad astra per aspera",
"Fortes fortuna adiuvat",
"Non ducor, duco",
"Veni, vidi, vici",
"Audentes fortuna iuvat",
"Virtus in actione consistit",
"Dum spiro, spero",
"Non scholae, sed vitae discimus",
"Faber est suae quisque fortunae"
};
const char *quotes_fr[] = {
"Liberté, égalité, fraternité",
"L'avenir appartient à ceux qui croient à la beauté de leurs rêves",
"Rien n'est impossible",
"La vie est un défi, relève-le!",
"Qui ne tente rien n'a rien",
"Le succès est la somme de petits efforts, répétés jour après jour",
"Faites de votre vie un rêve, et d'un rêve, une réalité",
"La seule façon de faire du bon travail est d'aimer ce que vous faites",
"Tout ce que vous pouvez faire, faites-le",
"Le succès, c'est d'aller d'échec en échec sans perdre son enthousiasme",
"Crois en toi et tout deviendra possible",
"C'est en tombant qu'on apprend à se relever"
};
const char *quotes_es[] = {
"El éxito es la suma de pequeños esfuerzos repetidos día tras día",
"Hazlo con pasión o no lo hagas",
"Nunca dejes de soñar",
"El único modo de hacer un gran trabajo es amar lo que haces",
"No hay que ir para atrás ni para darse impulso",
"Cada logro comienza con la decisión de intentarlo",
"No importa lo lento que vayas, siempre y cuando no te detengas",
"La disciplina es el puente entre las metas y los logros",
"Si puedes soñarlo, puedes lograrlo",
"La vida es una aventura, atrévete",
};
int r = 0;
srand((uint32_t)time(NULL));
uint8_t lang = detect_current_lang();
switch (lang) {
case 2: {
r = rand() % ARRAYLEN(quotes_fr);
return quotes_fr[r];
}
case 3: {
r = rand() % ARRAYLEN(quotes_es);
return quotes_es[r];
}
case 1:
default: {
r = rand() % ARRAYLEN(quotes_en);
return quotes_en[r];
}
}
}
static void showBanner(void) {
uint8_t old_printAndLog = g_printAndLog;
g_printAndLog &= PRINTANDLOG_PRINT;
PrintAndLogEx(NORMAL, "\n");
#if defined(_WIN32)
if (GetConsoleCP() == 65001) {
// If on Windows and using UTF-8 then we need utf-8 ascii art for banner.
showBanner_logo(UTF8);
} else {
showBanner_logo(ANSI);
}
#elif defined(__linux__) || defined(__APPLE__)
showBanner_logo(ANSI);
#else
showBanner_logo(ASCII);
#endif
PrintAndLogEx(NORMAL, " [ " _YELLOW_("%s!")" :coffee: ]", get_quote());
// PrintAndLogEx(NORMAL, " [ https://patreon.com/iceman1001/ ]");
// PrintAndLogEx(NORMAL, "");
// PrintAndLogEx(NORMAL, " Monero");
// PrintAndLogEx(NORMAL, " 43mNJLpgBVaTvyZmX9ajcohpvVkaRy1kbZPm8tqAb7itZgfuYecgkRF36rXrKFUkwEGeZedPsASRxgv4HPBHvJwyJdyvQuP");
PrintAndLogEx(NORMAL, "");
fflush(stdout);
g_printAndLog = old_printAndLog;
}
#endif //LIBPM3
static const char *prompt_dev = "";
static const char *prompt_ctx = "";
static const char *prompt_net = "";
static void prompt_set(void) {
if (g_session.pm3_present) {
switch (g_conn.send_via_ip) {
case PM3_TCPv4:
prompt_net = PROXPROMPT_NET_TCPV4;
break;
case PM3_TCPv6:
prompt_net = PROXPROMPT_NET_TCPV6;
break;
case PM3_UDPv4:
prompt_net = PROXPROMPT_NET_UDPV4;
break;
case PM3_UDPv6:
prompt_net = PROXPROMPT_NET_UDPV6;
break;
case PM3_NONE:
prompt_net = PROXPROMPT_NET_NONE;
break;
default:
break;
}
if (g_conn.send_via_fpc_usart)
prompt_dev = PROXPROMPT_DEV_FPC;
else
prompt_dev = PROXPROMPT_DEV_USB;
} else {
prompt_dev = PROXPROMPT_DEV_OFFLINE;
}
}
static void prompt_compose(char *buf, size_t buflen, const char *promptctx, const char *promptdev, const char *promptnet, bool no_newline) {
if (no_newline) {
snprintf(buf, buflen - 1, PROXPROMPT_COMPOSE, promptdev, promptnet, promptctx);
} else {
snprintf(buf, buflen - 1, "\33[2K\r" PROXPROMPT_COMPOSE, promptdev, promptnet, promptctx);
}
}
static bool c_update_reconnect_prompt = false;
// This function is hooked via RL_EVENT_HOOK.
static int check_comm(void) {
// If communications thread goes down. Device disconnected then this should hook up PM3 again.
if (IsCommunicationThreadDead() && g_session.pm3_present) {
#ifndef HAVE_READLINE
PrintAndLogEx(INFO, _YELLOW_("OFFLINE") " mode. Use "_YELLOW_("\"hw connect\"") " to reconnect\n");
#endif
prompt_dev = PROXPROMPT_DEV_OFFLINE;
char prompt[PROXPROMPT_MAX_SIZE] = {0};
prompt_compose(prompt, sizeof(prompt), prompt_ctx, prompt_dev, prompt_net, false);
char prompt_filtered[PROXPROMPT_MAX_SIZE] = {0};
memcpy_filter_ansi(prompt_filtered, prompt, sizeof(prompt_filtered), !g_session.supports_colors);
pm3line_update_prompt(prompt_filtered);
CloseProxmark(g_session.current_device);
StartReconnectProxmark();
c_update_reconnect_prompt = true;
}
// its alive again
if (c_update_reconnect_prompt && IsReconnectedOk() && g_session.pm3_present) {
prompt_set();
char prompt[PROXPROMPT_MAX_SIZE] = {0};
prompt_compose(prompt, sizeof(prompt), prompt_ctx, prompt_dev, prompt_net, false);
char prompt_filtered[PROXPROMPT_MAX_SIZE] = {0};
memcpy_filter_ansi(prompt_filtered, prompt, sizeof(prompt_filtered), !g_session.supports_colors);
pm3line_update_prompt(prompt_filtered);
c_update_reconnect_prompt = false;
}
msleep(50);
return 0;
}
#if defined(_WIN32)
static bool DetectWindowsAnsiSupport(void) {
#ifndef ENABLE_VIRTUAL_TERMINAL_PROCESSING
#define ENABLE_VIRTUAL_TERMINAL_PROCESSING 0x0004
#endif
// disable colors if stdin or stdout are redirected
if ((! g_session.stdinOnTTY) || (! g_session.stdoutOnTTY))
return false;
HANDLE hOut = GetStdHandle(STD_OUTPUT_HANDLE);
DWORD dwMode = 0;
GetConsoleMode(hOut, &dwMode);
//ENABLE_VIRTUAL_TERMINAL_PROCESSING is already set
if ((dwMode & ENABLE_VIRTUAL_TERMINAL_PROCESSING))
return true;
dwMode |= ENABLE_VIRTUAL_TERMINAL_PROCESSING;
return SetConsoleMode(hOut, dwMode) ? true : false;
}
#endif //_WIN32
// first slot is always NULL, indicating absence of script when idx=0
static FILE *cmdscriptfile[MAX_NESTED_CMDSCRIPT + 1] = {0};
static uint8_t cmdscriptfile_idx = 0;
static bool cmdscriptfile_stayafter = false;
int push_cmdscriptfile(char *path, bool stayafter) {
if (cmdscriptfile_idx == MAX_NESTED_CMDSCRIPT) {
PrintAndLogEx(ERR, "Too many nested scripts, skipping %s\n", path);
return PM3_EMALLOC;
}
FILE *f = fopen(path, "r");
if (f == NULL)
return PM3_EFILE;
if (cmdscriptfile_idx == 0)
cmdscriptfile_stayafter = stayafter;
cmdscriptfile[++cmdscriptfile_idx] = f;
return PM3_SUCCESS;
}
static FILE *current_cmdscriptfile(void) {
return cmdscriptfile[cmdscriptfile_idx];
}
static bool pop_cmdscriptfile(void) {
fclose(cmdscriptfile[cmdscriptfile_idx]);
cmdscriptfile[cmdscriptfile_idx--] = NULL;
if (cmdscriptfile_idx == 0)
return cmdscriptfile_stayafter;
else
return true;
}
// Main thread of PM3 Client
void
#ifdef __has_attribute
#if __has_attribute(force_align_arg_pointer)
__attribute__((force_align_arg_pointer))
#endif
#endif
main_loop(const char *script_cmds_file, char *script_cmd, bool stayInCommandLoop) {
char *cmd = NULL;
bool execCommand = (script_cmd != NULL);
bool fromInteractive = false;
uint16_t script_cmd_len = 0;
if (execCommand) {
script_cmd_len = strlen(script_cmd);
str_creplace(script_cmd, script_cmd_len, ';', '\0');
}
bool stdinOnPipe = !isatty(STDIN_FILENO);
char script_cmd_buf[256] = {0x00}; // iceman, needs lua script the same file_path_buffer as the rest
// cache Version information now:
if (execCommand || script_cmds_file || stdinOnPipe)
pm3_version(false, false);
else
pm3_version_short();
if (script_cmds_file) {
char *path;
int res = searchFile(&path, CMD_SCRIPTS_SUBDIR, script_cmds_file, ".cmd", false);
if (res == PM3_SUCCESS) {
if (push_cmdscriptfile(path, stayInCommandLoop) == PM3_SUCCESS)
PrintAndLogEx(SUCCESS, "executing commands from file: %s\n", path);
else
PrintAndLogEx(ERR, "could not open " _YELLOW_("%s") "...", path);
free(path);
}
}
g_session.history_path = NULL;
if (g_session.incognito) {
PrintAndLogEx(INFO, "No history will be recorded");
} else {
if (searchHomeFilePath(&g_session.history_path, NULL, PROXHISTORY, true) != PM3_SUCCESS) {
g_session.history_path = NULL;
PrintAndLogEx(ERR, "No history will be recorded");
} else {
if (pm3line_load_history(g_session.history_path) != PM3_SUCCESS) {
PrintAndLogEx(INFO, "No previous history could be loaded");
}
}
}
// loops every time enter is pressed...
while (1) {
bool printprompt = false;
prompt_set();
check_script:
// If there is a script file
if (current_cmdscriptfile()) {
// clear array
memset(script_cmd_buf, 0, sizeof(script_cmd_buf));
// read script file
if (fgets(script_cmd_buf, sizeof(script_cmd_buf), current_cmdscriptfile()) == NULL) {
if (pop_cmdscriptfile() == false) {
break;
}
goto check_script;
}
prompt_ctx = PROXPROMPT_CTX_SCRIPTFILE;
// remove linebreaks
str_cleanrn(script_cmd_buf, sizeof(script_cmd_buf));
cmd = str_dup(script_cmd_buf);
if (cmd != NULL) {
printprompt = true;
}
} else {
// If there is a script command
if (execCommand) {
prompt_ctx = stdinOnPipe ? PROXPROMPT_CTX_STDIN : PROXPROMPT_CTX_SCRIPTCMD;
cmd = str_dup(script_cmd);
if ((cmd != NULL) && (! fromInteractive))
printprompt = true;
uint16_t len = strlen(script_cmd) + 1;
script_cmd += len;
if (script_cmd_len == len - 1)
execCommand = false;
script_cmd_len -= len;
} else {
// exit after exec command
if (script_cmd && !stayInCommandLoop)
break;
// if there is a pipe from stdin
if (stdinOnPipe) {
// clear array
memset(script_cmd_buf, 0, sizeof(script_cmd_buf));
// get
if (fgets(script_cmd_buf, sizeof(script_cmd_buf), stdin) == NULL) {
PrintAndLogEx(ERR, "STDIN unexpected end, exit...");
break;
}
execCommand = true;
stayInCommandLoop = true;
fromInteractive = false;
script_cmd = script_cmd_buf;
script_cmd_len = strlen(script_cmd);
str_creplace(script_cmd, script_cmd_len, ';', '\0');
// remove linebreaks
str_cleanrn(script_cmd, script_cmd_len);
goto check_script;
} else {
pm3line_check(check_comm);
prompt_ctx = PROXPROMPT_CTX_INTERACTIVE;
char prompt[PROXPROMPT_MAX_SIZE] = {0};
prompt_compose(prompt, sizeof(prompt), prompt_ctx, prompt_dev, prompt_net, true);
char prompt_filtered[PROXPROMPT_MAX_SIZE] = {0};
memcpy_filter_ansi(prompt_filtered, prompt, sizeof(prompt_filtered), !g_session.supports_colors);
g_pendingPrompt = true;
script_cmd = pm3line_read(prompt_filtered);
#if defined(_WIN32)
//Check if color support needs to be enabled again in case the window buffer did change
g_session.supports_colors = DetectWindowsAnsiSupport();
#endif
if (script_cmd != NULL) {
execCommand = true;
stayInCommandLoop = true;
fromInteractive = true;
script_cmd_len = strlen(script_cmd);
str_creplace(script_cmd, script_cmd_len, ';', '\0');
// remove linebreaks
str_cleanrn(script_cmd, script_cmd_len);
goto check_script;
}
fflush(NULL);
}
}
}
// execute command
if (cmd) {
// rtrim
size_t l = strlen(cmd);
while (l > 0 && isspace(cmd[l - 1])) {
cmd[--l] = '\0';
}
// ltrim
size_t off = 0;
while ((cmd[off] != '\0') && isspace(cmd[off])) {
off++;
}
for (size_t i = 0; i < strlen(cmd) - off; i++) {
cmd[i] = cmd[i + off];
}
cmd[strlen(cmd) - off] = '\0';
if (cmd[0] != '\0') {
uint8_t old_printAndLog = g_printAndLog;
if (!printprompt) {
g_printAndLog &= ~PRINTANDLOG_PRINT;
}
char prompt[PROXPROMPT_MAX_SIZE] = {0};
prompt_compose(prompt, sizeof(prompt), prompt_ctx, prompt_dev, prompt_net, true);
// always filter RL magic separators if not using readline
char prompt_filtered[PROXPROMPT_MAX_SIZE] = {0};
memcpy_filter_rlmarkers(prompt_filtered, prompt, sizeof(prompt_filtered));
PrintAndLogEx(NORMAL, "%s%s", prompt_filtered, cmd);
g_printAndLog = old_printAndLog;
// add to history if not from a script
if (!current_cmdscriptfile()) {
pm3line_add_history(cmd);
}
// process cmd
g_pendingPrompt = false;
mainret = CommandReceived(cmd);
// exit or quit
if (mainret == PM3_EFATAL)
break;
if (mainret == PM3_SQUIT) {
// Normal quit, map to 0
mainret = PM3_SUCCESS;
break;
}
}
free(cmd);
cmd = NULL;
} else {
PrintAndLogEx(NORMAL, "\n");
if (script_cmds_file && stayInCommandLoop)
stayInCommandLoop = false;
else
break;
}
} // end while
if (g_session.pm3_present) {
clearCommandBuffer();
SendCommandNG(CMD_QUIT_SESSION, NULL, 0);
msleep(100); // Make sure command is sent before killing client
}
while (current_cmdscriptfile()) {
pop_cmdscriptfile();
}
pm3line_flush_history();
if (cmd) {
free(cmd);
cmd = NULL;
}
}
#ifndef LIBPM3
static void dumpAllHelp(int markdown, bool full_help) {
g_session.help_dump_mode = true;
PrintAndLogEx(NORMAL, "\n%sProxmark3 command dump%s\n\n", markdown ? "# " : "", markdown ? "" : "\n======================");
PrintAndLogEx(NORMAL, "Some commands are available only if a Proxmark3 is actually connected.%s\n", markdown ? " " : "");
PrintAndLogEx(NORMAL, "Check column \"offline\" for their availability.\n");
PrintAndLogEx(NORMAL, "\n");
command_t *cmds = getTopLevelCommandTable();
dumpCommandsRecursive(cmds, markdown, full_help);
g_session.help_dump_mode = false;
PrintAndLogEx(NORMAL, "Full help dump done.");
}
#endif //LIBPM3
static char *my_executable_path = NULL;
static char *my_executable_directory = NULL;
const char *get_my_executable_path(void) {
return my_executable_path;
}
const char *get_my_executable_directory(void) {
return my_executable_directory;
}
static void set_my_executable_path(void) {
int path_length = wai_getExecutablePath(NULL, 0, NULL);
if (path_length == -1)
return;
my_executable_path = (char *)calloc(path_length + 1, sizeof(uint8_t));
int dirname_length = 0;
if (wai_getExecutablePath(my_executable_path, path_length, &dirname_length) != -1) {
my_executable_path[path_length] = '\0';
my_executable_directory = (char *)calloc(dirname_length + 2, sizeof(uint8_t));
strncpy(my_executable_directory, my_executable_path, dirname_length + 1);
my_executable_directory[dirname_length + 1] = '\0';
}
}
static const char *my_user_directory = NULL;
// static char _cwd_Buffer [FILENAME_MAX] = {0};
const char *get_my_user_directory(void) {
return my_user_directory;
}
static void set_my_user_directory(void) {
/* my_user_directory = getenv("HOME");
// if not found, default to current directory
if (my_user_directory == NULL) {
my_user_directory = GetCurrentDir(_cwd_Buffer, sizeof(_cwd_Buffer));
// change all slashes to / (windows should not care...
for (int i = 0; i < strlen(_cwd_Buffer); i++)
if (_cwd_Buffer[i] == '\\') _cwd_Buffer[i] = '/';
// my_user_directory = ".";
}
*/
my_user_directory = getenv("HOME");
// if not found, default to current directory
if (my_user_directory == NULL) {
uint16_t pathLen = FILENAME_MAX; // should be a good starting point
char *cwd_buffer = (char *)calloc(pathLen, sizeof(uint8_t));
if (cwd_buffer == NULL) {
PrintAndLogEx(WARNING, "Failed to allocate memory");
return;
}
while (GetCurrentDir(cwd_buffer, pathLen) == NULL) {
if (errno == ERANGE) { // Need bigger buffer
pathLen += 10; // if buffer was too small add 10 characters and try again
char *tmp = realloc(cwd_buffer, pathLen);
if (tmp == NULL) {
PrintAndLogEx(WARNING, "Failed to allocate memory");
free(cwd_buffer);
return;
}
cwd_buffer = tmp;
} else {
free(cwd_buffer);
return;
}
}
for (int i = 0; i < strlen(cwd_buffer); i++) {
if (cwd_buffer[i] == '\\') {
cwd_buffer[i] = '/';
}
}
my_user_directory = cwd_buffer;
}
}
#ifndef LIBPM3
static void show_help(bool showFullHelp, char *exec_name) {
PrintAndLogEx(NORMAL, "\nsyntax: %s [-h|-t|-m|--fulltext]", exec_name);
#ifdef HAVE_PYTHON
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);
#else // HAVE_PYTHON
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);
#endif // HAVE_PYTHON
PrintAndLogEx(NORMAL, " %s [-p] <port> --flash [--unlock-bootloader] [--image <imagefile>]+ [-w] [-f] [-d <0|1|2>]", exec_name);
if (showFullHelp) {
PrintAndLogEx(NORMAL, "\nCommon options:");
PrintAndLogEx(NORMAL, " -h/--help this help");
PrintAndLogEx(NORMAL, " -v/--version print client version");
PrintAndLogEx(NORMAL, " -p/--port serial port to connect to");
PrintAndLogEx(NORMAL, " -w/--wait 20sec waiting the serial port to appear in the OS");
PrintAndLogEx(NORMAL, " -f/--flush output will be flushed after every print");
PrintAndLogEx(NORMAL, " -d/--debug <0|1|2> set debugmode");
PrintAndLogEx(NORMAL, "\nOptions in client mode:");
PrintAndLogEx(NORMAL, " -t/--text dump all interactive command list at once");
PrintAndLogEx(NORMAL, " --fulltext dump all interactive command's help at once");
PrintAndLogEx(NORMAL, " -m/--markdown dump all interactive command list at once in markdown syntax");
PrintAndLogEx(NORMAL, " -b/--baud serial port speed (only needed for physical UART, not for USB-CDC or BT)");
PrintAndLogEx(NORMAL, " -c/--command <command> execute one Proxmark3 command (or several separated by ';').");
PrintAndLogEx(NORMAL, " -l/--lua <lua_script_file> execute Lua script.");
#ifdef HAVE_PYTHON
// Technically, --lua and --py are identical and interexchangeable
PrintAndLogEx(NORMAL, " -y/--py <python_script_file> execute Python script.");
#endif // HAVE_PYTHON
PrintAndLogEx(NORMAL, " -s/--script-file <cmd_script_file> script file with one Proxmark3 command per line");
PrintAndLogEx(NORMAL, " -i/--interactive enter interactive mode after executing the script or the command");
PrintAndLogEx(NORMAL, " --incognito do not use history, prefs file nor log files");
PrintAndLogEx(NORMAL, " --ncpu <num_cores> override number of CPU cores");
PrintAndLogEx(NORMAL, "\nOptions in flasher mode:");
PrintAndLogEx(NORMAL, " --flash flash Proxmark3, requires at least one --image");
PrintAndLogEx(NORMAL, " --reboot-to-bootloader reboot Proxmark3 into bootloader mode");
PrintAndLogEx(NORMAL, " --unlock-bootloader Enable flashing of bootloader area *DANGEROUS* (need --flash)");
PrintAndLogEx(NORMAL, " --force Enable flashing even if firmware seems to not match client version");
PrintAndLogEx(NORMAL, " --image <imagefile> image to flash. Can be specified several times.");
PrintAndLogEx(NORMAL, "\nOptions in memory dump mode:");
PrintAndLogEx(NORMAL, " --dumpmem <dumpfile> dumps Proxmark3 flash memory to file");
PrintAndLogEx(NORMAL, " --dumpaddr <address> starting address for dump, default 0");
PrintAndLogEx(NORMAL, " --dumplen <length> number of bytes to dump, default 512KB");
PrintAndLogEx(NORMAL, " --dumpraw raw address mode: dump from anywhere, not just flash");
PrintAndLogEx(NORMAL, "\nExamples:");
PrintAndLogEx(NORMAL, "\n to run Proxmark3 client:\n");
PrintAndLogEx(NORMAL, " %s "SERIAL_PORT_EXAMPLE_H" -- runs the pm3 client", exec_name);
PrintAndLogEx(NORMAL, " %s "SERIAL_PORT_EXAMPLE_H" -f -- flush output every time", exec_name);
PrintAndLogEx(NORMAL, " %s "SERIAL_PORT_EXAMPLE_H" -w -- wait for serial port", exec_name);
PrintAndLogEx(NORMAL, " %s -- runs the pm3 client in OFFLINE mode", exec_name);
PrintAndLogEx(NORMAL, "\n to execute different commands from terminal:\n");
PrintAndLogEx(NORMAL, " %s "SERIAL_PORT_EXAMPLE_H" -c \"hf mf chk --1k\" -- execute cmd and quit client", exec_name);
PrintAndLogEx(NORMAL, " %s "SERIAL_PORT_EXAMPLE_H" -l hf_read -- execute Lua script `hf_read` and quit client", exec_name);
PrintAndLogEx(NORMAL, " %s "SERIAL_PORT_EXAMPLE_H" -s mycmds.txt -- execute each pm3 cmd in file and quit client", exec_name);
PrintAndLogEx(NORMAL, "\n to flash fullimage and bootloader:\n");
PrintAndLogEx(NORMAL, " %s "SERIAL_PORT_EXAMPLE_H" --flash --unlock-bootloader --image bootrom.elf --image fullimage.elf", exec_name);
#ifdef __linux__
PrintAndLogEx(NORMAL, "\nNote (Linux):\nif the flasher gets stuck in 'Waiting for Proxmark3 to reappear on <DEVICE>',");
PrintAndLogEx(NORMAL, "you need to blacklist Proxmark3 for modem-manager - see documentation for more details:");
PrintAndLogEx(NORMAL, "* https://github.com/RfidResearchGroup/proxmark3/blob/master/doc/md/Installation_Instructions/ModemManager-Must-Be-Discarded.md");
PrintAndLogEx(NORMAL, "\nMore info on flashing procedure from the official Proxmark3 wiki:");
PrintAndLogEx(NORMAL, "* https://github.com/Proxmark/proxmark3/wiki/Gentoo%%20Linux");
PrintAndLogEx(NORMAL, "* https://github.com/Proxmark/proxmark3/wiki/Ubuntu%%20Linux");
PrintAndLogEx(NORMAL, "* https://github.com/Proxmark/proxmark3/wiki/OSX\n");
#endif
}
}
static int dumpmem_to_file(const char *filename, uint32_t addr, uint32_t len, bool raw, bool in_bootloader) {
uint8_t *buffer = calloc(len, sizeof(uint8_t));
if (buffer == NULL) {
PrintAndLogEx(WARNING, "Failed to allocate memory");
return PM3_EMALLOC;
}
int res = PM3_EUNDEF;
size_t readlen = 0;
DeviceMemType_t type = raw ? MCU_MEM : MCU_FLASH;
if (GetFromDevice(type, buffer, len, addr, NULL, 0, NULL, 1000, true)) {
res = PM3_SUCCESS;
readlen = len; // GetFromDevice does not report the actual number of bytes received.
}
if (res == PM3_SUCCESS) {
res = saveFile(filename, ".bin", buffer, readlen);
if (res != PM3_SUCCESS) {
PrintAndLogEx(ERR, "error writing to file "_YELLOW_("%s"), filename);
}
}
free(buffer);
return res;
}
static int dumpmem_pm3(char *serial_port_name, const char *filename, uint32_t addr, uint32_t len, bool raw) {
int ret = PM3_EUNDEF;
bool in_bootloader = false;
if (serial_port_name == NULL) {
PrintAndLogEx(ERR, "You must specify a port.\n");
return PM3_EINVARG;
}
if (OpenProxmark(&g_session.current_device, serial_port_name, true, 60, true, FLASHMODE_SPEED)) {
PrintAndLogEx(NORMAL, _GREEN_(" found"));
msleep(200);
} else {
PrintAndLogEx(ERR, "Could not find Proxmark3 on " _RED_("%s") ".\n", serial_port_name);
ret = PM3_ETIMEOUT;
goto finish;
}
// Determine if we're talking to a bootloader or main firmware.
SendCommandBL(CMD_DEVICE_INFO, 0, 0, 0, NULL, 0);
PacketResponseNG resp;
if (WaitForResponseTimeout(CMD_UNKNOWN, &resp, 1000) == false) {
PrintAndLogEx(ERR, "Could not get device info.");
goto finish2;
}
uint32_t dev_info = resp.oldarg[0];
in_bootloader = (dev_info & DEVICE_INFO_FLAG_CURRENT_MODE_BOOTROM) != 0;
if (in_bootloader) {
if ((dev_info & DEVICE_INFO_FLAG_UNDERSTANDS_READ_MEM) != 0) {
PrintAndLogEx(INFO, "Device is running the bootloader.");
} else {
PrintAndLogEx(ERR, "Device is running the bootloader, but the bootloader"
" doesn't understand the READ MEM command.");
goto finish2;
}
}
PrintAndLogEx(SUCCESS, "Dump requested from address "_YELLOW_("%u")", length "_YELLOW_("%u")"%s.",
addr, len, raw ? ", in raw address mode" : "");
PrintAndLogEx(SUCCESS, _CYAN_("Memory dumping to file..."));
ret = dumpmem_to_file(filename, addr, len, raw, in_bootloader);
if (ret != PM3_SUCCESS) {
goto finish2;
}
PrintAndLogEx(NORMAL, "");
finish2:
clearCommandBuffer();
if (in_bootloader) {
g_session.current_device->g_conn->run = false;
SendCommandOLD(CMD_PING, 0, 0, 0, NULL, 0);
} else {
SendCommandNG(CMD_QUIT_SESSION, NULL, 0);
msleep(100);
}
CloseProxmark(g_session.current_device);
finish:
if (ret == PM3_SUCCESS)
PrintAndLogEx(SUCCESS, _CYAN_("All done"));
else if (ret == PM3_EOPABORTED)
PrintAndLogEx(FAILED, "Aborted by user");
else
PrintAndLogEx(ERR, "Aborted on error %u", ret);
return ret;
}
static int flash_pm3(char *serial_port_name, uint8_t num_files, const char *filenames[FLASH_MAX_FILES], bool can_write_bl, bool force) {
int ret = PM3_EUNDEF;
flash_file_t files[FLASH_MAX_FILES];
memset(files, 0, sizeof(files));
if (serial_port_name == NULL) {
PrintAndLogEx(ERR, "You must specify a port.\n");
return PM3_EINVARG;
}
for (int i = 0 ; i < num_files; ++i) {
char *path;
ret = searchFile(&path, FIRMWARES_SUBDIR, filenames[i], ".elf", true);
if (ret != PM3_SUCCESS) {
ret = searchFile(&path, BOOTROM_SUBDIR, filenames[i], ".elf", true);
}
if (ret != PM3_SUCCESS) {
// Last try, let the error msg be displayed if not found
ret = searchFile(&path, FULLIMAGE_SUBDIR, filenames[i], ".elf", false);
}
if (ret != PM3_SUCCESS) {
goto finish2;
}
files[i].filename = path;
}
PrintAndLogEx(SUCCESS, "About to use the following file%s:", num_files > 1 ? "s" : "");
for (int i = 0 ; i < num_files; ++i) {
PrintAndLogEx(SUCCESS, " "_YELLOW_("%s"), files[i].filename);
}
for (int i = 0 ; i < num_files; ++i) {
ret = flash_load(&files[i], force);
if (ret != PM3_SUCCESS) {
goto finish2;
}
PrintAndLogEx(NORMAL, "");
}
if (OpenProxmark(&g_session.current_device, serial_port_name, true, 60, true, FLASHMODE_SPEED)) {
PrintAndLogEx(NORMAL, _GREEN_(" found"));
msleep(200);
} else {
PrintAndLogEx(ERR, "Could not find Proxmark3 on " _RED_("%s") ".\n", serial_port_name);
ret = PM3_ETIMEOUT;
goto finish2;
}
uint32_t max_allowed = 0;
ret = flash_start_flashing(can_write_bl, serial_port_name, &max_allowed);
if (ret != PM3_SUCCESS) {
goto finish;
}
if (num_files == 0)
goto finish;
for (int i = 0 ; i < num_files; ++i) {
ret = flash_prepare(&files[i], can_write_bl, max_allowed * ONE_KB);
if (ret != PM3_SUCCESS) {
goto finish;
}
PrintAndLogEx(NORMAL, "");
}
PrintAndLogEx(SUCCESS, _CYAN_("Flashing..."));
for (int i = 0; i < num_files; i++) {
ret = flash_write(&files[i]);
if (ret != PM3_SUCCESS) {
goto finish;
}
PrintAndLogEx(NORMAL, "");
}
finish:
if (ret != PM3_SUCCESS)
PrintAndLogEx(WARNING, "The flashing procedure failed, follow the suggested steps!");
ret = flash_stop_flashing();
CloseProxmark(g_session.current_device);
finish2:
for (int i = 0 ; i < num_files; ++i) {
flash_free(&files[i]);
}
if (ret == PM3_SUCCESS)
PrintAndLogEx(SUCCESS, _CYAN_("All done"));
else if (ret == PM3_EOPABORTED)
PrintAndLogEx(FAILED, "Aborted by user");
else
PrintAndLogEx(ERR, "Aborted on error");
PrintAndLogEx(INFO, "\nHave a nice day!");
return ret;
}
static int reboot_bootloader_pm3(char *serial_port_name) {
if (serial_port_name == NULL) {
PrintAndLogEx(ERR, "You must specify a port.\n");
return PM3_EINVARG;
}
if (OpenProxmark(&g_session.current_device, serial_port_name, true, 60, true, FLASHMODE_SPEED) == false) {
PrintAndLogEx(ERR, "Could not find Proxmark3 on " _RED_("%s") ".\n", serial_port_name);
return PM3_ETIMEOUT;
}
PrintAndLogEx(NORMAL, _GREEN_(" found"));
return flash_reboot_bootloader(serial_port_name, true);
}
#endif //LIBPM3
void pm3_init(void) {
srand(time(0));
g_session.pm3_present = false;
g_session.help_dump_mode = false;
g_session.incognito = false;
g_session.supports_colors = false;
g_session.emoji_mode = EMO_ALTTEXT;
g_session.stdinOnTTY = false;
g_session.stdoutOnTTY = false;
// set global variables soon enough to get the log path
set_my_executable_path();
set_my_user_directory();
}
#ifndef LIBPM3
int main(int argc, char *argv[]) {
pm3_init();
bool waitCOMPort = false;
bool addScriptExec = false;
bool stayInCommandLoop = false;
char *script_cmds_file = NULL;
char *script_cmd = NULL;
char *port = NULL;
uint32_t speed = 0;
pm3line_init();
char exec_name[100] = {0};
strncpy(exec_name, basename(argv[0]), sizeof(exec_name) - 1);
bool flash_mode = false;
bool reboot_bootloader_mode = false;
bool flash_can_write_bl = false;
bool flash_force = false;
bool debug_mode_forced = false;
int flash_num_files = 0;
const char *flash_filenames[FLASH_MAX_FILES];
bool dumpmem_mode = false;
const char *dumpmem_filename = NULL;
uint32_t dumpmem_addr = 0;
uint32_t dumpmem_len = 512 * 1024;
bool dumpmem_raw = false;
// color management:
// 1. default = no color
// 2. enable colors if OS seems to support colors and if stdin/stdout aren't redirected
// 3. load prefs if available, overwrite colors choice if needed
// 4. disable colors anyway if stdin/stdout are redirected
//
// For info, grep --color=auto is doing sth like this, plus test getenv("TERM") != "dumb":
// struct stat tmp_stat;
// if ((fstat (STDOUT_FILENO, &tmp_stat) == 0) && (S_ISCHR (tmp_stat.st_mode)) && isatty(STDIN_FILENO))
g_session.stdinOnTTY = isatty(STDIN_FILENO);
g_session.stdoutOnTTY = isatty(STDOUT_FILENO);
g_session.supports_colors = false;
g_session.emoji_mode = EMO_ALTTEXT;
if (g_session.stdinOnTTY && g_session.stdoutOnTTY) {
#if defined(__linux__) || defined(__APPLE__)
g_session.supports_colors = true;
g_session.emoji_mode = EMO_EMOJI;
#elif defined(_WIN32)
g_session.supports_colors = DetectWindowsAnsiSupport();
g_session.emoji_mode = EMO_ALTTEXT;
#endif
}
for (int i = 1; i < argc; i++) {
if (argv[i][0] != '-') {
// For backward compatibility we accept direct port
if (port != NULL) {
// We got already one
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]);
show_help(false, exec_name);
return 1;
}
port = argv[i];
continue;
}
// port
if (strcmp(argv[i], "-p") == 0 || strcmp(argv[i], "--port") == 0) {
if (i + 1 == argc) {
PrintAndLogEx(ERR, _RED_("ERROR:") " missing port specification after -p\n");
show_help(false, exec_name);
return 1;
}
if (port != NULL) {
// We got already one
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]);
show_help(false, exec_name);
return 1;
}
port = argv[++i];
continue;
}
// short help
if (strcmp(argv[i], "-h") == 0 || strcmp(argv[i], "--help") == 0) {
g_printAndLog = PRINTANDLOG_PRINT;
show_help(true, exec_name);
return 0;
}
// dump help
if (strcmp(argv[i], "-t") == 0 || strcmp(argv[i], "--text") == 0) {
g_printAndLog = PRINTANDLOG_PRINT;
show_help(false, exec_name);
dumpAllHelp(0, false);
return 0;
}
// dump help
if (strcmp(argv[i], "--fulltext") == 0) {
g_printAndLog = PRINTANDLOG_PRINT;
show_help(false, exec_name);
dumpAllHelp(0, true);
return 0;
}
// dump markup
if (strcmp(argv[i], "-m") == 0 || strcmp(argv[i], "--markdown") == 0) {
g_printAndLog = PRINTANDLOG_PRINT;
dumpAllHelp(1, false);
return 0;
}
// print client version
if (strcmp(argv[i], "-v") == 0 || strcmp(argv[i], "--version") == 0) {
pm3_version(true, true);
return 0;
}
// set debugmode
if (strcmp(argv[i], "-d") == 0 || strcmp(argv[i], "--debug") == 0) {
if (i + 1 == argc) {
PrintAndLogEx(ERR, _RED_("ERROR:") " missing debugmode specification after -d\n");
show_help(false, exec_name);
return 1;
}
int demod = atoi(argv[i + 1]);
if (demod < 0 || demod > 2) {
PrintAndLogEx(ERR, _RED_("ERROR:") " invalid debugmode: -d " _YELLOW_("%s") "\n", argv[i + 1]);
return 1;
}
g_debugMode = demod;
debug_mode_forced = true;
i++;
continue;
}
// flush output
if (strcmp(argv[i], "-f") == 0 || strcmp(argv[i], "--flush") == 0) {
SetFlushAfterWrite(true);
PrintAndLogEx(INFO, "Output will be flushed after every print.\n");
continue;
}
// set baudrate
if (strcmp(argv[i], "-b") == 0 || strcmp(argv[i], "--baud") == 0) {
if (i + 1 == argc) {
PrintAndLogEx(ERR, _RED_("ERROR:") " missing baud specification after -b\n");
show_help(false, exec_name);
return 1;
}
uint64_t tmpspeed = strtoul(argv[i + 1], NULL, 10);
if ((tmpspeed == ULONG_MAX) || (tmpspeed == 0)) {
PrintAndLogEx(ERR, _RED_("ERROR:") " invalid baudrate: -b " _YELLOW_("%s") "\n", argv[i + 1]);
return 1;
}
speed = tmpspeed;
i++;
continue;
}
// wait for comport
if (strcmp(argv[i], "-w") == 0 || strcmp(argv[i], "--wait") == 0) {
waitCOMPort = true;
continue;
}
// execute pm3 command
if (strcmp(argv[i], "-c") == 0 || strcmp(argv[i], "--command") == 0) {
if (i + 1 == argc) {
PrintAndLogEx(ERR, _RED_("ERROR:") " missing command specification after -c\n");
show_help(false, exec_name);
return 1;
}
script_cmd = argv[++i];
continue;
}
// execute pm3 command file
if (strcmp(argv[i], "-s") == 0 || strcmp(argv[i], "--script-file") == 0) {
if (i + 1 == argc || strlen(argv[i + 1]) == 0) {
PrintAndLogEx(ERR, _RED_("ERROR:") " missing script file specification after -s\n");
show_help(false, exec_name);
return 1;
}
script_cmds_file = argv[++i];
continue;
}
// execute Lua script
if (strcmp(argv[i], "-l") == 0 || strcmp(argv[i], "--lua") == 0) {
if (i + 1 == argc || strlen(argv[i + 1]) == 0) {
PrintAndLogEx(ERR, _RED_("ERROR:") " missing Lua script specification after --lua\n");
show_help(false, exec_name);
return 1;
}
script_cmd = argv[++i];
if (script_cmd == NULL || strlen(script_cmd) == 0) {
return 1;
}
addScriptExec = true;
continue;
}
#ifdef HAVE_PYTHON
// execute Python script
if (strcmp(argv[i], "-y") == 0 || strcmp(argv[i], "--py") == 0) {
if (i + 1 == argc || strlen(argv[i + 1]) == 0) {
PrintAndLogEx(ERR, _RED_("ERROR:") " missing Python script specification after --py\n");
show_help(false, exec_name);
return 1;
}
script_cmd = argv[++i];
if (script_cmd == NULL || strlen(script_cmd) == 0) {
return 1;
}
addScriptExec = true;
continue;
}
#endif // HAVE_PYTHON
// go to interactive instead of quitting after a script/command
if (strcmp(argv[i], "-i") == 0 || strcmp(argv[i], "--interactive") == 0) {
stayInCommandLoop = true;
continue;
}
// do not use history nor log files
if (strcmp(argv[i], "--incognito") == 0) {
g_session.incognito = true;
continue;
}
// go to dump mode
if (strcmp(argv[i], "--dumpmem") == 0) {
dumpmem_mode = true;
if (i + 1 == argc) {
PrintAndLogEx(ERR, _RED_("ERROR:") " missing file specification after --dumpmem\n");
show_help(false, exec_name);
return 1;
}
dumpmem_filename = argv[++i];
continue;
}
if (strcmp(argv[i], "--dumpaddr") == 0) {
if (i + 1 == argc) {
PrintAndLogEx(ERR, _RED_("ERROR:") " missing address specification after -dumpaddr\n");
show_help(false, exec_name);
return 1;
}
uint32_t tmpaddr = strtoul(argv[i + 1], NULL, 0);
dumpmem_addr = tmpaddr;
i++;
continue;
}
if (strcmp(argv[i], "--dumplen") == 0) {
if (i + 1 == argc) {
PrintAndLogEx(ERR, _RED_("ERROR:") " missing address specification after -dumplen\n");
show_help(false, exec_name);
return 1;
}
uint32_t tmplen = strtoul(argv[i + 1], NULL, 0);
dumpmem_len = tmplen;
i++;
continue;
}
if (strcmp(argv[i], "--dumpraw") == 0) {
dumpmem_raw = true;
continue;
}
// go to flash mode
if (strcmp(argv[i], "--flash") == 0) {
flash_mode = true;
continue;
}
// go to flash mode
if (strcmp(argv[i], "--reboot-to-bootloader") == 0) {
reboot_bootloader_mode = true;
continue;
}
// unlock bootloader area
if (strcmp(argv[i], "--unlock-bootloader") == 0) {
flash_can_write_bl = true;
continue;
}
// force flash even if firmware seems to not match client version
if (strcmp(argv[i], "--force") == 0) {
flash_force = true;
continue;
}
// flash file
if (strcmp(argv[i], "--image") == 0) {
if (flash_num_files == FLASH_MAX_FILES) {
PrintAndLogEx(ERR, _RED_("ERROR:") " too many --image, please use it max %i times\n", FLASH_MAX_FILES);
return 1;
}
if (i + 1 == argc) {
PrintAndLogEx(ERR, _RED_("ERROR:") " missing image specification after --image\n");
show_help(false, exec_name);
return 1;
}
flash_filenames[flash_num_files++] = argv[++i];
continue;
}
if (strcmp(argv[i], "--ncpu") == 0) {
if (i + 1 == argc) {
PrintAndLogEx(ERR, _RED_("ERROR:") " missing CPU number specification after --ncpu\n");
show_help(false, exec_name);
return 1;
}
long int ncpus = strtol(argv[i + 1], NULL, 10);
const int detected_cpus = detect_num_CPUs();
if (ncpus < 0 || ncpus >= detected_cpus) {
PrintAndLogEx(ERR, _RED_("ERROR:") " invalid number of CPU cores: --ncpu " _YELLOW_("%s") " (available: %d)\n", argv[i + 1], detected_cpus);
return 1;
}
g_numCPUs = ncpus;
i++;
continue;
}
// We got an unknown parameter
PrintAndLogEx(ERR, _RED_("ERROR:") " invalid parameter: " _YELLOW_("%s") "\n", argv[i]);
show_help(false, exec_name);
return 1;
}
// Load Settings and assign
// This will allow the command line to override the settings.json values
preferences_load();
// quick patch for debug level
if (! debug_mode_forced) {
g_debugMode = g_session.client_debug_level;
}
// settings_save ();
// End Settings
// even if prefs, we disable colors if stdin or stdout is not a TTY
if ((! g_session.stdinOnTTY) || (! g_session.stdoutOnTTY)) {
g_session.supports_colors = false;
g_session.emoji_mode = EMO_ALTTEXT;
}
// Let's take a baudrate ok for real UART, USB-CDC & BT don't use that info anyway
if (speed == 0)
speed = USART_BAUD_RATE;
if (dumpmem_mode) {
dumpmem_pm3(port, dumpmem_filename, dumpmem_addr, dumpmem_len, dumpmem_raw);
exit(EXIT_SUCCESS);
}
if (flash_mode) {
flash_pm3(port, flash_num_files, flash_filenames, flash_can_write_bl, flash_force);
exit(EXIT_SUCCESS);
}
if (reboot_bootloader_mode) {
reboot_bootloader_pm3(port);
exit(EXIT_SUCCESS);
}
if (script_cmd) {
while (script_cmd[strlen(script_cmd) - 1] == ' ')
script_cmd[strlen(script_cmd) - 1] = 0x00;
if (strlen(script_cmd) == 0) {
script_cmd = NULL;
PrintAndLogEx(ERR, _RED_("ERROR:") " execute command: " _YELLOW_("command not found") ".\n");
return 2;
} else {
if (addScriptExec) {
// add "script run " to command
int len = strlen(script_cmd) + 11 + 1;
char *ctmp = (char *) calloc(len, sizeof(uint8_t));
if (ctmp != NULL) {
memset(ctmp, 0, len);
strcpy(ctmp, "script run ");
strcpy(&ctmp[11], script_cmd);
script_cmd = ctmp;
}
}
PrintAndLogEx(SUCCESS, "execute command from commandline: " _YELLOW_("%s") "\n", script_cmd);
}
}
// try to open USB connection to Proxmark
if (port != NULL) {
OpenProxmark(&g_session.current_device, port, waitCOMPort, 20, false, speed);
}
if (g_session.pm3_present && (TestProxmark(g_session.current_device) != PM3_SUCCESS)) {
PrintAndLogEx(ERR, _RED_("ERROR:") " cannot communicate with the Proxmark3\n");
CloseProxmark(g_session.current_device);
}
if ((port != NULL) && (!g_session.pm3_present)) {
exit(EXIT_FAILURE);
}
if (!g_session.pm3_present) {
PrintAndLogEx(INFO, _YELLOW_("OFFLINE") " mode. Check " _YELLOW_("\"%s -h\"") " if it's not what you want.\n", exec_name);
}
// ascii art only in interactive client
if (!script_cmds_file && !script_cmd && g_session.stdinOnTTY && g_session.stdoutOnTTY && !dumpmem_mode && !flash_mode && !reboot_bootloader_mode) {
showBanner();
}
// Save settings if not loaded from settings json file.
// Doing this here will ensure other checks and updates are saved to over rule default
// e.g. Linux color use check
if ((!g_session.preferences_loaded) && (!g_session.incognito)) {
PrintAndLogEx(INFO, "Creating initial preferences file"); // json save reports file name, so just info msg here
preferences_save(); // Save defaults
g_session.preferences_loaded = true;
} /* else {
// Set device debug level
PrintAndLogEx(INFO,"setting device debug loglevel");
if (g_session.pm3_present) {
SendCommandNG(CMD_SET_DBGMODE, &g_session.device_debug_level, 1);
PacketResponseNG resp;
if (WaitForResponseTimeout(CMD_SET_DBGMODE, &resp, 2000) == false)
PrintAndLogEx (INFO,"failed to set device debug loglevel");
}
else
PrintAndLogEx(WARNING,"Proxmark3 not ready to set debug level");
}
*/
#ifdef HAVE_GUI
# if defined(_WIN32)
InitGraphics(argc, argv, script_cmds_file, script_cmd, stayInCommandLoop);
MainGraphics();
# else
// for *nix distro's, check environment variable to verify a display
const char *display = getenv("DISPLAY");
if (display && strlen(display) > 1) {
InitGraphics(argc, argv, script_cmds_file, script_cmd, stayInCommandLoop);
MainGraphics();
} else {
main_loop(script_cmds_file, script_cmd, stayInCommandLoop);
}
# endif
#else
main_loop(script_cmds_file, script_cmd, stayInCommandLoop);
#endif
// Clean up the port
if (g_session.pm3_present) {
CloseProxmark(g_session.current_device);
}
// Plot/Overlay moved or resized
if (g_session.window_changed) {
preferences_save();
}
free_grabber();
return mainret;
}
#endif //LIBPM3