/* radare - LGPL - Copyright 2009-2024 - nibble, pancake */ #define INTERACTIVE_MAX_REP 1024 #include #include #include #if R2__UNIX__ #include #ifndef __wasi__ #include #endif #endif #define SPECIAL_CHARS "@;~$#|`\"'()<>" static const char help_message[] = \ "\nWelcome to radare2!\n" \ "\n" \ "* Type `?` for the root list of commands. \n" \ "* Append the `?` to any command to list the sub-commands.\n" \ "* Prefix the command with `'` to avoid evaluating special chars\n" \ "* The `@` modifier can be used for temporal seeks\n" \ "* The `~` represents the internal grep. System pipes also work `|`.\n" \ "* Multiple commands can be chained with `;`.\n" \ "* Run external scripts with the `.` source command (r2, r2js, python, ..) \n" \ "* Use the `?*~...` command to inspect all the commands in visual mode\n" \ "\n" \ "Use the `e` command to change the configuration options.\n" \ "* Run `edit` to tweak your ~/.radare2rc script\n" \ "\n" \ "Basic commands:\n" \ "\n" \ "* s [addr] - seek to a different address\n" \ "* px, pd - print hexadecimal, disassembly (pdf/pdr the whole function)\n" \ "* wx, wa - write hexpairs, write assembly (w - write string)\n" \ "* aaa, af - analyze the whole program or function\n" \ "* /, /x - search for strings or hexadecimal patterns\n" \ "* f~... - search for strings or hexadecimal patterns\n" \ "* q - quit (alias for ^D or exit)\n"; static bool isAnExport(RBinSymbol *s) { /* workaround for some bin plugs */ if (s->is_imported) { return false; } return (s->bind && !strcmp (s->bind, R_BIN_BIND_GLOBAL_STR)); } static int r_core_cmd_subst_i(RCore *core, char *cmd, char* colon, bool *tmpseek); static int bb_cmpaddr(const void *_a, const void *_b) { const RAnalBlock *a = _a; const RAnalBlock *b = _b; return a->addr > b->addr ? 1 : (a->addr < b->addr ? -1 : 0); } static void cmd_debug_reg(RCore *core, const char *str); R_VEC_TYPE(RVecAnalRef, RAnalRef); #define R_INCLUDE_BEGIN 1 #include "cmd_quit.inc.c" #include "cmd_hash.inc.c" #include "cmd_debug.inc.c" #include "cmd_log.inc.c" #include "cmd_flag.inc.c" #include "cmd_zign.inc.c" #include "cmd_project.inc.c" #include "cmd_write.inc.c" #include "cmd_cmp.inc.c" #include "cmd_eval.inc.c" #include "cmd_type.inc.c" #include "cmd_anal.inc.c" #include "cmd_open.inc.c" #include "cmd_meta.inc.c" #include "cmd_egg.inc.c" #include "cmd_info.inc.c" #include "cmd_macro.inc.c" #include "cmd_magic.inc.c" #include "cmd_mount.inc.c" #include "cmd_seek.inc.c" #include "cmd_search.inc.c" // defines incDigitBuffer... used by cmd_print #include "cmd_print.inc.c" #include "cmd_help.inc.c" #undef R_INCLUDE_BEGIN static const RCoreHelpMessage help_msg_dollar = { "Usage:", "$alias[=cmd] [args...]", "Alias commands and data (See ?$? for help on $variables)", "$", "", "list all defined aliases", "$*", "", "list all defined aliases and their values, with unprintable characters escaped", "$**", "", "same as above, but if an alias contains unprintable characters, b64 encode it", "$", "foo:=123", "alias for 'f foo=123'", "$", "foo-=4", "alias for 'f foo-=4'", "$", "foo+=4", "alias for 'f foo+=4'", "$", "foo", "alias for 's foo' (note that command aliases can override flag resolution)", "$", "dis=base64:AAA=", "alias $dis to the raw bytes from decoding this base64 string", "$", "dis=$hello world", "alias $dis to the string after '$'", "$", "dis=$hello\\\\nworld\\\\0a", "string aliases accept double-backslash and hex escaping", "$", "dis=-", "edit $dis in cfg.editor (use single-backslashes for escaping)", "$", "dis=af", "alias $dis to the af command", "\"$", "dis=af;pdf\"", "alias $dis to run af, then pdf. you must quote the whole command.", "$", "test=. /tmp/test.js", "create command - rlangpipe script", "$", "dis=", "undefine alias", "$", "dis", "execute a defined command alias, or print a data alias with unprintable characters escaped", "$", "dis?", "show commands aliased by $dis", NULL }; static const RCoreHelpMessage help_msg_l = { "Usage:", "l[erls] [arg]", "Internal less (~..) and file listing (!ls)", "lu", " [path]", "same as #!lua", "ll", " [path]", "same as ls -l", "lr", " [path]", "same as ls -r", "li", "", "list source of current function (like gdb's 'list' command)", "ls", " [-e,-l,-j,-q] [path]", "list files in current or given directory", "ls", " -e [path]", "list files using emojis", "ls", " -l [path]", "same as ll (list files with details)", "ls", " -j [path]", "list files in json format", "ls", " -q [path]", "quiet output (one file per line)", "le", "[ss] [path]", "same as cat file~.. (or less)", NULL }; static const RCoreHelpMessage help_msg_quote = { "Usage:", "\"[\"..|..\"]", "quote the command to avoid evaluating special characters", "\"?", "", "show this help, NOTE that a single quote is simpler and works the same", "\"", "?e hello \\\"world\\\"\"", "print (hello \"world\")", "\"", "?e x;y\";\"?e y;x\"", "run two commands (prints x;y\ny;x)", "\"\"", "[cmd]", "directly call a command ignoring all special chars (fast)", "\"\"@addr\"\"", "[cmd]", "call a command with a temporal seek (EXPERIMENTAL)", "\"\"?e x;y\";\"?e y;x", "", "run two commands ignoring special chars (prints x;y\";\"?e y;x) ", NULL }; static const RCoreHelpMessage help_msg_plus = { "Usage:", "+", "seek forward, same as s+X (see s? and -? for more help)", "+", "8", "seek 8 bytes forward, same as s+8", "++", "", "seek one block forward. Same as s++ (see `b` command)", NULL }; static const RCoreHelpMessage help_msg_j = { "Usage:", "j[:o]in", "run command with json facilities or join two files", "j:", "?e", "run '?e' command and show the result stats in json", "ji:", "[cmd]", "run command and indent it as json like (cmd~{})", "jq", " [...]", "same as !jq", "js", " [expr]", "run given javascript expression ('expr' can start with base64:)", "js-", "", "read from stdin until ^D", "js!", "", "reset js vm (same as #!!)", "js:", "", "enter the interactive repl with autocompletion and colors", "js:", "[file]", "interpret javascript file", "join", " f1 f2", "join the contents of two files", NULL }; static const RCoreHelpMessage help_msg_dash = { "Usage:", "-", "open editor and run the r2 commands in the saved document", "", "'-' '.-' '. -'", " those three commands do the same", "-", "8", "same as s-8, but shorter to type (see +? command)", "-a", " x86", "same as r2 -a x86 or e asm.arch=x86", "-b", " 32", "same as e or r2 -e", "-c", " cpu", "same as r2 -e asm.cpu=", "-e", " k=v", "same as r2 -b or e asm.bits", "-h", "", "show this help (same as -?)", "-H", " key", "same as r2 -H", "-k", " kernel", "same as r2 -k or e asm.os", "-f", "", "block size = file size (b $s)", "-j", "", "enter the js: repl", "-i", " [file]", "same as . [file], to run a script", "-s", " [addr]", "same as r2 -e asm.cpu=", "-L", "", "same as Lo (or r2 -L)", "-P", " project", "same as 'P [prjname]' to load a project", "-v", "", "same as -V", "-V", "", "show r2 version, same as ?V", "--", "", "seek one block backward. Same as s-- (see `b` command)", NULL }; static const RCoreHelpMessage help_msg_star = { "Usage:", "*[=[0x]value]", "Pointer read/write data/values", "*", "entry0=cc", "write trap in entrypoint", "*", "entry0+10=0x804800", "write value in delta address", "*", "entry0", "read byte at given address", "*", "/", "end multiline comment. (use '/*' to start mulitiline comment", NULL }; static const RCoreHelpMessage help_msg_comma = { "Usage:", ",[,.-/*jhr] [file]", "# load table data", ",", "", "display table", ", ", "[table-query]", "filter and print table. See ,? for more details", ",.", " file.csv", "load table from CSV file (comma dot)", ",,", "", "print table in csv format (comma comma)", ",-", "", "reset table", ",/", "?", "query/filter current table (non-destructive)", ",*", ">$foo", "print table as r2 commands", ",j", "", "print table in json format", ",h", " xxd foo bar cow", "define header column names and types", ",r", " 1 2 foo", "adds a row using the given format string", NULL }; static const RCoreHelpMessage help_msg_dot = { "Usage:", ".[r2cmd] | [file] | [!command] | [(macro)]", "# define macro or interpret r2, r_lang,\n" " cparse, d, es6, exe, go, js, lsp, pl, py, rb, sh, vala or zig file", ".", "", "repeat last command backward", ".", "C*", "run 'C*' command and interpret the printed commands", "..", "123", "alias for s..123 (notice the lack of space)", "..", " [file]", "run the output of the execution of a script as r2 commands", "...", "", "repeat last command forward (same as \\n)", // ".:", "8080", "listen for commands on given tcp port", ".--", "", "terminate tcp server for remote commands", ".", " foo.r2", "interpret script", ".", " foo.py", "also works for running r2pipe and rlang scripts", ".-", "", "open cfg.editor and interpret tmp file", ".*", " file ...", "same as #!pipe open cfg.editor and interpret tmp file", ".!", "rabin -ri $FILE", "interpret output of command", ".", "(foo 1 2 3)", "run macro 'foo' with args 1, 2, 3", "./", " ELF", "interpret output of command /m ELF as r. commands", NULL }; static const RCoreHelpMessage help_msg_equal = { "Usage:", " =[:!+-=ghH] [...]", " # connect with other instances of r2", "\nremote commands:", "", "", "=", "", "list all open connections", "=<", "[fd] cmd", "send output of local command to remote fd", // XXX may not be a special char "=", "[fd] cmd", "exec cmd at remote 'fd' (last open is default one)", "=!", " cmd", "run command via r_io_system", "=+", " [proto://]host:port", "connect to remote host:port (*rap://, raps://, tcp://, udp://, http://)", "=-", "[fd]", "remove all hosts or host 'fd'", "==", "[fd]", "open remote session with host 'fd', 'q' to quit", "=!=", "", "disable remote cmd mode", "!=!", "", "enable remote cmd mode", "\nservers:", "", "", // ".:", "9000", "start the tcp server (echo x|nc ::1 9090 or curl ::1:9090/cmd/x)", "=t", "port", "start the tcp server (echo x|nc ::1 9090 or curl ::1:9090/cmd/x)", "=r", "port", "start the rap server (o rap://9999)", "=g", "[?]", "start the gdbserver", "=h", "[?]", "start the http webserver", "=H", "[?]", "start the http webserver (and launch the web browser)", "\nother:", "", "", "=&", ":port", "start rap server in background (same as '&_=h')", "=", ":host:port cmd", "run 'cmd' command on remote server", "\nexamples:", "", "", "=+", "tcp://localhost:9090/", "connect to: r2 -c.:9090 ./bin", // "=+", "udp://localhost:9090/", "connect to: r2 -c.:9090 ./bin", "=+", "rap://localhost:9090/", "connect to: r2 rap://:9090", "=+", "http://localhost:9090/cmd/", "connect to: r2 -c'=h 9090' bin", "o ", "rap://:9090/", "start the rap server on tcp port 9090", NULL }; static const RCoreHelpMessage help_msg_equalh = { "Usage:", " =[hH] [...]", " # http server", "http server:", "", "", "=h", " port", "listen for http connections (r2 -qc=H /bin/ls)", "=h-", "", "stop background webserver", "=h--", "", "stop foreground webserver", "=h*", "", "restart current webserver", "=h&", " port", "start http server in background", "=H", " port", "launch browser and listen for http", "=H&", " port", "launch browser and listen for http in background", NULL }; static const RCoreHelpMessage help_msg_equal_equal = { "Usage:", " ==[=] ", "# add connection to remote r2", "==", "[fd]", "shell to send to the nth remote (see '=1 x' / '==1'", "===", "event", "returns socket file or udp port to read events from", NULL }; static const RCoreHelpMessage help_msg_equal_more = { "Usage:", " =+ [proto://][host]:[port](/[path])", " # add connection to remote r2", "=+", "tcp://localhost:9090", "communicates with another instance running '& .:9090'", "=+", "http://localhost:9090/cmd", "talks to remote r2 webserver '& =h'", "=+", "rap://localhost:9090/cmd", "talks to remote r2 webserver 'r2 rap://:9090'", NULL }; static const RCoreHelpMessage help_msg_equalg = { "Usage:", " =[g] [...]", " # gdb server", "gdbserver:", "", "", "=g", " port file [args]", "listen on 'port' debugging 'file' using gdbserver", NULL }; static const RCoreHelpMessage help_msg_b = { "Usage:", "b[f] [arg]", "change working block size", "b", " 32", "set block size to 33", "b", "=32", "same as 'b 32'", "b", " eip+4", "numeric argument can be an expression", "b", "", "display current block size", "b", "+3", "increase blocksize by 3", "b", "-16", "decrease blocksize by 16", "b*", "", "display current block size in r2 command", "b64:", "AA=", "receive a base64 string that is executed without evaluating special chars", "bf", " foo", "set block size to flag size", "bj", "", "display block size information in JSON", "bm", " 1M", "set max block size", NULL }; static const RCoreHelpMessage help_msg_k = { "Usage:", "k[s] [key[=value]]", "Sdb Query", "k", " anal/**", "list namespaces under anal", "k", " anal/meta/*", "list kv from anal > meta namespaces", "k", " anal/meta/meta.0x80404", "get value for meta.0x80404 key", "k", " foo", "show value", "k", " foo=bar", "set value", "k", "", "list keys", "kd", " [file.sdb] [ns]", "dump namespace to disk", "kj", "", "List all namespaces and sdb databases in JSON format", "ko", " [file.sdb] [ns]", "open file into namespace", "ks", " [ns]", "enter the sdb query shell", //"kl", " ha.sdb", "load keyvalue from ha.sdb", //"ks", " ha.sdb", "save keyvalue to ha.sdb", NULL, }; static const RCoreHelpMessage help_msg_r = { "Usage:", "r[+-][ size]", "Resize file", "r", "", "display file size", "rj", "", "display the file size in JSON format", "r", " size", "expand or truncate file to given size", "r-", "num", "remove num bytes, move following data down", "r+", "num", "insert num bytes, move following data up", "r2pm", " [...]", "run r2pm's main", "rabin2", " [...]", "run rabin2's main", "radare2", " [...]", "run radare2's main", "radiff2", " [...]", "run radiff2's main", "rafind2", " [...]", "run rafind2's main", "rahash2", " [...]", "run rahash2's main", "rasm2", " [...]", "run rasm2's main", "ravc2", " [...]", "run ravc2's main", "rax2", " [...]", "run rax2's main", "rb", " oldbase @ newbase", "rebase all flags, bin.info, breakpoints and analysis", "rm" ," [file]", "remove file", "rmrf", " [file|dir]", "recursive remove", "rh" ,"", "show size in human format", "r2" ," [file]", "launch r2 (same for rax2, rasm2, ...)", "reset" ,"", "reset console settings (clear --hard)", NULL }; static const RCoreHelpMessage help_msg_u = { "Usage:", "u", "uname or undo write/seek", "u", "", "show system uname (alias for uname)", "uw", "", "alias for wc (requires: e io.cache=true)", "us", "", "alias for s- (seek history)", "uc", "[?]", "undo core commands (uc?, ucl, uc*, ..) (see `e cmd.undo`)", "uid", "", "display numeric user id", "uniq", "", "filter rows to avoid duplicates", "uname", "[?]", "uname - show system information", NULL }; static const RCoreHelpMessage help_msg_uname = { "Usage:", "uname", "show information about the current system", "uname", "", "show host operating system", "uname", " -a", "show more system details", "uname", " -j", "show uname information in JSON", "uname", " -b", "show machine cpu register bits size", "uname", " -m", "show machine cpu architecture name", "uname", " -r", "show operating system version", NULL }; static const RCoreHelpMessage help_msg_uc = { "Usage:", "uc [cmd],[revert-cmd]", "undo core commands (see `e cmd.undo`)", "uc", " w hello,w world", "add a new undo command manually", "uc", "", "list all core undos commands", "uc*", "", "list all core undos as r2 commands", "ucu", "", "up : undo previous action", "ucd", "", "down : redo action", "uc-", "", "undo last action", "uc.", "", "list all reverts in current", NULL }; static const RCoreHelpMessage help_msg_y = { "Usage:", "y[fptxy] [len] [[@]addr]", " # See wd? for memcpy, same as 'yf'.", "y!", "", "open cfg.editor to edit the clipboard", "y", " 16 0x200", "copy 16 bytes into clipboard from 0x200", "y", " 16 @ 0x200", "copy 16 bytes into clipboard from 0x200", "y", " 16", "copy 16 bytes into clipboard", "y", "", "show yank buffer information (origin len bytes)", "y*", "", "print in r2 commands what's been yanked", "y-", "", "empty / reset clipboard", "y8", "", "print contents of clipboard in hexpairs", "yf", " [L] [O] [file]", "copy [L] bytes from offset [O] of [file] into clipboard", "yfa", " [filepath]", "copy all bytes from file into clipboard", "yfx", " 10203040", "yank from hexpairs (same as ywx)", "yj", "", "print in JSON commands what's been yanked", "yp", "", "print contents of clipboard", "ys", "", "print contents of clipboard as string", "yt", " 64 0x200", "copy 64 bytes from current seek to 0x200", "ytf", " file", "dump the clipboard to given file", "yw", " hello world", "yank from string", "ywx", " 10203040", "yank from hexpairs (same as yfx)", "yx", "", "print contents of clipboard in hexadecimal", "yy", " 0x3344", "paste contents of clipboard to 0x3344", "yy", " @ 0x3344", "paste contents of clipboard to 0x3344", "yy", "", "paste contents of clipboard at current seek", "yz", " [len]", "copy nul-terminated string (up to blocksize) into clipboard", NULL }; static const RCoreHelpMessage help_msg_triple_exclamation = { "Usage:", "!!![-*][cmd] [arg|$type...]", " # user-defined autocompletion for commands", "!!!", "", "list all autocompletions", "!!!?", "", "show this help", "!!!", "-*", "remove all user-defined autocompletions", "!!!", "-\\*", "remove autocompletions matching this glob expression", "!!!", "-foo", "remove autocompletion named 'foo'", "!!!", "foo", "add 'foo' for autocompletion", "!!!", "bar $flag", "add 'bar' for autocompletion with $flag as argument", "Types:", "", "", NULL }; static const RCoreHelpMessage help_msg_vertical_bar = { "Usage:", "[cmd] | [program|H|T|.|]", "", "|", " [program]", "pipe output of command to program", "|", "", "disable scr.html and scr.color", "|.", "", "alias for .[cmd]", "|?", "", "show this help", "|H", "", "enable scr.html, respect scr.color", "|J", "", "same as j:cmd", "|E", "", "base64 encode the output of the command", "|D", "", "decode the output of the command as base64", "|T", "", "use scr.tts to speak out the stdout", NULL }; static const RCoreHelpMessage help_msg_v = { "Usage:", "v[*i]", "", "v", "", "open visual panels", "v", " test", "load saved layout with name test", "ve", " [fg] [bg]", "define foreground and background for current panel", "v.", " [file]", "load visual script (also known as slides)", "v=", " test", "save current layout with name test", "vi", " test", "open the file test in 'cfg.editor'", NULL }; R_API void r_core_cmd_help(const RCore *core, RCoreHelpMessage help) { r_cons_cmd_help (help, core->print->flags & R_PRINT_FLAGS_COLOR); } R_API void r_core_cmd_help_match(const RCore *core, RCoreHelpMessage help, R_BORROW R_NONNULL char *cmd) { r_cons_cmd_help_match (help, core->print->flags & R_PRINT_FLAGS_COLOR, cmd, 0, true); } R_API void r_core_cmd_help_contains(const RCore *core, RCoreHelpMessage help, R_BORROW R_NONNULL char *cmd) { r_cons_cmd_help_match (help, core->print->flags & R_PRINT_FLAGS_COLOR, cmd, 0, false); } R_API void r_core_cmd_help_match_spec(const RCore *core, RCoreHelpMessage help, R_BORROW R_NONNULL char *cmd, char spec) { r_cons_cmd_help_match (help, core->print->flags & R_PRINT_FLAGS_COLOR, cmd, spec, true); } R_API void r_core_cmd_help_contains_spec(const RCore *core, RCoreHelpMessage help, R_BORROW R_NONNULL char *cmd, char spec) { r_cons_cmd_help_match (help, core->print->flags & R_PRINT_FLAGS_COLOR, cmd, spec, false); } struct duplicate_flag_t { RList *ret; const char *word; }; #if !R2_USE_NEW_ABI extern int Gload_index; static void __line_hist_list(bool full) { RLineHistory *hist = &r_cons_singleton()->line->history; if (hist && hist->data) { int i = full? 0: Gload_index; for (; i < hist->size && hist->data[i]; i++) { const char *pad = r_str_pad (' ', 32 - strlen (hist->data[i])); r_cons_printf ("%s %s # !%d\n", hist->data[i], pad, i); } } } #endif static bool duplicate_flag(RFlagItem *flag, void *u) { struct duplicate_flag_t *user = (struct duplicate_flag_t *)u; /* filter per flag spaces */ bool valid = strchr (user->word, '*') ? r_str_glob (flag->name, user->word) : strstr (flag->name, user->word) != NULL; if (valid) { RFlagItem *cloned_item = r_flag_item_clone (flag); if (!cloned_item) { return false; } r_list_append (user->ret, cloned_item); } return true; } static bool foreach_newline(RCore *core) { bool nl = r_config_get_b (core->config, "scr.loopnl"); if (nl) { r_cons_newline (); } return !r_cons_is_breaked (); } static void recursive_help(RCore *core, int detail, const char *cmd_prefix) { if (R_STR_ISEMPTY (cmd_prefix)) { recursive_help (core, detail, "%"); recursive_help (core, detail, "("); recursive_help (core, detail, "@"); recursive_help (core, detail, "!"); recursive_help (core, detail, "="); recursive_help (core, detail, "??"); recursive_help (core, detail, "~"); } if (strchr (cmd_prefix, '[')) { R_LOG_WARN ("Invalid char in command, help message must be fixed: %s", cmd_prefix); return; } R_LOG_DEBUG ("[recursive help] %s", cmd_prefix); char *s = r_core_cmd_strf (core, "%s?", cmd_prefix); if (R_STR_ISEMPTY (s)) { free (s); return; } RList *pending = r_list_newf (free); r_cons_print (s); RList *rows = r_str_split_list (s, "\n", 0); RListIter *iter; char *row; r_list_foreach (rows, iter, row) { if (strstr (row, "Usage:")) { continue; } char *ch = strstr (row, "[?]"); if (ch) { *ch = 0; char *sp = strchr (row, ' '); if (sp) { bool key_found = false; char *k = strdup (sp + 1); r_str_ansi_filter (k, NULL, NULL, -1); if (strcmp (cmd_prefix, k)) { if (!r_list_find (pending, k, (RListComparator)strcmp)) { r_list_append (pending, k); key_found = true; } } if (!key_found) { free (k); } } } } r_list_free (rows); free (s); r_list_foreach (pending, iter, row) { recursive_help (core, detail, row); } r_list_free (pending); } static bool lastcmd_repeat(RCore *core, int next) { int res = -1; // Fix for backtickbug px`~` if (!core->lastcmd || core->cur_cmd_depth < 1) { return false; } switch (*core->lastcmd) { case '.': if (core->lastcmd[1] == '(') { // macro call res = r_core_cmd0 (core, core->lastcmd); } break; case 'd': // debug res = r_core_cmd0 (core, core->lastcmd); switch (core->lastcmd[1]) { case 's': case 'c': r_core_cmd0 (core, "sr PC;pd 1"); } break; case 'p': // print case 'x': case '$': if (!strncmp (core->lastcmd, "pd", 2)) { if (core->lastcmd[2]== ' ') { r_core_cmd_callf (core, "so %s", r_str_trim_head_ro (core->lastcmd + 3)); } else { r_core_cmd0 (core, "so `pi~?`"); } } else { if (next) { r_core_seek (core, core->offset + core->blocksize, true); } else { if (core->blocksize > core->offset) { r_core_seek (core, 0, true); } else { r_core_seek (core, core->offset - core->blocksize, true); } } } res = r_core_cmd0 (core, core->lastcmd); break; } return res != -1; } static int r_core_cmd_nullcallback(void *data) { RCore *core = (RCore*) data; if (core->cons->context->breaked) { core->cons->context->breaked = false; return 0; } if (!core->cmdrepeat) { return 0; } lastcmd_repeat (core, true); return 1; } /* Escape raw bytes if not using b64 */ static bool print_aliases(void *use_b64, const void *key, const void *val) { const char *k = (char *) key; RCmdAliasVal *v = (RCmdAliasVal *) val; bool base64 = *(bool *)use_b64; if (v->is_str) { r_cons_printf ("$%s=%s\n", k, (char *)v->data); } else { char *val_str = base64 ? r_cmd_alias_val_strdup_b64 (v) : r_cmd_alias_val_strdup (v); r_cons_printf ("$%s=%s%s\n", k, base64? "base64:": "", val_str); free (val_str); } return true; } static int cmd_uname(void *data, const char *input) { // "uniq" RCore *core = (RCore *)(data); if (strstr (input, "-h") || strstr (input, "?")) { r_core_cmd_help (data, help_msg_uname); return 0; } RSysInfo *si = r_sys_info (); if (si) { if (strstr (input, "-a")) { r_cons_printf ("%s %s %s-%d", si->sysname, si->release, R_SYS_ARCH, (R_SYS_BITS & R_SYS_BITS_64)? 64: 32); } else if (strstr (input, "-j")) { PJ *pj = r_core_pj_new (core); pj_o (pj); pj_ks (pj, "platform", si->sysname); pj_ks (pj, "arch", R_SYS_ARCH); pj_kn (pj, "bits", (R_SYS_BITS & R_SYS_BITS_64)? 64: 32); pj_end (pj); char *s = pj_drain (pj); r_cons_printf ("%s", s); free (s); } else if (strstr (input, "-m")) { r_cons_printf ("%s", R_SYS_ARCH); } else if (strstr (input, "-b")) { r_cons_printf ("%d", (R_SYS_BITS & R_SYS_BITS_64)? 64: 32); } else { r_cons_printf ("%s", si->sysname); if (strstr (input, "-r")) { r_cons_printf (" %s", si->release); } } r_cons_newline (); r_sys_info_free (si); } return 0; } static int cmd_uniq(void *data, const char *input) { // "uniq" RCore *core = (RCore *)data; const char *arg = strchr (input, ' '); if (arg) { arg = r_str_trim_head_ro (arg + 1); } switch (*input) { case '?': // "uniq?" r_core_cmd_help_match (core, help_msg_u, "uniq"); break; default: // "uniq" if (!arg) { arg = ""; } if (r_fs_check (core->fs, arg)) { r_core_cmd_callf (core, "md %s", arg); } else { char *res = r_syscmd_uniq (arg); if (res) { r_cons_print (res); free (res); } } break; } return 0; } static int cmd_undo(void *data, const char *input) { RCore *core = (RCore *)data; switch (input[0]) { case 'c': // "uc" switch (input[1]) { case ' ': { char *cmd = r_str_trim_dup (input + 2); char *rcmd = strchr (cmd, ','); if (rcmd) { *rcmd++ = 0; RCoreUndo *undo = r_core_undo_new (core->offset, cmd, rcmd); r_core_undo_push (core, undo); } else { r_core_cmd_help_match (core, help_msg_uc, "uc"); } free (cmd); } break; case '?': // "uc?" r_core_cmd_help (core, help_msg_uc); break; case '.': { // "uc." RCoreUndoCondition cond = { .addr = core->offset, .minstamp = 0, .glob = NULL }; r_core_undo_print (core, 1, &cond); break; } case '*': // "uc*" r_core_undo_print (core, 1, NULL); break; case '-': // "uc-" r_core_undo_pop (core); break; case 'u': // "ucu" r_core_undo_up (core); break; case 'd': // "ucd" r_config_set_b (core->config, "cmd.undo", false); r_core_undo_down (core); r_config_set_b (core->config, "cmd.undo", true); break; default: r_core_undo_print (core, 0, NULL); break; } return 1; case 'i': // "ui" r_cons_printf ("%d\n", r_sys_uid ()); return 1; case 's': // "us" r_core_cmdf (data, "s-%s", input + 1); return 1; case 'w': // "uw" r_core_cmdf (data, "wc%s", input + 1); return 1; case 0: case ' ': (void)cmd_uname (core, input); return 1; case 'n': // "un" if (input[1] == 'a') { // "uname" (void)cmd_uname (core, input); } else if (input[1] == 'i' && input[2] == 'q') { (void)cmd_uniq (core, input); } else { r_core_cmd_help (data, help_msg_uname); } return 1; default: case '?': // "u?" if (*input && input[1] == 'j') { r_cons_cmd_help_json (help_msg_u); } else { r_core_cmd_help (data, help_msg_u); } return 1; } return 0; } static int cmd_alias(void *data, const char *input) { RCore *core = (RCore *)data; if (*input == '?') { r_core_cmd_help (core, help_msg_dollar); return 0; } char *buf = strdup (input); if (!buf) { return 0; } char *q = strchr (buf, ' '); char *def = strchr (buf, '='); char *desc = strchr (buf, '?'); if (buf == def) { R_LOG_ERROR ("No alias name given"); free (buf); return 0; } int defmode = 0; if (def && def > buf) { char *prev = def - 1; switch (*prev) { case ':': defmode = *prev; *prev = 0; break; case '+': defmode = *prev; *prev = 0; break; case '-': defmode = *prev; *prev = 0; break; } } /* create alias */ if ((def && q && (def < q)) || (def && !q)) { *def++ = 0; size_t len = strlen (def); if (defmode) { ut64 at = r_num_math (core->num, def); switch (defmode) { case ':': r_flag_set (core->flags, buf, at, 1); return 1; case '+': at = r_num_get (core->num, buf) + at; r_flag_set (core->flags, buf, at, 1); return 1; case '-': at = r_num_get (core->num, buf) - at; r_flag_set (core->flags, buf, at, 1); return 1; } } /* Remove quotes */ if (len > 0 && (def[0] == '\'') && (def[len - 1] == '\'')) { def[len - 1] = 0x00; def++; } r_str_arg_unescape (def); if (!q || (q && q > def)) { if (*def) { if (!strcmp (def, "-")) { RCmdAliasVal *v = r_cmd_alias_get (core->rcmd, buf); char *n; if (v) { char *v_str = r_cmd_alias_val_strdup (v); n = r_cons_editor (NULL, v_str); free (v_str); } else { n = r_cons_editor (NULL, NULL); } if (n) { int l = r_str_unescape (n); r_cmd_alias_set_raw (core->rcmd, buf, (ut8 *)n, l); free (n); } } else if (*def == '$') { char *s = strdup (def + 1); int l = r_str_unescape (s); r_cmd_alias_set_raw (core->rcmd, buf, (ut8 *)s, l); free (s); } else if (!strncmp (def, "base64:", 7)) { int b64_len = strlen (def + 7); if (b64_len > 0 && b64_len % 4 == 0) { /* b64 decode result is always shorter * than strlen() of input */ ut8* decoded = malloc (b64_len); if (decoded) { int decoded_sz = r_base64_decode (decoded, def+7, b64_len); if (decoded_sz > 0) { r_cmd_alias_set_raw (core->rcmd, buf, decoded, decoded_sz); } else { R_LOG_ERROR ("Invalid base64 string"); } free (decoded); } } else { R_LOG_ERROR ("Invalid base64 string"); } } else { r_cmd_alias_set_cmd (core->rcmd, buf, def); } } else { r_cmd_alias_del (core->rcmd, buf); } } /* Show command for alias */ } else if (desc && !q) { *desc = 0; RCmdAliasVal *v = r_cmd_alias_get (core->rcmd, buf); if (v && !v->is_data) { /* Commands are always strings */ r_cons_println ((char *)v->data); r_cons_flush (); free (buf); return 1; } else if (v) { R_LOG_ERROR ("Alias \"$%s\" is not a command", buf); } else { R_LOG_ERROR ("No such alias \"$%s\"", buf); } } else if (*buf == '*') { bool use_b64 = (buf[1] == '*'); ht_pp_foreach (core->rcmd->aliases, print_aliases, &use_b64); } else if (!*buf) { char **keys = (char **)r_cmd_alias_keys (core->rcmd); if (keys) { int i; const int count = core->rcmd->aliases->count; for (i = 0; i < count; i++) { r_cons_printf ("$%s\n", keys[i]); } free (keys); } } else { /* Execute or evaluate alias */ if (q) { *q = 0; } RCmdAliasVal *v = r_cmd_alias_get (core->rcmd, buf); if (v) { if (v->is_data) { char *v_str = r_cmd_alias_val_strdup (v); if (v_str) { r_cons_print (v_str); r_cons_newline (); free (v_str); } } else if (q) { char *out = r_str_newf ("%s %s", (char *)v->data, q + 1); r_core_cmd0 (core, out); free (out); } else { r_core_cmd0 (core, (char *)v->data); } r_core_return_value (core, 0); } else { R_LOG_ERROR ("No such alias \"$%s\"", buf); r_core_return_value (core, 1); } } free (buf); return 0; } static int getArg(char ch, int def) { switch (ch) { case '&': case '-': return ch; } return def; } static void cmd_remote(RCore *core, const char *input, bool retry) { if (!*input) { return; } if (*input == '?') { r_core_cmd_help_match (core, help_msg_equal, "=r"); return; } char *host = strdup (input); char *port = strchr (host, ':'); if (port) { *port++ = 0; } RSocket *s = r_socket_new (false); repeat: if (r_socket_connect (s, host, port, R_SOCKET_PROTO_TCP, 1500)) { const size_t buf_size = 1024; char *buf = calloc (buf_size, 1); if (!buf) { return; } void *bed = r_cons_sleep_begin (); r_cons_break_push (NULL, NULL); for (;;) { if (r_cons_is_breaked ()) { break; } r_socket_printf (s, "[0x%08"PFMT64x"]> ", core->offset); r_socket_flush (s); memset (buf, 0, buf_size); r_socket_block_time (s, true, 99999, 0); if (r_socket_read (s, (ut8*)buf, buf_size - 1) < 1) { break; } if (*buf == 'q') { break; } const bool orig = r_config_get_b (core->config, "scr.interactive"); r_config_set_b (core->config, "scr.interactive", false); char *res = r_core_cmd_str (core, buf); r_config_set_b (core->config, "scr.interactive", orig); r_socket_printf (s, "%s\n", res); r_socket_flush (s); free (res); } r_cons_break_pop (); r_cons_sleep_end (bed); free (buf); } else { if (retry) { r_sys_sleep (1); goto repeat; } } r_socket_close (s); r_socket_free (s); free (host); } static void cmd_tcp_server(RCore *core, const char *input) { char *ptr; if ((ptr = strchr (input, ' '))) { /* .:port cmd */ /* .:host:port cmd */ const char *host, *port; char *cmd = ptr + 1; *ptr = 0; char *eol = strchr (input, ':'); if (eol) { *eol = 0; host = input; port = eol + 1; } else { host = "localhost"; port = input + ((input[0] == ':')? 1: 0); } char *rbuf = r_core_rtr_cmds_query (core, host, port, cmd); if (rbuf) { r_cons_print (rbuf); free (rbuf); } } else { r_core_rtr_cmds (core, input); } } static int cmd_rap(void *data, const char *input) { RCore *core = (RCore *)data; switch (*input) { case '\0': // "=" r_core_rtr_list (core); break; case 't': // "=t" // tcp cmd_tcp_server (core, r_str_trim_head_ro (input + 1)); break; case 'r': // "=r" cmd_remote (core, r_str_trim_head_ro (input + 1), false); break; case 'R': // "=R" cmd_remote (core, r_str_trim_head_ro (input + 1), true); break; case 'j': // "=j" R_LOG_ERROR ("TODO: list connections in json"); break; case '!': // "=!" if (input[1] == 'q') { R_FREE (core->cmdremote); } else if (input[1] == '=') { // =!= or =!= for iosystem R_FREE (core->cmdremote); core->cmdremote = r_str_trim_dup (input + 2); } else { char *res = r_io_system (core->io, input + 1); if (res) { r_cons_printf ("%s\n", res); free (res); } } break; case '+': // "=+" if (input[1] && input[1] != '?') { r_core_rtr_add (core, input + 1); } else { r_core_cmd_help (core, help_msg_equal_more); } break; case '-': // "=-" r_core_rtr_remove (core, input + 1); break; //case ':': r_core_rtr_cmds (core, input + 1); break; case '<': // "=<" r_core_rtr_pushout (core, input + 1); break; case '=': // "==" if (input[1] == '=') { // === r_core_rtr_event (core, input + 2); } else if (input[1] != '?') { r_core_rtr_session (core, input + 1); } else { r_core_cmd_help (core, help_msg_equal_equal); } break; case 'g': // "=g" if (input[1] == '?') { r_core_cmd_help (core, help_msg_equalg); } else { r_core_rtr_gdb (core, getArg (input[1], 'g'), input + 1); } break; case 'h': // "=h" if (input[1] == '?') { r_core_cmd_help (core, help_msg_equalh); } else { r_core_rtr_http (core, getArg (input[1], 'h'), 'h', input + 1); } break; case 'H': // "=H" if (input[1] == '?') { r_core_cmd_help (core, help_msg_equalh); } else { const char *arg = r_str_trim_head_ro (input + 1); r_core_rtr_http (core, getArg (input[1], 'H'), 'H', arg); } break; case '?': // "=?" r_core_cmd_help (core, help_msg_equal); break; default: r_core_rtr_cmd (core, input); break; } return 0; } static int cmd_iosys(void *data, const char *input) { RCore *core = (RCore *)data; if (input[0] == ':') { char *s = r_core_cmd_str_r (core, r_str_trim_head_ro (input + 1)); if (s) { r_str_trim_tail (s); r_cons_printf ("%s\n", s); free (s); } return 0; } char *res = r_io_system (core->io, input); if (res) { r_str_trim (res); int ret = 0; if (*res) { ret = atoi (res); r_cons_printf ("%s\n", res); } free (res); r_core_return_value (core, ret); return ret; } return false; } static int cmd_yank(void *data, const char *input) { ut64 n; RCore *core = (RCore *)data; switch (input[0]) { case ' ': // "y " { char *args = r_str_trim_dup (input + 1); char *arg = r_str_after (args, ' '); ut64 addr = arg? r_num_math (core->num, arg): core->offset; r_core_yank (core, addr, r_num_math (core->num, args)); free (args); } break; case '-': // "y-" r_core_yank_unset (core); break; case 'l': // "yl" r_core_return_value (core, r_buf_size (core->yank_buf)); break; case 'r': R_LOG_ERROR ("Missing plugin. Run: r2pm -ci r2yara"); r_core_return_code (core, 1); break; case 'y': // "yy" input = r_str_trim_head_ro (input); n = input[1]? r_num_math (core->num, input + 1): core->offset; r_core_yank_paste (core, n, 0); break; case 'x': // "yx" r_core_yank_hexdump (core, r_num_math (core->num, input + 1)); break; case 'z': // "yz" r_core_yank_string (core, core->offset, r_num_math (core->num, input + 1)); break; case 'w': // "yw" ... we have yf which makes more sense than 'w' switch (input[1]) { case ' ': r_core_yank_set (core, 0, (const ut8*)input + 2, strlen (input + 2)); break; case 'x': if (input[2] == ' ') { char *out = strdup (input + 3); int len = r_hex_str2bin (input + 3, (ut8*)out); if (len > 0) { r_core_yank_set (core, core->offset, (const ut8*)out, len); } else { R_LOG_ERROR ("Invalid length"); } free (out); } else { r_core_cmd_help_match (core, help_msg_y, "ywx"); } // r_core_yank_write_hex (core, input + 2); break; default: r_core_cmd_help_match (core, help_msg_y, "ywx"); break; } break; case 'p': // "yp" r_core_yank_cat (core, r_num_math (core->num, input + 1)); break; case 's': // "ys" r_core_yank_cat_string (core, r_num_math (core->num, input + 1)); break; case 't': // "yt" if (input[1] == 'f') { // "ytf" ut64 tmpsz; const char *file = r_str_trim_head_ro (input + 2); const ut8 *tmp = r_buf_data (core->yank_buf, &tmpsz); if (!tmpsz) { R_LOG_ERROR ("No buffer has been yanked"); break; } if (*file == '$') { r_cmd_alias_set_raw (core->rcmd, file+1, tmp, tmpsz); } else if (*file == '?' || !*file) { r_core_cmd_help_match (core, help_msg_y, "ytf"); } else { if (!r_file_dump (file, tmp, tmpsz, false)) { R_LOG_ERROR ("Cannot dump to '%s'", file); } } } else if (input[1] == ' ') { r_core_yank_to (core, input + 1); } else { r_core_cmd_help_contains (core, help_msg_y, "yt"); } break; case 'f': // "yf" switch (input[1]) { case ' ': // "yf" // "yf [filename] [nbytes] [offset]" r_core_yank_file_ex (core, input + 1); break; case 'x': // "yfx" r_core_yank_hexpair (core, input + 2); break; case 'a': // "yfa" r_core_yank_file_all (core, input + 2); break; default: r_core_cmd_help_contains (core, help_msg_y, "yf"); break; } break; case '!': // "y!" { char *sig = r_core_cmd_str (core, "y*"); if (R_STR_ISEMPTY (sig)) { free (sig); sig = strdup ("wx 10203040"); } char *data = r_core_editor (core, NULL, sig); if (data) { char *save_ptr = NULL; (void) r_str_tok_r (data, ";\n", &save_ptr); r_core_cmdf (core, "y%s", data); free (data); } free (sig); } break; case '*': // "y*" case 'j': // "yj" case '8': // "y8" case '\0': // "y" r_core_yank_dump (core, 0, input[0]); break; default: r_core_cmd_help (core, help_msg_y); break; } return true; } static int lang_run_file(RCore *core, RLang *lang, const char *file) { r_core_sysenv_begin (core, NULL); return r_lang_run_file (core->lang, file); } static char *langFromHashbang(RCore *core, const char *file) { int fd = r_sandbox_open (file, O_RDONLY, 0); if (fd != -1) { char firstLine[128] = {0}; int len = r_sandbox_read (fd, (ut8*)firstLine, sizeof (firstLine) - 1); firstLine[len] = 0; if (r_str_startswith (firstLine, "#!/")) { // I CAN HAS A HASHBANG char *nl = strchr (firstLine, '\n'); if (nl) { *nl = 0; } nl = strchr (firstLine, ' '); if (nl) { *nl = 0; } nl = strdup (firstLine + 2); r_sandbox_close (fd); return nl; } r_sandbox_close (fd); } return NULL; } R_API bool r_core_run_script(RCore *core, const char *file) { bool ret = false; RListIter *iter; RLangPlugin *p; char *name; r_list_foreach (core->scriptstack, iter, name) { if (!strcmp (file, name)) { R_LOG_WARN ("ignored nested source: %s", file); return false; } } r_list_push (core->scriptstack, strdup (file)); if (!strcmp (file, "-")) { char *out = r_core_editor (core, NULL, NULL); eprintf ("power %s \n", out); if (out) { ret = r_core_cmd_lines (core, out); free (out); } } else if (r_str_endswith (file, ".pk")) { r_core_cmdf (core, "'poke -f %s", file); ret = true; } else if (r_str_endswith (file, ".html")) { const bool httpSandbox = r_config_get_b (core->config, "http.sandbox"); char *httpIndex = strdup (r_config_get (core->config, "http.index")); r_config_set_b (core->config, "http.sandbox", false); char *absfile = r_file_abspath (file); r_config_set (core->config, "http.index", absfile); free (absfile); r_core_cmdf (core, "=H"); r_config_set_b (core->config, "http.sandbox", httpSandbox); r_config_set (core->config, "http.index", httpIndex); free (httpIndex); ret = true; } else if (r_str_endswith (file, ".c")) { r_core_cmdf (core, "#!c %s", file); ret = true; } else if (r_file_is_c (file)) { // ".h" ".cparse" const char *dir = r_config_get (core->config, "dir.types"); char *out = r_anal_cparse_file (core->anal, file, dir, NULL); if (out) { r_cons_print (out); sdb_query_lines (core->anal->sdb_types, out); free (out); } ret = out; } else { p = r_lang_get_by_extension (core->lang, file); if (p) { r_lang_use (core->lang, p->meta.name); ret = lang_run_file (core, core->lang, file); } else { // XXX this is an ugly hack, we need to use execve here and specify args properly #if R2__WINDOWS__ #define cmdstr(x) r_str_newf (x" %s", file); #else #define cmdstr(x) r_str_newf (x" '%s'", file); #endif const char *ext = r_file_extension (file); if (ext) { /* TODO: handle this inside r_lang_pipe with new APIs */ if (!strcmp (ext, "js")) { if (r_str_endswith (file, ".r2.js")) { if (r_lang_use (core->lang, "qjs")) { ret = r_lang_run_file (core->lang, file); } else { R_LOG_ERROR ("Cannot instantiate the quickjs runtime"); ret = false; } } else { char *cmd = cmdstr ("node"); r_lang_use (core->lang, "pipe"); lang_run_file (core, core->lang, cmd); free (cmd); ret = 1; } } else if (!strcmp (ext, "exe")) { #if R2__WINDOWS__ char *cmd = r_str_newf ("%s", file); #else char *cmd = cmdstr ("wine"); #endif r_lang_use (core->lang, "pipe"); lang_run_file (core, core->lang, cmd); free (cmd); ret = 1; } else if (!strcmp (ext, "rexx")) { r_lang_use (core->lang, "pipe"); char *cmd = cmdstr ("rexx"); lang_run_file (core, core->lang, cmd); free (cmd); ret = 1; } else if (!strcmp (ext, "zig")) { char *cmd = cmdstr ("zig run"); r_lang_use (core->lang, "pipe"); lang_run_file (core, core->lang, cmd); free (cmd); ret = 1; } else if (!strcmp (ext, "d")) { char *cmd = cmdstr ("dmd -run"); r_lang_use (core->lang, "pipe"); lang_run_file (core, core->lang, cmd); free (cmd); ret = 1; } else if (!strcmp (ext, "lsp")) { char *cmd = cmdstr ("newlisp -n"); r_lang_use (core->lang, "pipe"); lang_run_file (core, core->lang, cmd); free (cmd); ret = 1; } else if (!strcmp (ext, "go")) { char *cmd = cmdstr ("go run"); r_lang_use (core->lang, "pipe"); lang_run_file (core, core->lang, cmd); free (cmd); ret = 1; } else if (!strcmp (ext, "es6")) { char *cmd = cmdstr ("babel-node"); r_lang_use (core->lang, "pipe"); lang_run_file (core, core->lang, cmd); free (cmd); ret = 1; } else if (!strcmp (ext, "rb")) { char *cmd = cmdstr ("ruby"); r_lang_use (core->lang, "pipe"); lang_run_file (core, core->lang, cmd); free (cmd); ret = 1; } else if (!strcmp (ext, "nim")) { r_lang_use (core->lang, "nim"); lang_run_file (core, core->lang, file); ret = 1; } else if (!strcmp (ext, "vala")) { r_lang_use (core->lang, "vala"); lang_run_file (core, core->lang, file); ret = 1; } else if (!strcmp (ext, "sh")) { char *shell = r_sys_getenv ("SHELL"); if (!shell) { shell = strdup ("sh"); } if (shell) { r_lang_use (core->lang, "pipe"); char *cmd = r_str_newf ("%s '%s'", shell, file); if (cmd) { lang_run_file (core, core->lang, cmd); free (cmd); } free (shell); } ret = 1; } else if (!strcmp (ext, "r2s")) { r_core_visual_slides (core, file); ret = 1; } else if (!strcmp (ext, "qjs")) { if (r_lang_use (core->lang, "qjs")) { ret = r_lang_run_file (core->lang, file); } else { R_LOG_ERROR ("Cannot instantiate the quickjs runtime"); ret = false; } } else if (!strcmp (ext, "wren")) { if (r_lang_use (core->lang, "wren")) { ret = r_lang_run_file (core->lang, file); } else { R_LOG_ERROR ("r2pm -ci rlang-wren"); ret = false; } } else if (!strcmp (ext, "tiny")) { if (r_lang_use (core->lang, "tiny")) { ret = r_lang_run_file (core->lang, file); } else { R_LOG_ERROR ("r2pm -ci rlang-tiny"); ret = false; } } else if (!strcmp (ext, "pl")) { char *cmd = cmdstr ("perl"); r_lang_use (core->lang, "pipe"); ret = lang_run_file (core, core->lang, cmd); free (cmd); } else if (!strcmp (ext, "py")) { static const char *python_bins[] = { "python3", "python", "python2", NULL }; const char *bin; char *bin_path; int i; for (i = 0; python_bins[i]; i++) { bin = python_bins[i]; bin_path = r_file_path (bin); if (bin_path) { break; } free (bin_path); } if (bin_path) { #if R2__WINDOWS__ char *cmd = r_str_newf ("%s %s", bin_path, file); #else char *cmd = r_str_newf ("%s '%s'", bin_path, file); #endif r_lang_use (core->lang, "pipe"); ret = lang_run_file (core, core->lang, cmd); free (cmd); free (bin_path); } else { R_LOG_ERROR ("Cannot find python in PATH"); ret = false; } } else { if (r_file_is_executable (file)) { r_core_cmdf (core, "#!pipe %s%s", (*file == '/')?"":"./", file); ret = true; } else { ret = r_core_cmd_file (core, file); } } } else { char *lang = langFromHashbang (core, file); if (lang) { r_lang_use (core->lang, "pipe"); char *cmd = r_str_newf ("%s '%s'", lang, file); lang_run_file (core, core->lang, cmd); free (lang); free (cmd); ret = 1; } else { if (r_file_is_executable (file)) { r_core_cmdf (core, "#!pipe %s%s", (*file == '/')?"":"./", file); ret = 1; } } } if (!ret) { ret = r_core_cmd_file (core, file); } } } free (r_list_pop (core->scriptstack)); return ret; } static int cmd_lr(RCore *core, const char *input) { // "lr" const char *path; RListIter *iter; const char *arg = R_STR_ISEMPTY (input)? ".": input; RList *files = r_file_lsrf (arg); if (!files) { R_LOG_ERROR ("Failed to read directories"); return 0; } r_list_sort (files, (RListComparator)strcmp); r_list_foreach (files, iter, path) { r_cons_println (path); } r_list_free (files); return 0; } static int cmd_l(void *data, const char *input) { // "l" RCore *core = (RCore *)data; const char *arg = strchr (input, ' '); if (arg) { arg = r_str_trim_head_ro (arg + 1); } arg = r_str_get (arg); switch (*input) { case 'l': // "ll" if (input[1] == '?') { r_core_cmd_help_match (core, help_msg_l, "ll"); break; } { char *carg = r_str_newf ("-l %s", arg); int w = r_cons_get_size (NULL) - 8; char *res = r_syscmd_ls (carg, w); if (res) { r_cons_print (res); free (res); } free (carg); } break; case 'e': // "le" if (input[1] == '?') { r_core_cmd_help_match (core, help_msg_l, "le"); break; } if (*arg) { r_core_cmdf (core, "cat %s~..", arg); } else { r_core_cmd_help_match (core, help_msg_l, "le"); } break; case 'i': // "li" r_core_cmd0 (core, "CLL@@c:afbo"); break; case 'r': // "lr" if (input[1] == '?') { r_core_cmd_help_match (core, help_msg_l, "lr"); break; } cmd_lr (core, arg); break; case 'u': // "lu"(a) - short for #!lua { const char *arg = strchr (input, ' '); if (arg) { char *cmd = r_str_newf ("#!lua %s", r_str_trim_head_ro (arg + 1)); r_core_cmd0 (core, cmd); free (cmd); } else { r_core_cmd0 (core, "#!lua"); } } break; case 's': // "ls" if (input[1] == '?') { r_core_cmd_help_match (core, help_msg_l, "ls"); break; } if (r_fs_check (core->fs, arg)) { r_core_cmdf (core, "md %s", arg); } else { int w = r_cons_get_size (NULL) - 8; char *res; if (*arg) { //arg++; } res = r_syscmd_ls (arg, w); if (res) { r_cons_print (res); free (res); } } break; case '?': // "l?" default: r_core_cmd_help (core, help_msg_l); break; } return 0; } #include "../../shlr/qjs/js_repl.c" static int cmd_join(void *data, const char *input) { // "join" RCore *core = (RCore *)data; char *tmp = strdup (input); const char *arg1 = strchr (tmp, ' '); if (!arg1) { goto beach; } arg1 = r_str_trim_head_ro (arg1); if (!arg1) { goto beach; } char *end = strchr (arg1, ' '); if (!end) { goto beach; } *end = '\0'; const char *arg2 = end + 1; if (!arg2) { goto beach; } arg2 = r_str_trim_head_ro (arg2); switch (*input) { case '?': // "join?" goto beach; default: // "join" if (!arg1) { arg1 = ""; } if (!arg2) { arg2 = ""; } if (!r_fs_check (core->fs, arg1) && !r_fs_check (core->fs, arg2)) { char *res = r_syscmd_join (arg1, arg2); if (res) { r_cons_print (res); free (res); } } break; } free (tmp); return R_CMD_RC_SUCCESS; beach: r_core_cmd_help (core, help_msg_j); free (tmp); return R_CMD_RC_SUCCESS; } static int cmd_j(void *data, const char *input) { // "j" RCore *core = (RCore *)data; if (r_str_startswith (input, "oin")) { return cmd_join (data, input); } if (r_str_startswith (input, "i:")) { char *res = r_core_cmd_str (core, input + 2); char *indented = r_print_json_indent (res, true, " ", NULL); r_cons_printf ("%s\n", indented); free (indented); free (res); return R_CMD_RC_SUCCESS; } if (input[0] == 'q') { // "jq" r_core_cmd_callf (core, "!jq%s", input + 1); return R_CMD_RC_SUCCESS; } if (input[0] == 's') { // "js" if (input[1] == ':' || input[1] == '.') { // "js:" if (input[2]) { if (r_lang_use (core->lang, "qjs")) { const char *fn = r_str_trim_head_ro (input + 2); if (!r_lang_run_file (core->lang, fn)) { R_LOG_ERROR ("Cannot find %s", fn); } } } else { if (r_config_get_b (core->config, "scr.interactive")) { if (r_lang_use (core->lang, "qjs")) { r_lang_run (core->lang, js_repl_qjs, strlen (js_repl_qjs)); } else { R_LOG_ERROR ("Requires lang.qjs"); } } else { R_LOG_ERROR ("Requires scr.interactive"); } } } else if (input[1] == '!') { // "js!" r_lang_setup (core->lang); } else if (input[1] == '-') { // "js-" if (r_config_get_b (core->config, "scr.interactive")) { int sz; char *data = r_stdin_slurp (&sz); if (data) { char *code = r_str_newf ("(function() { %s })()", data); if (r_lang_use (core->lang, "qjs")) { r_lang_run (core->lang, code, sz); } else { R_LOG_ERROR ("Requires mujs"); } free (code); free (data); } } else { R_LOG_ERROR ("requires scr.interactive"); } } else if (input[1] == ' ') { // "js " if (r_lang_use (core->lang, "qjs")) { const char *arg = r_str_trim_head_ro (input + 1); if (r_str_startswith (arg, "base64:")) { char *script = (char *)r_base64_decode_dyn (arg + 7, -1); if (script) { r_lang_run (core->lang, script, -1); free (script); } } else { r_lang_run (core->lang, input + 1, -1); } } else { r_core_cmdf (core, "#!pipe node -e '%s'", input + 1); } } else { r_core_cmd_help_contains (core, help_msg_j, "js"); } return R_CMD_RC_SUCCESS; } if (input[0] == ':') { PJ *pj = r_core_pj_new (core); // buffer rlog calls into a string char *s = r_core_cmd_str (core, input + 1); pj_o (pj); pj_ks (pj, "command", input + 1); pj_ks (pj, "output", s); pj_ki (pj, "offset", core->offset); pj_ki (pj, "blocksize", core->blocksize); pj_ks (pj, "log", ""); // TODO: use r_log api here pj_ki (pj, "rc", core->rc); // XXX always 0? pj_ki (pj, "value", core->num->value); pj_end (pj); free (s); s = pj_drain (pj); r_cons_printf ("%s\n", s); free (s); return R_CMD_RC_SUCCESS; } return R_CMD_RC_FAILURE; } static int cmd_plus(void *data, const char *input) { RCore *core = (RCore *)data; if (*input) { r_core_cmdf (core, "s+%s", r_str_trim_head_ro (input)); } else { r_core_cmd_help (core, help_msg_plus); } return 0; } static int cmd_stdin(void *data, const char *input) { RCore *core = (RCore *)data; if (*input) { const char *arg = r_str_trim_head_ro (input + 1); switch (*input) { case '?': // "-?" case 'h': // "-h" r_core_cmd_help (core, help_msg_dash); break; case 'v': // "-v" case 'V': // "-V" if (input[1] == 'j') { r_core_cmd_call (core, "?Vj"); } else { r_core_cmd_call (core, "?V"); } break; case 'L': // "-L" if (input[1]) { r_core_cmd_callf (core, "L%c", input[1]); } else { r_core_cmd_call (core, "Lo"); } break; case 'P': // "-P" if (input[1]) { r_core_cmd_callf (core, "P %s", r_str_trim_head_ro (input + 1)); } else { r_core_cmd_call (core, "P"); } break; case 'H': // "-H" r_core_cmd_callf (core, "r2 -H%s", input + 1); break; case 'D': // "-a" r_core_cmd_callf (core, "iD%s", input + 1); break; case 'a': // "-a" if (R_STR_ISEMPTY (arg)) { const char *arch = r_config_get (core->config, "asm.arch"); r_cons_printf ("%s\n", arch); } else { r_config_set (core->config, "asm.arch", arg); r_config_set (core->config, "anal.arch", arg); } break; case 'i': // "-i" r_core_cmdf (core, ". %s", arg); break; case 's': // "-s" r_core_cmdf (core, "'s %s", arg); break; case 'f': // "-f" r_core_cmd0 (core, "b $s"); break; case 'b': // "-b" if (R_STR_ISEMPTY (arg)) { const int bits = r_config_get_i (core->config, "asm.bits"); r_cons_printf ("%d\n", bits); } else { r_config_set_i (core->config, "asm.bits", r_num_math (core->num, arg)); } break; case 'j': // "-j" r_core_cmd_call (core, "js:"); break; case 'c': // "-c" r_core_cmdf (core, "e asm.cpu=%s", arg); break; case 'k': // "-k" if (R_STR_ISEMPTY (arg)) { const char *os = r_config_get (core->config, "asm.os"); r_cons_printf ("%s\n", os); } else { r_config_set (core->config, "asm.os", arg); } break; case 'e': // "-e" if (*arg == '?') { r_core_cmd_call (core, "e"); } else { r_core_cmdf (core, "e %s", arg); } break; default: r_core_cmdf (core, "s-%s", r_str_trim_head_ro (input)); break; } return 0; } return r_core_run_script (core, "-"); } static void load_table_json(RCore *core, RTable *t, char *data) { // parse json file and iterate over all the entries // RTableRow *row = r_table_row_new (items); // r_list_append (t->rows, row); R_LOG_TODO ("Loading tables from JSON is not yet implemented"); } static const char *get_type_string(const char *s) { if (r_str_startswith (s, "0x")) { return "x"; } if (*s == '0' || isdigit (s[1])) { return "d"; } return "s"; } static void load_table_csv(RCore *core, RTable *t, RList *lines) { RListIter *iter; char *line; int row = 0; RList *cols = NULL; r_list_foreach (lines, iter, line) { char *word; RListIter *iter2; RList *words = r_str_split_list (line, ",", 0); if (r_list_length (words) > 0) { switch (row) { case 0: cols = words; words = NULL; break; case 1: { RStrBuf *b = r_strbuf_new (",h "); RStrBuf *args = r_strbuf_new (""); r_list_foreach (words, iter2, word) { const char *type = get_type_string (word); r_strbuf_append (b, type); } r_list_foreach (cols, iter2, word) { r_strbuf_append (b , " "); r_strbuf_append (b, word); } r_core_cmd0 (core, r_strbuf_get (b)); r_strbuf_free (args); r_strbuf_free (b); } /* fallthrough */ default: { RStrBuf *b = r_strbuf_new (",r "); r_list_foreach (words, iter2, word) { r_strbuf_append (b, " "); r_strbuf_append (b, word); } r_core_cmd0 (core, r_strbuf_get (b)); r_strbuf_free (b); } break; } } r_list_free (words); row++; } } static void load_table_asciiart(RCore *core, RTable *t, RList *lines) { RListIter *iter; char *line; const char *separator = "|"; int ncols = 0; bool expect_header = false; bool expect_rows = false; r_list_foreach (lines, iter, line) { if (!expect_rows) { if (r_str_startswith (line, ".--")) { expect_header = true; separator = "|"; continue; } if (r_str_startswith (line, "┌")) { expect_header = true; separator = "│"; continue; } if (r_str_startswith (line, ")-")) { expect_rows = true; separator = "|"; continue; } if (r_str_startswith (line, "│─")) { expect_rows = true; separator = "│"; continue; } } RTableColumnType *typeString = r_table_type ("string"); RTableColumnType *typeNumber = r_table_type ("number"); if (expect_header) { char *arg; RList *args = r_str_split_list (line + strlen (separator), separator, 0); RListIter *iter2; ncols = 0; if (r_list_length (t->cols) > 0) { R_LOG_WARN ("Not re-adding headers. Use ,- to reset the table"); continue; } r_list_foreach (args, iter2, arg) { char *s = strchr (arg, ' '); char *ss = r_str_trim_dup (s? s + 1: arg); if (!*ss) { free (ss); continue; } r_table_add_column (t, typeString, ss, 0); ncols ++; } expect_header = false; } else if (expect_rows) { char *arg; size_t line_len = strlen (line); size_t separator_len = strlen (separator); size_t pos = (line_len < separator_len)? line_len: separator_len; RList *args = r_str_split_list (line + pos, separator, 0); RList *items = r_list_newf (free); RListIter *iter2; if (r_list_length (args) < ncols) { // dowarn? continue; } r_list_foreach (args, iter2, arg) { char *ss = r_str_trim_dup (arg); if (!*ss) { free (ss); continue; } if (isdigit ((unsigned char)*ss)) { int col = r_list_length (items); RTableColumn *c = r_list_get_n (t->cols, col); if (c) { c->type = typeNumber; } } r_list_append (items, ss); } RTableRow *row = r_table_row_new (items); r_list_append (t->rows, row); } } } static void load_table(RCore *core, RTable *t, char *data) { R_RETURN_IF_FAIL (core && t && data); if (*data == '[') { load_table_json (core, t, data); } else { RList *lines = r_str_split_list (data, "\n", 0); if (strchr (data, ',')) { load_table_csv (core, t, lines); } else { load_table_asciiart (core, t, lines); } r_list_free (lines); } free (data); } static void display_table(char *ts) { if (ts) { r_cons_printf ("%s\n", ts); free (ts); } } static void cmd_table_header(RCore *core, char *s) { RList *list = r_str_split_list (s, " ", 0); // owns *s RListIter *iter; char *format = r_list_pop_head (list); if (!format) { return; } if (!core->table) { core->table = r_core_table (core, "header"); } size_t i = 0; r_list_foreach (list, iter, s) { const char type_char = format[i]; if (!type_char) { break; } const char *type_name = (type_char == 's') ? "string": "number"; RTableColumnType *typeString = r_table_type (type_name); r_table_add_column (core->table, typeString, s, 0); i++; } r_list_free (list); free (format); } static bool display_table_filter(RCore *core, const char *input) { R_RETURN_VAL_IF_FAIL (core && input, false); if (!core->table) { return false; } int skip = (*input == ' ')? 1: (*input&&input[1])? 2: 0; if (skip) { const char *q = r_str_trim_head_ro (input + skip); return r_table_query (core->table, q); } return true; } static int cmd_table(void *data, const char *input) { RCore *core = (RCore*)data; if (!core->table) { core->table = r_table_new ("table"); } switch (*input) { case 'h': // table header columns case 'c': // table columns cmd_table_header (core, r_str_trim_dup (input + 1)); break; case 'r': // add row { char *args = r_str_trim_dup (input + 1); if (*args) { RList *list = r_str_split_list (args, " ", 0); if (list) { r_table_add_row_list (core->table, list); } } } break; case '-': r_table_free (core->table); core->table = r_table_new ("table"); break; case '/': // query here { RTable *ot = r_table_clone (core->table); if (display_table_filter (core, input)) { display_table (r_table_tostring (core->table)); } r_table_free (core->table); core->table = ot; } break; case '.': // ",." if (R_STR_ISEMPTY (input + 1)) { r_core_cmd_help_match (core, help_msg_comma, ",."); } else { const char *file = r_str_trim_head_ro (input + 1); if (*file == '$' && !file[1]) { R_LOG_ERROR ("No alias name given"); } else if (*file == '$') { RCmdAliasVal *file_data = r_cmd_alias_get (core->rcmd, file + 1); if (file_data) { char *file_data_str = r_cmd_alias_val_strdup (file_data); load_table (core, core->table, strdup (file_data_str)); free (file_data_str); } else { R_LOG_ERROR ("No such alias '$%s'", file+1); } } else { char *file_data = r_file_slurp (file, NULL); if (file_data) { load_table (core, core->table, file_data); } else { R_LOG_ERROR ("Cannot open file"); } } } break; case ' ': if (display_table_filter (core, input)) { display_table (r_table_tostring (core->table)); } break; case ',': if (display_table_filter (core, input)) { display_table (r_table_tocsv (core->table)); } break; case '*': if (display_table_filter (core, input)) { display_table (r_table_tor2cmds (core->table)); } break; case 'j': if (display_table_filter (core, input)) { display_table (r_table_tojson (core->table)); } break; case 0: if (core->table) { display_table (r_table_tofancystring (core->table)); } break; case '?': r_core_cmd_help (core, help_msg_comma); r_cons_printf ("%s\n", r_table_help ()); break; default: r_core_cmd_help (core, help_msg_comma); break; } return 0; } static int cmd_interpret(void *data, const char *input) { char *str, *ptr, *eol, *filter, *inp; RCore *core = (RCore *)data; if (!strcmp (input, "?")) { r_core_cmd_help (core, help_msg_dot); return 0; } switch (*input) { case '\0': // "." lastcmd_repeat (core, 0); break; #if 0 case ':': // ".:" if ((ptr = strchr (input + 1, ' '))) { /* .:port cmd */ /* .:host:port cmd */ cmd = ptr + 1; *ptr = 0; eol = strchr (input + 1, ':'); if (eol) { *eol = 0; host = input + 1; port = eol + 1; } else { host = "localhost"; port = input + ((input[1] == ':')? 2: 1); } rbuf = r_core_rtr_cmds_query (core, host, port, cmd); if (rbuf) { r_cons_print (rbuf); free (rbuf); } } else { r_core_rtr_cmds (core, input + 1); } break; #endif case '.': // ".." same as \n if (input[1] == '.') { // "..." run the last command repeated // same as \n with e cmd.repeat=true lastcmd_repeat (core, 1); } else if (input[1] == ' ') { char *str = r_core_cmd_str_pipe (core, r_str_trim_head_ro (input)); if (str) { r_core_cmd (core, str, 0); free (str); } } else if (input[1] && input[1] != '?') { r_core_cmdf (core, "s%s", input); } else { r_core_cmd_help (core, help_msg_dot); } break; case '*': // ".*" { const char *a = r_str_trim_head_ro (input + 1); char *s = strdup (a); char *sp = strchr (s, ' '); if (sp) { *sp = 0; } if (R_STR_ISNOTEMPTY (s)) { r_core_run_script (core, s); } free (s); } break; case '-': // ".-" if (input[1] == '?') { r_core_cmd_help_match (core, help_msg_dot, ".-"); } else { r_core_run_script (core, "-"); } break; case ' ': // ". " { const char *script_file = r_str_trim_head_ro (input + 1); if (*script_file == '$' && !script_file[1]) { R_LOG_ERROR ("No alias name given"); } else if (*script_file == '$') { RCmdAliasVal *v = r_cmd_alias_get (core->rcmd, script_file + 1); if (v) { char *cmd_text = r_cmd_alias_val_strdup (v); r_core_cmd0 (core, cmd_text); free (cmd_text); } else { R_LOG_ERROR ("No such alias \"$%s\"", script_file+1); } } else { if (!r_core_run_script (core, script_file)) { R_LOG_ERROR ("Cannot find script '%s'", script_file); r_core_return_value (core, R_CMD_RC_FAILURE); } else { r_core_return_value (core, R_CMD_RC_SUCCESS); } } } break; case '!': // ".!" /* from command */ r_core_cmd_command (core, input + 1); break; case '(': // ".(" if (input[1] == '*') { goto bypass; } r_cmd_macro_call (&core->rcmd->macro, input + 1); break; default: if (*input >= 0 && *input <= 9) { R_LOG_ERROR ("No .[0..9] to avoid infinite loops"); break; } bypass: inp = strdup (input); filter = strchr (inp, '~'); if (filter) { *filter = 0; } int tmp_html = r_cons_context ()->is_html; r_cons_context ()->is_html = false; ptr = str = r_core_cmd_str (core, inp); r_cons_context ()->is_html = tmp_html; if (filter) { *filter = '~'; } r_cons_break_push (NULL, NULL); if (ptr) { for (;;) { if (r_cons_is_breaked ()) { break; } eol = strchr (ptr, '\n'); if (eol) { *eol = '\0'; } if (*ptr) { char *p = r_str_append (strdup (ptr), filter); r_core_cmd0 (core, p); free (p); } if (!eol) { break; } ptr = eol + 1; } } r_cons_break_pop (); free (str); free (inp); break; } return 0; } static bool callback_foreach_kv(void *user, const char *k, const char *v) { r_cons_printf ("%s=%s\n", k, v); return true; } R_API int r_line_hist_sdb_up(RLine *line) { if (!line->sdbshell_hist_iter || !line->sdbshell_hist_iter->n) { return false; } line->sdbshell_hist_iter = line->sdbshell_hist_iter->n; strncpy (line->buffer.data, line->sdbshell_hist_iter->data, R_LINE_BUFSIZE - 1); line->buffer.index = line->buffer.length = strlen (line->buffer.data); return true; } R_API int r_line_hist_sdb_down(RLine *line) { if (!line->sdbshell_hist_iter || !line->sdbshell_hist_iter->p) { return false; } line->sdbshell_hist_iter = line->sdbshell_hist_iter->p; strncpy (line->buffer.data, line->sdbshell_hist_iter->data, R_LINE_BUFSIZE - 1); line->buffer.index = line->buffer.length = strlen (line->buffer.data); return true; } static int cmd_kuery(void *data, const char *input) { char *out; RCore *core = (RCore*)data; const char *sp, *p = "[sdb]> "; Sdb *s = core->sdb; char *cur_pos = NULL, *cur_cmd = NULL, *next_cmd = NULL; char *temp_pos = NULL, *temp_cmd = NULL; switch (input[0]) { case 'j': out = sdb_querys (s, NULL, 0, "anal/**"); if (!out) { r_cons_println ("No Output from sdb"); break; } PJ *pj = r_core_pj_new (core); if (!pj) { free (out); break; } pj_o (pj); pj_ko (pj, "anal"); pj_ka (pj, "cur_cmd"); while (*out) { cur_pos = strchr (out, '\n'); if (!cur_pos) { break; } cur_cmd = r_str_ndup (out, cur_pos - out); pj_s (pj, cur_cmd); free (next_cmd); next_cmd = r_str_newf ("anal/%s/*", cur_cmd); char *query_result = sdb_querys (s, NULL, 0, next_cmd); if (!query_result) { out = cur_pos + 1; continue; } char *temp = query_result; while (*temp) { temp_pos = strchr (temp, '\n'); if (!temp_pos) { break; } temp_cmd = r_str_ndup (temp, temp_pos - temp); pj_s (pj, temp_cmd); temp = temp_pos + 1; } out = cur_pos + 1; free (query_result); } pj_end (pj); pj_end (pj); pj_end (pj); r_cons_println (pj_string (pj)); pj_free (pj); R_FREE (next_cmd); free (next_cmd); free (cur_cmd); break; case ' ': if (s) { out = sdb_querys (s, NULL, 0, input + 1); if (out) { r_cons_print (out); } R_FREE (out); } break; //case 's': r_pair_save (s, input + 3); break; //case 'l': r_pair_load (sdb, input + 3); break; case '\0': sdb_foreach (s, callback_foreach_kv, NULL); break; // TODO: add command to list all namespaces // sdb_ns_foreach ? case 's': // "ks" if (core->http_up) { return false; } if (!r_cons_is_interactive ()) { return false; } if (input[1] == ' ') { char *n, *o, *p = strdup (input + 2); // TODO: slash split here? or inside sdb_ns ? for (n = o = p; n; o = n) { n = strchr (o, '/'); // SDB_NS_SEPARATOR NAMESPACE if (n) { *n++ = 0; } s = sdb_ns (s, o, 1); } free (p); } if (!s) { s = core->sdb; } RLine *line = core->cons->line; if (!line->sdbshell_hist) { line->sdbshell_hist = r_list_newf (free); } RList *sdb_hist = line->sdbshell_hist; r_line_set_hist_callback (line, &r_line_hist_sdb_up, &r_line_hist_sdb_down); const size_t buf_size = 1024; char *buf = malloc (1024); while (buf) { r_line_set_prompt (p); *buf = 0; if (r_cons_fgets (buf, buf_size, 0, NULL) < 1) { break; } if (!*buf) { break; } if (sdb_hist) { if ((r_list_length (sdb_hist) == 1) || (r_list_length (sdb_hist) > 1 && strcmp (r_list_get_n (sdb_hist, 1), buf))) { r_list_insert (sdb_hist, 1, strdup (buf)); } line->sdbshell_hist_iter = sdb_hist->head; } out = sdb_querys (s, NULL, 0, buf); if (out) { r_cons_println (out); r_cons_flush (); } } free (buf); r_line_set_hist_callback (core->cons->line, &r_line_hist_cmd_up, &r_line_hist_cmd_down); break; case 'o': // "ko" if (r_sandbox_enable (0)) { R_LOG_ERROR ("This command is disabled in sandbox mode"); return 0; } if (input[1] == ' ') { char *fn = strdup (input + 2); char *ns = strchr (fn, ' '); if (ns) { Sdb *db; *ns++ = 0; if (r_file_exists (fn)) { db = sdb_ns_path (core->sdb, ns, 1); if (db) { Sdb *newdb = sdb_new (NULL, fn, 0); if (newdb) { sdb_drain (db, newdb); } else { R_LOG_ERROR ("Cannot open sdb '%s'", fn); } } else { R_LOG_ERROR ("Cannot find sdb '%s'", ns); } } else { R_LOG_ERROR ("Cannot open file"); } } else { R_LOG_ERROR ("Missing sdb namespace"); } free (fn); } else { r_core_cmd_help_match (core, help_msg_k, "ko"); } break; case 'd': // "kd" if (r_sandbox_enable (0)) { R_LOG_ERROR ("The 'kd' command is disabled in sandbox mode"); return 0; } if (input[1] == ' ') { char *fn = strdup (input + 2); char *ns = strchr (fn, ' '); if (ns) { *ns++ = 0; Sdb *db = sdb_ns_path (core->sdb, ns, 0); if (db) { sdb_file (db, fn); sdb_sync (db); } else { R_LOG_ERROR ("Cannot find sdb '%s'", ns); } } else { R_LOG_ERROR ("Missing sdb namespace"); } free (fn); } else { r_core_cmd_help_match (core, help_msg_k, "kd"); } break; case '?': r_core_cmd_help (core, help_msg_k); break; } if (input[0] == '\0') { /* nothing more to do, the command has been parsed. */ return 0; } sp = strchr (input + 1, ' '); if (sp) { char *inp = strdup (input); inp [(size_t)(sp - input)] = 0; s = sdb_ns (core->sdb, inp + 1, 1); out = sdb_querys (s, NULL, 0, sp + 1); if (out) { r_cons_println (out); free (out); } free (inp); return 0; } return 0; } static int cmd_bsize(void *data, const char *input) { ut64 n; RFlagItem *flag; RCore *core = (RCore *)data; switch (input[0]) { case '6': // "b6" if (r_str_startswith (input, "64:")) { int len = 0; char *cmd = (char *)sdb_decode (input + 3, &len); if (cmd) { cmd[len] = 0; r_core_cmd_call (core, cmd); free (cmd); } else { R_LOG_ERROR ("Missing base64 string after b64:"); } } else { r_core_cmd_help_contains (core, help_msg_b, "b64:"); } break; case 'm': // "bm" n = r_num_math (core->num, input + 1); if (n > 1) { core->blocksize_max = n; } else { r_cons_printf ("0x%x\n", (ut32)core->blocksize_max); } break; case '+': // "b+" n = r_num_math (core->num, input + 1); r_core_block_size (core, core->blocksize + n); break; case '-': // "b-" n = r_num_math (core->num, input + 1); r_core_block_size (core, core->blocksize - n); break; case 'f': // "bf" if (input[1] == ' ') { flag = r_flag_get (core->flags, input + 2); if (flag) { r_core_block_size (core, flag->size); } else { R_LOG_ERROR ("bf: cannot find flag named '%s'", input + 2); } } else { r_core_cmd_help_match (core, help_msg_b, "bf"); } break; case 'j': { // "bj" PJ *pj = r_core_pj_new (core); if (!pj) { break; } pj_o (pj); pj_ki (pj, "blocksize", core->blocksize); pj_ki (pj, "blocksize_limit", core->blocksize_max); pj_end (pj); r_cons_println (pj_string (pj)); pj_free (pj); break; } case '*': // "b*" r_cons_printf ("b 0x%x\n", core->blocksize); break; case '\0': // "b" r_cons_printf ("0x%x\n", core->blocksize); break; case '=': case ' ': r_core_block_size (core, r_num_math (core->num, input + 1)); break; default: case '?': // "b?" r_core_cmd_help (core, help_msg_b); break; } return 0; } static int __runMain(RMainCallback cb, const char *arg) { char *a = r_str_trim_dup (arg); int argc = 0; char **args = r_str_argv (a, &argc); int res = cb? cb (argc, (const char **)args): -1; free (args); free (a); return res; } static bool cmd_r2cmd(RCore *core, const char *_input) { char *input = r_str_newf ("r%s", _input); int rc = 0; if (r_str_startswith (input, "rax2")) { rc = __runMain (core->r_main_rax2, input); } else if (r_str_startswith (input, "radare2")) { r_sys_cmdf ("%s", input); // rc = __runMain (core->r_main_radare2, input); } else if (r_str_startswith (input, "rasm2")) { r_sys_cmdf ("%s", input); // rc = __runMain (core->r_main_rasm2, input); } else if (r_str_startswith (input, "rabin2")) { r_sys_cmdf ("%s", input); // rc = __runMain (core->r_main_rabin2, input); } else if (r_str_startswith (input, "ragg2")) { r_sys_cmdf ("%s", input); // rc = __runMain (core->r_main_ragg2, input); } else if (r_str_startswith (input, "ravc2")) { rc = __runMain (core->r_main_ravc2, input); } else if (r_str_startswith (input, "r2pm")) { rc = __runMain (core->r_main_r2pm, input); } else if (r_str_startswith (input, "radiff2")) { rc = __runMain (core->r_main_radiff2, input); } else if (r_str_startswith (input, "r2.")) { r_core_cmdf (core, "\"\"js console.log(r2.%s)", input + 3); } else if (r_str_startswith (input, "r2")) { if (input[2] == ' ' || input[2] == 0) { r_sys_cmdf ("%s", input); } else { R_LOG_ERROR ("Invalid command"); } // rc = __runMain (core->r_main_radare2, input); } else { const char *r2cmds[] = { "rax2", "r2pm", "rasm2", "rabin2", "rahash2", "rafind2", "rarun2", "ragg2", "radare2", "r2", NULL }; int i; for (i = 0; r2cmds[i]; i++) { if (r_str_startswith (input, r2cmds[i])) { free (input); return true; } } free (input); return false; } free (input); r_core_return_value (core, rc); // r_core_return_code (core, rc); return true; } static int cmd_rebase(RCore *core, const char *input) { ut64 addr = r_num_math (core->num, input); if (!addr) { r_core_cmd_help_match (core, help_msg_r, "rb"); return 0; } // old base = addr // new base = core->offset r_debug_bp_rebase (core->dbg, addr, core->offset); r_bin_set_baddr (core->bin, core->offset); r_flag_move (core->flags, addr, core->offset); r_core_cmd0 (core, ".is*"); r_core_cmd0 (core, ".iM*"); r_core_cmd0 (core, ".ii*"); r_core_cmd0 (core, ".iz*"); // TODO: r_anal_move :?? // TODO: differentiate analysis by map ranges (associated with files or memory maps) return 0; } static int cmd_resize(void *data, const char *input) { RCore *core = (RCore *)data; ut64 newsize = 0; st64 delta = 0; int ret; if (cmd_r2cmd (core, input)) { return true; } ut64 oldsize = (core->io->desc) ? r_io_fd_size (core->io, core->io->desc->fd): 0; switch (*input) { case 'b': // "rb" rebase return cmd_rebase (core, input + 1); case '2': // "r2" // XXX should be handled already in cmd_r2cmd() if (r_str_startswith (input + 1, "ai")) { R_LOG_ERROR ("Missing plugin. Run: r2pm -ci r2yara"); r_core_return_code (core, 1); return true; } // TODO: use argv[0] instead of 'radare2' // TODO: { char **argv = { "r2", NULL }; r_main_radare2 (1, argv); } r_sys_cmdf ("radare%s", input); return true; case 'm': // "rm" if (r_str_startswith (input, "mrf")) { if (input[3] == ' ') { const char *file = r_str_trim_head_ro (input + 3); return r_file_rm_rf (file); } r_core_cmd_help_match (core, help_msg_r, "rmrf"); return false; } if (input[1] == ' ') { const char *file = r_str_trim_head_ro (input + 2); if (*file == '$') { if (!r_cmd_alias_del (core->rcmd, file + 1)) { R_LOG_ERROR ("Cannot find alias file %s", file); } } else { r_file_rm (file); } } else { r_core_cmd_help_contains (core, help_msg_r, "rm"); } return true; case '\0': if (core->io->desc) { if (oldsize != -1) { r_cons_printf ("%"PFMT64d"\n", oldsize); } } return true; case 'j': { // "rj" PJ *pj = r_core_pj_new (core); pj_o (pj); if (oldsize != -1) { pj_kn (pj, "size", oldsize); } pj_end (pj); char *s = pj_drain (pj); r_cons_println (s); free (s); return true; } case 'h': // "rh" if (core->io->desc) { if (oldsize != -1) { char humansz[8]; r_num_units (humansz, sizeof (humansz), oldsize); r_cons_printf ("%s\n", humansz); } } return true; case '+': // "r+" case '-': // "r-" delta = (st64)r_num_math (core->num, input); newsize = oldsize + delta; break; case '0': // "r0" if (input[1] == 'x') { // "r0x" newsize = r_num_math (core->num, input); } else { r_core_cmd_help (core, help_msg_r); } break; case ' ': // "r " "r +" "r -" { const char *arg = r_str_trim_head_ro (input + 1); if (*arg == '-' || *arg == '+') { delta = (st64)r_num_math (core->num, input); newsize = oldsize + delta; } else { newsize = r_num_math (core->num, arg); if (newsize == 0) { if (arg[1] == '0') { R_LOG_ERROR ("Invalid size"); } return false; } } } break; case 'e': // "re" { int rc = write (1, Color_RESET_TERMINAL, strlen (Color_RESET_TERMINAL)); if (rc == -1) { return false; } } return true; case '?': // "r?" default: r_core_cmd_help (core, help_msg_r); return true; } bool grow = (newsize > oldsize); if (grow) { ret = r_io_resize (core->io, newsize); if (ret < 1) { R_LOG_ERROR ("r_io_resize: cannot resize"); } } if (delta && core->offset < newsize) { r_io_shift (core->io, core->offset, grow?newsize:oldsize, delta); } if (!grow) { ret = r_io_resize (core->io, newsize); if (ret < 1) { R_LOG_ERROR ("cannot resize"); } } if (newsize < (core->offset + core->blocksize) || oldsize < (core->offset + core->blocksize)) { r_core_block_read (core); } return true; } static int cmd_panels(void *data, const char *input) { RCore *core = (RCore*) data; if (core->vmode) { return false; } if (*input == '.') { const char *f = r_str_trim_head_ro (input + 1); if (*f) { r_core_visual_slides (core, f); } return false; } if (*input == '?') { r_core_cmd_help (core, help_msg_v); return false; } if (!r_cons_is_interactive ()) { R_LOG_ERROR ("Panel mode requires scr.interactive=true"); return false; } if (*input == ' ') { if (core->panels) { r_core_panels_load (core, input + 1); } r_config_set (core->config, "scr.layout", input + 1); return true; } if (*input == 'e') { if (input[1] == ' ') { #define getpanel(x,y) ((x) && (y) < 16)? (x)->panel[y]: NULL RPanel *pan = getpanel (core->panels, core->panels->curnode); #undef getpanel if (pan) { char *r = r_cons_pal_parse (r_str_trim_head_ro (input + 2), NULL); if (r) { free (pan->model->bgcolor); pan->model->bgcolor = r_str_newf (Color_RESET"%s", r); free (r); } else { R_LOG_ERROR ("Invalid color %sXXX"Color_RESET, r); } } } else { r_core_cmd_help_match (core, help_msg_v, "ve"); } return true; } if (*input == '=') { if (input[1]) { r_core_panels_save (core, input + 1); r_config_set (core->config, "scr.layout", input + 1); } else { r_core_cmd_help_match (core, help_msg_v, "v="); } return true; } if (*input == 'i') { char *sp = strchr (input, ' '); if (sp) { char *r = r_core_editor (core, sp + 1, NULL); if (r) { free (r); } else { R_LOG_ERROR ("Cannot open file (%s)", sp + 1); } } else { r_core_cmd_help_match (core, help_msg_v, "vi"); } return false; } if (*input) { r_core_cmd_help (core, help_msg_v); } else { r_core_panels_root (core, core->panels_root); } return true; } static int cmd_visual(void *data, const char *input) { RCore *core = (RCore*) data; if (*input == '?') { // "mL?" r_core_cmd_help_match_spec (core, help_msg_root, "V", 0); return true; } if (core->http_up) { return false; } if (!r_cons_is_interactive ()) { R_LOG_ERROR ("Visual mode requires scr.interactive=true"); return false; } return r_core_visual ((RCore *)data, input); } static int cmd_pipein(void *user, const char *input) { char *buf = strdup (input); int len = r_str_unescape (buf); r_cons_readpush (buf, len); free (buf); return 0; } static int cmd_tasks(void *data, const char *input) { RCore *core = (RCore*) data; switch (input[0]) { case '\0': // "&" case 'j': // "&j" r_core_task_list (core, *input); break; case ':': // "&:" r_core_cmd_queue (core, input + 1); break; case 'w': // "&w" r_core_cmd_queue_wait (core); break; case 'b': { // "&b" if (r_sandbox_enable (0)) { R_LOG_ERROR ("The &b command is disabled in sandbox mode"); return 0; } int tid = r_num_math (core->num, input + 1); if (tid) { r_core_task_break (&core->tasks, tid); } break; } case '&': { // "&&" if (r_sandbox_enable (0)) { R_LOG_ERROR ("The && command is disabled in sandbox mode"); return 0; } int tid = r_num_math (core->num, input + 1); r_core_task_join (&core->tasks, core->tasks.current_task, tid ? tid : -1); break; } case '=': { // "&=" // r_core_task_list (core, '='); int tid = r_num_math (core->num, input + 1); if (tid) { RCoreTask *task = r_core_task_get_incref (&core->tasks, tid); if (task) { if (task->res) { r_cons_println (task->res); } r_core_task_decref (task); } else { R_LOG_ERROR ("Cannot find task"); } } break; } case '-': // "&-" if (r_sandbox_enable (0)) { R_LOG_ERROR ("The &- command is disabled in sandbox mode"); return 0; } if (input[1] == '*') { r_core_task_del_all_done (&core->tasks); } else { r_core_task_del (&core->tasks, r_num_math (core->num, input + 1)); } break; case '?': // "&?" default: r_core_cmd_help (core, help_msg_amper); break; case ' ': // "& " case '_': // "&_" case 't': { // "&t" if (r_sandbox_enable (0)) { R_LOG_ERROR ("This command is disabled in sandbox mode"); return 0; } RCoreTask *task = r_core_task_new (core, true, input + 1, NULL, core); if (!task) { break; } task->transient = input[0] == 't'; r_core_task_enqueue (&core->tasks, task); break; } } return 0; } static int cmd_pointer(void *data, const char *input) { RCore *core = (RCore*) data; int ret = 0; input = r_str_trim_head_ro (input); if (!*input || *input == '?') { r_core_cmd_help (core, help_msg_star); return ret; } char *str = strdup (input); char *eq = strchr (str, '='); if (eq) { *eq++ = 0; ret = r_core_cmdf (core, "wv %s@%s", eq, str); } else { ret = r_core_cmdf (core, "?v [%s]", input); } free (str); return ret; } static int cmd_env(void *data, const char *input) { RCore *core = (RCore*)data; int ret = 1; switch (*input) { case 'j': case '*': ret = r_core_cmdf (core, "env%c", *input); break; case '?': cmd_help_percent (core); break; default: ret = r_core_cmdf (core, "env %s", input); } return ret; } static struct autocomplete_flag_map_t { const char* name; const char* desc; int type; } autocomplete_flags [] = { { "$dflt", "default autocomplete flag", R_CORE_AUTOCMPLT_DFLT }, { "$flag", "shows known flag hints", R_CORE_AUTOCMPLT_FLAG }, { "$flsp", "shows known flag-spaces hints", R_CORE_AUTOCMPLT_FLSP }, { "$seek", "shows the seek hints", R_CORE_AUTOCMPLT_SEEK }, { "$fcn", "shows the functions hints", R_CORE_AUTOCMPLT_FCN }, { "$vars", "autocomplete function varnames", R_CORE_AUTOCMPLT_VARS }, { "$zign", "shows known zignatures hints", R_CORE_AUTOCMPLT_ZIGN }, { "$eval", "shows known evals hints", R_CORE_AUTOCMPLT_EVAL }, { "$prjt", "shows known projects hints", R_CORE_AUTOCMPLT_PRJT }, { "$mins", NULL, R_CORE_AUTOCMPLT_MINS }, { "$brkp", "shows known breakpoints hints", R_CORE_AUTOCMPLT_BRKP }, { "$macro", NULL, R_CORE_AUTOCMPLT_MACR }, { "$file", "hints file paths", R_CORE_AUTOCMPLT_FILE }, { "$thme", "shows known themes hints", R_CORE_AUTOCMPLT_THME }, { "$optn", "allows the selection for multiple options", R_CORE_AUTOCMPLT_OPTN }, { "$ms", "shows mount hints", R_CORE_AUTOCMPLT_MS}, { "$sdb", "shows sdb hints", R_CORE_AUTOCMPLT_SDB}, { NULL, NULL, 0 } }; static inline void print_dict(RCoreAutocomplete* a, int sub) { if (!a) { return; } int i, j; const char* name = "unknown"; for (i = 0; i < a->n_subcmds; i++) { RCoreAutocomplete* b = a->subcmds[i]; if (b->locked) { continue; } for (j = 0; j < R_CORE_AUTOCMPLT_END; j++) { if (b->type == autocomplete_flags[j].type) { name = autocomplete_flags[j].name; break; } } eprintf ("[%3d] %s: '%s'\n", sub, name, b->cmd); print_dict (a->subcmds[i], sub + 1); } } static int autocomplete_type(const char* strflag) { int i; for (i = 0; i < R_CORE_AUTOCMPLT_END; i++) { if (autocomplete_flags[i].desc && !strncmp (strflag, autocomplete_flags[i].name, 5)) { return autocomplete_flags[i].type; } } R_LOG_ERROR ("Invalid flag '%s'", strflag); return R_CORE_AUTOCMPLT_END; } static void cmd_autocomplete_help(RCore *core) { r_core_cmd_help (core, help_msg_triple_exclamation); // non-zero-cost survival without iterators 101 char const **help = calloc (R_CORE_AUTOCMPLT_END + 1, 3 * sizeof (char *)); int i; size_t n; for (i = 0, n = 0; i < R_CORE_AUTOCMPLT_END; i++) { if (autocomplete_flags[i].desc) { // highlight "$" as cmd and the rest of the name as args help[n + 0] = "$"; help[n + 1] = autocomplete_flags[i].name + 1; help[n + 2] = autocomplete_flags[i].desc; n += 3; } } r_core_cmd_help (core, help); free ((void*)help); } static void cmd_autocomplete(RCore *core, const char *input) { RCoreAutocomplete* b = core->autocomplete; input = r_str_trim_head_ro (input); char arg[256]; if (!*input) { print_dict (core->autocomplete, 0); return; } if (*input == '?') { cmd_autocomplete_help (core); return; } if (*input == '-') { const char *arg = input + 1; if (!*input) { R_LOG_ERROR ("Use !!!-* or !!!- to delete an autocompletion"); return; } r_core_autocomplete_remove (b, arg); return; } while (b) { const char* end = r_str_trim_head_wp (input); if (!end) { break; } if ((end - input) >= sizeof (arg)) { // wtf? R_LOG_ERROR ("Exceeded the max arg length (255)"); return; } if (end == input) { break; } memcpy (arg, input, end - input); arg[end - input] = 0; RCoreAutocomplete* a = r_core_autocomplete_find (b, arg, true); input = r_str_trim_head_ro (end); if (R_STR_ISNOTEMPTY (input) && !a) { if (b->type == R_CORE_AUTOCMPLT_DFLT && !(b = r_core_autocomplete_add (b, arg, R_CORE_AUTOCMPLT_DFLT, false))) { R_LOG_ERROR ("ENOMEM"); return; } else if (b->type != R_CORE_AUTOCMPLT_DFLT) { R_LOG_ERROR ("Cannot add autocomplete to '%s'. type not $dflt", b->cmd); return; } } else if (R_STR_ISEMPTY (input) && !a) { if (arg[0] == '$') { int type = autocomplete_type (arg); if (type != R_CORE_AUTOCMPLT_END && !b->locked && !b->n_subcmds) { b->type = type; } else if (b->locked || b->n_subcmds) { if (!b->cmd) { return; } R_LOG_ERROR ("Changing type of '%s' is forbidden", b->cmd); } } else { if (!r_core_autocomplete_add (b, arg, R_CORE_AUTOCMPLT_DFLT, false)) { R_LOG_ERROR ("ENOMEM"); return; } } return; } else if (R_STR_ISEMPTY (input) && a) { // eprintf ("Cannot add '%s'. Already exists.\n", arg); return; } else { b = a; } } R_LOG_ERROR ("Invalid usage of !!!"); } static int cmd_last(void *data, const char *input) { static RCoreHelpMessage help_msg_last = { "_", "", "print last output", NULL }; switch (*input) { case 0: r_cons_last (); break; default: r_core_cmd_help ((RCore *)data, help_msg_last); } return 0; } static bool stderr_cb(void *user, int type, const char *origin, const char *msg) { RList *stderr_list = (RList*)user; if (!msg) { return false; } PJ *j = pj_new (); pj_o (j); pj_ks (j, "type", r_log_level_tostring (type)); if (origin) { pj_ks (j, "origin", origin); } if (msg) { pj_ks (j, "message", msg); } pj_end (j); r_list_append (stderr_list, pj_drain (j)); return true; } static int cmd_json(void *data, const char *input) { RCore *core = (RCore *)data; if (*input == '?') { r_cons_printf ("Usage: {\"cmd\":\"...\",\"json\":false,\"trim\":true} # `cmd` is required\n"); return 0; } char *s_input = strdup (input - 1); const RJson *j_cmd = NULL; RJson *j = r_json_parse (s_input); if (j) { j_cmd = r_json_get (j, "cmd"); } PJ *pj = r_core_pj_new (core); pj_o (pj); if (j_cmd) { const RJson *j_json = r_json_get (j, "json"); const RJson *j_trim = r_json_get (j, "trim"); bool is_json = false; if (j_json && j_json->type == R_JSON_BOOLEAN) { is_json = j_json->num.u_value == 1; } bool is_trim = false; if (j_trim && j_trim->type == R_JSON_BOOLEAN) { is_trim = j_trim->num.u_value == 1; } const char *r_cmd = j_cmd->str_value; RList *stderr_list = r_list_newf (free); // capture stderr r_log_add_callback (stderr_cb, stderr_list); char *res = r_core_cmd_str (core, r_cmd); r_log_del_callback (stderr_cb); if (res) { if (is_trim || is_json) { r_str_trim (res); } if (is_json) { pj_k (pj, "res"); pj_raw (pj, res); } else { pj_ks (pj, "res", res); } free (res); pj_kb (pj, "error", false); } else { pj_kb (pj, "error", true); } pj_kn (pj, "value", core->num->value); pj_kn (pj, "code", core->rc); if (!r_list_empty (stderr_list)) { pj_ka (pj, "logs"); char *m; RListIter *iter; r_list_foreach (stderr_list, iter, m) { pj_raw (pj, m); if (iter->n) { pj_raw (pj, ","); } } pj_end (pj); } r_list_free (stderr_list); pj_kn (pj, "code", core->rc); } else { pj_ks (pj, "res", ""); pj_kb (pj, "error", true); pj_kn (pj, "value", core->num->value); pj_kn (pj, "code", core->rc); } pj_end (pj); char *j_res = pj_drain (pj); r_cons_printf ("%s\n", j_res); free (j_res); r_json_free (j); free (s_input); return 0; } static int cmd_system(void *data, const char *input) { RCore *core = (RCore*)data; ut64 n; int ret = 0; switch (*input) { case '-': //!- if (input[1]) { r_line_hist_free (); char *history_file = r_xdg_cachedir ("history"); r_line_hist_save (history_file); free (history_file); } else { r_line_hist_free (); } break; case '=': //!= if (input[1] == '?') { r_core_cmd_help_match (core, help_msg_exclamation, "!=!"); r_core_cmd_help_match (core, help_msg_exclamation, "=!="); } else { if (!r_sandbox_enable (0)) { R_FREE (core->cmdremote); } } break; case '.': // "!." { char *history_file = r_xdg_cachedir ("history"); R_LOG_INFO ("History saved to %s", history_file); r_line_hist_save (history_file); free (history_file); } break; case '!': // "!!" if (input[1] == '!') { // !!! & !!!- cmd_autocomplete (core, input + 2); } else if (input[1] == '?') { cmd_help_exclamation (core); } else if (input[1] == '*') { char *cmd = r_str_trim_dup (input + 1); (void)r_core_cmdf (core, "\"#!pipe %s\"", cmd); free (cmd); } else { if (input[1]) { if (r_sandbox_enable (0)) { R_LOG_ERROR ("The !! command is disabled in sandbox mode"); return 0; } int olen; char *out = NULL; char *cmd = r_core_sysenv_begin (core, input); if (cmd) { void *bed = r_cons_sleep_begin (); ret = r_sys_cmd_str_full (cmd + 1, NULL, 0, &out, &olen, NULL); r_cons_sleep_end (bed); r_core_sysenv_end (core, input); r_cons_write (out, olen); free (out); free (cmd); } } else { #if R2_USE_NEW_ABI r_line_hist_list (false); #else __line_hist_list (false); #endif } } break; case '\0': #if R2_USE_NEW_ABI r_line_hist_list (true); #else r_line_hist_list (); #endif break; case '?': // "!?" cmd_help_exclamation (core); break; case '*': // "!*" // TODO: use the api { char *cmd = r_str_trim_dup (input + 1); cmd = r_str_replace (cmd, " ", "\\ ", true); cmd = r_str_replace (cmd, "\\ ", " ", false); cmd = r_str_replace (cmd, "\"", "'", false); ret = r_core_cmdf (core, "\"#!pipe %s\"", cmd); free (cmd); } break; default: n = atoi (input); if (*input == '0' || n > 0) { const char *cmd = r_line_hist_get (n); if (cmd) { r_core_cmd0 (core, cmd); } } else { char *cmd = r_core_sysenv_begin (core, input); if (cmd) { void *bed = r_cons_sleep_begin (); ret = r_sys_cmd (cmd); if (ret != 0) { r_cons_singleton()->context->was_breaked = true; } r_cons_sleep_end (bed); r_core_sysenv_end (core, input); free (cmd); } else { R_LOG_ERROR ("Cannot setup the environment"); } } break; } return ret; } static char *unescape_special_chars(const char *s, const char *special_chars) { char *dst = R_NEWS (char, strlen (s) + 1); int i, j = 0; for (i = 0; s[i]; i++) { if (s[i] != '\\' || !strchr (special_chars, s[i + 1])) { dst[j++] = s[i]; continue; } if (!s[i + 1]) { break; } dst[j++] = s[i + 1]; i++; } dst[j++] = '\0'; return dst; } #if R2__WINDOWS__ #include #define __CLOSE_DUPPED_PIPES() \ close (1); \ close (fd_out); \ fd_out = -1; static void r_w32_cmd_pipe(RCore *core, char *radare_cmd, char *shell_cmd) { STARTUPINFO si = {0}; PROCESS_INFORMATION pi = {0}; SECURITY_ATTRIBUTES sa; HANDLE pipe[2] = {NULL, NULL}; int fd_out = -1, cons_out = -1; char *_shell_cmd = NULL; LPTSTR _shell_cmd_ = NULL; DWORD mode; TCHAR *systemdir = NULL; GetConsoleMode (GetStdHandle (STD_OUTPUT_HANDLE), &mode); sa.nLength = sizeof (SECURITY_ATTRIBUTES); sa.bInheritHandle = TRUE; sa.lpSecurityDescriptor = NULL; if (!CreatePipe (&pipe[0], &pipe[1], &sa, 0)) { r_sys_perror ("r_w32_cmd_pipe/CreatePipe"); goto err_r_w32_cmd_pipe; } if (!SetHandleInformation (pipe[1], HANDLE_FLAG_INHERIT, 0)) { r_sys_perror ("r_w32_cmd_pipe/SetHandleInformation"); goto err_r_w32_cmd_pipe; } si.hStdError = GetStdHandle (STD_ERROR_HANDLE); si.hStdOutput = GetStdHandle (STD_OUTPUT_HANDLE); si.hStdInput = pipe[0]; si.dwFlags |= STARTF_USESTDHANDLES; si.cb = sizeof (si); _shell_cmd = shell_cmd; while (*_shell_cmd && isspace ((ut8)*_shell_cmd)) { _shell_cmd++; } char *tmp = r_str_newf ("/Q /c \"%s\"", _shell_cmd); if (!tmp) { goto err_r_w32_cmd_pipe; } _shell_cmd = tmp; _shell_cmd_ = r_sys_conv_utf8_to_win (_shell_cmd); free (tmp); if (!_shell_cmd_) { goto err_r_w32_cmd_pipe; } systemdir = calloc (MAX_PATH, sizeof (TCHAR)); if (!systemdir) { goto err_r_w32_cmd_pipe; } int ret = GetSystemDirectory (systemdir, MAX_PATH); if (!ret) { r_sys_perror ("r_w32_cmd_pipe/systemdir"); goto err_r_w32_cmd_pipe; } _tcscat_s (systemdir, MAX_PATH, TEXT("\\cmd.exe")); // exec windows process if (!CreateProcess (systemdir, _shell_cmd_, NULL, NULL, TRUE, 0, NULL, NULL, &si, &pi)) { r_sys_perror ("r_w32_cmd_pipe/CreateProcess"); goto err_r_w32_cmd_pipe; } fd_out = _open_osfhandle ((intptr_t)pipe[1], _O_WRONLY|_O_TEXT); if (fd_out == -1) { r_sys_perror ("_open_osfhandle"); goto err_r_w32_cmd_pipe; } cons_out = dup (1); dup2 (fd_out, 1); // exec radare command r_core_cmd (core, radare_cmd, 0); HANDLE th = CreateThread (NULL, 0,(LPTHREAD_START_ROUTINE) r_cons_flush, NULL, 0, NULL); if (!th) { __CLOSE_DUPPED_PIPES (); goto err_r_w32_cmd_pipe; } while (true) { int ret = WaitForSingleObject (th, 50); if (!ret) { // Successfully written everything to pipe __CLOSE_DUPPED_PIPES (); WaitForSingleObject (pi.hProcess, INFINITE); break; } ret = WaitForSingleObject (pi.hProcess, 50); if (!ret) { // Process exited before we finished writing to pipe DWORD exit; if (GetExitCodeThread (th, &exit) && exit == STILL_ACTIVE) { r_w32_CancelSynchronousIo (th); } // Windows XP WaitForSingleObject (th, INFINITE); __CLOSE_DUPPED_PIPES (); break; } } CloseHandle (th); err_r_w32_cmd_pipe: if (pi.hProcess) { CloseHandle (pi.hProcess); } if (pi.hThread) { CloseHandle (pi.hThread); } if (pipe[0]) { CloseHandle (pipe[0]); } if (fd_out != -1) { close (fd_out); } if (cons_out != -1) { dup2 (cons_out, 1); close (cons_out); } free (systemdir); free (_shell_cmd_); SetConsoleMode (GetStdHandle (STD_OUTPUT_HANDLE), mode); } #undef __CLOSE_DUPPED_PIPES #endif R_API int r_core_cmd_pipe(RCore *core, char *radare_cmd, char *shell_cmd) { #if R2__UNIX__ && !__wasi__ && HAVE_FORK int stdout_fd, fds[2]; int child; #endif int olen, ret = -1, pipecolor = -1; char *str, *out = NULL; if (r_sandbox_enable (0)) { R_LOG_ERROR ("Pipes are not allowed in sandbox mode"); return -1; } bool si = r_cons_is_interactive (); r_config_set_b (core->config, "scr.interactive", false); if (!r_config_get_b (core->config, "scr.color.pipe")) { pipecolor = r_config_get_i (core->config, "scr.color"); r_config_set_i (core->config, "scr.color", COLOR_MODE_DISABLED); } if (*shell_cmd == '!') { r_cons_grep_parsecmd (shell_cmd, "\""); olen = 0; out = NULL; // TODO: implement foo str = r_core_cmd_str (core, radare_cmd); r_sys_cmd_str_full (shell_cmd + 1, str, -1, &out, &olen, NULL); free (str); r_cons_write (out, olen); free (out); ret = 0; } #if !HAVE_FORK // nothing #elif R2__UNIX__ r_str_trim_head (radare_cmd); r_str_trim_head (shell_cmd); r_sys_signal (SIGPIPE, SIG_IGN); stdout_fd = dup (1); if (stdout_fd != -1) { if (pipe (fds) == 0) { child = r_sys_fork (); if (child == -1) { R_LOG_ERROR ("Cannot fork"); } else if (child) { dup2 (fds[1], 1); close (fds[1]); close (fds[0]); r_core_cmd (core, radare_cmd, 0); r_cons_flush (); close (1); wait (&ret); dup2 (stdout_fd, 1); } else { close (fds[1]); dup2 (fds[0], 0); //dup2 (1, 2); // stderr goes to stdout r_sandbox_system (shell_cmd, 0); } } else { R_LOG_ERROR ("Cannot pipe"); } close (stdout_fd); } #elif R2__WINDOWS__ r_w32_cmd_pipe (core, radare_cmd, shell_cmd); #else #ifdef _MSC_VER #pragma message ("r_core_cmd_pipe UNIMPLEMENTED FOR THIS PLATFORM") #else #warning r_core_cmd_pipe UNIMPLEMENTED FOR THIS PLATFORM #endif R_LOG_ERROR ("unimplemented for this platform"); #endif if (pipecolor != -1) { r_config_set_i (core->config, "scr.color", pipecolor); } r_config_set_b (core->config, "scr.interactive", si); return ret; } static char *parse_tmp_evals(RCore *core, const char *str) { char *s = strdup (str); int i, argc = r_str_split (s, ','); char *res = strdup (""); if (!s || !res) { free (s); free (res); return NULL; } for (i = 0; i < argc; i++) { char *eq, *kv = (char *)r_str_word_get0 (s, i); if (!kv) { break; } eq = strchr (kv, '='); if (eq) { *eq = 0; const char *ov = r_config_get (core->config, kv); if (!ov) { continue; } char *cmd = r_str_newf ("e %s=%s;", kv, ov); if (!cmd) { free (s); free (res); return NULL; } res = r_str_prepend (res, cmd); free (cmd); r_config_set (core->config, kv, eq + 1); *eq = '='; } else { R_LOG_ERROR ("Missing '=' in e: expression (%s)", kv); } } free (s); return res; } static bool is_macro_command(const char *ptr) { if (!strchr (ptr, ')')) { return false; } ptr = r_str_trim_head_ro (ptr); while (IS_DIGIT (*ptr)) { ptr++; } return *ptr == '('; } static char *find_ch_after_macro(char *ptr, char ch) { int depth = 0; while (*ptr) { if (depth == 0 && *ptr == ch) { return ptr; } if (*ptr == '(') { depth++; } else if (*ptr == ')') { depth--; } ptr++; } return NULL; } static int handle_command_call(RCore *core, const char *cmd) { const char cmd0 = *cmd; if (cmd0 != '\'' && cmd0 != '"') { return -1; } if (R_UNLIKELY (*cmd == '\'')) { if (cmd[1] == '@') { cmd += 2; } else { cmd++; } if (r_str_startswith (cmd, "0x")) { int res = 1; char *arg = strdup (cmd); char *end = strstr (arg, "'"); if (!end) { R_LOG_ERROR ("Invalid syntax, expected \"'@addr'command\""); free (arg); } else { *end = 0; cmd = end + 1; ut64 addr = core->offset; ut64 at = r_num_math (core->num, arg); r_core_seek (core, at, true); res = r_core_cmd_call (core, cmd); r_core_seek (core, addr, true); free (arg); } return res; } return r_core_cmd_call (core, cmd); } if (R_UNLIKELY (r_str_startswith (cmd, "\"\""))) { // R2_600 - deprecate "" -> use ' <---------- discuss! if (cmd[2] == '@') { int res = 1; char *arg = strdup (cmd + 2); char *end = strstr (arg, "\"\""); if (!end) { R_LOG_ERROR ("Invalid syntax, expected \"\"@addr\"\"command"); free (arg); } else { *end = 0; cmd = end + 2; ut64 addr = core->offset; ut64 at = r_num_math (core->num, arg + 1); r_core_seek (core, at, true); res = r_core_cmd_call (core, cmd); r_core_seek (core, addr, true); free (arg); } return res; } return r_core_cmd_call (core, cmd + 2); } return -1; } static int r_core_cmd_subst(RCore *core, char *cmd) { ut64 rep = strtoull (cmd, NULL, 10); int ret = 0, orep; char *colon = NULL, *icmd = NULL; bool tmpseek = false; bool original_tmpseek = core->tmpseek; int res = handle_command_call (core, cmd); if (res != -1) { return res; } if (R_UNLIKELY (r_str_startswith (cmd, "?t\"\""))) { char *c = r_str_newf ("?t\"\"%s", cmd + 4); // XXX char *c = r_str_newf ("?t'%s", cmd + 4); int res = r_core_cmd_call (core, c); free (c); return res; } if (R_UNLIKELY (r_str_startswith (cmd, "GET /cmd/"))) { memmove (cmd, cmd + 9, strlen (cmd + 9) + 1); char *http = strstr (cmd, "HTTP"); if (http) { *http = 0; http--; if (*http == ' ') { *http = 0; } } r_cons_printf ("HTTP/1.0 %d %s\r\n%s" "Connection: close\r\nContent-Length: %d\r\n\r\n", 200, "OK", "", -1); return r_core_cmd0 (core, cmd); } R_CRITICAL_ENTER (core); /* must store a local orig_offset because there can be * nested call of this function */ ut64 orig_offset = core->offset; icmd = strdup (cmd); if (!icmd) { goto beach; } if (core->max_cmd_depth - core->cur_cmd_depth == 1) { core->prompt_offset = core->offset; } cmd = (char *)r_str_trim_head_ro (icmd); r_str_trim_tail (cmd); R_CRITICAL_LEAVE (core); // lines starting with # are ignored (never reach cmd_hash()), except #! and #? if (R_UNLIKELY (!*cmd)) { if (core->cmdrepeat > 0) { lastcmd_repeat (core, true); ret = r_core_cmd_nullcallback (core); } goto beach; } if (!icmd || (cmd[0] == '#' && cmd[1] != '!' && cmd[1] != '?')) { goto beach; } if (*cmd) { char *hash = (char *) r_str_firstbut_escape (cmd, '#', "'\""); if (R_UNLIKELY (hash && hash != cmd)) { *hash = 0; r_str_trim_tail (cmd); } } if (*cmd != '"') { if (is_macro_command (cmd)) { colon = find_ch_after_macro (cmd, ';'); } else { colon = (char *) r_str_firstbut_escape (cmd, ';', "'\""); } if (colon) { *colon = 0; } } else { colon = NULL; } // repeat command N times if ((st64)rep > 0) { while (isdigit (*cmd)) { cmd++; } // do not repeat null cmd if (!*cmd) { goto beach; } } if ((st64)rep < 1) { rep = 1; } // XXX if output is a pipe then we don't want to be interactive if ((st64)rep > 1 && r_sandbox_enable (0)) { R_LOG_ERROR ("The command repeat syntax sugar is disabled in sandbox mode (%s)", cmd); goto beach; } if ((st64)rep > 1 && rep > INTERACTIVE_MAX_REP) { if (r_cons_is_interactive ()) { if (!r_cons_yesno ('n', "Are you sure to repeat this %"PFMT64d" times? (y/N)", rep)) { goto beach; } } } // TODO: store in core->cmdtimes to speedup ? const char *cmdrep = r_str_get (core->cmdtimes); orep = rep; bool is_root_cmd = core->cur_cmd_depth + 1 == core->max_cmd_depth; if (is_root_cmd) { r_cons_break_clear (); } r_cons_break_push (NULL, NULL); R_CRITICAL_ENTER (core); bool ocur_enabled = core->print && core->print->cur_enabled; R_CRITICAL_LEAVE (core); while (rep-- > 0 && *cmd) { if (r_cons_was_breaked ()) { break; } if (core->print) { core->print->cur_enabled = false; if (ocur_enabled && core->seltab >= 0) { if (core->seltab == core->curtab) { core->print->cur_enabled = true; } } } char *cr = strdup (cmdrep); R_CRITICAL_ENTER (core); core->break_loop = false; R_CRITICAL_LEAVE (core); if (rep > 1 && strstr (cmd, "@@")) { char *repcmd = r_str_newf ("%"PFMT64d"%s", rep + 1, cmd); ret = r_core_cmd_subst_i (core, repcmd, colon, (rep == orep - 1) ? &tmpseek : NULL); free (repcmd); rep = 0; } else { ret = r_core_cmd_subst_i (core, cmd, colon, (rep == orep - 1) ? &tmpseek : NULL); } if (*cmd == 's') { // do not restore tmpseek if the command executed is the 's'eek tmpseek = false; } if (ret && *cmd == 'q') { free (cr); goto beach; } if (core->break_loop) { free (cr); break; } if (colon) { r_cons_flush (); } if (cr && *cr && orep > 1) { // XXX: do not flush here, we need r_cons_push () and r_cons_pop() r_cons_flush (); // XXX: we must import register flags in C // r_core_cmd_subst (core, ".dr*"); // r_core_cmd_subst (core, cr); (void)r_core_cmd0 (core, ".dr*"); (void)r_core_cmd0 (core, cr); } free (cr); } r_cons_break_pop (); if (is_root_cmd) { r_cons_break_clear (); } if (tmpseek) { r_core_seek (core, orig_offset, true); core->tmpseek = original_tmpseek; } if (core->print) { core->print->cur_enabled = ocur_enabled; } if (colon && colon[1]) { for (colon++; *colon == ';'; colon++) { ; } r_core_cmd_subst (core, colon); } else { if (!*icmd) { r_core_cmd_nullcallback (core); } } beach: free (icmd); return ret; } static char *find_eoq(char *p) { for (; *p; p++) { if (*p == '"') { break; } if (*p == '\\' && p[1] == '"') { p++; } } return p; } static char* findSeparator(char *p) { char *q = strchr (p, '+'); return q? q: strchr (p, '-'); } static void tmpenvs_free(void *item) { if (item) { r_sys_setenv (item, NULL); free (item); } } static bool set_tmp_arch(RCore *core, char *arch, char **tmparch) { R_RETURN_VAL_IF_FAIL (tmparch, false); *tmparch = strdup (r_config_get (core->config, "asm.arch")); r_config_set (core->config, "asm.arch", arch); core->fixedarch = true; return true; } static bool set_tmp_bits(RCore *core, int bits, char **tmpbits, int *cmd_ignbithints) { R_RETURN_VAL_IF_FAIL (tmpbits, false); *tmpbits = strdup (r_config_get (core->config, "asm.bits")); r_config_set_i (core->config, "asm.bits", bits); core->fixedbits = true; // XXX: why? *cmd_ignbithints = r_config_get_i (core->config, "anal.ignbithints"); r_config_set_b (core->config, "anal.ignbithints", true); return true; } static char *r_core_cmd_find_subcmd_begin(char *cmd) { R_RETURN_VAL_IF_FAIL (cmd, NULL); int quote = 0; char *p; for (p = cmd; *p; p++) { if (*p == '\\') { p++; if (*p == '\'') { continue; } else if (!*p) { break; } } if (*p == '\'') { quote ^= 1; continue; } if (*p == '`' && !quote) { return p; } if (*p == '$' && p[1] == '(' && !quote) { return p; } } return NULL; } static char *r_core_cmd_find_subcmd_end(char *cmd, bool backquote) { return (char *)r_str_firstbut_escape (cmd, backquote ? '`' : ')', "'"); } static int r_core_cmd_subst_i(RCore *core, char *cmd, char *colon, bool *tmpseek) { R_CRITICAL_ENTER (core); RList *tmpenvs = r_list_newf (tmpenvs_free); const char quotestr[] = "`\"'"; const char *tick = NULL; char *ptr, *ptr2, *str; char *arroba = NULL; char *grep = NULL; RIODesc *tmpdesc = NULL; bool old_iova = r_config_get_b (core->config, "io.va"); bool pamode = (core->io? !core->io->va: false); int i, ret = 0, pipefd; bool usemyblock = false; int scr_html = -1; int scr_color = -1; bool eos = false; bool haveQuote = false; bool oldfixedarch = core->fixedarch; bool oldfixedbits = core->fixedbits; bool cmd_tmpseek = false; ut64 tmpbsz = core->blocksize; int cmd_ignbithints = -1; if (!cmd) { r_list_free (tmpenvs); return 0; } r_str_trim (cmd); R_CRITICAL_LEAVE (core); /* quoted / raw command */ switch (*cmd) { case '.': if (cmd[1] == '"') { /* interpret */ r_list_free (tmpenvs); return r_cmd_call (core->rcmd, cmd); } break; case '"': for (; *cmd; ) { int pipefd = -1; ut64 oseek = UT64_MAX; char *line, *p; haveQuote = *cmd == '"'; if (haveQuote) { cmd++; p = *cmd ? find_eoq (cmd) : NULL; if (!p || !*p) { if (!strcmp (cmd, "?")) { r_core_cmd_help (core, help_msg_quote); } else { R_LOG_ERROR ("Missing \" in (%s)", cmd); } r_list_free (tmpenvs); return false; } *p++ = 0; if (!*p) { eos = true; } } else { char *sc = strchr (cmd, ';'); if (sc) { *sc = 0; } r_core_cmd0 (core, cmd); if (!sc) { break; } cmd = sc + 1; continue; } char op0 = 0; if (*p) { // workaround :D if (p[0] == '@') { p--; } while (p[1] == ';' || IS_WHITESPACE (p[1])) { p++; } if (p[1] == '@' || (p[1] && p[2] == '@')) { char *q = strchr (p + 1, '"'); if (q) { op0 = *q; *q = 0; } haveQuote = q; oseek = core->offset; r_core_seek (core, r_num_math (core->num, p + 2), true); if (q) { *p = '"'; p = q; } else { p = strchr (p + 1, ';'); } } if (R_STR_ISNOTEMPTY (p) && p[0] != '<' && p[1] == '>') { str = p + 2; while (*str == '>') { str++; } str = (char *)r_str_trim_head_ro (str); r_cons_flush (); const bool append = p[2] == '>'; pipefd = r_cons_pipe_open (str, 1, append); } } line = strdup (cmd); line = r_str_replace (line, "\\\"", "\"", true); if (p && *p && p[1] == '|') { str = (char *)r_str_trim_head_ro (p + 2); r_core_cmd_pipe (core, cmd, str); } else { r_cmd_call (core->rcmd, line); } free (line); if (oseek != UT64_MAX) { r_core_seek (core, oseek, true); } if (pipefd != -1) { r_cons_flush (); r_cons_pipe_close (pipefd); } if (!p) { break; } if (eos) { break; } if (haveQuote) { if (*p == ';') { cmd = p + 1; } else { if (*p == '"') { cmd = p; } else { *p = op0; cmd = p; } } } else { cmd = p + 1; } } r_list_free (tmpenvs); return true; case '(': if (cmd[1] != '*' && cmd[1] != 'j' && !strstr (cmd, ")()")) { r_list_free (tmpenvs); return r_cmd_call (core->rcmd, cmd); } break; case '?': if (cmd[1] == '>') { r_core_cmd_help (core, help_msg_greater_sign); r_list_free (tmpenvs); return true; } } /* multiple commands */ if (*cmd != '#') { ptr = (char *)(is_macro_command (cmd) ? find_ch_after_macro (cmd, ';') : r_str_lastbut (cmd, ';', quotestr)); if (colon && ptr) { int ret ; *ptr = '\0'; if (r_core_cmd_subst (core, cmd) == -1) { r_list_free (tmpenvs); return -1; } cmd = ptr + 1; ret = r_core_cmd_subst (core, cmd); *ptr = ';'; r_list_free (tmpenvs); return ret; } } // TODO must honor " and ` /* pipe console to shell process */ ptr = (char *)r_str_lastbut (cmd, '|', quotestr); if (ptr) { if (ptr > cmd) { char *ch = ptr - 1; if (*ch == '\\') { memmove (ch, ptr, strlen (ptr) + 1); goto escape_pipe; } } char *ptr2 = strchr (cmd, '`'); if (!ptr2 || (ptr2 && ptr2 > ptr)) { if (!tick || (tick && tick > ptr)) { *ptr = '\0'; cmd = r_str_trim_nc (cmd); if (!strcmp (ptr + 1, "?")) { // "|?" r_core_cmd_help (core, help_msg_vertical_bar); r_list_free (tmpenvs); return ret; } else if (ptr[1] == 'D') { // "|D" char *s = r_core_cmd_str (core, cmd); int len; char *e = (char *)sdb_decode (s, &len); r_cons_printf ("%s\n", e); free (e); free (s); return 0; } else if (ptr[1] == 'E') { // "|E" char *s = r_core_cmd_str (core, cmd); char *e = sdb_encode ((const ut8*)s, strlen (s)); r_cons_printf ("%s\n", e); free (e); free (s); return 0; } else if (ptr[1] == 'J') { // "|J" same as "j:" char *ncmd = r_str_newf ("j:%s", cmd); int ret = r_core_cmd0 (core, ncmd); free (ncmd); return ret; } else if (ptr[1] == 'H') { // "|H" scr_html = r_config_get_b (core->config, "scr.html"); r_config_set_b (core->config, "scr.html", true); r_cons_context ()->tmp_html = true; r_cons_context ()->is_html = true; r_cons_context ()->was_html = scr_html; return r_core_cmd0 (core, cmd); } else if (!ptr[1] || !strcmp (ptr + 1, "T")) { // "|T" scr_html = r_config_get_b (core->config, "scr.html"); r_config_set_b (core->config, "scr.html", false); scr_color = r_config_get_i (core->config, "scr.color"); r_config_set_i (core->config, "scr.color", COLOR_MODE_DISABLED); core->cons->context->use_tts = true; } else if (!strcmp (ptr + 1, ".")) { // "|." ret = *cmd ? r_core_cmdf (core, ".%s", cmd) : 0; r_list_free (tmpenvs); return ret; } else if (ptr[1]) { // "| grep .." int value = core->num->value; if (*cmd) { r_core_cmd_pipe (core, cmd, ptr + 1); } else { char *res = r_io_system (core->io, ptr + 1); if (res) { r_cons_printf ("%s\n", res); free (res); } } r_core_return_value (core, value); r_list_free (tmpenvs); return 0; } else { // "|" scr_html = r_config_get_b (core->config, "scr.html"); r_config_set_b (core->config, "scr.html", false); scr_color = r_config_get_i (core->config, "scr.color"); r_config_set_i (core->config, "scr.color", COLOR_MODE_DISABLED); } } } } escape_pipe: // TODO must honor " and ` /* bool conditions */ ptr = (char *)r_str_lastbut (cmd, '&', quotestr); //ptr = strchr (cmd, '&'); while (ptr && *ptr && ptr[1] == '&') { *ptr = '\0'; ret = r_cmd_call (core->rcmd, cmd); if (ret == -1) { R_LOG_ERROR ("command error(%s)", cmd); if (scr_html != -1) { r_config_set_b (core->config, "scr.html", scr_html); } if (scr_color != -1) { r_config_set_i (core->config, "scr.color", scr_color); } r_list_free (tmpenvs); return ret; } for (cmd = ptr + 2; cmd && *cmd == ' '; cmd++) { ; } ptr = strchr (cmd, '&'); } ptr = strstr (cmd, "?*"); if (ptr && (ptr == cmd || ptr[-1] != '~')) { char *pipechar = strchr (ptr, '>'); if (pipechar) { *pipechar++ = 0; const bool appendResult = *pipechar == '>'; const char *pipefile = r_str_trim_head_ro (appendResult? pipechar + 1: pipechar); int pipefd = r_cons_pipe_open (pipefile, 1, appendResult); if (pipefd != -1) { int scr_color = -1; bool pipecolor = r_config_get_b (core->config, "scr.color.pipe"); if (!pipecolor) { scr_color = r_config_get_i (core->config, "scr.color"); r_config_set_i (core->config, "scr.color", COLOR_MODE_DISABLED); } ret = r_core_cmd_subst (core, cmd); r_cons_flush (); close (pipefd); r_cons_pipe_close (pipefd); if (!pipecolor) { r_config_set_i (core->config, "scr.color", scr_color); } } return ret; } ptr[0] = 0; if (*cmd != '#') { int detail = 0; if (cmd < ptr && ptr[-1] == '?') { detail++; if (cmd < ptr - 1 && ptr[-2] == '?') { detail++; } } r_cons_break_push (NULL, NULL); recursive_help (core, detail, cmd); r_cons_break_pop (); r_cons_grep_parsecmd (ptr + 2, "`"); if (scr_html != -1) { r_config_set_b (core->config, "scr.html", scr_html); } if (scr_color != -1) { r_config_set_i (core->config, "scr.color", scr_color); } r_list_free (tmpenvs); return 0; } } /* pipe console to file */ ptr = (char *)r_str_firstbut (cmd, '>', "\""); // TODO honor ` if (ptr != NULL && ptr + 2 > cmd) { // Handle ~<> char *prev = ptr - 2; if (r_str_startswith (prev, "~<>")) { ptr = NULL; } } int fdn = 1; char *next_redirect = NULL; if (ptr) { if (ptr > cmd) { char *ch = ptr - 1; if (*ch == '\\') { memmove (ch, ptr, strlen (ptr) + 1); goto escape_redir; } } if (ptr[0] && ptr[1] == '?') { r_core_cmd_help (core, help_msg_greater_sign); r_list_free (tmpenvs); return true; } bool pipecolor = r_config_get_b (core->config, "scr.color.pipe"); bool use_editor = false; int ocolor = r_config_get_i (core->config, "scr.color"); *ptr = '\0'; r_cons_set_interactive (false); repeat:; str = ptr + 1 + (ptr[1] == '>'); r_str_trim (str); if (!*str) { R_LOG_ERROR ("No output?"); goto next2; } fdn = 1; /* r_cons_flush() handles interactive output (to the terminal) * differently (e.g. asking about too long output). This conflicts * with piping to a file. Disable it while piping. */ // note that 'x>a' is not working .. but 'x > a' or 'x >a' is valid bool redirect_check = (ptr > cmd && (!ptr[-1] || !ptr[-2] || IS_WHITECHAR (ptr[-2]))); if (redirect_check) { // R2R db/cmd/cmd_macros R_LOG_DEBUG ("FD FROM (%s)", ptr - 1); char *fdnum = ptr - 1; if (*fdnum == 'H') { // "H>" scr_html = r_cons_context ()->is_html; r_config_set_b (core->config, "scr.html", true); pipecolor = true; *fdnum = 0; } else { if (IS_DIGIT (*fdnum)) { fdn = *fdnum - '0'; } *fdnum = 0; } } R_LOG_DEBUG ("FD %d", fdn); if (!strcmp (str, "-")) { use_editor = true; str = r_file_temp ("dumpedit"); r_config_set_i (core->config, "scr.color", COLOR_MODE_DISABLED); } char *nextgt = strchr (r_str_trim_head_ro (ptr + 1), '>'); if (nextgt && nextgt[0] != '>') { char *back = ptr + 1; while (nextgt > back) { if (!isdigit (*nextgt) && *nextgt != 'H') { break; } nextgt--; } next_redirect = nextgt; while (nextgt > back) { if (*nextgt == ' ') { *nextgt = 0; break; } nextgt--; } } else { next_redirect = NULL; } // eprintf ("---> (%s)\n", ptr + 1); // eprintf ("next (%s)\n", next_redirect); const bool appendResult = (ptr[1] == '>'); if (*str == '$' && !str[1]) { R_LOG_ERROR ("No alias name given"); } else if (*str == '$') { // pipe to alias variable // register output of command as an alias r_config_set_i (core->config, "scr.color", COLOR_MODE_DISABLED); RBuffer *cmd_out = r_core_cmd_tobuf (core, cmd); if (cmd_out) { int alias_len; ut8 *alias_data = r_buf_read_all (cmd_out, &alias_len); const char *arg = r_str_trim_head_ro (str + 1); if (appendResult) { if (!r_cmd_alias_append_raw (core->rcmd, arg, alias_data, alias_len)) { R_LOG_INFO ("Alias '$%s' is a command - will not attempt to append", arg); } } else { r_cmd_alias_set_raw (core->rcmd, arg, alias_data, alias_len); } ret = 0; r_buf_free (cmd_out); free (alias_data); } } else if (fdn > 0) { // pipe to file (or append) pipefd = r_cons_pipe_open (str, fdn, appendResult); if (pipefd == -1) { // R_LOG_ERROR ("Cannot open pipe with fd %d", fdn); // goto errorout; } *str = 0; if (next_redirect) { ptr = next_redirect; *next_redirect = ' '; next_redirect = NULL; goto repeat; } if (!pipecolor) { r_config_set_i (core->config, "scr.color", COLOR_MODE_DISABLED); } ret = r_core_cmd_subst (core, cmd); r_cons_flush (); } if (!pipecolor) { r_config_set_i (core->config, "scr.color", ocolor); } if (use_editor) { const char *editor = r_config_get (core->config, "cfg.editor"); if (R_STR_ISNOTEMPTY (editor)) { r_sys_cmdf ("%s '%s'", editor, str); r_file_rm (str); } else { R_LOG_ERROR ("No value defined for cfg.editor"); } r_config_set_i (core->config, "scr.color", ocolor); free (str); } if (scr_html != -1) { r_config_set_b (core->config, "scr.html", scr_html); } if (scr_color != -1) { r_config_set_i (core->config, "scr.color", scr_color); } core->cons->context->use_tts = false; r_list_free (tmpenvs); r_cons_pipe_close_all (); r_cons_set_last_interactive (); return ret; } escape_redir: next2: /* sub commands */ ptr = r_core_cmd_find_subcmd_begin (cmd); if (R_UNLIKELY (ptr)) { bool backquote = false; if (*ptr == '`') { backquote = true; } if (ptr > cmd) { char *ch = ptr - 1; if (*ch == '\\') { memmove (ch, ptr, strlen (ptr) + 1); goto escape_backtick; } } if (!backquote) { memmove (ptr + 1, ptr + 2, strlen (ptr) - 1); } if ((ptr[1] == '`' && backquote) || (ptr[1] == ')' && !backquote)) { memmove (ptr, ptr + 2, strlen (ptr) - 1); goto escape_backtick; } ptr2 = r_core_cmd_find_subcmd_end (ptr + 1, backquote); if (!ptr2) { R_LOG_ERROR ("parse: Missing sub-command closing in expression"); goto fail; } else { int value = core->num->value; *ptr = '\0'; *ptr2 = '\0'; if (ptr[1] == '!') { str = r_core_cmd_str_pipe (core, ptr + 1); } else { // Color disabled when doing backticks ?e `pi 1` int ocolor = r_config_get_i (core->config, "scr.color"); r_config_set_i (core->config, "scr.color", 0); str = r_core_cmd_str (core, ptr + 1); r_config_set_i (core->config, "scr.color", ocolor); } if (!str) { goto fail; } // ignore contents if first char is pipe or comment if (*str == '|' || *str == '*') { R_LOG_ERROR ("invalid sub-command"); free (str); goto fail; } if (str) { r_str_replace_ch (str, '\n', ' ', true); } str = r_str_append (str, ptr2 + 1); cmd = r_str_append (strdup (cmd), str); r_core_return_value (core, value); ret = r_core_cmd_subst (core, cmd); free (cmd); if (scr_html != -1) { r_config_set_b (core->config, "scr.html", scr_html); } free (str); r_list_free (tmpenvs); return ret; } } escape_backtick: // TODO must honor " and ` if (*cmd != '"' && *cmd) { const char *s = strstr (cmd, "~?"); if (R_UNLIKELY (s)) { bool showHelp = false; if (cmd == s) { // ~? // ~?? showHelp = true; } else { // pd~? // pd~?? if (!strcmp (s, "~??")) { showHelp = true; } } if (showHelp) { r_cons_grep_help (); r_list_free (tmpenvs); return true; } } } if (*cmd != '.') { grep = r_cons_grep_strip (cmd, quotestr); } /* temporary seek commands */ if (*cmd != '"') { ptr = (char *)r_str_firstbut_escape (cmd, '@', "\"'"); if (ptr == cmd + 1 && *cmd == '?') { ptr = NULL; } } else { ptr = NULL; } cmd_tmpseek = core->tmpseek = ptr; int rc = 0; if (ptr) { char *f, *ptr2 = strchr (ptr + 1, '!'); ut64 addr = core->offset; bool addr_is_set = false; char *tmpbits = NULL; const char *offstr = NULL; bool is_bits_set = false; bool is_arch_set = false; char *tmpeval = NULL; char *tmpasm = NULL; bool flgspc_changed = false; int tmpfd = -1; size_t sz; int len; ut8 *buf; *ptr++ = '\0'; repeat_arroba: arroba = (ptr[0] && ptr[1] && ptr[2])? strchr (ptr + 2, '@'): NULL; if (r_str_startswith (ptr, "@@@")) { // "@@@@" R_LOG_ERROR ("Cannot iterate that much"); goto fuji; } if (arroba) { *arroba = 0; } for (; *ptr == ' '; ptr++) { //nothing to see here } if (*ptr && ptr[1] == ':') { /* do nothing here */ } else { ptr--; } r_str_trim_tail (ptr); if (ptr[1] == '?') { r_core_cmd_help (core, help_msg_at); } else if (ptr[1] == '%') { // "@%" char *k = strdup (ptr + 2); char *v = strchr (k, '='); if (v) { *v++ = 0; r_str_trim (k); r_str_trim (v); if (*k) { char *last = k + strlen (k) - 1; if (*last == '%') { *last = 0; r_str_trim (k); } r_sys_setenv (k, v); r_list_append (tmpenvs, k); } } free (k); } else if (ptr[1] == '.') { // "@." if (ptr[2] == '.') { // "@.." if (ptr[3] == '.') { // "@..." ut64 addr = r_num_tail (core->num, core->offset, ptr + 4); r_core_block_size (core, R_ABS ((st64)addr - (st64)core->offset)); goto fuji; } else { addr = r_num_tail (core->num, core->offset, ptr + 3); r_core_seek (core, addr, true); cmd_tmpseek = core->tmpseek = true; goto fuji; } } else { // WAT DU R_LOG_TODO ("what do you expect for @. import offset from file maybe?"); } } else if (ptr[0] && ptr[1] == ':' && ptr[2]) { switch (ptr[0]) { case 'F': // "@F:" // temporary flag space flgspc_changed = r_flag_space_push (core->flags, ptr + 2); break; case 'B': // "@B:#" // seek to the last instruction in current bb { int index = (int)r_num_math (core->num, ptr + 2); RAnalBlock *bb = r_anal_bb_from_offset (core->anal, core->offset); if (bb) { // handle negative indices if (index < 0) { index = bb->ninstr + index; } if (index >= 0 && index < bb->ninstr) { ut16 inst_off = r_anal_bb_offset_inst (bb, index); r_core_seek (core, bb->addr + inst_off, true); cmd_tmpseek = core->tmpseek = true; } else { R_LOG_INFO ("The current basic block has %d instructions", bb->ninstr); } } else { R_LOG_ERROR ("Can't find a basic block for 0x%08"PFMT64x, core->offset); } break; } break; case 'f': // "@f:" // slurp file in block f = r_file_slurp (ptr + 2, &sz); if (f) { { RBuffer *b = r_buf_new_with_bytes ((const ut8*)f, (ut64)sz); RIODesc *d = r_io_open_buffer (core->io, b, R_PERM_RWX, 0); if (d) { if (tmpdesc) { r_io_desc_close (tmpdesc); } tmpdesc = d; if (pamode) { r_config_set_b (core->config, "io.va", true); } r_io_map_add (core->io, d->fd, d->perm, 0, core->offset, r_buf_size (b)); } } } else { R_LOG_ERROR ("cannot open '%s'", ptr + 3); } break; case 'r': // "@r:" // regname if (ptr[1] == '{') { // @r{PC} ut64 regval; char *mander = strdup (ptr + 2); char *brace = strchr (mander, '}'); if (brace) { *brace = 0; } char *sep = findSeparator (mander); if (sep) { char ch = *sep; *sep = 0; regval = r_debug_reg_get (core->dbg, mander); *sep = ch; char *numexpr = r_str_newf ("0x%"PFMT64x"%s", regval, sep); regval = r_num_math (core->num, numexpr); free (numexpr); } else { regval = r_debug_reg_get (core->dbg, ptr + 2); } r_core_seek (core, regval, true); cmd_tmpseek = core->tmpseek = true; free (mander); } else if (ptr[1] == ':') { // @r:PC ut64 regval; char *mander = strdup (ptr + 2); char *sep = findSeparator (mander); if (sep) { char ch = *sep; *sep = 0; regval = r_debug_reg_get (core->dbg, mander); *sep = ch; char *numexpr = r_str_newf ("0x%"PFMT64x"%s", regval, sep); regval = r_num_math (core->num, numexpr); free (numexpr); } else { regval = r_debug_reg_get (core->dbg, ptr + 2); } r_core_seek (core, regval, true); cmd_tmpseek = core->tmpseek = true; free (mander); } break; case 'b': // "@b:" // bits is_bits_set = set_tmp_bits (core, r_num_math (core->num, ptr + 2), &tmpbits, &cmd_ignbithints); break; case 'i': // "@i:" { ut64 addr = r_num_math (core->num, ptr + 2); if (addr) { r_core_cmdf (core, "so %s", ptr + 2); cmd_tmpseek = core->tmpseek = true; } } break; case 'c': // "@c:" { char *s = r_core_cmd_str (core, ptr + 2); if (*s) { ut64 addr = r_num_math (core->num, s); if (core->num->nc.errors == 0) { r_core_seek (core, addr, true); cmd_tmpseek = core->tmpseek = true; } } free (s); } break; case 'e': // "@e:" { char *cmd = parse_tmp_evals (core, ptr + 2); if (!tmpeval) { tmpeval = cmd; } else { tmpeval = r_str_prepend (tmpeval, cmd); free (cmd); } } break; case 'v': // "@v:" // value (honors asm.bits and cfg.bigendian) if (ptr[1] == ':') { ut8 buf[8] = {0}; ut64 v = r_num_math (core->num, ptr + 2); int be = r_config_get_i (core->config, "cfg.bigendian"); int bi = r_config_get_i (core->config, "asm.bits"); if (bi == 64) { r_write_ble64 (buf, v, be); len = 8; } else { r_write_ble32 (buf, v, be); len = 4; } tmpfd = r_io_fd_get_current(core->io); r_core_block_size (core, R_ABS (len)); RBuffer *b = r_buf_new_with_bytes (buf, len); RIODesc *d = r_io_open_buffer (core->io, b, R_PERM_RWX, 0); if (d) { if (tmpdesc) { r_io_desc_close (tmpdesc); } tmpdesc = d; if (pamode) { r_config_set_b (core->config, "io.va", true); } r_io_map_add (core->io, d->fd, d->perm, 0, core->offset, r_buf_size (b)); r_core_block_size (core, len); r_core_block_read (core); } } else { R_LOG_ERROR ("Invalid @v: syntax"); } break; case 'x': // "@x:" // hexpairs if (ptr[1] == ':') { buf = malloc (strlen (ptr + 2) + 1); if (buf) { len = r_hex_str2bin (ptr + 2, buf); r_core_block_size (core, R_ABS (len)); if (len > 0) { RBuffer *b = r_buf_new_with_bytes (buf, len); RIODesc *d = r_io_open_buffer (core->io, b, R_PERM_RWX, 0); if (d) { if (tmpdesc) { r_io_desc_close (tmpdesc); } tmpdesc = d; if (pamode) { r_config_set_b (core->config, "io.va", true); } r_io_map_add (core->io, d->fd, d->perm, 0, core->offset, r_buf_size (b)); r_core_block_size (core, len); r_core_block_read (core); } } else { R_LOG_ERROR ("Invalid hexpairs for @x:"); } free (buf); } else { R_LOG_ERROR ("cannot allocate"); } } else { R_LOG_ERROR ("Invalid @x: syntax"); } break; case 'k': // "@k" { char *out = sdb_querys (core->sdb, NULL, 0, ptr + ((ptr[1])? 2: 1)); if (out) { r_core_seek (core, r_num_math (core->num, out), true); free (out); usemyblock = true; } } break; case 'o': // "@o:3" if (ptr[1] == ':') { tmpfd = core->io->desc ? core->io->desc->fd : -1; r_io_use_fd (core->io, atoi (ptr + 2)); } break; case 'a': // "@a:" if (ptr[1] == ':') { char *q = strchr (ptr + 2, ':'); if (q) { *q++ = 0; int bits = r_num_math (core->num, q); is_bits_set = set_tmp_bits (core, bits, &tmpbits, &cmd_ignbithints); } is_arch_set = set_tmp_arch (core, ptr + 2, &tmpasm); } else { r_core_cmd_help_match (core, help_msg_at, "@a:"); } break; case 's': // "@s:" // wtf syntax { len = strlen (ptr + 2); r_core_block_size (core, len); const ut8 *buf = (const ut8*)r_str_trim_head_ro (ptr + 2); if (len > 0) { RBuffer *b = r_buf_new_with_bytes (buf, len); RIODesc *d = r_io_open_buffer (core->io, b, R_PERM_RWX, 0); if (!core->io->va) { r_config_set_b (core->config, "io.va", true); } if (d) { if (tmpdesc) { r_io_desc_close (tmpdesc); } tmpdesc = d; if (pamode) { r_config_set_b (core->config, "io.va", true); } r_io_map_add (core->io, d->fd, d->perm, 0, core->offset, r_buf_size (b)); r_core_block_size (core, len); // r_core_block_read (core); } } } break; default: goto ignore; } *ptr = '@'; /* trim whitespaces before the @ */ /* Fixes pd @x:9090 */ char *trim = ptr - 2; while (trim > cmd) { if (!IS_WHITESPACE (*trim)) { break; } *trim = 0; trim--; } goto next_arroba; } ignore: r_str_trim_head (ptr + 1); cmd = r_str_trim_nc (cmd); if (ptr2) { if (strlen (ptr + 1) == 13 && strlen (ptr2 + 1) == 6 && r_str_startswith (ptr + 1, "0x") && r_str_startswith (ptr2 + 1, "0x")) { /* 0xXXXX:0xYYYY */ } else if (strlen (ptr + 1) == 9 && strlen (ptr2 + 1) == 4) { /* XXXX:YYYY */ } else { *ptr2 = '\0'; if (!ptr2[1]) { goto fail; } r_core_block_size (core, r_num_math (core->num, ptr2 + 1)); } } r_str_trim_head (ptr + 1); offstr = ptr + 1; addr = (*offstr == '{')? core->offset: r_num_math (core->num, offstr); addr_is_set = true; if (isalpha ((ut8)ptr[1]) && !addr) { if (!r_flag_get (core->flags, ptr + 1)) { R_LOG_ERROR ("Invalid address (%s)", ptr + 1); goto fail; } } else { char ch = *offstr; if (ch == '-' || ch == '+') { addr = core->offset + addr; } } // remap thhe tmpdesc if any if (addr) { RIODesc *d = tmpdesc; if (d) { r_io_map_add (core->io, d->fd, d->perm, 0, addr, r_io_desc_size (d)); } } next_arroba: if (arroba) { ptr = arroba + 1; *arroba = '@'; arroba = NULL; goto repeat_arroba; } core->fixedblock = !!tmpdesc; if (core->fixedblock) { r_core_block_read (core); } if (ptr[1] == '@') { // "@@" if (ptr[2] == '@') { // "@@@" char *rule = ptr + 3; while (*rule && *rule == ' ') { rule++; } ret = r_core_cmd_foreach3 (core, cmd, rule); } else { ret = r_core_cmd_foreach (core, cmd, ptr + 2); } } else { bool tmpseek = false; const char *fromvars[] = { "anal.from", "diff.from", "graph.from", "search.from", "zoom.from", NULL }; const char *tovars[] = { "anal.to", "diff.to", "graph.to", "search.to", "zoom.to", NULL }; ut64 curfrom[R_ARRAY_SIZE (fromvars) - 1], curto[R_ARRAY_SIZE (tovars) - 1]; // "@{A B}" if (ptr[1] == '{') { char *range = ptr + 2; char *p = strchr (range, ' '); if (!p) { r_core_cmd_help_match (core, help_msg_at, "@{"); free (tmpeval); free (tmpasm); free (tmpbits); goto fail; } char *arg = p + 1; int arg_len = strlen (arg); if (arg_len > 0) { arg[arg_len - 1] = 0; } *p = '\x00'; ut64 from = r_num_math (core->num, range); ut64 to = r_num_math (core->num, arg); if (from >= to) { R_LOG_WARN ("Invalid @{from to} range"); } // save current ranges for (i = 0; fromvars[i]; i++) { curfrom[i] = r_config_get_i (core->config, fromvars[i]); } for (i = 0; tovars[i]; i++) { curto[i] = r_config_get_i (core->config, tovars[i]); } // set new ranges for (i = 0; fromvars[i]; i++) { r_config_set_i (core->config, fromvars[i], from); } for (i = 0; tovars[i]; i++) { r_config_set_i (core->config, tovars[i], to); } tmpseek = true; #if 0 // TODO may not work well for search commands XXX r_core_seek (core, from, true); r_core_block_size (core, to - from); #endif } if (usemyblock) { if (addr_is_set) { core->offset = addr; } } else { if (addr_is_set && ptr[1]) { r_core_seek (core, addr, true); r_core_block_read (core); } } ret = r_cmd_call (core->rcmd, r_str_trim_head_ro (cmd)); if (tmpseek) { // restore ranges for (i = 0; fromvars[i]; i++) { r_config_set_i (core->config, fromvars[i], curfrom[i]); } for (i = 0; tovars[i]; i++) { r_config_set_i (core->config, tovars[i], curto[i]); } } } if (ptr2) { *ptr2 = '!'; r_core_block_size (core, tmpbsz); } if (is_arch_set) { core->fixedarch = oldfixedarch; r_config_set (core->config, "asm.arch", tmpasm); R_FREE (tmpasm); } if (tmpdesc) { if (pamode) { r_config_set_b (core->config, "io.va", old_iova); } r_io_desc_close (tmpdesc); tmpdesc = NULL; } if (tmpfd != -1) { // TODO: reuse tmpfd instead of r_io_use_fd (core->io, tmpfd); tmpfd = -1; } if (is_bits_set) { r_config_set (core->config, "asm.bits", tmpbits); core->fixedbits = oldfixedbits; } if (tmpbsz != core->blocksize) { r_core_block_size (core, tmpbsz); } if (tmpeval) { r_core_cmd0 (core, tmpeval); R_FREE (tmpeval); } if (flgspc_changed) { r_flag_space_pop (core->flags); } *ptr = '@'; rc = ret; free (tmpbits); goto beach; } fuji: if (cmd) { r_str_trim_head (cmd); rc = r_cmd_call (core->rcmd, cmd); } else { rc = 0; } if (rc == 1) { r_core_return_value (core, rc); } beach: if (grep) { char *old_grep = grep; grep = unescape_special_chars (old_grep, SPECIAL_CHARS); free (old_grep); r_cons_grep_expression (grep); free (grep); } if (scr_html != -1) { r_cons_flush (); r_config_set_i (core->config, "scr.html", scr_html); } if (scr_color != -1) { r_config_set_i (core->config, "scr.color", scr_color); } r_list_free (tmpenvs); if (tmpdesc) { if (pamode) { r_config_set_b (core->config, "io.va", old_iova); } r_io_desc_close (tmpdesc); tmpdesc = NULL; } if (tmpseek) { *tmpseek = cmd_tmpseek; } if (cmd_ignbithints != -1) { r_config_set_i (core->config, "anal.ignbithints", cmd_ignbithints); } return rc; fail: rc = -1; if (tmpdesc) { if (pamode) { r_config_set_b (core->config, "io.va", old_iova); } r_io_desc_close (tmpdesc); tmpdesc = NULL; } goto beach; } struct exec_command_t { RCore *core; const char *cmd; }; typedef struct { char *name; ut64 addr; ut64 size; } ForeachListItem; static void foreach3list_free(void* u) { ForeachListItem *fli = (ForeachListItem*)u; free (fli->name); free (fli); } static void append_item(RList *list, const char *name, ut64 addr, ut64 size) { ForeachListItem *fli = R_NEW0 (ForeachListItem); if (fli) { if (name) { fli->name = strdup (name); } fli->addr = addr; fli->size = size; r_list_append (list, fli); } } static bool copy_into_flagitem_list(RFlagItem *item, void *u) { RList *list = (RList*)u; append_item (list, item->name, item->offset, item->size); return true; } static void foreach_pairs(RCore *core, const char *cmd, const char *each) { const char *arg; int pair = 0; for (arg = each ; ; ) { char *next = strchr (arg, ' '); if (next) { *next = 0; } if (arg && *arg) { ut64 n = r_num_get (NULL, arg); if (pair % 2) { r_core_block_size (core, n); r_core_cmd0 (core, cmd); } else { r_core_seek (core, n, true); } pair++; } if (!next) { break; } arg = next + 1; } } static RList *foreach3list(RCore *core, char type, const char *glob) { bool va = r_config_get_b (core->config, "io.va"); RList *list = r_list_newf (foreach3list_free); RListIter *iter; int i; switch (type) { default: R_LOG_DEBUG ("Unhandled CL subcommand '%c'", type); break; case '?': // eprintf ("HALP\n"); break; case 'C': // "@@@C" { RIntervalTreeIter it; RAnalMetaItem *meta; r_interval_tree_foreach (&core->anal->meta, it, meta) { if (meta->type != R_META_TYPE_COMMENT) { continue; } if (!glob || (meta->str && r_str_glob (meta->str, glob))) { ut64 addr = r_interval_tree_iter_get (&it)->start; append_item (list, NULL, addr, UT64_MAX); } } } break; case 'm': // "@@@m" { int fd = r_io_fd_get_current (core->io); // only iterate maps of current fd RList *maps = r_io_map_get_by_fd (core->io, fd); RIOMap *map; if (maps) { RListIter *iter; r_list_foreach (maps, iter, map) { append_item (list, NULL, r_io_map_begin (map), r_io_map_size (map)); } r_list_free (maps); } } break; case 'M': // @@@M if (core->dbg && core->dbg->current && core->dbg->maps) { RDebugMap *map; r_list_foreach (core->dbg->maps, iter, map) { append_item (list, NULL, map->addr, map->size); } } break; case 'e': // @@@e { RBinAddr *entry; const RList *elist = r_bin_get_entries (core->bin); r_list_foreach (elist, iter, entry) { ut64 addr = va? entry->vaddr: entry->paddr; append_item (list, NULL, addr, UT64_MAX); } // NOUAF r_list_free (elist); } break; case 't': // @@@t { RDebugPlugin *plugin = R_UNWRAP3 (core->dbg, current, plugin); // iterate over all threads if (plugin && plugin->threads) { RDebugPid *p; RList *thlist = plugin->threads (core->dbg, core->dbg->pid); r_list_foreach (thlist, iter, p) { append_item (list, NULL, (ut64)p->pid, UT64_MAX); } r_list_free (thlist); } } break; case 'i': // @@@i { RBinImport *imp; const RList *implist = r_bin_get_imports (core->bin); r_list_foreach (implist, iter, imp) { const char *name = r_bin_name_tostring (imp->name); char *impflag = r_str_newf ("sym.imp.%s", name); ut64 addr = r_num_math (core->num, impflag); if (addr != 0 && addr != UT64_MAX) { append_item (list, NULL, addr, UT64_MAX); } free (impflag); } } break; case 'E': { RBinSymbol *sym; RVecRBinSymbol *symbols = r_bin_get_symbols_vec (core->bin); const bool va = r_config_get_b (core->config, "io.va"); R_VEC_FOREACH (symbols, sym) { if (!isAnExport (sym)) { continue; } ut64 addr = va? sym->vaddr: sym->paddr; append_item (list, NULL, addr, UT64_MAX); } } break; case 's': // @@@s symbols { RBinSymbol *sym; RVecRBinSymbol *symbols = r_bin_get_symbols_vec (core->bin); R_VEC_FOREACH (symbols, sym) { ut64 addr = va? sym->vaddr: sym->paddr; append_item (list, NULL, addr, sym->size); } } break; case 'S': // "@@@S" { RList *sections = r_bin_get_sections (core->bin); if (sections) { RBinSection *sec; r_list_foreach (sections, iter, sec) { if (sec->is_segment) { continue; } ut64 addr = va ? sec->vaddr: sec->paddr; ut64 size = va ? sec->vsize: sec->size; append_item (list, NULL, addr, size); } } } break; case 'G': // "@@@G" // @@@SS - seGments { RList *sections = r_bin_get_sections (core->bin); if (sections) { RBinSection *sec; r_list_foreach (sections, iter, sec) { if (!sec->is_segment) { continue; } ut64 addr = va ? sec->vaddr: sec->paddr; ut64 size = va ? sec->vsize: sec->size; append_item (list, NULL, addr, size); } } } break; case 'z': { RList *zlist = r_bin_get_strings (core->bin); if (zlist) { RBinString *s; r_list_foreach (zlist, iter, s) { ut64 addr = va? s->vaddr: s->paddr; append_item (list, NULL, addr, s->size); } } } break; case 'b': // "@@@b" { RAnalFunction *fcn = r_anal_get_fcn_in (core->anal, core->offset, 0); if (fcn) { RListIter *iter; RAnalBlock *bb; r_list_foreach (fcn->bbs, iter, bb) { append_item (list, NULL, bb->addr, bb->size); } } } break; case 'F': // "@@@F" { RAnalFunction *fcn; r_list_foreach (core->anal->fcns, iter, fcn) { if (!glob || r_str_glob (fcn->name, glob)) { ut64 size = r_anal_function_linear_size (fcn); append_item (list, NULL, fcn->addr, size); } } } break; case 'R': // "@@@R" relocs { RRBTree *rels = r_bin_get_relocs (core->bin); if (rels) { RRBNode *node = r_crbtree_first_node (rels); while (node) { RBinReloc *rel = (RBinReloc *)node->data; ut64 addr = va? rel->vaddr: rel->paddr; append_item (list, NULL, addr, UT64_MAX); node = r_rbnode_next (node); } } } break; case 'r': // "@@@r" registers { const int bits = core->anal->config->bits; for (i = 0; i < R_REG_TYPE_LAST; i++) { RRegItem *item; RList *head = r_reg_get_list (core->dbg->reg, i); r_list_foreach (head, iter, item) { if (item->size != bits) { continue; } if (item->type != i) { continue; } ut64 addr = r_reg_getv (core->dbg->reg, item->name); append_item (list, item->name, addr, item->size); } } } break; case 'f': // "@@@f" r_flag_foreach_glob (core->flags, glob, copy_into_flagitem_list, list); break; } return list; } R_API int r_core_cmd_foreach3(RCore *core, const char *cmd, char *each) { // "@@@" ForeachListItem *item; RListIter *iter; char ch = each[0]; if (r_str_startswith (each, "SS")) { ch = 'G'; // @@@SS = @@@G } char *glob = (each[0] && each[1] == ':') ? r_str_trim_dup (each + 2) : NULL; RList *list = foreach3list (core, ch, glob); switch (ch) { case '=': // "@@@=" foreach_pairs (core, cmd, each + 1); break; case '?': // "@@@?" r_core_cmd_help (core, help_msg_at_at_at); break; case 'c': // "@@@c" if (glob) { char *arg = r_core_cmd_str (core, glob); if (arg) { foreach_pairs (core, cmd, arg); free (arg); } } else { r_core_cmd_help (core, help_msg_at_at_at); } break; case 'C': case 's': case 'm': case 'M': case 'e': case 'E': case 'f': case 'F': case 'b': case 'z': case 'R': case 'S': case 'G': case 'r': case 'i': { ut64 offorig = core->offset; ut64 bszorig = core->blocksize; r_cons_break_push (NULL, NULL); r_list_foreach (list, iter, item) { if (r_cons_is_breaked ()) { break; } if (item->addr == UT64_MAX) { continue; } if (item->name) { r_cons_printf ("%s: ", item->name); } r_core_seek (core, item->addr, true); if (item->size) { r_core_block_size (core, item->size); } r_core_cmd0 (core, cmd); if (!foreach_newline (core)) { break; } } r_core_seek (core, offorig, true); r_core_block_size (core, bszorig); r_cons_break_pop (); } break; case 't': // TODO: generalize like the rest, just call dp before and after { int origpid = core->dbg->pid; r_list_foreach (list, iter, item) { int curpid = (int) item->addr; r_core_cmdf (core, "dp %d", curpid); r_cons_printf ("# PID %d\n", curpid); r_core_cmd0 (core, cmd); if (!foreach_newline (core)) { break; } } r_core_cmdf (core, "dp %d", origpid); } break; case 0: R_LOG_INFO ("Nothing to repeat. Check @@@?"); break; case '@': // "@@@@" R_LOG_WARN ("I can't iterate that much!"); break; default: R_LOG_ERROR ("Invalid repeat type, Check @@@? for help"); break; } r_list_free (list); free (glob); return 0; } static void foreachWord(RCore *core, const char *_cmd, const char *each) { char *cmd = strdup (_cmd); char *nextLine = NULL; /* foreach list of items */ while (each) { // skip spaces each = r_str_trim_head_ro (each); // stahp on empty string if (!*each) { break; } // find newline char *nl = strchr (each, '\n'); if (nl) { *nl = 0; nextLine = nl + 1; } else { nextLine = NULL; } // chop comment in line nl = strchr (each, '#'); if (nl) { *nl = 0; } // space separated numbers while (R_STR_ISNOTEMPTY (each)) { each = r_str_trim_head_ro (each); char *curword = NULL; char *str = strchr (each, ' '); if (str) { *str = '\0'; curword = strdup (each); *str = ' '; each = str + 1; } else { if (!*each) { break; } curword = strdup (each); each = NULL; } r_core_cmdf (core, "%s %s", cmd, curword); R_FREE (curword); if (!foreach_newline (core)) { break; } r_cons_flush (); } each = nextLine; } free (cmd); } static void foreachOffset(RCore *core, const char *_cmd, const char *each) { char *cmd = strdup (_cmd); char *nextLine = NULL; ut64 addr; /* foreach list of items */ while (R_STR_ISNOTEMPTY (each)) { each = r_str_trim_head_ro (each); // stahp if empty string if (!*each) { break; } // find newline char *nl = strchr (each, '\n'); if (nl) { *nl = 0; nextLine = nl + 1; } else { nextLine = NULL; } // chop comment in line nl = strchr (each, '#'); if (nl) { *nl = 0; } // space separated numbers while (R_STR_ISNOTEMPTY (each)) { // find spaces while (*each == ' ') { each++; } char *str = strchr (each, ' '); if (str) { *str = '\0'; addr = r_num_math (core->num, each); *str = ' '; each = str + 1; } else { if (!*each) { break; } addr = r_num_math (core->num, each); each = NULL; } r_core_seek (core, addr, true); r_core_cmd (core, cmd, 0); foreach_newline (core); r_cons_flush (); } each = nextLine; } free (cmd); } R_API int r_core_cmd_foreach(RCore *core, const char *cmd, char *each) { int i, j; char ch; char *word = NULL; char *str, *ostr = NULL; RListIter *iter; RFlagItem *flag; ut64 oseek, addr; cmd = r_str_trim_head_ro (cmd); oseek = core->offset; ostr = str = strdup (each); r_cons_break_push (NULL, NULL); //pop on return switch (each[0]) { case '/': // "@@/" { char *cmdhit = strdup (r_config_get (core->config, "cmd.hit")); r_config_set (core->config, "cmd.hit", cmd); r_core_cmd0 (core, each); r_config_set (core->config, "cmd.hit", cmdhit); free (cmdhit); } free (ostr); return 0; case 0: R_LOG_ERROR ("Nothing to repeat. Check @@?"); break; case '?': // "@@?" r_core_cmd_help (core, help_msg_at_at); break; case 'b': // "@@b" - function basic blocks { RListIter *iter; RAnalBlock *bb; RAnalFunction *fcn = r_anal_get_function_at (core->anal, core->offset); int bs = core->blocksize; if (fcn) { r_list_sort (fcn->bbs, bb_cmp); r_list_foreach (fcn->bbs, iter, bb) { r_core_block_size (core, bb->size); r_core_seek (core, bb->addr, true); r_core_cmd (core, cmd, 0); if (!foreach_newline (core)) { break; } } } r_core_block_size (core, bs); goto out_finish; } break; case 's': // "@@s" - sequence { char *str = each + 1; if (*str == ':' || *str == ' ') { str++; } int count = r_str_split (str, ' '); if (count == 3) { ut64 cur; ut64 from = r_num_math (core->num, r_str_word_get0 (str, 0)); ut64 to = r_num_math (core->num, r_str_word_get0 (str, 1)); ut64 step = r_num_math (core->num, r_str_word_get0 (str, 2)); for (cur = from; cur <= to; cur += step) { (void) r_core_seek (core, cur, true); r_core_cmd (core, cmd, 0); if (!foreach_newline (core)) { break; } } } else { R_LOG_ERROR ("Use the sequence iterator like this: 'cmd @@s:from to step'"); } goto out_finish; } break; case 'i': // "@@i" - function instructions { RListIter *iter; RAnalBlock *bb; int i; RAnalFunction *fcn = r_anal_get_function_at (core->anal, core->offset); SetU *set = set_u_new (); if (fcn) { r_list_sort (fcn->bbs, bb_cmp); r_list_foreach (fcn->bbs, iter, bb) { r_core_seek (core, bb->addr, true); r_core_cmd (core, cmd, 0); for (i = 0; i < bb->op_pos_size; i++) { if (!bb->op_pos[i]) { break; } ut64 addr = bb->addr + bb->op_pos[i]; if (set_u_contains (set, addr)) { continue; } r_core_seek (core, addr, true); r_core_cmd (core, cmd, 0); set_u_add (set, addr); if (!foreach_newline (core)) { break; } } } } set_u_free (set); goto out_finish; } break; case 'f': // "@@f" if (each[1] == ':') { RAnalFunction *fcn; RListIter *iter; if (core->anal) { r_list_foreach (core->anal->fcns, iter, fcn) { if (each[2] && strstr (fcn->name, each + 2)) { r_core_seek (core, fcn->addr, true); r_core_cmd (core, cmd, 0); if (!foreach_newline (core)) { break; } } } } goto out_finish; } else { RAnalFunction *fcn; RListIter *iter; if (core->anal) { RConsGrep grep = core->cons->context->grep; RStrBuf *sb = r_strbuf_new (""); r_list_foreach (core->anal->fcns, iter, fcn) { r_core_seek (core, fcn->addr, true); #if 0 r_cons_push (); r_core_cmd (core, cmd, 0); char *buf = (char *)r_cons_get_buffer (); if (buf) { buf = strdup (buf); } r_cons_pop (); // r_cons_print (buf); r_strbuf_append (sb, buf); free (buf); #else char *buf = r_core_cmd_str (core, cmd); r_strbuf_appendf (sb, "%s", buf); free (buf); #endif if (!foreach_newline (core)) { break; } } char *s = r_strbuf_drain (sb); if (s) { r_cons_print (s); free (s); } core->cons->context->grep = grep; } goto out_finish; } break; case 't': // "@@t" { RDebugPid *p; int pid = core->dbg->pid; RDebugPlugin *plugin = R_UNWRAP3 (core->dbg, current, plugin); if (plugin && plugin->pids) { RList *list = plugin->pids (core->dbg, R_MAX (0, pid)); r_list_foreach (list, iter, p) { r_cons_printf ("# PID %d\n", p->pid); r_debug_select (core->dbg, p->pid, p->pid); r_core_cmd (core, cmd, 0); if (!foreach_newline (core)) { break; } } r_list_free (list); } r_debug_select (core->dbg, pid, pid); goto out_finish; } break; case 'c': // "@@c:" if (each[1] == ':') { char *arg = r_core_cmd_str (core, each + 2); if (arg) { foreachOffset (core, cmd, arg); free (arg); } } break; case '=': // "@@=" if (each[1] == '=') { foreachWord (core, cmd, r_str_trim_head_ro (str + 2)); } else { foreachOffset (core, cmd, r_str_trim_head_ro (str + 1)); } break; case 'd': // "@@d" if (each[1] == 'b' && each[2] == 't') { ut64 oseek = core->offset; RDebugFrame *frame; RListIter *iter; RList *list; list = r_debug_frames (core->dbg, UT64_MAX); i = 0; r_list_foreach (list, iter, frame) { switch (each[3]) { case 'b': r_core_seek (core, frame->bp, true); break; case 's': r_core_seek (core, frame->sp, true); break; default: case 'a': r_core_seek (core, frame->addr, true); break; } r_core_cmd (core, cmd, 0); if (!foreach_newline (core)) { break; } i++; } r_core_seek (core, oseek, false); r_list_free (list); } else { R_LOG_ERROR ("Invalid for-each statement. Use @@=dbt[abs]"); } break; case 'k': // "@@k" /* foreach list of items */ { char *out = sdb_querys (core->sdb, NULL, 0, str + ((str[1])? 2: 1)); if (out) { each = out; do { while (*each == ' ') { each++; } if (!*each) { break; } str = strchr (each, ' '); if (str) { *str = '\0'; addr = r_num_math (core->num, each); *str = ' '; } else { addr = r_num_math (core->num, each); } //eprintf ("; 0x%08"PFMT64x":\n", addr); each = str + 1; r_core_seek (core, addr, true); r_core_cmd (core, cmd, 0); if (!foreach_newline (core)) { break; } r_cons_flush (); } while (str); free (out); } } break; case '.': // "@@." if (each[1] == '(') { // XXX what's this 999 ? i = 0; for (core->rcmd->macro.counter = 0; i < 999; core->rcmd->macro.counter++) { if (r_cons_is_breaked ()) { break; } r_cmd_macro_call (&core->rcmd->macro, each + 2); if (!core->rcmd->macro.brk_value) { break; } addr = core->rcmd->macro._brk_value; r_core_seek (core, addr, true); r_core_cmdf (core, "%s @ 0x%08"PFMT64x, cmd, addr); if (!foreach_newline (core)) { break; } i++; } } else { FILE *fd = r_sandbox_fopen (each + 1, "r"); if (fd) { core->rcmd->macro.counter = 0; size_t buf_size = 1024; char *buf = calloc (buf_size, 1); if (buf) { while (!feof (fd)) { buf[0] = '\0'; if (!fgets (buf, buf_size, fd)) { break; } if (*buf) { addr = r_num_math (core->num, buf); r_core_seek (core, addr, true); // XXX r_core_cmdf (core, "%s @ 0x%08"PFMT64x, cmd, addr); if (!foreach_newline (core)) { break; } core->rcmd->macro.counter++; } } } free (buf); fclose (fd); } else { R_LOG_ERROR ("cannot open file '%s' to read offsets", each + 1); } } break; default: core->rcmd->macro.counter = 0; for (; *each == ' '; each++) { ; } i = 0; while (str[i]) { j = i; for (; str[j] && str[j] == ' '; j++) { ; // skip spaces } for (i = j; str[i] && str[i] != ' '; i++) { ; // find EOS } ch = str[i]; str[i] = '\0'; word = strdup (str + j); if (!word) { break; } str[i] = ch; { const RSpace *flagspace = r_flag_space_cur (core->flags); RList *match_flag_items = r_list_newf ((RListFree)r_flag_item_free); if (!match_flag_items) { break; } /* duplicate flags that match word, to be sure the command is going to be executed on flags values at the moment the command is called (without side effects) */ struct duplicate_flag_t u = { .ret = match_flag_items, .word = word, }; r_flag_foreach_space (core->flags, flagspace, duplicate_flag, &u); /* for all flags that match */ r_list_foreach (match_flag_items, iter, flag) { if (r_cons_is_breaked ()) { break; } char *buf = NULL; const char *tmp = NULL; r_core_seek (core, flag->offset, true); r_cons_push (); r_core_cmd (core, cmd, 0); tmp = r_cons_get_buffer (); buf = tmp? strdup (tmp): NULL; r_cons_pop (); if (buf) { r_cons_print (buf); free (buf); } if (!foreach_newline (core)) { break; } r_core_task_yield (&core->tasks); } r_list_free (match_flag_items); core->rcmd->macro.counter++ ; R_FREE (word); } } } r_cons_break_pop (); // XXX: use r_core_seek here core->offset = oseek; free (word); free (ostr); return true; out_finish: free (ostr); r_cons_break_pop (); return false; } static int run_cmd_depth(RCore *core, char *cmd) { char *rcmd; int ret = false; int *depth = &core->cur_cmd_depth; if (depth) { if (*depth < 1) { R_LOG_ERROR ("That '%s' was too deep", cmd); return false; } (*depth)--; } for (rcmd = cmd;;) { char *ptr = strchr (rcmd, '\n'); if (R_UNLIKELY (ptr)) { *ptr = '\0'; } ret = r_core_cmd_subst (core, rcmd); if (R_UNLIKELY (ret == -1)) { R_LOG_ERROR ("Invalid command '%s' (0x%02x)", rcmd, *rcmd); break; } if (!ptr) { break; } rcmd = ptr + 1; } if (depth) { (*depth)++; } return ret; } R_API int r_core_cmd(RCore *core, const char *cstr, bool log) { R_RETURN_VAL_IF_FAIL (core && cstr, 0); R_LOG_DEBUG ("RCoreCmd: %s", cstr); int ret = 0; if (core->incomment) { if (r_str_startswith (cstr, "*/")) { core->incomment = false; } goto beach; // false } r_core_return_code (core, 0); ret = handle_command_call (core, cstr); if (!strcmp (cstr, "!") || !strcmp (cstr, "!!")) { log = false; } if (ret != -1) { if (log) { r_line_hist_add (cstr); } return ret; } if (R_STR_ISNOTEMPTY (core->cmdfilter)) { const char invalid_chars[] = ";|>`@"; size_t i; for (i = 0; invalid_chars[i]; i++) { if (strchr (cstr, invalid_chars[i])) { ret = true; goto beach; } } if (r_str_startswith (cstr, "\"\"")) { cstr += 2; } if (!r_str_startswith (cstr, core->cmdfilter)) { ret = true; goto beach; } } if (core->cmdremote) { if (*cstr == 'q') { R_FREE (core->cmdremote); goto beach; // false } else if (*cstr != '=' && !r_str_startswith (cstr, "!=")) { if (core->cmdremote[0]) { char *s = r_str_newf ("%s %s", core->cmdremote, cstr); r_core_rtr_cmd (core, s); free (s); } else { char *res = r_io_system (core->io, cstr); if (res) { r_cons_printf ("%s\n", res); free (res); } } if (log) { r_line_hist_add (cstr); } goto beach; // false } } if (*cstr == '|' && cstr[1] != '?') { // raw comment syntax goto beach; // false; } if (r_str_startswith (cstr, "/*")) { if (r_sandbox_enable (0)) { R_LOG_ERROR ("This command is disabled in sandbox mode"); goto beach; // false } core->incomment = true; goto beach; } if (log && (*cstr && (*cstr != '.' || !strncmp (cstr, ".(", 2)))) { free (core->lastcmd); core->lastcmd = strdup (cstr); } char *cmd = malloc (strlen (cstr) + 4096); if (!cmd) { goto beach; } r_str_cpy (cmd, cstr); if (log) { r_line_hist_add (cstr); } ret = run_cmd_depth (core, cmd); free (cmd); beach: return ret; } R_API int r_core_cmd_lines(RCore *core, const char *lines) { int r, ret = true; char *nl, *data, *odata; if (R_STR_ISEMPTY (lines)) { return true; } data = odata = strdup (lines); if (!odata) { return false; } size_t line_count = r_str_char_count(lines, '\n'); const bool istty = r_cons_is_tty (); const bool show_progress_bar = core->print->enable_progressbar && r_config_get_b (core->config, "scr.interactive") && r_config_get_i (core->config, "scr.progressbar") && istty; size_t current_line = 0; nl = strchr (odata, '\n'); if (nl) { r_cons_break_push (NULL, NULL); do { if (show_progress_bar) { r_print_progressbar_with_count (core->print, current_line++, line_count, 80, true); } if (r_cons_is_breaked ()) { free (odata); r_cons_break_pop (); return ret; } *nl = '\0'; r = r_core_cmd (core, data, 0); if (r < 0) { data = nl + 1; ret = -1; break; } r_cons_flush (); if (data[0] == 'q') { if (data[1] == '!') { ret = -1; } else { R_LOG_WARN ("'q': quit ignored. Use 'q!'"); } data = nl + 1; break; } data = nl + 1; r_core_task_yield (&core->tasks); } while ((nl = strchr (data, '\n'))); r_cons_break_pop (); if (show_progress_bar) { r_print_progressbar_with_count (core->print, line_count, line_count, 80, true); r_cons_newline (); } } if (ret >= 0 && R_STR_ISNOTEMPTY (data)) { r_core_cmd (core, data, 0); r_cons_flush (); r_core_task_yield (&core->tasks); } free (odata); return ret; } // R2_600 - return bool R_API int r_core_cmd_file(RCore *core, const char *file) { R_RETURN_VAL_IF_FAIL (core && file, false); char *data = r_file_abspath (file); if (!data) { return false; } char *odata = r_file_slurp (data, NULL); free (data); if (!odata) { return false; } if (!r_core_cmd_lines (core, odata)) { R_LOG_ERROR ("Failed to run script '%s'", file); free (odata); return false; } free (odata); return true; } R_API int r_core_cmd_command(RCore *core, const char *command) { int ret, len; char *buf, *rcmd, *ptr; char *cmd = r_core_sysenv_begin (core, command); rcmd = ptr = buf = r_sys_cmd_str (cmd, 0, &len); if (!buf) { free (cmd); return -1; } ret = r_core_cmd (core, rcmd, 0); r_core_sysenv_end (core, command); free (buf); return ret; } // TODO: Fix disasm loop is mandatory R_API char *r_core_disassemble_instr(RCore *core, ut64 addr, int l) { char *cmd, *ret = NULL; cmd = r_str_newf ("pd %i @ 0x%08"PFMT64x, l, addr); if (cmd) { ret = r_core_cmd_str (core, cmd); free (cmd); } return ret; } R_API char *r_core_disassemble_bytes(RCore *core, ut64 addr, int b) { R_RETURN_VAL_IF_FAIL (core, NULL); char *ret = NULL; char *cmd = r_str_newf ("pD %i @ 0x%08"PFMT64x, b, addr); if (cmd) { ret = r_core_cmd_str (core, cmd); free (cmd); } return ret; } // R2_600 - return boolean here R_API int r_core_cmd_buffer(RCore *core, const char *buf) { R_RETURN_VAL_IF_FAIL (core && buf, false); char *ptr, *optr, *str = strdup (buf); if (!str) { return false; } optr = str; ptr = strchr (str, '\n'); while (ptr) { *ptr = '\0'; r_core_cmd (core, optr, 0); optr = ptr + 1; ptr = strchr (str, '\n'); } r_core_cmd (core, optr, 0); free (str); return true; } R_API int r_core_cmdf_at(RCore *core, ut64 addr, const char *fmt, ...) { R_RETURN_VAL_IF_FAIL (core && fmt, -1); va_list ap; va_start (ap, fmt); ut64 oaddr = core->offset; if (oaddr != addr) { r_core_seek (core, addr, 1); } char *cmd = r_str_newvf (fmt, ap); int ret = r_core_cmd (core, cmd, 0); free (cmd); if (oaddr != addr) { r_core_seek (core, oaddr, 1); } va_end (ap); return ret; } R_API int r_core_cmdf(RCore *core, const char *fmt, ...) { R_RETURN_VAL_IF_FAIL (core && fmt, -1); va_list ap; va_start (ap, fmt); char *cmd = r_str_newvf (fmt, ap); int ret = r_core_cmd (core, cmd, 0); free (cmd); va_end (ap); return ret; } R_API int r_core_cmd0(RCore *core, const char *cmd) { return r_core_cmd (core, cmd, 0); } R_API char *r_core_cmd_str_pipe(RCore *core, const char *cmd) { char *tmp = NULL; char *p = (*cmd != '"')? strchr (cmd, '|'): NULL; if (!p && *cmd != '!' && *cmd != '.') { return r_core_cmd_str (core, cmd); } r_cons_reset (); r_sandbox_disable (true); if (r_file_mkstemp ("cmd", &tmp) != -1) { int pipefd = r_cons_pipe_open (tmp, 1, false); if (pipefd == -1) { r_file_rm (tmp); r_sandbox_disable (false); free (tmp); return r_core_cmd_str (core, cmd); } char *_cmd = strdup (cmd); r_core_cmd (core, _cmd, 0); r_cons_flush (); r_cons_pipe_close (pipefd); if (r_file_exists (tmp)) { char *s = r_file_slurp (tmp, NULL); r_file_rm (tmp); r_sandbox_disable (false); free (tmp); free (_cmd); return s? s: strdup (""); } R_LOG_ERROR ("slurp %s fails", tmp); r_file_rm (tmp); free (tmp); free (_cmd); r_sandbox_disable (false); return r_core_cmd_str (core, cmd); } r_sandbox_disable (0); return NULL; } R_API char *r_core_cmd_strf_at(RCore *core, ut64 addr, const char *fmt, ...) { va_list ap; ut64 oaddr = core->offset; va_start (ap, fmt); if (addr != core->offset) { r_core_seek (core, addr, 1); } char *cmd = r_str_newvf (fmt, ap); char *ret = r_core_cmd_str (core, cmd); free (cmd); if (addr != core->offset) { r_core_seek (core, oaddr, 1); } va_end (ap); return ret; } R_API char *r_core_cmd_strf(RCore *core, const char *fmt, ...) { R_RETURN_VAL_IF_FAIL (core && fmt, NULL); va_list ap; va_start (ap, fmt); char *cmd = r_str_newvf (fmt, ap); char *ret = r_core_cmd_str (core, cmd); free (cmd); va_end (ap); return ret; } R_API int r_core_cmd_call_at(RCore *core, ut64 addr, const char *cmd) { R_RETURN_VAL_IF_FAIL (core && cmd, -1); R_LOG_DEBUG ("RCoreCallAt(0x%08"PFMT64x"): %s", addr, cmd); const ut64 oaddr = core->offset; const bool mustseek = addr != oaddr; if (mustseek) { r_core_seek (core, addr, 1); } int res = r_cmd_call (core->rcmd, cmd); if (mustseek) { r_core_seek (core, oaddr, 1); } return res; } // run an r2 command without evaluating any special character R_API int r_core_cmd_call(RCore *core, const char *cmd) { return r_cmd_call (core->rcmd, cmd); } R_API int r_core_cmd_callf(RCore *core, const char *fmt, ...) { va_list ap; va_start (ap, fmt); char *cmd = r_str_newvf (fmt, ap); int res = r_cmd_call (core->rcmd, cmd); free (cmd); va_end (ap); return res; } R_API char *r_core_cmd_str_at(RCore *core, ut64 addr, const char *cmd) { ut64 oseek = core->offset; r_core_seek (core, addr, true); char *res = r_core_cmd_str (core, cmd); r_core_seek (core, oseek, true); return res; } /* return: pointer to a buffer with the output of the command */ R_API char *r_core_cmd_str(RCore *core, const char *cmd) { R_RETURN_VAL_IF_FAIL (core, NULL); if (cmd && *cmd != '"' && strchr (cmd, '>')) { r_core_cmd0 (core, cmd); return strdup (""); } r_cons_push (); if (core->cons) { core->cons->context->noflush = true; core->cons->context->cmd_str_depth++; if (cmd && r_core_cmd (core, cmd, 0) == -1) { //eprintf ("Invalid command: %s\n", cmd); if (--core->cons->context->cmd_str_depth == 0) { core->cons->context->noflush = false; r_cons_flush (); } r_cons_pop (); return NULL; } if (--core->cons->context->cmd_str_depth == 0) { core->cons->context->noflush = false; } } r_cons_filter (); const char *static_str = r_cons_get_buffer (); char *retstr = strdup (r_str_get (static_str)); r_cons_pop (); r_cons_echo (NULL); return retstr; } /* get command output in raw bytes */ R_API RBuffer *r_core_cmd_tobuf(RCore *core, const char *cmd) { r_cons_push (); core->cons->context->noflush = true; core->cons->context->cmd_str_depth++; if (r_core_cmd0 (core, cmd) == -1) { //eprintf ("Invalid command: %s\n", cmd); if (--core->cons->context->cmd_str_depth == 0) { core->cons->context->noflush = false; r_cons_flush (); } r_cons_pop (); return NULL; } if (--core->cons->context->cmd_str_depth == 0) { core->cons->context->noflush = false; } r_cons_filter (); RBuffer *out = r_buf_new_with_bytes ((const ut8*)r_cons_get_buffer (), r_cons_get_buffer_len ()); r_cons_pop (); r_cons_echo (NULL); return out; } /* run cmd in the main task synchronously */ R_API int r_core_cmd_task_sync(RCore *core, const char *cmd, bool log) { RCoreTask *task = core->tasks.main_task; char *s = strdup (cmd); if (R_LIKELY (s)) { task->cmd = s; task->cmd_log = log; task->state = R_CORE_TASK_STATE_BEFORE_START; int res = r_core_task_run_sync (&core->tasks, task); free (s); return res; } return 0; } static int cmd_ox(void *data, const char *input) { // "0x" RCore *core = (RCore*)data; char *s = r_str_newf ("s 0%s", input); int ret = r_core_cmd_call (core, s); free (s); return ret; } static int core_cmd0_wrapper(void *core, const char *cmd) { return r_core_cmd0 ((RCore *)core, cmd); } R_API void r_core_cmd_init(RCore *core) { struct { const char *cmd; const char *description; RCmdCb cb; } cmds[] = { { "!", "run system command", cmd_system }, { "_", "print last output", cmd_last }, { "{", "run a command in json", cmd_json }, { "#", "calculate hash", cmd_hash }, { "$", "alias", cmd_alias }, { "%", "short version of 'env' command", cmd_env }, { "&", "tasks", cmd_tasks }, { "(", "macro", cmd_macro }, { "*", "pointer read/write", cmd_pointer }, { "+", "relative seek forward", cmd_plus }, { "-", "open cfg.editor and run script", cmd_stdin }, { ".", "interpret", cmd_interpret }, { ",", "create and manipulate tables", cmd_table }, { "/", "search kw, pattern aes", cmd_search }, { "=", "io pipe", cmd_rap }, { "?", "help message", cmd_help }, { ":", "alias for =!", cmd_iosys }, { "0", "alias for s 0x", cmd_ox }, { "a", "analysis", cmd_anal }, { "b", "change block size", cmd_bsize }, { "c", "compare memory", cmd_cmp }, { "C", "code metadata", cmd_meta }, { "d", "debugger operations", cmd_debug }, { "e", "evaluate configuration variable", cmd_eval }, { "f", "get/set flags", cmd_flag }, { "g", "egg manipulation", cmd_egg }, { "i", "get file info", cmd_info }, { "k", "perform sdb query", cmd_kuery }, { "l", "list files and directories", cmd_l }, { "j", "join the contents of the two files", cmd_j }, { "h", "show the top n number of line in file", cmd_h }, { "L", "manage dynamically loaded plugins", cmd_plugins }, { "m", "mount filesystem", cmd_mount }, { "o", "open or map file", cmd_open }, { "p", "print current block", cmd_print }, { "P", "project", cmd_project }, { "q", "exit program session", cmd_quit }, { "Q", "alias for q!", cmd_Quit }, { "r", "change file size", cmd_resize }, { "s", "seek to an offset", cmd_seek }, { "t", "type information (cparse)", cmd_type }, { "T", "Text log utility", cmd_log }, { "u", "uname/undo", cmd_undo }, { "<", "pipe into RCons.readChar", cmd_pipein }, { "V", "enter visual mode", cmd_visual }, { "v", "enter visual panels", cmd_panels }, { "w", "write bytes", cmd_write }, { "x", "alias for px", cmd_hexdump }, { "y", "yank bytes", cmd_yank }, { "z", "zignatures", cmd_zign }, }; core->rcmd = r_cmd_new (); if (core->rcmd) { core->rcmd->macro.user = core; core->rcmd->macro.num = core->num; core->rcmd->macro.cmd = core_cmd0_wrapper; core->rcmd->nullcallback = r_core_cmd_nullcallback; core->rcmd->macro.cb_printf = (PrintfCallback)r_cons_printf; r_cmd_set_data (core->rcmd, core); core->cmd_descriptors = r_list_newf (free); size_t i; for (i = 0; i < R_ARRAY_SIZE (cmds); i++) { r_cmd_add (core->rcmd, cmds[i].cmd, cmds[i].cb); } } }