mirror of
https://github.com/radareorg/radare2.git
synced 2024-12-13 16:18:33 +00:00
12830 lines
352 KiB
C
12830 lines
352 KiB
C
/* radare - LGPL - Copyright 2009-2022 - pancake, maijin */
|
|
|
|
#include <r_core.h>
|
|
#include <r_util/r_graph_drawable.h>
|
|
|
|
#define SLOW_ANALYSIS 1
|
|
#define MAX_SCAN_SIZE 0x7ffffff
|
|
// should be 1 unless it makes the CI sad
|
|
|
|
static const char *help_msg_af_plus[] = {
|
|
"Usage:", "af+", " [addr] ([name] ([type] [diff]))",
|
|
"af+", "$$", "add a raw function element. See afb+ to add basic blocks to it",
|
|
"af+", "$$ main", "add new function in current offset with 'main' as name",
|
|
NULL
|
|
};
|
|
|
|
static const char *help_msg_aex[] = {
|
|
"Usage:", "aex", "[a] [9090]",
|
|
"aex", " 90", "decode the given hexpairs and execute them",
|
|
"aexa", " mov rax, 33", "assemble instruction and execute it",
|
|
NULL
|
|
};
|
|
|
|
static const char *help_msg_a[] = {
|
|
"Usage:", "a", "[abdefFghoprxstc] [...]",
|
|
"a", "", "alias for aai - analysis information",
|
|
"a*", "", "same as afl*;ah*;ax*",
|
|
"aa", "[?]", "analyze all (fcns + bbs) (aa0 to avoid sub renaming)",
|
|
"a8", " [hexpairs]", "analyze bytes",
|
|
"ab", "[?]", "analyze basic block",
|
|
"ac", "[?]", "manage classes",
|
|
"aC", "[?]", "analyze function call",
|
|
"ad", "[?]", "analyze data trampoline (wip)",
|
|
"ad", " [from] [to]", "analyze data pointers to (from-to)",
|
|
"ae", "[?] [expr]", "analyze opcode eval expression (see ao)",
|
|
"af", "[?]", "analyze functions",
|
|
"aF", "", "same as above, but using anal.depth=1",
|
|
"ag", "[?] [options]", "draw graphs in various formats",
|
|
"ah", "[?]", "analysis hints (force opcode size, ...)",
|
|
"ai", " [addr]", "address information (show perms, stack, heap, ...)",
|
|
"aj", "", "same as a* but in json (aflj)",
|
|
"aL", "[jq]", "list all asm/anal plugins (See `e asm.arch=?` and `La[jq]`)",
|
|
"an", "[?] [name]", "show/rename/create whatever var/flag/function is used in current instruction",
|
|
"ao", "[?] [len]", "analyze Opcodes (or emulate it)",
|
|
"aO", "[?] [len]", "analyze N instructions in M bytes",
|
|
"ap", "", "find prelude for current offset",
|
|
"ar", "[?]", "like 'dr' but for the esil vm. (registers)",
|
|
"as", "[?] [num]", "analyze syscall using dbg.reg",
|
|
"av", "[?] [.]", "show vtables",
|
|
"avg", "[?] [.]", "manage global variables",
|
|
"ax", "[?]", "manage refs/xrefs (see also afx?)",
|
|
NULL
|
|
};
|
|
|
|
static const char *help_msg_afna[] = {
|
|
"Usage:", "afna", " # construct a function name and rename the function for the current offset.",
|
|
"", "", "Based on flags or methods calls found inside that function.",
|
|
NULL
|
|
};
|
|
|
|
static const char *help_msg_afu[] = {
|
|
"Usage:", "afu", "[addr] # resize and analyze function from current address until addr.",
|
|
"afu", " 0x100004093", "resize and analyze function from current address until 0x100004093",
|
|
NULL
|
|
};
|
|
|
|
static const char *help_msg_aae[] = {
|
|
"Usage:", "aae", "[pf] ([addr]) # analyze all kind of stuff using esil",
|
|
"aaep", "", "same as aepa@@@i - define anal pins by import flag names",
|
|
"aaep", "a", "run 'aep ret0@@@i' and then 'aaep' - all unknown imports are faked to return 0",
|
|
"aaef", "", "emulate all functions using esil to find out computed references (same as aef@@@F)",
|
|
"aae", " [addr]", "same as aepa@@@i - define anal pins by import flag names",
|
|
"aae", "", "honor anal.{in,from,to} and emulate all executable regions",
|
|
NULL
|
|
};
|
|
|
|
static const char *help_msg_aav[] = {
|
|
"Usage:", "aav", "[sat] # find values referencing a specific section or map",
|
|
"aav", "", "find absolute reference values",
|
|
"aavr", "", "find relative reference values (address + 4 byte signed int)",
|
|
NULL
|
|
};
|
|
|
|
static const char *help_msg_aan[] = {
|
|
"Usage:", "aan", "[rg] # automatically name functions.",
|
|
"aan", "", "autoname all functions",
|
|
"aang", "", "autoname all golang functions",
|
|
"aanr", "", "auto-noreturn propagation",
|
|
NULL
|
|
};
|
|
|
|
static const char *help_msg_afm[] = {
|
|
"Usage:", "afm", "[name] # merge two functions.",
|
|
"afm", " sym.func.100003d74", "merge current function into 0x100003d74",
|
|
NULL
|
|
};
|
|
|
|
static const char *help_msg_aF[] = {
|
|
"Usage:", "aF", " # analyze a function, but using anal.depth=1",
|
|
"aF", "", "check af? for more options and information.",
|
|
NULL
|
|
};
|
|
|
|
static const char *help_msg_an[] = {
|
|
"Usage:", "an", " # analyze name for the current address",
|
|
"an", "", "show flag/function/symbol name",
|
|
"an*", "", "same as above but in r2 commands",
|
|
"anj", "", "same as above but in json",
|
|
NULL
|
|
};
|
|
|
|
static const char *help_msg_a8[] = {
|
|
"Usage:", "a8", "[hexpairs] # analyze the byte array given as input",
|
|
"a8 ", "5548", "analyzes 5548 byte array",
|
|
NULL
|
|
};
|
|
|
|
static const char *help_msg_ap[] = {
|
|
"Usage:", "ap[?]", " # analyze prelude in current offset",
|
|
"ap", "", "check if current offset contains a function prelude",
|
|
NULL
|
|
};
|
|
|
|
static const char *help_msg_avg[] = {
|
|
"Usage:", "avg", " # analyze variable global",
|
|
"avg", "", "use ESIL emulation to find out arguments of a call (uses 'abte')",
|
|
"avg", " [type] [name]", "add global",
|
|
"avg-", "", "delete global",
|
|
NULL
|
|
};
|
|
|
|
static const char *help_msg_aC[] = {
|
|
"Usage:", "aC[fej] [addr-of-call]", " # analyze call args",
|
|
"aCe", "", "use ESIL emulation to find out arguments of a call (uses 'abte')",
|
|
"aCf", "", "same as .aCe* $$ @@=`pdr~call`",
|
|
NULL
|
|
};
|
|
|
|
static const char *help_msg_aaf[] = {
|
|
"Usage:", "aaf[efrt?]", " # analyse all functionsee also 'af' and 'afna'",
|
|
"aaf", "", "same as afr@@c:isq",
|
|
"aafe", " ", "same as aef@@F",
|
|
"aaff", "", "set a flag for every function",
|
|
"aafr", " [len]", "consecutive function analysis (e anal.hasnext=1;afr@@c:isq)",
|
|
"aaft", "", "recursive type matching across all functions",
|
|
"aafs", "", "single basic block function analysis",
|
|
NULL
|
|
};
|
|
|
|
static const char *help_msg_aaa[] = {
|
|
"Usage:", "aa[a[a[a]]]", " # automatically analyze the whole program",
|
|
"aa", " ", "alias for 'af@@ sym.*;af@entry0;afva'",
|
|
"aaa", "", "perform deeper analysis, most common use",
|
|
"aaaa", "", "same as aaa but adds a bunch of experimental iterations",
|
|
NULL
|
|
};
|
|
|
|
static const char *help_msg_aa[] = {
|
|
"Usage:", "aa[0*?]", " # see also 'af' and 'afna'",
|
|
"aa", " ", "alias for 'af@@ sym.*;af@entry0;afva'", //;.afna @@ fcn.*'",
|
|
"aaa", "[?]", "autoname functions after aa (see afna)",
|
|
"aab", "", "abb across bin.sections.rx",
|
|
"aac", " [len]", "analyze function calls (af @@ `pi len~call[1]`)",
|
|
"aac*", " [len]", "flag function calls without performing a complete analysis",
|
|
"aaci", "", "flag import xrefs only",
|
|
"aad", " [len]", "analyze data references to code",
|
|
"aae", " [len] ([addr])", "analyze references with ESIL (optionally to address)",
|
|
"aaef", "", "analyze references with ESIL in all functions",
|
|
"aaf", "[?][efrt] ", "analyze all functions relationships with flags, type matching and consecutive",
|
|
"aaF", " [sym*]", "set anal.in=block for all the spaces between flags matching glob",
|
|
"aaFa", " [sym*]", "same as aaF but uses af/a2f instead of af+/afb+ (slower but more accurate)",
|
|
"aai", "[j]", "show info of all analysis parameters",
|
|
"aaj", " ", "analyze all jumps",
|
|
"aan", "[?][gr]", "autoname functions (aang = golang, aanr = noreturn propagation)",
|
|
"aao", "", "analyze all objc references",
|
|
"aap", "", "find and analyze function preludes",
|
|
"aar", "[?] [len]", "analyze len bytes of instructions for references",
|
|
"aas", " [len]", "analyze symbols (af @@= `isq~[0]`)",
|
|
"aaS", "", "analyze all flags starting with sym. (af @@ sym.*)",
|
|
"aat", " [fcn]", "analyze all/given function to convert immediate to linked structure offsets (see tl?)",
|
|
"aaT", " [len]", "analyze code after trap-sleds",
|
|
"aau", " [len]", "list mem areas (larger than len bytes) not covered by functions",
|
|
"aav", "[?] [sat]", "find values referencing a specific section or map",
|
|
"aaw", "", "analyze all meta words (Cd) and add r. named flags for referenced pointers",
|
|
NULL
|
|
};
|
|
|
|
static const char *help_msg_afls[] = {
|
|
"Usage:", "afls", "[afls] # sort function list",
|
|
"afls", "", "same as aflsa",
|
|
"aflsa", "", "sort by address (same as afls)",
|
|
"aflss", "", "sort by size",
|
|
"aflsn", "", "sort by name",
|
|
"aflsb", "", "sort by number of basic blocks",
|
|
NULL
|
|
};
|
|
|
|
static const char *help_msg_ai[] = {
|
|
"Usage:", "ai", "[j*] [sz] # analysis/address information/imports",
|
|
"ai", " @addr", "show address information",
|
|
"aii", " [namespace]", "global import (like afii, but global)",
|
|
"aii", "-", "delete all global imports",
|
|
"aij", " @addr", "show address information in JSON format",
|
|
NULL
|
|
};
|
|
|
|
static const char *help_msg_aar[] = {
|
|
"Usage:", "aar", "[j*] [sz] # search and analyze xrefs",
|
|
"aar", " [sz]", "analyze xrefs in current section or sz bytes of code",
|
|
"aar*", " [sz]", "list found xrefs in radare commands format",
|
|
"aarj", " [sz]", "list found xrefs in JSON format",
|
|
NULL
|
|
};
|
|
|
|
static const char *help_msg_ab[] = {
|
|
"Usage:", "ab", "# analyze basic block",
|
|
"ab", " [addr]", "show basic block information at given address",
|
|
"ab.", "", "same as: ab $$",
|
|
"ab-", "[addr]", "delete basic block at given address",
|
|
"aba", " [addr]", "analyze esil accesses in basic block (see aea?)",
|
|
"abb", " [length]", "analyze N bytes and extract basic blocks",
|
|
"abe", " [addr]", "emulate basic block (alias for aeb)",
|
|
"abf", " [addr]", "address of incoming (from) basic blocks",
|
|
"abj", " [addr]", "display basic block information in JSON",
|
|
"abl", "[?] [.-cqj]", "list all basic blocks",
|
|
"abx", " [hexpair-bytes]", "analyze N bytes",
|
|
"abt", "[?] [addr] [num]", "find num paths from current offset to addr",
|
|
NULL
|
|
};
|
|
|
|
static const char *help_msg_abl[] = {
|
|
"Usage:", "abl", "analyzed basicblocks listing",
|
|
"abl", "", "list all program-wide basic blocks analyzed",
|
|
"abl,", " [table-query]", "render the list using a table",
|
|
"ablc", "", "count how many basic blocks are registered",
|
|
"ablj", "", "in json format",
|
|
"ablq", "", "in quiet format",
|
|
NULL
|
|
};
|
|
|
|
static const char *help_msg_abt[] = {
|
|
"Usage:", "abt", "[addr] [num] # find num paths from current offset to addr",
|
|
"abt", " [addr] [num]", "find num paths from current offset to addr",
|
|
"abte", " [addr]", "emulate from beginning of function to the given address",
|
|
"abtj", " [addr] [num]", "display paths in JSON",
|
|
NULL
|
|
};
|
|
|
|
static const char *help_msg_ac[] = {
|
|
"Usage:", "ac", "anal classes commands",
|
|
"acl", "[j*]", "list all classes",
|
|
"acll", "[j] (class_name)", "list all or single class detailed",
|
|
"ac", " [class name]", "add class",
|
|
"ac-", " [class name]", "delete class",
|
|
"acn", " [class name] [new class name]", "rename class",
|
|
"acv", " [class name] [addr] ([offset]) ([size])", "add vtable address to class",
|
|
"acvf", " [offset] ([class name])", "lookup function address on vtable offset",
|
|
"acv-", " [class name] [vtable id]", "delete vtable by id (from acv [class name])",
|
|
"acb", " [class name]", "list bases of class",
|
|
"acb", " [class name] [base class name] ([offset])", "add base class",
|
|
"acb-", " [class name] [base class id]", "delete base by id (from acb [class name])",
|
|
"acm", " [class name] [method name] [offset] ([vtable offset])", "add/edit method",
|
|
"acm-", " [class name] [method name]", "delete method",
|
|
"acmn", " [class name] [method name] [new name]", "rename method",
|
|
"acg", "", "print inheritance ascii graph",
|
|
"ac?", "", "show this help",
|
|
NULL
|
|
};
|
|
|
|
static const char *help_msg_ad[] = {
|
|
"Usage:", "ad", "[kt] [...]",
|
|
"ad", " [N] [D]", "analyze N data words at D depth",
|
|
"ad4", " [N] [D]", "analyze N data words at D depth (asm.bits=32)",
|
|
"ad8", " [N] [D]", "analyze N data words at D depth (asm.bits=64)",
|
|
"adf", "", "analyze data in function (use like .adf @@=`afl~[0]`",
|
|
"adfg", "", "analyze data in function gaps",
|
|
"adt", "", "analyze data trampolines (wip)",
|
|
"adk", "", "analyze data kind (code, text, data, invalid, ...)",
|
|
NULL
|
|
};
|
|
|
|
static const char *help_msg_aes[] = {
|
|
"Usage:", "aes[pbosu]", "esil stepping utilities",
|
|
"aesp", " [X] [N]", "evaluate N instr from offset X",
|
|
"aesb", "", "step back",
|
|
"aeso", " ", "step over",
|
|
"aesou", " [addr]", "step over until given address",
|
|
"aess", " ", "step skip (in case of CALL, just skip, instead of step into)",
|
|
"aesu", " [addr]", "step until given address",
|
|
"aesue", " [esil]", "step until esil expression match",
|
|
"aesuo", " [optype]", "step until given opcode type",
|
|
NULL
|
|
};
|
|
|
|
static const char *help_msg_aei[] = {
|
|
"Usage:", "aei", "[smp] [...]",
|
|
"aei", "", "initialize ESIL VM state (aei- to deinitialize)",
|
|
"aeis", " argc [argv] [envp]", "initialize entrypoint stack environment",
|
|
"aeim", " [addr] [size] [name]", "initialize ESIL VM stack (aeim- remove)",
|
|
"aeip", "", "initialize ESIL program counter to curseek",
|
|
NULL
|
|
};
|
|
|
|
static const char *help_msg_ae[] = {
|
|
"Usage:", "ae[idesr?] [arg]", "ESIL code emulation",
|
|
"ae", " [expr]", "evaluate ESIL expression",
|
|
"ae?", "", "show this help",
|
|
"ae??", "", "show ESIL help",
|
|
"aea", "[f] [count]", "analyse n esil instructions accesses (regs, mem..)",
|
|
"aeA", "[f] [count]", "analyse n bytes for their esil accesses (regs, mem..)",
|
|
"aeb", " ([addr])", "emulate block in current or given address",
|
|
"aeC", "[arg0 arg1..] @ addr", "appcall in esil",
|
|
"aec", "[?]", "continue until ^C",
|
|
"aef", " [addr]", "emulate function",
|
|
"aefa", " [addr]", "emulate function to find out args in given or current offset",
|
|
"aeg", " [expr]", "esil data flow graph",
|
|
"aegf", " [expr] [register]", "esil data flow graph filter",
|
|
"aei", "[?]", "initialize ESIL VM state (aei- to deinitialize)",
|
|
"aek", "[?] [query]", "perform sdb query on ESIL.info",
|
|
"aeL", "", "list ESIL plugins",
|
|
"aep", "[?] [addr]", "manage esil pin hooks (see 'e cmd.esil.pin')",
|
|
"aepc", " [addr]", "change esil PC to this address",
|
|
"aer", "[?] [..]", "handle ESIL registers like 'ar' or 'dr' does",
|
|
"aes", "[?]", "perform emulated debugger step",
|
|
"aets", "[?]", "esil Trace session",
|
|
"aev", " [esil]", "visual esil debugger for the given expression or current instruction",
|
|
"aex", " [hex]", "evaluate opcode expression",
|
|
NULL
|
|
};
|
|
|
|
static const char *help_detail_ae[] = {
|
|
"Examples:", "ESIL", " examples and documentation",
|
|
"=", "", "assign updating internal flags",
|
|
":=", "", "assign without updating internal flags",
|
|
"+=", "", "a+=b => b,a,+=",
|
|
"/", "", "division",
|
|
"*", "", "multiply",
|
|
"*=", "", "multiply and assign a *= b",
|
|
"L*", "", "long multiply",
|
|
"+", "", "a=a+b => b,a,+,a,=",
|
|
"++", "", "increment, 2,a,++ == 3 (see rsi,--=[1], ... )",
|
|
"--", "", "decrement, 2,a,-- == 1",
|
|
"*=", "", "a*=b => b,a,*=",
|
|
"/=", "", "a/=b => b,a,/=",
|
|
"%", "", "module",
|
|
"%=", "", "a%=b => b,a,%=",
|
|
"&=", "", "and ax, bx => bx,ax,&=",
|
|
"^", "", "xor",
|
|
"&", "", "and",
|
|
"|", "", "or r0, r1, r2 => r2,r1,|,r0,=",
|
|
"!=", "", "negate all bits",
|
|
"^=", "", "xor ax, bx => bx,ax,^=",
|
|
"", "[]", "mov eax,[eax] => eax,[],eax,=",
|
|
"=", "[]", "mov [eax+3], 1 => 1,3,eax,+,=[]",
|
|
"=", "[1]", "mov byte[eax],1 => 1,eax,=[1]",
|
|
"=", "[8]", "mov [rax],1 => 1,rax,=[8]",
|
|
"()", "", "execute an esil syscall",
|
|
"[]", "", "peek from random position",
|
|
"[N]", "", "peek word of N bytes from popped address",
|
|
"[*]", "", "peek some from random position",
|
|
"=", "[*]", "poke some at random position",
|
|
"$", "", "int 0x80 => 0x80,$",
|
|
"$$", "", "simulate a hardware trap",
|
|
"==", "", "pops twice, compare and update esil flags",
|
|
"<", "", "compare for smaller",
|
|
"<=", "", "compare for smaller or equal",
|
|
">", "", "compare for bigger",
|
|
">=", "", "compare bigger for or equal",
|
|
">>=", "", "shr ax, bx => bx,ax,>>= # shift right",
|
|
"<<=", "", "shl ax, bx => bx,ax,<<= # shift left",
|
|
">>>=", "", "ror ax, bx => bx,ax,>>>= # rotate right",
|
|
"<<<=", "", "rol ax, bx => bx,ax,<<<= # rotate left",
|
|
"?{", "", "if popped value != 0 run the block until }",
|
|
"}{", "", "else block",
|
|
"}", "", "end of conditional block",
|
|
"NAN", "", "checks if result in stack is not a number",
|
|
"I2D", "", "signed to double",
|
|
// "S2D", "", "signed to double",
|
|
"U2D", "", "unsigned to double",
|
|
"D2I", "", "double to signed",
|
|
"D2U", "", "double to unsigned",
|
|
"F==", "", "float comparison",
|
|
"CEIL", "", "float ceil",
|
|
"FLOOR", "", "float floor",
|
|
"ROUND", "", "float round",
|
|
"SQRT", "", "float square root",
|
|
"POP", "", "drops last element in the esil stack",
|
|
"DUP", "", "duplicate last value in stack",
|
|
"NUM", "", "evaluate last item in stack to number",
|
|
"SWAP", "", "swap last two values in stack",
|
|
"TRAP", "", "stop execution",
|
|
"BITS", "", "16,BITS # change bits, useful for arm/thumb",
|
|
"TODO", "", "the instruction is not yet esilized",
|
|
"STACK", "", "show contents of stack",
|
|
"CLEAR", "", "clears the esil stack",
|
|
"REPEAT", "", "repeat n times",
|
|
"BREAK", "", "terminates the string parsing",
|
|
"SETJT", "", "set jump target",
|
|
"SETJTS", "", "set jump target set",
|
|
"SETD", "", "set delay slot",
|
|
"GOTO", "", "jump to the Nth word popped from the stack",
|
|
"$", "", "esil interrupt",
|
|
"$z", "", "internal flag: zero",
|
|
"$c", "", "internal flag: carry",
|
|
"$b", "", "internal flag: borrow",
|
|
"$p", "", "internal flag: parity",
|
|
"$s", "", "internal flag: sign",
|
|
"$o", "", "internal flag: overflow",
|
|
"$ds", "", "internal flag: delay-slot",
|
|
"$jt", "", "internal flag: jump-target",
|
|
"$js", "", "internal flag: jump-target-set",
|
|
// DEPRECATED "$r", "", "internal flag: jump-sign",
|
|
"$$", "", "internal flag: pc address",
|
|
NULL
|
|
};
|
|
|
|
static const char *help_msg_aea[] = {
|
|
"Examples:", "aea", " show regs and memory accesses used in a range",
|
|
"aea", " [ops]", "show regs/memory accesses used in N instructions",
|
|
"aea*", " [ops]", "create mem.* flags for memory accesses",
|
|
"aeab", "", "show regs used in current basic block",
|
|
"aeaf", "", "show regs used in current function",
|
|
"aear", " [ops]", "show regs read in N instructions",
|
|
"aeaw", " [ops]", "show regs written in N instructions",
|
|
"aean", " [ops]", "show regs not written in N instructions",
|
|
"aeaj", " [ops]", "show aea output in JSON format",
|
|
"aeA", " [len]", "show regs used in N bytes (subcommands are the same)",
|
|
"Legend:", "", "",
|
|
"I", "", "input registers (read before being set)",
|
|
"A", "", "all regs accessed",
|
|
"R", "", "register values read",
|
|
"W", "", "registers written",
|
|
"N", "", "read but never written",
|
|
"V", "", "values",
|
|
"@R", "", "memreads",
|
|
"@W", "", "memwrites",
|
|
"NOTE:", "", "mem{reads,writes} with PIC only fetch the offset",
|
|
NULL
|
|
};
|
|
|
|
static const char *help_msg_aec[] = {
|
|
"Examples:", "aec", " continue until ^c",
|
|
"aec", "", "continue until exception",
|
|
"aecs", "", "continue until syscall",
|
|
"aecc", "", "continue until call",
|
|
"aecu", "[addr]", "continue until address",
|
|
"aecue", "[addr]", "continue until esil expression",
|
|
NULL
|
|
};
|
|
|
|
static const char *help_msg_aeC[] = {
|
|
"Examples:", "aeC", " arg0 arg1 ... @ calladdr",
|
|
"aeC", " 1 2 @ sym._add", "Call sym._add(1,2)",
|
|
NULL
|
|
};
|
|
|
|
static const char *help_msg_aeg[] = {
|
|
"Usage:", "aeg[cfiv*]", " [...]",
|
|
"aeg", "", "analyze current instruction as an esil graph",
|
|
"aegf", "", "analyze given expression and filter for register",
|
|
"aeg*", "", "analyze current instruction as an esil graph",
|
|
"aegv", "", "analyse and launch the visual interactive mode (.aeg*;aggv == aegv)",
|
|
NULL
|
|
};
|
|
|
|
static const char *help_msg_aep[] = {
|
|
"Usage:", "aep[-*c] ", " [...] manage esil pins, run r2 commands instead of esil",
|
|
"aepc", " [addr]", "change program counter for esil",
|
|
"aep*", "", "list pins in r2 commands",
|
|
"aep-", "*", "remove all pins",
|
|
"aep-", "[addr]", "remove pin",
|
|
"aep-", "[name]", "remove pin command",
|
|
"aepa", " ([addr])", "auto set pin in current or given address by flag name (see aaep)",
|
|
"aep", " [name] @ [addr]", "set pin",
|
|
"aep ", "memcpy=wf `dr?A1` `dr?A2` @r:A0", "override esil.cmd.pin for this pin name",
|
|
"aep", "", "list pins",
|
|
"aep.", "", "show pin name in current address if any",
|
|
"aepk", " [query]", "kuery the sdb of pins",
|
|
NULL
|
|
};
|
|
|
|
static const char *help_msg_aek[] = {
|
|
"Usage:", "aek ", " [...]",
|
|
"aek", "", "dump the esil.stats database contents",
|
|
"aek ", "sdb.query", "evaluate sdb query on esil.stats db",
|
|
"aek-", "", "clear the esil.stats sdb instance",
|
|
NULL
|
|
};
|
|
|
|
static const char *help_msg_aets[] = {
|
|
"Usage:", "aets ", " [...]",
|
|
"aets+", "", "start ESIL trace session",
|
|
"aets-", "", "stop ESIL trace session",
|
|
NULL
|
|
};
|
|
|
|
static const char *help_msg_af[] = {
|
|
"Usage:", "af", "",
|
|
"af", " ([name]) ([addr])", "analyze functions (start at addr or $$)",
|
|
"af+", " addr name [type] [diff]", "hand craft a function (requires afb+)",
|
|
"af-", " [addr]", "clean all function analysis data (or function at addr)",
|
|
"afa", "", "analyze function arguments in a call (afal honors dbg.funcarg)",
|
|
"afB", " 16", "set current function as thumb (change asm.bits)",
|
|
"afb", "[?] [addr]", "List basic blocks of given function",
|
|
"afb+", " fcnA bbA sz [j] [f] ([t]( [d]))", "add bb to function @ fcnaddr",
|
|
"afbF", "([0|1])", "Toggle the basic-block 'folded' attribute",
|
|
"afc", "[?] type @[addr]", "set calling convention for function",
|
|
"afC[lc]", " ([addr])@[addr]", "calculate the Cycles (afC) or Cyclomatic Complexity (afCc)",
|
|
"afd", "[addr]","show function + delta for given offset",
|
|
"afF", "[1|0|]", "fold/unfold/toggle",
|
|
"afi", " [addr|fcn.name]", "show function(s) information (verbose afl)",
|
|
"afj", " [tableaddr] [elem_sz] [count] [seg]", "analyze function jumptable (adding seg to each elem)",
|
|
"afl", "[?] [ls*] [fcn name]", "list functions (addr, size, bbs, name) (see afll)",
|
|
"afm", " name", "merge two functions",
|
|
"afM", " name", "print functions map",
|
|
"afn", "[?] name [addr]", "rename name for function at address (change flag too)",
|
|
"afna", "", "suggest automatic name for current offset",
|
|
"afo", "[?j] [fcn.name]", "show address for the function name or current offset",
|
|
"afr", " ([name]) ([addr])", "analyze functions recursively",
|
|
"afs", "[!] ([fcnsign])", "get/set function signature at current address (afs! uses cfg.editor)",
|
|
"afS", "[stack_size]", "set stack frame size for function at current address",
|
|
"afsr", " [function_name] [new_type]", "change type for given function",
|
|
"aft", "[?]", "type matching, type propagation",
|
|
"afu", " addr", "resize and analyze function from current address until addr",
|
|
"afv[absrx]", "?", "manipulate args, registers and variables in function",
|
|
"afx", "[m]", "list function references",
|
|
NULL
|
|
};
|
|
|
|
static const char *help_msg_afb[] = {
|
|
"Usage:", "afb", " list basic blocks of given function",
|
|
".afbr-", "", "set breakpoint on every return address of the function",
|
|
".afbr-*", "", "remove breakpoint on every return address of the function",
|
|
"afb", " [addr]", "list basic blocks of function",
|
|
"afb.", " [addr]", "show info of current basic block",
|
|
"afb=", "", "display ascii-art bars for basic block regions",
|
|
"afb+", " fcn_at bbat bbsz [jump] [fail] ([diff])", "add basic block by hand",
|
|
"afbc", " [color] ([addr])", "colorize basic block",
|
|
"afbe", " bbfrom bbto", "add basic-block edge for switch-cases",
|
|
"afbi", "[j]", "print current basic block information",
|
|
"afbj", " [addr]", "show basic blocks information in json",
|
|
"afbr", "", "show addresses of instructions which leave the function",
|
|
"afbt", "", "show basic blocks of current function in a table",
|
|
"afB", " [bits]", "define asm.bits for the given function",
|
|
NULL
|
|
};
|
|
|
|
static const char *help_msg_afc[] = {
|
|
"Usage:", "afc[agl?]", "# see also tcc command to manage all calling conventions",
|
|
"afc", " ccname", "manually set calling convention for current function",
|
|
"afc", "", "show calling convention for the Current function (same as tcc)",
|
|
"afcr", "[j]", "show register usage for the current function",
|
|
"afcf", "[j] [name]", "prints return type function(arg1, arg2...), see afij",
|
|
"afci", "", "information about the current calling convention",
|
|
"afck", "", "list SDB details of call loaded calling conventions",
|
|
"afcl", "", "list all available calling conventions",
|
|
"afcll", "", "show all call conventions and its definition",
|
|
"afco", " path", "open Calling Convention sdb profile from given path",
|
|
"afcR", "", "register telescoping using the calling conventions order",
|
|
NULL
|
|
};
|
|
|
|
static const char *help_msg_afC[] = {
|
|
"Usage:", "afC", " [addr]",
|
|
"afC", "", "function cycles cost",
|
|
"afCc", "", "cyclomatic complexity",
|
|
"afCl", "", "loop count (backward jumps)",
|
|
NULL
|
|
};
|
|
|
|
static const char *help_msg_afi[] = {
|
|
"Usage:", "afi[jlp*]", " <addr>",
|
|
"afi", "", "show information of the function",
|
|
"afii", "[-][import]", "show/add/delete imports used in function",
|
|
"afi.", "", "show function name in current offset",
|
|
"afi*", "", "function, variables and arguments",
|
|
"afij", "", "function info in json format",
|
|
"afil", "", "verbose function info",
|
|
"afip", "", "show whether the function is pure or not",
|
|
"afiq", "", "show quite few info about the function",
|
|
"afis", "", "show function stats (opcode, meta)",
|
|
NULL
|
|
};
|
|
|
|
static const char *help_msg_afis[] = {
|
|
"Usage:", "afis[ft]", "",
|
|
"afis", "", "enumerate unique opcodes in function",
|
|
"afisa", "[fo]", "enumerate all the meta of all the functions",
|
|
"afisf", "", "enumerate unique opcode families in function",
|
|
"afiso", "", "enumerate unique opcode types in function",
|
|
"afist", " [query]", "list in table format",
|
|
NULL
|
|
};
|
|
|
|
static const char *help_msg_afl[] = {
|
|
"Usage:", "afl", " List all functions",
|
|
"afl", "", "list functions",
|
|
"afl.", "", "display function in current offset (see afi.)",
|
|
"afl+", "", "display sum all function sizes",
|
|
"afl=", "", "display ascii-art bars with function ranges",
|
|
"aflc", "", "count of functions",
|
|
"aflj", "", "list functions in json",
|
|
"aflt", " [query]", "list functions in table format",
|
|
"afll", " [column]", "list functions in verbose mode (sorted by column name)",
|
|
"afllj", "", "list functions in verbose mode (alias to aflj)",
|
|
"aflm", "[?]", "list functions in makefile style (af@@=`aflm~0x`)",
|
|
"aflq", "", "list functions in quiet mode",
|
|
"aflqj", "", "list functions in json quiet mode",
|
|
"afls", "[?asn]", "sort function list by address, size or name",
|
|
"aflx", "", "list function xrefs (who references or calls the current function)",
|
|
NULL
|
|
};
|
|
|
|
static const char *help_msg_afll[] = {
|
|
"Usage:", "", " List functions in verbose mode",
|
|
"", "", "",
|
|
"Table fields:", "", "",
|
|
"", "", "",
|
|
"address", "", "start address",
|
|
"size", "", "function size (realsize)",
|
|
"nbbs", "", "number of basic blocks",
|
|
"edges", "", "number of edges between basic blocks",
|
|
"cc", "", "cyclomatic complexity ( cc = edges - blocks + 2 * exit_blocks)",
|
|
"cost", "", "cyclomatic cost",
|
|
"min bound", "", "minimal address",
|
|
"range", "", "function size",
|
|
"max bound", "", "maximal address",
|
|
"calls", "", "number of caller functions",
|
|
"locals", "", "number of local variables",
|
|
"args", "", "number of function arguments",
|
|
"xref", "", "number of cross references",
|
|
"frame", "", "function stack size",
|
|
"name", "", "function name",
|
|
NULL
|
|
};
|
|
|
|
static const char *help_msg_afn[] = {
|
|
"Usage:", "afn[sa]", " Analyze function names",
|
|
"afn", " [name]", "rename the function",
|
|
"afn", " base64:encodedname", "rename the function",
|
|
"afn.", "", "same as afn without arguments. show the function name in current offset",
|
|
"afna", "", "construct a function name for the current offset",
|
|
"afns", "", "list all strings associated with the current function",
|
|
"afnsj", "", "list all strings associated with the current function in JSON format",
|
|
NULL
|
|
};
|
|
|
|
static const char *help_msg_afs[] = {
|
|
"Usage:", "afs[r]", " Analyze function signatures",
|
|
"afs", "[!] ([fcnsign])", "get/set function signature at current address (afs! uses cfg.editor)",
|
|
"afs*", " ([signame])", "get function signature in flags",
|
|
"afsj", " ([signame])", "get function signature in JSON",
|
|
"afsr", " [function_name] [new_type]", "change type for given function",
|
|
NULL
|
|
};
|
|
|
|
static const char *help_msg_aft[] = {
|
|
"Usage:", "aft", "",
|
|
"aft", "", "type matching analysis for current function",
|
|
NULL
|
|
};
|
|
|
|
static const char *help_msg_afv[] = {
|
|
"Usage:", "afv[rbs]"," Function variables manipulation",
|
|
"afv*", "", "output r2 command to add args/locals to flagspace",
|
|
"afv-", "([name])", "remove all or given var",
|
|
"afv=", "", "list function variables and arguments with disasm refs",
|
|
"afva", "", "analyze function arguments/locals",
|
|
"afvb", "[?]", "manipulate bp based arguments/locals",
|
|
"afvd", " name", "output r2 command for displaying the value of args/locals in the debugger",
|
|
"afvf", "", "show BP relative stackframe variables",
|
|
"afvn", " [new_name] ([old_name])", "rename argument/local",
|
|
"afvr", "[?]", "manipulate register based arguments",
|
|
"afvR", " [varname]", "list addresses where vars are accessed (READ)",
|
|
"afvs", "[?]", "manipulate sp based arguments/locals",
|
|
"afvt", " [name] [new_type]", "change type for given argument/local",
|
|
"afvW", " [varname]", "list addresses where vars are accessed (WRITE)",
|
|
"afvx", "", "show function variable xrefs (same as afvR+afvW)",
|
|
NULL
|
|
};
|
|
|
|
static const char *help_msg_aeim[] = {
|
|
"Usage:", "aeim", " [addr] [size] [name] - initialize the ESIL VM stack",
|
|
"aeim", "", "initialize esil memory with default values from esil.stack.* evals",
|
|
"aeim", " 0x10000", "same as aeim@e:esil.stack.addr=0x10000",
|
|
"aeim", " 0x10000 2M mystack", "give a name to that new 2MB stack",
|
|
NULL
|
|
};
|
|
|
|
static const char *help_msg_afvb[] = {
|
|
"Usage:", "afvb", " [idx] [name] ([type])",
|
|
"afvb", "", "list base pointer based arguments, locals",
|
|
"afvb*", "", "same as afvb but in r2 commands",
|
|
"afvb", " [idx] [name] ([type])", "define base pointer based arguments, locals",
|
|
"afvbj", "", "return list of base pointer based arguments, locals in JSON format",
|
|
"afvb-", " [name]", "delete argument/locals at the given name",
|
|
"afvbg", " [idx] [addr]", "define var get reference",
|
|
"afvbs", " [idx] [addr]", "define var set reference",
|
|
NULL
|
|
};
|
|
|
|
static const char *help_msg_afvr[] = {
|
|
"Usage:", "afvr", " [reg] [type] [name]",
|
|
"afvr", "", "list register based arguments",
|
|
"afvr*", "", "same as afvr but in r2 commands",
|
|
"afvr", " [reg] [name] ([type])", "define register arguments",
|
|
"afvrj", "", "return list of register arguments in JSON format",
|
|
"afvr-", " [name]", "delete register arguments at the given index",
|
|
"afvrg", " [reg] [addr]", "define argument get reference",
|
|
"afvrs", " [reg] [addr]", "define argument set reference",
|
|
NULL
|
|
};
|
|
|
|
static const char *help_msg_afvs[] = {
|
|
"Usage:", "afvs", " [idx] [type] [name]",
|
|
"afvs", "", "list stack based arguments and locals",
|
|
"afvs*", "", "same as afvs but in r2 commands",
|
|
"afvs", " [idx] [name] [type]", "define stack based arguments,locals",
|
|
"afvsj", "", "return list of stack based arguments and locals in JSON format",
|
|
"afvs-", " [name]", "delete stack based argument or locals with the given name",
|
|
"afvsg", " [idx] [addr]", "define var get reference",
|
|
"afvss", " [idx] [addr]", "define var set reference",
|
|
NULL
|
|
};
|
|
|
|
static const char *help_msg_ag[] = {
|
|
"Usage:", "ag<graphtype><format> [addr]", "",
|
|
"Graph commands:", "", "",
|
|
"aga", "[format]", "data references graph",
|
|
"agA", "[format]", "global data references graph",
|
|
"agc", "[format]", "function callgraph",
|
|
"agC", "[format]", "global callgraph",
|
|
"agd", "[format] [fcn addr]", "diff graph",
|
|
"agf", "[format]", "basic blocks function graph",
|
|
"agi", "[format]", "imports graph",
|
|
"agr", "[format]", "references graph",
|
|
"agR", "[format]", "global references graph",
|
|
"agx", "[format]", "cross references graph",
|
|
"agg", "[format]", "custom graph",
|
|
"agt", "[format]", "tree map graph",
|
|
"ag-", "", "clear the custom graph",
|
|
"agn", "[?] title body", "add a node to the custom graph",
|
|
"age", "[?] title1 title2", "add an edge to the custom graph",
|
|
"","","",
|
|
"Output formats:", "", "",
|
|
"<blank>", "", "ascii art",
|
|
"*", "", "r2 commands",
|
|
"b", "", "braile art rendering (agfb)",
|
|
"d", "", "graphviz dot",
|
|
"g", "", "graph Modelling Language (gml)",
|
|
"j", "", "json ('J' for formatted disassembly)",
|
|
"k", "", "sdb key-value",
|
|
"m", "", "mermaid",
|
|
"t", "", "tiny ascii art",
|
|
"v", "", "interactive ascii art",
|
|
"w", " [path]", "write to path or display graph image (see graph.gv.format and graph.web)",
|
|
NULL
|
|
};
|
|
|
|
static const char *help_msg_age[] = {
|
|
"Usage:", "age [title1] [title2]", "",
|
|
"Examples:", "", "",
|
|
"age", " title1 title2", "add an edge from the node with \"title1\" as title to the one with title \"title2\"",
|
|
"age", " \"title1 with spaces\" title2", "add an edge from node \"title1 with spaces\" to node \"title2\"",
|
|
"age-", " title1 title2", "remove an edge from the node with \"title1\" as title to the one with title \"title2\"",
|
|
"ageh", "", "list all the highlighted edges",
|
|
"ageh", " nodeA nodeB", "highlight edge between nodeA and nodeB",
|
|
"ageh-", " nodeA nodeB", "highlight edge between nodeA and nodeB",
|
|
"age?", "", "show this help",
|
|
NULL
|
|
};
|
|
|
|
static const char *help_msg_agn[] = {
|
|
"Usage:", "agn [title] [body]", "",
|
|
"Examples:", "", "",
|
|
"agn", " title1 body1", "add a node with title \"title1\" and body \"body1\"",
|
|
"agn", " \"title with space\" \"body with space\"", "add a node with spaces in the title and in the body",
|
|
"agn", " title1 base64:Ym9keTE=", "add a node with the body specified as base64",
|
|
"agn-", " title1", "remove a node with title \"title1\"",
|
|
"agn?", "", "show this help",
|
|
NULL
|
|
};
|
|
|
|
static const char *help_msg_ah[] = {
|
|
"Usage:", "ah[lba-]", "analysis Hints",
|
|
"ah?", "", "show this help",
|
|
"ah?", " offset", "show hint of given offset",
|
|
"ah", "", "list hints in human-readable format",
|
|
"ah.", "", "list hints in human-readable format from current offset",
|
|
"ah-", "", "remove all hints",
|
|
"ah-", " offset [size]", "remove hints at given offset",
|
|
"ah*", " offset", "list hints in radare commands format",
|
|
"aha", " ppc @ 0x42", "force arch ppc for all addrs >= 0x42 or until the next hint",
|
|
"aha", " 0 @ 0x84", "disable the effect of arch hints for all addrs >= 0x84 or until the next hint",
|
|
"ahb", "[-*] [8,16,32,64] @ 0x42", "get/set asm.bits for given address and beyond",
|
|
"ahc", " 0x804804", "override call/jump address",
|
|
"ahd", " foo a0,33", "replace opcode string",
|
|
"ahe", " 3,eax,+=", "set vm analysis string",
|
|
"ahf", " 0x804840", "override fallback address for call",
|
|
"ahF", " 0x10", "set stackframe size at current offset",
|
|
"ahh", " 0x804840", "highlight this address offset in disasm",
|
|
"ahi", "[?] 10", "define numeric base for immediates (2, 8, 10, 10u, 16, i, p, S, s)",
|
|
"ahj", "", "list hints in JSON",
|
|
"aho", " call", "change opcode type (see aho?)",
|
|
"ahp", " addr", "set pointer hint",
|
|
"ahr", " val", "set hint for return value of a function",
|
|
"ahs", " 4", "set opcode size=4",
|
|
"ahS", " jz", "set asm.syntax=jz for this opcode",
|
|
"aht", "[s][?] <type>", "mark immediate as a type offset (deprecated, moved to \"aho\")",
|
|
"ahv", " val", "change opcode's val field (useful to set jmptbl sizes in jmp rax)",
|
|
NULL
|
|
};
|
|
|
|
static const char *help_msg_aho[] = {
|
|
"Usage:", "aho [optype] [@ addr]", " Define opcode type hint",
|
|
"aho", " nop", "change the opcode type in current address to be considered a NOP",
|
|
"aho", "", "show the current opcode hint if any",
|
|
"aho-", "$$", "delete hints in current offset",
|
|
"aho*", "", "same as above but in r2 commands syntax",
|
|
NULL
|
|
};
|
|
|
|
static const char *help_msg_ahb[] = {
|
|
"Usage:", "ahb [8|16|32|64] [@ addr]", " Define asm.bits hint at given address",
|
|
"ahb", " 16", "set asm.bits=16 in the given address",
|
|
"ahb", "", "get asm.bits used in given addr (current seek)",
|
|
"ahb", "-$$", "delete all the hints in the given address",
|
|
"ahb*", "", "show defined bits hints as r2 commands",
|
|
NULL
|
|
};
|
|
|
|
static const char *help_msg_ahr[] = {
|
|
"Usage:", "ahr addr", " Set instruction as return type (similar to 'aho ret'?)",
|
|
"ahr", " $$", "current instruction may be considered as the end of a function",
|
|
NULL
|
|
};
|
|
|
|
static const char *help_msg_ahi[] = {
|
|
"Usage:", "ahi [2|8|10|10u|16|bodhipSs] [@ offset]", " Define numeric base",
|
|
"ahi", " <base>", "set numeric base (2, 8, 10, 16)",
|
|
"ahi", " 10|d", "set base to signed decimal (10), sign bit should depend on receiver size",
|
|
"ahi", " 10u|du", "set base to unsigned decimal (11)",
|
|
"ahi", " b", "set base to binary (2)",
|
|
"ahi", " o", "set base to octal (8)",
|
|
"ahi", " h", "set base to hexadecimal (16)",
|
|
"ahi", " i", "set base to IP address (32)",
|
|
"ahi", " p", "set base to htons(port) (3)",
|
|
"ahi", " S", "set base to syscall (80)",
|
|
"ahi", " s", "set base to string (1)",
|
|
"ahi1", " 10", "set base of argument 1 to base 10 (same as ahi1 d)",
|
|
NULL
|
|
};
|
|
|
|
static const char *help_msg_aht[] = {
|
|
"Usage:", "aht[s] [addr|type]", "mark immediate as type offset (moved to aho)",
|
|
"ahts", " <offset>", "list all matching structure offsets",
|
|
"aht", " <struct.member>", "change immediate to structure offset",
|
|
"aht?", "", "show this help",
|
|
NULL
|
|
};
|
|
|
|
static const char *help_msg_aot[] = {
|
|
"Usage:", "aot[l]", "list opcode types",
|
|
"aot", "", "show type of the current instruction",
|
|
"aotl", "", "list all possible opcode types (See /atl)",
|
|
NULL
|
|
};
|
|
|
|
static const char *help_msg_aom[] = {
|
|
"Usage:", "aom[ljd] [arg]", "list opcode mnemonics",
|
|
"aom", "", "show instruction mnemonic",
|
|
"aom.", "", "show instruction mnemonic in current address",
|
|
"aoml", "", "list all mnemonics",
|
|
"aomj", "", "list in json format (TODO: add instruction description too?)",
|
|
"aomd", "", "verbose mnemonic listing with instruction description",
|
|
NULL
|
|
};
|
|
|
|
static const char *help_msg_ao[] = {
|
|
"Usage:", "ao[e?] [len]", "analyze Opcodes",
|
|
"ao", " 5", "display opcode analysis of 5 opcodes",
|
|
"ao*", "", "display opcode in r commands",
|
|
"aoc", " [cycles]", "analyze which op could be executed in [cycles]",
|
|
"aod", " [mnemonic]", "describe opcode for asm.arch",
|
|
"aoda", "", "show all mnemonic descriptions",
|
|
"aoe", " N", "display esil form for N opcodes",
|
|
"aoef", " expr", "filter esil expression of opcode by given output",
|
|
"aoeq", " N", "display only the esil expression of N opcodes",
|
|
"aoj", " N", "display opcode analysis information in JSON for N opcodes",
|
|
"aom", "[?] [id]", "list current or all mnemonics for current arch",
|
|
"aor", " [N]", "run N esil instructions + esil.dumpstack",
|
|
"aos", " N", "display size of N opcodes",
|
|
"aot", "[?]", "list all opcode types",
|
|
NULL
|
|
};
|
|
|
|
static const char *help_msg_ar[] = {
|
|
"Usage: ar", "", "# Analysis Registers",
|
|
"ar", "", "show 'gpr' registers",
|
|
"ar.", ">$snapshot", "show r2 commands to set register values to the current state",
|
|
"ar,", "", "show registers in table format (see dr,)",
|
|
".ar*", "", "import register values as flags",
|
|
".ar-", "", "unflag all registers",
|
|
"ar0", "", "reset register arenas to 0",
|
|
"ara", "[?]", "manage register arenas",
|
|
"arj", "", "show 'gpr' registers in JSON format",
|
|
"arA", "", "show values of function argument calls (A0, A1, A2, ..)",
|
|
"ar", " 16", "show 16 bit registers",
|
|
"ar", " 32", "show 32 bit registers",
|
|
"ar", " all", "show all bit registers",
|
|
"ar", " <type>", "show all registers of given type",
|
|
"arC", "", "display register profile comments",
|
|
"arr", "", "show register references (telescoping)",
|
|
"arrj", "", "show register references (telescoping) in JSON format",
|
|
"ar=", "([size])(:[regs])", "show register values in columns",
|
|
"ar?", " <reg>", "show register value",
|
|
"arb", " <type>", "display hexdump of the given arena",
|
|
"arc", "[cq=] <name>", "conditional flag registers",
|
|
"arcc", "", "derive calling convention from the register profile",
|
|
"ard", " <name>", "show only different registers",
|
|
"arn", " <regalias>", "get regname for pc,sp,bp,a0-3,zf,cf,of,sg",
|
|
"aro", "", "show old (previous) register values",
|
|
"arp", "[?] <file>", "load register profile from file",
|
|
"ars", "", "stack register state",
|
|
"arS", "", "show the size of the register profile",
|
|
"art", "", "list all register types",
|
|
"arw", " <hexnum>", "set contents of the register arena",
|
|
NULL
|
|
};
|
|
|
|
static const char *help_msg_ara[] = {
|
|
"Usage:", "ara[+-s]", "register Arena Push/Pop/Swap",
|
|
"ara", "", "show all register arenas allocated",
|
|
"ara", "+", "push a new register arena for each type",
|
|
"ara", "-", "pop last register arena",
|
|
"aras", "", "swap last two register arenas",
|
|
NULL
|
|
};
|
|
|
|
static const char *help_msg_arw[] = {
|
|
"Usage:", "arw ", "# Set contents of the register arena",
|
|
"arw", " <hexnum>", "set contents of the register arena",
|
|
NULL
|
|
};
|
|
|
|
static const char *help_msg_as[] = {
|
|
"Usage: as[ljk?]", "", "syscall name <-> number utility",
|
|
"as", "", "show current syscall and arguments",
|
|
"as", " 4", "show syscall 4 based on asm.os and current regs/mem",
|
|
"asc[a]", " 4", "dump syscall info in .asm or .h",
|
|
"asj", "", "list of syscalls in JSON",
|
|
"asl", "", "list of syscalls by asm.os and asm.arch",
|
|
"asl", " close", "returns the syscall number for close",
|
|
"asl", " 4", "returns the name of the syscall number 4",
|
|
"ask", " [query]", "perform syscall/ queries",
|
|
NULL
|
|
};
|
|
|
|
static const char *help_msg_av[] = {
|
|
"Usage:", "av[?jr*]", " C++ vtables and RTTI",
|
|
"av", "", "search for vtables in data sections and show results",
|
|
"avj", "", "like av, but as json",
|
|
"av*", "", "like av, but as r2 commands",
|
|
"avr", "[j@addr]", "try to parse RTTI at vtable addr (see anal.cxxabi)",
|
|
"avra", "[j]", "search for vtables and try to parse RTTI at each of them",
|
|
"avrr", "", "recover class info from all findable RTTI (see ac)",
|
|
"avrD", " [classname]", "demangle a class name from RTTI",
|
|
NULL
|
|
};
|
|
|
|
static const char *help_msg_ax[] = {
|
|
"Usage:", "ax[?d-l*]", " # see also 'afx?'",
|
|
"ax", "", "list refs",
|
|
"ax*", "", "output radare commands",
|
|
"ax", " addr [at]", "add code ref pointing to addr (from curseek)",
|
|
"ax-", " [at]", "clean all refs/refs from addr",
|
|
"ax-*", "", "clean all refs/refs",
|
|
"ax.", " [addr]", "find data/code references from and to this address",
|
|
"axc", " addr [at]", "add generic code ref",
|
|
"axC", " addr [at]", "add code call ref",
|
|
"axd", " addr [at]", "add data ref",
|
|
"axf", "[?] [addr]", "find data/code references from this address",
|
|
"axff[j]", " [addr]", "find data/code references from this function",
|
|
"axF", " [flg-glob]", "find data/code references of flags",
|
|
"axg", " [addr]", "show xrefs graph to reach current function",
|
|
"axg*", " [addr]", "show xrefs graph to given address, use .axg*;aggv",
|
|
"axgj", " [addr]", "show xrefs graph to reach current function in json format",
|
|
"axj", "", "list refs in json format",
|
|
"axl", "[cq]", "list xrefs (axlc = count, axlq = quiet)",
|
|
"axm", " addr [at]", "copy data/code references pointing to addr to also point to curseek (or at)",
|
|
"axq", "", "list refs in quiet/human-readable format",
|
|
"axr", " addr [at]", "add data-read ref",
|
|
"axt", "[?] [addr]", "find data/code references to this address",
|
|
"axv", "[?] [addr]", "list local variables read-write-exec references",
|
|
"axw", " addr [at]", "add data-write ref",
|
|
"axs", " addr [at]", "add string ref",
|
|
NULL
|
|
};
|
|
|
|
static const char *help_msg_axl[]= {
|
|
"Usage:", "axl[cq]", "show global xrefs",
|
|
"axl", "", "list all xrefs",
|
|
"axlc", "", "count how many xrefs are registered",
|
|
"axlq", "", "list xrefs in quiet mode (axq)",
|
|
NULL
|
|
};
|
|
|
|
static const char *help_msg_axv[]= {
|
|
"Usage:", "axv[?j]", "show xrefs to local variables in current function",
|
|
"axv", " ([addr])", "optionally you can specify address instead of current seek",
|
|
"axvj", " ([addr])", "show in json",
|
|
NULL
|
|
};
|
|
|
|
static const char *help_msg_axt[]= {
|
|
"Usage:", "axt[?gq*]", "find data/code references to this address",
|
|
"axtj", " [addr]", "find data/code references to this address and print in json format",
|
|
"axtg", " [addr]", "display commands to generate graphs according to the xrefs",
|
|
"axtq", " [addr]", "find and list the data/code references in quiet mode",
|
|
"axtm", " [addr]", "show xrefs to in 'make' syntax (see aflm and axfm)",
|
|
"axt*", " [addr]", "same as axt, but prints as r2 commands",
|
|
NULL
|
|
};
|
|
|
|
static const char *help_msg_axf[]= {
|
|
"Usage:", "axf[?gq*]", "find data/code references from this address",
|
|
"axfj", " [addr]", "find data/code references to this address and print in json format",
|
|
"axfg", " [addr]", "display commands to generate graphs according to the xrefs",
|
|
"axfq", " [addr]", "find and list the data/code references in quiet mode",
|
|
"axfm", " [addr]", "show refs to in 'make' syntax (see aflm and axtm)",
|
|
"axf*", " [addr]", "same as axt, but prints as r2 commands",
|
|
NULL
|
|
};
|
|
|
|
static int cmpname(const void *_a, const void *_b) {
|
|
const RAnalFunction *a = _a, *b = _b;
|
|
return (int)strcmp (a->name, b->name);
|
|
}
|
|
|
|
static int cmpsize(const void *a, const void *b) {
|
|
ut64 sa = (int) r_anal_function_linear_size ((RAnalFunction *) a);
|
|
ut64 sb = (int) r_anal_function_linear_size ((RAnalFunction *) b);
|
|
return (sa > sb)? -1: (sa < sb)? 1 : 0;
|
|
}
|
|
|
|
static int cmpbbs(const void *_a, const void *_b) {
|
|
const RAnalFunction *a = _a, *b = _b;
|
|
int la = (int)r_list_length (a->bbs);
|
|
int lb = (int)r_list_length (b->bbs);
|
|
return (la > lb)? -1: (la < lb)? 1 : 0;
|
|
}
|
|
|
|
static int cmpaddr(const void *_a, const void *_b) {
|
|
const RAnalFunction *a = _a, *b = _b;
|
|
return (a->addr > b->addr)? 1: (a->addr < b->addr)? -1: 0;
|
|
}
|
|
|
|
static bool listOpDescriptions(void *_core, const char *k, const char *v) {
|
|
r_cons_printf ("%s=%s\n", k, v);
|
|
return true;
|
|
}
|
|
|
|
/* better aac for windows-x86-32 */
|
|
#define JAYRO_03 0
|
|
|
|
#if JAYRO_03
|
|
|
|
static bool anal_is_bad_call(RCore *core, ut64 from, ut64 to, ut64 addr, ut8 *buf, int bufi) {
|
|
ut64 align = R_ABS (addr % PE_ALIGN);
|
|
ut32 call_bytes;
|
|
|
|
// XXX this is x86 specific
|
|
if (align == 0) {
|
|
call_bytes = (ut32)((ut8*)buf)[bufi + 3] << 24;
|
|
call_bytes |= (ut32)((ut8*)buf)[bufi + 2] << 16;
|
|
call_bytes |= (ut32)((ut8*)buf)[bufi + 1] << 8;
|
|
call_bytes |= (ut32)((ut8*)buf)[bufi];
|
|
} else {
|
|
call_bytes = (ut32)((ut8*)buf)[bufi - align + 3] << 24;
|
|
call_bytes |= (ut32)((ut8*)buf)[bufi - align + 2] << 16;
|
|
call_bytes |= (ut32)((ut8*)buf)[bufi - align + 1] << 8;
|
|
call_bytes |= (ut32)((ut8*)buf)[bufi - align];
|
|
}
|
|
if (call_bytes >= from && call_bytes <= to) {
|
|
return true;
|
|
}
|
|
call_bytes = (ut32)((ut8*)buf)[bufi + 4] << 24;
|
|
call_bytes |= (ut32)((ut8*)buf)[bufi + 3] << 16;
|
|
call_bytes |= (ut32)((ut8*)buf)[bufi + 2] << 8;
|
|
call_bytes |= (ut32)((ut8*)buf)[bufi + 1];
|
|
call_bytes += addr + 5;
|
|
if (call_bytes >= from && call_bytes <= to) {
|
|
return false;
|
|
}
|
|
return false;
|
|
}
|
|
#endif
|
|
|
|
static ut64 faddr(RCore *core, ut64 addr, bool *nr) {
|
|
RList *fcns = r_anal_get_functions_in (core->anal, addr);
|
|
if (fcns && r_list_length (fcns) > 0) {
|
|
RListIter *iter;
|
|
RAnalFunction *fcn;
|
|
r_list_foreach (fcns, iter, fcn) {
|
|
if (nr && fcn->is_noreturn) {
|
|
*nr = true;
|
|
}
|
|
return fcn->addr;
|
|
}
|
|
}
|
|
r_list_free (fcns);
|
|
return addr;
|
|
}
|
|
|
|
// function argument types and names into anal/types
|
|
static void __add_vars_sdb(RCore *core, RAnalFunction *fcn) {
|
|
RAnalFcnVarsCache cache;
|
|
r_anal_function_vars_cache_init (core->anal, &cache, fcn);
|
|
RListIter *iter;
|
|
RAnalVar *var;
|
|
size_t arg_count = 0;
|
|
|
|
char *args = r_str_newf ("func.%s.args", fcn->name);
|
|
RList *all_vars = cache.rvars;
|
|
r_list_join (all_vars, cache.bvars);
|
|
r_list_join (all_vars, cache.svars);
|
|
#if 0
|
|
r_list_foreach (all_vars, iter, var) {
|
|
if (var->isarg) {
|
|
arg_count++;
|
|
}
|
|
}
|
|
int old_arg_count = r_num_get (NULL, args);
|
|
if (old_arg_count >= arg_count) {
|
|
return;
|
|
}
|
|
#endif
|
|
r_list_foreach (all_vars, iter, var) {
|
|
if (var->isarg) {
|
|
char *k = r_str_newf ("func.%s.arg.%d", fcn->name, (int)arg_count);
|
|
const char *o = sdb_const_get (core->anal->sdb_types, k, 0);
|
|
char *comma = o? strchr (o, ','): NULL;
|
|
char *db_type = comma? r_str_ndup (o, comma - o): NULL;
|
|
char *db_name = comma? strdup (comma + 1): NULL;
|
|
if (!strstr (var->name, "arg_") || (o && strstr (o, ",arg_"))) {
|
|
// #if 0
|
|
char *ks = r_str_newf ("func.%s.arg.%d", fcn->name, (int)arg_count);
|
|
// eprintf ("VARNAME %s %s %c", var->name, db_name, 10);
|
|
// eprintf ("VARTYPE %s %s %c", var->type,db_type, 10);
|
|
char *type = db_type && strstr (var->type, "arg_")? db_type: var->type;
|
|
char *v = r_str_newf ("%s,%s", type, var->name);
|
|
sdb_set (core->anal->sdb_types, ks, v, 0);
|
|
free (ks);
|
|
free (v);
|
|
} else {
|
|
char *name = db_name? db_name: var->name;
|
|
char *type = strdup (db_type? db_type: var->type);
|
|
// eprintf ("VARTYPE1 %s %s %c", var->type,db_type, 10);
|
|
if (var->name && !strstr (var->name, "arg_")) {
|
|
o = NULL;
|
|
}
|
|
#if 0
|
|
if (name != var->name) {
|
|
o = NULL;
|
|
} else {
|
|
type = strdup (var->type);
|
|
}
|
|
#endif
|
|
char *v = comma? strdup (o): r_str_newf ("%s,%s", type, name);
|
|
/// eprintf("arg (%s) %s -- %s%c", k, v, var->name, 10);
|
|
char *s = strdup (name);
|
|
if (o) {
|
|
char *v2 = r_str_newf ("%s,%s", var->type, name);
|
|
if (!strstr (var->name, ",arg_")) {
|
|
free (var->name);
|
|
var->name = s;
|
|
} else {
|
|
free (s);
|
|
}
|
|
// sdb_set (core->anal->sdb_types, k, v, 0);
|
|
free (v2);
|
|
} else {
|
|
free (var->name);
|
|
var->name = s;
|
|
sdb_set (core->anal->sdb_types, k, v, 0);
|
|
}
|
|
free (v);
|
|
free (type);
|
|
// #endif
|
|
}
|
|
free (db_name);
|
|
free (db_type);
|
|
free (k);
|
|
arg_count++;
|
|
}
|
|
}
|
|
// sdb_num_set (core->anal->sdb_types, args, (int)arg_count, 0);
|
|
if (arg_count > 0) {
|
|
char *k = r_str_newf ("func.%s.args", fcn->name);
|
|
char *v = r_str_newf ("%d", (int)arg_count);
|
|
sdb_set (core->anal->sdb_types, k, v, 0);
|
|
// sdb_num_set (core->anal->sdb_types, k, (ut64)arg_count, 0);
|
|
free (k);
|
|
free (v);
|
|
}
|
|
free (args);
|
|
r_anal_function_vars_cache_fini (&cache);
|
|
}
|
|
|
|
static bool cmd_anal_aaft(RCore *core) {
|
|
RListIter *it;
|
|
RAnalFunction *fcn;
|
|
ut64 seek;
|
|
const char *io_cache_key = "io.pcache.write";
|
|
bool io_cache = r_config_get_b (core->config, io_cache_key);
|
|
if (r_config_get_b (core->config, "cfg.debug")) {
|
|
R_LOG_WARN ("aaft is disabled in debugger mode");
|
|
return false;
|
|
}
|
|
if (!io_cache) {
|
|
// XXX. we shouldnt need this, but it breaks 'r2 -c aaa -w ls'
|
|
r_config_set_i (core->config, io_cache_key, true);
|
|
}
|
|
const bool iova = r_config_get_b (core->config, "io.va");
|
|
seek = core->offset;
|
|
r_reg_arena_push (core->anal->reg);
|
|
r_reg_arena_zero (core->anal->reg);
|
|
r_core_cmd0 (core, "aei;aeim");
|
|
int saved_arena_size = 0;
|
|
ut8 *saved_arena = r_reg_arena_peek (core->anal->reg, &saved_arena_size);
|
|
// Iterating Reverse so that we get function in top-bottom call order
|
|
r_list_foreach_prev (core->anal->fcns, it, fcn) {
|
|
int ret = r_core_seek (core, fcn->addr, true);
|
|
if (!ret) {
|
|
continue;
|
|
}
|
|
r_reg_arena_poke (core->anal->reg, saved_arena, saved_arena_size);
|
|
r_anal_esil_set_pc (core->anal->esil, fcn->addr);
|
|
r_core_anal_type_match (core, fcn);
|
|
if (r_cons_is_breaked ()) {
|
|
break;
|
|
}
|
|
__add_vars_sdb (core, fcn);
|
|
}
|
|
r_config_set_b (core->config, "io.va", iova);
|
|
r_core_seek (core, seek, true);
|
|
r_reg_arena_pop (core->anal->reg);
|
|
r_config_set_i (core->config, io_cache_key, io_cache);
|
|
free (saved_arena);
|
|
return true;
|
|
}
|
|
|
|
static void type_cmd(RCore *core, const char *input) {
|
|
RAnalFunction *fcn = r_anal_get_fcn_in (core->anal, core->offset, -1);
|
|
if (!fcn && *input != '?') {
|
|
R_LOG_WARN ("cant find function here");
|
|
return;
|
|
}
|
|
ut64 seek;
|
|
r_cons_break_push (NULL, NULL);
|
|
switch (*input) {
|
|
case '\0': // "aft"
|
|
seek = core->offset;
|
|
r_anal_esil_set_pc (core->anal->esil, fcn? fcn->addr: core->offset);
|
|
r_core_anal_type_match (core, fcn);
|
|
r_core_seek (core, seek, true);
|
|
break;
|
|
case '?':
|
|
r_core_cmd_help (core, help_msg_aft);
|
|
break;
|
|
}
|
|
r_cons_break_pop ();
|
|
}
|
|
|
|
static void find_refs(RCore *core, const char *glob) {
|
|
ut64 curseek = core->offset;
|
|
while (*glob == ' ') {
|
|
glob++;
|
|
}
|
|
if (!*glob) {
|
|
glob = "str.";
|
|
}
|
|
if (*glob == '?') {
|
|
eprintf ("Usage: axF [flag-str-filter]\n");
|
|
return;
|
|
}
|
|
R_LOG_WARN ("Finding references of flags matching '%s'", glob);
|
|
char *cmd = r_str_newf (".(findstref) @@=`f~%s[0]`", glob);
|
|
r_core_cmd0 (core, "(findstref;f here=$$;s entry0;/r here;f-here)");
|
|
r_core_cmd0 (core, cmd);
|
|
r_core_cmd0 (core, "(-findstref)");
|
|
r_core_seek (core, curseek, true);
|
|
free (cmd);
|
|
}
|
|
|
|
static ut64 sort64val(const void *a) {
|
|
ut64 *na = (ut64*)a;
|
|
return *na;
|
|
}
|
|
static int sort64(const void *a, const void *b) {
|
|
ut64 *na = (ut64*)a;
|
|
ut64 *nb = (ut64*)b;
|
|
return *na - *nb;
|
|
}
|
|
|
|
static RList *collect_addresses(RCore *core) {
|
|
#if 0
|
|
WIP: return addresses where functions start from different sources:
|
|
* [x] symbols
|
|
* [ ] exports
|
|
* [ ] prelude search
|
|
* [ ] call ref analysis
|
|
result is then sorted and uniqified
|
|
#endif
|
|
RList *list = r_list_newf (free);
|
|
RListIter *iter;
|
|
RBinSymbol *sym;
|
|
RList *syms = r_bin_get_symbols (core->bin);
|
|
r_list_foreach (syms, iter, sym) {
|
|
r_list_append (list, ut64_new (sym->vaddr));
|
|
}
|
|
// find all calls and mark the destinations as function entrypoints
|
|
// r_core_search_preludes (core, true); // __prelude_cb_hit uses globals and calls 'af', should be changed to just return a list for later processing
|
|
r_list_sort (list, sort64);
|
|
r_list_uniq_inplace (list, sort64val);
|
|
return list;
|
|
}
|
|
|
|
static void single_block_analysis(RCore *core) {
|
|
const ut64 max_fcn_size = 1024 * 1024;
|
|
RList *list = collect_addresses (core);
|
|
RListIter *iter;
|
|
ut64 *addr;
|
|
r_list_foreach (list, iter, addr) {
|
|
ut64 *next = iter->n? iter->n->data: addr;
|
|
int len = (*next) - *addr;
|
|
if (len > 0 && len < max_fcn_size) {
|
|
ut64 at = *addr;
|
|
if (r_anal_get_function_at (core->anal, at)) {
|
|
continue;
|
|
}
|
|
RFlagItem *fi = r_flag_get_at (core->flags, at, false);
|
|
char *name = (fi) ? strdup (fi->name): r_str_newf ("fcn.%08"PFMT64x, at);
|
|
RAnalFunction *fcn = r_anal_create_function (core->anal, name, at, 0, NULL);
|
|
if (fcn) {
|
|
r_anal_function_add_bb (core->anal, fcn, at, len, UT64_MAX, UT64_MAX, 0);
|
|
}
|
|
free (name);
|
|
}
|
|
}
|
|
r_list_free (list);
|
|
}
|
|
|
|
/* set flags for every function */
|
|
static void flag_every_function(RCore *core) {
|
|
RListIter *iter;
|
|
RAnalFunction *fcn;
|
|
r_flag_space_push (core->flags, R_FLAGS_FS_FUNCTIONS);
|
|
r_list_foreach (core->anal->fcns, iter, fcn) {
|
|
r_flag_set (core->flags, fcn->name,
|
|
fcn->addr, r_anal_function_size_from_entry (fcn));
|
|
}
|
|
r_flag_space_pop (core->flags);
|
|
}
|
|
|
|
static void var_help(RCore *core, char ch) {
|
|
switch (ch) {
|
|
case 'b':
|
|
r_core_cmd_help (core, help_msg_afvb);
|
|
break;
|
|
case 's':
|
|
r_core_cmd_help (core, help_msg_afvs);
|
|
break;
|
|
case 'r':
|
|
r_core_cmd_help (core, help_msg_afvr);
|
|
break;
|
|
default:
|
|
r_core_cmd_help (core, help_msg_afv);
|
|
break;
|
|
}
|
|
}
|
|
|
|
static void var_accesses_list(RAnalFunction *fcn, RAnalVar *var, PJ *pj, int access_type, const char *name) {
|
|
RAnalVarAccess *acc;
|
|
bool first = true;
|
|
if (r_vector_empty (&var->accesses)) {
|
|
R_LOG_WARN ("Variable '%s' have no references?", name);
|
|
}
|
|
if (pj) {
|
|
pj_o (pj);
|
|
pj_ks (pj, "name", name);
|
|
pj_ka (pj, "addrs");
|
|
} else {
|
|
r_cons_printf ("%10s", name);
|
|
}
|
|
r_vector_foreach (&var->accesses, acc) {
|
|
if (!(acc->type & access_type)) {
|
|
continue;
|
|
}
|
|
ut64 addr = fcn->addr + acc->offset;
|
|
if (pj) {
|
|
pj_n (pj, addr);
|
|
} else {
|
|
r_cons_printf ("%s0x%" PFMT64x, first ? " " : ",", addr);
|
|
}
|
|
first = false;
|
|
}
|
|
if (pj) {
|
|
pj_end (pj);
|
|
pj_end (pj);
|
|
} else {
|
|
r_cons_newline ();
|
|
}
|
|
}
|
|
|
|
static void list_vars(RCore *core, RAnalFunction *fcn, PJ *pj, int type, const char *name) {
|
|
RAnalVar *var = NULL;
|
|
RListIter *iter;
|
|
RList *list = r_anal_var_all_list (core->anal, fcn);
|
|
if (type == '=') {
|
|
ut64 oaddr = core->offset;
|
|
r_list_foreach (list, iter, var) {
|
|
r_cons_printf ("* %s\n", var->name);
|
|
RAnalVarAccess *acc;
|
|
r_vector_foreach (&var->accesses, acc) {
|
|
if (!(acc->type & R_ANAL_VAR_ACCESS_TYPE_READ)) {
|
|
continue;
|
|
}
|
|
r_cons_printf ("R 0x%"PFMT64x" ", fcn->addr + acc->offset);
|
|
r_core_seek (core, fcn->addr + acc->offset, 1);
|
|
r_core_print_disasm_instructions (core, 0, 1);
|
|
}
|
|
r_vector_foreach (&var->accesses, acc) {
|
|
if (!(acc->type & R_ANAL_VAR_ACCESS_TYPE_WRITE)) {
|
|
continue;
|
|
}
|
|
r_cons_printf ("W 0x%"PFMT64x" ", fcn->addr + acc->offset);
|
|
r_core_seek (core, fcn->addr + acc->offset, 1);
|
|
r_core_print_disasm_instructions (core, 0, 1);
|
|
}
|
|
}
|
|
r_core_seek (core, oaddr, 0);
|
|
r_list_free (list);
|
|
return;
|
|
}
|
|
if (type == '*') {
|
|
const char *bp = r_reg_get_name (core->anal->reg, R_REG_NAME_BP);
|
|
r_cons_printf ("f-fcnvar*\n");
|
|
r_list_foreach (list, iter, var) {
|
|
r_cons_printf ("f fcnvar.%s @ %s%s%d\n", var->name, bp,
|
|
var->delta >= 0? "+":"", var->delta);
|
|
}
|
|
r_list_free (list);
|
|
return;
|
|
}
|
|
if (type != 'W' && type != 'R') {
|
|
r_list_free (list);
|
|
return;
|
|
}
|
|
int access_type = type == 'R' ? R_ANAL_VAR_ACCESS_TYPE_READ : R_ANAL_VAR_ACCESS_TYPE_WRITE;
|
|
if (pj) {
|
|
pj_a (pj);
|
|
}
|
|
if (name && *name) {
|
|
var = r_anal_function_get_var_byname (fcn, name);
|
|
if (var) {
|
|
var_accesses_list (fcn, var, pj, access_type, var->name);
|
|
}
|
|
} else {
|
|
r_list_foreach (list, iter, var) {
|
|
var_accesses_list (fcn, var, pj, access_type, var->name);
|
|
}
|
|
}
|
|
if (pj) {
|
|
pj_end (pj);
|
|
}
|
|
r_list_free (list);
|
|
}
|
|
|
|
static void cmd_afvx(RCore *core, RAnalFunction *fcn, bool json) {
|
|
r_return_if_fail (core);
|
|
if (!fcn) {
|
|
fcn = r_anal_get_fcn_in (core->anal, core->offset, R_ANAL_FCN_TYPE_ANY);
|
|
}
|
|
if (fcn) {
|
|
PJ *pj = NULL;
|
|
if (json) {
|
|
pj = pj_new ();
|
|
pj_o (pj);
|
|
pj_k (pj, "reads");
|
|
} else {
|
|
r_cons_printf ("afvR\n");
|
|
}
|
|
list_vars (core, fcn, pj, 'R', NULL);
|
|
if (json) {
|
|
pj_k (pj, "writes");
|
|
} else {
|
|
r_cons_printf ("afvW\n");
|
|
}
|
|
list_vars (core, fcn, pj, 'W', NULL);
|
|
if (json) {
|
|
pj_end (pj);
|
|
char *j = pj_drain (pj);
|
|
r_cons_printf ("%s\n", j);
|
|
free (j);
|
|
}
|
|
}
|
|
}
|
|
|
|
static int cmd_an(RCore *core, const char *name, int mode) {
|
|
int ret = 0;
|
|
RAnalOp op = {0};
|
|
PJ *pj = NULL;
|
|
|
|
if (mode == 'j') {
|
|
pj = pj_new ();
|
|
pj_a (pj);
|
|
}
|
|
if (r_anal_op (core->anal, &op, core->offset, core->block, core->blocksize, R_ANAL_OP_MASK_BASIC) < 1) {
|
|
goto failure;
|
|
}
|
|
RAnalVar *var = r_anal_get_used_function_var (core->anal, op.addr);
|
|
|
|
ut64 tgt_addr = op.jump != UT64_MAX? op.jump: op.ptr;
|
|
if (var) {
|
|
if (name) {
|
|
ret = r_anal_var_rename (var, name, true) ? 0 : -1;
|
|
} else if (mode == '*') {
|
|
r_cons_printf ("f %s=0x%" PFMT64x "\n", var->name, tgt_addr);
|
|
} else if (mode == 'j') {
|
|
pj_o (pj);
|
|
pj_ks (pj, "name", var->name);
|
|
pj_ks (pj, "type", "var");
|
|
pj_kn (pj, "offset", tgt_addr);
|
|
pj_end (pj);
|
|
} else {
|
|
r_cons_println (var->name);
|
|
}
|
|
} else {
|
|
if (tgt_addr == UT64_MAX) {
|
|
tgt_addr = core->offset;
|
|
}
|
|
RFlagItem *f = r_flag_get_by_spaces (core->flags, tgt_addr, R_FLAGS_FS_SYMBOLS, R_FLAGS_FS_IMPORTS, NULL);
|
|
if (!f) {
|
|
f = r_flag_get_i (core->flags, tgt_addr);
|
|
}
|
|
RAnalFunction *fcn = r_anal_get_function_at (core->anal, tgt_addr);
|
|
if (fcn) {
|
|
if (name) {
|
|
ret = r_anal_function_rename (fcn, name)? 0: -1;
|
|
} else if (mode == '*') {
|
|
r_cons_printf ("f %s=0x%" PFMT64x "\n", fcn->name, core->offset);
|
|
} else if (mode == 'j') {
|
|
pj_o (pj);
|
|
pj_ks (pj, "name", fcn->name);
|
|
pj_ks (pj, "type", "function");
|
|
pj_kn (pj, "offset", tgt_addr);
|
|
pj_end (pj);
|
|
} else {
|
|
r_cons_println (fcn->name);
|
|
}
|
|
} else if (f) {
|
|
if (name) {
|
|
ret = r_flag_rename (core->flags, f, name)? 0: -1;
|
|
} else if (mode == '*') {
|
|
r_cons_printf ("f %s=0x%" PFMT64x "\n", r_str_get (name), core->offset);
|
|
} else if (mode == 'j') {
|
|
pj_o (pj);
|
|
pj_ks (pj, "name", f->name);
|
|
if (f->realname) {
|
|
pj_ks (pj, "realname", f->realname);
|
|
}
|
|
pj_ks (pj, "type", "flag");
|
|
pj_kn (pj, "offset", tgt_addr);
|
|
pj_end (pj);
|
|
} else {
|
|
r_cons_println (f->name);
|
|
}
|
|
} else {
|
|
if (name) {
|
|
ret = r_flag_set (core->flags, name, tgt_addr, 1)? 0: -1;
|
|
} else if (mode == '*') {
|
|
r_cons_printf ("f %s=0x%" PFMT64x "\n", r_str_get (name), core->offset);
|
|
} else if (mode == 'j') {
|
|
pj_o (pj);
|
|
pj_ks (pj, "name", r_str_get (name));
|
|
pj_ks (pj, "type", "address");
|
|
pj_kn (pj, "offset", tgt_addr);
|
|
pj_end (pj);
|
|
} else {
|
|
r_cons_printf ("0x%" PFMT64x "\n", tgt_addr);
|
|
}
|
|
}
|
|
}
|
|
failure:
|
|
if (mode == 'j') {
|
|
pj_end (pj);
|
|
r_cons_println (pj_string (pj));
|
|
pj_free (pj);
|
|
}
|
|
|
|
r_anal_op_fini (&op);
|
|
return ret;
|
|
}
|
|
|
|
// EBP BASED
|
|
static int delta_cmp(const void *a, const void *b) {
|
|
const RAnalVar *va = a;
|
|
const RAnalVar *vb = b;
|
|
return vb->delta - va->delta;
|
|
}
|
|
|
|
static int delta_cmp2(const void *a, const void *b) {
|
|
const RAnalVar *va = a;
|
|
const RAnalVar *vb = b;
|
|
return va->delta - vb->delta;
|
|
}
|
|
|
|
static void __cmd_afvf(RCore *core, const char *input) {
|
|
RAnalFunction *fcn = r_anal_get_fcn_in (core->anal, core->offset, -1);
|
|
RListIter *iter;
|
|
RAnalVar *p;
|
|
RList *list = r_anal_var_all_list (core->anal, fcn);
|
|
r_list_sort (list, delta_cmp2);
|
|
r_list_foreach (list, iter, p) {
|
|
if (p->isarg || p->delta > 0) {
|
|
continue;
|
|
}
|
|
const char *pad = r_str_pad (' ', 10 - strlen (p->name));
|
|
r_cons_printf ("0x%08"PFMT64x" %s:%s%s\n", (ut64)-p->delta, p->name, pad, p->type);
|
|
}
|
|
r_list_sort (list, delta_cmp);
|
|
r_list_foreach (list, iter, p) {
|
|
if (!p->isarg && p->delta < 0) {
|
|
continue;
|
|
}
|
|
// TODO: only stack vars if (p->kind == 's') { }
|
|
const char *pad = r_str_pad (' ', 10 - strlen (p->name));
|
|
// XXX this 0x6a is a hack
|
|
r_cons_printf ("0x%08"PFMT64x" %s:%s%s\n", ((ut64)p->delta) - 0x6a, p->name, pad, p->type);
|
|
}
|
|
r_list_free (list);
|
|
|
|
}
|
|
|
|
static int var_cmd(RCore *core, const char *str) {
|
|
int delta, type = *str, res = true;
|
|
RAnalVar *v1;
|
|
RAnalFunction *fcn = r_anal_get_fcn_in (core->anal, core->offset, -1);
|
|
if (!str[0]) {
|
|
if (fcn) {
|
|
// "afv"
|
|
r_core_cmd0 (core, "afvr");
|
|
r_core_cmd0 (core, "afvs");
|
|
r_core_cmd0 (core, "afvb");
|
|
} else {
|
|
R_LOG_WARN ("Cannot find function in 0x%08"PFMT64x, core->offset);
|
|
}
|
|
return true;
|
|
}
|
|
if (str[1] == '?'|| str[0] == '?') {
|
|
var_help (core, *str);
|
|
return res;
|
|
}
|
|
PJ *pj = NULL;
|
|
if (str[0] == 'j') { // "afvj"
|
|
pj = r_core_pj_new (core);
|
|
if (!pj) {
|
|
return false;
|
|
}
|
|
pj_o (pj);
|
|
pj_k (pj, "reg");
|
|
r_anal_var_list_show (core->anal, fcn, 'r', 'j', pj);
|
|
pj_k (pj, "sp");
|
|
r_anal_var_list_show (core->anal, fcn, 's', 'j', pj);
|
|
pj_k (pj, "bp");
|
|
r_anal_var_list_show (core->anal, fcn, 'b', 'j', pj);
|
|
pj_end (pj);
|
|
r_cons_println (pj_string (pj));
|
|
pj_free (pj);
|
|
return true;
|
|
}
|
|
char *p = strdup (str);
|
|
char *ostr = p;
|
|
/* Variable access CFvs = set fun var */
|
|
switch (str[0]) {
|
|
case '-': // "afv-"
|
|
r_core_cmdf (core, "afvr-%s", str + 1);
|
|
r_core_cmdf (core, "afvs-%s", str + 1);
|
|
r_core_cmdf (core, "afvb-%s", str + 1);
|
|
return true;
|
|
case 'x': // "afvx"
|
|
if (fcn) {
|
|
cmd_afvx (core, fcn, str[1] == 'j');
|
|
} else {
|
|
R_LOG_WARN ("Cannot find function in 0x%08"PFMT64x, core->offset);
|
|
}
|
|
free (ostr);
|
|
return true;
|
|
case 'R': // "afvR"
|
|
case 'W': // "afvW"
|
|
case '*': // "afv*"
|
|
case '=': // "afv="
|
|
if (fcn) {
|
|
const char *name = strchr (ostr, ' ');
|
|
if (name) {
|
|
name = r_str_trim_head_ro (name);
|
|
}
|
|
if (str[1] == 'j') {
|
|
pj = r_core_pj_new (core);
|
|
if (!pj) {
|
|
return false;
|
|
}
|
|
}
|
|
list_vars (core, fcn, pj, str[0], name);
|
|
if (str[1] == 'j') {
|
|
pj_end (pj);
|
|
r_cons_println (pj_string (pj));
|
|
pj_free (pj);
|
|
}
|
|
free (ostr);
|
|
return true;
|
|
} else {
|
|
R_LOG_ERROR ("Cannot find function in 0x%08"PFMT64x, core->offset);
|
|
free (ostr);
|
|
return false;
|
|
}
|
|
case 'a': // "afva"
|
|
if (fcn) {
|
|
char *type = r_str_newf ("func.%s.ret", fcn->name);
|
|
if (type && sdb_exists (core->anal->sdb_types, type)) {
|
|
// if function type exists
|
|
// do not analize vars if function has a signature
|
|
} else {
|
|
r_anal_function_delete_all_vars (fcn);
|
|
r_core_recover_vars (core, fcn, false);
|
|
}
|
|
free (type);
|
|
free (p);
|
|
return true;
|
|
} else {
|
|
R_LOG_ERROR ("Cannot find function in 0x%08"PFMT64x, core->offset);
|
|
return false;
|
|
}
|
|
case 'n': // "afvn"
|
|
if (str[1]) {
|
|
RAnalOp *op = r_core_anal_op (core, core->offset, R_ANAL_OP_MASK_BASIC);
|
|
const char *new_name = r_str_trim_head_ro (strchr (ostr, ' '));
|
|
if (!new_name) {
|
|
r_anal_op_free (op);
|
|
free (ostr);
|
|
return false;
|
|
}
|
|
char *old_name = strchr (new_name, ' ');
|
|
if (!old_name) {
|
|
RAnalVar *var = op ? r_anal_get_used_function_var (core->anal, op->addr) : NULL;
|
|
if (var) {
|
|
old_name = var->name;
|
|
} else {
|
|
R_LOG_ERROR ("Cannot find var @ 0x%08"PFMT64x, core->offset);
|
|
r_anal_op_free (op);
|
|
free (ostr);
|
|
return false;
|
|
}
|
|
} else {
|
|
*old_name++ = 0;
|
|
r_str_trim (old_name);
|
|
}
|
|
if (fcn) {
|
|
v1 = r_anal_function_get_var_byname (fcn, old_name);
|
|
if (v1) {
|
|
r_anal_var_rename (v1, new_name, true);
|
|
} else {
|
|
R_LOG_ERROR ("Cant find var by name");
|
|
}
|
|
} else {
|
|
R_LOG_ERROR ("Cannot find function in 0x%08"PFMT64x, core->offset);
|
|
r_anal_op_free (op);
|
|
free (ostr);
|
|
return false;
|
|
}
|
|
r_anal_op_free (op);
|
|
free (ostr);
|
|
} else {
|
|
RListIter *iter;
|
|
RAnalVar *v;
|
|
RList *list = r_anal_var_all_list (core->anal, fcn);
|
|
r_list_foreach (list, iter, v) {
|
|
r_cons_printf ("%s\n", v->name);
|
|
}
|
|
r_list_free (list);
|
|
}
|
|
return true;
|
|
case 'd': // "afvd"
|
|
if (!fcn) {
|
|
R_LOG_ERROR ("Cannot find function");
|
|
} else if (str[1]) {
|
|
p = strchr (ostr, ' ');
|
|
if (!p) {
|
|
free (ostr);
|
|
return false;
|
|
}
|
|
r_str_trim (p);
|
|
v1 = r_anal_function_get_var_byname (fcn, p);
|
|
if (!v1) {
|
|
free (ostr);
|
|
return false;
|
|
}
|
|
r_anal_var_display (core->anal, v1);
|
|
} else {
|
|
RListIter *iter;
|
|
RAnalVar *p;
|
|
RList *list = r_anal_var_all_list (core->anal, fcn);
|
|
r_list_foreach (list, iter, p) {
|
|
char *a = r_core_cmd_strf (core, ".afvd %s", p->name);
|
|
if ((a && !*a) || !a) {
|
|
free (a);
|
|
a = strdup ("\n");
|
|
}
|
|
r_cons_printf ("%s %s = %s", p->isarg? "arg": "var", p->name, a);
|
|
free (a);
|
|
}
|
|
r_list_free (list);
|
|
}
|
|
free (ostr);
|
|
return true;
|
|
case 'f': // "afvf"
|
|
__cmd_afvf (core, ostr);
|
|
break;
|
|
case 't': // "afvt"
|
|
if (fcn) {
|
|
p = strchr (ostr, ' ');
|
|
if (!p++) {
|
|
free (ostr);
|
|
return false;
|
|
}
|
|
|
|
char *type = strchr (p, ' ');
|
|
if (!type) {
|
|
free (ostr);
|
|
return false;
|
|
}
|
|
*type++ = 0;
|
|
v1 = r_anal_function_get_var_byname (fcn, p);
|
|
if (!v1) {
|
|
R_LOG_ERROR ("Cant find get by name %s", p);
|
|
free (ostr);
|
|
return false;
|
|
}
|
|
r_anal_var_set_type (v1, type);
|
|
free (ostr);
|
|
return true;
|
|
} else {
|
|
R_LOG_ERROR ("Cannot find function");
|
|
return false;
|
|
}
|
|
}
|
|
switch (str[1]) { // afv[bsr]
|
|
case '\0':
|
|
case '*': // "afv[bsr]*"
|
|
if (fcn) {
|
|
r_anal_var_list_show (core->anal, fcn, type, str[1], NULL);
|
|
} else {
|
|
R_LOG_ERROR ("Cannot find function");
|
|
}
|
|
break;
|
|
case 'j': // "afv[bsr]j"
|
|
pj = r_core_pj_new (core);
|
|
if (!pj) {
|
|
return false;
|
|
}
|
|
r_anal_var_list_show (core->anal, fcn, type, str[1], pj);
|
|
r_cons_println (pj_string (pj));
|
|
pj_free (pj);
|
|
break;
|
|
case '.': // "afv[bsr]."
|
|
r_anal_var_list_show (core->anal, fcn, core->offset, 0, NULL);
|
|
break;
|
|
case '-': // "afv[bsr]-"
|
|
if (!fcn) {
|
|
R_LOG_ERROR ("afv: Cannot find function");
|
|
return false;
|
|
}
|
|
if (str[2] == '*') {
|
|
r_anal_function_delete_vars_by_kind (fcn, type);
|
|
} else {
|
|
RAnalVar *var = NULL;
|
|
if (IS_DIGIT (str[2])) {
|
|
var = r_anal_function_get_var (fcn, type, (int)r_num_math (core->num, str + 1));
|
|
} else {
|
|
char *name = r_str_trim_dup (str + 2);
|
|
if (name) {
|
|
var = r_anal_function_get_var_byname (fcn, name);
|
|
r_free (name);
|
|
}
|
|
}
|
|
if (var) {
|
|
r_anal_var_delete (var);
|
|
}
|
|
}
|
|
break;
|
|
case 's': // "afv[bsr]s" // "afvs"
|
|
case 'g': // "afv[bsr]g"
|
|
if (str[2]) {
|
|
int idx = r_num_math (core->num, str + 2);
|
|
char *vaddr;
|
|
p = strchr (ostr, ' ');
|
|
if (!p) {
|
|
var_help (core, type);
|
|
break;
|
|
}
|
|
r_str_trim (p);
|
|
ut64 addr = core->offset;
|
|
if ((vaddr = strchr (p, ' '))) {
|
|
addr = r_num_math (core->num, vaddr);
|
|
}
|
|
RAnalVar *var = r_anal_function_get_var (fcn, str[0], idx);
|
|
if (!var) {
|
|
R_LOG_ERROR ("Cannot find variable with delta %d", idx);
|
|
res = false;
|
|
break;
|
|
}
|
|
int rw = (str[1] == 'g') ? R_ANAL_VAR_ACCESS_TYPE_READ : R_ANAL_VAR_ACCESS_TYPE_WRITE;
|
|
int ptr = *var->type == 's' ? idx - fcn->maxstack : idx;
|
|
RAnalOp *op = r_core_anal_op (core, addr, 0);
|
|
const char *ireg = op ? op->ireg : NULL;
|
|
r_anal_var_set_access (var, ireg, addr, rw, ptr);
|
|
r_anal_op_free (op);
|
|
} else {
|
|
R_LOG_ERROR ("Missing argument");
|
|
}
|
|
break;
|
|
case ' ': { // "afvs" "afvb" "afvr"
|
|
bool isarg = false;
|
|
const int size = 4;
|
|
p = strchr (ostr, ' ');
|
|
if (!p) {
|
|
var_help (core, type);
|
|
break;
|
|
}
|
|
if (!fcn) {
|
|
R_LOG_ERROR ("Missing function at 0x%08" PFMT64x, core->offset);
|
|
break;
|
|
}
|
|
*p++ = 0;
|
|
r_str_trim_head (p);
|
|
char *name = strchr (p, ' ');
|
|
if (!name) {
|
|
R_LOG_ERROR ("Missing name");
|
|
break;
|
|
}
|
|
*name++ = 0;
|
|
r_str_trim_head (name);
|
|
|
|
if (type == 'r') { //registers
|
|
RRegItem *i = r_reg_get (core->anal->reg, p, -1);
|
|
if (!i) {
|
|
R_LOG_ERROR ("Register not found");
|
|
break;
|
|
}
|
|
delta = i->index;
|
|
isarg = true;
|
|
} else {
|
|
delta = r_num_math (core->num, p);
|
|
}
|
|
|
|
char *vartype = strchr (name, ' ');
|
|
if (!vartype) {
|
|
vartype = "int";
|
|
} else {
|
|
*vartype++ = 0;
|
|
r_str_trim (vartype);
|
|
}
|
|
if (type == 'b') {
|
|
eprintf ("DELTA\n");
|
|
delta -= fcn->bp_off;
|
|
}
|
|
if ((type == 'b') && delta > 0) {
|
|
isarg = true;
|
|
} else if (type == 's' && delta > fcn->maxstack) {
|
|
isarg = true;
|
|
}
|
|
r_anal_function_set_var (fcn, delta, type, vartype, size, isarg, name);
|
|
}
|
|
break;
|
|
}
|
|
free (ostr);
|
|
return res;
|
|
}
|
|
|
|
static void print_trampolines(RCore *core, ut64 a, ut64 b, size_t element_size) {
|
|
int i;
|
|
for (i = 0; i < core->blocksize; i += element_size) {
|
|
ut32 n;
|
|
memcpy (&n, core->block + i, sizeof (ut32));
|
|
if (n >= a && n <= b) {
|
|
if (element_size == 4) {
|
|
r_cons_printf ("f trampoline.%x @ 0x%" PFMT64x "\n", n, core->offset + i);
|
|
} else {
|
|
r_cons_printf ("f trampoline.%" PFMT32x " @ 0x%" PFMT64x "\n", n, core->offset + i);
|
|
}
|
|
r_cons_printf ("Cd %u @ 0x%" PFMT64x ":%u\n", (unsigned int)element_size, core->offset + i, (unsigned int)element_size);
|
|
// TODO: add data xrefs
|
|
}
|
|
}
|
|
}
|
|
|
|
static void cmd_anal_trampoline(RCore *core, const char *input) {
|
|
int bits = r_config_get_i (core->config, "asm.bits");
|
|
char *p, *inp = strdup (input);
|
|
p = strchr (inp, ' ');
|
|
if (p) {
|
|
*p = 0;
|
|
}
|
|
ut64 a = r_num_math (core->num, inp);
|
|
ut64 b = p? r_num_math (core->num, p + 1): 0;
|
|
free (inp);
|
|
|
|
switch (bits) {
|
|
case 32:
|
|
print_trampolines (core, a, b, 4);
|
|
break;
|
|
case 64:
|
|
print_trampolines (core, a, b, 8);
|
|
break;
|
|
}
|
|
}
|
|
|
|
static const char *syscallNumber(char *snstr, int n) {
|
|
snprintf (snstr, 32, (n>1000)?"0x%x": "%d", n);
|
|
return snstr;
|
|
}
|
|
|
|
R_API char *cmd_syscall_dostr(RCore *core, st64 n, ut64 addr) {
|
|
int i;
|
|
char str[64], snstr[32];
|
|
st64 N = n;
|
|
int defVector = r_syscall_get_swi (core->anal->syscall);
|
|
if (defVector > 0) {
|
|
n = -1;
|
|
}
|
|
if (n == -1 || defVector > 0) {
|
|
n = (int)r_debug_reg_get (core->dbg, "oeax");
|
|
if (!n || n == -1) {
|
|
const char *a0 = r_reg_get_name (core->anal->reg, R_REG_NAME_SN);
|
|
n = (a0 == NULL)? -1: (int)r_debug_reg_get (core->dbg, a0);
|
|
}
|
|
}
|
|
RSyscallItem *item = r_syscall_get (core->anal->syscall, n, defVector);
|
|
if (!item) {
|
|
item = r_syscall_get (core->anal->syscall, N, -1);
|
|
}
|
|
if (!item) {
|
|
return r_str_newf ("%s = unknown ()", syscallNumber (snstr, n));
|
|
}
|
|
char *res = r_str_newf ("%s = %s (", syscallNumber (snstr, item->num), item->name);
|
|
// TODO: move this to r_syscall
|
|
const char *cc = r_anal_syscc_default (core->anal);
|
|
//TODO replace the hardcoded CC with the sdb ones
|
|
for (i = 0; i < item->args; i++) {
|
|
// XXX this is a hack to make syscall args work on x86-32 and x86-64
|
|
// we need to shift sn first.. which is bad, but needs to be redesigned
|
|
int regidx = i;
|
|
if (core->rasm->config->bits == 32 &&
|
|
((core->rasm->cur && !strcmp (core->rasm->cur->arch, "x86")) ||
|
|
(core->anal->cur && !strcmp (core->anal->cur->arch , "x86")))) {
|
|
regidx++;
|
|
}
|
|
ut64 arg = r_debug_arg_get (core->dbg, cc, regidx);
|
|
//r_cons_printf ("(%d:0x%"PFMT64x")\n", i, arg);
|
|
if (item->sargs) {
|
|
switch (item->sargs[i]) {
|
|
case 'p': // pointer
|
|
res = r_str_appendf (res, "0x%08" PFMT64x, arg);
|
|
break;
|
|
case 'i':
|
|
res = r_str_appendf (res, "%" PFMT64u "", arg);
|
|
break;
|
|
case 'z':
|
|
memset (str, 0, sizeof (str));
|
|
r_io_read_at (core->io, arg, (ut8 *)str, sizeof (str) - 1);
|
|
r_str_filter (str, strlen (str));
|
|
res = r_str_appendf (res, "\"%s\"", str);
|
|
break;
|
|
case 'Z': {
|
|
//TODO replace the hardcoded CC with the sdb ones
|
|
ut64 len = r_debug_arg_get (core->dbg, cc, i + 2);
|
|
len = R_MIN (len + 1, sizeof (str) - 1);
|
|
if (len == 0) {
|
|
len = 16; // override default
|
|
}
|
|
(void)r_io_read_at (core->io, arg, (ut8 *)str, len);
|
|
str[len] = 0;
|
|
r_str_filter (str, -1);
|
|
res = r_str_appendf (res, "\"%s\"", str);
|
|
break;
|
|
}
|
|
default:
|
|
res = r_str_appendf (res, "0x%08" PFMT64x, arg);
|
|
}
|
|
} else {
|
|
res = r_str_appendf (res, "0x%08" PFMT64x, arg);
|
|
}
|
|
if (i + 1 < item->args) {
|
|
res = r_str_append (res, ", ");
|
|
}
|
|
}
|
|
r_syscall_item_free (item);
|
|
return r_str_append (res, ")");
|
|
}
|
|
|
|
static bool mw(RAnalEsil *esil, ut64 addr, const ut8 *buf, int len) {
|
|
int *ec = (int*)esil->user;
|
|
*ec += (len * 2);
|
|
return true;
|
|
}
|
|
|
|
static bool mr(RAnalEsil *esil, ut64 addr, ut8 *buf, int len) {
|
|
int *ec = (int*)esil->user;
|
|
*ec += len;
|
|
return true;
|
|
}
|
|
|
|
static int esil_cost(RCore *core, ut64 addr, const char *expr) {
|
|
if (R_STR_ISEMPTY (expr)) {
|
|
return 0;
|
|
}
|
|
int ec = 0;
|
|
RAnalEsil *e = r_anal_esil_new (256, 0, 0);
|
|
r_anal_esil_setup (e, core->anal, false, false, false);
|
|
e->user = &ec;
|
|
e->cb.mem_read = mr;
|
|
e->cb.mem_write = mw;
|
|
r_anal_esil_parse (e, expr);
|
|
r_anal_esil_free (e);
|
|
return ec;
|
|
}
|
|
|
|
static void cmd_syscall_do(RCore *core, st64 n, ut64 addr) {
|
|
char *msg = cmd_syscall_dostr (core, n, addr);
|
|
if (msg) {
|
|
r_cons_println (msg);
|
|
free (msg);
|
|
}
|
|
}
|
|
|
|
static inline RAnalEsil *esil_new_setup(RCore *core) {
|
|
int stacksize = r_config_get_i (core->config, "esil.stack.depth");
|
|
int iotrap = r_config_get_i (core->config, "esil.iotrap");
|
|
unsigned int addrsize = r_config_get_i (core->config, "esil.addr.size");
|
|
RAnalEsil *esil = r_anal_esil_new (stacksize, iotrap, addrsize);
|
|
if (esil) {
|
|
int romem = r_config_get_i (core->config, "esil.romem");
|
|
int stats = r_config_get_i (core->config, "esil.stats");
|
|
bool nonull = r_config_get_b (core->config, "esil.nonull");
|
|
r_anal_esil_setup (esil, core->anal, romem, stats, nonull);
|
|
esil->verbose = r_config_get_i (core->config, "esil.verbose");
|
|
}
|
|
return esil;
|
|
}
|
|
|
|
static void core_anal_bytes(RCore *core, const ut8 *buf, int len, int nops, int fmt) {
|
|
bool be = core->rasm->config->big_endian;
|
|
bool use_color = core->print->flags & R_PRINT_FLAGS_COLOR;
|
|
core->parser->subrel = r_config_get_i (core->config, "asm.sub.rel");
|
|
int ret, i, j, idx, size;
|
|
const char *color = "";
|
|
const char *esilstr;
|
|
const char *opexstr;
|
|
RAnalHint *hint;
|
|
RAnalOp op = {0};
|
|
ut64 addr;
|
|
PJ *pj = NULL;
|
|
int totalsize = 0;
|
|
#if 0
|
|
RAnalEsil *esil = r_anal_esil_new (256, 0, 0);
|
|
r_anal_esil_setup (esil, core->anal, false, false, false);
|
|
esil->user = &ec;
|
|
esil->cb.mem_read = mr;
|
|
esil->cb.mem_write = mw;
|
|
#else
|
|
RAnalEsil *esil = NULL;
|
|
#endif
|
|
|
|
// Variables required for setting up ESIL to REIL conversion
|
|
if (use_color) {
|
|
color = core->cons->context->pal.label;
|
|
}
|
|
switch (fmt) {
|
|
case 'j': {
|
|
pj = r_core_pj_new (core);
|
|
if (!pj) {
|
|
break;
|
|
}
|
|
pj_a (pj);
|
|
break;
|
|
}
|
|
}
|
|
for (i = idx = ret = 0; idx < len && (!nops || (nops && i < nops)); i++, idx += ret) {
|
|
RAsmOp asmop = {0};
|
|
addr = core->offset + idx;
|
|
r_asm_set_pc (core->rasm, addr);
|
|
hint = r_anal_hint_get (core->anal, addr);
|
|
ret = r_anal_op (core->anal, &op, addr, buf + idx, len - idx,
|
|
R_ANAL_OP_MASK_ESIL | R_ANAL_OP_MASK_OPEX | R_ANAL_OP_MASK_HINT | R_ANAL_OP_MASK_DISASM);
|
|
(void)r_asm_disassemble (core->rasm, &asmop, buf + idx, len - idx);
|
|
esilstr = R_STRBUF_SAFEGET (&op.esil);
|
|
opexstr = R_STRBUF_SAFEGET (&op.opex);
|
|
char *mnem = strdup (r_asm_op_get_asm (&asmop));
|
|
char *sp = strchr (mnem, ' ');
|
|
if (sp) {
|
|
*sp = 0;
|
|
if (op.prefix) {
|
|
char *arg = strdup (sp + 1);
|
|
char *sp = strchr (arg, ' ');
|
|
if (sp) {
|
|
*sp = 0;
|
|
}
|
|
free (mnem);
|
|
mnem = arg;
|
|
}
|
|
}
|
|
if (ret < 1 && fmt != 'd') {
|
|
RStrBuf *sb = r_strbuf_new ("");
|
|
for (i = idx, j = 0; i < core->blocksize && j < 3; i++, j++) {
|
|
r_strbuf_appendf (sb, "%02x ", buf[i]);
|
|
}
|
|
R_LOG_ERROR ("Oops at 0x%08" PFMT64x " (%s...)", core->offset + idx, r_strbuf_get (sb));
|
|
r_strbuf_free (sb);
|
|
free (mnem);
|
|
break;
|
|
}
|
|
size = op.size;
|
|
if (fmt == 'd') {
|
|
char *opname = strdup (r_asm_op_get_asm (&asmop));
|
|
if (opname) {
|
|
r_str_split (opname, ' ');
|
|
char *d = r_asm_describe (core->rasm, opname);
|
|
if (R_STR_ISNOTEMPTY (d)) {
|
|
r_cons_printf ("%s: %s\n", opname, d);
|
|
free (d);
|
|
} else {
|
|
R_LOG_ERROR ("Unknown opcode");
|
|
}
|
|
free (opname);
|
|
}
|
|
} else if (fmt == 'E') {
|
|
r_cons_printf ("%s\n", esilstr);
|
|
} else if (fmt == 'e') {
|
|
if (R_STR_ISNOTEMPTY (esilstr)) {
|
|
if (use_color) {
|
|
r_cons_printf ("%s0x%" PFMT64x Color_RESET " %s\n", color, core->offset + idx, esilstr);
|
|
} else {
|
|
r_cons_printf ("0x%" PFMT64x " %s\n", core->offset + idx, esilstr);
|
|
}
|
|
}
|
|
} else if (fmt == 's') {
|
|
totalsize += op.size;
|
|
} else if (fmt == '*') {
|
|
// TODO: ao* useful for wat? wx [bytes] ?
|
|
} else if (fmt == 'j') {
|
|
char strsub[128] = {0};
|
|
// pc+33
|
|
r_parse_subvar (core->parser, NULL,
|
|
core->offset + idx,
|
|
asmop.size, r_asm_op_get_asm (&asmop),
|
|
strsub, sizeof (strsub));
|
|
ut64 killme = UT64_MAX;
|
|
if (r_io_read_i (core->io, op.ptr, &killme, op.refptr, be)) {
|
|
core->parser->subrel_addr = killme;
|
|
}
|
|
// 0x33->sym.xx
|
|
char *p = strdup (strsub);
|
|
if (p) {
|
|
r_parse_filter (core->parser, addr, core->flags, hint, p,
|
|
strsub, sizeof (strsub), be);
|
|
free (p);
|
|
}
|
|
pj_o (pj);
|
|
pj_ks (pj, "opcode", r_asm_op_get_asm (&asmop));
|
|
if (!*strsub) {
|
|
r_str_ncpy (strsub, r_asm_op_get_asm (&asmop), sizeof (strsub) -1 );
|
|
}
|
|
{
|
|
RAnalFunction *fcn = r_anal_get_fcn_in (core->anal, addr, 0);
|
|
if (fcn) {
|
|
r_parse_subvar (core->parser, fcn, addr, asmop.size,
|
|
strsub, strsub, sizeof (strsub));
|
|
}
|
|
}
|
|
pj_ks (pj, "disasm", strsub);
|
|
// apply pseudo if needed
|
|
{
|
|
char *pseudo = calloc (128 + strlen (strsub), 3);
|
|
r_parse_parse (core->parser, strsub, pseudo);
|
|
if (pseudo && *pseudo) {
|
|
pj_ks (pj, "pseudo", pseudo);
|
|
}
|
|
free (pseudo);
|
|
}
|
|
{
|
|
char *opname = strdup (strsub);
|
|
char *sp = strchr (opname, ' ');
|
|
if (sp) {
|
|
*sp = 0;
|
|
}
|
|
char *d = r_asm_describe (core->rasm, opname);
|
|
if (d && *d) {
|
|
pj_ks (pj, "description", d);
|
|
}
|
|
free (d);
|
|
free (opname);
|
|
}
|
|
pj_ks (pj, "mnemonic", mnem);
|
|
{
|
|
ut8 *mask = r_anal_mask (core->anal, len - idx, buf + idx, core->offset + idx);
|
|
char *maskstr = r_hex_bin2strdup (mask, size);
|
|
pj_ks (pj, "mask", maskstr);
|
|
free (mask);
|
|
free (maskstr);
|
|
}
|
|
if (hint && hint->opcode) {
|
|
pj_ks (pj, "ophint", hint->opcode);
|
|
}
|
|
if (hint && hint->jump != UT64_MAX) {
|
|
op.jump = hint->jump;
|
|
}
|
|
if (hint && hint->fail != UT64_MAX) {
|
|
op.fail = hint->fail;
|
|
}
|
|
if (op.jump != UT64_MAX) {
|
|
pj_kn (pj, "jump", op.jump);
|
|
}
|
|
if (op.fail != UT64_MAX) {
|
|
pj_kn (pj, "fail", op.fail);
|
|
}
|
|
const char *jesil = (hint && hint->esil) ? hint->esil: esilstr;
|
|
if (jesil && *jesil) {
|
|
pj_ks (pj, "esil", jesil);
|
|
}
|
|
pj_kb (pj, "sign", op.sign);
|
|
pj_kn (pj, "prefix", op.prefix);
|
|
pj_ki (pj, "id", op.id);
|
|
if (op.vliw > 0) {
|
|
pj_ki (pj, "id", op.vliw);
|
|
}
|
|
if (opexstr && *opexstr) {
|
|
pj_k (pj, "opex");
|
|
pj_j (pj, opexstr);
|
|
}
|
|
pj_kn (pj, "addr", core->offset + idx);
|
|
{
|
|
char *bytes = r_hex_bin2strdup (buf + idx, size);
|
|
pj_ks (pj, "bytes", bytes);
|
|
free (bytes);
|
|
}
|
|
if (op.val != UT64_MAX) {
|
|
pj_kn (pj, "val", op.val);
|
|
}
|
|
if (op.disp && op.disp != UT64_MAX) {
|
|
pj_kn (pj, "disp", op.disp);
|
|
}
|
|
if (op.ptr != UT64_MAX) {
|
|
pj_kn (pj, "ptr", op.ptr);
|
|
}
|
|
pj_ki (pj, "size", size);
|
|
pj_ks (pj, "type", r_anal_optype_to_string (op.type));
|
|
{
|
|
const char *datatype = r_anal_datatype_to_string (op.datatype);
|
|
if (datatype) {
|
|
pj_ks (pj, "datatype", datatype);
|
|
}
|
|
|
|
}
|
|
if (esilstr) {
|
|
int ec = esil_cost (core, addr, esilstr);
|
|
pj_ki (pj, "esilcost", ec);
|
|
}
|
|
if (op.reg) {
|
|
pj_ks (pj, "reg", op.reg);
|
|
}
|
|
if (op.ireg) {
|
|
pj_ks (pj, "ireg", op.ireg);
|
|
}
|
|
pj_ki (pj, "scale", op.scale);
|
|
if (op.refptr != -1) {
|
|
pj_ki (pj, "refptr", op.refptr);
|
|
}
|
|
pj_ki (pj, "cycles", op.cycles);
|
|
pj_ki (pj, "failcycles", op.failcycles);
|
|
pj_ki (pj, "delay", op.delay);
|
|
const char *p1 = r_anal_stackop_tostring (op.stackop);
|
|
if (strcmp (p1, "null")) {
|
|
pj_ks (pj, "stack", p1);
|
|
}
|
|
pj_kn (pj, "stackptr", op.stackptr);
|
|
if (op.direction != 0) {
|
|
pj_ks (pj, "direction", r_anal_op_direction_tostring (&op));
|
|
}
|
|
const char *arg = (op.type & R_ANAL_OP_TYPE_COND)
|
|
? r_anal_cond_tostring (op.cond): NULL;
|
|
if (arg) {
|
|
pj_ks (pj, "cond", arg);
|
|
}
|
|
pj_ks (pj, "family", r_anal_op_family_to_string (op.family));
|
|
pj_end (pj);
|
|
} else if (fmt == 'r') {
|
|
if (R_STR_ISNOTEMPTY (esilstr)) {
|
|
if (core->anal->esil) {
|
|
if (use_color) {
|
|
r_cons_printf ("%s0x%" PFMT64x Color_RESET " %s\n", color, core->offset + idx, esilstr);
|
|
} else {
|
|
r_cons_printf ("0x%" PFMT64x " %s\n", core->offset + idx, esilstr);
|
|
}
|
|
esil = core->anal->esil;
|
|
r_anal_esil_parse (esil, esilstr);
|
|
r_anal_esil_dumpstack (esil);
|
|
r_anal_esil_stack_free (esil);
|
|
esil = NULL;
|
|
} else {
|
|
R_LOG_ERROR ("ESIL is not initialized. Run `aei`");
|
|
break;
|
|
}
|
|
} else {
|
|
// ignored/skipped eprintf ("No esil for '%s'\n", op.mnemonic);
|
|
}
|
|
} else {
|
|
char disasm[128] = {0};
|
|
r_parse_subvar (core->parser, NULL,
|
|
core->offset + idx,
|
|
asmop.size, r_asm_op_get_asm (&asmop),
|
|
disasm, sizeof (disasm));
|
|
ut64 killme = UT64_MAX;
|
|
if (r_io_read_i (core->io, op.ptr, &killme, op.refptr, be)) {
|
|
core->parser->subrel_addr = killme;
|
|
}
|
|
char *p = strdup (disasm);
|
|
if (p) {
|
|
r_parse_filter (core->parser, addr, core->flags, hint, p,
|
|
disasm, sizeof (disasm), be);
|
|
free (p);
|
|
}
|
|
#define printline(k, fmt, arg)\
|
|
{ \
|
|
if (use_color)\
|
|
r_cons_printf ("%s%s: " Color_RESET, color, k);\
|
|
else\
|
|
r_cons_printf ("%s: ", k);\
|
|
if (fmt) r_cons_printf (fmt, arg);\
|
|
}
|
|
printline ("address", "0x%" PFMT64x "\n", core->offset + idx);
|
|
printline ("opcode", "%s\n", r_asm_op_get_asm (&asmop));
|
|
if (!*disasm) {
|
|
r_str_ncpy (disasm, r_asm_op_get_asm (&asmop), sizeof (disasm) - 1);
|
|
}
|
|
{
|
|
RAnalFunction *fcn = r_anal_get_fcn_in (core->anal, addr, 0);
|
|
if (fcn) {
|
|
r_parse_subvar (core->parser, fcn, addr, asmop.size,
|
|
disasm, disasm, sizeof (disasm));
|
|
}
|
|
}
|
|
if (esilstr) {
|
|
int ec = esil_cost (core, addr, esilstr);
|
|
printline ("esilcost", "%d\n", ec);
|
|
}
|
|
printline ("disasm", "%s\n", disasm);
|
|
{
|
|
char *pseudo = calloc (128 + strlen (disasm), 3);
|
|
r_parse_parse (core->parser, disasm, pseudo);
|
|
if (pseudo && *pseudo) {
|
|
printline ("pseudo", "%s\n", pseudo);
|
|
}
|
|
free (pseudo);
|
|
}
|
|
printline ("mnemonic", "%s\n", mnem);
|
|
{
|
|
char *opname = strdup (disasm);
|
|
char *sp = strchr (opname, ' ');
|
|
if (sp) {
|
|
*sp = 0;
|
|
}
|
|
char *d = r_asm_describe (core->rasm, opname);
|
|
if (d && *d) {
|
|
printline ("description", "%s\n", d);
|
|
}
|
|
free (d);
|
|
free (opname);
|
|
}
|
|
{
|
|
ut8 *mask = r_anal_mask (core->anal, len - idx, buf + idx, core->offset + idx);
|
|
char *maskstr = r_hex_bin2strdup (mask, size);
|
|
printline ("mask", "%s\n", maskstr);
|
|
free (mask);
|
|
free (maskstr);
|
|
}
|
|
if (hint) {
|
|
if (hint->opcode) {
|
|
printline ("ophint", "%s\n", hint->opcode);
|
|
}
|
|
}
|
|
printline ("prefix", "%u\n", op.prefix);
|
|
printline ("id", "%d\n", op.id);
|
|
if (op.vliw > 0) {
|
|
printline ("id", "%d\n", op.vliw);
|
|
}
|
|
#if 0
|
|
// no opex here to avoid lot of tests broken..and having json in here is not much useful imho
|
|
if (opexstr && *opexstr) {
|
|
printline ("opex", "%s\n", opexstr);
|
|
}
|
|
#endif
|
|
printline ("bytes", "%s", "");
|
|
int minsz = R_MIN (len, size);
|
|
minsz = R_MAX (minsz, 0);
|
|
for (j = 0; j < minsz; j++) {
|
|
r_cons_printf ("%02x", buf[idx + j]);
|
|
}
|
|
r_cons_newline ();
|
|
if (op.val != UT64_MAX) {
|
|
printline ("val", "0x%08" PFMT64x "\n", op.val);
|
|
}
|
|
if (op.ptr != UT64_MAX) {
|
|
printline ("ptr", "0x%08" PFMT64x "\n", op.ptr);
|
|
}
|
|
if (op.disp && op.disp != UT64_MAX) {
|
|
printline ("disp", "0x%08" PFMT64x "\n", op.disp);
|
|
}
|
|
if (op.refptr != -1) {
|
|
printline ("refptr", "%d\n", op.refptr);
|
|
}
|
|
printline ("size", "%d\n", size);
|
|
printline ("sign", "%s\n", r_str_bool (op.sign));
|
|
printline ("type", "%s\n", r_anal_optype_to_string (op.type));
|
|
const char *datatype = r_anal_datatype_to_string (op.datatype);
|
|
if (datatype) {
|
|
printline ("datatype", "%s\n", datatype);
|
|
}
|
|
printline ("cycles", "%d\n", op.cycles);
|
|
if (op.failcycles) {
|
|
printline ("failcycles", "%d\n", op.failcycles);
|
|
}
|
|
if (op.type2) {
|
|
printline ("type2", "0x%x\n", op.type2);
|
|
}
|
|
if (op.reg) {
|
|
printline ("reg", "%s\n", op.reg);
|
|
}
|
|
if (op.ireg) {
|
|
printline ("ireg", "%s\n", op.ireg);
|
|
}
|
|
if (op.scale) {
|
|
printline ("scale", "%d\n", op.scale);
|
|
}
|
|
if (hint && hint->esil) {
|
|
printline ("esil", "%s\n", hint->esil);
|
|
} else if (R_STR_ISNOTEMPTY (esilstr)) {
|
|
printline ("esil", "%s\n", esilstr);
|
|
}
|
|
if (hint && hint->jump != UT64_MAX) {
|
|
op.jump = hint->jump;
|
|
}
|
|
if (op.jump != UT64_MAX) {
|
|
printline ("jump", "0x%08" PFMT64x "\n", op.jump);
|
|
}
|
|
if (op.direction != 0) {
|
|
printline ("direction", "%s\n", r_anal_op_direction_tostring (&op));
|
|
}
|
|
if (hint && hint->fail != UT64_MAX) {
|
|
op.fail = hint->fail;
|
|
}
|
|
if (op.fail != UT64_MAX) {
|
|
printline ("fail", "0x%08" PFMT64x "\n", op.fail);
|
|
}
|
|
if (op.delay) {
|
|
printline ("delay", "%d\n", op.delay);
|
|
}
|
|
{
|
|
const char *arg = (op.type & R_ANAL_OP_TYPE_COND)? r_anal_cond_tostring (op.cond): NULL;
|
|
if (arg) {
|
|
printline ("cond", "%s\n", arg);
|
|
}
|
|
}
|
|
printline ("family", "%s\n", r_anal_op_family_to_string (op.family));
|
|
if (op.stackop != R_ANAL_STACK_NULL) {
|
|
printline ("stackop", "%s\n", r_anal_stackop_tostring (op.stackop));
|
|
}
|
|
if (op.stackptr) {
|
|
printline ("stackptr", "%"PFMT64u"\n", op.stackptr);
|
|
}
|
|
}
|
|
//r_cons_printf ("false: 0x%08"PFMT64x"\n", core->offset+idx);
|
|
//free (hint);
|
|
free (mnem);
|
|
r_anal_hint_free (hint);
|
|
r_anal_op_fini (&op);
|
|
r_asm_op_fini (&asmop);
|
|
}
|
|
r_anal_op_fini (&op);
|
|
if (fmt == 's') {
|
|
r_cons_printf ("%d\n", totalsize);
|
|
} else if (fmt == 'j') {
|
|
pj_end (pj);
|
|
r_cons_println (pj_string (pj));
|
|
pj_free (pj);
|
|
}
|
|
r_anal_esil_free (esil);
|
|
}
|
|
|
|
static int bb_cmp(const void *a, const void *b) {
|
|
const RAnalBlock *ba = a;
|
|
const RAnalBlock *bb = b;
|
|
return ba->addr - bb->addr;
|
|
}
|
|
|
|
static ut64 caseval(const void* _a) {
|
|
const RAnalCaseOp* a = _a;
|
|
return a->addr;
|
|
}
|
|
|
|
static ut64 __opaddr(const RAnalBlock *b, ut64 addr) {
|
|
int i;
|
|
if (addr >= b->addr && addr < (b->addr + b->size)) {
|
|
for (i = 0; i < b->ninstr; i++) {
|
|
ut64 aa = b->addr + r_anal_bb_offset_inst (b, i);
|
|
ut64 ab = b->addr + r_anal_bb_offset_inst (b, i + 1);
|
|
if (addr >= aa && addr < ab) {
|
|
return aa;
|
|
}
|
|
}
|
|
}
|
|
return UT64_MAX;
|
|
}
|
|
|
|
static RList *get_xrefs(RAnalBlock *block) {
|
|
RListIter *iter;
|
|
RAnalRef *ref;
|
|
RList *list = NULL;
|
|
size_t i;
|
|
for (i = 0; i < block->ninstr; i++) {
|
|
ut64 ia = block->addr + block->op_pos[i];
|
|
RList *xrefs = r_anal_xrefs_get (block->anal, ia);
|
|
r_list_foreach (xrefs, iter, ref) {
|
|
if (!list) {
|
|
list = r_list_newf (free);
|
|
}
|
|
r_list_push (list, ut64_new (ref->addr));
|
|
}
|
|
}
|
|
return list;
|
|
}
|
|
|
|
static char *fcnjoin(RList *list) {
|
|
RAnalFunction *n;
|
|
RListIter *iter;
|
|
RStrBuf buf;
|
|
r_strbuf_init (&buf);
|
|
r_list_foreach (list, iter, n) {
|
|
r_strbuf_appendf (&buf, " 0x%08" PFMT64x, n->addr);
|
|
}
|
|
char *s = strdup (r_strbuf_get (&buf));
|
|
r_strbuf_fini (&buf);
|
|
return s;
|
|
}
|
|
|
|
static char *ut64join(RList *list) {
|
|
ut64 *n;
|
|
RListIter *iter;
|
|
RStrBuf buf;
|
|
r_strbuf_init (&buf);
|
|
r_list_foreach (list, iter, n) {
|
|
r_strbuf_appendf (&buf, " 0x%08" PFMT64x, *n);
|
|
}
|
|
char *s = strdup (r_strbuf_get (&buf));
|
|
r_strbuf_fini (&buf);
|
|
return s;
|
|
}
|
|
|
|
static RList *get_calls(RAnalBlock *block) {
|
|
RList *list = NULL;
|
|
RAnalOp op;
|
|
ut8 *data = malloc (block->size);
|
|
if (data) {
|
|
block->anal->iob.read_at (block->anal->iob.io, block->addr, data, block->size);
|
|
size_t i;
|
|
for (i = 0; i < block->size; i++) {
|
|
int ret = r_anal_op (block->anal, &op, block->addr + i, data + i, block->size - i, R_ANAL_OP_MASK_HINT);
|
|
if (ret < 1) {
|
|
continue;
|
|
}
|
|
if (op.type == R_ANAL_OP_TYPE_CALL) {
|
|
if (!list) {
|
|
list = r_list_newf (free);
|
|
}
|
|
r_list_push (list, ut64_new (op.jump));
|
|
}
|
|
r_anal_op_fini (&op);
|
|
if (op.size > 0) {
|
|
i += op.size - 1;
|
|
}
|
|
}
|
|
|
|
}
|
|
return list;
|
|
}
|
|
|
|
static void anal_bb_list(RCore *core, const char *input) {
|
|
const int mode = *input;
|
|
PJ *pj = NULL;
|
|
RTable *table = NULL;
|
|
RBIter iter;
|
|
RAnalBlock *block;
|
|
if (mode == 'c') {
|
|
ut64 count = 0;
|
|
r_rbtree_foreach (core->anal->bb_tree, iter, block, RAnalBlock, _rb) {
|
|
count++;
|
|
}
|
|
r_cons_printf ("%"PFMT64d"\n", count);
|
|
return;
|
|
}
|
|
if (mode == 'j') {
|
|
pj = pj_new ();
|
|
pj_o (pj);
|
|
pj_ka (pj, "blocks");
|
|
} else if (mode == ',' || mode == 't') {
|
|
table = r_table_new ("bbs");
|
|
RTableColumnType *s = r_table_type ("string");
|
|
RTableColumnType *n = r_table_type ("number");
|
|
r_table_add_column (table, n, "addr", 0);
|
|
r_table_add_column (table, n, "size", 0);
|
|
r_table_add_column (table, n, "traced", 0);
|
|
r_table_add_column (table, n, "ninstr", 0);
|
|
r_table_add_column (table, s, "jump", 0);
|
|
r_table_add_column (table, s, "fail", 0);
|
|
r_table_add_column (table, s, "fcns", 0);
|
|
r_table_add_column (table, s, "calls", 0);
|
|
r_table_add_column (table, s, "xrefs", 0);
|
|
}
|
|
|
|
r_rbtree_foreach (core->anal->bb_tree, iter, block, RAnalBlock, _rb) {
|
|
RList *xrefs = get_xrefs (block);
|
|
RList *calls = get_calls (block);
|
|
switch (mode) {
|
|
case 'j':
|
|
pj_o (pj);
|
|
char *addr = r_str_newf ("0x%" PFMT64x, block->addr);
|
|
pj_ks (pj, "addr", addr);
|
|
free (addr);
|
|
pj_kb (pj, "traced", block->traced);
|
|
pj_kn (pj, "ninstr", block->ninstr);
|
|
pj_kn (pj, "size", block->size);
|
|
if (block->jump != UT64_MAX) {
|
|
pj_kn (pj, "jump", block->jump);
|
|
}
|
|
if (block->fail != UT64_MAX) {
|
|
pj_kn (pj, "fail", block->fail);
|
|
}
|
|
if (xrefs) {
|
|
pj_ka (pj, "xrefs");
|
|
RListIter *iter2;
|
|
ut64 *addr;
|
|
r_list_foreach (xrefs, iter2, addr) {
|
|
pj_n (pj, *addr);
|
|
}
|
|
pj_end (pj);
|
|
}
|
|
if (calls) {
|
|
pj_ka (pj, "calls");
|
|
RListIter *iter2;
|
|
ut64 *addr;
|
|
r_list_foreach (calls, iter2, addr) {
|
|
pj_n (pj, *addr);
|
|
}
|
|
pj_end (pj);
|
|
}
|
|
pj_ka (pj, "fcns");
|
|
RListIter *iter2;
|
|
RAnalFunction *fcn;
|
|
r_list_foreach (block->fcns, iter2, fcn) {
|
|
pj_n (pj, fcn->addr);
|
|
}
|
|
pj_end (pj);
|
|
pj_end (pj);
|
|
break;
|
|
case ',':
|
|
case 't':
|
|
{
|
|
char *jump = block->jump != UT64_MAX? r_str_newf ("0x%08" PFMT64x, block->jump): strdup ("");
|
|
char *fail = block->fail != UT64_MAX? r_str_newf ("0x%08" PFMT64x, block->fail): strdup ("");
|
|
char *call = ut64join (calls);
|
|
char *xref = ut64join (calls);
|
|
char *fcns = fcnjoin (block->fcns);
|
|
r_table_add_rowf (table, "xnddsssss",
|
|
block->addr,
|
|
block->size,
|
|
block->traced,
|
|
block->ninstr,
|
|
jump,
|
|
fail,
|
|
fcns,
|
|
call,
|
|
xref
|
|
);
|
|
free (jump);
|
|
free (fail);
|
|
free (call);
|
|
free (xref);
|
|
free (fcns);
|
|
}
|
|
break;
|
|
case 'q':
|
|
r_cons_printf ("0x%08" PFMT64x"\n", block->addr);
|
|
break;
|
|
default:
|
|
r_cons_printf ("0x%08" PFMT64x , block->addr);
|
|
if (block->jump != UT64_MAX) {
|
|
r_cons_printf (" .j 0x%08" PFMT64x, block->jump);
|
|
}
|
|
if (block->fail != UT64_MAX) {
|
|
r_cons_printf (" .f 0x%08" PFMT64x, block->fail);
|
|
}
|
|
if (xrefs) {
|
|
RListIter *iter2;
|
|
r_cons_printf (" .x");
|
|
ut64 *addr;
|
|
r_list_foreach (xrefs, iter2, addr) {
|
|
r_cons_printf (" 0x%08" PFMT64x, *addr);
|
|
}
|
|
}
|
|
if (calls) {
|
|
r_cons_printf (" .c");
|
|
RListIter *iter2;
|
|
ut64 *addr;
|
|
r_list_foreach (calls, iter2, addr) {
|
|
r_cons_printf (" 0x%08" PFMT64x, *addr);
|
|
}
|
|
}
|
|
if (block->fcns) {
|
|
RListIter *iter2;
|
|
RAnalFunction *fcn;
|
|
r_list_foreach (block->fcns, iter2, fcn) {
|
|
r_cons_printf (" .u 0x%" PFMT64x, fcn->addr);
|
|
}
|
|
}
|
|
r_cons_printf (" .s %" PFMT64d "\n", block->size);
|
|
}
|
|
r_list_free (calls);
|
|
r_list_free (xrefs);
|
|
}
|
|
if (mode == 'j') {
|
|
pj_end (pj);
|
|
pj_end (pj);
|
|
char *j = pj_drain (pj);
|
|
r_cons_println (j);
|
|
free (j);
|
|
} else if (mode == 't' || mode == ',') {
|
|
char *q = strchr (input, ' ');
|
|
if (q) {
|
|
r_table_query (table, q + 1);
|
|
}
|
|
char *s = r_table_tofancystring (table);
|
|
r_cons_println (s);
|
|
free (s);
|
|
r_table_free (table);
|
|
}
|
|
}
|
|
|
|
static void print_bb(PJ *pj, const RAnalBlock *b, const RAnalFunction *fcn, const ut64 addr) {
|
|
RListIter *iter2;
|
|
RAnalBlock *b2;
|
|
int outputs = (b->jump != UT64_MAX) + (b->fail != UT64_MAX);
|
|
int inputs = 0;
|
|
r_list_foreach (fcn->bbs, iter2, b2) {
|
|
inputs += (b2->jump == b->addr) + (b2->fail == b->addr);
|
|
}
|
|
ut64 opaddr = __opaddr (b, addr);
|
|
if (pj) {
|
|
pj_o (pj);
|
|
pj_kn (pj, "addr", b->addr);
|
|
pj_ki (pj, "size", b->size);
|
|
if (b->jump != UT64_MAX) {
|
|
pj_kn (pj, "jump", b->jump);
|
|
}
|
|
if (b->fail != UT64_MAX) {
|
|
pj_kn (pj, "fail", b->fail);
|
|
}
|
|
if (b->switch_op) {
|
|
pj_k (pj, "switch_op");
|
|
pj_o (pj);
|
|
pj_kn (pj, "addr", b->switch_op->addr);
|
|
pj_kn (pj, "min_val", b->switch_op->min_val);
|
|
pj_kn (pj, "def_val", b->switch_op->def_val);
|
|
pj_kn (pj, "max_val", b->switch_op->max_val);
|
|
pj_k (pj, "cases");
|
|
pj_a (pj);
|
|
{
|
|
RListIter *case_op_iter;
|
|
RAnalCaseOp *case_op;
|
|
r_list_foreach (b->switch_op->cases, case_op_iter, case_op) {
|
|
pj_o (pj);
|
|
pj_kn (pj, "addr", case_op->addr);
|
|
pj_kn (pj, "jump", case_op->jump);
|
|
pj_kn (pj, "value", case_op->value);
|
|
pj_end (pj);
|
|
}
|
|
}
|
|
pj_end (pj);
|
|
pj_end (pj);
|
|
}
|
|
pj_kn (pj, "opaddr", opaddr);
|
|
pj_ki (pj, "inputs", inputs);
|
|
pj_ki (pj, "outputs", outputs);
|
|
pj_ki (pj, "ninstr", b->ninstr);
|
|
pj_ka (pj, "instrs");
|
|
{
|
|
int i;
|
|
for (i = 0; i < b->ninstr; i++) {
|
|
int delta = (i > 0)? b->op_pos[i - 1]: 0;
|
|
pj_n (pj, b->addr + delta);
|
|
}
|
|
}
|
|
pj_end (pj);
|
|
pj_kb (pj, "traced", b->traced);
|
|
pj_end (pj);
|
|
} else {
|
|
if (b->switch_op) {
|
|
r_list_uniq_inplace (b->switch_op->cases, caseval);
|
|
outputs += r_list_length (b->switch_op->cases);
|
|
}
|
|
if (b->jump != UT64_MAX) {
|
|
r_cons_printf ("jump: 0x%08"PFMT64x"\n", b->jump);
|
|
}
|
|
if (b->fail != UT64_MAX) {
|
|
r_cons_printf ("fail: 0x%08"PFMT64x"\n", b->fail);
|
|
}
|
|
r_cons_printf ("opaddr: 0x%08"PFMT64x"\n", opaddr);
|
|
r_cons_printf ("addr: 0x%08" PFMT64x "\nsize: %" PFMT64d "\ninputs: %d\noutputs: %d\nninstr: %d\ntraced: %s\n",
|
|
b->addr, b->size, inputs, outputs, b->ninstr, r_str_bool (b->traced));
|
|
}
|
|
}
|
|
|
|
static bool anal_fcn_list_bb(RCore *core, const char *input, bool one) {
|
|
RDebugTracepoint *tp = NULL;
|
|
RListIter *iter;
|
|
RAnalBlock *b;
|
|
int mode = 0;
|
|
ut64 addr, bbaddr = UT64_MAX;
|
|
PJ *pj = NULL;
|
|
|
|
if (*input == '.') {
|
|
one = true;
|
|
input++;
|
|
}
|
|
if (*input) {
|
|
mode = *input;
|
|
input++;
|
|
}
|
|
if (*input == '.') {
|
|
one = true;
|
|
input++;
|
|
}
|
|
if (input && *input) {
|
|
addr = bbaddr = r_num_math (core->num, input);
|
|
if (!addr && *input != '0') {
|
|
addr = core->offset;
|
|
}
|
|
} else {
|
|
addr = core->offset;
|
|
}
|
|
input = r_str_trim_head_ro (input);
|
|
if (one) {
|
|
bbaddr = addr;
|
|
}
|
|
if (mode == 'j') {
|
|
pj = r_core_pj_new (core);
|
|
if (!pj) {
|
|
r_cons_println ("[]");
|
|
return false;
|
|
}
|
|
pj_a (pj);
|
|
}
|
|
RAnalFunction *fcn = r_anal_get_fcn_in (core->anal, addr, 0);
|
|
if (!fcn) {
|
|
if (mode == 'j') {
|
|
pj_end (pj);
|
|
r_cons_println (pj_string (pj));
|
|
pj_free (pj);
|
|
}
|
|
if (mode == 'i' && input && *input == 'j') {
|
|
r_cons_println ("{}");
|
|
}
|
|
R_LOG_ERROR ("Cannot find function in 0x%08"PFMT64x, addr);
|
|
return false;
|
|
}
|
|
if (mode == '*') {
|
|
r_cons_printf ("fs blocks\n");
|
|
}
|
|
if (fcn->bbs) {
|
|
r_list_sort (fcn->bbs, bb_cmp);
|
|
}
|
|
if (mode == '=') { // afb
|
|
RList *flist = r_list_newf ((RListFree) r_listinfo_free);
|
|
if (!flist) {
|
|
return false;
|
|
}
|
|
ls_foreach (fcn->bbs, iter, b) {
|
|
RInterval inter = (RInterval) {b->addr, b->size};
|
|
RListInfo *info = r_listinfo_new (NULL, inter, inter, -1, NULL);
|
|
if (!info) {
|
|
break;
|
|
}
|
|
r_list_append (flist, info);
|
|
}
|
|
RTable *table = r_core_table (core, "fcnbbs");
|
|
r_table_visual_list (table, flist, core->offset, core->blocksize,
|
|
r_cons_get_size (NULL), r_config_get_i (core->config, "scr.color"));
|
|
r_cons_printf ("\n%s\n", r_table_tostring (table));
|
|
r_table_free (table);
|
|
r_list_free (flist);
|
|
return true;
|
|
}
|
|
|
|
RTable *t = NULL;
|
|
if (mode == 't') {
|
|
t = r_table_new ("fcnbbs");
|
|
r_table_set_columnsf (t, "xdxx", "addr", "size", "jump", "fail");
|
|
}
|
|
r_list_foreach (fcn->bbs, iter, b) {
|
|
if (one) {
|
|
if (bbaddr != UT64_MAX && (bbaddr < b->addr || bbaddr >= (b->addr + b->size))) {
|
|
continue;
|
|
}
|
|
}
|
|
switch (mode) {
|
|
case 't':
|
|
r_table_add_rowf (t, "xdxx", b->addr, b->size, b->jump, b->fail);
|
|
break;
|
|
case 'r':
|
|
if (b->jump == UT64_MAX) {
|
|
ut64 retaddr = r_anal_bb_opaddr_i (b, b->ninstr - 1);
|
|
if (retaddr == UT64_MAX) {
|
|
break;
|
|
}
|
|
|
|
if (!strcmp (input, "*")) {
|
|
r_cons_printf ("db 0x%08"PFMT64x"\n", retaddr);
|
|
} else if (!strcmp (input, "-*")) {
|
|
r_cons_printf ("db-0x%08"PFMT64x"\n", retaddr);
|
|
} else {
|
|
r_cons_printf ("0x%08"PFMT64x"\n", retaddr);
|
|
}
|
|
}
|
|
break;
|
|
case '*':
|
|
r_cons_printf ("f bb.%05" PFMT64x " = 0x%08" PFMT64x "\n",
|
|
b->addr & 0xFFFFF, b->addr);
|
|
break;
|
|
case 'q':
|
|
r_cons_printf ("0x%08" PFMT64x "\n", b->addr);
|
|
break;
|
|
case 'j':
|
|
print_bb (pj, b, fcn, addr);
|
|
break;
|
|
case 'i':
|
|
if (*input == 'j') { // "afbij"
|
|
pj = r_core_pj_new (core);
|
|
if (!pj) {
|
|
return false;
|
|
}
|
|
print_bb (pj, b, fcn, addr);
|
|
r_cons_println (pj_string (pj));
|
|
pj_free (pj);
|
|
} else {
|
|
print_bb (NULL, b, fcn, addr);
|
|
}
|
|
break;
|
|
default:
|
|
tp = r_debug_trace_get (core->dbg, b->addr);
|
|
r_cons_printf ("0x%08" PFMT64x " 0x%08" PFMT64x " %02X:%04X %" PFMT64d,
|
|
b->addr, b->addr + b->size,
|
|
tp? tp->times: 0, tp? tp->count: 0,
|
|
b->size);
|
|
if (b->jump != UT64_MAX) {
|
|
r_cons_printf (" j 0x%08" PFMT64x, b->jump);
|
|
}
|
|
if (b->fail != UT64_MAX) {
|
|
r_cons_printf (" f 0x%08" PFMT64x, b->fail);
|
|
}
|
|
if (b->switch_op) {
|
|
RAnalCaseOp *cop;
|
|
RListIter *iter;
|
|
r_list_uniq_inplace (b->switch_op->cases, caseval);
|
|
r_list_foreach (b->switch_op->cases, iter, cop) {
|
|
r_cons_printf (" s 0x%08" PFMT64x, cop->addr);
|
|
}
|
|
}
|
|
r_cons_newline ();
|
|
break;
|
|
}
|
|
}
|
|
if (mode == 't') {
|
|
const char *arg = input;
|
|
if (r_table_query (t, arg)) {
|
|
char *ts = r_table_tofancystring (t);
|
|
r_cons_printf ("%s", ts);
|
|
free (ts);
|
|
}
|
|
r_table_free (t);
|
|
} else if (mode == 'j') {
|
|
pj_end (pj);
|
|
r_cons_println (pj_string (pj));
|
|
pj_free (pj);
|
|
}
|
|
return true;
|
|
}
|
|
|
|
static bool anal_bb_edge(RCore *core, const char *input) {
|
|
// "afbe" switch-bb-addr case-bb-addr
|
|
char *arg = strdup (r_str_trim_head_ro (input));
|
|
char *sp = strchr (arg, ' ');
|
|
bool ret = false;
|
|
if (sp) {
|
|
*sp++ = 0;
|
|
ut64 switch_addr = r_num_math (core->num, arg);
|
|
ut64 case_addr = r_num_math (core->num, sp);
|
|
RList *blocks = r_anal_get_blocks_in (core->anal, switch_addr);
|
|
if (blocks && !r_list_empty (blocks)) {
|
|
r_anal_block_add_switch_case (r_list_first (blocks), switch_addr, 0, case_addr);
|
|
ret = true;
|
|
}
|
|
r_list_free (blocks);
|
|
}
|
|
free (arg);
|
|
return ret;
|
|
}
|
|
|
|
static bool anal_fcn_del_bb(RCore *core, const char *input) {
|
|
ut64 addr = r_num_math (core->num, input);
|
|
if (!addr) {
|
|
addr = core->offset;
|
|
}
|
|
RAnalFunction *fcn = r_anal_get_fcn_in (core->anal, addr, -1);
|
|
if (fcn) {
|
|
if (!strcmp (input, "*")) {
|
|
while (!r_list_empty (fcn->bbs)) {
|
|
r_anal_function_remove_block (fcn, r_list_first (fcn->bbs));
|
|
}
|
|
} else {
|
|
RAnalBlock *b;
|
|
RListIter *iter;
|
|
r_list_foreach (fcn->bbs, iter, b) {
|
|
if (b->addr == addr) {
|
|
r_anal_function_remove_block (fcn, b);
|
|
return true;
|
|
}
|
|
}
|
|
R_LOG_ERROR ("Cannot find basic block");
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
static int cmd_afbplus(RCore *core, const char *input) {
|
|
// fcn_addr bb_addr bb_size [jump] [fail]
|
|
const char *ptr2 = NULL;
|
|
ut64 fcnaddr = -1LL, addr = -1LL;
|
|
ut64 size = 0LL;
|
|
ut64 jump = UT64_MAX;
|
|
ut64 fail = UT64_MAX;
|
|
RAnalFunction *fcn = NULL;
|
|
RAnalDiff *diff = NULL;
|
|
|
|
char *ptr = r_str_trim_dup (input);
|
|
|
|
switch (r_str_word_set0 (ptr)) {
|
|
case 6:
|
|
ptr2 = r_str_word_get0 (ptr, 6);
|
|
if (!(diff = r_anal_diff_new ())) {
|
|
R_LOG_ERROR ("Cannot init RAnalDiff");
|
|
free (ptr);
|
|
return false;
|
|
}
|
|
if (ptr2[0] == 'm') {
|
|
diff->type = R_ANAL_DIFF_TYPE_MATCH;
|
|
} else if (ptr2[0] == 'u') {
|
|
diff->type = R_ANAL_DIFF_TYPE_UNMATCH;
|
|
}
|
|
case 5: // get fail
|
|
fail = r_num_math (core->num, r_str_word_get0 (ptr, 4));
|
|
case 4: // get jump
|
|
jump = r_num_math (core->num, r_str_word_get0 (ptr, 3));
|
|
case 3: // get size
|
|
size = r_num_math (core->num, r_str_word_get0 (ptr, 2));
|
|
case 2: // get addr
|
|
addr = r_num_math (core->num, r_str_word_get0 (ptr, 1));
|
|
case 1: // get fcnaddr
|
|
fcnaddr = r_num_math (core->num, r_str_word_get0 (ptr, 0));
|
|
}
|
|
fcn = r_anal_get_function_at (core->anal, fcnaddr);
|
|
if (fcn) {
|
|
if (!r_anal_function_add_bb (core->anal, fcn, addr, size, jump, fail, diff)) {
|
|
R_LOG_ERROR ("afb+: Cannot add basic block at 0x%08"PFMT64x" with size %d", addr, (int)size);
|
|
}
|
|
} else {
|
|
R_LOG_ERROR ("afb+ No function at 0x%" PFMT64x " from 0x%08"PFMT64x" -> 0x%08"PFMT64x,
|
|
fcnaddr, addr, jump);
|
|
}
|
|
r_anal_diff_free (diff);
|
|
free (ptr);
|
|
return true;
|
|
}
|
|
|
|
static void r_core_anal_nofunclist(RCore *core, const char *input) {
|
|
int minlen = (int)(input[0]==' ') ? r_num_math (core->num, input + 1): 16;
|
|
ut64 code_size = r_num_get (core->num, "$SS");
|
|
ut64 base_addr = r_num_get (core->num, "$S");
|
|
ut64 chunk_size, chunk_offset, i;
|
|
RListIter *iter, *iter2;
|
|
RAnalFunction *fcn;
|
|
RAnalBlock *b;
|
|
char* bitmap;
|
|
int counter;
|
|
|
|
if (minlen < 1) {
|
|
minlen = 1;
|
|
}
|
|
if (code_size < 1) {
|
|
return;
|
|
}
|
|
bitmap = calloc (1, code_size + 64);
|
|
if (!bitmap) {
|
|
return;
|
|
}
|
|
|
|
// for each function
|
|
r_list_foreach (core->anal->fcns, iter, fcn) {
|
|
// for each basic block in the function
|
|
r_list_foreach (fcn->bbs, iter2, b) {
|
|
// if it is not withing range, continue
|
|
if ((fcn->addr < base_addr) || (fcn->addr >= base_addr+code_size))
|
|
continue;
|
|
// otherwise mark each byte in the BB in the bitmap
|
|
for (counter = 0; counter < b->size; counter++) {
|
|
bitmap[b->addr+counter-base_addr] = '=';
|
|
}
|
|
// finally, add a special marker to show the beginning of a
|
|
// function
|
|
bitmap[fcn->addr-base_addr] = 'F';
|
|
}
|
|
}
|
|
|
|
// Now we print the list of memory regions that are not assigned to a function
|
|
chunk_size = 0;
|
|
chunk_offset = 0;
|
|
for (i = 0; i < code_size; i++) {
|
|
if (bitmap[i]) {
|
|
// We only print a region is its size is bigger than 15 bytes
|
|
if (chunk_size >= minlen) {
|
|
fcn = r_anal_get_fcn_in (core->anal, base_addr+chunk_offset, R_ANAL_FCN_TYPE_FCN | R_ANAL_FCN_TYPE_SYM);
|
|
if (fcn) {
|
|
r_cons_printf ("0x%08" PFMT64x " %6" PFMT64u " %s\n",
|
|
base_addr+chunk_offset, chunk_size, fcn->name);
|
|
} else {
|
|
r_cons_printf ("0x%08" PFMT64x " %6" PFMT64u "\n",
|
|
base_addr+chunk_offset, chunk_size);
|
|
}
|
|
}
|
|
chunk_size = 0;
|
|
chunk_offset = i + 1;
|
|
continue;
|
|
}
|
|
chunk_size += 1;
|
|
}
|
|
if (chunk_size >= 16) {
|
|
fcn = r_anal_get_fcn_in (core->anal, base_addr+chunk_offset, R_ANAL_FCN_TYPE_FCN | R_ANAL_FCN_TYPE_SYM);
|
|
if (fcn) {
|
|
r_cons_printf ("0x%08"PFMT64x" %6" PFMT64u " %s\n", base_addr+chunk_offset, chunk_size, fcn->name);
|
|
} else {
|
|
r_cons_printf ("0x%08"PFMT64x" %6" PFMT64u "\n", base_addr+chunk_offset, chunk_size);
|
|
}
|
|
}
|
|
free (bitmap);
|
|
}
|
|
|
|
static void r_core_anal_fmap(RCore *core, const char *input) {
|
|
int show_color = r_config_get_i (core->config, "scr.color");
|
|
int cols = r_config_get_i (core->config, "hex.cols") * 4;
|
|
ut64 code_size = r_num_get (core->num, "$SS");
|
|
ut64 base_addr = r_num_get (core->num, "$S");
|
|
RListIter *iter, *iter2;
|
|
RAnalFunction *fcn;
|
|
RAnalBlock *b;
|
|
int assigned;
|
|
ut64 i;
|
|
|
|
if (code_size < 1) {
|
|
return;
|
|
}
|
|
char *bitmap = calloc (1, code_size + 64);
|
|
if (!bitmap) {
|
|
return;
|
|
}
|
|
|
|
// for each function
|
|
r_list_foreach (core->anal->fcns, iter, fcn) {
|
|
// for each basic block in the function
|
|
r_list_foreach (fcn->bbs, iter2, b) {
|
|
// if it is not within range, continue
|
|
if ((fcn->addr < base_addr) || (fcn->addr >= base_addr+code_size))
|
|
continue;
|
|
// otherwise mark each byte in the BB in the bitmap
|
|
int counter = 1;
|
|
for (counter = 0; counter < b->size; counter++) {
|
|
bitmap[b->addr+counter-base_addr] = '=';
|
|
}
|
|
bitmap[fcn->addr-base_addr] = 'F';
|
|
}
|
|
}
|
|
// print the bitmap
|
|
assigned = 0;
|
|
if (cols < 1) {
|
|
cols = 1;
|
|
}
|
|
for (i = 0; i < code_size; i += 1) {
|
|
if (!(i % cols)) {
|
|
r_cons_printf ("\n0x%08"PFMT64x" ", base_addr+i);
|
|
}
|
|
if (bitmap[i]) {
|
|
assigned++;
|
|
}
|
|
if (show_color) {
|
|
if (bitmap[i]) {
|
|
r_cons_printf ("%s%c\x1b[0m", Color_GREEN, bitmap[i]);
|
|
} else {
|
|
r_cons_printf (".");
|
|
}
|
|
} else {
|
|
r_cons_printf ("%c", bitmap[i] ? bitmap[i] : '.' );
|
|
}
|
|
}
|
|
r_cons_printf ("\n%d / %" PFMT64u " (%.2lf%%) bytes assigned to a function\n",
|
|
assigned, code_size, 100.0 * ( (float) assigned) / code_size);
|
|
free (bitmap);
|
|
}
|
|
|
|
static bool fcnNeedsPrefix(const char *name) {
|
|
if (!strncmp (name, "entry", 5)) {
|
|
return false;
|
|
}
|
|
if (!strncmp (name, "main", 4)) {
|
|
return false;
|
|
}
|
|
return (!strchr (name, '.'));
|
|
}
|
|
|
|
static char *getFunctionName(RCore *core, ut64 off, const char *name, bool prefix) {
|
|
const char *fcnpfx = "";
|
|
if (prefix) {
|
|
if (fcnNeedsPrefix (name) && (!fcnpfx || !*fcnpfx)) {
|
|
fcnpfx = "fcn";
|
|
} else {
|
|
fcnpfx = r_config_get (core->config, "anal.fcnprefix");
|
|
}
|
|
}
|
|
if (r_reg_get (core->anal->reg, name, -1)) {
|
|
return r_str_newf ("%s.%08"PFMT64x, "fcn", off);
|
|
}
|
|
return strdup (name); // r_str_newf ("%s%s%s", fcnpfx, *fcnpfx? ".": "", name);
|
|
}
|
|
|
|
static void rename_fcnsig(RAnal *anal, const char *oname, const char *nname) {
|
|
#define DB anal->sdb_types
|
|
// rename type
|
|
const char *type = sdb_const_get (DB, oname, 0);
|
|
if (type && !strcmp (type, "func")) {
|
|
sdb_unset (DB, oname, 0);
|
|
sdb_set (DB, nname, "func", 0);
|
|
}
|
|
// rename args
|
|
char *k = r_str_newf ("func.%s.args", oname);
|
|
const char *argstr = sdb_const_get (DB, k, 0);
|
|
if (R_STR_ISEMPTY (argstr)) {
|
|
free (k);
|
|
return;
|
|
}
|
|
int i, args = r_num_get (NULL, argstr);
|
|
sdb_unset (DB, k, 0);
|
|
free (k);
|
|
k = r_str_newf ("func.%s.args", nname);
|
|
char *v = r_str_newf ("%d", (int)args);
|
|
sdb_set (DB, k, v, 0);
|
|
free (v);
|
|
// rename arg#
|
|
free (k);
|
|
for (i = 0; i < args; i++) {
|
|
k = r_str_newf ("func.%s.arg.%d", oname, i);
|
|
char *v = sdb_get (DB, k, 0);
|
|
if (v) {
|
|
sdb_unset (DB, k, 0);
|
|
free (k);
|
|
k = r_str_newf ("func.%s.arg.%d", nname, i);
|
|
sdb_set (DB, k, v, 0);
|
|
free (v);
|
|
}
|
|
free (k);
|
|
}
|
|
// unset the leftovers
|
|
for (; i < args + 8; i++) {
|
|
k = r_str_newf ("func.%s.arg.%d", oname, i);
|
|
sdb_unset (DB, k, 0);
|
|
free (k);
|
|
}
|
|
// rename ret
|
|
k = r_str_newf ("func.%s.ret", oname);
|
|
v = sdb_get (DB, k, 0);
|
|
sdb_unset (DB, k, 0);
|
|
free (k);
|
|
k = r_str_newf ("func.%s.ret", nname);
|
|
sdb_set (DB, k, v, 0);
|
|
free (k);
|
|
free (v);
|
|
#undef DB
|
|
}
|
|
|
|
/* TODO: move into r_anal_function_rename (); */
|
|
static bool __setFunctionName(RCore *core, ut64 addr, const char *_name, bool prefix) {
|
|
r_return_val_if_fail (core && _name, false);
|
|
_name = r_str_trim_head_ro (_name);
|
|
char *name = getFunctionName (core, addr, _name, prefix);
|
|
char *fname = r_name_filter_dup (name);
|
|
// RAnalFunction *fcn = r_anal_get_fcn_in (core->anal, addr, R_ANAL_FCN_TYPE_ANY);
|
|
RAnalFunction *fcn = r_anal_get_function_at (core->anal, addr);
|
|
if (fcn) {
|
|
char *oname = strdup (fcn->name);
|
|
RFlagItem *flag = r_flag_get (core->flags, fcn->name);
|
|
if (flag && flag->space && strcmp (flag->space->name, R_FLAGS_FS_FUNCTIONS) == 0) {
|
|
// Only flags in the functions fs should be renamed, e.g. we don't want to rename symbol flags.
|
|
r_flag_rename (core->flags, flag, fname);
|
|
} else {
|
|
// No flag or not specific to the function, create a new one.
|
|
r_flag_space_push (core->flags, R_FLAGS_FS_FUNCTIONS);
|
|
r_flag_set (core->flags, name, fcn->addr, r_anal_function_size_from_entry (fcn));
|
|
r_flag_space_pop (core->flags);
|
|
}
|
|
rename_fcnsig (core->anal, oname, name);
|
|
r_anal_function_rename (fcn, name);
|
|
if (core->anal->cb.on_fcn_rename) {
|
|
core->anal->cb.on_fcn_rename (core->anal, core->anal->user, fcn, name);
|
|
}
|
|
free (oname);
|
|
free (name);
|
|
free (fname);
|
|
return true;
|
|
}
|
|
free (name);
|
|
free (fname);
|
|
return false;
|
|
}
|
|
|
|
static void afCc(RCore *core, const char *input) {
|
|
ut64 addr;
|
|
RAnalFunction *fcn;
|
|
if (*input == ' ') {
|
|
addr = r_num_math (core->num, input);
|
|
} else {
|
|
addr = core->offset;
|
|
}
|
|
if (addr == 0LL) {
|
|
fcn = r_anal_get_function_byname (core->anal, input + 3);
|
|
} else {
|
|
fcn = r_anal_get_fcn_in (core->anal, addr, R_ANAL_FCN_TYPE_NULL);
|
|
}
|
|
if (fcn) {
|
|
ut32 totalCycles = r_anal_function_cost (fcn);
|
|
// FIXME: This defeats the purpose of the function, but afC is used in project files.
|
|
// cf. canal.c
|
|
r_cons_printf ("%d\n", totalCycles);
|
|
} else {
|
|
R_LOG_ERROR ("afCc: Cannot find function");
|
|
}
|
|
}
|
|
|
|
static void cmd_anal_fcn_sig(RCore *core, const char *input) {
|
|
bool json = (input[0] == 'j');
|
|
char *p = strchr (input, ' ');
|
|
char *fcn_name = p ? r_str_trim_dup (p): NULL;
|
|
RListIter *iter;
|
|
RAnalFuncArg *arg;
|
|
|
|
RAnalFunction *fcn;
|
|
if (fcn_name) {
|
|
fcn = r_anal_get_function_byname (core->anal, fcn_name);
|
|
} else {
|
|
fcn = r_anal_get_fcn_in (core->anal, core->offset, 0);
|
|
if (fcn) {
|
|
fcn_name = fcn->name;
|
|
}
|
|
}
|
|
if (!fcn) {
|
|
return;
|
|
}
|
|
|
|
if (json) {
|
|
PJ *j = pj_new ();
|
|
if (!j) {
|
|
return;
|
|
}
|
|
pj_a (j);
|
|
|
|
char *key = NULL;
|
|
if (fcn_name) {
|
|
key = resolve_fcn_name (core->anal, fcn_name);
|
|
}
|
|
|
|
if (key) {
|
|
const char *fcn_type = r_type_func_ret (core->anal->sdb_types, key);
|
|
int nargs = r_type_func_args_count (core->anal->sdb_types, key);
|
|
if (fcn_type) {
|
|
pj_o (j);
|
|
pj_ks (j, "name", r_str_getf (key));
|
|
pj_ks (j, "return", r_str_getf (fcn_type));
|
|
pj_k (j, "args");
|
|
pj_a (j);
|
|
if (nargs) {
|
|
RList *list = r_core_get_func_args (core, fcn_name);
|
|
r_list_foreach (list, iter, arg) {
|
|
char *type = arg->orig_c_type;
|
|
pj_o (j);
|
|
pj_ks (j, "name", arg->name);
|
|
pj_ks (j, "type", type);
|
|
pj_end (j);
|
|
}
|
|
r_list_free (list);
|
|
}
|
|
pj_end (j);
|
|
pj_ki (j, "count", nargs);
|
|
pj_end (j);
|
|
}
|
|
free (key);
|
|
} else {
|
|
pj_o (j);
|
|
pj_ks (j, "name", r_str_getf (fcn_name));
|
|
pj_k (j, "args");
|
|
pj_a (j);
|
|
|
|
RAnalFcnVarsCache cache;
|
|
r_anal_function_vars_cache_init (core->anal, &cache, fcn);
|
|
int nargs = 0;
|
|
RAnalVar *var;
|
|
r_list_foreach (cache.rvars, iter, var) {
|
|
nargs++;
|
|
pj_o (j);
|
|
pj_ks (j, "name", var->name);
|
|
pj_ks (j, "type", var->type);
|
|
pj_end (j);
|
|
}
|
|
r_list_foreach (cache.bvars, iter, var) {
|
|
if (var->delta <= 0) {
|
|
continue;
|
|
}
|
|
nargs++;
|
|
pj_o (j);
|
|
pj_ks (j, "name", var->name);
|
|
pj_ks (j, "type", var->type);
|
|
pj_end (j);
|
|
}
|
|
r_list_foreach (cache.svars, iter, var) {
|
|
if (!var->isarg) {
|
|
continue;
|
|
}
|
|
nargs++;
|
|
pj_o (j);
|
|
pj_ks (j, "name", var->name);
|
|
pj_ks (j, "type", var->type);
|
|
pj_end (j);
|
|
}
|
|
r_anal_function_vars_cache_fini (&cache);
|
|
|
|
pj_end (j);
|
|
pj_ki (j, "count", nargs);
|
|
pj_end (j);
|
|
}
|
|
pj_end (j);
|
|
const char *s = pj_string (j);
|
|
if (s) {
|
|
r_cons_printf ("%s\n", s);
|
|
}
|
|
pj_free (j);
|
|
} else {
|
|
char *sig = r_anal_function_format_sig (core->anal, fcn, fcn_name, NULL, NULL, NULL);
|
|
if (sig) {
|
|
r_cons_printf ("%s\n", sig);
|
|
free (sig);
|
|
}
|
|
}
|
|
}
|
|
|
|
static void __updateStats(RCore *core, Sdb *db, ut64 addr, int statsMode) {
|
|
RAnalOp *op = r_core_anal_op (core, addr, R_ANAL_OP_MASK_BASIC | R_ANAL_OP_MASK_HINT | R_ANAL_OP_MASK_DISASM);
|
|
if (!op) {
|
|
return;
|
|
}
|
|
if (statsMode == 'f') {
|
|
const char *family = r_anal_op_family_to_string (op->family);
|
|
sdb_num_inc (db, family, 1, 0);
|
|
} else if (statsMode == 'o') {
|
|
const char *type = r_anal_optype_to_string (op->type);
|
|
sdb_num_inc (db, type, 1, 0);
|
|
} else {
|
|
char *mnem = strdup (op->mnemonic);
|
|
char *sp = strchr (mnem, ' ');
|
|
if (sp) {
|
|
*sp = 0;
|
|
//memmove (mnem, sp + 1, strlen (sp));
|
|
}
|
|
sdb_num_inc (db, mnem, 1, 0);
|
|
}
|
|
//sdb_set (db, family, "1", 0);
|
|
//r_cons_printf ("0x%08"PFMT64x" %s\n", addr, family);
|
|
r_anal_op_free (op);
|
|
// r_core_cmdf (core, "pd 1 @ 0x%08"PFMT64x, addr);
|
|
}
|
|
|
|
static Sdb *__core_cmd_anal_fcn_stats(RCore *core, const char *input) {
|
|
bool silentMode = false;
|
|
int statsMode = 0;
|
|
if (*input == '*') {
|
|
silentMode = true;
|
|
input++;
|
|
}
|
|
switch (*input) {
|
|
case '?':
|
|
r_core_cmd_help (core, help_msg_afis);
|
|
return NULL;
|
|
case 'f':
|
|
case 'o':
|
|
statsMode = *input;
|
|
input++;
|
|
break;
|
|
}
|
|
|
|
RAnalFunction *fcn = r_anal_get_fcn_in (core->anal, core->offset, -1);
|
|
if (!fcn) {
|
|
R_LOG_ERROR ("Cannot find any function at 0x%08"PFMT64x, core->offset);
|
|
return NULL;
|
|
}
|
|
Sdb *db = sdb_new0 ();
|
|
RAnalBlock *bb;
|
|
RListIter *iter;
|
|
r_list_foreach (fcn->bbs, iter, bb) {
|
|
int i;
|
|
__updateStats (core, db, bb->addr, statsMode);
|
|
for (i = 0; i < bb->op_pos_size; i++) {
|
|
ut16 op_pos = bb->op_pos[i];
|
|
__updateStats (core, db, bb->addr + op_pos, statsMode);
|
|
}
|
|
}
|
|
if (silentMode) {
|
|
// nothing
|
|
} else if (*input == 't') {
|
|
SdbList *ls = sdb_foreach_list (db, true);
|
|
SdbListIter *it;
|
|
RTable *t = r_table_new ("fcnstats");
|
|
SdbKv *kv;
|
|
RTableColumnType *typeString = r_table_type ("string");
|
|
RTableColumnType *typeNumber = r_table_type ("number");
|
|
r_table_add_column (t, typeString, "name", 0);
|
|
ls_foreach (ls, it, kv) {
|
|
const char *key = sdbkv_key (kv);
|
|
r_table_add_column (t, typeNumber, key, 0);
|
|
}
|
|
RList *items = r_list_newf (free);
|
|
r_list_append (items, strdup (fcn->name));
|
|
ls_foreach (ls, it, kv) {
|
|
const char *value = sdbkv_value (kv);
|
|
int nv = (int)r_num_get (NULL, value);
|
|
r_list_append (items, r_str_newf ("%d", nv));
|
|
}
|
|
r_table_add_row_list (t, items);
|
|
r_table_query (t, input + 1);
|
|
char *ts = r_table_tostring (t);
|
|
r_cons_printf ("%s", ts);
|
|
free (ts);
|
|
r_table_free (t);
|
|
} else {
|
|
SdbList *ls = sdb_foreach_list (db, true);
|
|
SdbListIter *it;
|
|
SdbKv *kv;
|
|
ls_foreach (ls, it, kv) {
|
|
const char *key = sdbkv_key(kv);
|
|
const char *value = sdbkv_value(kv);
|
|
r_cons_printf ("%4d %s\n", (int)r_num_get (NULL, value), key);
|
|
}
|
|
}
|
|
return db;
|
|
//sdb_free (db);
|
|
}
|
|
|
|
static void __core_cmd_anal_fcn_allstats(RCore *core, const char *input) {
|
|
RAnalFunction *fcn;
|
|
SdbKv *kv;
|
|
RListIter *iter;
|
|
SdbListIter *it;
|
|
RList *dbs = r_list_newf ((RListFree)sdb_free);
|
|
Sdb *d = sdb_new0 ();
|
|
ut64 oseek = core->offset;
|
|
bool isJson = strchr (input, 'j');
|
|
|
|
char *inp = r_str_newf ("*%s", input);
|
|
r_list_foreach (core->anal->fcns, iter, fcn) {
|
|
r_core_seek (core, fcn->addr, true);
|
|
Sdb *db = __core_cmd_anal_fcn_stats (core, inp);
|
|
sdb_num_set (db, ".addr", fcn->addr, 0);
|
|
r_list_append (dbs, db);
|
|
}
|
|
free (inp);
|
|
Sdb *db;
|
|
r_list_foreach (dbs, iter, db) {
|
|
SdbList *ls = sdb_foreach_list (db, true);
|
|
ls_foreach (ls, it, kv) {
|
|
const char *name = sdbkv_key(kv);
|
|
sdb_add (d, name, "1", 0);
|
|
}
|
|
ls_free (ls);
|
|
}
|
|
RTable *t = r_table_new ("fcnallstats");
|
|
SdbList *ls = sdb_foreach_list (d, true);
|
|
RTableColumnType *typeString = r_table_type ("string");
|
|
RTableColumnType *typeNumber = r_table_type ("number");
|
|
r_table_add_column (t, typeString, "name", 0);
|
|
r_table_add_column (t, typeNumber, "addr", 0);
|
|
ls_foreach (ls, it, kv) {
|
|
const char *key = sdbkv_key (kv);
|
|
if (*key == '.') continue;
|
|
r_table_add_column (t, typeNumber, key, 0);
|
|
}
|
|
sdb_free (d);
|
|
|
|
r_list_foreach (dbs, iter, db) {
|
|
SdbList *ls = sdb_foreach_list (db, false);
|
|
SdbListIter *it;
|
|
SdbKv *kv;
|
|
char *names[100];
|
|
int i;
|
|
for (i = 0; i < 100; i++) {
|
|
names[i] = NULL;
|
|
}
|
|
ls_foreach (ls, it, kv) {
|
|
const char *key = sdbkv_key(kv);
|
|
const char *value = sdbkv_value (kv);
|
|
if (*key == '.') {
|
|
continue;
|
|
}
|
|
int idx = r_table_column_nth (t, key);
|
|
if (idx != -1) {
|
|
ut64 nv = r_num_get (NULL, value);
|
|
names[idx] = r_str_newf ("%d", (int)nv);
|
|
} else {
|
|
R_LOG_ERROR ("Invalid column name (%s)", key);
|
|
}
|
|
}
|
|
RList *items = r_list_newf (free);
|
|
ut64 fcnAddr = sdb_num_get (db, ".addr", 0);
|
|
|
|
RAnalFunction *fcn = r_anal_get_fcn_in (core->anal, fcnAddr, 0);
|
|
r_list_append (items, fcn?strdup (fcn->name):strdup (""));
|
|
r_list_append (items, fcn?r_str_newf ("0x%08"PFMT64x, fcnAddr): strdup ("0"));
|
|
int cols = r_list_length (t->cols);
|
|
for (i = 2; i < cols; i++) {
|
|
if (names[i]) {
|
|
if (names[i][0] != '.') {
|
|
r_list_append (items, strdup (names[i]));
|
|
}
|
|
R_FREE (names[i]);
|
|
} else {
|
|
r_list_append (items, strdup ("0"));
|
|
}
|
|
}
|
|
r_table_add_row_list (t, items);
|
|
}
|
|
r_table_query (t, (*input)?input + 1: "");
|
|
char *ts = isJson? r_table_tojson(t): r_table_tostring (t);
|
|
r_cons_printf ("%s%s", ts, isJson ? "\n" : "");
|
|
free (ts);
|
|
r_table_free (t);
|
|
r_core_seek (core, oseek, true);
|
|
r_list_free (dbs);
|
|
}
|
|
|
|
R_API char *fcnshowr(RAnalFunction *function) {
|
|
RAnal *a = function->anal;
|
|
//PJ *pj = a->coreb.pjWithEncoding (a->coreb.core);
|
|
const char *realname = NULL, *import_substring = NULL;
|
|
RStrBuf *sb = r_strbuf_new ("");
|
|
|
|
RFlagItem *flag = a->flag_get (a->flb.f, function->addr);
|
|
// Can't access R_FLAGS_FS_IMPORTS, since it is defined in r_core.h
|
|
if (flag && flag->space && !strcmp (flag->space->name, "imports")) {
|
|
// Get substring after last dot
|
|
import_substring = r_str_rchr (function->name, NULL, '.');
|
|
if (import_substring) {
|
|
realname = import_substring + 1;
|
|
}
|
|
} else {
|
|
realname = function->name;
|
|
}
|
|
|
|
char *args = strdup ("");
|
|
char *sdb_ret = r_str_newf ("func.%s.ret", realname);
|
|
char *sdb_args = r_str_newf ("func.%s.args", realname);
|
|
// RList *args_list = r_list_newf ((RListFree) free);
|
|
unsigned int i;
|
|
// const char *ret_type = sdb_const_get (a->sdb_types, sdb_ret, 0);
|
|
const char *argc_str = sdb_const_get (a->sdb_types, sdb_args, 0);
|
|
const int argc = argc_str? atoi (argc_str): 0;
|
|
|
|
const bool no_return = r_anal_noreturn_at_addr (a, function->addr);
|
|
if (no_return) {
|
|
r_strbuf_appendf (sb, "tn %s\n", function->name);
|
|
}
|
|
if (function->cc) {
|
|
r_strbuf_appendf (sb, "afc %s\n", function->cc);
|
|
}
|
|
for (i = 0; i < argc; i++) {
|
|
char *sdb_arg_i = r_str_newf ("func.%s.arg.%d", realname, i);
|
|
char *type = sdb_get (a->sdb_types, sdb_arg_i, 0);
|
|
if (!type) continue;
|
|
char *comma = strchr (type, ',');
|
|
if (comma) {
|
|
*comma = 0;
|
|
r_strf_var (regname, 32, "A%d", i);
|
|
const char *cc_arg = r_reg_get_name (a->reg, r_reg_get_name_idx (regname));
|
|
r_strbuf_appendf (sb, "afvr %s %s %s\n", cc_arg, comma + 1, type);
|
|
}
|
|
free (type);
|
|
free (sdb_arg_i);
|
|
}
|
|
free (sdb_args);
|
|
free (sdb_ret);
|
|
free (args);
|
|
return r_strbuf_drain (sb);
|
|
}
|
|
|
|
static void cmd_afsr(RCore *core, const char *input) {
|
|
ut64 addr = core->offset;
|
|
RAnalFunction *f;
|
|
if ((f = r_anal_get_fcn_in (core->anal, addr, R_ANAL_FCN_TYPE_NULL))) {
|
|
char *res = fcnshowr (f);
|
|
if (R_STR_ISNOTEMPTY (res)) {
|
|
r_cons_println (res);
|
|
}
|
|
free (res);
|
|
} else {
|
|
R_LOG_ERROR ("No function defined at 0x%08" PFMT64x, addr);
|
|
}
|
|
}
|
|
|
|
static void cmd_afsj(RCore *core, const char *arg) {
|
|
ut64 a = r_num_math (core->num, arg);
|
|
const ut64 addr = a? a: core->offset;
|
|
RAnalFunction *f = r_anal_get_fcn_in (core->anal, addr, -1);
|
|
if (f) {
|
|
char *s = r_anal_function_get_json (f);
|
|
r_cons_printf ("%s\n", s);
|
|
free (s);
|
|
} else {
|
|
R_LOG_ERROR ("Cannot find function in 0x%08"PFMT64x, addr);
|
|
}
|
|
}
|
|
|
|
static void cmd_afbc(RCore *core, const char *input) {
|
|
r_return_if_fail (core && input);
|
|
char *ptr = strdup (input);
|
|
if (!ptr) {
|
|
return;
|
|
}
|
|
if (*ptr == '?') {
|
|
eprintf ("Usage: afbc red @ addrOfBlock\n");
|
|
} else if (!*ptr) {
|
|
RAnalBlock *bb = r_anal_get_block_at (core->anal, core->offset);
|
|
if (bb && (bb->color.r || bb->color.g || bb->color.b)) {
|
|
char *s = r_cons_rgb_str (NULL, -1, &bb->color);
|
|
if (s) {
|
|
char *name = r_cons_rgb_tostring (bb->color.r, bb->color.g, bb->color.b);
|
|
r_cons_printf ("%s%s"Color_RESET"\n", s, name);
|
|
free (name);
|
|
free (s);
|
|
}
|
|
}
|
|
} else {
|
|
ut64 addr = core->offset;
|
|
char *space = strchr (ptr, ' ');
|
|
if (space) {
|
|
*space++ = 0;
|
|
addr = r_num_math (core->num, space);
|
|
}
|
|
RColor color = {0};
|
|
(void)r_cons_pal_parse (ptr, &color);
|
|
if (color.r || color.g || color.b) {
|
|
RAnalBlock *bb = r_anal_get_block_at (core->anal, addr);
|
|
if (bb) {
|
|
bb->color = color;
|
|
}
|
|
} else {
|
|
R_LOG_ERROR ("Invalid color: '%s'", ptr);
|
|
}
|
|
}
|
|
free (ptr);
|
|
}
|
|
|
|
// Fcn Xrefs Map
|
|
static void xrefs_map(RCore *core, const char *input) {
|
|
RListIter *iter, *iter2, *iter3;
|
|
RAnalRef *r;
|
|
RAnalFunction *f, *f2;
|
|
int col = 0;
|
|
int count = 0;
|
|
do {
|
|
r_cons_print (" ");
|
|
count = 0;
|
|
r_list_foreach (core->anal->fcns, iter, f) {
|
|
int nlen = strlen (f->name);
|
|
if (col >= nlen) {
|
|
r_cons_printf ("|");
|
|
continue;
|
|
}
|
|
count++;
|
|
r_cons_printf ("%c", f->name[col]);
|
|
}
|
|
r_cons_newline ();
|
|
col++;
|
|
} while (count);
|
|
|
|
int total = 0;
|
|
r_list_foreach (core->anal->fcns, iter, f) {
|
|
RList *refs = r_anal_function_get_refs (f);
|
|
r_cons_printf ("0x%08"PFMT64x" ", f->addr);
|
|
total = 0;
|
|
r_list_foreach (core->anal->fcns, iter2, f2) {
|
|
int count = 0;
|
|
r_list_foreach (refs, iter3, r) {
|
|
if (r->addr == f2->addr) {
|
|
count ++;
|
|
}
|
|
}
|
|
if (count > 0) {
|
|
total++;
|
|
if (count < 10) {
|
|
r_cons_printf ("%d", count);
|
|
} else {
|
|
r_cons_printf ("+");
|
|
}
|
|
} else {
|
|
r_cons_printf (".");
|
|
}
|
|
}
|
|
if (total > 0) {
|
|
r_cons_printf (" %s\n", f->name);
|
|
} else {
|
|
r_cons_printf ("\r");
|
|
}
|
|
r_list_free (refs);
|
|
}
|
|
}
|
|
|
|
R_API void r_core_af(RCore *core, ut64 addr, const char *name, bool anal_calls) {
|
|
int depth = r_config_get_i (core->config, "anal.depth");
|
|
|
|
//r_core_anal_undefine (core, core->offset);
|
|
r_core_anal_fcn (core, addr, UT64_MAX, R_ANAL_REF_TYPE_NULL, depth);
|
|
RAnalFunction *fcn = r_anal_get_fcn_in (core->anal, addr, 0);
|
|
if (fcn) {
|
|
/* ensure we use a proper name */
|
|
__setFunctionName (core, addr, fcn->name, false);
|
|
if (core->anal->opt.vars) {
|
|
r_core_recover_vars (core, fcn, true);
|
|
}
|
|
__add_vars_sdb (core, fcn);
|
|
} else {
|
|
if (core->anal->verbose) {
|
|
R_LOG_WARN ("Unable to analyze function at 0x%08"PFMT64x, addr);
|
|
}
|
|
}
|
|
if (anal_calls) {
|
|
fcn = r_anal_get_fcn_in (core->anal, addr, 0); /// XXX wrong in case of nopskip
|
|
if (fcn) {
|
|
RAnalRef *ref;
|
|
RListIter *iter;
|
|
RList *refs = r_anal_function_get_refs (fcn);
|
|
r_list_foreach (refs, iter, ref) {
|
|
if (ref->addr == UT64_MAX) {
|
|
R_LOG_DEBUG ("ignore 0x%08"PFMT64x" call 0x%08"PFMT64x, ref->at, ref->addr);
|
|
continue;
|
|
}
|
|
int rt = R_ANAL_REF_TYPE_MASK (ref->type);
|
|
if (rt != R_ANAL_REF_TYPE_CODE && rt != R_ANAL_REF_TYPE_CALL) {
|
|
/* only follow code/call references */
|
|
continue;
|
|
}
|
|
if (!r_io_is_valid_offset (core->io, ref->addr, !core->anal->opt.noncode)) {
|
|
continue;
|
|
}
|
|
r_core_anal_fcn (core, ref->addr, fcn->addr, R_ANAL_REF_TYPE_CALL, depth - 1);
|
|
/* use recursivity here */
|
|
#if 1
|
|
RAnalFunction *f = r_anal_get_function_at (core->anal, ref->addr);
|
|
if (f) {
|
|
RListIter *iter;
|
|
RAnalRef *ref;
|
|
RList *refs1 = r_anal_function_get_refs (f);
|
|
r_list_foreach (refs1, iter, ref) {
|
|
if (!r_io_is_valid_offset (core->io, ref->addr, !core->anal->opt.noncode)) {
|
|
continue;
|
|
}
|
|
int rt = R_ANAL_REF_TYPE_MASK (ref->type);
|
|
if (rt != R_ANAL_REF_TYPE_CALL && rt != R_ANAL_REF_TYPE_CODE) {
|
|
continue;
|
|
}
|
|
r_core_anal_fcn (core, ref->addr, f->addr, R_ANAL_REF_TYPE_CALL, depth - 1);
|
|
// recursively follow fcn->refs again and again
|
|
}
|
|
r_list_free (refs1);
|
|
} else {
|
|
f = r_anal_get_fcn_in (core->anal, fcn->addr, 0);
|
|
if (f) {
|
|
/* cut function */
|
|
r_anal_function_resize (f, addr - fcn->addr);
|
|
r_core_anal_fcn (core, ref->addr, fcn->addr, R_ANAL_REF_TYPE_CALL, depth - 1);
|
|
f = r_anal_get_function_at (core->anal, fcn->addr);
|
|
}
|
|
if (!f) {
|
|
R_LOG_ERROR ("af: Cannot find function at 0x%08" PFMT64x, fcn->addr);
|
|
}
|
|
}
|
|
#endif
|
|
}
|
|
r_list_free (refs);
|
|
if (core->anal->opt.vars) {
|
|
r_core_recover_vars (core, fcn, true);
|
|
}
|
|
}
|
|
}
|
|
if (name) {
|
|
if (*name && !__setFunctionName (core, addr, name, true)) {
|
|
R_LOG_ERROR ("af: Cannot find function at 0x%08" PFMT64x, addr);
|
|
}
|
|
}
|
|
#if SLOW_ANALYSIS
|
|
// r_core_anal_propagate_noreturn (core, addr);
|
|
#if 0
|
|
// XXX THIS IS VERY SLOW
|
|
if (core->anal->opt.vars) {
|
|
RListIter *iter;
|
|
RAnalFunction *fcni = NULL;
|
|
r_list_foreach (core->anal->fcns, iter, fcni) {
|
|
if (r_cons_is_breaked ()) {
|
|
break;
|
|
}
|
|
r_core_recover_vars (core, fcni, true);
|
|
}
|
|
}
|
|
#endif
|
|
// flag_every_function (core);
|
|
#endif
|
|
}
|
|
|
|
static void cmd_afci(RCore *core, RAnalFunction *fcn) {
|
|
const char *cc = (fcn && fcn->cc)? fcn->cc: "reg";
|
|
r_core_cmdf (core, "afcll~%s (", cc);
|
|
}
|
|
|
|
static int cmd_af(RCore *core, const char *input) {
|
|
r_cons_break_timeout (r_config_get_i (core->config, "anal.timeout"));
|
|
switch (input[1]) {
|
|
case '-': // "af-"
|
|
if (!input[2]) { // "af-"
|
|
cmd_af (core, "f-$$");
|
|
r_core_anal_undefine (core, core->offset);
|
|
} else if (!strcmp (input + 2, "*")) { // "af-*"
|
|
RAnalFunction *f;
|
|
RListIter *iter, *iter2;
|
|
r_list_foreach_safe (core->anal->fcns, iter, iter2, f) {
|
|
ut64 addr = f->addr;
|
|
r_anal_del_jmprefs (core->anal, f);
|
|
// r_anal_function_del_locs (core->anal, f->addr);
|
|
// r_anal_function_del (core->anal, addr);
|
|
r_core_anal_undefine (core, addr);
|
|
}
|
|
} else {
|
|
ut64 addr = input[2]
|
|
? r_num_math (core->num, input + 2)
|
|
: core->offset;
|
|
// r_anal_function_del_locs (core->anal, addr);
|
|
// r_anal_function_del (core->anal, addr);
|
|
r_core_anal_undefine (core, addr);
|
|
}
|
|
break;
|
|
case 'j': // "afj"
|
|
{
|
|
RList *blocks = r_anal_get_blocks_in (core->anal, core->offset);
|
|
RAnalBlock *block = r_list_first (blocks);
|
|
if (block && !r_list_empty (block->fcns)) {
|
|
char *args = strdup (input + 2);
|
|
RList *argv = r_str_split_list (args, " ", 0);
|
|
ut64 table = r_num_math (core->num, r_list_get_n (argv, 1));
|
|
ut64 sz = r_num_math (core->num, r_list_get_n (argv, 2));
|
|
ut64 elements = r_num_math (core->num, r_list_get_n (argv, 3));
|
|
ut64 seg = r_num_math (core->num, r_list_get_n (argv, 4));
|
|
int depth = 50;
|
|
try_walkthrough_jmptbl (core->anal, r_list_first (block->fcns), block,
|
|
depth, core->offset, 0, table, seg, sz, elements, 0, false);
|
|
free (args);
|
|
} else {
|
|
R_LOG_ERROR ("No function defined here");
|
|
}
|
|
r_list_free (blocks);
|
|
}
|
|
break;
|
|
case 'a': // "afa"
|
|
if (input[2] == 'l') { // "afal" : list function call arguments
|
|
int show_args = r_config_get_i (core->config, "dbg.funcarg");
|
|
if (show_args) {
|
|
r_core_print_func_args (core);
|
|
}
|
|
} else {
|
|
r_core_print_func_args (core);
|
|
}
|
|
break;
|
|
case 'd': // "afd"
|
|
{
|
|
ut64 addr = 0;
|
|
if (input[2] == '?') {
|
|
eprintf ("afd [offset]\n");
|
|
} else if (input[2] == ' ') {
|
|
addr = r_num_math (core->num, input + 2);
|
|
} else {
|
|
addr = core->offset;
|
|
}
|
|
RAnalFunction *fcn = r_anal_get_fcn_in (core->anal, addr, 0);
|
|
if (input[2] == 'j') { // afdj
|
|
PJ *pj = r_core_pj_new (core);
|
|
if (!pj) {
|
|
return false;
|
|
}
|
|
pj_o (pj);
|
|
if (fcn) {
|
|
pj_ks (pj, "name", fcn->name);
|
|
pj_ki (pj, "offset", (int)(addr - fcn->addr));
|
|
}
|
|
pj_end (pj);
|
|
r_cons_println (pj_string (pj));
|
|
pj_free (pj);
|
|
} else {
|
|
if (fcn) {
|
|
if (fcn->addr != addr) {
|
|
r_cons_printf ("%s + %d\n", fcn->name,
|
|
(int)(addr - fcn->addr));
|
|
} else {
|
|
r_cons_println (fcn->name);
|
|
}
|
|
} else {
|
|
eprintf ("afd: Cannot find function\n");
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
case 'u': // "afu"
|
|
if (input[2] == '?') {
|
|
r_core_cmd_help (core, help_msg_afu);
|
|
break;
|
|
}
|
|
|
|
if (input[2] != ' ') {
|
|
R_LOG_ERROR ("Missing argument");
|
|
return false;
|
|
}
|
|
|
|
ut64 addr = core->offset;
|
|
ut64 addr_end = r_num_math (core->num, input + 2);
|
|
if (addr_end < addr) {
|
|
R_LOG_ERROR ("Invalid address ranges");
|
|
} else {
|
|
ut64 a, b;
|
|
const char *c;
|
|
a = r_config_get_i (core->config, "anal.from");
|
|
b = r_config_get_i (core->config, "anal.to");
|
|
c = r_config_get (core->config, "anal.limits");
|
|
r_config_set_i (core->config, "anal.from", addr);
|
|
r_config_set_i (core->config, "anal.to", addr_end);
|
|
r_config_set (core->config, "anal.limits", "true");
|
|
|
|
RAnalFunction *fcn = r_anal_get_fcn_in (core->anal, addr, 0);
|
|
if (fcn) {
|
|
r_anal_function_resize (fcn, addr_end - addr);
|
|
}
|
|
r_core_anal_fcn (core, addr, UT64_MAX, R_ANAL_REF_TYPE_NULL, 1);
|
|
fcn = r_anal_get_fcn_in (core->anal, addr, 0);
|
|
if (fcn) {
|
|
r_anal_function_resize (fcn, addr_end - addr);
|
|
}
|
|
r_config_set_i (core->config, "anal.from", a);
|
|
r_config_set_i (core->config, "anal.to", b);
|
|
r_config_set (core->config, "anal.limits", r_str_get (c));
|
|
}
|
|
break;
|
|
case '+': { // "af+"
|
|
if (input[2] == '?' || !input[2]) {
|
|
r_core_cmd_help (core, help_msg_af_plus);
|
|
break;
|
|
}
|
|
char *ptr = input[2]? r_str_trim_dup (input + 2): r_str_newf ("0x%"PFMT64x, core->offset);
|
|
const char *ptr2;
|
|
int n = r_str_word_set0 (ptr);
|
|
const char *name = NULL;
|
|
char *hname = NULL; // heaped name
|
|
ut64 addr = UT64_MAX;
|
|
RAnalDiff *diff = NULL;
|
|
int type = R_ANAL_FCN_TYPE_FCN;
|
|
if (n > 0) {
|
|
switch (n) {
|
|
case 4:
|
|
ptr2 = r_str_word_get0 (ptr, 3);
|
|
if (!(diff = r_anal_diff_new ())) {
|
|
R_LOG_ERROR ("Cannot initialize RAnalDiff");
|
|
free (ptr);
|
|
return false;
|
|
}
|
|
if (ptr2[0] == 'm') {
|
|
diff->type = R_ANAL_DIFF_TYPE_MATCH;
|
|
} else if (ptr2[0] == 'u') {
|
|
diff->type = R_ANAL_DIFF_TYPE_UNMATCH;
|
|
}
|
|
/* fallthrough */
|
|
case 3:
|
|
ptr2 = r_str_word_get0 (ptr, 2);
|
|
if (strchr (ptr2, 'l')) {
|
|
type = R_ANAL_FCN_TYPE_LOC;
|
|
} else if (strchr (ptr2, 'i')) {
|
|
type = R_ANAL_FCN_TYPE_IMP;
|
|
} else if (strchr (ptr2, 's')) {
|
|
type = R_ANAL_FCN_TYPE_SYM;
|
|
} else {
|
|
type = R_ANAL_FCN_TYPE_FCN;
|
|
}
|
|
/* fallthrough */
|
|
case 2:
|
|
name = r_str_word_get0 (ptr, 1);
|
|
/* fallthrough */
|
|
case 1:
|
|
addr = r_num_math (core->num, r_str_word_get0 (ptr, 0));
|
|
if (!name) {
|
|
RFlagItem *fi = r_flag_get_at (core->flags, addr, false);
|
|
name = hname = (fi)
|
|
? strdup (fi->name)
|
|
: r_str_newf ("fcn.%08"PFMT64x, addr);
|
|
}
|
|
break;
|
|
}
|
|
RAnalFunction *fcn = r_anal_create_function (core->anal, name, addr, type, diff);
|
|
if (!fcn) {
|
|
R_LOG_ERROR ("Cannot add function '%s' (duplicated) at 0x%08"PFMT64x, name, addr);
|
|
}
|
|
free (hname);
|
|
}
|
|
r_anal_diff_free (diff);
|
|
free (ptr);
|
|
}
|
|
break;
|
|
case 'o': // "afo"
|
|
switch (input[2]) {
|
|
case '?':
|
|
eprintf ("Usage: afo[?sj] ([name|offset])\n");
|
|
break;
|
|
case 'j':
|
|
{
|
|
RAnalFunction *fcn = r_anal_get_fcn_in (core->anal, core->offset, R_ANAL_FCN_TYPE_NULL);
|
|
PJ *pj = r_core_pj_new (core);
|
|
if (!pj) {
|
|
return false;
|
|
}
|
|
pj_o (pj);
|
|
if (fcn) {
|
|
pj_ki (pj, "address", fcn->addr);
|
|
}
|
|
pj_end (pj);
|
|
r_cons_println (pj_string (pj));
|
|
pj_free (pj);
|
|
}
|
|
break;
|
|
case '\0':
|
|
{
|
|
RAnalFunction *fcn = r_anal_get_fcn_in (core->anal, core->offset, R_ANAL_FCN_TYPE_NULL);
|
|
if (fcn) {
|
|
r_cons_printf ("0x%08" PFMT64x "\n", fcn->addr);
|
|
}
|
|
}
|
|
break;
|
|
case 's': // "afos"
|
|
{
|
|
ut64 addr = core->offset;
|
|
RListIter *iter;
|
|
RList *list = r_anal_get_functions_in (core->anal, addr);
|
|
RAnalFunction *fcn;
|
|
r_list_foreach (list, iter, fcn) {
|
|
r_cons_printf ("= 0x%08" PFMT64x "\n", fcn->addr);
|
|
}
|
|
r_list_free (list);
|
|
}
|
|
break;
|
|
case ' ':
|
|
{
|
|
RAnalFunction *fcn;
|
|
ut64 addr = r_num_math (core->num, input + 3);
|
|
if (addr == 0LL) {
|
|
fcn = r_anal_get_function_byname (core->anal, input + 3);
|
|
} else {
|
|
fcn = r_anal_get_fcn_in (core->anal, addr, R_ANAL_FCN_TYPE_NULL);
|
|
}
|
|
if (fcn) {
|
|
r_cons_printf ("0x%08" PFMT64x "\n", fcn->addr);
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
break;
|
|
case 'i': // "afi"
|
|
switch (input[2]) {
|
|
case '?':
|
|
r_core_cmd_help (core, help_msg_afi);
|
|
break;
|
|
case '.': // "afi."
|
|
{
|
|
ut64 addr = core->offset;
|
|
if (input[3] == ' ') {
|
|
addr = r_num_math (core->num, input + 3);
|
|
}
|
|
RAnalFunction *fcn = r_anal_get_fcn_in (core->anal, addr, R_ANAL_FCN_TYPE_NULL);
|
|
if (fcn) {
|
|
r_cons_printf ("%s\n", fcn->name);
|
|
}
|
|
}
|
|
break;
|
|
case 'l': // "afil"
|
|
if (input[3] == '?') {
|
|
// TODO #7967 help refactor
|
|
help_msg_afll[1] = "afil";
|
|
r_core_cmd_help (core, help_msg_afll);
|
|
break;
|
|
}
|
|
/* fallthrough */
|
|
case 'i': // "afii"
|
|
if (input[3] == '-') {
|
|
RAnalFunction *fcn = r_anal_get_fcn_in (core->anal, core->offset, R_ANAL_FCN_TYPE_NULL);
|
|
if (fcn) {
|
|
r_list_free (fcn->imports);
|
|
fcn->imports = NULL;
|
|
}
|
|
} else if (input[3] == ' ') {
|
|
RAnalFunction *fcn = r_anal_get_fcn_in (core->anal, core->offset, R_ANAL_FCN_TYPE_NULL);
|
|
if (fcn) {
|
|
if (!fcn->imports) {
|
|
fcn->imports = r_list_newf ((RListFree)free);
|
|
}
|
|
r_list_append (fcn->imports, r_str_trim_dup (input + 4));
|
|
} else {
|
|
R_LOG_ERROR ("No function found");
|
|
}
|
|
} else {
|
|
RAnalFunction *fcn = r_anal_get_fcn_in (core->anal, core->offset, R_ANAL_FCN_TYPE_NULL);
|
|
if (fcn && fcn->imports) {
|
|
char *imp;
|
|
RListIter *iter;
|
|
r_list_foreach (fcn->imports, iter, imp) {
|
|
r_cons_printf ("%s\n", imp);
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
case 's': // "afis"
|
|
if (input[3] == 'a') { // "afisa"
|
|
__core_cmd_anal_fcn_allstats (core, input + 4);
|
|
} else {
|
|
sdb_free (__core_cmd_anal_fcn_stats (core, input + 3));
|
|
}
|
|
break;
|
|
case 'j': // "afij"
|
|
case '*': // "afi*"
|
|
r_core_anal_fcn_list (core, input + 3, input + 2);
|
|
break;
|
|
case 'p': // "afip"
|
|
{
|
|
RAnalFunction *fcn = r_anal_get_fcn_in (core->anal, core->offset, R_ANAL_FCN_TYPE_NULL);
|
|
if (fcn) {
|
|
r_cons_printf ("is-pure: %s\n", r_str_bool (r_anal_function_purity (fcn)));
|
|
}
|
|
}
|
|
break;
|
|
case '=':
|
|
case 'q':
|
|
{
|
|
RAnalFunction *fcn = r_anal_get_fcn_in (core->anal, core->offset, R_ANAL_FCN_TYPE_NULL);
|
|
if (fcn) {
|
|
// TODO: add info about xrefs and call counts
|
|
int nargs = r_type_func_args_count (core->anal->sdb_types, 0);
|
|
int nvars = r_anal_var_count_locals (fcn);
|
|
int nins = r_anal_function_instrcount (fcn);
|
|
int ebbs = 0;
|
|
int edges = r_anal_function_count_edges (fcn, &ebbs);
|
|
r_anal_function_count_edges (fcn, NULL);
|
|
r_cons_printf ("0x%08" PFMT64x " : %s\n", fcn->addr, fcn->name);
|
|
char *sig = r_core_cmd_strf (core, "afcf @ 0x%"PFMT64x, fcn->addr);
|
|
if (sig) {
|
|
r_str_trim (sig);
|
|
r_cons_printf (" sign: %s\n", sig);
|
|
free (sig);
|
|
}
|
|
r_cons_printf (" stack: 0x%08x (vars:%d args:%d)\n",
|
|
fcn->maxstack, nvars , nargs);
|
|
r_cons_printf (" size: %d (0x%08" PFMT64x " .. 0x%08" PFMT64x ")\n",
|
|
(int)r_anal_function_realsize (fcn),
|
|
r_anal_function_min_addr (fcn),
|
|
r_anal_function_max_addr (fcn));
|
|
r_cons_printf (" nbbs: %d edges:%d ebbs:%d ninstr:%d\n",
|
|
r_list_length (fcn->bbs), edges, ebbs, nins);
|
|
r_cons_printf (" cost: %d complexity:%d\n",
|
|
r_anal_function_cost (fcn), r_anal_function_complexity (fcn));
|
|
r_cons_printf (" attr: ");
|
|
if (r_anal_function_islineal (fcn)) {
|
|
r_cons_printf ("lineal");
|
|
}
|
|
if (fcn->is_noreturn) {
|
|
r_cons_printf ("noreturn");
|
|
}
|
|
r_cons_newline ();
|
|
}
|
|
}
|
|
break;
|
|
default:
|
|
{
|
|
const char *arg = input[2]? input + 2: "";
|
|
const char *sec = "\x01";
|
|
r_core_anal_fcn_list (core, arg, sec);
|
|
}
|
|
break;
|
|
}
|
|
break;
|
|
case 'l': // "afl"
|
|
switch (input[2]) {
|
|
case '?':
|
|
r_core_cmd_help (core, help_msg_afl);
|
|
break;
|
|
case 'x': // "aflx"
|
|
{
|
|
ut64 addr = faddr (core, core->offset, NULL);
|
|
RList *xrefs = r_anal_xrefs_get (core->anal, addr);
|
|
Sdb *db = sdb_new0 ();
|
|
// sort by function and uniq to avoid dupped results
|
|
RListIter *iter;
|
|
RAnalRef *ref;
|
|
r_list_foreach (xrefs, iter, ref) {
|
|
bool nr = false;
|
|
ut64 fa = faddr (core, ref->addr, &nr);
|
|
char *key = r_str_newf ("0x%08"PFMT64x, fa);
|
|
sdb_array_add_num (db, key, ref->addr, 0);
|
|
}
|
|
SdbList *keys = sdb_foreach_list (db, true);
|
|
SdbListIter *liter;
|
|
SdbKv *kv;
|
|
bool rad = input[3] == '*';
|
|
ls_foreach (keys, liter, kv) {
|
|
if (rad) {
|
|
r_cons_printf ("s %s;af-;af;s-\n", (const char *)kv->base.key);
|
|
} else {
|
|
r_cons_printf ("%s %s\n", (const char *)kv->base.key, (const char *)kv->base.value);
|
|
}
|
|
}
|
|
sdb_free (db);
|
|
ls_free (keys);
|
|
}
|
|
break;
|
|
case 's': // "afls"
|
|
switch (input[3]) {
|
|
default:
|
|
case '?':
|
|
r_core_cmd_help (core, help_msg_afls);
|
|
return true;
|
|
case 0: // default for "afls"
|
|
case 'a': // "aflsa"
|
|
core->anal->fcns->sorted = false;
|
|
r_list_sort (core->anal->fcns, cmpaddr);
|
|
break;
|
|
case 'b': // "aflsb"
|
|
core->anal->fcns->sorted = false;
|
|
r_list_sort (core->anal->fcns, cmpbbs);
|
|
break;
|
|
case 's': // "aflss"
|
|
core->anal->fcns->sorted = false;
|
|
r_list_sort (core->anal->fcns, cmpsize);
|
|
break;
|
|
case 'n': // "aflsn"
|
|
core->anal->fcns->sorted = false;
|
|
r_list_sort (core->anal->fcns, cmpname);
|
|
break;
|
|
}
|
|
break;
|
|
case 'l': // "afll"
|
|
if (input[3] == '?') {
|
|
// TODO #7967 help refactor
|
|
help_msg_afll[1] = "afll";
|
|
r_core_cmd_help (core, help_msg_afll);
|
|
break;
|
|
}
|
|
/* fallthrough */
|
|
case ',': // "afl,"
|
|
case 't': // "aflt"
|
|
case 'j': // "aflj"
|
|
case 'q': // "aflq"
|
|
case 'm': // "aflm"
|
|
case '+': // "afl+"
|
|
case '=': // "afl="
|
|
case '*': // "afl*"
|
|
case '.': // "afl*"
|
|
r_core_anal_fcn_list (core, NULL, input + 2);
|
|
break;
|
|
case 'c': // "aflc"
|
|
r_cons_printf ("%d\n", r_list_length (core->anal->fcns));
|
|
break;
|
|
case ' ': // "afl [addr]" argument ignored
|
|
case 0: // "afl"
|
|
r_core_anal_fcn_list (core, NULL, "o");
|
|
break;
|
|
default: // "afl "
|
|
r_core_cmd_help (core, help_msg_afl);
|
|
break;
|
|
}
|
|
break;
|
|
case 's': // "afs"
|
|
switch (input[2]) {
|
|
case '!': { // "afs!"
|
|
char *sig = r_core_cmd_str (core, "afs");
|
|
char *data = r_core_editor (core, NULL, sig);
|
|
if (sig && data) {
|
|
r_core_cmdf (core, "\"afs %s\"", data);
|
|
}
|
|
free (sig);
|
|
free (data);
|
|
break;
|
|
}
|
|
case 'r': { // "afsr"
|
|
RAnalFunction *fcn = r_anal_get_fcn_in (core->anal, core->offset, -1);
|
|
if (fcn) {
|
|
char *type = r_str_newf ("type.%s", input + 4);
|
|
if (sdb_exists (core->anal->sdb_types, type)) {
|
|
char *query = r_str_newf ("anal/types/func.%s.ret=%s", fcn->name, input + 4);
|
|
sdb_querys (core->sdb, NULL, 0, query);
|
|
free (query);
|
|
}
|
|
free (type);
|
|
} else {
|
|
R_LOG_ERROR ("There's no function defined here");
|
|
}
|
|
break;
|
|
}
|
|
case '*': // "afs*"
|
|
cmd_afsr (core, input + 2);
|
|
break;
|
|
case 'j': // "afsj"
|
|
cmd_afsj (core, input + 2);
|
|
break;
|
|
case 0:
|
|
case ' ': { // "afs"
|
|
ut64 addr = core->offset;
|
|
RAnalFunction *f;
|
|
const char *arg = r_str_trim_head_ro (input + 2);
|
|
if ((f = r_anal_get_fcn_in (core->anal, addr, R_ANAL_FCN_TYPE_NULL))) {
|
|
if (R_STR_ISNOTEMPTY (arg)) {
|
|
// parse function signature here
|
|
char *fcnstr = r_str_newf ("%s;", arg), *fcnstr_copy = strdup (fcnstr);
|
|
char *fcnname_aux = strtok (fcnstr_copy, "(");
|
|
r_str_trim_tail (fcnname_aux);
|
|
const char *ls = r_str_lchr (fcnname_aux, ' ');
|
|
char *fcnname = strdup (ls? ls: fcnname_aux);
|
|
if (fcnname) {
|
|
// TODO: move this into r_anal_str_to_fcn()
|
|
if (strcmp (f->name, fcnname)) {
|
|
(void)__setFunctionName (core, addr, fcnname, false);
|
|
f = r_anal_get_fcn_in (core->anal, addr, -1);
|
|
}
|
|
r_anal_str_to_fcn (core->anal, f, fcnstr);
|
|
}
|
|
free (fcnname);
|
|
free (fcnstr_copy);
|
|
free (fcnstr);
|
|
} else {
|
|
char *str = r_anal_function_get_signature (f);
|
|
if (str) {
|
|
r_cons_println (str);
|
|
free (str);
|
|
}
|
|
}
|
|
} else {
|
|
R_LOG_ERROR ("No function defined at 0x%08" PFMT64x, addr);
|
|
}
|
|
break;
|
|
}
|
|
default:
|
|
// case '?': // "afs?"
|
|
r_core_cmd_help (core, help_msg_afs);
|
|
break;
|
|
}
|
|
break;
|
|
case 'm': // "afm" - merge two functions
|
|
if (input[2] == '?') {
|
|
r_core_cmd_help (core, help_msg_afm);
|
|
break;
|
|
}
|
|
r_core_anal_fcn_merge (core, core->offset, r_num_math (core->num, input + 2));
|
|
break;
|
|
case 'M': // "afM" - print functions map
|
|
r_core_anal_fmap (core, input + 1);
|
|
break;
|
|
case 'v': // "afv"
|
|
var_cmd (core, input + 2);
|
|
break;
|
|
case 't': // "aft"
|
|
type_cmd (core, input + 2);
|
|
break;
|
|
case 'C': // "afC"
|
|
if (input[2] == 'c') {
|
|
RAnalFunction *fcn;
|
|
if ((fcn = r_anal_get_fcn_in (core->anal, core->offset, 0))) {
|
|
r_cons_printf ("%i\n", r_anal_function_complexity (fcn));
|
|
} else {
|
|
R_LOG_ERROR ("Cannot find function at 0x08%" PFMT64x, core->offset);
|
|
}
|
|
} else if (input[2] == 'l') {
|
|
RAnalFunction *fcn;
|
|
if ((fcn = r_anal_get_fcn_in (core->anal, core->offset, 0))) {
|
|
r_cons_printf ("%d\n", r_anal_function_loops (fcn));
|
|
} else {
|
|
R_LOG_ERROR ("Cannot find function at 0x08%" PFMT64x, core->offset);
|
|
}
|
|
} else if (input[2] == '?') {
|
|
r_core_cmd_help (core, help_msg_afC);
|
|
} else {
|
|
afCc (core, r_str_trim_head_ro (input + 2));
|
|
}
|
|
break;
|
|
case 'c': { // "afc"
|
|
RAnalFunction *fcn = NULL;
|
|
if (!input[2] || input[2] == ' ' || input[2] == 'i' || input[2] == 'r' || input[2] == 'a') {
|
|
fcn = r_anal_get_fcn_in (core->anal, core->offset, 0);
|
|
if (!fcn) {
|
|
R_LOG_ERROR ("afc: Cannot find function here");
|
|
break;
|
|
}
|
|
}
|
|
switch (input[2]) {
|
|
case '\0': // "afc"
|
|
r_cons_println (fcn->cc);
|
|
break;
|
|
case ' ': { // "afc "
|
|
char *cc = r_str_trim_dup (input + 3);
|
|
if (!r_anal_cc_exist (core->anal, cc)) {
|
|
const char *asmOs = r_config_get (core->config, "asm.os");
|
|
R_LOG_ERROR ("afc: Unknown calling convention '%s' for '%s'. See afcl for available types", cc, asmOs);
|
|
} else {
|
|
fcn->cc = r_str_constpool_get (&core->anal->constpool, cc);
|
|
}
|
|
free (cc);
|
|
}
|
|
break;
|
|
case 'i':
|
|
cmd_afci (core, fcn);
|
|
break;
|
|
case 'f': // "afcf" "afcfj"
|
|
cmd_anal_fcn_sig (core, input + 3);
|
|
break;
|
|
case 'k': // "afck"
|
|
cmd_afck (core, NULL);
|
|
break;
|
|
case 'l': // "afcl" list all function Calling conventions.
|
|
if (input[3] == '?') {
|
|
r_core_cmd_help (core, help_msg_afc);
|
|
} else {
|
|
cmd_tcc (core, input + 3);
|
|
}
|
|
break;
|
|
case 'o': { // "afco"
|
|
char *dbpath = r_str_trim_dup (input + 3);
|
|
if (R_STR_ISNOTEMPTY (dbpath) && r_file_exists (dbpath)) {
|
|
Sdb *db = sdb_new (0, dbpath, 0);
|
|
if (db) {
|
|
sdb_merge (core->anal->sdb_cc, db);
|
|
sdb_close (db);
|
|
sdb_free (db);
|
|
}
|
|
} else {
|
|
eprintf ("Usage: afco [dbpath] - open calling conventions defined in local file.\n");
|
|
}
|
|
free (dbpath);
|
|
break;
|
|
}
|
|
case 'r': { // "afcr"
|
|
int i;
|
|
PJ *pj = NULL;
|
|
bool json = input[3] == 'j';
|
|
if (json) {
|
|
pj = r_core_pj_new (core);
|
|
if (!pj) {
|
|
return false;
|
|
}
|
|
pj_o (pj);
|
|
}
|
|
char *cmd = r_str_newf ("cc.%s.ret", fcn->cc);
|
|
const char *regname = sdb_const_get (core->anal->sdb_cc, cmd, 0);
|
|
if (regname) {
|
|
if (json) {
|
|
pj_ks (pj, "ret", regname);
|
|
} else {
|
|
r_cons_printf ("%s: %s\n", cmd, regname);
|
|
}
|
|
}
|
|
free (cmd);
|
|
if (json) {
|
|
pj_ka (pj, "args");
|
|
}
|
|
for (i = 0; i < R_ANAL_CC_MAXARG; i++) {
|
|
cmd = r_str_newf ("cc.%s.arg%d", fcn->cc, i);
|
|
regname = sdb_const_get (core->anal->sdb_cc, cmd, 0);
|
|
if (regname) {
|
|
if (json) {
|
|
pj_s (pj, regname);
|
|
} else {
|
|
r_cons_printf ("%s: %s\n", cmd, regname);
|
|
}
|
|
}
|
|
free (cmd);
|
|
}
|
|
if (json) {
|
|
pj_end (pj);
|
|
}
|
|
|
|
cmd = r_str_newf ("cc.%s.self", fcn->cc);
|
|
regname = sdb_const_get (core->anal->sdb_cc, cmd, 0);
|
|
if (regname) {
|
|
if (json) {
|
|
pj_ks (pj, "self", regname);
|
|
} else {
|
|
r_cons_printf ("%s: %s\n", cmd, regname);
|
|
}
|
|
}
|
|
free (cmd);
|
|
cmd = r_str_newf ("cc.%s.error", fcn->cc);
|
|
regname = sdb_const_get (core->anal->sdb_cc, cmd, 0);
|
|
if (regname) {
|
|
if (json) {
|
|
pj_ks (pj, "error", regname);
|
|
} else {
|
|
r_cons_printf ("%s: %s\n", cmd, regname);
|
|
}
|
|
}
|
|
free (cmd);
|
|
if (json) {
|
|
pj_end (pj);
|
|
r_cons_println (pj_string (pj));
|
|
pj_free (pj);
|
|
}
|
|
break;
|
|
}
|
|
case 'R': { // "afcR"
|
|
/* very slow, but im tired of waiting for having this, so this is the quickest implementation */
|
|
int i;
|
|
char *cc = r_core_cmd_str (core, "k anal/cc/default.cc");
|
|
r_str_trim (cc);
|
|
for (i = 0; i < 6; i++) {
|
|
char *res = r_core_cmd_strf (core, "k anal/cc/cc.%s.arg%d", cc, i);
|
|
r_str_trim_nc (res);
|
|
if (*res) {
|
|
char *row = r_core_cmd_strf (core, "drr~%s 0x", res);
|
|
r_str_trim (row);
|
|
r_cons_printf ("arg[%d] %s\n", i, row);
|
|
free (row);
|
|
}
|
|
free (res);
|
|
}
|
|
free (cc);
|
|
break;
|
|
}
|
|
case '?': // "afc?"
|
|
default:
|
|
r_core_cmd_help (core, help_msg_afc);
|
|
}
|
|
break;
|
|
}
|
|
case 'B': // "afB" // set function bits
|
|
if (input[2] == ' ') {
|
|
RAnalFunction *fcn = r_anal_get_fcn_in (core->anal, core->offset, 0);
|
|
if (fcn) { // bits = 0 means unset
|
|
int nbits = atoi (input + 3);
|
|
int obits = core->anal->config->bits;
|
|
if (nbits > 0) {
|
|
r_anal_hint_set_bits (core->anal, r_anal_function_min_addr (fcn), nbits);
|
|
r_anal_hint_set_bits (core->anal, r_anal_function_max_addr (fcn), obits);
|
|
fcn->bits = nbits;
|
|
} else {
|
|
r_anal_hint_unset_bits (core->anal, r_anal_function_min_addr (fcn));
|
|
fcn->bits = 0;
|
|
}
|
|
} else {
|
|
R_LOG_ERROR ("afB: Cannot find function to set bits at 0x%08"PFMT64x, core->offset);
|
|
}
|
|
} else {
|
|
eprintf ("Usage: afB [bits] # bits can be: 0, 8, 16, 32 or 64. when using 0, disables the hint\n");
|
|
}
|
|
break;
|
|
case 'b': // "afb"
|
|
switch (input[2]) {
|
|
case '-': // "afb-"
|
|
anal_fcn_del_bb (core, r_str_trim_head_ro (input + 3));
|
|
break;
|
|
case 'e': // "afbe"
|
|
anal_bb_edge (core, r_str_trim_head_ro (input + 3));
|
|
break;
|
|
case 'F': { // "afbF"
|
|
RAnalFunction *fcn = r_anal_get_fcn_in (core->anal, core->offset, R_ANAL_FCN_TYPE_NULL);
|
|
if (fcn) {
|
|
RAnalBlock *bb = r_anal_function_bbget_in (core->anal, fcn, core->offset);
|
|
if (bb) {
|
|
if (input[3]) {
|
|
int n = atoi (input + 3);
|
|
bb->folded = n;
|
|
} else {
|
|
bb->folded = !bb->folded;
|
|
}
|
|
} else {
|
|
r_warn_if_reached ();
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
case 0:
|
|
case ' ': // "afb "
|
|
case 'q': // "afbq"
|
|
case 'r': // "afbr"
|
|
case '=': // "afb="
|
|
case '*': // "afb*"
|
|
case 'j': // "afbj"
|
|
case 't': // "afbt"
|
|
anal_fcn_list_bb (core, input + 2, false);
|
|
break;
|
|
case 'i': // "afbi"
|
|
anal_fcn_list_bb (core, input + 2, true);
|
|
break;
|
|
case '.': // "afb."
|
|
anal_fcn_list_bb (core, input[2]? " $$": input + 2, true);
|
|
break;
|
|
case '+': // "afb+"
|
|
cmd_afbplus (core, input + 3);
|
|
break;
|
|
case 'c': // "afbc"
|
|
cmd_afbc (core, r_str_trim_head_ro (input + 3));
|
|
break;
|
|
default:
|
|
r_core_cmd_help (core, help_msg_afb);
|
|
break;
|
|
}
|
|
break;
|
|
case 'n': // "afn"
|
|
switch (input[2]) {
|
|
case 's': // "afns"
|
|
if (input[3] == 'j') { // "afnsj"
|
|
free (r_core_anal_fcn_autoname (core, core->offset, 1, input[3]));
|
|
} else {
|
|
free (r_core_anal_fcn_autoname (core, core->offset, 1, 0));
|
|
}
|
|
break;
|
|
case 'a': // "afna"
|
|
if (input[3] == '?') {
|
|
r_core_cmd_help (core, help_msg_afna);
|
|
break;
|
|
}
|
|
char *name = r_core_anal_fcn_autoname (core, core->offset, 0, 0);
|
|
if (name) {
|
|
r_cons_printf ("afn %s 0x%08" PFMT64x "\n", name, core->offset);
|
|
free (name);
|
|
}
|
|
break;
|
|
case '.': // "afn."
|
|
case 0: { // "afn"
|
|
RAnalFunction *fcn = r_anal_get_fcn_in (core->anal, core->offset, -1);
|
|
if (fcn) {
|
|
r_cons_printf ("%s\n", fcn->name);
|
|
}
|
|
}
|
|
break;
|
|
case ' ': { // "afn "
|
|
ut64 off = core->offset;
|
|
char *p, *name = strdup (r_str_trim_head_ro (input + 3));
|
|
if ((p = strchr (name, ' '))) {
|
|
*p++ = 0;
|
|
off = r_num_math (core->num, p);
|
|
}
|
|
if (*name == '?') {
|
|
eprintf ("Usage: afn newname [off] # set new name to given function\n");
|
|
} else {
|
|
if (r_str_startswith (name, "base64:")) {
|
|
char *res = (char *)r_base64_decode_dyn (name + 7, -1);
|
|
if (res) {
|
|
free (name);
|
|
name = res;
|
|
}
|
|
}
|
|
if (!*name || !__setFunctionName (core, off, name, false)) {
|
|
R_LOG_ERROR ("Cannot find function at 0x%08" PFMT64x, off);
|
|
}
|
|
}
|
|
free (name);
|
|
}
|
|
break;
|
|
default:
|
|
r_core_cmd_help (core, help_msg_afn);
|
|
break;
|
|
} // end of switch (input[2])
|
|
break;
|
|
case 'S': { // afS"
|
|
RAnalFunction *fcn = r_anal_get_fcn_in (core->anal, core->offset, -1);
|
|
if (fcn) {
|
|
fcn->maxstack = r_num_math (core->num, input + 3);
|
|
//fcn->stack = fcn->maxstack;
|
|
}
|
|
break;
|
|
}
|
|
#if 0
|
|
/* this is undocumented and probably have no uses. plz discuss */
|
|
case 'e': // "afe"
|
|
{
|
|
RAnalFunction *fcn;
|
|
ut64 off = core->offset;
|
|
char *p, *name = strdup ((input[2]&&input[3])? input + 3: "");
|
|
if ((p = strchr (name, ' '))) {
|
|
*p = 0;
|
|
off = r_num_math (core->num, p + 1);
|
|
}
|
|
fcn = r_anal_get_fcn_in (core->anal, off, R_ANAL_FCN_TYPE_FCN | R_ANAL_FCN_TYPE_SYM);
|
|
if (fcn) {
|
|
RAnalBlock *b;
|
|
RListIter *iter;
|
|
RAnalRef *r;
|
|
r_list_foreach (fcn->refs, iter, r) {
|
|
r_cons_printf ("0x%08" PFMT64x " -%c 0x%08" PFMT64x "\n", r->at, r->type, r->addr);
|
|
}
|
|
r_list_foreach (fcn->bbs, iter, b) {
|
|
int ok = 0;
|
|
if (b->type == R_ANAL_BB_TYPE_LAST) ok = 1;
|
|
if (b->type == R_ANAL_BB_TYPE_FOOT) ok = 1;
|
|
if (b->jump == UT64_MAX && b->fail == UT64_MAX) ok = 1;
|
|
if (ok) {
|
|
r_cons_printf ("0x%08" PFMT64x " -r\n", b->addr);
|
|
// TODO: check if destination is outside the function boundaries
|
|
}
|
|
}
|
|
} else eprintf ("Cannot find function at 0x%08" PFMT64x "\n", core->offset);
|
|
free (name);
|
|
}
|
|
break;
|
|
#endif
|
|
case 'x': // "afx"
|
|
switch (input[2]) {
|
|
case 'm': // "afxm"
|
|
xrefs_map (core, input + 1);
|
|
break;
|
|
case '\0': // "afx"
|
|
case 'j': // "afxj"
|
|
case ' ': // "afx "
|
|
{
|
|
PJ *pj = r_core_pj_new (core);
|
|
if (input[2] == 'j') {
|
|
pj_a (pj);
|
|
}
|
|
if (!pj) {
|
|
return false;
|
|
}
|
|
// list xrefs from current address
|
|
{
|
|
ut64 addr = input[2]==' '? r_num_math (core->num, input + 2): core->offset;
|
|
RAnalFunction *fcn = r_anal_get_fcn_in (core->anal, addr, R_ANAL_FCN_TYPE_NULL);
|
|
if (fcn) {
|
|
ut64 oaddr = core->offset;
|
|
RAnalRef *ref;
|
|
RListIter *iter;
|
|
RList *refs = r_anal_function_get_refs (fcn);
|
|
r_list_foreach (refs, iter, ref) {
|
|
if (input[2] == 'j') {
|
|
pj_o (pj);
|
|
pj_ks (pj, "type", r_anal_ref_type_tostring (ref->type));
|
|
pj_kn (pj, "from", ref->at);
|
|
pj_kn (pj, "to", ref->addr);
|
|
pj_end (pj);
|
|
} else {
|
|
r_cons_printf ("%c 0x%08" PFMT64x " -> ", ref->type, ref->at);
|
|
switch (R_ANAL_REF_TYPE_MASK (ref->type)) {
|
|
case R_ANAL_REF_TYPE_NULL:
|
|
r_cons_printf ("0x%08" PFMT64x " ", ref->addr);
|
|
break;
|
|
case R_ANAL_REF_TYPE_CODE:
|
|
case R_ANAL_REF_TYPE_JUMP:
|
|
case R_ANAL_REF_TYPE_CALL:
|
|
case R_ANAL_REF_TYPE_DATA:
|
|
r_cons_printf ("0x%08" PFMT64x " ", ref->addr);
|
|
r_core_seek (core, ref->at, 1);
|
|
r_core_print_disasm_instructions (core, 0, 1);
|
|
break;
|
|
case R_ANAL_REF_TYPE_STRING:
|
|
{
|
|
char *s = r_core_cmd_strf (core, "pxr 8 @ 0x%08"PFMT64x, ref->addr);
|
|
char *nl = strchr (s, '\n');
|
|
if (nl) {
|
|
*nl = 0;
|
|
}
|
|
r_cons_printf ("%s\n", s);
|
|
free (s);
|
|
}
|
|
break;
|
|
default:
|
|
// ignore rwx
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
r_list_free (refs);
|
|
r_core_seek (core, oaddr, 1);
|
|
} else {
|
|
R_LOG_ERROR ("afx: Cannot find function at 0x%08"PFMT64x, addr);
|
|
}
|
|
}
|
|
if (input[2] == 'j') {
|
|
pj_end (pj);
|
|
r_cons_println (pj_string (pj));
|
|
}
|
|
pj_free (pj);
|
|
break;
|
|
}
|
|
default:
|
|
R_LOG_ERROR ("Invalid command. Look at af?");
|
|
break;
|
|
}
|
|
break;
|
|
case 'F': // "afF"
|
|
{
|
|
int val = input[2] && r_num_math (core->num, input + 2);
|
|
RAnalFunction *fcn = r_anal_get_fcn_in (core->anal, core->offset, R_ANAL_FCN_TYPE_NULL);
|
|
if (fcn) {
|
|
fcn->folded = input[2]? val: !fcn->folded;
|
|
}
|
|
}
|
|
break;
|
|
case '?': // "af?"
|
|
r_core_cmd_help (core, help_msg_af);
|
|
break;
|
|
case 'r': // "afr" // analyze function recursively
|
|
case ' ': // "af "
|
|
case '\0': // "af"
|
|
{
|
|
bool anal_calls = false;
|
|
if (input[0] && input[1] == 'r') {
|
|
input++;
|
|
anal_calls = true;
|
|
} else {
|
|
anal_calls = r_config_get_b (core->config, "anal.calls");
|
|
}
|
|
ut64 addr = core->offset;
|
|
const char *name = NULL;
|
|
// first undefine
|
|
if (input[0] && input[1] == ' ') {
|
|
name = r_str_trim_head_ro (input + 2);
|
|
char *uaddr = strchr (name, ' ');
|
|
if (uaddr) {
|
|
*uaddr++ = 0;
|
|
addr = r_num_math (core->num, uaddr);
|
|
}
|
|
// depth = 1; // or 1?
|
|
// disable hasnext
|
|
}
|
|
r_core_af (core, addr, name, anal_calls);
|
|
}
|
|
break;
|
|
default:
|
|
return false;
|
|
break;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
R_API void r_core_anal_undefine(RCore *core, ut64 off) {
|
|
// very slow
|
|
// RAnalFunction *f = r_anal_get_fcn_in (core->anal, off, -1);
|
|
RAnalFunction *f = r_anal_get_function_at (core->anal, off);
|
|
if (f) {
|
|
if (!strncmp (f->name, "fcn.", 4)) {
|
|
r_flag_unset_name (core->flags, f->name);
|
|
}
|
|
r_meta_del (core->anal, R_META_TYPE_ANY, r_anal_function_min_addr (f), r_anal_function_linear_size (f));
|
|
r_anal_function_del (core->anal, off);
|
|
}
|
|
//r_anal_function_del_locs (core->anal, off);
|
|
r_anal_delete_block_at (core->anal, off);
|
|
char *abcmd = r_str_newf ("ab-0x%"PFMT64x, off);
|
|
if (abcmd) {
|
|
cmd_af (core, abcmd);
|
|
free (abcmd);
|
|
}
|
|
}
|
|
|
|
// size: 0: bits; -1: any; >0: exact size
|
|
static void __anal_reg_list(RCore *core, int type, int bits, char mode) {
|
|
PJ *pj = NULL;
|
|
if (mode == 'i') {
|
|
r_core_debug_ri (core, core->anal->reg, 0);
|
|
return;
|
|
} else if (mode == 'j') {
|
|
pj = r_core_pj_new (core);
|
|
if (!pj) {
|
|
return;
|
|
}
|
|
}
|
|
RReg *hack = core->dbg->reg;
|
|
const char *use_color;
|
|
int use_colors = r_config_get_i (core->config, "scr.color");
|
|
if (use_colors) {
|
|
#undef ConsP
|
|
#define ConsP(x) (core->cons && core->cons->context->pal.x)? core->cons->context->pal.x
|
|
use_color = ConsP (creg) : Color_BWHITE;
|
|
} else {
|
|
use_color = NULL;
|
|
}
|
|
if (bits < 0) {
|
|
// TODO Change the `size` argument of r_debug_reg_list to use -1 for any and 0 for anal->config->bits
|
|
bits = 0;
|
|
} else if (!bits) {
|
|
bits = core->anal->config->bits;
|
|
}
|
|
int mode2 = mode;
|
|
if (core->anal) {
|
|
core->dbg->reg = core->anal->reg;
|
|
if (core->anal->cur && core->anal->cur->arch) {
|
|
/* workaround for thumb */
|
|
if (!strcmp (core->anal->cur->arch, "arm") && bits == 16) {
|
|
bits = 32;
|
|
}
|
|
int defsz = r_reg_default_bits (core->anal->reg);
|
|
if (defsz) {
|
|
bits = defsz;
|
|
}
|
|
/* workaround for 6502 and avr*/
|
|
if ((!strcmp (core->anal->cur->arch, "6502") && bits == 8)
|
|
|| (!strcmp (core->anal->cur->arch, "avr") && bits == 8)) {
|
|
if (mode == 'j') {
|
|
mode2 = 'J';
|
|
pj_o (pj);
|
|
}
|
|
r_debug_reg_list (core->dbg, R_REG_TYPE_GPR, 16, pj, mode2, use_color); // XXX detect which one is current usage
|
|
}
|
|
}
|
|
}
|
|
|
|
if (mode == '=') {
|
|
int pcbits = 0;
|
|
const char *pcname = r_reg_get_name (core->anal->reg, R_REG_NAME_PC);
|
|
if (pcname) {
|
|
RRegItem *reg = r_reg_get (core->anal->reg, pcname, 0);
|
|
if (reg && bits != reg->size) {
|
|
pcbits = reg->size;
|
|
}
|
|
if (pcbits) {
|
|
r_debug_reg_list (core->dbg, R_REG_TYPE_GPR, pcbits, NULL, mode, use_color); // XXX detect which one is current usage
|
|
}
|
|
}
|
|
}
|
|
r_debug_reg_list (core->dbg, type, bits, pj, mode2, use_color);
|
|
if (mode == 'j') {
|
|
if (mode2 == 'J') {
|
|
pj_end (pj);
|
|
}
|
|
r_cons_println (pj_string (pj));
|
|
pj_free (pj);
|
|
}
|
|
|
|
core->dbg->reg = hack;
|
|
}
|
|
|
|
static RRegItem *reg_by_name_role(RCore *core, const char *n) {
|
|
RRegItem *r = r_reg_get (core->anal->reg, n, -1);
|
|
if (!r) {
|
|
int role = r_reg_get_name_idx (n);
|
|
if (role != -1) {
|
|
const char *alias = r_reg_get_name (core->anal->reg, role);
|
|
if (alias) {
|
|
r = r_reg_get (core->anal->reg, alias, -1);
|
|
}
|
|
}
|
|
}
|
|
return r;
|
|
}
|
|
|
|
static bool reg_name_roll_set(RCore *core, const char *name, ut64 n) {
|
|
RRegItem *r = reg_by_name_role (core, name);
|
|
if (r) {
|
|
r_reg_set_value (core->anal->reg, r, n);
|
|
r_debug_reg_sync (core->dbg, R_REG_TYPE_ALL, true);
|
|
r_core_cmdf (core, ".dr*%d", core->anal->config->bits); // XXX: replace in future
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
// XXX dup from drp :OOO
|
|
void cmd_anal_reg(RCore *core, const char *str) {
|
|
#if 0
|
|
if (0) {
|
|
/* enable this block when dr and ar use the same code but just using
|
|
core->dbg->reg or core->anal->reg and removing all the debugger
|
|
dependent code */
|
|
RReg *reg = core->dbg->reg;
|
|
core->dbg->reg = core->anal->reg;
|
|
cmd_debug_reg (core, str);
|
|
core->dbg->reg = reg;
|
|
return;
|
|
}
|
|
#endif
|
|
int size = 0, i, type = R_REG_TYPE_GPR;
|
|
int use_colors = r_config_get_i (core->config, "scr.color");
|
|
const char *use_color;
|
|
const char *name;
|
|
char *arg;
|
|
|
|
if (use_colors) {
|
|
#define ConsP(x) (core->cons && core->cons->context->pal.x)? core->cons->context->pal.x
|
|
use_color = ConsP (creg)
|
|
: Color_BWHITE;
|
|
} else {
|
|
use_color = NULL;
|
|
}
|
|
switch (str[0]) {
|
|
case 'l': // "arl"
|
|
{
|
|
const bool use_json = str[1] == 'j';
|
|
RRegSet *rs = r_reg_regset_get (core->anal->reg, R_REG_TYPE_GPR);
|
|
if (rs) {
|
|
RRegItem *r;
|
|
RListIter *iter;
|
|
PJ *pj = pj_new ();
|
|
pj_a (pj);
|
|
r_list_foreach (rs->regs, iter, r) {
|
|
if (use_json) {
|
|
pj_s (pj, r->name);
|
|
} else {
|
|
r_cons_println (r->name);
|
|
}
|
|
}
|
|
if (use_json) {
|
|
pj_end (pj);
|
|
const char *s = pj_string (pj);
|
|
r_cons_println (s);
|
|
}
|
|
pj_free (pj);
|
|
}
|
|
} break;
|
|
case ',': // "ar,"
|
|
__tableRegList (core, core->anal->reg, str + 1);
|
|
break;
|
|
case '0': // "ar0"
|
|
r_reg_arena_zero (core->anal->reg);
|
|
break;
|
|
case 'A': // "arA"
|
|
{
|
|
int nargs = 4;
|
|
RReg *reg = core->anal->reg;
|
|
for (i = 0; i < nargs; i++) {
|
|
r_strf_var (regname, 32, "A%d", i);
|
|
const char *name = r_reg_get_name (reg, r_reg_get_name_idx (regname));
|
|
ut64 off = r_reg_getv (core->anal->reg, name);
|
|
r_cons_printf ("0x%08"PFMT64x" ", off);
|
|
// XXX very ugly hack
|
|
char *s = r_core_cmd_strf (core, "pxr 32 @ 0x%08"PFMT64x, off);
|
|
if (s) {
|
|
char *nl = strchr (s, '\n');
|
|
if (nl) {
|
|
*nl = 0;
|
|
r_cons_printf ("%s\n", s);
|
|
}
|
|
free (s);
|
|
}
|
|
// r_core_cmd0 (core, "ar A0,A1,A2,A3");
|
|
}
|
|
}
|
|
break;
|
|
case 'C': // "arC"
|
|
if (core->anal->reg->reg_profile_cmt) {
|
|
r_cons_println (core->anal->reg->reg_profile_cmt);
|
|
}
|
|
break;
|
|
case 'w': // "arw"
|
|
switch (str[1]) {
|
|
case '?': {
|
|
r_core_cmd_help (core, help_msg_arw);
|
|
break;
|
|
}
|
|
case ' ':
|
|
r_reg_arena_set_bytes (core->anal->reg, str + 1);
|
|
break;
|
|
default:
|
|
r_core_cmd_help (core, help_msg_arw);
|
|
break;
|
|
}
|
|
break;
|
|
case 'a': // "ara"
|
|
switch (str[1]) {
|
|
case '?': // "ara?"
|
|
r_core_cmd_help (core, help_msg_ara);
|
|
break;
|
|
case 's': // "aras"
|
|
r_reg_arena_swap (core->anal->reg, false);
|
|
break;
|
|
case '+': // "ara+"
|
|
r_reg_arena_push (core->anal->reg);
|
|
break;
|
|
case '-': // "ara-"
|
|
r_reg_arena_pop (core->anal->reg);
|
|
break;
|
|
default: {
|
|
int i, j;
|
|
RRegArena *a;
|
|
RListIter *iter;
|
|
for (i = 0; i < R_REG_TYPE_LAST; i++) {
|
|
RRegSet *rs = &core->anal->reg->regset[i];
|
|
j = 0;
|
|
r_list_foreach (rs->pool, iter, a) {
|
|
r_cons_printf ("%s %p %d %d %s %d\n",
|
|
(a == rs->arena)? "*": ".", a,
|
|
i, j, r_reg_get_type (i), a->size);
|
|
j++;
|
|
}
|
|
}
|
|
} break;
|
|
}
|
|
break;
|
|
case '?': // "ar?"
|
|
if (str[1]) {
|
|
ut64 off = r_reg_getv (core->anal->reg, str + 1);
|
|
r_cons_printf ("0x%08" PFMT64x "\n", off);
|
|
} else {
|
|
r_core_cmd_help (core, help_msg_ar);
|
|
}
|
|
break;
|
|
case 'r': // "arr"
|
|
switch (str[1]) {
|
|
case 'j': // "arrj"
|
|
r_core_debug_rr (core, core->anal->reg, 'j');
|
|
break;
|
|
default:
|
|
r_core_debug_rr (core, core->anal->reg, 0);
|
|
break;
|
|
}
|
|
break;
|
|
case 'S': { // "arS"
|
|
int sz;
|
|
ut8 *buf = r_reg_get_bytes (core->anal->reg, R_REG_TYPE_GPR, &sz);
|
|
r_cons_printf ("%d\n", sz);
|
|
free (buf);
|
|
} break;
|
|
case 'b': { // "arb" WORK IN PROGRESS // DEBUG COMMAND
|
|
int len, type = R_REG_TYPE_GPR;
|
|
arg = strchr (str, ' ');
|
|
if (arg) {
|
|
char *string = r_str_trim_dup (arg + 1);
|
|
if (string) {
|
|
type = r_reg_type_by_name (string);
|
|
if (type == -1 && string[0] != 'a') {
|
|
type = R_REG_TYPE_GPR;
|
|
}
|
|
free (string);
|
|
}
|
|
}
|
|
ut8 *buf = r_reg_get_bytes (core->dbg->reg, type, &len);
|
|
if (buf) {
|
|
r_print_hexdump (core->print, 0LL, buf, len, 32, 4, 1);
|
|
free (buf);
|
|
}
|
|
} break;
|
|
case 'c': // "arc"
|
|
// TODO: set flag values with drc zf=1
|
|
if (str[1] == 'q') { // "arcq"
|
|
RRegFlags *rf = r_reg_cond_retrieve (core->dbg->reg, NULL);
|
|
if (rf) {
|
|
r_cons_printf ("s:%d z:%d c:%d o:%d p:%d\n",
|
|
rf->s, rf->z, rf->c, rf->o, rf->p);
|
|
free (rf);
|
|
}
|
|
break;
|
|
}
|
|
if (str[1] == 'c') { // "arcc"
|
|
char *s = r_reg_profile_to_cc (core->anal->reg);
|
|
if (s) {
|
|
r_cons_printf ("%s\n", s);
|
|
free (s);
|
|
}
|
|
} else {
|
|
RRegItem *r;
|
|
const char *name = r_str_trim_head_ro (str + 1);
|
|
if (*name && name[1]) {
|
|
r = r_reg_cond_get (core->dbg->reg, name);
|
|
if (r) {
|
|
r_cons_println (r->name);
|
|
} else {
|
|
int id = r_reg_cond_from_string (name);
|
|
RRegFlags *rf = r_reg_cond_retrieve (core->dbg->reg, NULL);
|
|
if (rf) {
|
|
int o = r_reg_cond_bits (core->dbg->reg, id, rf);
|
|
r_core_return_value (core, o);
|
|
// ORLY?
|
|
r_cons_printf ("%d\n", o);
|
|
free (rf);
|
|
} else {
|
|
R_LOG_ERROR ("unknown conditional or flag register");
|
|
}
|
|
}
|
|
} else {
|
|
RRegFlags *rf = r_reg_cond_retrieve (core->dbg->reg, NULL);
|
|
if (rf) {
|
|
r_cons_printf ("| s:%d z:%d c:%d o:%d p:%d\n",
|
|
rf->s, rf->z, rf->c, rf->o, rf->p);
|
|
if (*name == '=') {
|
|
for (i = 0; i < R_REG_COND_LAST; i++) {
|
|
r_cons_printf ("%s:%d ",
|
|
r_reg_cond_to_string (i),
|
|
r_reg_cond_bits (core->dbg->reg, i, rf));
|
|
}
|
|
r_cons_newline ();
|
|
} else {
|
|
for (i = 0; i < R_REG_COND_LAST; i++) {
|
|
r_cons_printf ("%d %s\n",
|
|
r_reg_cond_bits (core->dbg->reg, i, rf),
|
|
r_reg_cond_to_string (i));
|
|
}
|
|
}
|
|
free (rf);
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
case 's': // "ars"
|
|
switch (str[1]) {
|
|
case '-': // "ars-"
|
|
r_reg_arena_pop (core->dbg->reg);
|
|
// restore debug registers if in debugger mode
|
|
r_debug_reg_sync (core->dbg, R_REG_TYPE_GPR, true);
|
|
break;
|
|
case '+': // "ars+"
|
|
r_reg_arena_push (core->dbg->reg);
|
|
break;
|
|
case '?': { // "ars?"
|
|
// TODO #7967 help refactor: dup from drp
|
|
const char *help_msg[] = {
|
|
"Usage:", "drs", " # Register states commands",
|
|
"drs", "", "list register stack",
|
|
"drs+", "", "push register state",
|
|
"drs-", "", "pop register state",
|
|
NULL };
|
|
r_core_cmd_help (core, help_msg);
|
|
} break;
|
|
default:
|
|
{
|
|
void *p = core->dbg->reg->regset[0].pool;
|
|
int len = p? r_list_length (p): 0;
|
|
r_cons_printf ("%d\n", len);
|
|
}
|
|
break;
|
|
}
|
|
break;
|
|
case 'p': // "arp"
|
|
// XXX we have to break out .h for these cmd_xxx files.
|
|
cmd_reg_profile (core, 'a', str);
|
|
break;
|
|
case 't': // "art"
|
|
for (i = 0; (name = r_reg_get_type (i)); i++) {
|
|
r_cons_println (name);
|
|
}
|
|
break;
|
|
case 'n': // "arn"
|
|
if (*(str + 1) == '\0') {
|
|
R_LOG_ERROR ("Oops. try arn [PC|SP|BP|SN|A0|A1|A2|A3|A4|R0|R1|ZF|SF|NF|OF]");
|
|
break;
|
|
}
|
|
name = r_reg_get_name (core->dbg->reg, r_reg_get_name_idx (str + 2));
|
|
if (name && *name) {
|
|
r_cons_println (name);
|
|
} else {
|
|
R_LOG_ERROR ("Oops. try arn [PC|SP|BP|SN|A0|A1|A2|A3|A4|R0|R1|ZF|SF|NF|OF]");
|
|
}
|
|
break;
|
|
case 'd': // "ard"
|
|
r_debug_reg_list (core->dbg, R_REG_TYPE_GPR, core->anal->config->bits,
|
|
NULL, 3, use_color); // XXX detect which one is current usage
|
|
break;
|
|
case 'o': // "aro"
|
|
r_reg_arena_swap (core->dbg->reg, false);
|
|
r_debug_reg_list (core->dbg, R_REG_TYPE_GPR, core->anal->config->bits,
|
|
NULL, 0, use_color); // XXX detect which one is current usage
|
|
r_reg_arena_swap (core->dbg->reg, false);
|
|
break;
|
|
case '=': // "ar="
|
|
{
|
|
char *p = NULL;
|
|
char *bitstr = NULL;
|
|
if (str[1]) {
|
|
p = strdup (str + 1);
|
|
if (str[1] != ':') {
|
|
// Bits were specified
|
|
bitstr = strtok (p, ":");
|
|
if (r_str_isnumber (bitstr)) {
|
|
st64 sz = r_num_math (core->num, bitstr);
|
|
if (sz > 0) {
|
|
size = sz;
|
|
}
|
|
} else {
|
|
r_core_cmd_help (core, help_msg_ar);
|
|
break;
|
|
}
|
|
}
|
|
int len = bitstr ? strlen (bitstr) : 0;
|
|
if (str[len + 1] == ':') {
|
|
// We have some regs
|
|
char *regs = bitstr ? strtok (NULL, ":") : strtok ((char *)str + 1, ":");
|
|
char *reg = strtok (regs, " ");
|
|
RList *q_regs = r_list_new ();
|
|
if (q_regs) {
|
|
while (reg) {
|
|
r_list_append (q_regs, reg);
|
|
reg = strtok (NULL, " ");
|
|
}
|
|
core->dbg->q_regs = q_regs;
|
|
}
|
|
}
|
|
}
|
|
__anal_reg_list (core, type, size, str[0]);
|
|
if (!r_list_empty (core->dbg->q_regs)) {
|
|
r_list_free (core->dbg->q_regs);
|
|
}
|
|
core->dbg->q_regs = NULL;
|
|
free (p);
|
|
}
|
|
break;
|
|
case '.': // "ar."
|
|
case '-': // "ar-"
|
|
case '*': // "ar*"
|
|
case 'R': // "arR"
|
|
case 'j': // "arj"
|
|
case 'i': // "ari"
|
|
case '\0': // "ar"
|
|
__anal_reg_list (core, type, size, str[0]);
|
|
break;
|
|
case ' ': { // "ar "
|
|
arg = strchr (str + 1, '=');
|
|
if (arg) {
|
|
*arg = 0;
|
|
ut64 n = r_num_math (core->num, arg + 1);
|
|
char *ostr = r_str_trim_dup (str + 1);
|
|
char *regname = r_str_trim_nc (ostr);
|
|
if (!reg_name_roll_set (core, regname, n)) {
|
|
R_LOG_ERROR ("ar: Unknown register '%s'", regname);
|
|
}
|
|
free (ostr);
|
|
return;
|
|
}
|
|
char name[32];
|
|
int i = 1, j;
|
|
while (str[i]) {
|
|
if (str[i] == ',') {
|
|
i++;
|
|
} else {
|
|
for (j = i; str[++j] && str[j] != ','; );
|
|
if (j - i + 1 <= sizeof name) {
|
|
r_str_ncpy (name, str + i, j - i + 1);
|
|
if (IS_DIGIT (name[0])) { // e.g. ar 32
|
|
__anal_reg_list (core, R_REG_TYPE_GPR, atoi (name), '\0');
|
|
} else if (showreg (core, name) > 0) { // e.g. ar rax
|
|
} else { // e.g. ar gpr ; ar all
|
|
type = r_reg_type_by_name (name);
|
|
// TODO differentiate ALL and illegal register types and print error message for the latter
|
|
__anal_reg_list (core, type, -1, '\0');
|
|
}
|
|
}
|
|
i = j;
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
static ut64 initializeEsil(RCore *core) {
|
|
int exectrap = r_config_get_i (core->config, "esil.exectrap");
|
|
RAnalEsil *esil = esil_new_setup (core);
|
|
if (esil) {
|
|
r_anal_esil_free (core->anal->esil);
|
|
core->anal->esil = esil;
|
|
} else {
|
|
return UT64_MAX;
|
|
}
|
|
ut64 addr;
|
|
esil->cmd = r_core_esil_cmd;
|
|
{
|
|
const char *cmd_esil_step = r_config_get (core->config, "cmd.esil.step");
|
|
if (cmd_esil_step && *cmd_esil_step) {
|
|
esil->cmd_step = strdup (cmd_esil_step);
|
|
}
|
|
const char *cmd_esil_step_out = r_config_get (core->config, "cmd.esil.stepout");
|
|
if (cmd_esil_step_out && *cmd_esil_step_out) {
|
|
esil->cmd_step_out = strdup (cmd_esil_step_out);
|
|
}
|
|
{
|
|
const char *s = r_config_get (core->config, "cmd.esil.intr");
|
|
if (s) {
|
|
char *my = strdup (s);
|
|
if (my) {
|
|
r_config_set (core->config, "cmd.esil.intr", my);
|
|
free (my);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
esil->exectrap = exectrap;
|
|
RList *entries = r_bin_get_entries (core->bin);
|
|
RBinAddr *entry = NULL;
|
|
RBinInfo *info = NULL;
|
|
if (entries && !r_list_empty (entries)) {
|
|
entry = (RBinAddr *)r_list_pop_head (entries);
|
|
info = r_bin_get_info (core->bin);
|
|
addr = info->has_va? entry->vaddr: entry->paddr;
|
|
r_list_push (entries, entry);
|
|
} else {
|
|
addr = core->offset;
|
|
}
|
|
// set memory read only
|
|
return addr;
|
|
}
|
|
|
|
R_API int r_core_esil_step(RCore *core, ut64 until_addr, const char *until_expr, ut64 *prev_addr, bool stepOver) {
|
|
#define return_tail(x) { tail_return_value = x; goto tail_return; }
|
|
int tail_return_value = 0;
|
|
int ret;
|
|
ut8 code[32];
|
|
RAnalOp op = {0};
|
|
RAnalEsil *esil = core->anal->esil;
|
|
const char *_pcname = r_reg_get_name (core->anal->reg, R_REG_NAME_PC);
|
|
if (R_STR_ISEMPTY (_pcname)) {
|
|
R_LOG_ERROR ("Cannot find =PC in current reg profile");
|
|
return 0;
|
|
}
|
|
char *pcname = strdup (_pcname);
|
|
ut64 addr = r_reg_getv (core->anal->reg, pcname);
|
|
bool r2wars = r_config_get_b (core->config, "cfg.r2wars");
|
|
bool breakoninvalid = r_config_get_b (core->config, "esil.breakoninvalid");
|
|
int esiltimeout = r_config_get_i (core->config, "esil.timeout");
|
|
ut64 startTime = 0;
|
|
|
|
if (esiltimeout > 0) {
|
|
startTime = r_time_now_mono ();
|
|
}
|
|
r_cons_break_push (NULL, NULL);
|
|
for (; ; r_anal_op_fini (&op)) {
|
|
if (r_cons_is_breaked ()) {
|
|
R_LOG_INFO ("[+] ESIL emulation interrupted at 0x%08" PFMT64x, addr);
|
|
return_tail (0);
|
|
}
|
|
//Break if we have exceeded esil.timeout
|
|
if (esiltimeout > 0) {
|
|
ut64 elapsedTime = r_time_now_mono () - startTime;
|
|
elapsedTime >>= 20;
|
|
if (elapsedTime >= esiltimeout) {
|
|
R_LOG_INFO ("[ESIL] Timeout exceeded");
|
|
return_tail (0);
|
|
}
|
|
}
|
|
if (!esil) {
|
|
addr = initializeEsil (core);
|
|
esil = core->anal->esil;
|
|
if (!esil) {
|
|
return_tail (0);
|
|
}
|
|
} else {
|
|
esil->trap = 0;
|
|
addr = r_reg_getv (core->anal->reg, pcname);
|
|
}
|
|
if (prev_addr) {
|
|
*prev_addr = addr;
|
|
}
|
|
if (esil->exectrap) {
|
|
if (!r_io_is_valid_offset (core->io, addr, R_PERM_X)) {
|
|
esil->trap = R_ANAL_TRAP_EXEC_ERR;
|
|
esil->trap_code = addr;
|
|
R_LOG_INFO ("[ESIL] Trap, trying to execute on non-executable memory");
|
|
return_tail (1);
|
|
}
|
|
} else {
|
|
if (!r_io_is_valid_offset (core->io, addr, 0)) {
|
|
esil->trap = R_ANAL_TRAP_EXEC_ERR;
|
|
esil->trap_code = addr;
|
|
R_LOG_INFO ("[ESIL] Trap, trying to execute on non-executable memory");
|
|
return_tail (1);
|
|
}
|
|
}
|
|
r_asm_set_pc (core->rasm, addr);
|
|
// run esil pin command here
|
|
const char *pincmd = r_anal_pin_call (core->anal, addr);
|
|
if (pincmd) {
|
|
r_core_cmd0 (core, pincmd);
|
|
ut64 pc = r_reg_getv (core->anal->reg, pcname);
|
|
if (addr != pc) {
|
|
return_tail (1);
|
|
}
|
|
}
|
|
int dataAlign = r_anal_archinfo (esil->anal, R_ANAL_ARCHINFO_DATA_ALIGN);
|
|
if (dataAlign > 1) {
|
|
if (addr % dataAlign) {
|
|
if (esil->cmd && esil->cmd_trap) {
|
|
esil->cmd (esil, esil->cmd_trap, addr, R_ANAL_TRAP_UNALIGNED);
|
|
}
|
|
if (breakoninvalid) {
|
|
r_cons_printf ("[ESIL] Stopped execution in an unaligned instruction (see e??esil.breakoninvalid)\n");
|
|
return_tail (0);
|
|
}
|
|
}
|
|
}
|
|
(void) r_io_read_at_mapped (core->io, addr, code, sizeof (code));
|
|
// TODO: sometimes this is dupe
|
|
ret = r_anal_op (core->anal, &op, addr, code, sizeof (code), R_ANAL_OP_MASK_ESIL | R_ANAL_OP_MASK_HINT);
|
|
// if type is JMP then we execute the next N instructions
|
|
// update the esil pointer because RAnal.op() can change it
|
|
esil = core->anal->esil;
|
|
if (op.size < 1 || ret < 1) {
|
|
if (esil->cmd && esil->cmd_trap) {
|
|
esil->cmd (esil, esil->cmd_trap, addr, R_ANAL_TRAP_INVALID);
|
|
}
|
|
if (breakoninvalid) {
|
|
R_LOG_INFO ("[ESIL] Stopped execution in an invalid instruction (see e??esil.breakoninvalid)");
|
|
return_tail (0);
|
|
}
|
|
op.size = 1; // avoid inverted stepping
|
|
}
|
|
if (stepOver) {
|
|
switch (op.type) {
|
|
case R_ANAL_OP_TYPE_SWI:
|
|
case R_ANAL_OP_TYPE_UCALL:
|
|
case R_ANAL_OP_TYPE_CALL:
|
|
case R_ANAL_OP_TYPE_JMP:
|
|
case R_ANAL_OP_TYPE_RCALL:
|
|
case R_ANAL_OP_TYPE_RJMP:
|
|
case R_ANAL_OP_TYPE_CJMP:
|
|
case R_ANAL_OP_TYPE_RET:
|
|
case R_ANAL_OP_TYPE_CRET:
|
|
case R_ANAL_OP_TYPE_UJMP:
|
|
if (addr == until_addr) {
|
|
return_tail (0);
|
|
} else {
|
|
r_reg_setv (core->anal->reg, pcname, op.addr + op.size);
|
|
r_reg_setv (core->dbg->reg, pcname, op.addr + op.size);
|
|
}
|
|
return_tail (1);
|
|
}
|
|
}
|
|
if (r2wars) {
|
|
// this is x86 and r2wars specific, shouldnt hurt outside x86
|
|
ut64 vECX = r_reg_getv (core->anal->reg, "ecx");
|
|
if (op.prefix & R_ANAL_OP_PREFIX_REP && vECX > 1) {
|
|
//char *tmp = strstr (op.esil.ptr, ",ecx,?{,5,GOTO,}");
|
|
char *tmp = strstr (op.esil.ptr, ",0,GOTO");
|
|
if (tmp) {
|
|
tmp[0] = 0;
|
|
op.esil.len -= 7; //16;
|
|
} else {
|
|
r_reg_setv (core->anal->reg, pcname, addr + op.size);
|
|
}
|
|
} else {
|
|
r_reg_setv (core->anal->reg, pcname, addr + op.size);
|
|
}
|
|
} else {
|
|
r_reg_setv (core->anal->reg, pcname, addr + op.size);
|
|
}
|
|
if (ret) {
|
|
r_anal_esil_set_pc (esil, addr);
|
|
const char *e = R_STRBUF_SAFEGET (&op.esil);
|
|
if (core->dbg->trace->enabled) {
|
|
RReg *reg = core->dbg->reg;
|
|
core->dbg->reg = core->anal->reg;
|
|
r_debug_trace_op (core->dbg, &op);
|
|
core->dbg->reg = reg;
|
|
} else if (R_STR_ISNOTEMPTY (e)) {
|
|
r_anal_esil_parse (esil, e);
|
|
if (core->anal->cur && core->anal->cur->esil_post_loop) {
|
|
core->anal->cur->esil_post_loop (esil, &op);
|
|
}
|
|
r_anal_esil_stack_free (esil);
|
|
}
|
|
bool isNextFall = false;
|
|
if (op.type == R_ANAL_OP_TYPE_CJMP) {
|
|
int err = 0;
|
|
ut64 pc = r_reg_getv (core->anal->reg, pcname);
|
|
if (err) {
|
|
R_LOG_ERROR ("Missing PC register in the current profile");
|
|
break;
|
|
}
|
|
if (pc == addr + op.size) {
|
|
// do not opdelay here
|
|
isNextFall = true;
|
|
}
|
|
}
|
|
// only support 1 slot for now
|
|
if (op.delay && !isNextFall) {
|
|
ut8 code2[32];
|
|
ut64 naddr = addr + op.size;
|
|
RAnalOp op2 = {0};
|
|
// emulate only 1 instruction
|
|
r_anal_esil_set_pc (esil, naddr);
|
|
(void)r_io_read_at (core->io, naddr, code2, sizeof (code2));
|
|
// TODO: sometimes this is dupe
|
|
ret = r_anal_op (core->anal, &op2, naddr, code2, sizeof (code2), R_ANAL_OP_MASK_ESIL | R_ANAL_OP_MASK_HINT);
|
|
if (ret > 0) {
|
|
switch (op2.type) {
|
|
case R_ANAL_OP_TYPE_CJMP:
|
|
case R_ANAL_OP_TYPE_JMP:
|
|
case R_ANAL_OP_TYPE_CRET:
|
|
case R_ANAL_OP_TYPE_RET:
|
|
// branches are illegal in a delay slot
|
|
esil->trap = R_ANAL_TRAP_EXEC_ERR;
|
|
esil->trap_code = addr;
|
|
R_LOG_INFO ("[ESIL] Trap, trying to execute a branch in a delay slot");
|
|
return_tail (1);
|
|
break;
|
|
}
|
|
const char *e = R_STRBUF_SAFEGET (&op2.esil);
|
|
if (R_STR_ISNOTEMPTY (e)) {
|
|
r_anal_esil_parse (esil, e);
|
|
}
|
|
} else {
|
|
R_LOG_ERROR ("Invalid instruction at 0x%08"PFMT64x, naddr);
|
|
}
|
|
r_anal_op_fini (&op2);
|
|
}
|
|
tail_return_value = 1;
|
|
}
|
|
// esil->verbose ?
|
|
// eprintf ("REPE 0x%llx %s => 0x%llx\n", addr, R_STRBUF_SAFEGET (&op.esil), r_reg_getv (core->anal->reg, "PC"));
|
|
ut64 pc = r_reg_getv (core->anal->reg, pcname);
|
|
if (pc == UT64_MAX || pc == UT32_MAX) {
|
|
R_LOG_ERROR ("Invalid program counter PC=-1");
|
|
break;
|
|
}
|
|
if (core->anal->config->pcalign > 0) {
|
|
pc -= (pc % core->anal->config->pcalign);
|
|
r_reg_setv (core->anal->reg, pcname, pc);
|
|
r_reg_setv (core->dbg->reg, pcname, pc);
|
|
}
|
|
st64 follow = (st64)r_config_get_i (core->config, "dbg.follow");
|
|
if (follow > 0) {
|
|
if ((pc < core->offset) || (pc > (core->offset + follow))) {
|
|
r_core_seek (core, pc, true);
|
|
}
|
|
}
|
|
// check breakpoints
|
|
if (r_bp_get_at (core->dbg->bp, pc)) {
|
|
r_cons_printf ("[ESIL] hit breakpoint at 0x%"PFMT64x "\n", pc);
|
|
return_tail (0);
|
|
}
|
|
// check addr
|
|
if (until_addr != UT64_MAX) {
|
|
if (pc == until_addr) {
|
|
return_tail (0);
|
|
}
|
|
continue;
|
|
}
|
|
// check esil
|
|
if (esil && esil->trap) {
|
|
if (core->anal->esil->verbose) {
|
|
R_LOG_INFO ("TRAP");
|
|
}
|
|
return_tail (0);
|
|
}
|
|
if (until_expr) {
|
|
if (r_anal_esil_condition (core->anal->esil, until_expr)) {
|
|
if (core->anal->esil->verbose) {
|
|
R_LOG_INFO ("ESIL BREAK!");
|
|
}
|
|
return_tail (0);
|
|
}
|
|
continue;
|
|
}
|
|
break;
|
|
}
|
|
tail_return:
|
|
r_anal_op_fini (&op);
|
|
r_cons_break_pop ();
|
|
free (pcname);
|
|
return tail_return_value;
|
|
}
|
|
|
|
R_API bool r_core_esil_step_back(RCore *core) {
|
|
r_return_val_if_fail (core && core->anal, false);
|
|
if (!core->anal->esil || !core->anal->esil->trace) {
|
|
R_LOG_INFO ("Run `aeim` to initialize the esil VM and enable e dbg.trace=true");
|
|
return false;
|
|
}
|
|
RAnalEsil *esil = core->anal->esil;
|
|
if (esil->trace->idx > 0) {
|
|
r_anal_esil_trace_restore (esil, esil->trace->idx - 1);
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
static void cmd_address_info(RCore *core, const char *addrstr, int fmt) {
|
|
ut64 addr = R_STR_ISEMPTY (addrstr)? core->offset: r_num_math (core->num, addrstr);
|
|
ut64 type = r_core_anal_address (core, addr);
|
|
switch (fmt) {
|
|
case 'j': {
|
|
PJ *pj = r_core_pj_new (core);
|
|
if (!pj) {
|
|
return;
|
|
}
|
|
pj_o (pj);
|
|
if (type & R_ANAL_ADDR_TYPE_PROGRAM)
|
|
pj_ks (pj, "program", "true");
|
|
if (type & R_ANAL_ADDR_TYPE_LIBRARY)
|
|
pj_ks (pj, "library", "true");
|
|
if (type & R_ANAL_ADDR_TYPE_EXEC)
|
|
pj_ks (pj, "exec", "true");
|
|
if (type & R_ANAL_ADDR_TYPE_READ)
|
|
pj_ks (pj, "read", "true");
|
|
if (type & R_ANAL_ADDR_TYPE_WRITE)
|
|
pj_ks (pj, "write", "true");
|
|
if (type & R_ANAL_ADDR_TYPE_FLAG)
|
|
pj_ks (pj, "flag", "true");
|
|
if (type & R_ANAL_ADDR_TYPE_FUNC)
|
|
pj_ks (pj, "func", "true");
|
|
if (type & R_ANAL_ADDR_TYPE_STACK)
|
|
pj_ks (pj, "stack", "true");
|
|
if (type & R_ANAL_ADDR_TYPE_HEAP)
|
|
pj_ks (pj, "heap", "true");
|
|
if (type & R_ANAL_ADDR_TYPE_REG)
|
|
pj_ks (pj, "reg", "true");
|
|
if (type & R_ANAL_ADDR_TYPE_ASCII)
|
|
pj_ks (pj, "ascii", "true");
|
|
if (type & R_ANAL_ADDR_TYPE_SEQUENCE)
|
|
pj_ks (pj, "sequence", "true");
|
|
pj_end (pj);
|
|
r_cons_println (pj_string (pj));
|
|
pj_free (pj);
|
|
}
|
|
break;
|
|
default:
|
|
if (type & R_ANAL_ADDR_TYPE_PROGRAM)
|
|
r_cons_printf ("program\n");
|
|
if (type & R_ANAL_ADDR_TYPE_LIBRARY)
|
|
r_cons_printf ("library\n");
|
|
if (type & R_ANAL_ADDR_TYPE_EXEC)
|
|
r_cons_printf ("exec\n");
|
|
if (type & R_ANAL_ADDR_TYPE_READ)
|
|
r_cons_printf ("read\n");
|
|
if (type & R_ANAL_ADDR_TYPE_WRITE)
|
|
r_cons_printf ("write\n");
|
|
if (type & R_ANAL_ADDR_TYPE_FLAG)
|
|
r_cons_printf ("flag\n");
|
|
if (type & R_ANAL_ADDR_TYPE_FUNC)
|
|
r_cons_printf ("func\n");
|
|
if (type & R_ANAL_ADDR_TYPE_STACK)
|
|
r_cons_printf ("stack\n");
|
|
if (type & R_ANAL_ADDR_TYPE_HEAP)
|
|
r_cons_printf ("heap\n");
|
|
if (type & R_ANAL_ADDR_TYPE_REG)
|
|
r_cons_printf ("reg\n");
|
|
if (type & R_ANAL_ADDR_TYPE_ASCII)
|
|
r_cons_printf ("ascii\n");
|
|
if (type & R_ANAL_ADDR_TYPE_SEQUENCE)
|
|
r_cons_printf ("sequence\n");
|
|
break;
|
|
}
|
|
}
|
|
|
|
static void cmd_anal_info(RCore *core, const char *input) {
|
|
switch (input[0]) {
|
|
case '?':
|
|
r_core_cmd_help (core, help_msg_ai);
|
|
break;
|
|
case ' ':
|
|
cmd_address_info (core, input, 0);
|
|
break;
|
|
case 'i': // "aii"
|
|
// global imports
|
|
if (input[1]) {
|
|
if (input[1] == ' ') {
|
|
char *s = r_str_trim_dup (input + 1);
|
|
if (s) {
|
|
r_anal_add_import (core->anal, s);
|
|
free (s);
|
|
}
|
|
} else if (input[1] == '-') {
|
|
r_anal_purge_imports (core->anal);
|
|
} else {
|
|
eprintf ("Usagae: aii [namespace] # see afii - imports\n");
|
|
}
|
|
} else {
|
|
if (core->anal->imports) {
|
|
char *imp;
|
|
RListIter *iter;
|
|
r_list_foreach (core->anal->imports, iter, imp) {
|
|
r_cons_printf ("%s\n", imp);
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
case 'j': // "aij"
|
|
cmd_address_info (core, input + 1, 'j');
|
|
break;
|
|
default:
|
|
cmd_address_info (core, NULL, 0);
|
|
break;
|
|
}
|
|
}
|
|
|
|
static void initialize_stack(RCore *core, ut64 addr, ut64 size) {
|
|
const char *mode = r_config_get (core->config, "esil.fillstack");
|
|
if (mode && *mode && *mode != '0') {
|
|
const ut64 bs = 4096 * 32;
|
|
ut64 i;
|
|
for (i = 0; i < size; i += bs) {
|
|
ut64 left = R_MIN (bs, size - i);
|
|
// r_core_cmdf (core, "wx 10203040 @ 0x%llx", addr);
|
|
switch (*mode) {
|
|
case 'd': // "debrujn"
|
|
r_core_cmdf (core, "wopD %"PFMT64u" @ 0x%"PFMT64x, left, addr + i);
|
|
break;
|
|
case 's': // "seq"
|
|
r_core_cmdf (core, "woe 1 0xff 1 4 @ 0x%"PFMT64x"!0x%"PFMT64x, addr + i, left);
|
|
break;
|
|
case 'r': // "random"
|
|
r_core_cmdf (core, "woR %"PFMT64u" @ 0x%"PFMT64x"!0x%"PFMT64x, left, addr + i, left);
|
|
break;
|
|
case 'z': // "zero"
|
|
case '0':
|
|
r_core_cmdf (core, "wow 00 @ 0x%"PFMT64x"!0x%"PFMT64x, addr + i, left);
|
|
break;
|
|
}
|
|
}
|
|
// eprintf ("[*] Initializing ESIL stack with pattern\n");
|
|
// r_core_cmdf (core, "woe 0 10 4 @ 0x%"PFMT64x, size, addr);
|
|
}
|
|
}
|
|
|
|
static void cmd_esil_mem(RCore *core, const char *input) {
|
|
RAnalEsil *esil = core->anal->esil;
|
|
RIOMap *stack_map;
|
|
ut64 curoff = core->offset;
|
|
const char *patt = "";
|
|
ut64 addr = 0x100000;
|
|
ut32 size = 0xf0000;
|
|
char name[128];
|
|
RFlagItem *fi;
|
|
char uri[32];
|
|
char nomalloc[256];
|
|
char *p;
|
|
if (!esil) {
|
|
esil = esil_new_setup (core);
|
|
if (!esil) {
|
|
return;
|
|
}
|
|
r_anal_esil_free (core->anal->esil);
|
|
core->anal->esil = esil;
|
|
{
|
|
const char *s = r_config_get (core->config, "cmd.esil.intr");
|
|
if (s) {
|
|
char *my = strdup (s);
|
|
if (my) {
|
|
r_config_set (core->config, "cmd.esil.intr", my);
|
|
free (my);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
if (*input == '?') {
|
|
r_core_cmd_help (core, help_msg_aeim);
|
|
return;
|
|
}
|
|
|
|
if (input[0] == 'p') {
|
|
fi = r_flag_get (core->flags, "aeim.stack");
|
|
if (fi) {
|
|
addr = fi->offset;
|
|
size = fi->size;
|
|
} else {
|
|
cmd_esil_mem (core, "");
|
|
}
|
|
esil->stack_addr = addr;
|
|
esil->stack_size = size;
|
|
initialize_stack (core, addr, size);
|
|
return;
|
|
}
|
|
|
|
if (!*input) {
|
|
char *fi = sdb_get (core->sdb, "aeim.fd", 0);
|
|
if (fi) {
|
|
// Close the fd associated with the aeim stack
|
|
ut64 fd = sdb_atoi (fi);
|
|
(void)r_io_fd_close (core->io, fd);
|
|
free (fi);
|
|
}
|
|
}
|
|
size = r_config_get_i (core->config, "esil.stack.size");
|
|
addr = r_config_get_i (core->config, "esil.stack.addr");
|
|
|
|
if (r_io_map_is_mapped (core->io, addr)) {
|
|
addr = core->offset;
|
|
r_io_map_locate (core->io, &addr, size, 0x10000000);
|
|
}
|
|
patt = r_config_get (core->config, "esil.stack.pattern");
|
|
p = strncpy (nomalloc, input, 255);
|
|
if ((p = strchr (p, ' '))) {
|
|
while (*p == ' ') p++;
|
|
addr = r_num_math (core->num, p);
|
|
if ((p = strchr (p, ' '))) {
|
|
while (*p == ' ') p++;
|
|
size = (ut32)r_num_math (core->num, p);
|
|
if (size < 1) {
|
|
size = 0xf0000;
|
|
}
|
|
if ((p = strchr (p, ' '))) {
|
|
while (*p == ' ') {
|
|
p++;
|
|
}
|
|
snprintf (name, sizeof (name), "mem.%s", p);
|
|
} else {
|
|
snprintf (name, sizeof (name), "mem.0x%" PFMT64x "_0x%x", addr, size);
|
|
}
|
|
} else {
|
|
snprintf (name, sizeof (name), "mem.0x%" PFMT64x "_0x%x", addr, size);
|
|
}
|
|
} else {
|
|
snprintf (name, sizeof (name), "mem.0x%" PFMT64x "_0x%x", addr, size);
|
|
}
|
|
if (*input == '-') {
|
|
if (esil->stack_fd > 2) { //0, 1, 2 are reserved for stdio/stderr
|
|
r_io_fd_close (core->io, esil->stack_fd);
|
|
// no need to kill the maps, r_io_map_cleanup does that for us in the close
|
|
esil->stack_fd = 0;
|
|
} else {
|
|
R_LOG_ERROR ("Cannot deinitialize %s", name);
|
|
}
|
|
r_flag_unset_name (core->flags, name);
|
|
r_flag_unset_name (core->flags, "aeim.stack");
|
|
sdb_unset(core->sdb, "aeim.fd", 0);
|
|
// eprintf ("Deinitialized %s\n", name);
|
|
return;
|
|
}
|
|
|
|
snprintf (uri, sizeof (uri), "malloc://%d", (int)size);
|
|
esil->stack_fd = r_io_fd_open (core->io, uri, R_PERM_RW, 0);
|
|
if (!(stack_map = r_io_map_add (core->io, esil->stack_fd, R_PERM_RW, 0LL, addr, size))) {
|
|
r_io_fd_close (core->io, esil->stack_fd);
|
|
R_LOG_ERROR ("Cannot create map for tha stack, fd %d got closed again", esil->stack_fd);
|
|
esil->stack_fd = 0;
|
|
return;
|
|
}
|
|
r_io_map_set_name (stack_map, name);
|
|
// r_flag_set (core->flags, name, addr, size); //why is this here?
|
|
char val[128], *v;
|
|
v = sdb_itoa (esil->stack_fd, val, 10);
|
|
sdb_set (core->sdb, "aeim.fd", v, 0);
|
|
|
|
r_config_set_i (core->config, "io.va", true);
|
|
if (patt && *patt) {
|
|
switch (*patt) {
|
|
case '0':
|
|
// do nothing
|
|
break;
|
|
case 'd':
|
|
r_core_cmdf (core, "wopD %d @ 0x%"PFMT64x, size, addr);
|
|
break;
|
|
case 'i':
|
|
r_core_cmdf (core, "woe 0 255 1 @ 0x%"PFMT64x"!%d",addr, size);
|
|
break;
|
|
case 'w':
|
|
r_core_cmdf (core, "woe 0 0xffff 1 4 @ 0x%"PFMT64x"!%d",addr, size);
|
|
break;
|
|
}
|
|
}
|
|
// SP
|
|
ut64 sp = addr + (size / 2);
|
|
r_reg_setv (core->anal->reg, "SP", sp);
|
|
r_reg_setv (core->anal->reg, "BP", sp);
|
|
r_reg_setv (core->anal->reg, "PC", curoff);
|
|
r_core_cmd0 (core, ".ar*");
|
|
esil->stack_addr = addr;
|
|
esil->stack_size = size;
|
|
initialize_stack (core, addr, size);
|
|
r_core_seek (core, curoff, false);
|
|
}
|
|
|
|
#if 0
|
|
static ut64 opc = UT64_MAX;
|
|
static ut8 *regstate = NULL;
|
|
|
|
static void esil_init(RCore *core) {
|
|
const char *pc = r_reg_get_name (core->anal->reg, R_REG_NAME_PC);
|
|
int nonull = r_config_get_i (core->config, "esil.nonull");
|
|
opc = r_reg_getv (core->anal->reg, pc);
|
|
if (!opc || opc==UT64_MAX) {
|
|
opc = core->offset;
|
|
}
|
|
if (!core->anal->esil) {
|
|
core->anal->esil = esil_new_from_core (core);
|
|
if (!core->anal->esil) {
|
|
R_FREE (regstate);
|
|
return;
|
|
}
|
|
r_anal_esil_setup (core->anal->esil, core->anal, 0, 0, nonull);
|
|
}
|
|
free (regstate);
|
|
regstate = r_reg_arena_peek (core->anal->reg);
|
|
}
|
|
|
|
static void esil_fini(RCore *core) {
|
|
const char *pc = r_reg_get_name (core->anal->reg, R_REG_NAME_PC);
|
|
r_reg_arena_poke (core->anal->reg, regstate);
|
|
r_reg_setv (core->anal->reg, pc, opc);
|
|
R_FREE (regstate);
|
|
}
|
|
#endif
|
|
|
|
typedef struct {
|
|
RList *regs;
|
|
RList *regread;
|
|
RList *regwrite;
|
|
RList *regvalues;
|
|
RList *inputregs;
|
|
} AeaStats;
|
|
|
|
static void aea_stats_init(AeaStats *stats) {
|
|
stats->regs = r_list_newf (free);
|
|
stats->regread = r_list_newf (free);
|
|
stats->regwrite = r_list_newf (free);
|
|
stats->regvalues = r_list_newf (free);
|
|
stats->inputregs = r_list_newf (free);
|
|
}
|
|
|
|
static void aea_stats_fini(AeaStats *stats) {
|
|
R_FREE (stats->regs);
|
|
R_FREE (stats->regread);
|
|
R_FREE (stats->regwrite);
|
|
R_FREE (stats->inputregs);
|
|
}
|
|
|
|
static bool contains(RList *list, const char *name) {
|
|
RListIter *iter;
|
|
const char *n;
|
|
r_list_foreach (list, iter, n) {
|
|
if (!strcmp (name, n))
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
static char *oldregread = NULL;
|
|
static RList *mymemxsr = NULL;
|
|
static RList *mymemxsw = NULL;
|
|
|
|
#define R_NEW_DUP(x) memcpy((void*)malloc(sizeof (x)), &(x), sizeof (x))
|
|
typedef struct {
|
|
ut64 addr;
|
|
int size;
|
|
} AeaMemItem;
|
|
|
|
static bool mymemwrite(RAnalEsil *esil, ut64 addr, const ut8 *buf, int len) {
|
|
AeaMemItem *n;
|
|
RListIter *iter;
|
|
r_list_foreach (mymemxsw, iter, n) {
|
|
if (addr == n->addr) {
|
|
return true;
|
|
}
|
|
}
|
|
if (!r_io_is_valid_offset (esil->anal->iob.io, addr, 0)) {
|
|
return false;
|
|
}
|
|
n = R_NEW (AeaMemItem);
|
|
if (n) {
|
|
n->addr = addr;
|
|
n->size = len;
|
|
r_list_push (mymemxsw, n);
|
|
}
|
|
return true;
|
|
}
|
|
|
|
static bool mymemread(RAnalEsil *esil, ut64 addr, ut8 *buf, int len) {
|
|
RListIter *iter;
|
|
AeaMemItem *n;
|
|
r_list_foreach (mymemxsr, iter, n) {
|
|
if (addr == n->addr) {
|
|
return true;
|
|
}
|
|
}
|
|
if (!r_io_is_valid_offset (esil->anal->iob.io, addr, 0)) {
|
|
return false;
|
|
}
|
|
n = R_NEW (AeaMemItem);
|
|
if (n) {
|
|
n->addr = addr;
|
|
n->size = len;
|
|
r_list_push (mymemxsr, n);
|
|
}
|
|
return true;
|
|
}
|
|
|
|
static bool myregwrite(RAnalEsil *esil, const char *name, ut64 *val) {
|
|
AeaStats *stats = esil->user;
|
|
if (oldregread && !strcmp (name, oldregread)) {
|
|
r_list_pop (stats->regread);
|
|
R_FREE (oldregread)
|
|
}
|
|
if (!IS_DIGIT (*name)) {
|
|
if (!contains (stats->regs, name)) {
|
|
r_list_push (stats->regs, strdup (name));
|
|
}
|
|
if (!contains (stats->regwrite, name)) {
|
|
r_list_push (stats->regwrite, strdup (name));
|
|
}
|
|
char *v = r_str_newf ("%"PFMT64d, *val);
|
|
if (!contains (stats->regvalues, v)) {
|
|
r_list_push (stats->regvalues, strdup (v));
|
|
}
|
|
free (v);
|
|
}
|
|
return false;
|
|
}
|
|
|
|
static bool myregread(RAnalEsil *esil, const char *name, ut64 *val, int *len) {
|
|
AeaStats *stats = esil->user;
|
|
if (!IS_DIGIT (*name)) {
|
|
if (!contains (stats->inputregs, name)) {
|
|
if (!contains (stats->regwrite, name)) {
|
|
r_list_push (stats->inputregs, strdup (name));
|
|
}
|
|
}
|
|
if (!contains (stats->regs, name)) {
|
|
r_list_push (stats->regs, strdup (name));
|
|
}
|
|
if (!contains (stats->regread, name)) {
|
|
r_list_push (stats->regread, strdup (name));
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
static void showregs(RList *list) {
|
|
if (!r_list_empty (list)) {
|
|
char *reg;
|
|
RListIter *iter;
|
|
r_list_foreach (list, iter, reg) {
|
|
r_cons_print (reg);
|
|
if (iter->n) {
|
|
r_cons_printf (" ");
|
|
}
|
|
}
|
|
}
|
|
r_cons_newline ();
|
|
}
|
|
|
|
static void showmem(RList *list) {
|
|
if (!r_list_empty (list)) {
|
|
AeaMemItem *item;
|
|
RListIter *iter;
|
|
r_list_foreach (list, iter, item) {
|
|
r_cons_printf (" 0x%08"PFMT64x, item->addr);
|
|
|
|
}
|
|
}
|
|
r_cons_newline ();
|
|
}
|
|
|
|
static void showregs_json(RList *list, PJ *pj) {
|
|
pj_a (pj);
|
|
if (!r_list_empty (list)) {
|
|
char *reg;
|
|
RListIter *iter;
|
|
|
|
r_list_foreach (list, iter, reg) {
|
|
pj_s (pj, reg);
|
|
}
|
|
}
|
|
pj_end (pj);
|
|
}
|
|
|
|
static void showmem_json(RList *list, PJ *pj) {
|
|
pj_a (pj);
|
|
if (!r_list_empty (list)) {
|
|
RListIter *iter;
|
|
AeaMemItem *item;
|
|
r_list_foreach (list, iter, item) {
|
|
pj_n (pj, item->addr);
|
|
}
|
|
}
|
|
|
|
pj_end (pj);
|
|
}
|
|
|
|
static bool cmd_aea(RCore* core, int mode, ut64 addr, int length) {
|
|
RAnalEsil *esil;
|
|
int ptr, ops, ops_end = 0, len, buf_sz;
|
|
ut64 addr_end;
|
|
AeaStats stats;
|
|
const char *esilstr;
|
|
RAnalOp aop = {0};
|
|
RList* regnow;
|
|
PJ *pj = NULL;
|
|
if (!core) {
|
|
return false;
|
|
}
|
|
int maxopsize = r_anal_archinfo (core->anal, R_ANAL_ARCHINFO_MAX_OP_SIZE);
|
|
if (maxopsize < 1) {
|
|
maxopsize = 16;
|
|
}
|
|
if (mode & 1) {
|
|
// number of bytes / length
|
|
buf_sz = length;
|
|
} else {
|
|
// number of instructions / opcodes
|
|
ops_end = length;
|
|
if (ops_end < 1) {
|
|
ops_end = 1;
|
|
}
|
|
buf_sz = ops_end * maxopsize;
|
|
}
|
|
if (buf_sz < 1) {
|
|
buf_sz = maxopsize;
|
|
}
|
|
addr_end = addr + buf_sz;
|
|
ut8 *buf = malloc (buf_sz);
|
|
if (!buf) {
|
|
return false;
|
|
}
|
|
(void)r_io_read_at (core->io, addr, (ut8 *)buf, buf_sz);
|
|
aea_stats_init (&stats);
|
|
|
|
//esil_init (core);
|
|
//esil = core->anal->esil;
|
|
r_reg_arena_push (core->anal->reg);
|
|
const bool cfg_r2wars = r_config_get_i (core->config, "cfg.r2wars");
|
|
esil = esil_new_setup (core);
|
|
if (!esil) {
|
|
free (buf);
|
|
return false;
|
|
}
|
|
# define hasNext(x) (x&1) ? (addr<addr_end) : (ops<ops_end)
|
|
|
|
mymemxsr = r_list_new ();
|
|
mymemxsw = r_list_new ();
|
|
esil->user = &stats;
|
|
esil->cb.hook_reg_write = myregwrite;
|
|
esil->cb.hook_reg_read = myregread;
|
|
esil->cb.hook_mem_write = mymemwrite;
|
|
esil->cb.hook_mem_read = mymemread;
|
|
esil->nowrite = true;
|
|
r_cons_break_push (NULL, NULL);
|
|
for (ops = ptr = 0; ptr < buf_sz && hasNext (mode); ops++, ptr += len) {
|
|
if (r_cons_is_breaked ()) {
|
|
break;
|
|
}
|
|
len = r_anal_op (core->anal, &aop, addr + ptr, buf + ptr, buf_sz - ptr, R_ANAL_OP_MASK_ESIL | R_ANAL_OP_MASK_HINT);
|
|
esilstr = R_STRBUF_SAFEGET (&aop.esil);
|
|
if (R_STR_ISNOTEMPTY (esilstr)) {
|
|
if (len < 1) {
|
|
R_LOG_ERROR ("Invalid 0x%08"PFMT64x" instruction %02x %02x",
|
|
addr + ptr, buf[ptr], buf[ptr + 1]);
|
|
break;
|
|
}
|
|
if (cfg_r2wars) {
|
|
if (aop.prefix & R_ANAL_OP_PREFIX_REP) {
|
|
//char * tmp = strstr (esilstr, ",ecx,?{,5,GOTO,}");
|
|
char *tmp = strstr (esilstr, ",0,GOTO");
|
|
if (tmp) {
|
|
tmp[0] = 0;
|
|
}
|
|
}
|
|
}
|
|
r_anal_esil_parse (esil, esilstr);
|
|
r_anal_esil_stack_free (esil);
|
|
}
|
|
r_anal_op_fini (&aop);
|
|
if (len < 1) {
|
|
len = 1;
|
|
}
|
|
}
|
|
r_cons_break_pop ();
|
|
esil->nowrite = false;
|
|
esil->cb.hook_reg_write = NULL;
|
|
esil->cb.hook_reg_read = NULL;
|
|
//esil_fini (core);
|
|
r_anal_esil_free (esil);
|
|
r_reg_arena_pop (core->anal->reg);
|
|
regnow = r_list_newf (free);
|
|
{
|
|
RListIter *iter;
|
|
char *reg;
|
|
r_list_foreach (stats.regs, iter, reg) {
|
|
if (!contains (stats.regwrite, reg)) {
|
|
r_list_push (regnow, strdup (reg));
|
|
}
|
|
}
|
|
}
|
|
if ((mode >> 5) & 1) {
|
|
RListIter *iter;
|
|
AeaMemItem *n;
|
|
int c = 0;
|
|
r_cons_printf ("f-mem.*\n");
|
|
r_list_foreach (mymemxsr, iter, n) {
|
|
r_cons_printf ("f mem.read.%d 0x%08x @ 0x%08"PFMT64x"\n", c++, n->size, n->addr);
|
|
}
|
|
c = 0;
|
|
r_list_foreach (mymemxsw, iter, n) {
|
|
r_cons_printf ("f mem.write.%d 0x%08x @ 0x%08"PFMT64x"\n", c++, n->size, n->addr);
|
|
}
|
|
}
|
|
|
|
/* show registers used */
|
|
if ((mode >> 1) & 1) {
|
|
showregs (stats.regread);
|
|
} else if ((mode >> 2) & 1) {
|
|
showregs (stats.regwrite);
|
|
} else if ((mode >> 3) & 1) {
|
|
showregs (regnow);
|
|
} else if ((mode >> 4) & 1) {
|
|
pj = pj_new ();
|
|
if (!pj) {
|
|
return false;
|
|
}
|
|
pj_o (pj);
|
|
pj_k (pj, "A");
|
|
showregs_json (stats.regs, pj);
|
|
pj_k (pj, "I");
|
|
showregs_json (stats.inputregs, pj);
|
|
pj_k (pj, "R");
|
|
showregs_json (stats.regread, pj);
|
|
pj_k (pj, "W");
|
|
showregs_json (stats.regwrite, pj);
|
|
if (!r_list_empty (stats.regvalues)) {
|
|
pj_k (pj, "V");
|
|
showregs_json (stats.regvalues, pj);
|
|
}
|
|
if (!r_list_empty (regnow)) {
|
|
pj_k (pj, "N");
|
|
showregs_json (regnow, pj);
|
|
}
|
|
if (!r_list_empty (mymemxsr)) {
|
|
pj_k (pj, "@R");
|
|
showmem_json (mymemxsr, pj);
|
|
}
|
|
if (!r_list_empty (mymemxsw)) {
|
|
pj_k (pj, "@W");
|
|
showmem_json (mymemxsw, pj);
|
|
}
|
|
|
|
pj_end (pj);
|
|
r_cons_println (pj_string (pj));
|
|
pj_free (pj);
|
|
} else if ((mode >> 5) & 1) {
|
|
// nothing
|
|
} else {
|
|
if (!r_list_empty (stats.inputregs)) {
|
|
r_cons_printf (" I: ");
|
|
showregs (stats.inputregs);
|
|
}
|
|
if (!r_list_empty (stats.regs)) {
|
|
r_cons_printf (" A: ");
|
|
showregs (stats.regs);
|
|
}
|
|
if (!r_list_empty (stats.regread)) {
|
|
r_cons_printf (" R: ");
|
|
showregs (stats.regread);
|
|
}
|
|
if (!r_list_empty (stats.regwrite)) {
|
|
r_cons_printf (" W: ");
|
|
showregs (stats.regwrite);
|
|
}
|
|
if (!r_list_empty (stats.regvalues)) {
|
|
r_cons_printf (" V: ");
|
|
showregs (stats.regvalues);
|
|
}
|
|
if (!r_list_empty (regnow)) {
|
|
r_cons_printf (" N: ");
|
|
showregs (regnow);
|
|
}
|
|
if (!r_list_empty (mymemxsr)) {
|
|
r_cons_printf ("@R:");
|
|
showmem (mymemxsr);
|
|
}
|
|
if (!r_list_empty (mymemxsw)) {
|
|
r_cons_printf ("@W:");
|
|
showmem (mymemxsw);
|
|
}
|
|
}
|
|
|
|
r_list_free (mymemxsr);
|
|
r_list_free (mymemxsw);
|
|
mymemxsr = NULL;
|
|
mymemxsw = NULL;
|
|
aea_stats_fini (&stats);
|
|
free (buf);
|
|
R_FREE (regnow);
|
|
return true;
|
|
}
|
|
|
|
static void cmd_aespc(RCore *core, ut64 addr, ut64 until_addr, int ninstr) {
|
|
RAnalEsil *esil = core->anal->esil;
|
|
int i, j = 0;
|
|
ut8 *buf;
|
|
RAnalOp aop = {0};
|
|
int ret , bsize = R_MAX (4096, core->blocksize);
|
|
const int mininstrsz = r_anal_archinfo (core->anal, R_ANAL_ARCHINFO_MIN_OP_SIZE);
|
|
const int minopcode = R_MAX (1, mininstrsz);
|
|
const char *pc = r_reg_get_name (core->dbg->reg, R_REG_NAME_PC);
|
|
|
|
// eprintf (" aesB %llx %llx %d\n", addr, until_addr, off); // 0x%08llx %d %s\n", aop.addr, ret, aop.mnemonic);
|
|
if (!esil) {
|
|
R_LOG_DEBUG ("cmd_espc: creating new esil instance");
|
|
core->anal->esil = esil = esil_new_setup (core);
|
|
if (!esil) {
|
|
return;
|
|
}
|
|
}
|
|
buf = malloc (bsize);
|
|
if (!buf) {
|
|
R_LOG_ERROR ("Cannot allocate %d byte(s)", bsize);
|
|
return;
|
|
}
|
|
if (addr == -1) {
|
|
addr = r_reg_getv (core->dbg->reg, pc);
|
|
}
|
|
ut64 cursp = r_reg_getv (core->dbg->reg, "SP");
|
|
ut64 oldoff = core->offset;
|
|
const ut64 flags = R_ANAL_OP_MASK_BASIC | R_ANAL_OP_MASK_HINT | R_ANAL_OP_MASK_ESIL | R_ANAL_OP_MASK_DISASM;
|
|
for (i = 0, j = 0; j < ninstr; i++, j++) {
|
|
if (r_cons_is_breaked ()) {
|
|
break;
|
|
}
|
|
if (i >= (bsize - 32)) {
|
|
i = 0;
|
|
R_LOG_WARN ("Chomp %d of %d", i, bsize);
|
|
}
|
|
if (!i) {
|
|
r_io_read_at (core->io, addr, buf, bsize);
|
|
}
|
|
if (addr == until_addr) {
|
|
break;
|
|
}
|
|
ret = r_anal_op (core->anal, &aop, addr, buf + i, bsize - i, flags);
|
|
if (ret < 1) {
|
|
R_LOG_ERROR ("Failed analysis at 0x%08"PFMT64x, addr);
|
|
break;
|
|
}
|
|
// skip calls and such
|
|
switch (aop.type) {
|
|
case R_ANAL_OP_TYPE_CALL:
|
|
case R_ANAL_OP_TYPE_UCALL:
|
|
case R_ANAL_OP_TYPE_RCALL:
|
|
case R_ANAL_OP_TYPE_ICALL:
|
|
case R_ANAL_OP_TYPE_IRCALL:
|
|
case R_ANAL_OP_TYPE_CCALL:
|
|
case R_ANAL_OP_TYPE_UCCALL:
|
|
// skip
|
|
break;
|
|
default:
|
|
r_reg_setv (core->anal->reg, "PC", aop.addr + aop.size);
|
|
r_reg_setv (core->dbg->reg, "PC", aop.addr + aop.size);
|
|
const char *e = R_STRBUF_SAFEGET (&aop.esil);
|
|
if (e && *e) {
|
|
// eprintf (" 0x%08llx %d %s\n", aop.addr, ret, aop.mnemonic);
|
|
(void)r_anal_esil_parse (esil, e);
|
|
}
|
|
break;
|
|
}
|
|
int inc = (core->search->align > 0)? core->search->align - 1: ret - 1;
|
|
if (inc < 0) {
|
|
inc = minopcode;
|
|
}
|
|
i += inc;
|
|
addr += aop.size;
|
|
r_anal_op_fini (&aop);
|
|
}
|
|
free (buf);
|
|
r_core_seek (core, oldoff, true);
|
|
r_reg_setv (core->dbg->reg, "SP", cursp);
|
|
}
|
|
|
|
static void r_anal_aefa(RCore *core, const char *arg) {
|
|
ut64 to = r_num_math (core->num, arg);
|
|
ut64 at, from = core->offset;
|
|
RAnalFunction *fcn = r_anal_get_fcn_in (core->anal, to, -1);
|
|
if (!from || from == UT64_MAX) {
|
|
if (fcn) {
|
|
from = fcn->addr;
|
|
} else {
|
|
R_LOG_INFO ("Usage: aefa [from] # if no from address is given, uses fcn.addr");
|
|
return;
|
|
}
|
|
}
|
|
R_LOG_INFO ("Emulate from 0x%08"PFMT64x" to 0x%08"PFMT64x, from, to);
|
|
R_LOG_INFO ("Resolve call args for 0x%08"PFMT64x, to);
|
|
|
|
// emulate
|
|
// XXX do not use commands, here, just use the api
|
|
r_core_cmd0 (core, "aeim"); // XXX
|
|
ut64 off = core->offset;
|
|
for (at = from; at < to ; at++) {
|
|
r_core_cmdf (core, "aepc 0x%08"PFMT64x, at);
|
|
r_core_cmd0 (core, "aeso");
|
|
r_core_seek (core, at, true);
|
|
int delta = r_num_get (core->num, "$l");
|
|
if (delta < 1) {
|
|
break;
|
|
}
|
|
at += delta - 1;
|
|
}
|
|
r_core_seek (core, off, true);
|
|
|
|
// the logic of identifying args by function types and
|
|
// show json format and arg name goes into arA
|
|
r_core_cmd0 (core, "arA");
|
|
#if 0
|
|
// get results
|
|
const char *fcn_type = r_type_func_ret (core->anal->sdb_types, fcn->name);
|
|
const char *key = resolve_fcn_name (core->anal, fcn->name);
|
|
RList *list = r_core_get_func_args (core, key);
|
|
if (!r_list_empty (list)) {
|
|
eprintf ("HAS signature\n");
|
|
}
|
|
int i, nargs = 3; // r_type_func_args_count (core->anal->sdb_types, fcn->name);
|
|
if (nargs > 0) {
|
|
int i;
|
|
eprintf ("NARGS %d (%s)\n", nargs, key);
|
|
for (i = 0; i < nargs; i++) {
|
|
ut64 v = r_debug_arg_get (core->dbg, "reg", i);
|
|
eprintf ("arg: 0x%08"PFMT64x"\n", v);
|
|
}
|
|
}
|
|
#endif
|
|
}
|
|
|
|
static void __core_anal_appcall(RCore *core, const char *input) {
|
|
// r_reg_arena_push (core->dbg->reg);
|
|
RListIter *iter;
|
|
char *arg;
|
|
char *inp = strdup (input);
|
|
RList *args = r_str_split_list (inp, " ", 0);
|
|
int i = 0;
|
|
r_list_foreach (args, iter, arg) {
|
|
r_strf_var (alias, 32, "A%d", i);
|
|
r_reg_setv (core->anal->reg, alias, r_num_math (core->num, arg));
|
|
i++;
|
|
}
|
|
ut64 sp = r_reg_getv (core->anal->reg, "SP");
|
|
r_reg_setv (core->anal->reg, "SP", 0);
|
|
|
|
r_reg_setv (core->anal->reg, "PC", core->offset);
|
|
r_core_cmd0 (core, "aesu 0");
|
|
|
|
r_reg_setv (core->anal->reg, "SP", sp);
|
|
free (inp);
|
|
|
|
// r_reg_arena_pop (core->dbg->reg);
|
|
}
|
|
|
|
static void cmd_debug_stack_init(RCore *core, int argc, char **argv, char **envp) {
|
|
// TODO: add support for 32 bit
|
|
RBuffer *b = r_buf_new ();
|
|
if (!b) {
|
|
return;
|
|
}
|
|
ut64 sp = core->offset;
|
|
int i;
|
|
ut64 dyld_call_from = UT64_MAX;
|
|
r_buf_append_ut64 (b, dyld_call_from);
|
|
r_buf_append_ut64 (b, 0); // rbp
|
|
r_buf_append_ut64 (b, argc); // rbp
|
|
int envp_count = 0;
|
|
for (i = 0; envp[i]; i++) {
|
|
envp_count++;
|
|
}
|
|
ut64 strp = sp + 40 + (argc * 8) + (envp_count * 8);
|
|
// pointer table
|
|
for (i = 0; i < argc && argv[i]; i++) {
|
|
r_buf_append_ut64 (b, strp);
|
|
strp += strlen (argv[i]) + 1;
|
|
}
|
|
r_buf_append_ut64 (b, 0);
|
|
for (i = 0; i < envp_count; i++) {
|
|
r_buf_append_ut64 (b, strp);
|
|
strp += strlen (envp[i]) + 1;
|
|
}
|
|
r_buf_append_ut64 (b, 0);
|
|
// string table
|
|
for (i = 0; i < argc && argv[i]; i++) {
|
|
r_buf_append_string (b, argv[i]);
|
|
r_buf_append_ut8 (b, 0);
|
|
}
|
|
for (i = 0; i < envp_count; i++) {
|
|
r_buf_append_string (b, envp[i]);
|
|
r_buf_append_ut8 (b, 0);
|
|
}
|
|
int slen = 0;
|
|
ut8 *s = r_buf_read_all (b, &slen);
|
|
char *x = r_hex_bin2strdup (s, slen);
|
|
r_cons_printf ("wx %s\n", x);
|
|
free (x);
|
|
free (s);
|
|
r_buf_free (b);
|
|
}
|
|
|
|
R_IPI int core_type_by_addr(RCore *core, ut64 addr) {
|
|
const RList *list = r_flag_get_list (core->flags, addr);
|
|
RListIter *iter;
|
|
RFlagItem *item;
|
|
bool has_flag = false;
|
|
int type = R_ANAL_REF_TYPE_DATA;
|
|
r_list_foreach (list, iter, item) {
|
|
if (item && strchr (item->name, '.')) {
|
|
if (r_str_startswith (item->name, "str")) {
|
|
type = R_ANAL_REF_TYPE_STRING;
|
|
}
|
|
//// r_anal_xrefs_set (core->anal, *val, esil->address, type);
|
|
has_flag = true;
|
|
}
|
|
}
|
|
if (!has_flag) {
|
|
return -1;
|
|
}
|
|
return type;
|
|
}
|
|
|
|
static bool regwrite_hook(RAnalEsil *esil, const char *name, ut64 *val) {
|
|
RCore *core = esil->user;
|
|
int type = core_type_by_addr (core, *val);
|
|
if (type != -1) {
|
|
r_anal_xrefs_set (core->anal, esil->address, *val, type);
|
|
}
|
|
return false;
|
|
}
|
|
|
|
static void __anal_esil_function(RCore *core, ut64 addr) {
|
|
RListIter *iter;
|
|
RAnalBlock *bb;
|
|
if (!core->anal->esil) {
|
|
r_core_cmd0 (core, "aei");
|
|
r_core_cmd0 (core, "aeim");
|
|
}
|
|
void *u = core->anal->esil->user;
|
|
core->anal->esil->user = core;
|
|
void *p = core->anal->esil->cb.hook_reg_write;
|
|
core->anal->esil->cb.hook_reg_write = regwrite_hook;
|
|
RAnalFunction *fcn = r_anal_get_fcn_in (core->anal,
|
|
addr, R_ANAL_FCN_TYPE_FCN | R_ANAL_FCN_TYPE_SYM);
|
|
const char *pcname = r_reg_get_name (core->anal->reg, R_REG_NAME_PC);
|
|
const ut64 old_pc = r_reg_getv (core->anal->reg, pcname);
|
|
if (fcn) {
|
|
bool anal_verbose = r_config_get_b (core->config, "anal.verbose");
|
|
// emulate every instruction in the function recursively across all the basic blocks
|
|
r_list_foreach (fcn->bbs, iter, bb) {
|
|
ut64 pc = bb->addr;
|
|
ut64 end = bb->addr + bb->size;
|
|
RAnalOp op;
|
|
int ret, bbs = end - pc;
|
|
if (bbs < 1 || bbs > 0xfffff || pc >= end) {
|
|
R_LOG_ERROR ("Invalid block size");
|
|
continue;
|
|
}
|
|
// eprintf ("[*] Emulating 0x%08"PFMT64x" basic block 0x%08" PFMT64x " - 0x%08" PFMT64x "\r[", fcn->addr, pc, end);
|
|
ut8 *buf = calloc (1, bbs + 1);
|
|
if (!buf) {
|
|
break;
|
|
}
|
|
// const char *pcname = r_reg_get_name (core->dbg->reg, R_REG_NAME_PC);
|
|
r_io_read_at (core->io, pc, buf, bbs);
|
|
int left;
|
|
while (pc < end) {
|
|
left = R_MIN (end - pc, 32);
|
|
// r_asm_set_pc (core->rasm, pc);
|
|
ret = r_anal_op (core->anal, &op, pc, buf + pc - bb->addr, left, R_ANAL_OP_MASK_HINT | R_ANAL_OP_MASK_ESIL| R_ANAL_OP_MASK_DISASM); // read overflow
|
|
if (ret) {
|
|
bool opskip = false;
|
|
#if 0
|
|
switch (op.type) {
|
|
case R_ANAL_OP_TYPE_JMP:
|
|
case R_ANAL_OP_TYPE_NOP:
|
|
case R_ANAL_OP_TYPE_CJMP:
|
|
case R_ANAL_OP_TYPE_UJMP:
|
|
case R_ANAL_OP_TYPE_CALL:
|
|
case R_ANAL_OP_TYPE_RET:
|
|
opskip = true;
|
|
break;
|
|
}
|
|
#endif
|
|
if (!opskip) {
|
|
const char *esilstr = R_STRBUF_SAFEGET (&op.esil);
|
|
// eprintf ("0x%08"PFMT64x" %s\n", pc, op.mnemonic);
|
|
if (R_STR_ISNOTEMPTY (esilstr)) {
|
|
r_reg_setv (core->anal->reg, pcname, pc + op.size);
|
|
r_anal_esil_set_pc (core->anal->esil, pc);
|
|
r_anal_esil_parse (core->anal->esil, esilstr);
|
|
if (anal_verbose) {
|
|
r_anal_esil_dumpstack (core->anal->esil);
|
|
}
|
|
r_anal_esil_stack_free (core->anal->esil);
|
|
}
|
|
}
|
|
pc += op.size;
|
|
} else {
|
|
pc += 4; // XXX
|
|
}
|
|
r_anal_op_fini (&op);
|
|
}
|
|
free (buf);
|
|
}
|
|
} else {
|
|
R_LOG_ERROR ("Cannot find function at 0x%08" PFMT64x, addr);
|
|
}
|
|
core->anal->esil->cb.hook_reg_write = p;
|
|
core->anal->esil->user = u;
|
|
r_reg_setv (core->anal->reg, pcname, old_pc);
|
|
#if 0
|
|
r_anal_esil_free (core->anal->esil);
|
|
core->anal->esil = NULL;
|
|
#endif
|
|
}
|
|
|
|
static void print_esil_dfg_as_commands(RCore *core, RAnalEsilDFG *dfg) {
|
|
RListIter *iter, *ator;
|
|
RGraphNode *node, *edon;
|
|
RStrBuf *sb = r_strbuf_new ("");
|
|
if (!sb) {
|
|
return;
|
|
}
|
|
r_cons_println ("ag-");
|
|
r_list_foreach (r_graph_get_nodes (dfg->flow), iter, node) {
|
|
const RAnalEsilDFGNode *enode = (RAnalEsilDFGNode *)node->data;
|
|
char *esc_str = r_str_escape (r_strbuf_get (enode->content));
|
|
if (!esc_str) {
|
|
r_strbuf_free (sb);
|
|
return;
|
|
}
|
|
r_strbuf_set (sb, esc_str);
|
|
if (enode->type == R_ANAL_ESIL_DFG_BLOCK_GENERATIVE) {
|
|
r_strbuf_prepend (sb, "generative:");
|
|
}
|
|
char *b64_buf = r_base64_encode_dyn (r_strbuf_get (sb), sb->len);
|
|
if (!b64_buf) {
|
|
r_strbuf_free (sb);
|
|
free (esc_str);
|
|
return;
|
|
}
|
|
|
|
r_cons_printf ("agn %d base64:%s\n", enode->idx, b64_buf);
|
|
free (b64_buf);
|
|
free (esc_str);
|
|
}
|
|
r_strbuf_free (sb);
|
|
|
|
r_list_foreach (r_graph_get_nodes (dfg->flow), iter, node) {
|
|
const RAnalEsilDFGNode *enode = (RAnalEsilDFGNode *)node->data;
|
|
r_list_foreach (r_graph_get_neighbours (dfg->flow, node), ator, edon) {
|
|
const RAnalEsilDFGNode *edone = (RAnalEsilDFGNode *)edon->data;
|
|
r_cons_printf ("age %d %d\n", enode->idx, edone->idx);
|
|
}
|
|
}
|
|
}
|
|
|
|
static void cmd_aeg(RCore *core, int argc, char *argv[]) {
|
|
r_return_if_fail (core && argc >= 0 && argv);
|
|
if (argc == 0) {
|
|
r_core_cmd0 (core, ".aeg*;agg");
|
|
return;
|
|
}
|
|
if ((argc == 1) && !argv[0][0]) { // "aeg"
|
|
RAnalEsilDFG *dfg = r_anal_esil_dfg_expr (core->anal, NULL, argv[0]);
|
|
r_return_if_fail (dfg);
|
|
print_esil_dfg_as_commands (core, dfg);
|
|
r_anal_esil_dfg_free (dfg);
|
|
return;
|
|
}
|
|
switch (argv[0][0]) {
|
|
case '*': // "aeg*"
|
|
{
|
|
RAnalOp *aop = r_core_anal_op (core, core->offset, R_ANAL_OP_MASK_ESIL);
|
|
if (!aop) {
|
|
return;
|
|
}
|
|
const char *esilstr = r_strbuf_get (&aop->esil);
|
|
if (R_STR_ISNOTEMPTY (esilstr)) {
|
|
RAnalEsilDFG *dfg = r_anal_esil_dfg_expr (core->anal, NULL, esilstr);
|
|
if (!dfg) {
|
|
r_anal_op_free (aop);
|
|
return;
|
|
}
|
|
print_esil_dfg_as_commands (core, dfg);
|
|
r_anal_esil_dfg_free (dfg);
|
|
}
|
|
}
|
|
break;
|
|
case 'i': // "aegi"
|
|
case 'v': // "aegv"
|
|
{
|
|
RConfigHold *hc = r_config_hold_new (core->config);
|
|
if (!hc) {
|
|
return;
|
|
}
|
|
r_config_hold (hc, "cmd.gprompt", NULL);
|
|
r_config_set (core->config, "cmd.gprompt", "pi 1");
|
|
r_core_cmd0 (core, ".aeg*;aggv");
|
|
r_config_hold_free (hc);
|
|
}
|
|
break;
|
|
case 'f': // "aegf"
|
|
{
|
|
RStrBuf *filtered = r_anal_esil_dfg_filter_expr (core->anal, argv[1], argv[2]);
|
|
if (filtered) {
|
|
r_cons_printf ("%s\n", r_strbuf_get (filtered));
|
|
r_strbuf_free (filtered);
|
|
}
|
|
}
|
|
break;
|
|
case 'c': // "aegc"
|
|
{
|
|
RAnalEsilDFG *dfg = r_anal_esil_dfg_expr (core->anal, NULL, argv[1]);
|
|
if (!dfg) {
|
|
return;
|
|
}
|
|
r_anal_esil_dfg_fold_const (core->anal, dfg);
|
|
if (argv[0][1] == 'f') { // "aegcf"
|
|
RStrBuf *filtered = r_anal_esil_dfg_filter (dfg, argv[2]);
|
|
if (filtered) {
|
|
r_cons_printf ("%s\n", r_strbuf_get (filtered));
|
|
r_strbuf_free (filtered);
|
|
}
|
|
} else {
|
|
print_esil_dfg_as_commands (core, dfg);
|
|
}
|
|
r_anal_esil_dfg_free (dfg);
|
|
}
|
|
break;
|
|
case '?': // "aeg?"
|
|
default:
|
|
r_core_cmd_help (core, help_msg_aeg);
|
|
break;
|
|
}
|
|
}
|
|
|
|
static void cmd_anal_esil(RCore *core, const char *input, bool verbose) {
|
|
RAnalEsil *esil = core->anal->esil;
|
|
ut64 addr = core->offset;
|
|
ut64 adr ;
|
|
char *n, *n1;
|
|
int off;
|
|
ut64 until_addr = UT64_MAX;
|
|
|
|
const char *until_expr = NULL;
|
|
RAnalOp *op = NULL;
|
|
|
|
switch (input[0]) {
|
|
case 'v': // "aev"
|
|
r_core_visual_esil (core, r_str_trim_head_ro (input + 1));
|
|
break;
|
|
case 'p': // "aep"
|
|
switch (input[1]) {
|
|
case 'a': // "aepa"
|
|
{
|
|
ut64 at = core->offset;
|
|
if (input[2] == ' ') {
|
|
at = r_num_math (core->num, input + 2);
|
|
}
|
|
// get flag in current offset
|
|
// find a pin named like the flag, skip dots if any
|
|
RFlagItem *f = r_flag_get_by_spaces (core->flags, at, R_FLAGS_FS_SYMBOLS, R_FLAGS_FS_IMPORTS, NULL);
|
|
if (!f) {
|
|
f = r_flag_get_i (core->flags, at);
|
|
}
|
|
if (f) {
|
|
const char *last = r_str_rchr (f->name, NULL, '.');
|
|
const char *pin_name = last? last + 1: f->name;
|
|
const char *havepin = r_anal_pin_get (core->anal, pin_name);
|
|
if (havepin) {
|
|
r_core_cmdf (core, "aep %s @ 0x%08" PFMT64x, pin_name, at);
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
case '.': // "aep."
|
|
{
|
|
const char *n = r_anal_pin_at (core->anal, core->offset);
|
|
if (R_STR_ISNOTEMPTY (n)) {
|
|
r_cons_printf ("%s\n", n);
|
|
}
|
|
}
|
|
break;
|
|
case 'c': // "aepc"
|
|
if (input[2] == ' ' || input[2] == '=') {
|
|
// seek to this address
|
|
reg_name_roll_set (core, "PC", r_num_math (core->num, input + 3));
|
|
r_core_cmd0 (core, ".ar*");
|
|
} else {
|
|
eprintf ("Usage: aepc [address] # same as 'ar PC=..'\n");
|
|
}
|
|
break;
|
|
case 'k':
|
|
{
|
|
char *out = sdb_querys (core->anal->sdb_pins, NULL, 0, r_str_trim_head_ro (input + 3));
|
|
if (out) {
|
|
r_cons_printf ("%s\n", out);
|
|
free (out);
|
|
}
|
|
}
|
|
break;
|
|
case '*':
|
|
case 0:
|
|
r_anal_pin_list (core->anal);
|
|
break;
|
|
case '-':
|
|
if (input[2] == '*') {
|
|
r_anal_pin_init (core->anal);
|
|
} else {
|
|
if (input[2]) {
|
|
addr = r_num_math (core->num, input + 2);
|
|
}
|
|
r_anal_pin_unset (core->anal, addr);
|
|
}
|
|
break;
|
|
case ' ':
|
|
r_anal_pin (core->anal, addr, input + 2);
|
|
break;
|
|
default:
|
|
r_core_cmd_help (core, help_msg_aep);
|
|
break;
|
|
}
|
|
break;
|
|
case 'r': // "aer"
|
|
// 'aer' is an alias for 'ar'
|
|
cmd_anal_reg (core, input + 1);
|
|
break;
|
|
case '*':
|
|
// XXX: this is wip, not working atm
|
|
if (core->anal->esil) {
|
|
r_cons_printf ("trap: %d\n", core->anal->esil->trap);
|
|
r_cons_printf ("trap-code: %d\n", core->anal->esil->trap_code);
|
|
} else {
|
|
R_LOG_WARN ("esil vm is not initialized. Please run `aei`");
|
|
}
|
|
break;
|
|
case ' ':
|
|
case 'q':
|
|
//r_anal_esil_eval (core->anal, input + 1);
|
|
if (!esil) {
|
|
core->anal->esil = esil = esil_new_setup (core);
|
|
if (!esil) {
|
|
return;
|
|
}
|
|
}
|
|
r_anal_esil_set_pc (esil, core->offset);
|
|
r_anal_esil_parse (esil, input + 1);
|
|
if (verbose && *input != 'q') {
|
|
r_anal_esil_dumpstack (esil);
|
|
}
|
|
r_anal_esil_stack_free (esil);
|
|
break;
|
|
case 's': // "aes"
|
|
// "aes" "aeso" "aesu" "aesue"
|
|
// aes -> single step
|
|
// aesb -> single step back
|
|
// aeso -> single step over
|
|
// aesu -> until address
|
|
// aesue -> until esil expression
|
|
switch (input[1]) {
|
|
case '?':
|
|
r_core_cmd_help (core, help_msg_aes);
|
|
break;
|
|
case 'l': // "aesl"
|
|
{
|
|
ut64 pc = r_debug_reg_get (core->dbg, "PC");
|
|
RAnalOp *op = r_core_anal_op (core, pc, R_ANAL_OP_MASK_BASIC | R_ANAL_OP_MASK_HINT);
|
|
// TODO: honor hint
|
|
if (!op) {
|
|
break;
|
|
}
|
|
r_core_esil_step (core, UT64_MAX, NULL, NULL, false);
|
|
r_debug_reg_set (core->dbg, "PC", pc + op->size);
|
|
r_anal_esil_set_pc (esil, pc + op->size);
|
|
r_core_cmd0 (core, ".ar*");
|
|
r_anal_op_free (op);
|
|
} break;
|
|
case 'b': // "aesb"
|
|
if (!r_core_esil_step_back (core)) {
|
|
R_LOG_ERROR ("Cannot step back");
|
|
}
|
|
r_core_cmd0 (core, ".ar*");
|
|
break;
|
|
case 'B': // "aesB"
|
|
{
|
|
n = strchr (input + 2, ' ');
|
|
char *n2 = NULL;
|
|
if (n) {
|
|
n = (char *)r_str_trim_head_ro (n + 1);
|
|
}
|
|
if (n) {
|
|
n2 = strchr (n, ' ');
|
|
if (n2) {
|
|
*n2++ = 0;
|
|
}
|
|
ut64 off = r_num_math (core->num, n);
|
|
ut64 nth = n2? r_num_math (core->num, n2): 1;
|
|
cmd_aespc (core, core->offset, off, (int)nth);
|
|
} else {
|
|
eprintf ("Usage: aesB [until-addr] [nth-opcodes] @ [from-addr]\n");
|
|
}
|
|
}
|
|
break;
|
|
case 'u': // "aesu"
|
|
until_expr = NULL;
|
|
until_addr = UT64_MAX;
|
|
if (r_str_endswith (input, "?")) {
|
|
r_core_cmd0 (core, "aes?~aesu");
|
|
} else switch (input[2]) {
|
|
case 'e': // "aesue"
|
|
until_expr = input + 3;
|
|
break;
|
|
case ' ': // "aesu"
|
|
until_addr = r_num_math (core->num, input + 2);
|
|
break;
|
|
case 'o': // "aesuo"
|
|
step_until_optype (core, r_str_trim_head_ro (input + 3));
|
|
break;
|
|
default:
|
|
r_core_cmd0 (core, "aes?~aesu");
|
|
break;
|
|
}
|
|
if (until_expr || until_addr != UT64_MAX) {
|
|
r_core_esil_step (core, until_addr, until_expr, NULL, false);
|
|
}
|
|
r_core_cmd0 (core, ".ar*");
|
|
break;
|
|
case 's': // "aess"
|
|
if (input[2] == 'u') { // "aessu"
|
|
if (input[3] == 'e') {
|
|
until_expr = input + 3;
|
|
} else {
|
|
until_addr = r_num_math (core->num, input + 2);
|
|
}
|
|
r_core_esil_step (core, until_addr, until_expr, NULL, true);
|
|
} else {
|
|
r_core_esil_step (core, UT64_MAX, NULL, NULL, true);
|
|
}
|
|
r_core_cmd0 (core, ".ar*");
|
|
break;
|
|
case 'o': // "aeso"
|
|
if (input[2] == 'u') { // "aesou"
|
|
if (input[3] == 'e') {
|
|
until_expr = input + 3;
|
|
} else {
|
|
until_addr = r_num_math (core->num, input + 2);
|
|
}
|
|
r_core_esil_step (core, until_addr, until_expr, NULL, true);
|
|
r_core_cmd0 (core, ".ar*");
|
|
} else if (!input[2] || input[2] == ' ') { // "aeso [addr]"
|
|
// step over
|
|
op = r_core_anal_op (core, r_reg_getv (core->anal->reg,
|
|
r_reg_get_name (core->anal->reg, R_REG_NAME_PC)), R_ANAL_OP_MASK_BASIC | R_ANAL_OP_MASK_HINT);
|
|
if (op && op->type == R_ANAL_OP_TYPE_CALL) {
|
|
until_addr = op->addr + op->size;
|
|
}
|
|
r_core_esil_step (core, until_addr, until_expr, NULL, false);
|
|
r_anal_op_free (op);
|
|
r_core_cmd0 (core, ".ar*");
|
|
} else {
|
|
eprintf ("Usage: aesou [addr] # step over until given address\n");
|
|
}
|
|
break;
|
|
case 'p': //"aesp"
|
|
n = strchr (input, ' ');
|
|
n1 = R_STR_ISNOTEMPTY (n) ? strchr (n + 1, ' ') : NULL;
|
|
if ((!n || !n1) || (!*n || !*n1)) {
|
|
eprintf ("aesp [offset] [num]\n");
|
|
break;
|
|
}
|
|
adr = R_STR_ISNOTEMPTY (n)? r_num_math (core->num, n + 1): 0;
|
|
off = R_STR_ISNOTEMPTY (n1)? r_num_math (core->num, n1 + 1): 0;
|
|
cmd_aespc (core, adr, -1, off);
|
|
break;
|
|
case ' ':
|
|
n = strchr (input, ' ');
|
|
n1 = n ? n + 1: NULL;
|
|
if (!n1 || !*n1) {
|
|
r_core_esil_step (core, until_addr, until_expr, NULL, false);
|
|
break;
|
|
}
|
|
off = r_num_math (core->num, n1);
|
|
cmd_aespc (core, -1, -1, off);
|
|
break;
|
|
default:
|
|
r_core_esil_step (core, until_addr, until_expr, NULL, false);
|
|
r_core_cmd0 (core, ".ar*");
|
|
break;
|
|
}
|
|
break;
|
|
case 'C': // "aeC"
|
|
if (input[1] == '?') { // "aec?"
|
|
r_core_cmd_help (core, help_msg_aeC);
|
|
} else {
|
|
__core_anal_appcall (core, r_str_trim_head_ro (input + 1));
|
|
}
|
|
break;
|
|
case 'c': // "aec"
|
|
if (input[1] == '?') { // "aec?"
|
|
r_core_cmd_help (core, help_msg_aec);
|
|
} else if (input[1] == 's') { // "aecs"
|
|
st64 maxsteps = r_config_get_i (core->config, "esil.maxsteps");
|
|
ut64 countsteps = 0;
|
|
const char *pc = r_reg_get_name (core->anal->reg, R_REG_NAME_PC);
|
|
for (; !maxsteps || countsteps < maxsteps; countsteps++) {
|
|
// ignore return value is not an error, should 0, 1, -1 imho
|
|
(void)r_core_esil_step (core, UT64_MAX, NULL, NULL, false);
|
|
r_core_cmd0 (core, ".ar*");
|
|
addr = r_reg_getv (core->anal->reg, pc);
|
|
op = r_core_anal_op (core, addr, R_ANAL_OP_MASK_BASIC | R_ANAL_OP_MASK_HINT);
|
|
if (!op) {
|
|
R_LOG_ERROR ("invalid instruction at 0x%08" PFMT64x, addr);
|
|
break;
|
|
}
|
|
if (op->type == R_ANAL_OP_TYPE_SWI) {
|
|
R_LOG_INFO ("syscall instruction at 0x%08" PFMT64x, addr);
|
|
break;
|
|
} else if (op->type == R_ANAL_OP_TYPE_TRAP) {
|
|
R_LOG_INFO ("trap instruction at 0x%08" PFMT64x, addr);
|
|
break;
|
|
}
|
|
r_anal_op_free (op);
|
|
op = NULL;
|
|
if (core->anal->esil->trap || core->anal->esil->trap_code) {
|
|
R_LOG_INFO ("esil trap '%s' (%d) at 0x%08" PFMT64x,
|
|
r_anal_esil_trapstr (core->anal->esil->trap),
|
|
core->anal->esil->trap_code, addr);
|
|
break;
|
|
}
|
|
}
|
|
if (op) {
|
|
r_anal_op_free (op);
|
|
op = NULL;
|
|
}
|
|
} else if (input[1] == 'c') { // "aecc"
|
|
const char *pc = r_reg_get_name (core->anal->reg, R_REG_NAME_PC);
|
|
st64 maxsteps = r_config_get_i (core->config, "esil.maxsteps");
|
|
ut64 countsteps = 0;
|
|
for (; !maxsteps || countsteps < maxsteps; countsteps++) {
|
|
(void)r_core_esil_step (core, UT64_MAX, NULL, NULL, false);
|
|
r_core_cmd0 (core, ".ar*");
|
|
addr = r_num_get (core->num, pc);
|
|
op = r_core_anal_op (core, addr, R_ANAL_OP_MASK_BASIC);
|
|
if (!op) {
|
|
break;
|
|
}
|
|
if (op->type == R_ANAL_OP_TYPE_CALL || op->type == R_ANAL_OP_TYPE_UCALL) {
|
|
eprintf ("stop in call instruction at 0x%08" PFMT64x "\n", addr);
|
|
break;
|
|
}
|
|
r_anal_op_free (op);
|
|
op = NULL;
|
|
if (core->anal->esil->trap || core->anal->esil->trap_code) {
|
|
break;
|
|
}
|
|
}
|
|
if (op) {
|
|
r_anal_op_free (op);
|
|
}
|
|
} else {
|
|
// "aec" -> continue until ^C
|
|
// "aecu" -> until address
|
|
// "aecue" -> until esil expression
|
|
if (input[1] == 'u' && input[2] == 'e') {
|
|
until_expr = input + 3;
|
|
} else if (input[1] == 'u') {
|
|
until_addr = r_num_math (core->num, input + 2);
|
|
} else {
|
|
until_expr = "0";
|
|
}
|
|
r_core_esil_step (core, until_addr, until_expr, NULL, false);
|
|
r_core_cmd0 (core, ".ar*");
|
|
}
|
|
break;
|
|
case 'i': // "aei"
|
|
switch (input[1]) {
|
|
case 's': // "aeis"
|
|
{
|
|
char *arg = r_str_trim_dup (input + 2);
|
|
RList *args = r_str_split_list (arg, " ", 0);
|
|
int i, argc = atoi (r_list_pop_head (args));
|
|
if (argc < 1) {
|
|
r_core_cmd_help (core, help_msg_aei);
|
|
break;
|
|
}
|
|
char **argv = calloc (argc + 1, sizeof (void *));
|
|
for (i = 0; i < argc; i++) {
|
|
char *arg = r_list_pop_head (args);
|
|
if (!arg) {
|
|
break;
|
|
}
|
|
argv[i] = arg;
|
|
}
|
|
argv[i] = 0;
|
|
char **envp = calloc (r_list_length (args) + 1, sizeof (void *));
|
|
for (i = 0; ; i++) {
|
|
char *arg = r_list_pop_head (args);
|
|
if (!arg) {
|
|
break;
|
|
}
|
|
envp[i] = arg;
|
|
}
|
|
envp[i] = 0;
|
|
#if __UNIX__
|
|
if (strstr (input, "$env")) {
|
|
extern char **environ;
|
|
cmd_debug_stack_init (core, argc, argv, environ);
|
|
} else {
|
|
cmd_debug_stack_init (core, argc, argv, envp);
|
|
}
|
|
#else
|
|
cmd_debug_stack_init (core, argc, argv, envp);
|
|
#endif
|
|
free (arg);
|
|
}
|
|
break;
|
|
case 'm': // "aeim"
|
|
cmd_esil_mem (core, input + 2);
|
|
break;
|
|
case 'p': // "aeip" // initialize pc = $$
|
|
reg_name_roll_set (core, "PC", core->offset);
|
|
break;
|
|
case '?':
|
|
r_core_cmd_help (core, help_msg_aei);
|
|
cmd_esil_mem (core, "?");
|
|
break;
|
|
case '-':
|
|
if (esil) {
|
|
sdb_reset (esil->stats);
|
|
}
|
|
r_anal_esil_free (esil);
|
|
core->anal->esil = NULL;
|
|
break;
|
|
case 0: //lolololol
|
|
r_anal_esil_free (esil);
|
|
esil = core->anal->esil = esil_new_setup (core);
|
|
if (!esil) {
|
|
return;
|
|
}
|
|
// reinitialize
|
|
{
|
|
const char *pc = r_reg_get_name (core->anal->reg, R_REG_NAME_PC);
|
|
if (pc && r_reg_getv (core->anal->reg, pc) == 0LL) {
|
|
reg_name_roll_set (core, "PC", core->offset);
|
|
}
|
|
}
|
|
/* restore user settings for interrupt handling */
|
|
{
|
|
const char *s = r_config_get (core->config, "cmd.esil.intr");
|
|
if (s) {
|
|
char *my = strdup (s);
|
|
if (my) {
|
|
r_config_set (core->config, "cmd.esil.intr", my);
|
|
free (my);
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
default:
|
|
cmd_esil_mem (core, "?");
|
|
break;
|
|
|
|
}
|
|
break;
|
|
case 'k': // "aek"
|
|
switch (input[1]) {
|
|
case '\0':
|
|
input = "*";
|
|
/* fall through */
|
|
case ' ':
|
|
if (esil && esil->stats) {
|
|
char *out = sdb_querys (esil->stats, NULL, 0, input + 2);
|
|
if (out) {
|
|
r_cons_println (out);
|
|
free (out);
|
|
}
|
|
} else {
|
|
eprintf ("esil.stats is empty. Run 'aei'\n");
|
|
}
|
|
break;
|
|
case '-':
|
|
if (esil) {
|
|
sdb_reset (esil->stats);
|
|
}
|
|
break;
|
|
default:
|
|
r_core_cmd_help (core, help_msg_aek);
|
|
break;
|
|
}
|
|
break;
|
|
case 'L': // aeL commands
|
|
{
|
|
RAnalEsilPlugin *p;
|
|
RListIter *iter;
|
|
r_list_foreach (core->anal->esil_plugins, iter, p) {
|
|
r_cons_printf ("%s\n", p->name);
|
|
}
|
|
}
|
|
break;
|
|
case 'g': // "aeg"
|
|
{
|
|
int argc;
|
|
char **argv = r_str_argv (&input[1], &argc);
|
|
r_return_if_fail (argv);
|
|
cmd_aeg (core, argc, argv);
|
|
int i;
|
|
for (i = 0; i < argc; i++) {
|
|
free (argv[i]);
|
|
}
|
|
free (argv);
|
|
}
|
|
break;
|
|
case 'b': // "aeb"
|
|
{
|
|
ut64 addr = r_num_math (core->num, input + 1);
|
|
if (!addr || addr == UT64_MAX) {
|
|
addr = core->offset;
|
|
}
|
|
RAnalFunction *fcn = r_anal_get_fcn_in (core->anal, addr, R_ANAL_FCN_TYPE_NULL);
|
|
if (fcn) {
|
|
RAnalBlock *bb = r_anal_function_bbget_in (core->anal, fcn, addr);
|
|
if (bb) {
|
|
cmd_aespc (core, bb->addr, bb->addr + bb->size, bb->ninstr);
|
|
// r_core_cmdf (core, "aesp `ab~addr[1]` `ab~ninstr[1]`");
|
|
} else {
|
|
eprintf ("No basic block in this address\n");
|
|
}
|
|
} else {
|
|
eprintf ("No function in this address\n");
|
|
}
|
|
// ab~ninstr[1]
|
|
}
|
|
break;
|
|
case 'f': // "aef"
|
|
if (input[1] == 'a') { // "aefa"
|
|
r_anal_aefa (core, r_str_trim_head_ro (input + 2));
|
|
} else { // This should be aefb -> because its emulating all the bbs
|
|
// anal ESIL to REIL.
|
|
__anal_esil_function (core, core->offset);
|
|
} break;
|
|
case 't': // "aet"
|
|
switch (input[1]) {
|
|
case 's': // "aets"
|
|
switch (input[2]) {
|
|
case '+': // "aets+"
|
|
if (!esil) {
|
|
R_LOG_ERROR ("ESIL is not initialized. Use `aeim` first");
|
|
break;
|
|
}
|
|
if (esil->trace) {
|
|
R_LOG_INFO ("ESIL trace already started");
|
|
break;
|
|
}
|
|
esil->trace = r_anal_esil_trace_new (esil);
|
|
if (!esil->trace) {
|
|
break;
|
|
}
|
|
r_config_set_b (core->config, "dbg.trace", true);
|
|
break;
|
|
case '-': // "aets-"
|
|
if (!esil) {
|
|
R_LOG_ERROR ("ESIL is not initialized. Use `aeim` first");
|
|
break;
|
|
}
|
|
if (!esil->trace) {
|
|
eprintf ("No ESIL trace started\n");
|
|
break;
|
|
}
|
|
r_anal_esil_trace_free (esil->trace);
|
|
esil->trace = NULL;
|
|
r_config_set_b (core->config, "dbg.trace", false);
|
|
break;
|
|
default:
|
|
r_core_cmd_help (core, help_msg_aets);
|
|
break;
|
|
}
|
|
break;
|
|
default:
|
|
eprintf ("Unknown command. Use `aetr`.\n");
|
|
break;
|
|
}
|
|
break;
|
|
case 'A': // "aeA"
|
|
switch (input[1]) {
|
|
case '?':
|
|
r_core_cmd_help (core, help_msg_aea);
|
|
break;
|
|
case 'r':
|
|
cmd_aea (core, 1 + (1<<1), core->offset, r_num_math (core->num, input+2));
|
|
break;
|
|
case 'w':
|
|
cmd_aea (core, 1 + (1<<2), core->offset, r_num_math (core->num, input+2));
|
|
break;
|
|
case 'n':
|
|
cmd_aea (core, 1 + (1<<3), core->offset, r_num_math (core->num, input+2));
|
|
break;
|
|
case 'j':
|
|
cmd_aea (core, 1 + (1<<4), core->offset, r_num_math (core->num, input+2));
|
|
break;
|
|
case '*':
|
|
cmd_aea (core, 1 + (1<<5), core->offset, r_num_math (core->num, input+2));
|
|
break;
|
|
case 'f': {
|
|
RAnalFunction *fcn = r_anal_get_fcn_in (core->anal, core->offset, -1);
|
|
if (fcn) {
|
|
cmd_aea (core, 1, r_anal_function_min_addr (fcn), r_anal_function_linear_size (fcn));
|
|
}
|
|
break;
|
|
}
|
|
default:
|
|
cmd_aea (core, 1, core->offset, (int)r_num_math (core->num, input[1]? input + 2:input + 1));
|
|
}
|
|
break;
|
|
case 'a': { // "aea"
|
|
RReg *reg = core->anal->reg;
|
|
ut64 pc = r_reg_getv (reg, "PC");
|
|
RAnalOp *op = r_core_anal_op (core, pc, 0);
|
|
if (!op) {
|
|
break;
|
|
}
|
|
ut64 newPC = core->offset + op->size;
|
|
r_reg_setv (reg, "PC", newPC);
|
|
switch (input[1]) {
|
|
case '?':
|
|
r_core_cmd_help (core, help_msg_aea);
|
|
break;
|
|
case 'r':
|
|
cmd_aea (core, 1<<1, core->offset, r_num_math (core->num, input+2));
|
|
break;
|
|
case 'w':
|
|
cmd_aea (core, 1<<2, core->offset, r_num_math (core->num, input+2));
|
|
break;
|
|
case 'n':
|
|
cmd_aea (core, 1<<3, core->offset, r_num_math (core->num, input+2));
|
|
break;
|
|
case 'j':
|
|
cmd_aea (core, 1<<4, core->offset, r_num_math (core->num, input+2));
|
|
break;
|
|
case '*':
|
|
cmd_aea (core, 1<<5, core->offset, r_num_math (core->num, input+2));
|
|
break;
|
|
case 'B': { // "aeaB"
|
|
bool json = input[2] == 'j';
|
|
int a = json? 3: 2;
|
|
ut64 addr = (input[a] == ' ')? r_num_math (core->num, input + a): core->offset;
|
|
RList *l = r_anal_get_blocks_in (core->anal, addr);
|
|
RAnalBlock *b;
|
|
RListIter *iter;
|
|
r_list_foreach (l, iter, b) {
|
|
int mode = json? (1<<4): 1;
|
|
cmd_aea (core, mode, b->addr, b->size);
|
|
break;
|
|
}
|
|
break;
|
|
}
|
|
case 'f': { // "aeaf"
|
|
RAnalFunction *fcn = r_anal_get_fcn_in (core->anal, core->offset, -1);
|
|
if (fcn) {
|
|
switch (input[2]) {
|
|
case 'j': // "aeafj"
|
|
cmd_aea (core, 1<<4, r_anal_function_min_addr (fcn), r_anal_function_linear_size (fcn));
|
|
break;
|
|
default:
|
|
cmd_aea (core, 1, r_anal_function_min_addr (fcn), r_anal_function_linear_size (fcn));
|
|
break;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
break;
|
|
case 'b': { // "aeab"
|
|
RAnalBlock *bb = r_anal_bb_from_offset (core->anal, core->offset);
|
|
if (bb) {
|
|
switch (input[2]) {
|
|
case 'j': // "aeabj"
|
|
cmd_aea (core, 1 | (1<<4), bb->addr, bb->size);
|
|
break;
|
|
default:
|
|
cmd_aea (core, 1, bb->addr, bb->size);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
default: {
|
|
const char *arg = input[1]? input + 2: "";
|
|
ut64 len = r_num_math (core->num, arg);
|
|
cmd_aea (core, 0, core->offset, len);
|
|
}
|
|
break;
|
|
}
|
|
r_reg_setv (reg, "PC", pc);
|
|
break;
|
|
}
|
|
case 'x':
|
|
if (input[1] == ' ') { // "aex"
|
|
char *hex;
|
|
int ret, bufsz;
|
|
|
|
input = r_str_trim_head_ro (input + 1);
|
|
hex = strdup (input);
|
|
if (!hex) {
|
|
break;
|
|
}
|
|
|
|
RAnalOp aop = {0};
|
|
bufsz = r_hex_str2bin (hex, (ut8*)hex);
|
|
ret = r_anal_op (core->anal, &aop, core->offset,
|
|
(const ut8*)hex, bufsz, R_ANAL_OP_MASK_ESIL);
|
|
if (ret > 0) {
|
|
const char *str = R_STRBUF_SAFEGET (&aop.esil);
|
|
char *str2 = r_str_newf (" %s", str);
|
|
cmd_anal_esil (core, str2, false);
|
|
free (str2);
|
|
r_core_return_value (core, 1);
|
|
} else {
|
|
// fail to exevute, update code
|
|
r_core_return_value (core, 0);
|
|
}
|
|
r_anal_op_fini (&aop);
|
|
free (hex);
|
|
} else if (input[1] == 'a') { // "aexa"
|
|
char *bytes = r_core_cmd_strf (core, "\"pa %s\"", r_str_trim_head_ro (input + 2));
|
|
if (R_STR_ISNOTEMPTY (bytes)) {
|
|
r_core_cmdf (core, "aex %s", bytes);
|
|
}
|
|
free (bytes);
|
|
} else { // "aex?"
|
|
r_core_cmd_help (core, help_msg_aex);
|
|
}
|
|
break;
|
|
case '?': // "ae?"
|
|
if (input[1] == '?') {
|
|
r_core_cmd_help (core, help_detail_ae);
|
|
break;
|
|
}
|
|
/* fallthrough */
|
|
default:
|
|
r_core_cmd_help (core, help_msg_ae);
|
|
break;
|
|
}
|
|
}
|
|
|
|
static void cmd_anal_bytes(RCore *core, const char *input) {
|
|
int len = core->blocksize;
|
|
int tbs = len;
|
|
if (input[0]) {
|
|
len = (int)r_num_get (core->num, input + 1);
|
|
if (len > tbs) {
|
|
r_core_block_size (core, len);
|
|
}
|
|
}
|
|
core_anal_bytes (core, core->block, len, 0, input[0]);
|
|
if (tbs != core->blocksize) {
|
|
r_core_block_size (core, tbs);
|
|
}
|
|
}
|
|
|
|
static void cmd_anal_opcode(RCore *core, const char *input) {
|
|
int l, len = core->blocksize;
|
|
ut32 tbs = core->blocksize;
|
|
r_core_block_read (core);
|
|
switch (input[0]) {
|
|
case 's': // "aos"
|
|
case 'j': // "aoj"
|
|
case 'e': // "aoe"
|
|
case 'r': { // "aor"
|
|
int count = 1;
|
|
int obs = core->blocksize;
|
|
int fmt = input[0];
|
|
if (input[0] == 'e' && input[1] == 'q') { // "aoeq"
|
|
fmt = 'E'; // quiet esil
|
|
input++;
|
|
}
|
|
if (input[1] && input[2]) {
|
|
l = (int)r_num_get (core->num, input + 1);
|
|
if (l > 0) {
|
|
count = l;
|
|
}
|
|
l *= 8;
|
|
if (l > obs) {
|
|
r_core_block_size (core, l);
|
|
}
|
|
} else {
|
|
count = 1;
|
|
}
|
|
core_anal_bytes (core, core->block, core->blocksize, count, fmt);
|
|
if (obs != core->blocksize) {
|
|
r_core_block_size (core, obs);
|
|
}
|
|
}
|
|
break;
|
|
case 't': // "aot"
|
|
if (input[1] == 'l') {
|
|
r_core_cmd0 (core, "/atl");
|
|
} else if (input[1] == '\0') {
|
|
r_core_cmd0 (core, "ao~^type[1]");
|
|
} else {
|
|
r_core_cmd_help (core, help_msg_aot);
|
|
}
|
|
break;
|
|
case 'm': // "aom"
|
|
if (input[1] == '?') {
|
|
r_core_cmd_help (core, help_msg_aom);
|
|
} else if (input[1] == 'd') {
|
|
const int id = (input[2]==' ')
|
|
?(int)r_num_math (core->num, input + 2): -1;
|
|
char *ops = r_asm_mnemonics (core->rasm, id, false);
|
|
if (ops) {
|
|
char *ptr = ops;
|
|
char *nl = strchr (ptr, '\n');
|
|
while (nl) {
|
|
*nl = 0;
|
|
char *desc = r_asm_describe (core->rasm, ptr);
|
|
if (desc) {
|
|
const char *pad = r_str_pad (' ', 16 - strlen (ptr));
|
|
r_cons_printf ("%s%s%s\n", ptr, pad, desc);
|
|
free (desc);
|
|
} else {
|
|
r_cons_printf ("%s\n", ptr);
|
|
}
|
|
ptr = nl + 1;
|
|
nl = strchr (ptr, '\n');
|
|
}
|
|
free (ops);
|
|
}
|
|
} else if (input[1] == 'l' || input[1] == '=' || input[1] == ' ' || input[1] == 'j') {
|
|
if (input[1] == ' ' && !IS_DIGIT (input[2])) {
|
|
r_cons_printf ("%d\n", r_asm_mnemonics_byname (core->rasm, input + 2));
|
|
} else {
|
|
// "aoml"
|
|
const int id = (input[1] == ' ')
|
|
?(int)r_num_math (core->num, input + 2): -1;
|
|
char *ops = r_asm_mnemonics (core->rasm, id, input[1] == 'j');
|
|
if (ops) {
|
|
r_str_trim (ops);
|
|
r_cons_println (ops);
|
|
free (ops);
|
|
}
|
|
}
|
|
} else {
|
|
r_core_cmd0 (core, "ao~mnemonic[1]");
|
|
}
|
|
break;
|
|
case 'c': // "aoc"
|
|
{
|
|
RList *hooks;
|
|
RListIter *iter;
|
|
RAnalCycleHook *hook;
|
|
char *instr_tmp = NULL;
|
|
int ccl = input[1]? r_num_math (core->num, &input[2]): 0; //get cycles to look for
|
|
bool cr = r_config_get_b (core->config, "asm.cmt.right");
|
|
bool fu = r_config_get_b (core->config, "asm.functions");
|
|
bool li = r_config_get_b (core->config, "asm.lines");
|
|
bool xr = r_config_get_b (core->config, "asm.xrefs");
|
|
|
|
r_config_set_b (core->config, "asm.cmt.right", true);
|
|
r_config_set_b (core->config, "asm.functions", false);
|
|
r_config_set_b (core->config, "asm.lines", false);
|
|
r_config_set_b (core->config, "asm.xrefs", false);
|
|
|
|
hooks = r_core_anal_cycles (core, ccl); //analyse
|
|
r_cons_clear_line (1);
|
|
r_list_foreach (hooks, iter, hook) {
|
|
instr_tmp = r_core_disassemble_instr (core, hook->addr, 1);
|
|
r_cons_printf ("After %4i cycles:\t%s", (ccl - hook->cycles), instr_tmp);
|
|
r_cons_flush ();
|
|
free (instr_tmp);
|
|
}
|
|
r_list_free (hooks);
|
|
|
|
r_config_set_b (core->config, "asm.cmt.right", cr); //reset settings
|
|
r_config_set_b (core->config, "asm.functions", fu);
|
|
r_config_set_b (core->config, "asm.lines", li);
|
|
r_config_set_b (core->config, "asm.xrefs", xr);
|
|
}
|
|
break;
|
|
case 'd': // "aod"
|
|
if (input[1] == 'a') { // "aoda"
|
|
// list sdb database
|
|
sdb_foreach (core->rasm->pair, listOpDescriptions, core);
|
|
} else if (input[1] == 0) {
|
|
int cur = R_MAX (core->print->cur, 0);
|
|
// XXX: we need cmd_xxx.h (cmd_anal.h)
|
|
core_anal_bytes (core, core->block + cur, core->blocksize, 1, 'd');
|
|
} else if (input[1] == ' ') {
|
|
char *d = r_asm_describe (core->rasm, input + 2);
|
|
if (d && *d) {
|
|
r_cons_println (d);
|
|
free (d);
|
|
} else {
|
|
eprintf ("Unknown mnemonic\n");
|
|
}
|
|
} else {
|
|
eprintf ("Use: aod[?a] ([opcode]) describe current, [given] or all mnemonics\n");
|
|
}
|
|
break;
|
|
case '*':
|
|
r_core_anal_hint_list (core->anal, input[0]);
|
|
break;
|
|
case 0:
|
|
case ' ': {
|
|
int count = 0;
|
|
if (input[0]) {
|
|
l = (int)r_num_get (core->num, input + 1);
|
|
if (l > 0) {
|
|
count = l;
|
|
}
|
|
if (l > tbs) {
|
|
r_core_block_size (core, l * 4);
|
|
//len = l;
|
|
}
|
|
} else {
|
|
len = l = core->blocksize;
|
|
count = 1;
|
|
}
|
|
core_anal_bytes (core, core->block, len, count, 0);
|
|
}
|
|
break;
|
|
case 'f': // "aof"
|
|
if (strlen (input + 1) > 1) {
|
|
RAnalOp aop = {0};
|
|
ut8 data[32];
|
|
r_io_read_at (core->io, core->offset, data, sizeof (data));
|
|
int ret = r_anal_op (core->anal, &aop, core->offset, data, sizeof (data), R_ANAL_OP_MASK_ESIL);
|
|
if (ret > 0) {
|
|
const char *arg = input + 2;
|
|
const char *expr = R_STRBUF_SAFEGET (&aop.esil);
|
|
RStrBuf *b = r_anal_esil_dfg_filter_expr (core->anal, expr, arg);
|
|
if (b) {
|
|
char *s = r_strbuf_drain (b);
|
|
r_cons_printf ("%s\n", s);
|
|
free (s);
|
|
}
|
|
} else {
|
|
R_LOG_WARN ("Unable to analyze instruction");
|
|
}
|
|
}
|
|
break;
|
|
default:
|
|
case '?':
|
|
r_core_cmd_help (core, help_msg_ao);
|
|
break;
|
|
}
|
|
}
|
|
|
|
static void cmd_anal_jumps(RCore *core, const char *input) {
|
|
r_core_cmdf (core, "af @@= `ax~ref.code.jmp[1]`");
|
|
}
|
|
|
|
// TODO: cleanup to reuse code
|
|
static void cmd_anal_aftertraps(RCore *core, const char *input) {
|
|
int bufi, minop = 1; // 4
|
|
ut8 *buf;
|
|
RAnalOp op = {0};
|
|
ut64 addr, addr_end;
|
|
ut64 len = r_num_math (core->num, input);
|
|
if (len > 0xffffff) {
|
|
eprintf ("Too big\n");
|
|
return;
|
|
}
|
|
RBinFile *bf = r_bin_cur (core->bin);
|
|
if (!bf) {
|
|
return;
|
|
}
|
|
addr = core->offset;
|
|
if (!len) {
|
|
// ignore search.in to avoid problems. analysis != search
|
|
RIOMap *map = r_io_map_get_at (core->io, addr);
|
|
if (map && (map->perm & R_PERM_X)) {
|
|
// search in current section
|
|
if (r_io_map_size (map) > bf->size) {
|
|
addr = r_io_map_begin (map);
|
|
if (bf->size > map->delta) {
|
|
len = bf->size - map->delta;
|
|
} else {
|
|
eprintf ("Opps something went wrong aac\n");
|
|
return;
|
|
}
|
|
} else {
|
|
addr = r_io_map_begin (map);
|
|
len = r_io_map_size (map);
|
|
}
|
|
} else {
|
|
if (map && r_io_map_begin (map) != map->delta && bf->size > (core->offset - r_io_map_begin (map) + map->delta)) {
|
|
len = bf->size - (core->offset - r_io_map_begin (map) + map->delta);
|
|
} else {
|
|
if (bf->size > core->offset) {
|
|
len = bf->size - core->offset;
|
|
} else {
|
|
eprintf ("Oops invalid range\n");
|
|
len = 0;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
addr_end = addr + len;
|
|
if (!(buf = malloc (4096))) {
|
|
return;
|
|
}
|
|
bufi = 0;
|
|
int trapcount = 0;
|
|
int nopcount = 0;
|
|
r_cons_break_push (NULL, NULL);
|
|
while (addr < addr_end) {
|
|
if (r_cons_is_breaked ()) {
|
|
break;
|
|
}
|
|
// TODO: too many ioreads here
|
|
if (bufi > 4000) {
|
|
bufi = 0;
|
|
}
|
|
if (!bufi) {
|
|
r_io_read_at (core->io, addr, buf, 4096);
|
|
}
|
|
if (r_anal_op (core->anal, &op, addr, buf + bufi, 4096 - bufi, R_ANAL_OP_MASK_BASIC)) {
|
|
if (op.size < 1) {
|
|
// XXX must be +4 on arm/mips/.. like we do in disasm.c
|
|
op.size = minop;
|
|
}
|
|
if (op.type == R_ANAL_OP_TYPE_TRAP) {
|
|
trapcount ++;
|
|
} else if (op.type == R_ANAL_OP_TYPE_NOP) {
|
|
nopcount ++;
|
|
} else {
|
|
if (nopcount > 1) {
|
|
r_cons_printf ("af @ 0x%08"PFMT64x"\n", addr);
|
|
nopcount = 0;
|
|
}
|
|
if (trapcount > 0) {
|
|
r_cons_printf ("af @ 0x%08"PFMT64x"\n", addr);
|
|
trapcount = 0;
|
|
}
|
|
}
|
|
} else {
|
|
op.size = minop;
|
|
}
|
|
addr += (op.size > 0)? op.size : 1;
|
|
bufi += (op.size > 0)? op.size : 1;
|
|
r_anal_op_fini (&op);
|
|
}
|
|
r_cons_break_pop ();
|
|
free (buf);
|
|
}
|
|
|
|
static void cmd_anal_blocks(RCore *core, const char *input) {
|
|
ut64 from , to;
|
|
char *arg = strchr (input, ' ');
|
|
r_cons_break_push (NULL, NULL);
|
|
if (!arg) {
|
|
r_core_cmd0 (core, "abb $SS @ $S");
|
|
RList *list = r_core_get_boundaries_prot (core, R_PERM_X, NULL, "anal");
|
|
RListIter *iter;
|
|
RIOMap* map;
|
|
if (!list) {
|
|
goto ctrl_c;
|
|
}
|
|
r_list_foreach (list, iter, map) {
|
|
from = r_io_map_begin (map);
|
|
to = r_io_map_end (map);
|
|
if (r_cons_is_breaked ()) {
|
|
goto ctrl_c;
|
|
}
|
|
if (!from && !to) {
|
|
R_LOG_ERROR ("Cannot determine search boundaries");
|
|
} else if (to - from > UT32_MAX) {
|
|
R_LOG_WARN ("Skipping huge range");
|
|
} else {
|
|
R_LOG_DEBUG ("abb 0x%08"PFMT64x" @ 0x%08"PFMT64x, (to - from), from);
|
|
r_core_cmdf (core, "abb 0x%08"PFMT64x" @ 0x%08"PFMT64x, (to - from), from);
|
|
}
|
|
}
|
|
} else {
|
|
st64 sz = r_num_math (core->num, arg + 1);
|
|
if (sz < 1) {
|
|
R_LOG_ERROR ("Invalid range");
|
|
return;
|
|
}
|
|
r_core_cmdf (core, "abb 0x%08"PFMT64x" @ 0x%08"PFMT64x, sz, core->offset);
|
|
}
|
|
ctrl_c:
|
|
r_cons_break_pop ();
|
|
}
|
|
|
|
static void _anal_calls(RCore *core, ut64 addr, ut64 addr_end, bool printCommands, bool importsOnly) {
|
|
RAnalOp op;
|
|
int depth = r_config_get_i (core->config, "anal.depth");
|
|
const int addrbytes = core->io->addrbytes;
|
|
const int bsz = 4096;
|
|
int bufi = 0;
|
|
int bufi_max = bsz - 16;
|
|
if (addr_end - addr > UT32_MAX) {
|
|
return;
|
|
}
|
|
ut8 *buf = malloc (bsz);
|
|
ut8 *block0 = calloc (1, bsz);
|
|
ut8 *block1 = malloc (bsz);
|
|
if (!buf || !block0 || !block1) {
|
|
free (buf);
|
|
free (block0);
|
|
free (block1);
|
|
return;
|
|
}
|
|
memset (block1, -1, bsz);
|
|
int minop = r_anal_archinfo (core->anal, R_ANAL_ARCHINFO_MIN_OP_SIZE);
|
|
if (minop < 1) {
|
|
minop = 1;
|
|
}
|
|
int setBits = r_config_get_i (core->config, "asm.bits");
|
|
r_cons_break_push (NULL, NULL);
|
|
while (addr < addr_end && !r_cons_is_breaked ()) {
|
|
// TODO: too many ioreads here
|
|
if (bufi > bufi_max) {
|
|
bufi = 0;
|
|
}
|
|
if (!bufi) {
|
|
(void)r_io_read_at (core->io, addr, buf, bsz);
|
|
}
|
|
if (!memcmp (buf, block0, bsz) || !memcmp (buf, block1, bsz)) {
|
|
addr += bsz;
|
|
continue;
|
|
}
|
|
RAnalHint *hint = r_anal_hint_get (core->anal, addr);
|
|
if (hint && hint->bits) {
|
|
setBits = hint->bits;
|
|
}
|
|
r_anal_hint_free (hint);
|
|
if (setBits != core->rasm->config->bits) {
|
|
r_config_set_i (core->config, "asm.bits", setBits);
|
|
}
|
|
if (r_anal_op (core->anal, &op, addr, buf + bufi, bsz - bufi, 0) > 0) {
|
|
if (op.size < 1) {
|
|
op.size = minop;
|
|
}
|
|
if (op.type == R_ANAL_OP_TYPE_CALL) {
|
|
bool isValidCall = true;
|
|
if (importsOnly) {
|
|
RFlagItem *f = r_flag_get_i (core->flags, op.jump);
|
|
if (!f || !strstr (f->name, "imp.")) {
|
|
isValidCall = false;
|
|
}
|
|
}
|
|
RBinReloc *rel = r_core_getreloc (core, addr, op.size);
|
|
if (rel && (rel->import || rel->symbol)) {
|
|
isValidCall = false;
|
|
}
|
|
if (isValidCall) {
|
|
ut8 buf[4];
|
|
r_io_read_at (core->io, op.jump, buf, 4);
|
|
isValidCall = memcmp (buf, "\x00\x00\x00\x00", 4);
|
|
}
|
|
if (isValidCall) {
|
|
#if JAYRO_03
|
|
if (!anal_is_bad_call (core, from, to, addr, buf, bufi)) {
|
|
fcn = r_anal_get_fcn_in (core->anal, op.jump, R_ANAL_FCN_TYPE_ROOT);
|
|
if (!fcn) {
|
|
r_core_anal_fcn (core, op.jump, addr, R_ANAL_REF_TYPE_CALL, depth - 1);
|
|
}
|
|
}
|
|
#else
|
|
if (printCommands) {
|
|
r_cons_printf ("ax 0x%08" PFMT64x " 0x%08" PFMT64x "\n", op.jump, addr);
|
|
r_cons_printf ("af @ 0x%08" PFMT64x"\n", op.jump);
|
|
} else {
|
|
// add xref here
|
|
r_anal_xrefs_set (core->anal, addr, op.jump, R_ANAL_REF_TYPE_CALL);
|
|
if (r_io_is_valid_offset (core->io, op.jump, 1)) {
|
|
r_core_anal_fcn (core, op.jump, addr, R_ANAL_REF_TYPE_CALL, depth - 1);
|
|
}
|
|
}
|
|
#endif
|
|
}
|
|
}
|
|
} else {
|
|
op.size = minop;
|
|
}
|
|
if ((int)op.size < 1) {
|
|
op.size = minop;
|
|
}
|
|
addr += op.size;
|
|
bufi += addrbytes * op.size;
|
|
r_anal_op_fini (&op);
|
|
}
|
|
r_cons_break_pop ();
|
|
free (buf);
|
|
free (block0);
|
|
free (block1);
|
|
}
|
|
|
|
static void cmd_anal_calls(RCore *core, const char *input, bool printCommands, bool importsOnly) {
|
|
RList *ranges = NULL;
|
|
RIOMap *r;
|
|
ut64 addr;
|
|
ut64 len = r_num_math (core->num, input);
|
|
if (len > 0xffffff) {
|
|
R_LOG_ERROR ("Too big");
|
|
return;
|
|
}
|
|
RBinFile *binfile = r_bin_cur (core->bin);
|
|
addr = core->offset;
|
|
if (binfile) {
|
|
if (len) {
|
|
RIOMap *m = R_NEW0 (RIOMap);
|
|
m->itv.addr = addr;
|
|
m->itv.size = len;
|
|
ranges = r_list_newf ((RListFree)free);
|
|
r_list_append (ranges, m);
|
|
} else {
|
|
ranges = r_core_get_boundaries_prot (core, R_PERM_X, NULL, "anal");
|
|
}
|
|
}
|
|
r_cons_break_push (NULL, NULL);
|
|
if (!binfile || (ranges && !r_list_length (ranges))) {
|
|
RListIter *iter;
|
|
RIOMap *map;
|
|
r_list_free (ranges);
|
|
ranges = r_core_get_boundaries_prot (core, 0, NULL, "anal");
|
|
if (ranges) {
|
|
r_list_foreach (ranges, iter, map) {
|
|
ut64 addr = r_io_map_begin (map);
|
|
_anal_calls (core, addr, r_io_map_end (map), printCommands, importsOnly);
|
|
}
|
|
}
|
|
} else {
|
|
RListIter *iter;
|
|
if (binfile) {
|
|
r_list_foreach (ranges, iter, r) {
|
|
addr = r->itv.addr;
|
|
//this normally will happen on fuzzed binaries, dunno if with huge
|
|
//binaries as well
|
|
if (r_cons_is_breaked ()) {
|
|
break;
|
|
}
|
|
_anal_calls (core, addr, r_itv_end (r->itv), printCommands, importsOnly);
|
|
}
|
|
}
|
|
}
|
|
r_cons_break_pop ();
|
|
r_list_free (ranges);
|
|
}
|
|
|
|
static void cmd_sdbk(Sdb *db, const char *input) {
|
|
char *out = (input[0] == ' ')
|
|
? sdb_querys (db, NULL, 0, input + 1)
|
|
: sdb_querys (db, NULL, 0, "*");
|
|
if (out) {
|
|
r_cons_println (out);
|
|
free (out);
|
|
} else {
|
|
R_LOG_ERROR ("Usage: ask [query]");
|
|
}
|
|
}
|
|
|
|
static void cmd_anal_syscall(RCore *core, const char *input) {
|
|
char snstr[32];
|
|
PJ *pj = NULL;
|
|
RSyscallItem *si;
|
|
RListIter *iter;
|
|
RList *list;
|
|
int n;
|
|
|
|
switch (input[0]) {
|
|
case 'c': // "asc"
|
|
if (input[1] == 'a') {
|
|
if (input[2] == ' ') {
|
|
if (!isalpha ((ut8)input[3]) && (n = r_num_math (core->num, input + 3)) >= 0 ) {
|
|
si = r_syscall_get (core->anal->syscall, n, -1);
|
|
if (si) {
|
|
r_cons_printf (".equ SYS_%s %s\n", si->name, syscallNumber (snstr, n));
|
|
r_syscall_item_free (si);
|
|
} else {
|
|
R_LOG_ERROR ("Unknown syscall number");
|
|
}
|
|
} else {
|
|
n = r_syscall_get_num (core->anal->syscall, input + 3);
|
|
if (n != -1) {
|
|
r_cons_printf (".equ SYS_%s %s\n", input + 3, syscallNumber (snstr, n));
|
|
} else {
|
|
R_LOG_ERROR ("Unknown syscall name");
|
|
}
|
|
}
|
|
} else {
|
|
list = r_syscall_list (core->anal->syscall);
|
|
r_list_foreach (list, iter, si) {
|
|
r_cons_printf (".equ SYS_%s %s\n", si->name, syscallNumber (snstr, si->num));
|
|
}
|
|
r_list_free (list);
|
|
}
|
|
} else {
|
|
if (input[1] == ' ') {
|
|
if (!isalpha ((ut8)input[2]) && (n = r_num_math (core->num, input + 2)) >= 0) {
|
|
si = r_syscall_get (core->anal->syscall, n, -1);
|
|
if (si) {
|
|
r_cons_printf ("#define SYS_%s %s\n", si->name, syscallNumber (snstr, n));
|
|
r_syscall_item_free (si);
|
|
} else {
|
|
R_LOG_ERROR ("Unknown syscall number");
|
|
}
|
|
} else {
|
|
n = r_syscall_get_num (core->anal->syscall, input + 2);
|
|
if (n != -1) {
|
|
r_cons_printf ("#define SYS_%s %s\n", input + 2, syscallNumber (snstr, n));
|
|
} else {
|
|
R_LOG_ERROR ("Unknown syscall name");
|
|
}
|
|
}
|
|
} else {
|
|
list = r_syscall_list (core->anal->syscall);
|
|
r_list_foreach (list, iter, si) {
|
|
r_cons_printf ("#define SYS_%s %s\n",
|
|
si->name, syscallNumber (snstr, si->num));
|
|
}
|
|
r_list_free (list);
|
|
}
|
|
}
|
|
break;
|
|
case 'k': // "ask"
|
|
cmd_sdbk (core->anal->syscall->db, input + 1);
|
|
break;
|
|
case 'l': // "asl"
|
|
if (input[1] == ' ') {
|
|
const char *sc_name = r_str_trim_head_ro (input + 2);
|
|
int sc_number = r_syscall_get_num (core->anal->syscall, sc_name);
|
|
if (sc_number != 0) {
|
|
r_cons_printf ("%s\n", syscallNumber (snstr, sc_number));
|
|
} else {
|
|
sc_number = r_num_math (core->num, sc_name);
|
|
si = r_syscall_get (core->anal->syscall, sc_number, -1);
|
|
if (!si) {
|
|
si = r_syscall_get (core->anal->syscall, -1, sc_number);
|
|
}
|
|
if (si) {
|
|
r_cons_println (si->name);
|
|
r_syscall_item_free (si);
|
|
} else {
|
|
R_LOG_ERROR ("Unknown syscall number");
|
|
}
|
|
}
|
|
} else {
|
|
list = r_syscall_list (core->anal->syscall);
|
|
r_list_foreach (list, iter, si) {
|
|
r_cons_printf ("%s = 0x%02x.%s\n",
|
|
si->name, si->swi, syscallNumber (snstr, si->num));
|
|
}
|
|
r_list_free (list);
|
|
}
|
|
break;
|
|
case 'j': // "asj"
|
|
pj = pj_new ();
|
|
pj_a (pj);
|
|
list = r_syscall_list (core->anal->syscall);
|
|
r_list_foreach (list, iter, si) {
|
|
pj_o (pj);
|
|
pj_ks (pj, "name", si->name);
|
|
pj_ki (pj, "swi", si->swi);
|
|
pj_ki (pj, "num", si->num);
|
|
pj_end (pj);
|
|
}
|
|
pj_end (pj);
|
|
if (pj) {
|
|
r_cons_println (pj_string (pj));
|
|
pj_free (pj);
|
|
}
|
|
r_list_free (list);
|
|
break;
|
|
case '\0':
|
|
cmd_syscall_do (core, -1, core->offset);
|
|
break;
|
|
case ' ':
|
|
{
|
|
const char *sn = r_str_trim_head_ro (input + 1);
|
|
st64 num = r_syscall_get_num (core->anal->syscall, sn);
|
|
if (num < 1) {
|
|
num = (int)r_num_get (core->num, sn);
|
|
}
|
|
cmd_syscall_do (core, num, -1);
|
|
}
|
|
break;
|
|
default:
|
|
case '?':
|
|
r_core_cmd_help (core, help_msg_as);
|
|
break;
|
|
}
|
|
}
|
|
|
|
static void anal_axg(RCore *core, const char *input, int level, Sdb *db, int opts, PJ* pj) {
|
|
char arg[32], pre[128];
|
|
RListIter *iter;
|
|
RAnalRef *ref;
|
|
ut64 addr = core->offset;
|
|
bool is_json = opts & R_CORE_ANAL_JSON;
|
|
bool is_r2 = opts & R_CORE_ANAL_GRAPHBODY;
|
|
if (is_json && !pj) {
|
|
return;
|
|
}
|
|
if (input && *input) {
|
|
addr = r_num_math (core->num, input);
|
|
}
|
|
// eprintf ("Path between 0x%08"PFMT64x" .. 0x%08"PFMT64x"\n", core->offset, addr);
|
|
int spaces = (level + 1) * 2;
|
|
if (spaces > sizeof (pre) - 4) {
|
|
spaces = sizeof (pre) - 4;
|
|
}
|
|
memset (pre, ' ', sizeof (pre));
|
|
strcpy (pre + spaces, "- ");
|
|
|
|
RList *xrefs = r_anal_xrefs_get (core->anal, addr);
|
|
bool open_object = false;
|
|
if (!r_list_empty (xrefs)) {
|
|
RAnalFunction *fcn = r_anal_get_fcn_in (core->anal, addr, -1);
|
|
if (fcn) {
|
|
if (is_r2) {
|
|
r_cons_printf ("agn 0x%08"PFMT64x" %s\n", fcn->addr, fcn->name);
|
|
} else if (is_json) {
|
|
char taddr[64];
|
|
pj_o (pj);
|
|
pj_k (pj, sdb_itoa (addr, taddr, 10));
|
|
pj_o (pj);
|
|
pj_ks (pj, "type", "fcn");
|
|
pj_kn (pj, "fcn_addr", fcn->addr);
|
|
pj_ks (pj, "name", fcn->name);
|
|
pj_k (pj, "refs");
|
|
pj_a (pj);
|
|
open_object = true;
|
|
} else {
|
|
//if (sdb_add (db, fcn->name, "1", 0)) {
|
|
r_cons_printf ("%s0x%08"PFMT64x" fcn 0x%08"PFMT64x" %s\n",
|
|
pre + 2, addr, fcn->addr, fcn->name);
|
|
//}
|
|
}
|
|
} else {
|
|
if (is_r2) {
|
|
r_cons_printf ("age 0x%08"PFMT64x"\n", addr);
|
|
} else if (is_json) {
|
|
char taddr[64];
|
|
pj_o (pj);
|
|
pj_k (pj, sdb_itoa (addr, taddr, 10));
|
|
pj_o (pj);
|
|
pj_k (pj, "refs");
|
|
pj_a (pj);
|
|
open_object = true;
|
|
} else {
|
|
//snprintf (arg, sizeof (arg), "0x%08"PFMT64x, addr);
|
|
//if (sdb_add (db, arg, "1", 0)) {
|
|
r_cons_printf ("%s0x%08"PFMT64x"\n", pre+2, addr);
|
|
//}
|
|
}
|
|
}
|
|
}
|
|
r_list_foreach (xrefs, iter, ref) {
|
|
RAnalFunction *fcn = r_anal_get_fcn_in (core->anal, ref->addr, -1);
|
|
if (fcn) {
|
|
if (is_r2) {
|
|
r_cons_printf ("agn 0x%08"PFMT64x" %s\n", fcn->addr, fcn->name);
|
|
r_cons_printf ("age 0x%08"PFMT64x" 0x%08"PFMT64x"\n", fcn->addr, addr);
|
|
} else if (is_json) {
|
|
if (level == 0) {
|
|
char taddr[64];
|
|
pj_o (pj);
|
|
pj_k (pj, sdb_itoa (ref->addr, taddr, 10));
|
|
pj_o (pj);
|
|
pj_ks (pj, "type", "fcn");
|
|
pj_kn (pj, "fcn_addr", fcn->addr);
|
|
pj_ks (pj, "name", fcn->name);
|
|
pj_k (pj, "refs");
|
|
pj_a (pj);
|
|
open_object = true;
|
|
} else {
|
|
char taddr[64];
|
|
pj_end (pj);
|
|
pj_end (pj);
|
|
pj_end (pj);
|
|
pj_o (pj);
|
|
pj_k (pj, sdb_itoa (ref->addr, taddr, 10));
|
|
pj_o (pj);
|
|
pj_ks (pj, "type", "fcn");
|
|
pj_kn (pj, "fcn_addr", fcn->addr);
|
|
pj_ks (pj, "refs", fcn->name);
|
|
pj_k (pj, "refs");
|
|
pj_a (pj);
|
|
}
|
|
} else {
|
|
r_cons_printf ("%s0x%08"PFMT64x" fcn 0x%08"PFMT64x" %s\n", pre, ref->addr, fcn->addr, fcn->name);
|
|
}
|
|
if (sdb_add (db, fcn->name, "1", 0)) {
|
|
snprintf (arg, sizeof (arg), "0x%08"PFMT64x, fcn->addr);
|
|
anal_axg (core, arg, level + 1, db, opts, pj);
|
|
} else {
|
|
if (is_json) {
|
|
pj_end (pj);
|
|
pj_end (pj);
|
|
pj_end (pj);
|
|
open_object = false;
|
|
}
|
|
}
|
|
} else {
|
|
if (is_r2) {
|
|
r_cons_printf ("agn 0x%08"PFMT64x" ???\n", ref->addr);
|
|
r_cons_printf ("age 0x%08"PFMT64x" 0x%08"PFMT64x"\n", ref->addr, addr);
|
|
} else if (is_json) {
|
|
char taddr[64];
|
|
pj_o (pj);
|
|
pj_k (pj, sdb_itoa (ref->addr, taddr, 10));
|
|
pj_o (pj);
|
|
pj_ks (pj, "type", "???");
|
|
pj_k (pj, "refs");
|
|
pj_a (pj);
|
|
open_object = true;
|
|
} else {
|
|
r_cons_printf ("%s0x%08"PFMT64x" ???\n", pre, ref->addr);
|
|
}
|
|
snprintf (arg, sizeof (arg), "0x%08"PFMT64x, ref->addr);
|
|
if (sdb_add (db, arg, "1", 0)) {
|
|
anal_axg (core, arg, level + 1, db, opts, pj);
|
|
} else {
|
|
if (is_json) {
|
|
pj_end (pj);
|
|
pj_end (pj);
|
|
pj_end (pj);
|
|
open_object = false;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
if (is_json) {
|
|
if (open_object) {
|
|
pj_end (pj);
|
|
pj_end (pj);
|
|
pj_end (pj);
|
|
}
|
|
if (level == 0) {
|
|
if (open_object) {
|
|
pj_end (pj);
|
|
pj_end (pj);
|
|
pj_end (pj);
|
|
}
|
|
}
|
|
}
|
|
r_list_free (xrefs);
|
|
}
|
|
|
|
static void cmd_anal_ucall_ref(RCore *core, ut64 addr) {
|
|
RAnalFunction * fcn = r_anal_get_function_at (core->anal, addr);
|
|
if (fcn) {
|
|
r_cons_printf (" ; %s", fcn->name);
|
|
} else {
|
|
r_cons_printf (" ; 0x%" PFMT64x, addr);
|
|
}
|
|
}
|
|
|
|
static char *get_op_ireg(void *user, ut64 addr) {
|
|
RCore *core = (RCore *)user;
|
|
char *res = NULL;
|
|
RAnalOp *op = r_core_anal_op (core, addr, 0);
|
|
if (op && op->ireg) {
|
|
res = strdup (op->ireg);
|
|
}
|
|
r_anal_op_free (op);
|
|
return res;
|
|
}
|
|
|
|
static char *get_buf_asm(RCore *core, ut64 from, ut64 addr, RAnalFunction *fcn, bool color) {
|
|
int has_color = core->print->flags & R_PRINT_FLAGS_COLOR;
|
|
char str[512];
|
|
const int size = 12;
|
|
ut8 buf[12];
|
|
RAsmOp asmop = {0};
|
|
char *buf_asm = NULL;
|
|
bool asm_subvar = r_config_get_b (core->config, "asm.sub.var");
|
|
bool be = core->rasm->config->big_endian;
|
|
core->parser->pseudo = r_config_get_b (core->config, "asm.pseudo");
|
|
core->parser->subrel = r_config_get_i (core->config, "asm.sub.rel");
|
|
core->parser->localvar_only = r_config_get_b (core->config, "asm.sub.varonly");
|
|
|
|
if (core->parser->subrel) {
|
|
core->parser->subrel_addr = from;
|
|
}
|
|
r_io_read_at (core->io, addr, buf, size);
|
|
r_asm_set_pc (core->rasm, addr);
|
|
r_asm_disassemble (core->rasm, &asmop, buf, size);
|
|
int ba_len = r_strbuf_length (&asmop.buf_asm) + 128;
|
|
char *ba = malloc (ba_len);
|
|
strcpy (ba, r_strbuf_get (&asmop.buf_asm));
|
|
if (asm_subvar) {
|
|
core->parser->get_ptr_at = r_anal_function_get_var_stackptr_at;
|
|
core->parser->get_reg_at = r_anal_function_get_var_reg_at;
|
|
core->parser->get_op_ireg = get_op_ireg;
|
|
r_parse_subvar (core->parser, fcn, addr, asmop.size,
|
|
ba, ba, sizeof (asmop.buf_asm));
|
|
}
|
|
RAnalHint *hint = r_anal_hint_get (core->anal, addr);
|
|
r_parse_filter (core->parser, addr, core->flags, hint,
|
|
ba, str, sizeof (str), be);
|
|
r_anal_hint_free (hint);
|
|
r_asm_op_set_asm (&asmop, ba);
|
|
free (ba);
|
|
if (color && has_color) {
|
|
buf_asm = r_print_colorize_opcode (core->print, str,
|
|
core->cons->context->pal.reg, core->cons->context->pal.num, false, fcn ? fcn->addr : 0);
|
|
} else {
|
|
buf_asm = r_str_new (str);
|
|
}
|
|
r_strbuf_fini (&asmop.buf_asm);
|
|
return buf_asm;
|
|
}
|
|
|
|
static const char *axtm_name(RCore *core, ut64 addr) {
|
|
const char *name = NULL;
|
|
RAnalFunction *fcn = r_anal_get_fcn_in (core->anal, addr, -1);
|
|
if (fcn) {
|
|
name = fcn->name;
|
|
} else {
|
|
RFlagItem *f = r_flag_get_at (core->flags, addr, false);
|
|
if (f) {
|
|
name = f->name;
|
|
}
|
|
}
|
|
return name;
|
|
}
|
|
|
|
static bool axtm_cb(void *u, const ut64 k, const void *v) {
|
|
RCore *core = (RCore*)u;
|
|
const char *name = axtm_name (core, k);
|
|
RListIter *iter;
|
|
RAnalRef *ref;
|
|
RList *list = r_anal_xrefs_get (core->anal, k);
|
|
if (list && r_list_length (list) > 0) {
|
|
r_cons_printf ("0x%"PFMT64x": %s\n", k, name? name: "?");
|
|
r_list_foreach (list, iter, ref) {
|
|
name = axtm_name (core, ref->addr);
|
|
r_cons_printf (" 0x%"PFMT64x": %s\n", ref->addr, name? name: "?");
|
|
}
|
|
}
|
|
r_list_free (list);
|
|
return true;
|
|
}
|
|
|
|
static void axtm(RCore *core) {
|
|
ht_up_foreach (core->anal->dict_refs, axtm_cb, core);
|
|
}
|
|
|
|
static void axfm(RCore *core) {
|
|
ht_up_foreach (core->anal->dict_xrefs, axtm_cb, core);
|
|
}
|
|
|
|
static bool cmd_anal_refs(RCore *core, const char *input) {
|
|
bool be = core->print->config->big_endian;
|
|
ut64 addr = core->offset;
|
|
switch (input[0]) {
|
|
case '-': { // "ax-"
|
|
RList *list;
|
|
RListIter *iter;
|
|
RAnalRef *ref;
|
|
char *cp_inp = strdup (input + 1);
|
|
char *ptr = cp_inp;
|
|
r_str_trim_head (ptr);
|
|
if (!strcmp (ptr, "*")) { // "ax-*"
|
|
r_anal_xrefs_init (core->anal);
|
|
} else {
|
|
int n = r_str_word_set0 (ptr);
|
|
ut64 from = UT64_MAX, to = UT64_MAX;
|
|
switch (n) {
|
|
case 2:
|
|
from = r_num_math (core->num, r_str_word_get0 (ptr, 1));
|
|
//fall through
|
|
case 1: // get addr
|
|
to = r_num_math (core->num, r_str_word_get0 (ptr, 0));
|
|
break;
|
|
default:
|
|
to = core->offset;
|
|
break;
|
|
}
|
|
list = r_anal_xrefs_get (core->anal, to);
|
|
if (list) {
|
|
r_list_foreach (list, iter, ref) {
|
|
if (from != UT64_MAX && from == ref->addr) {
|
|
r_anal_xref_del (core->anal, ref->addr, ref->at);
|
|
}
|
|
if (from == UT64_MAX) {
|
|
r_anal_xref_del (core->anal, ref->addr, ref->at);
|
|
}
|
|
}
|
|
}
|
|
r_list_free (list);
|
|
}
|
|
free (cp_inp);
|
|
} break;
|
|
case 'g': // "axg"
|
|
{
|
|
Sdb *db = sdb_new0 ();
|
|
if (input[1] == '*') {
|
|
anal_axg (core, input + 2, 0, db, R_CORE_ANAL_GRAPHBODY, NULL); // r2 commands
|
|
} else if (input[1] == 'j') {
|
|
PJ *pj = pj_new ();
|
|
anal_axg (core, input + 2, 0, db, R_CORE_ANAL_JSON, pj);
|
|
r_cons_printf("%s\n", pj_string (pj));
|
|
pj_free (pj);
|
|
} else {
|
|
anal_axg (core, input[1] ? input + 2 : NULL, 0, db, 0, NULL);
|
|
}
|
|
sdb_free (db);
|
|
}
|
|
break;
|
|
case '\0': // "ax"
|
|
case 'j': // "axj"
|
|
case 'q': // "axq"
|
|
case '*': // "ax*"
|
|
case ',': // "ax,"
|
|
r_anal_xrefs_list (core->anal, input[0], *input? r_str_trim_head_ro (input + 1): "");
|
|
break;
|
|
case '.': { // "ax."
|
|
char *tInput = strdup (input);
|
|
if (r_str_replace_ch (tInput, '.', 't', false)) {
|
|
cmd_anal_refs (core, tInput);
|
|
}
|
|
char *fInput = strdup (input);
|
|
if (r_str_replace_ch (fInput, '.', 'f', false)) {
|
|
cmd_anal_refs (core, fInput);
|
|
}
|
|
free (tInput);
|
|
free (fInput);
|
|
} break;
|
|
case 'm': { // "axm"
|
|
RList *list;
|
|
RAnalRef *ref;
|
|
RListIter *iter;
|
|
char *ptr = strdup (r_str_trim_head_ro (input + 1));
|
|
int n = r_str_word_set0 (ptr);
|
|
ut64 at = core->offset;
|
|
ut64 addr = UT64_MAX;
|
|
switch (n) {
|
|
case 2: // get at
|
|
at = r_num_math (core->num, r_str_word_get0 (ptr, 1));
|
|
/* fall through */
|
|
case 1: // get addr
|
|
addr = r_num_math (core->num, r_str_word_get0 (ptr, 0));
|
|
break;
|
|
default:
|
|
free (ptr);
|
|
return false;
|
|
}
|
|
//get all xrefs pointing to addr
|
|
list = r_anal_xrefs_get (core->anal, addr);
|
|
r_list_foreach (list, iter, ref) {
|
|
r_cons_printf ("0x%"PFMT64x" %s %s\n", ref->addr,
|
|
r_anal_ref_perm_tostring (ref),
|
|
r_anal_ref_type_tostring (ref->type));
|
|
r_anal_xrefs_set (core->anal, ref->addr, at, ref->type);
|
|
}
|
|
r_list_free (list);
|
|
free (ptr);
|
|
} break;
|
|
case 'v': // "axv"
|
|
if (input[1] == '?') {
|
|
r_core_cmd_help (core, help_msg_axv);
|
|
} else {
|
|
cmd_afvx (core, NULL, input[1] == 'j');
|
|
}
|
|
break;
|
|
case 'l': // "axl"
|
|
switch (input[1]) {
|
|
case '?':
|
|
r_core_cmd_help (core, help_msg_axl);
|
|
break;
|
|
case 'c':
|
|
{
|
|
ut64 count = r_anal_xrefs_count (core->anal);
|
|
r_cons_printf ("%"PFMT64d"\n", count);
|
|
}
|
|
break;
|
|
case 'q':
|
|
r_core_cmd0 (core, "axq");
|
|
break;
|
|
default:
|
|
r_core_cmd0 (core, "ax");
|
|
break;
|
|
}
|
|
break;
|
|
case 't': { // "axt"
|
|
if (input[1] == '?') { // "axt?"
|
|
r_core_cmd_help (core, help_msg_axt);
|
|
break;
|
|
}
|
|
if (input[1] == 'm') { // "axtm"
|
|
// like aflm but reversed
|
|
axtm (core);
|
|
break;
|
|
}
|
|
RAnalFunction *fcn;
|
|
RAnalRef *ref;
|
|
RListIter *iter;
|
|
char *space = strchr (input, ' ');
|
|
if (space) {
|
|
addr = r_num_math (core->num, space + 1);
|
|
if (core->num->nc.errors > 0) {
|
|
eprintf ("Invalid argument.\n");
|
|
break;
|
|
}
|
|
} else {
|
|
addr = core->offset;
|
|
}
|
|
RList *list = r_anal_xrefs_get (core->anal, addr);
|
|
if (list) {
|
|
if (input[1] == 'q') { // "axtq"
|
|
r_list_foreach (list, iter, ref) {
|
|
r_cons_printf ("0x%" PFMT64x "\n", ref->addr);
|
|
}
|
|
} else if (input[1] == 'j') { // "axtj"
|
|
PJ *pj = pj_new ();
|
|
if (!pj) {
|
|
return false;
|
|
}
|
|
pj_a (pj);
|
|
r_list_foreach (list, iter, ref) {
|
|
fcn = r_anal_get_fcn_in (core->anal, ref->addr, 0);
|
|
char *str = get_buf_asm (core, addr, ref->addr, fcn, false);
|
|
pj_o (pj);
|
|
pj_kn (pj, "from", ref->addr);
|
|
if (ref->type) {
|
|
pj_ks (pj, "type", r_anal_ref_type_tostring (ref->type));
|
|
pj_ks (pj, "perm", r_anal_ref_perm_tostring (ref));
|
|
}
|
|
pj_ks (pj, "opcode", str);
|
|
if (fcn) {
|
|
pj_kn (pj, "fcn_addr", fcn->addr);
|
|
pj_ks (pj, "fcn_name", fcn->name);
|
|
}
|
|
RFlagItem *fi = r_flag_get_at (core->flags, fcn? fcn->addr: ref->addr, true);
|
|
if (fi) {
|
|
if (fcn) {
|
|
if (strcmp (fcn->name, fi->name)) {
|
|
pj_ks (pj, "flag", fi->name);
|
|
}
|
|
} else {
|
|
pj_k (pj, "name");
|
|
if (fi->offset != ref->addr) {
|
|
int delta = (int)(ref->addr - fi->offset);
|
|
char *name_ref = r_str_newf ("%s+%d", fi->name, delta);
|
|
pj_s (pj, name_ref);
|
|
free (name_ref);
|
|
} else {
|
|
pj_s (pj, fi->name);
|
|
}
|
|
}
|
|
if (fi->realname && strcmp (fi->name, fi->realname)) {
|
|
char *escaped = r_str_escape (fi->realname);
|
|
if (escaped) {
|
|
pj_ks (pj, "realname", escaped);
|
|
free (escaped);
|
|
}
|
|
}
|
|
} else {
|
|
RFlagItem *fi = r_flag_get_at (core->flags, fcn? fcn->addr: ref->addr, false);
|
|
if (fi) {
|
|
pj_ks (pj, "near_name", fi->name);
|
|
pj_kn (pj, "near_addr", fi->offset);
|
|
}
|
|
}
|
|
char *refname = core->anal->coreb.getNameDelta (core, ref->at);
|
|
if (refname) {
|
|
r_str_replace_ch (refname, ' ', 0, true);
|
|
pj_ks (pj, "refname", refname);
|
|
free (refname);
|
|
}
|
|
pj_end (pj);
|
|
free (str);
|
|
}
|
|
pj_end (pj);
|
|
r_cons_printf ("%s", pj_string (pj));
|
|
pj_free (pj);
|
|
r_cons_newline ();
|
|
} else if (input[1] == 'g') { // axtg
|
|
r_list_foreach (list, iter, ref) {
|
|
char *str = r_core_cmd_strf (core, "fd 0x%"PFMT64x, ref->addr);
|
|
if (!str) {
|
|
str = strdup ("?\n");
|
|
}
|
|
r_str_trim_tail (str);
|
|
r_cons_printf ("agn 0x%" PFMT64x " \"%s\"\n", ref->addr, str);
|
|
free (str);
|
|
}
|
|
if (input[2] != '*') {
|
|
RAnalFunction *fcn = r_anal_get_fcn_in (core->anal, addr, 0);
|
|
r_cons_printf ("agn 0x%" PFMT64x " \"%s\"\n", addr, fcn?fcn->name: "$$");
|
|
}
|
|
r_list_foreach (list, iter, ref) {
|
|
r_cons_printf ("age 0x%" PFMT64x " 0x%"PFMT64x"\n", ref->addr, addr);
|
|
}
|
|
} else if (input[1] == '*') { // axt*
|
|
// TODO: implement multi-line comments
|
|
r_list_foreach (list, iter, ref) {
|
|
r_cons_printf ("CCa 0x%" PFMT64x " \"XREF type %d at 0x%" PFMT64x"%s\n",
|
|
ref->addr, ref->type, addr, iter->n? ",": "");
|
|
}
|
|
} else if (input[1] == ' ' || input[1] == 0) { // "axt"
|
|
RAnalFunction *fcn;
|
|
r_list_foreach (list, iter, ref) {
|
|
fcn = r_anal_get_fcn_in (core->anal, ref->addr, 0);
|
|
char *buf_asm = get_buf_asm (core, addr, ref->addr, fcn, true);
|
|
const char *comment = r_meta_get_string (core->anal, R_META_TYPE_COMMENT, ref->addr);
|
|
char *print_comment = NULL;
|
|
const char *nl = comment ? strchr (comment, '\n') : NULL;
|
|
if (nl) { // display only until the first newline
|
|
comment = print_comment = r_str_ndup (comment, nl - comment);
|
|
}
|
|
char *buf_fcn = comment
|
|
? r_str_newf ("%s; %s", fcn ? fcn->name : "(nofunc)", comment)
|
|
: r_str_newf ("%s", fcn ? fcn->name : "(nofunc)");
|
|
free (print_comment);
|
|
r_cons_printf ("%s 0x%" PFMT64x " [%s:%s] %s\n",
|
|
buf_fcn, ref->addr, r_anal_ref_type_tostring (ref->type),
|
|
r_anal_ref_perm_tostring (ref), buf_asm);
|
|
free (buf_asm);
|
|
free (buf_fcn);
|
|
}
|
|
} else {
|
|
r_core_cmd_help (core, help_msg_axt);
|
|
break;
|
|
}
|
|
} else {
|
|
if (input[1] == 'j') { // "axtj"
|
|
PJ *pj = pj_new ();
|
|
if (!pj) {
|
|
return false;
|
|
}
|
|
pj_a (pj);
|
|
pj_end (pj);
|
|
r_cons_println (pj_string (pj));
|
|
pj_free (pj);
|
|
}
|
|
}
|
|
r_list_free (list);
|
|
} break;
|
|
case 'f':
|
|
if (input[1] == 'f') { // "axff"
|
|
RAnalFunction * fcn = r_anal_get_fcn_in (core->anal, addr, 0);
|
|
RListIter *iter;
|
|
PJ *pj = NULL;
|
|
RAnalRef *refi;
|
|
if (input[2] == 'j') { // "axffj"
|
|
// start a new JSON object
|
|
pj = pj_new ();
|
|
pj_a (pj);
|
|
}
|
|
if (fcn) {
|
|
RList *refs = r_anal_function_get_refs (fcn);
|
|
r_list_foreach (refs, iter, refi) {
|
|
RFlagItem *f = r_flag_get_at (core->flags, refi->addr, true);
|
|
const char *name = f ? f->name: "";
|
|
if (pj) {
|
|
pj_o (pj);
|
|
pj_ks (pj, "type", r_anal_ref_type_tostring(refi->type));
|
|
pj_kn (pj, "at", refi->at);
|
|
pj_kn (pj, "ref", refi->addr);
|
|
pj_ks (pj, "name", name);
|
|
pj_end (pj);
|
|
} else {
|
|
r_cons_printf ("%s 0x%08"PFMT64x" 0x%08"PFMT64x" %s\n",
|
|
r_anal_ref_type_tostring(refi->type), refi->at, refi->addr, name);
|
|
}
|
|
}
|
|
if (pj) {
|
|
pj_end (pj);
|
|
r_cons_println (pj_string (pj));
|
|
}
|
|
} else {
|
|
eprintf ("Cannot find any function\n");
|
|
}
|
|
pj_free (pj);
|
|
} else { // "axf"
|
|
if (input[1] == '?') { // "axf?"
|
|
r_core_cmd_help (core, help_msg_axf);
|
|
break;
|
|
}
|
|
if (input[1] == 'm') { // "axfm"
|
|
// like aflm but reversed
|
|
axfm (core);
|
|
break;
|
|
}
|
|
RAsmOp asmop;
|
|
RList *list = NULL;
|
|
RAnalRef *ref;
|
|
RListIter *iter;
|
|
char *space = strchr (input, ' ');
|
|
if (space) {
|
|
addr = r_num_math (core->num, space + 1);
|
|
if (core->num->nc.errors > 0) {
|
|
eprintf ("Invalid argument.\n");
|
|
break;
|
|
}
|
|
} else {
|
|
addr = core->offset;
|
|
}
|
|
RAnalFunction * fcn = r_anal_get_fcn_in (core->anal, addr, 0);
|
|
if (input[1] == '.') { // "axf."
|
|
list = r_anal_xrefs_get_from (core->anal, addr);
|
|
if (!list) {
|
|
list = r_anal_function_get_refs (fcn);
|
|
}
|
|
} else {
|
|
list = r_anal_refs_get (core->anal, addr);
|
|
}
|
|
|
|
if (list) {
|
|
if (input[1] == 'q') { // "axfq"
|
|
r_list_foreach (list, iter, ref) {
|
|
r_cons_printf ("0x%" PFMT64x "\n", ref->at);
|
|
}
|
|
} else if (input[1] == 'j') { // "axfj"
|
|
PJ *pj = pj_new ();
|
|
if (!pj) {
|
|
return false;
|
|
}
|
|
pj_a (pj);
|
|
r_list_foreach (list, iter, ref) {
|
|
// TODO: Use r_core_anal_op(DISASM) instead of all those 4 lines
|
|
ut8 buf[16];
|
|
r_io_read_at (core->io, ref->addr, buf, sizeof (buf));
|
|
r_asm_set_pc (core->rasm, ref->addr);
|
|
r_asm_disassemble (core->rasm, &asmop, buf, sizeof (buf));
|
|
pj_o (pj);
|
|
pj_kn (pj, "from", ref->at);
|
|
pj_kn (pj, "to", ref->addr);
|
|
pj_ks (pj, "type", r_anal_ref_type_tostring (ref->type));
|
|
pj_ks (pj, "perm", r_anal_ref_perm_tostring (ref));
|
|
pj_ks (pj, "opcode", r_asm_op_get_asm (&asmop));
|
|
pj_end (pj);
|
|
}
|
|
pj_end (pj);
|
|
r_cons_println (pj_string (pj));
|
|
pj_free (pj);
|
|
} else if (input[1] == '*') { // "axf*"
|
|
// TODO: implement multi-line comments
|
|
r_list_foreach (list, iter, ref) {
|
|
r_cons_printf ("CCa 0x%" PFMT64x " \"XREF from 0x%u \n",
|
|
ref->at, ref->type);
|
|
}
|
|
} else { // "axf"
|
|
char str[512];
|
|
int has_color = core->print->flags & R_PRINT_FLAGS_COLOR;
|
|
r_list_foreach (list, iter, ref) {
|
|
ut8 buf[16] = {0};
|
|
char *desc;
|
|
char *desc_to_free = NULL;
|
|
RFlagItem *flag = r_flag_get_at (core->flags, ref->addr, false);
|
|
if (flag) {
|
|
desc = flag->name;
|
|
} else {
|
|
r_io_read_at (core->io, ref->addr, buf, sizeof (buf));
|
|
r_asm_set_pc (core->rasm, ref->addr);
|
|
r_asm_disassemble (core->rasm, &asmop, buf, sizeof (buf));
|
|
RAnalHint *hint = r_anal_hint_get (core->anal, ref->addr);
|
|
r_parse_filter (core->parser, ref->addr, core->flags, hint, r_asm_op_get_asm (&asmop),
|
|
str, sizeof (str), be);
|
|
r_anal_hint_free (hint);
|
|
if (has_color) {
|
|
desc = desc_to_free = r_print_colorize_opcode (core->print, str,
|
|
core->cons->context->pal.reg, core->cons->context->pal.num, false, fcn ? fcn->addr : 0);
|
|
} else {
|
|
desc = str;
|
|
}
|
|
}
|
|
r_cons_printf ("%s 0x%" PFMT64x " %s",
|
|
r_anal_ref_type_tostring (ref->type), ref->addr, desc);
|
|
|
|
if (R_ANAL_REF_TYPE_MASK (ref->type) == R_ANAL_REF_TYPE_CALL) {
|
|
RAnalOp aop;
|
|
r_anal_op (core->anal, &aop, ref->addr, buf, sizeof (buf), R_ANAL_OP_MASK_BASIC);
|
|
if (aop.type == R_ANAL_OP_TYPE_UCALL) {
|
|
cmd_anal_ucall_ref (core, ref->addr);
|
|
}
|
|
}
|
|
r_cons_newline ();
|
|
free (desc_to_free);
|
|
}
|
|
}
|
|
} else {
|
|
if (input[1] == 'j') { // "axfj"
|
|
r_cons_print ("[]\n");
|
|
}
|
|
}
|
|
r_list_free (list);
|
|
}
|
|
break;
|
|
case 'F': // "axF"
|
|
find_refs (core, input + 1);
|
|
break;
|
|
case 'C': // "axC"
|
|
case 'c': // "axc"
|
|
case 'r': // "axr"
|
|
case 'w': // "axw"
|
|
case 'd': // "axd"
|
|
case 's': // "axs"
|
|
case ' ': // "ax "
|
|
{
|
|
char *ptr = strdup (r_str_trim_head_ro ((char *)input + 1));
|
|
int n = r_str_word_set0 (ptr);
|
|
ut64 at = core->offset;
|
|
ut64 addr = UT64_MAX;
|
|
RAnalRefType reftype = r_anal_xrefs_type (input[0]);
|
|
switch (n) {
|
|
case 2: // get at
|
|
at = r_num_math (core->num, r_str_word_get0 (ptr, 1));
|
|
/* fall through */
|
|
case 1: // get addr
|
|
addr = r_num_math (core->num, r_str_word_get0 (ptr, 0));
|
|
break;
|
|
default:
|
|
free (ptr);
|
|
return false;
|
|
}
|
|
r_anal_xrefs_set (core->anal, at, addr, reftype);
|
|
free (ptr);
|
|
}
|
|
break;
|
|
default:
|
|
case '?':
|
|
r_core_cmd_help (core, help_msg_ax);
|
|
break;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
static void cmd_anal_hint(RCore *core, const char *input) {
|
|
switch (input[0]) {
|
|
case '?':
|
|
if (input[1]) {
|
|
ut64 addr = r_num_math (core->num, input + 1);
|
|
r_core_anal_hint_print (core->anal, addr, 0);
|
|
} else {
|
|
r_core_cmd_help (core, help_msg_ah);
|
|
}
|
|
break;
|
|
case '.': // "ah."
|
|
r_core_anal_hint_print (core->anal, core->offset, 0);
|
|
break;
|
|
case 'a': // "aha" set arch
|
|
if (input[1] == ' ') {
|
|
char *ptr = strdup (input + 2);
|
|
r_str_word_set0 (ptr);
|
|
const char *arch = r_str_word_get0 (ptr, 0);
|
|
r_anal_hint_set_arch (core->anal, core->offset, !arch || strcmp (arch, "0") == 0 ? NULL : arch);
|
|
free (ptr);
|
|
} else if (input[1] == '-') {
|
|
r_anal_hint_unset_arch (core->anal, core->offset);
|
|
} else {
|
|
eprintf ("Missing argument\n");
|
|
}
|
|
break;
|
|
case 'o': // "aho"
|
|
if (input[1] == '*') {
|
|
// show in r2
|
|
RAnalHint *hint = r_anal_hint_get (core->anal, core->offset);
|
|
if (hint && hint->opcode) {
|
|
r_cons_printf ("aho %s @ 0x%08"PFMT64x"\n", hint->opcode, hint->addr);
|
|
}
|
|
r_anal_hint_free (hint);
|
|
} else if (input[1] == 0) {
|
|
// show if any
|
|
RAnalHint *hint = r_anal_hint_get (core->anal, core->offset);
|
|
if (hint && hint->type > 0) {
|
|
r_cons_printf ("%s\n", r_anal_optype_to_string (hint->type));
|
|
}
|
|
r_anal_hint_free (hint);
|
|
} else if (input[1] == '-') {
|
|
ut64 off = input[2]? r_num_math (core->num, input + 2): core->offset;
|
|
r_anal_hint_unset_bits (core->anal, off);
|
|
} else if (input[1] == ' ') {
|
|
const char *arg = r_str_trim_head_ro (input + 1);
|
|
const int type = r_anal_optype_from_string (arg);
|
|
if (type != -1) {
|
|
r_anal_hint_set_type (core->anal, core->offset, type);
|
|
} else {
|
|
eprintf ("Unknown opcode type. Try: io, acmp, add, sync, call, cjmp, cmp, nop, ...\n");
|
|
}
|
|
} else {
|
|
r_core_cmd_help (core, help_msg_aho);
|
|
}
|
|
break;
|
|
case 'b': // "ahb" set bits
|
|
if (input[1] == '?') {
|
|
r_core_cmd_help (core, help_msg_ahb);
|
|
} else if (input[1] == ' ') {
|
|
char *ptr = r_str_trim_dup (input + 2);
|
|
int bits;
|
|
int i = r_str_word_set0 (ptr);
|
|
if (i == 2) {
|
|
r_num_math (core->num, r_str_word_get0 (ptr, 1));
|
|
}
|
|
bits = r_num_math (core->num, r_str_word_get0 (ptr, 0));
|
|
r_anal_hint_set_bits (core->anal, core->offset, bits);
|
|
free (ptr);
|
|
} else if (input[1] == '-') {
|
|
if (!strcmp (input + 2, "*")) {
|
|
eprintf ("Delete all asm.bits hints is not yet supported.\n");
|
|
} else {
|
|
ut64 off = input[2]? r_num_math (core->num, input + 2): core->offset;
|
|
r_anal_hint_unset_bits (core->anal, off);
|
|
}
|
|
} else {
|
|
RAnalHint *hint = r_anal_hint_get (core->anal, core->offset);
|
|
if (hint && hint->bits) {
|
|
r_cons_printf ("%d\n", hint->bits);
|
|
}
|
|
r_anal_hint_free (hint);
|
|
}
|
|
break;
|
|
case 'i': // "ahi"
|
|
if (input[1] == '?') {
|
|
r_core_cmd_help (core, help_msg_ahi);
|
|
break;
|
|
}
|
|
if (input[1] == '-') { // "ahi-"
|
|
ut64 addr = r_num_get (core->num, input + 2);
|
|
// add ahi-*
|
|
r_anal_hint_set_immbase (core->anal, addr? addr: core->offset, 0);
|
|
break;
|
|
}
|
|
if (isdigit ((unsigned char)input[1])) {
|
|
r_anal_hint_set_nword (core->anal, core->offset, input[1] - '0');
|
|
input++;
|
|
}
|
|
if (input[1] == ' ') {
|
|
// You can either specify immbase with letters, or numbers
|
|
int base;
|
|
if (r_str_startswith (input + 2, "10u") || r_str_startswith (input + 2, "du")) {
|
|
base = 11;
|
|
} else {
|
|
base = (input[2] == 's') ? 1 :
|
|
(input[2] == 'b') ? 2 :
|
|
(input[2] == 'p') ? 3 :
|
|
(input[2] == 'o') ? 8 :
|
|
(input[2] == 'd') ? 10 :
|
|
(input[2] == 'h') ? 16 :
|
|
(input[2] == 'i') ? 32 : // ip address
|
|
(input[2] == 'S') ? 80 : // syscall
|
|
(int) r_num_math (core->num, input + 1);
|
|
}
|
|
r_anal_hint_set_immbase (core->anal, core->offset, base);
|
|
} else if (!input[1]) {
|
|
RAnalHint *hint = r_anal_hint_get (core->anal, core->offset);
|
|
if (hint && hint->immbase) {
|
|
r_cons_printf ("%d\n", hint->immbase);
|
|
}
|
|
r_anal_hint_free (hint);
|
|
} else {
|
|
r_core_cmd_help (core, help_msg_ahi);
|
|
}
|
|
break;
|
|
case 'h': // "ahh"
|
|
if (input[1] == '-') {
|
|
r_anal_hint_unset_high (core->anal, core->offset);
|
|
} else if (input[1] == ' ') {
|
|
r_anal_hint_set_high (core->anal, r_num_math (core->num, input + 1));
|
|
} else {
|
|
r_anal_hint_set_high (core->anal, core->offset);
|
|
}
|
|
break;
|
|
case 'c': // "ahc"
|
|
if (input[1] == ' ') {
|
|
r_anal_hint_set_jump (
|
|
core->anal, core->offset,
|
|
r_num_math (core->num, input + 1));
|
|
} else if (input[1] == '-') {
|
|
r_anal_hint_unset_jump (core->anal, core->offset);
|
|
}
|
|
break;
|
|
case 'f': // "ahf"
|
|
if (input[1] == ' ') {
|
|
r_anal_hint_set_fail (
|
|
core->anal, core->offset,
|
|
r_num_math (core->num, input + 1));
|
|
} else if (input[1] == '-') {
|
|
r_anal_hint_unset_fail (core->anal, core->offset);
|
|
}
|
|
break;
|
|
case 'F': // "ahF" set stackframe size
|
|
if (input[1] == ' ') {
|
|
r_anal_hint_set_stackframe (
|
|
core->anal, core->offset,
|
|
r_num_math (core->num, input + 1));
|
|
} else if (input[1] == '-') {
|
|
r_anal_hint_unset_stackframe (core->anal, core->offset);
|
|
}
|
|
break;
|
|
case 's': // "ahs" set size (opcode length)
|
|
if (input[1] == ' ') {
|
|
r_anal_hint_set_size (core->anal, core->offset, atoi (input + 1));
|
|
} else if (input[1] == '-') {
|
|
r_anal_hint_unset_size (core->anal, core->offset);
|
|
} else {
|
|
eprintf ("Usage: ahs 16\n");
|
|
}
|
|
break;
|
|
case 'S': // "ahS" set asm.syntax
|
|
if (input[1] == ' ') {
|
|
r_anal_hint_set_syntax (core->anal, core->offset, input + 2);
|
|
} else if (input[1] == '-') {
|
|
r_anal_hint_unset_syntax (core->anal, core->offset);
|
|
} else {
|
|
eprintf ("Usage: ahS att\n");
|
|
}
|
|
break;
|
|
case 'd': // "ahd" set opcode string
|
|
if (input[1] == ' ') {
|
|
r_anal_hint_set_opcode (core->anal, core->offset, input + 2);
|
|
} else if (input[1] == '-') {
|
|
r_anal_hint_unset_opcode (core->anal, core->offset);
|
|
} else {
|
|
eprintf ("Usage: ahd popall\n");
|
|
}
|
|
break;
|
|
case 'e': // "ahe" set ESIL string
|
|
if (input[1] == ' ') {
|
|
r_anal_hint_set_esil (core->anal, core->offset, input + 2);
|
|
} else if (input[1] == '-') {
|
|
r_anal_hint_unset_esil (core->anal, core->offset);
|
|
} else {
|
|
eprintf ("Usage: ahe r0,pc,=\n");
|
|
}
|
|
break;
|
|
#if 0
|
|
case 'e': // set endian
|
|
if (input[1] == ' ') {
|
|
r_anal_hint_set_opcode (core->anal, core->offset, atoi (input + 1));
|
|
} else if (input[1] == '-') {
|
|
r_anal_hint_unset_opcode (core->anal, core->offset);
|
|
}
|
|
break;
|
|
#endif
|
|
case 'p': // "ahp"
|
|
if (input[1] == ' ') {
|
|
r_anal_hint_set_pointer (core->anal, core->offset, r_num_math (core->num, input + 1));
|
|
} else if (input[1] == '-') { // "ahp-"
|
|
r_anal_hint_unset_pointer (core->anal, core->offset);
|
|
}
|
|
break;
|
|
case 'r': // "ahr"
|
|
// XXX isnt this the same as 'aho ret' ?
|
|
if (input[1] == ' ') {
|
|
r_anal_hint_set_ret (core->anal, core->offset, r_num_math (core->num, input + 1));
|
|
} else if (input[1] == '-') { // "ahr-"
|
|
r_anal_hint_unset_ret (core->anal, core->offset);
|
|
} else {
|
|
r_core_cmd_help (core, help_msg_ahr);
|
|
}
|
|
break;
|
|
case '*': // "ah*"
|
|
case 'j': // "ahj"
|
|
case '\0': // "ah"
|
|
if (input[0] && input[1] == ' ') {
|
|
char *ptr = strdup (r_str_trim_head_ro (input + 2));
|
|
r_str_word_set0 (ptr);
|
|
ut64 addr = r_num_math (core->num, r_str_word_get0 (ptr, 0));
|
|
r_core_anal_hint_print (core->anal, addr, input[0]);
|
|
free (ptr);
|
|
} else {
|
|
r_core_anal_hint_list (core->anal, input[0]);
|
|
}
|
|
break;
|
|
case 'v': // "ahv"
|
|
if (input[1] == ' ') {
|
|
r_anal_hint_set_val (
|
|
core->anal, core->offset,
|
|
r_num_math (core->num, input + 1));
|
|
} else if (input[1] == '-') {
|
|
r_anal_hint_unset_val (core->anal, core->offset);
|
|
}
|
|
break;
|
|
case '-': // "ah-"
|
|
if (input[1]) {
|
|
if (input[1] == '*') {
|
|
r_anal_hint_clear (core->anal);
|
|
} else {
|
|
char *ptr = strdup (r_str_trim_head_ro (input + 1));
|
|
ut64 addr;
|
|
int size = 1;
|
|
int i = r_str_word_set0 (ptr);
|
|
if (i == 2) {
|
|
size = r_num_math (core->num, r_str_word_get0 (ptr, 1));
|
|
}
|
|
const char *a0 = r_str_word_get0 (ptr, 0);
|
|
if (a0 && *a0) {
|
|
addr = r_num_math (core->num, a0);
|
|
} else {
|
|
addr = core->offset;
|
|
}
|
|
r_anal_hint_del (core->anal, addr, size);
|
|
free (ptr);
|
|
}
|
|
} else {
|
|
r_anal_hint_clear (core->anal);
|
|
} break;
|
|
case 't': // "aht"
|
|
switch (input[1]) {
|
|
case 's': { // "ahts"
|
|
char *off = strdup (input + 2);
|
|
r_str_trim (off);
|
|
int toff = r_num_math (NULL, off);
|
|
if (toff) {
|
|
RList *typeoffs = r_type_get_by_offset (core->anal->sdb_types, toff);
|
|
RListIter *iter;
|
|
char *ty;
|
|
r_list_foreach (typeoffs, iter, ty) {
|
|
r_cons_printf ("%s\n", ty);
|
|
}
|
|
r_list_free (typeoffs);
|
|
}
|
|
free (off);
|
|
break;
|
|
}
|
|
case ' ': {
|
|
// r_anal_hint_set_opcode (core->anal, core->offset, input + 2);
|
|
const char *off = NULL;
|
|
char *type = strdup (r_str_trim_head_ro (input + 2));
|
|
char *idx = strchr (type, ' ');
|
|
if (idx) {
|
|
*idx++ = 0;
|
|
off = idx;
|
|
}
|
|
char *ptr = strchr (type, '=');
|
|
ut64 offimm = 0;
|
|
int i = 0;
|
|
ut64 addr;
|
|
|
|
if (ptr) {
|
|
*ptr++ = 0;
|
|
r_str_trim (ptr);
|
|
if (ptr && *ptr) {
|
|
addr = r_num_math (core->num, ptr);
|
|
} else {
|
|
eprintf ("address is unvalid\n");
|
|
free (type);
|
|
break;
|
|
}
|
|
} else {
|
|
addr = core->offset;
|
|
}
|
|
r_str_trim (type);
|
|
RAsmOp asmop;
|
|
RAnalOp op = {0};
|
|
ut8 code[128] = {0};
|
|
(void)r_io_read_at (core->io, core->offset, code, sizeof (code));
|
|
r_asm_set_pc (core->rasm, addr);
|
|
(void)r_asm_disassemble (core->rasm, &asmop, code, core->blocksize);
|
|
int ret = r_anal_op (core->anal, &op, core->offset, code, core->blocksize, R_ANAL_OP_MASK_VAL);
|
|
if (ret >= 0) {
|
|
// HACK: Just convert only the first imm seen
|
|
for (i = 0; i < 3; i++) {
|
|
if (op.src[i]) {
|
|
if (op.src[i]->imm) {
|
|
offimm = op.src[i]->imm;
|
|
} else if (op.src[i]->delta) {
|
|
offimm = op.src[i]->delta;
|
|
}
|
|
}
|
|
}
|
|
if (!offimm && op.dst) {
|
|
if (op.dst->imm) {
|
|
offimm = op.dst->imm;
|
|
} else if (op.dst->delta) {
|
|
offimm = op.dst->delta;
|
|
}
|
|
}
|
|
if (offimm != 0) {
|
|
if (off) {
|
|
offimm += r_num_math (NULL, off);
|
|
}
|
|
// TODO: Allow to select from multiple choices
|
|
RList *otypes = r_type_get_by_offset (core->anal->sdb_types, offimm);
|
|
RListIter *iter;
|
|
char *otype = NULL;
|
|
r_list_foreach (otypes, iter, otype) {
|
|
// TODO: I don't think we should silently error, it is confusing
|
|
if (!strcmp (type, otype)) {
|
|
//eprintf ("Adding type offset %s\n", type);
|
|
r_type_link_offset (core->anal->sdb_types, type, addr);
|
|
r_anal_hint_set_offset (core->anal, addr, otype);
|
|
break;
|
|
}
|
|
}
|
|
if (!otype) {
|
|
eprintf ("wrong type for opcode offset\n");
|
|
}
|
|
r_list_free (otypes);
|
|
}
|
|
}
|
|
r_anal_op_fini (&op);
|
|
free (type);
|
|
} break;
|
|
default:
|
|
r_core_cmd_help (core, help_msg_aht);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
static void agraph_print_node_gml(RANode *n, void *user) {
|
|
r_cons_printf (" node [\n"
|
|
" id %d\n"
|
|
" label \"%s\"\n"
|
|
" ]\n", n->gnode->idx, n->title);
|
|
}
|
|
|
|
static void agraph_print_edge_gml(RANode *from, RANode *to, void *user) {
|
|
r_cons_printf (" edge [\n"
|
|
" source %d\n"
|
|
" target %d\n"
|
|
" ]\n", from->gnode->idx, to->gnode->idx
|
|
);
|
|
}
|
|
|
|
static void agraph_print_node_dot(RANode *n, void *user) {
|
|
if (R_STR_ISEMPTY (n->body)) {
|
|
r_cons_printf ("\"%s\" [URL=\"%s\", color=\"lightgray\", label=\"%s\"]\n",
|
|
n->title, n->title, n->title);
|
|
} else {
|
|
char *label = strdup (n->body);
|
|
//label = r_str_replace (label, "\n", "\\l", 1);
|
|
r_cons_printf ("\"%s\" [URL=\"%s\", color=\"lightgray\", label=\"%s\\n%s\"]\n",
|
|
n->title, n->title, n->title, label);
|
|
free (label);
|
|
}
|
|
}
|
|
|
|
static void agraph_print_node(RANode *n, void *user) {
|
|
size_t len = strlen (n->body);
|
|
|
|
if (len > 0 && n->body[len - 1] == '\n') {
|
|
len--;
|
|
}
|
|
char *encbody = r_base64_encode_dyn (n->body, len);
|
|
char *cmd = r_str_newf ("agn \"%s\" base64:%s\n", n->title, encbody);
|
|
r_cons_print (cmd);
|
|
free (cmd);
|
|
free (encbody);
|
|
}
|
|
|
|
static char *getViewerPath(void) {
|
|
int i;
|
|
const char *viewers[] = {
|
|
#if __WINDOWS__
|
|
"explorer",
|
|
#else
|
|
"open",
|
|
"geeqie",
|
|
"gqview",
|
|
"eog",
|
|
"xdg-open",
|
|
#endif
|
|
NULL
|
|
};
|
|
for (i = 0; viewers[i]; i++) {
|
|
char *viewerPath = r_file_path (viewers[i]);
|
|
if (viewerPath && strcmp (viewerPath, viewers[i])) {
|
|
return viewerPath;
|
|
}
|
|
free (viewerPath);
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
static char *dot_executable_path(void) {
|
|
const char *dot = "dot";
|
|
char *dotPath = r_file_path (dot);
|
|
if (!strcmp (dotPath, dot)) {
|
|
free (dotPath);
|
|
dot = "xdot";
|
|
dotPath = r_file_path (dot);
|
|
if (!strcmp (dotPath, dot)) {
|
|
free (dotPath);
|
|
return NULL;
|
|
}
|
|
}
|
|
return dotPath;
|
|
}
|
|
|
|
static bool convert_dot_to_image(RCore *core, const char *dot_file, const char *save_path) {
|
|
char *dot = dot_executable_path ();
|
|
bool result = false;
|
|
if (!dot) {
|
|
eprintf ("Graphviz not found\n");
|
|
return false;
|
|
}
|
|
const char *ext = r_config_get (core->config, "graph.gv.format");
|
|
|
|
char *cmd = NULL;
|
|
if (save_path && *save_path) {
|
|
cmd = r_str_newf ("!%s -T%s -o%s a.dot;", dot, ext, save_path);
|
|
} else {
|
|
char *viewer = getViewerPath();
|
|
if (viewer) {
|
|
cmd = r_str_newf ("!%s -T%s -oa.%s a.dot;!%s a.%s",
|
|
dot, ext, ext, viewer, ext);
|
|
free (viewer);
|
|
} else {
|
|
eprintf ("Cannot find a valid picture viewer\n");
|
|
goto end;
|
|
}
|
|
}
|
|
r_core_cmd0 (core, cmd);
|
|
result = true;
|
|
end:
|
|
free (cmd);
|
|
free (dot);
|
|
return result;
|
|
}
|
|
|
|
static bool convert_dotcmd_to_image(RCore *core, char *r2_cmd, const char *save_path) {
|
|
if (save_path && *save_path) {
|
|
r_cons_printf ("Saving to file '%s'...\n", save_path);
|
|
r_cons_flush ();
|
|
}
|
|
r_core_cmdf (core, "%s > a.dot", r2_cmd); // TODO: check error here
|
|
return convert_dot_to_image (core, "a.dot", save_path);
|
|
}
|
|
|
|
static bool convert_dot_str_to_image(RCore *core, char *str, const char *save_path) {
|
|
if (save_path && *save_path) {
|
|
r_cons_printf ("Saving to file '%s'...\n", save_path);
|
|
r_cons_flush ();
|
|
}
|
|
if (!r_file_dump ("a.dot", (const unsigned char *)str, -1, false)) {
|
|
return false;
|
|
}
|
|
return convert_dot_to_image (core, "a.dot", save_path);
|
|
}
|
|
|
|
static void agraph_print_edge_dot(RANode *from, RANode *to, void *user) {
|
|
RCore *core = (RCore *)user;
|
|
ut64 a = r_num_math (NULL, from->title);
|
|
ut64 b = r_num_math (NULL, to->title);
|
|
r_strf_var (k, 64, "agraph.edge.0x%"PFMT64x"_0x%"PFMT64x".highlight", a, b);
|
|
if (sdb_exists (core->sdb, k)) {
|
|
r_cons_printf ("\"%s\" -> \"%s\" [color=cyan]\n", from->title, to->title);
|
|
} else {
|
|
r_cons_printf ("\"%s\" -> \"%s\"\n", from->title, to->title);
|
|
}
|
|
}
|
|
|
|
static void agraph_print_edge(RANode *from, RANode *to, void *user) {
|
|
r_cons_printf ("age \"%s\" \"%s\"\n", from->title, to->title);
|
|
}
|
|
|
|
static void cmd_agraph_node(RCore *core, const char *input) {
|
|
switch (*input) {
|
|
case ' ': { // "agn"
|
|
char *newbody = NULL;
|
|
char *body;
|
|
int n_args, B_LEN = strlen ("base64:");
|
|
char *color = NULL;
|
|
input++;
|
|
char **args = r_str_argv (input, &n_args);
|
|
if (n_args < 1 || n_args > 3) {
|
|
R_LOG_ERROR ("wrong arguments for agn");
|
|
r_str_argv_free (args);
|
|
break;
|
|
}
|
|
// strdup cause there is double free in r_str_argv_free due to a realloc call
|
|
if (n_args > 1) {
|
|
body = strdup (args[1]);
|
|
if (strncmp (body, "base64:", B_LEN) == 0) {
|
|
body = r_str_replace (body, "\\n", "", true);
|
|
newbody = (char *)r_base64_decode_dyn (body + B_LEN, -1);
|
|
free (body);
|
|
if (!newbody) {
|
|
R_LOG_ERROR ("Cannot allocate buffer");
|
|
r_str_argv_free (args);
|
|
break;
|
|
}
|
|
body = newbody;
|
|
}
|
|
body = r_str_append (body, "\n");
|
|
if (n_args > 2) {
|
|
RColor kolor = {0};
|
|
(void)r_cons_pal_parse (args[2], &kolor);
|
|
color = r_cons_rgb_str (NULL, -1, &kolor);
|
|
}
|
|
} else {
|
|
body = strdup ("");
|
|
}
|
|
r_agraph_add_node (core->graph, args[0], body, color);
|
|
r_str_argv_free (args);
|
|
free (body);
|
|
free (color);
|
|
// free newbody it's not necessary since r_str_append reallocate the space
|
|
break;
|
|
}
|
|
case '-': { // "agn-"
|
|
input++;
|
|
int n_args;
|
|
char **args = r_str_argv (input, &n_args);
|
|
if (n_args != 1) {
|
|
R_LOG_ERROR ("Wrong arguments");
|
|
r_str_argv_free (args);
|
|
break;
|
|
}
|
|
r_agraph_del_node (core->graph, args[0]);
|
|
r_str_argv_free (args);
|
|
break;
|
|
}
|
|
case '?':
|
|
default:
|
|
r_core_cmd_help (core, help_msg_agn);
|
|
break;
|
|
}
|
|
}
|
|
|
|
static bool cmd_ageh(RCore *core, const char *input) {
|
|
if (!*input) {
|
|
r_core_cmd0 (core, "k~agraph.edge");
|
|
return false;
|
|
}
|
|
bool add = true;
|
|
if (*input == '-') {
|
|
add = false;
|
|
input++;
|
|
}
|
|
char *arg = r_str_trim_dup (input + 1);
|
|
char *sp = strchr (arg, ' ');
|
|
if (!sp) {
|
|
free (arg);
|
|
return false;
|
|
}
|
|
*sp++ = 0;
|
|
ut64 a = r_num_math (core->num, arg);
|
|
ut64 b = r_num_math (core->num, sp);
|
|
|
|
r_strf_var (k, 64, "agraph.edge.0x%"PFMT64x"_0x%"PFMT64x".highlight", a, b);
|
|
sdb_set (core->sdb, k, add? "true": "", 0);
|
|
free (arg);
|
|
return true;
|
|
}
|
|
|
|
static void cmd_agraph_edge(RCore *core, const char *input) {
|
|
switch (*input) {
|
|
case ' ': // "age"
|
|
case '-': { // "age-"
|
|
RANode *u, *v;
|
|
char **args;
|
|
int n_args;
|
|
|
|
args = r_str_argv (input + 1, &n_args);
|
|
if (n_args != 2) {
|
|
r_cons_printf ("Wrong arguments\n");
|
|
r_str_argv_free (args);
|
|
break;
|
|
}
|
|
|
|
u = r_agraph_get_node (core->graph, args[0]);
|
|
v = r_agraph_get_node (core->graph, args[1]);
|
|
if (!u || !v) {
|
|
const char *arg = args[u? 1: 0];
|
|
r_cons_printf ("Node %s not found!\n", arg);
|
|
r_str_argv_free (args);
|
|
break;
|
|
}
|
|
if (*input == ' ') {
|
|
r_agraph_add_edge (core->graph, u, v, false);
|
|
} else {
|
|
r_agraph_del_edge (core->graph, u, v);
|
|
}
|
|
r_str_argv_free (args);
|
|
break;
|
|
}
|
|
case 'h':
|
|
cmd_ageh (core, input + 1);
|
|
break;
|
|
case '?':
|
|
default:
|
|
r_core_cmd_help (core, help_msg_age);
|
|
break;
|
|
}
|
|
}
|
|
|
|
R_API void cmd_agfb(RCore *core) {
|
|
const int c = r_config_get_b (core->config, "scr.color");
|
|
r_config_set_i (core->config, "scr.color", 0);
|
|
r_cons_push ();
|
|
r_core_visual_graph (core, NULL, NULL, false);
|
|
r_config_set_i (core->config, "scr.color", c);
|
|
char *s = strdup (r_cons_singleton()->context->buffer);
|
|
r_cons_pop ();
|
|
cmd_agfb2 (core, s);
|
|
free (s);
|
|
}
|
|
|
|
R_API void cmd_aggb(RCore *core) {
|
|
const int c = r_config_get_b (core->config, "scr.color");
|
|
r_config_set_i (core->config, "scr.color", 0);
|
|
r_cons_push ();
|
|
int ograph_zoom = r_config_get_i (core->config, "graph.zoom");
|
|
r_config_set_i (core->config, "graph.zoom", 1);
|
|
r_core_cmd0 (core, "agg");
|
|
r_config_set_i (core->config, "scr.color", c);
|
|
char *s = strdup (r_cons_singleton()->context->buffer);
|
|
r_cons_pop ();
|
|
cmd_agfb2 (core, s);
|
|
r_config_set_i (core->config, "graph.zoom", ograph_zoom);
|
|
free (s);
|
|
}
|
|
|
|
R_API void cmd_agfb3(RCore *core, const char *s, int x, int y) {
|
|
int h, w = r_str_size (s, &h);
|
|
RConsPixel *p = r_cons_pixel_new (w, h);
|
|
r_cons_pixel_sets (p, 0, 0, s);
|
|
r_cons_pixel_flush (p, x, y);
|
|
R_FREE (p);
|
|
}
|
|
|
|
R_API void cmd_agfb2(RCore *core, const char *s) {
|
|
int h, w = r_str_size (s, &h);
|
|
RConsPixel *p = r_cons_pixel_new (w, h);
|
|
r_cons_pixel_sets (p, 0, 0, s);
|
|
char *pix = r_cons_pixel_drain (p);
|
|
r_cons_printf ("%s\n", pix);
|
|
free (pix);
|
|
}
|
|
|
|
|
|
static char *mermaid_sanitize_str(const char *str) {
|
|
if (!str) {
|
|
return NULL;
|
|
}
|
|
size_t len = strlen (str) * 4 + 1; // '\n' -> "\x0a"
|
|
char *buf = malloc (len);
|
|
if (buf) {
|
|
size_t i;
|
|
for (i = 0; i < len - 5 && *str;) {
|
|
char c = *str++;
|
|
if (c < ' ' || c > '~' || c == '\\' || c == '"' || c == '<') {
|
|
snprintf (buf + i, 5, "\\x%02x", c); // 5 b/c null byte
|
|
i += 4;
|
|
} else {
|
|
buf[i++] = c;
|
|
}
|
|
}
|
|
buf[i] = '\0';
|
|
}
|
|
// buffer wont live long enough to merit a realloc
|
|
return buf;
|
|
}
|
|
|
|
static inline char *mermaid_title_body_node_str(const char *title, const char *body) {
|
|
char *t = mermaid_sanitize_str (title);
|
|
char *b = mermaid_sanitize_str (body);
|
|
if (t && b) {
|
|
char *ret = r_str_newf ("[%s]\\n%s", t, b);
|
|
free (t);
|
|
free (b);
|
|
return ret;
|
|
}
|
|
return t? t: b;
|
|
}
|
|
|
|
static char *mermaid_anod_body(RGraphNode *n) {
|
|
RANode *an = (RANode *)n->data;
|
|
return mermaid_title_body_node_str (an->title, an->body);
|
|
}
|
|
|
|
static char *mermaid_nodeinfo_body(RGraphNode *n) {
|
|
RGraphNodeInfo *nfo = (RGraphNodeInfo *)n->data;
|
|
return mermaid_title_body_node_str (nfo->title, nfo->body);
|
|
}
|
|
|
|
typedef char *(*node_content_cb) (RGraphNode *);
|
|
static void mermaid_graph(RGraph *graph, node_content_cb get_body) {
|
|
if (!graph) {
|
|
return;
|
|
}
|
|
if (r_list_empty (graph->nodes)) {
|
|
R_LOG_INFO ("The graph is empty");
|
|
return;
|
|
}
|
|
bool printit = true;
|
|
RStrBuf *nodes = r_strbuf_new ("stateDiagram-v2\n");
|
|
RStrBuf *edges = r_strbuf_new ("");
|
|
RGraphNode *n;
|
|
RListIter *it;
|
|
r_list_foreach (graph->nodes, it, n) {
|
|
char *free_body = get_body (n);
|
|
char *body = free_body? free_body: "";
|
|
printit &= r_strbuf_appendf (nodes, " state \"%s\" as node_%u\n", body, n->idx);
|
|
free (free_body);
|
|
|
|
// edgdes
|
|
RGraphNode *nxt;
|
|
RListIter *itt;
|
|
r_list_foreach (n->out_nodes, itt, nxt) {
|
|
printit &= r_strbuf_appendf (edges, " node_%u --> node_%u\n", n->idx, nxt->idx);
|
|
}
|
|
if (!printit) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (printit) {
|
|
char *n = r_strbuf_drain_nofree (nodes);
|
|
char *e = r_strbuf_drain_nofree (edges);
|
|
if (n && e) {
|
|
r_cons_print (n);
|
|
r_cons_print (e);
|
|
}
|
|
free (n);
|
|
free (e);
|
|
}
|
|
r_strbuf_free (nodes);
|
|
r_strbuf_free (edges);
|
|
}
|
|
|
|
typedef struct {
|
|
RAnalFunction *fcn;
|
|
char *name;
|
|
ut64 addr;
|
|
int size;
|
|
int x;
|
|
int y;
|
|
int w;
|
|
int h;
|
|
} TreeMapItem;
|
|
|
|
static void *add_item(RAnalFunction *f, const char *name, ut64 addr, ut64 size) {
|
|
TreeMapItem *item = R_NEW0 (TreeMapItem);
|
|
item->fcn = f;
|
|
item->addr = addr;
|
|
item->name = strdup (name);
|
|
item->size = size;
|
|
return item;
|
|
}
|
|
|
|
static void free_item(void *a) {
|
|
TreeMapItem *item = a;
|
|
if (item) {
|
|
free (item->name);
|
|
free (item);
|
|
}
|
|
}
|
|
|
|
static int bysize(const void *_a, const void *_b) {
|
|
const TreeMapItem *a = _a;
|
|
const TreeMapItem *b = _b;
|
|
return b->size - a->size;
|
|
}
|
|
|
|
static void treemap_layout(RConsCanvas *canvas, RList *maps) {
|
|
RListIter *iter;
|
|
TreeMapItem *mi;
|
|
int i = 0;
|
|
int n = r_list_length (maps);
|
|
if (n == 0) {
|
|
return;
|
|
}
|
|
int nx = 0;
|
|
int ny = 0;
|
|
int nw = canvas->w;
|
|
int nh = canvas->h;
|
|
float mfact = 0.33;
|
|
bool s = true; // layout
|
|
|
|
r_list_sort (maps, bysize);
|
|
r_list_foreach (maps, iter, mi) {
|
|
if ((i % 2 && nh / 2 > 2 * mi->w) || (!(i % 2) && nw / 2 > 2 * mi->w)) {
|
|
const int imodfour = (i % 4);
|
|
if (i < n - 1) {
|
|
if (i % 2) {
|
|
nh /= 2;
|
|
} else {
|
|
nw /= 2;
|
|
}
|
|
if (!s) {
|
|
if (imodfour == 2) {
|
|
nx += nw;
|
|
} else if (imodfour == 3) {
|
|
ny += nh;
|
|
}
|
|
}
|
|
}
|
|
switch (imodfour) {
|
|
case 0:
|
|
ny += s? nh: -nh;
|
|
break;
|
|
case 1:
|
|
nx += nw;
|
|
break;
|
|
case 2:
|
|
ny += nh;
|
|
break;
|
|
case 3:
|
|
nx += s? nw: -nw;
|
|
break;
|
|
}
|
|
if (i == 0) {
|
|
if (n != 1) {
|
|
nw = canvas->w * mfact;
|
|
}
|
|
ny = 0;
|
|
} else if (i == 1) {
|
|
nw = canvas->w - nw;
|
|
nx--;
|
|
} else if (i == 2) {
|
|
nh++;
|
|
// ny--;
|
|
}
|
|
if (2== (i%4)) {
|
|
nh++;
|
|
// nw--;
|
|
ny--;
|
|
}
|
|
i++;
|
|
}
|
|
mi->x = nx;
|
|
mi->y = ny;
|
|
mi->w = nw;
|
|
mi->h = nh;
|
|
}
|
|
}
|
|
|
|
R_API void r_core_agraph_treemap(RCore *core, int use_utf, const char *input) {
|
|
int a = r_config_get_i (core->config, "scr.color");
|
|
r_config_set_i (core->config, "scr.color", 0);
|
|
// walk all the functions and create a treemap and render it
|
|
int h, w = r_cons_get_size (&h);
|
|
w--;
|
|
h--;
|
|
RConsCanvas *canvas = r_cons_canvas_new (w, h);
|
|
r_cons_canvas_box (canvas, 1, 1, w - 1, h - 1, "");
|
|
RListIter *iter;
|
|
RAnalFunction *fcn = NULL;
|
|
RList *maps = r_list_newf (free_item);
|
|
#if 1
|
|
RList *list = r_anal_get_fcns (core->anal);
|
|
r_list_foreach (list, iter, fcn) {
|
|
ut64 fsz = r_anal_function_realsize (fcn);
|
|
r_list_append (maps, add_item (fcn, fcn->name, fcn->addr, fsz));
|
|
}
|
|
#else
|
|
RAnalBlock *bb;
|
|
if (!fcn) {
|
|
fcn = r_anal_get_function_at (core->anal, core->offset);
|
|
r_list_foreach (fcn->bbs, iter, bb) {
|
|
char *name = r_str_newf ("%d", (int)(size_t)(bb->addr - fcn->addr));
|
|
r_list_append (maps, add_item (fcn, name, bb->addr, bb->size));
|
|
free (name);
|
|
}
|
|
}
|
|
#endif
|
|
treemap_layout (canvas, maps);
|
|
TreeMapItem *mi;
|
|
r_list_foreach (maps, iter, mi) {
|
|
// char *s = r_core_cmd_strf (core, "pdb@0x%"PFMT64x"@e:asm.byte=0@e:asm.bytes=0", mi->addr);
|
|
char *s = r_core_cmd_strf (core, "pid@0x%"PFMT64x"@e:asm.bytes=0", mi->addr);
|
|
if (mi->w > 4 && mi->h > 3) {
|
|
char *ns = r_str_crop (s, 0, 0, mi->w * 2, mi->h - 2);
|
|
if (r_cons_canvas_gotoxy (canvas, mi->x + 2, mi->y + 2)) {
|
|
r_cons_canvas_write (canvas, ns);
|
|
}
|
|
free (ns);
|
|
}
|
|
free (s);
|
|
}
|
|
r_list_foreach (maps, iter, mi) {
|
|
if (r_cons_canvas_gotoxy (canvas, mi->x + 2, mi->y + 1)) {
|
|
r_cons_canvas_write (canvas, mi->name);
|
|
r_cons_canvas_box (canvas, mi->x, mi->y, mi->w, mi->h, "");
|
|
}
|
|
}
|
|
char *s = r_cons_canvas_to_string (canvas);
|
|
if (s) {
|
|
r_cons_println (s);
|
|
free (s);
|
|
}
|
|
r_list_free (maps);
|
|
r_config_set_i (core->config, "scr.color", a);
|
|
r_cons_canvas_free (canvas);
|
|
}
|
|
|
|
R_API void r_core_agraph_print(RCore *core, int use_utf, const char *input) {
|
|
if (use_utf != -1) {
|
|
r_config_set_i (core->config, "scr.utf8", use_utf);
|
|
}
|
|
switch (*input) {
|
|
case 0:
|
|
core->graph->can->linemode = r_config_get_i (core->config, "graph.linemode");
|
|
core->graph->can->color = r_config_get_i (core->config, "scr.color");
|
|
r_agraph_set_title (core->graph, r_config_get (core->config, "graph.title"));
|
|
r_agraph_print (core->graph);
|
|
break;
|
|
case 't': { // "aggt" - tiny graph
|
|
core->graph->is_tiny = true;
|
|
int e = r_config_get_i (core->config, "graph.edges");
|
|
r_config_set_i (core->config, "graph.edges", 0);
|
|
r_core_visual_graph (core, core->graph, NULL, false);
|
|
r_config_set_i (core->config, "graph.edges", e);
|
|
core->graph->is_tiny = false;
|
|
break;
|
|
}
|
|
case 'b': // "aggb"
|
|
cmd_aggb (core);
|
|
break;
|
|
case 'm': // "aggm"
|
|
if (core->graph) {
|
|
mermaid_graph (core->graph->graph, mermaid_anod_body);
|
|
}
|
|
break;
|
|
case 'k': // "aggk"
|
|
{
|
|
Sdb *db = r_agraph_get_sdb (core->graph);
|
|
if (db) {
|
|
char *o = sdb_querys (db, "null", 0, "*");
|
|
if (o) {
|
|
r_cons_print (o);
|
|
free (o);
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
case 'v': // "aggv"
|
|
case 'i': // "aggi" - open current core->graph in interactive mode
|
|
{
|
|
RANode *ran = r_agraph_get_first_node (core->graph);
|
|
if (ran) {
|
|
ut64 oseek = core->offset;
|
|
r_agraph_set_title (core->graph, r_config_get (core->config, "graph.title"));
|
|
r_agraph_set_curnode (core->graph, ran);
|
|
core->graph->force_update_seek = true;
|
|
core->graph->need_set_layout = true;
|
|
core->graph->layout = r_config_get_i (core->config, "graph.layout");
|
|
bool ov = r_cons_is_interactive ();
|
|
core->graph->need_update_dim = true;
|
|
int update_seek = r_core_visual_graph (core, core->graph, NULL, true);
|
|
r_config_set_b (core->config, "scr.interactive", ov);
|
|
r_cons_show_cursor (true);
|
|
r_cons_enable_mouse (false);
|
|
if (update_seek != -1) {
|
|
r_core_seek (core, oseek, false);
|
|
}
|
|
} else {
|
|
R_LOG_INFO ("This graph contains no nodes");
|
|
}
|
|
break;
|
|
}
|
|
case 'd': { // "aggd" - dot format
|
|
const char *font = r_config_get (core->config, "graph.font");
|
|
r_cons_printf ("digraph code {\nrankdir=LR;\noutputorder=edgesfirst\ngraph [bgcolor=azure];\n"
|
|
"edge [arrowhead=normal, color=\"#3030c0\" style=bold weight=2];\n"
|
|
"node [fillcolor=white, style=filled shape=box "
|
|
"fontname=\"%s\" fontsize=\"8\"];\n",
|
|
font);
|
|
r_agraph_foreach (core->graph, agraph_print_node_dot, core);
|
|
r_agraph_foreach_edge (core->graph, agraph_print_edge_dot, core);
|
|
r_cons_printf ("}\n");
|
|
break;
|
|
}
|
|
case '*': // "agg*" -
|
|
r_agraph_foreach (core->graph, agraph_print_node, core);
|
|
r_agraph_foreach_edge (core->graph, agraph_print_edge, core);
|
|
break;
|
|
case 'J':
|
|
case 'j': {
|
|
PJ *pj = r_core_pj_new (core);
|
|
if (!pj) {
|
|
return;
|
|
}
|
|
pj_o (pj);
|
|
pj_k (pj, "nodes");
|
|
pj_a (pj);
|
|
r_agraph_print_json (core->graph, pj);
|
|
pj_end (pj);
|
|
pj_end (pj);
|
|
r_cons_println (pj_string (pj));
|
|
pj_free (pj);
|
|
break;
|
|
}
|
|
case 'g':
|
|
r_cons_printf ("graph\n[\n"
|
|
"hierarchic 1\n"
|
|
"label \"\"\n"
|
|
"directed 1\n");
|
|
r_agraph_foreach (core->graph, agraph_print_node_gml, NULL);
|
|
r_agraph_foreach_edge (core->graph, agraph_print_edge_gml, NULL);
|
|
r_cons_print ("]\n");
|
|
break;
|
|
case 'w': // "aggw"
|
|
if (r_config_get_i (core->config, "graph.web")) {
|
|
r_core_cmd0 (core, "=H /graph/");
|
|
} else {
|
|
const char *filename = r_str_trim_head_ro (input + 1);
|
|
convert_dotcmd_to_image (core, "aggd", filename);
|
|
}
|
|
break;
|
|
default:
|
|
r_core_cmd_help (core, help_msg_ag);
|
|
break;
|
|
}
|
|
}
|
|
|
|
static void print_graph_agg(RGraph /*RGraphNodeInfo*/ *graph) {
|
|
RGraphNodeInfo *print_node;
|
|
RGraphNode *node, *target;
|
|
RListIter *it, *edge_it;
|
|
r_list_foreach (graph->nodes, it, node) {
|
|
char *encbody;
|
|
int len;
|
|
print_node = node->data;
|
|
if (R_STR_ISNOTEMPTY (print_node->body)) {
|
|
len = strlen (print_node->body);
|
|
|
|
if (len > 0 && print_node->body[len - 1] == '\n') {
|
|
len--;
|
|
}
|
|
encbody = r_base64_encode_dyn (print_node->body, len);
|
|
r_cons_printf ("agn \"%s\" base64:%s\n", print_node->title, encbody);
|
|
free (encbody);
|
|
} else {
|
|
r_cons_printf ("agn \"%s\"\n", print_node->title);
|
|
}
|
|
}
|
|
r_list_foreach (graph->nodes, it, node) {
|
|
print_node = node->data;
|
|
r_list_foreach (node->out_nodes, edge_it, target) {
|
|
RGraphNodeInfo *to = target->data;
|
|
r_cons_printf ("age \"%s\" \"%s\"\n", print_node->title, to->title);
|
|
}
|
|
}
|
|
}
|
|
|
|
static char *print_graph_dot(RCore *core, RGraph /*<RGraphNodeInfo>*/ *graph) {
|
|
const char *font = r_config_get (core->config, "graph.font");
|
|
char *node_properties = r_str_newf ("fontname=\"%s\"", font);
|
|
char *result = r_graph_drawable_to_dot (graph, node_properties, NULL);
|
|
free (node_properties);
|
|
return result;
|
|
}
|
|
|
|
static void r_core_graph_print(RCore *core, RGraph /*<RGraphNodeInfo>*/ *graph, int use_utf, bool use_offset, const char *input) {
|
|
RAGraph *agraph = NULL;
|
|
RListIter *it;
|
|
RListIter *edge_it;
|
|
RGraphNode *graphNode, *target;
|
|
RGraphNodeInfo *print_node;
|
|
if (use_utf != -1) {
|
|
r_config_set_i (core->config, "scr.utf8", use_utf);
|
|
}
|
|
switch (*input) {
|
|
case 0:
|
|
case 't':
|
|
case 'k':
|
|
case 'v':
|
|
case 'i': {
|
|
agraph = create_agraph_from_graph (graph);
|
|
switch (*input) {
|
|
case 0:
|
|
agraph->can->linemode = r_config_get_i (core->config, "graph.linemode");
|
|
agraph->can->color = r_config_get_i (core->config, "scr.color");
|
|
r_agraph_set_title (agraph,
|
|
r_config_get (core->config, "graph.title"));
|
|
r_agraph_print (agraph);
|
|
break;
|
|
case 't': { // "ag_t" - tiny graph
|
|
agraph->is_tiny = true;
|
|
int e = r_config_get_i (core->config, "graph.edges");
|
|
r_config_set_i (core->config, "graph.edges", 0);
|
|
r_core_visual_graph (core, agraph, NULL, false);
|
|
r_config_set_i (core->config, "graph.edges", e);
|
|
break;
|
|
}
|
|
case 'k': // "ag_k"
|
|
{
|
|
Sdb *db = r_agraph_get_sdb (agraph);
|
|
char *o = sdb_querys (db, "null", 0, "*");
|
|
r_cons_print (o);
|
|
free (o);
|
|
break;
|
|
}
|
|
case 'v': // "ag_v"
|
|
case 'i': // "ag_i" - open current core->graph in interactive mode
|
|
{
|
|
RANode *ran = r_agraph_get_first_node (agraph);
|
|
if (ran) {
|
|
ut64 oseek = core->offset;
|
|
r_agraph_set_title (agraph, r_config_get (core->config, "graph.title"));
|
|
r_agraph_set_curnode (agraph, ran);
|
|
agraph->force_update_seek = true;
|
|
agraph->need_set_layout = true;
|
|
agraph->layout = r_config_get_i (core->config, "graph.layout");
|
|
bool ov = r_cons_is_interactive ();
|
|
agraph->need_update_dim = true;
|
|
int update_seek = r_core_visual_graph (core, agraph, NULL, true);
|
|
r_config_set_b (core->config, "scr.interactive", ov);
|
|
r_cons_show_cursor (true);
|
|
r_cons_enable_mouse (false);
|
|
if (update_seek != -1) {
|
|
r_core_seek (core, oseek, false);
|
|
}
|
|
} else {
|
|
R_LOG_INFO ("This graph contains no nodes");
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
case 'd': { // "ag_d" - dot format
|
|
char *dot_text = print_graph_dot (core, graph);
|
|
if (dot_text) {
|
|
r_cons_print (dot_text);
|
|
free (dot_text);
|
|
}
|
|
break;
|
|
}
|
|
case '*': // "ag_*" -
|
|
print_graph_agg (graph);
|
|
break;
|
|
case 'J':
|
|
case 'j': {
|
|
PJ *pj = pj_new ();
|
|
if (pj) {
|
|
r_graph_drawable_to_json (graph, pj, use_offset);
|
|
r_cons_println (pj_string (pj));
|
|
pj_free (pj);
|
|
}
|
|
} break;
|
|
case 'g':
|
|
r_cons_printf ("graph\n[\n"
|
|
"hierarchic 1\n"
|
|
"label \"\"\n"
|
|
"directed 1\n");
|
|
r_list_foreach (graph->nodes, it, graphNode) {
|
|
print_node = graphNode->data;
|
|
r_cons_printf (" node [\n"
|
|
" id %d\n"
|
|
" label \"%s\"\n"
|
|
" ]\n",
|
|
graphNode->idx, print_node->title);
|
|
}
|
|
r_list_foreach (graph->nodes, it, graphNode) {
|
|
print_node = graphNode->data;
|
|
r_list_foreach (graphNode->out_nodes, edge_it, target) {
|
|
r_cons_printf (" edge [\n"
|
|
" source %d\n"
|
|
" target %d\n"
|
|
" ]\n",
|
|
graphNode->idx, target->idx);
|
|
}
|
|
}
|
|
r_cons_print ("]\n");
|
|
break;
|
|
case 'w': { // "ag_w"
|
|
const char *filename = r_str_trim_head_ro (input + 1);
|
|
char *dot_text = print_graph_dot (core, graph);
|
|
if (dot_text) {
|
|
convert_dot_str_to_image (core, dot_text, filename);
|
|
free (dot_text);
|
|
}
|
|
break;
|
|
}
|
|
case 'm':
|
|
mermaid_graph (graph, mermaid_nodeinfo_body);
|
|
break;
|
|
default:
|
|
r_core_cmd_help (core, help_msg_ag);
|
|
break;
|
|
}
|
|
}
|
|
|
|
static inline bool mermaid_add_node_asm(RAnal *a, RAnalBlock *bb, RStrBuf *nodes) {
|
|
ut8 *bb_buf = calloc (1, bb->size);
|
|
if (!bb_buf) {
|
|
return false;
|
|
}
|
|
if (!a->iob.read_at (a->iob.io, bb->addr, (ut8 *)bb_buf, bb->size)) {
|
|
return false;
|
|
}
|
|
RAnalOpMask mask = R_ANAL_OP_MASK_BASIC | R_ANAL_OP_MASK_DISASM | R_ANAL_OP_HINT_MASK;
|
|
RAnalOp op = {0};
|
|
|
|
// escaped newline to get out of title line
|
|
bool ret = r_strbuf_append (nodes, "\\n");
|
|
int i;
|
|
for (i = 0; i < bb->ninstr; i++) {
|
|
const ut64 prev_pos = r_anal_bb_offset_inst (bb, i);
|
|
const ut64 op_addr = r_anal_bb_opaddr_i (bb, i);
|
|
if (prev_pos >= bb->size) {
|
|
continue;
|
|
}
|
|
int buflen = bb->size - prev_pos;
|
|
ut8 *loc = bb_buf + prev_pos;
|
|
if (r_anal_op (a, &op, op_addr, loc, buflen, mask) > 0) {
|
|
ret &= r_strbuf_appendf (nodes, "%s\\n", op.mnemonic);
|
|
} else {
|
|
ret &= r_strbuf_append (nodes, "...\\n");
|
|
}
|
|
if (!ret) {
|
|
break;
|
|
}
|
|
r_anal_op_fini (&op);
|
|
}
|
|
free (bb_buf);
|
|
return ret;
|
|
}
|
|
|
|
static inline bool fcn_siwtch_mermaid(RAnalBlock *b, RStrBuf *buf) {
|
|
if (b->switch_op) {
|
|
r_return_val_if_fail (b->switch_op->cases, false);
|
|
RListIter *itt;
|
|
RAnalCaseOp *c;
|
|
r_list_foreach (b->switch_op->cases, itt, c) {
|
|
if (!r_strbuf_appendf (buf, " _0x%" PFMT64x " --> _0x%" PFMT64x ": Case %" PFMT64d "\n", b->addr, c->addr, c->value)) {
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
static bool cmd_graph_mermaid(RCore *core, bool add_asm) {
|
|
RAnalFunction *fcn = r_anal_get_fcn_in (core->anal, core->offset, 0);
|
|
if (!fcn || !fcn->bbs) {
|
|
return false;
|
|
}
|
|
|
|
bool ret = true;
|
|
|
|
// for info on mermaid syntax: https://mermaid-js.github.io/mermaid/#/stateDiagram
|
|
RStrBuf *nodes = r_strbuf_new ("stateDiagram-v2\n");
|
|
RStrBuf *edges = r_strbuf_new ("");
|
|
|
|
// TODO: add themeing to nodes buff here -> https://mermaid-js.github.io/mermaid/#/theming
|
|
|
|
RAnalBlock *b;
|
|
RListIter *iter;
|
|
|
|
r_list_sort (fcn->bbs, bb_cmp);
|
|
r_list_foreach (fcn->bbs, iter, b) {
|
|
ret &= r_strbuf_appendf (nodes, " state \"[0x%" PFMT64x "]", b->addr);
|
|
if (b->addr == fcn->addr) {
|
|
ret &= r_strbuf_appendf (nodes, " %s", fcn->name);
|
|
}
|
|
if (add_asm) {
|
|
ret &= mermaid_add_node_asm (core->anal, b, nodes);
|
|
}
|
|
// ending of nodes string `... " as _0xfffff`
|
|
// node names start with _0x b/c 0x makes mermaids mad somehow
|
|
ret &= r_strbuf_appendf (nodes, "\" as _0x%" PFMT64x "\n", b->addr);
|
|
|
|
if (b->jump != UT64_MAX) {
|
|
if (b->fail != UT64_MAX) {
|
|
ret &= r_strbuf_appendf (edges, " _0x%" PFMT64x " --> _0x%" PFMT64x ": true\n", b->addr, b->jump);
|
|
ret &= r_strbuf_appendf (edges, " _0x%" PFMT64x " --> _0x%" PFMT64x ": false\n", b->addr, b->fail);
|
|
} else {
|
|
ret &= r_strbuf_appendf (edges, " _0x%" PFMT64x " --> _0x%" PFMT64x "\n", b->addr, b->jump);
|
|
}
|
|
} else if (b->fail != UT64_MAX) {
|
|
ret &= r_strbuf_appendf (edges, " _0x%" PFMT64x " --> _0x%" PFMT64x "\n", b->addr, b->fail);
|
|
}
|
|
ret &= fcn_siwtch_mermaid (b, edges);
|
|
if (!ret) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (ret) {
|
|
char *n = r_strbuf_drain_nofree (nodes);
|
|
char *e = r_strbuf_drain_nofree (edges);
|
|
if (n && e) {
|
|
r_cons_print (n);
|
|
r_cons_print (e);
|
|
}
|
|
free (n);
|
|
free (e);
|
|
}
|
|
r_strbuf_free (nodes);
|
|
r_strbuf_free (edges);
|
|
return ret;
|
|
}
|
|
|
|
static void cmd_anal_graph(RCore *core, const char *input) {
|
|
core->graph->show_node_titles = r_config_get_i (core->config, "graph.ntitles");
|
|
r_cons_enable_highlight (false);
|
|
if (*input == '?') {
|
|
r_core_cmd_help (core, help_msg_ag);
|
|
return;
|
|
}
|
|
switch (input[0]) {
|
|
case 'f': // "agf"
|
|
switch (input[1]) {
|
|
case 0: // "agf"
|
|
r_core_visual_graph (core, NULL, NULL, false);
|
|
break;
|
|
case 'b': // "agfb" // braile
|
|
cmd_agfb (core);
|
|
break;
|
|
case 'm': /// "agfm" // mermaid
|
|
cmd_graph_mermaid (core, input[2] == 'a');
|
|
break;
|
|
case ' ': { // "agf "
|
|
RAnalFunction *fcn = r_anal_get_fcn_in (core->anal, core->offset, 0);
|
|
r_core_visual_graph (core, NULL, fcn, false);
|
|
break;
|
|
}
|
|
case 'v': // "agfv"
|
|
{
|
|
RAnalFunction *fcn = r_anal_get_fcn_in (core->anal, core->offset, R_ANAL_FCN_TYPE_ROOT);
|
|
if (fcn) {
|
|
r_core_visual_graph (core, NULL, fcn, 1);
|
|
}
|
|
r_cons_enable_mouse (false);
|
|
r_cons_show_cursor (true);
|
|
}
|
|
break;
|
|
case 't': { // "agft" - tiny graph
|
|
int e = r_config_get_i (core->config, "graph.edges");
|
|
r_config_set_i (core->config, "graph.edges", 0);
|
|
RAnalFunction *fcn = r_anal_get_fcn_in (core->anal, core->offset, 0);
|
|
r_core_visual_graph (core, NULL, fcn, 2);
|
|
r_config_set_i (core->config, "graph.edges", e);
|
|
break;
|
|
}
|
|
case 'd': // "agfd"
|
|
if (input[2] == 'm') { // "agfdm"
|
|
r_core_anal_graph (core, r_num_math (core->num, input + 3),
|
|
R_CORE_ANAL_GRAPHLINES);
|
|
} else {
|
|
r_core_anal_graph (core, r_num_math (core->num, input + 2),
|
|
R_CORE_ANAL_GRAPHBODY);
|
|
}
|
|
break;
|
|
case 'j': // "agfj"
|
|
r_core_anal_graph (core, r_num_math (core->num, input + 2), R_CORE_ANAL_JSON);
|
|
break;
|
|
case 'J': { // "agfJ"
|
|
// Honor asm.graph=false in json as well
|
|
RConfigHold *hc = r_config_hold_new (core->config);
|
|
r_config_hold (hc, "asm.offset", NULL);
|
|
const bool o_graph_offset = r_config_get_i (core->config, "graph.offset");
|
|
r_config_set_i (core->config, "asm.offset", o_graph_offset);
|
|
r_core_anal_graph (core, r_num_math (core->num, input + 2),
|
|
R_CORE_ANAL_JSON | R_CORE_ANAL_JSON_FORMAT_DISASM);
|
|
r_config_hold_restore (hc);
|
|
r_config_hold_free (hc);
|
|
break;
|
|
}
|
|
case 'g':{ // "agfg"
|
|
RAnalFunction *fcn = r_anal_get_fcn_in (core->anal, core->offset, 0);
|
|
r_core_print_bb_gml (core, fcn);
|
|
break;
|
|
}
|
|
case 'k':{ // "agfk"
|
|
r_core_cmdf (core, "ag-; .agf* @ %"PFMT64u"; aggk", core->offset);
|
|
break;
|
|
}
|
|
case '*':{// "agf*"
|
|
RAnalFunction *fcn = r_anal_get_fcn_in (core->anal, core->offset, 0);
|
|
r_core_print_bb_custom (core, fcn);
|
|
break;
|
|
}
|
|
case 'w': // "agfw"
|
|
if (r_config_get_i (core->config, "graph.web")) {
|
|
R_LOG_ERROR ("graph.web is deprecated");
|
|
} else {
|
|
char *cmdargs = r_str_newf ("agfd @ 0x%"PFMT64x, core->offset);
|
|
convert_dotcmd_to_image (core, cmdargs, input + 2);
|
|
free (cmdargs);
|
|
}
|
|
break;
|
|
default:
|
|
r_core_cmd_help (core, help_msg_ag);
|
|
break;
|
|
}
|
|
break;
|
|
case '-': // "ag-"
|
|
r_agraph_reset (core->graph);
|
|
break;
|
|
case 'n': // "agn"
|
|
cmd_agraph_node (core, input + 1);
|
|
break;
|
|
case 'e': // "age"
|
|
cmd_agraph_edge (core, input + 1);
|
|
break;
|
|
case 't': // "agt"
|
|
r_core_agraph_treemap (core, -1, input + 1);
|
|
break;
|
|
case 'g': // "agg"
|
|
r_core_agraph_print (core, -1, input + 1);
|
|
break;
|
|
case 's': // "ags"
|
|
r_core_anal_graph (core, r_num_math (core->num, input + 1), 0);
|
|
break;
|
|
case 'C': // "agC"
|
|
switch (input[1]) {
|
|
case 'm':
|
|
case 'v':
|
|
case 't':
|
|
case 'k':
|
|
case 'w':
|
|
case ' ':
|
|
case 0:
|
|
core->graph->is_callgraph = true;
|
|
r_core_cmdf (core, "ag-; .agC*;");
|
|
r_core_agraph_print(core, -1, input + 1);
|
|
core->graph->is_callgraph = false;
|
|
break;
|
|
case 'J':
|
|
case 'j':
|
|
r_core_anal_callgraph (core, UT64_MAX, R_GRAPH_FORMAT_JSON);
|
|
break;
|
|
case 'g':
|
|
r_core_anal_callgraph (core, UT64_MAX, R_GRAPH_FORMAT_GML);
|
|
break;
|
|
case 'd':
|
|
r_core_anal_callgraph (core, UT64_MAX, R_GRAPH_FORMAT_DOT);
|
|
break;
|
|
case '*':
|
|
r_core_anal_callgraph (core, UT64_MAX, R_GRAPH_FORMAT_CMD);
|
|
break;
|
|
default:
|
|
r_core_cmd_help (core, help_msg_ag);
|
|
break;
|
|
}
|
|
break;
|
|
case 'r': // "agr" references graph
|
|
switch (input[1]) {
|
|
case '*':
|
|
r_core_anal_coderefs (core, core->offset);
|
|
break;
|
|
default:
|
|
core->graph->is_callgraph = true;
|
|
r_core_cmdf (core, "ag-; .agr* @ %"PFMT64u";", core->offset);
|
|
r_core_agraph_print(core, -1, input + 1);
|
|
core->graph->is_callgraph = false;
|
|
break;
|
|
}
|
|
break;
|
|
case 'R': // "agR" global refs
|
|
switch (input[1]) {
|
|
case '*': {
|
|
ut64 from = r_config_get_i (core->config, "graph.from");
|
|
ut64 to = r_config_get_i (core->config, "graph.to");
|
|
RListIter *it;
|
|
RAnalFunction *fcn;
|
|
r_list_foreach (core->anal->fcns, it, fcn) {
|
|
if ((from == UT64_MAX && to == UT64_MAX) || R_BETWEEN (from, fcn->addr, to)) {
|
|
r_core_anal_coderefs (core, fcn->addr);
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
default: {
|
|
core->graph->is_callgraph = true;
|
|
r_core_cmdf (core, "ag-; .agR*;");
|
|
r_core_agraph_print(core, -1, input + 1);
|
|
core->graph->is_callgraph = false;
|
|
break;
|
|
}
|
|
}
|
|
break;
|
|
case 'x': {// "agx" cross refs
|
|
RGraph *graph = r_core_anal_codexrefs (core, core->offset);
|
|
if (!graph) {
|
|
R_LOG_ERROR ("Cannot create graph");
|
|
break;
|
|
}
|
|
r_core_graph_print (core, graph, -1, true, input + 1);
|
|
r_graph_free (graph);
|
|
break;
|
|
}
|
|
case 'i': { // "agi" import graph
|
|
RGraph *graph = r_core_anal_importxrefs (core);
|
|
if (!graph) {
|
|
R_LOG_ERROR ("Cannot create graph");
|
|
break;
|
|
}
|
|
r_core_graph_print (core, graph, -1, true, input + 1);
|
|
r_graph_free (graph);
|
|
break;
|
|
}
|
|
case 'c': // "agc"
|
|
switch (input[1]) {
|
|
case 'v':
|
|
case 't':
|
|
case 'k':
|
|
case 'w':
|
|
case 'm':
|
|
case ' ': {
|
|
core->graph->is_callgraph = true;
|
|
r_core_cmdf (core, "ag-; .agc* @ %" PFMT64u "; agg%s;", core->offset, input + 1);
|
|
core->graph->is_callgraph = false;
|
|
break;
|
|
}
|
|
case 0:
|
|
core->graph->is_callgraph = true;
|
|
r_core_cmd0 (core, "ag-; .agc* $$; agg;");
|
|
core->graph->is_callgraph = false;
|
|
break;
|
|
case 'g': {
|
|
r_core_anal_callgraph (core, core->offset, R_GRAPH_FORMAT_GMLFCN);
|
|
break;
|
|
}
|
|
case 'd': {
|
|
r_core_anal_callgraph (core, core->offset, R_GRAPH_FORMAT_DOT);
|
|
break;
|
|
}
|
|
case 'J':
|
|
case 'j': {
|
|
r_core_anal_callgraph (core, core->offset, R_GRAPH_FORMAT_JSON);
|
|
break;
|
|
}
|
|
case '*': {
|
|
r_core_anal_callgraph (core, core->offset, R_GRAPH_FORMAT_CMD);
|
|
break;
|
|
}
|
|
default:
|
|
r_core_cmd_help (core, help_msg_ag);
|
|
break;
|
|
}
|
|
break;
|
|
case 'j': // "agj" alias for agfj
|
|
r_core_cmdf (core, "agfj%s", input + 1);
|
|
break;
|
|
case 'J': // "agJ" alias for agfJ
|
|
r_core_cmdf (core, "agfJ%s", input + 1);
|
|
break;
|
|
case 'k': // "agk" alias for agfk
|
|
r_core_cmdf (core, "agfk%s", input + 1);
|
|
break;
|
|
case 'l': // "agl"
|
|
r_core_anal_graph (core, r_num_math (core->num, input + 1), R_CORE_ANAL_GRAPHLINES);
|
|
break;
|
|
case 'a': // "aga"
|
|
switch (input[1]) {
|
|
case '*': {
|
|
r_core_anal_datarefs (core, core->offset);
|
|
break;
|
|
}
|
|
default:
|
|
r_core_cmdf (core, "ag-; .aga* @ %"PFMT64u";", core->offset);
|
|
r_core_agraph_print(core, -1, input + 1);
|
|
break;
|
|
}
|
|
break;
|
|
case 'A': // "agA" global data refs
|
|
switch (input[1]) {
|
|
case '*': {
|
|
ut64 from = r_config_get_i (core->config, "graph.from");
|
|
ut64 to = r_config_get_i (core->config, "graph.to");
|
|
RListIter *it;
|
|
RAnalFunction *fcn;
|
|
r_list_foreach (core->anal->fcns, it, fcn) {
|
|
if ((from == UT64_MAX && to == UT64_MAX) || R_BETWEEN (from, fcn->addr, to)) {
|
|
r_core_anal_datarefs (core, fcn->addr);
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
default:
|
|
r_core_cmdf (core, "ag-; .agA*;");
|
|
r_core_agraph_print(core, -1, input + 1);
|
|
break;
|
|
}
|
|
break;
|
|
case 'd': {// "agd"
|
|
int diff_opt = R_CORE_ANAL_GRAPHBODY | R_CORE_ANAL_GRAPHDIFF;
|
|
switch (input[1]) {
|
|
case 'j': {
|
|
ut64 addr = input[2] ? r_num_math (core->num, input + 2) : core->offset;
|
|
r_core_gdiff_fcn (core, addr, core->offset);
|
|
r_core_anal_graph (core, addr, diff_opt | R_CORE_ANAL_JSON);
|
|
break;
|
|
}
|
|
case 'J': {
|
|
ut64 addr = input[2] ? r_num_math (core->num, input + 2) : core->offset;
|
|
r_core_gdiff_fcn (core, addr, core->offset);
|
|
r_core_anal_graph (core, addr, diff_opt | R_CORE_ANAL_JSON | R_CORE_ANAL_JSON_FORMAT_DISASM);
|
|
break;
|
|
}
|
|
case '*': {
|
|
ut64 addr = input[2] ? r_num_math (core->num, input + 2) : core->offset;
|
|
r_core_gdiff_fcn (core, addr, core->offset);
|
|
r_core_anal_graph (core, addr, diff_opt | R_CORE_ANAL_STAR);
|
|
break;
|
|
}
|
|
case ' ':
|
|
case 0:
|
|
case 't':
|
|
case 'k':
|
|
case 'v':
|
|
case 'm':
|
|
case 'g': {
|
|
ut64 addr = input[2]? r_num_math (core->num, input + 2): core->offset;
|
|
r_core_cmdf (core, "ag-; .agd* @ %"PFMT64u"; agg%s;", addr, input + 1);
|
|
break;
|
|
}
|
|
case 'd': {
|
|
ut64 addr = input[2]? r_num_math (core->num, input + 2): core->offset;
|
|
r_core_gdiff_fcn (core, addr, core->offset);
|
|
r_core_anal_graph (core, addr, diff_opt);
|
|
break;
|
|
}
|
|
case 'w': {
|
|
char *cmdargs = r_str_newf ("agdd 0x%"PFMT64x, core->offset);
|
|
convert_dotcmd_to_image (core, cmdargs, input + 2);
|
|
free (cmdargs);
|
|
break;
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
case 'v': // "agv" alias for "agfv"
|
|
r_core_cmdf (core, "agfv%s", input + 1);
|
|
break;
|
|
case 'w':// "agw"
|
|
if (r_config_get_i (core->config, "graph.web")) {
|
|
R_LOG_ERROR ("graph.web is deprecated");
|
|
} else {
|
|
char *cmdargs = r_str_newf ("agfd @ 0x%"PFMT64x, core->offset);
|
|
convert_dotcmd_to_image (core, cmdargs, input + 1);
|
|
free (cmdargs);
|
|
}
|
|
break;
|
|
default:
|
|
r_core_cmd_help (core, help_msg_ag);
|
|
break;
|
|
}
|
|
}
|
|
|
|
R_API int r_core_anal_refs(RCore *core, const char *input) {
|
|
const bool cfg_debug = r_config_get_b (core->config, "cfg.debug");
|
|
ut64 from, to;
|
|
int rad;
|
|
PJ *pj = NULL;
|
|
if (*input == '?') {
|
|
r_core_cmd_help (core, help_msg_aar);
|
|
return 0;
|
|
}
|
|
|
|
if (*input == 'j' || *input == '*') {
|
|
rad = *input;
|
|
input++;
|
|
if (rad == 'j') {
|
|
pj = r_core_pj_new (core);
|
|
if (!pj) {
|
|
return 0;
|
|
}
|
|
}
|
|
} else {
|
|
rad = 0;
|
|
}
|
|
|
|
from = to = 0;
|
|
char *ptr = r_str_trim_dup (input);
|
|
int n = r_str_word_set0 (ptr);
|
|
if (!n) {
|
|
// get boundaries of current memory map, section or io map
|
|
if (cfg_debug) {
|
|
RDebugMap *map = r_debug_map_get (core->dbg, core->offset);
|
|
if (map) {
|
|
from = map->addr;
|
|
to = map->addr_end;
|
|
}
|
|
} else {
|
|
RList *list = r_core_get_boundaries_prot (core, R_PERM_X, NULL, "anal");
|
|
RListIter *iter;
|
|
RIOMap* map;
|
|
if (!list) {
|
|
return 0;
|
|
}
|
|
if (rad == 'j') {
|
|
pj_o (pj);
|
|
}
|
|
r_list_foreach (list, iter, map) {
|
|
from = r_io_map_begin (map);
|
|
to = r_io_map_end (map);
|
|
if (r_cons_is_breaked ()) {
|
|
break;
|
|
}
|
|
if (!from && !to) {
|
|
R_LOG_ERROR ("Cannot determine xref search boundaries");
|
|
} else if (to - from > UT32_MAX) {
|
|
R_LOG_ERROR ("Skipping huge range");
|
|
} else {
|
|
if (rad == 'j') {
|
|
pj_ki (pj, "mapid", map->id);
|
|
pj_ko (pj, "refs");
|
|
}
|
|
r_core_anal_search_xrefs (core, from, to, pj, rad);
|
|
if (rad == 'j') {
|
|
pj_end (pj);
|
|
}
|
|
}
|
|
}
|
|
if (rad == 'j') {
|
|
pj_end (pj);
|
|
r_cons_println (pj_string (pj));
|
|
pj_free (pj);
|
|
}
|
|
free (ptr);
|
|
r_list_free (list);
|
|
return 1;
|
|
}
|
|
} else if (n == 1) {
|
|
from = core->offset;
|
|
to = core->offset + r_num_math (core->num, r_str_word_get0 (ptr, 0));
|
|
} else {
|
|
R_LOG_ERROR ("Invalid number of arguments");
|
|
}
|
|
free (ptr);
|
|
|
|
if (from == UT64_MAX && to == UT64_MAX) {
|
|
return false;
|
|
}
|
|
if (!from && !to) {
|
|
return false;
|
|
}
|
|
if (to - from > r_io_size (core->io)) {
|
|
return false;
|
|
}
|
|
if (rad == 'j') {
|
|
pj_o (pj);
|
|
}
|
|
bool res = r_core_anal_search_xrefs (core, from, to, pj, rad);
|
|
if (rad == 'j') {
|
|
pj_end (pj);
|
|
r_cons_println (pj_string (pj));
|
|
pj_free (pj);
|
|
}
|
|
return res;
|
|
}
|
|
|
|
static R_TH_LOCAL const char *oldstr = NULL;
|
|
|
|
static int compute_coverage(RCore *core) {
|
|
RListIter *iter;
|
|
RAnalFunction *fcn;
|
|
int cov = 0;
|
|
cov += r_meta_get_size(core->anal, R_META_TYPE_DATA);
|
|
RIOBank *bank = r_io_bank_get (core->io, core->io->bank);
|
|
if (bank) {
|
|
r_list_foreach (core->anal->fcns, iter, fcn) {
|
|
RIOMapRef *mapref;
|
|
RListIter *ator;
|
|
r_list_foreach (bank->maprefs, ator, mapref) {
|
|
RIOMap *map = r_io_map_get (core->io, mapref->id);
|
|
if (map->perm & R_PERM_X) {
|
|
ut64 section_end = r_io_map_end (map);
|
|
ut64 s = r_anal_function_realsize (fcn);
|
|
if (fcn->addr >= r_io_map_from (map) && (fcn->addr + s) < section_end) {
|
|
cov += s;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return cov;
|
|
}
|
|
|
|
static int compute_code(RCore* core) {
|
|
int code = 0;
|
|
RIOBank *bank = r_io_bank_get (core->io, core->io->bank);
|
|
if (bank) {
|
|
RIOMapRef *mapref;
|
|
RListIter *ator;
|
|
r_list_foreach (bank->maprefs, ator, mapref) {
|
|
RIOMap *map = r_io_map_get (core->io, mapref->id);
|
|
if (map->perm & R_PERM_X) {
|
|
code += r_io_map_size (map);
|
|
}
|
|
}
|
|
}
|
|
return code;
|
|
}
|
|
|
|
static int compute_calls(RCore *core) {
|
|
RListIter *iter;
|
|
RAnalFunction *fcn;
|
|
RList *xrefs;
|
|
int cov = 0;
|
|
r_list_foreach (core->anal->fcns, iter, fcn) {
|
|
xrefs = r_anal_function_get_xrefs (fcn);
|
|
if (xrefs) {
|
|
cov += r_list_length (xrefs);
|
|
r_list_free (xrefs);
|
|
xrefs = NULL;
|
|
}
|
|
}
|
|
return cov;
|
|
}
|
|
|
|
static void r_core_anal_info(RCore *core, const char *input) {
|
|
int fcns = r_list_length (core->anal->fcns);
|
|
int strs = r_flag_count (core->flags, "str.*");
|
|
int syms = r_flag_count (core->flags, "sym.*");
|
|
int imps = r_flag_count (core->flags, "sym.imp.*");
|
|
int code = compute_code (core);
|
|
int covr = compute_coverage (core);
|
|
int call = compute_calls (core);
|
|
int xrfs = r_anal_xrefs_count (core->anal);
|
|
int cvpc = (code > 0)? (int)((covr * 100.0) / code): 0;
|
|
if (*input == 'j') {
|
|
PJ *pj = pj_new ();
|
|
if (!pj) {
|
|
return;
|
|
}
|
|
pj_o (pj);
|
|
pj_ki (pj, "fcns", fcns);
|
|
pj_ki (pj, "xrefs", xrfs);
|
|
pj_ki (pj, "calls", call);
|
|
pj_ki (pj, "strings", strs);
|
|
pj_ki (pj, "symbols", syms);
|
|
pj_ki (pj, "imports", imps);
|
|
pj_ki (pj, "covrage", covr);
|
|
pj_ki (pj, "codesz", code);
|
|
pj_ki (pj, "percent", cvpc);
|
|
pj_end (pj);
|
|
r_cons_println (pj_string (pj));
|
|
pj_free (pj);
|
|
} else {
|
|
r_cons_printf ("fcns %d\n", fcns);
|
|
r_cons_printf ("xrefs %d\n", xrfs);
|
|
r_cons_printf ("calls %d\n", call);
|
|
r_cons_printf ("strings %d\n", strs);
|
|
r_cons_printf ("symbols %d\n", syms);
|
|
r_cons_printf ("imports %d\n", imps);
|
|
r_cons_printf ("covrage %d\n", covr);
|
|
r_cons_printf ("codesz %d\n", code);
|
|
r_cons_printf ("percent %d%%\n", cvpc);
|
|
}
|
|
}
|
|
|
|
static void cmd_anal_aad(RCore *core, const char *input) {
|
|
RListIter *iter;
|
|
RAnalRef *ref;
|
|
RList *list = r_list_newf (NULL);
|
|
r_anal_xrefs_from (core->anal, list, "xref", R_ANAL_REF_TYPE_DATA, UT64_MAX);
|
|
r_list_foreach (list, iter, ref) {
|
|
if (r_io_is_valid_offset (core->io, ref->addr, false)) {
|
|
r_core_anal_fcn (core, ref->at, ref->addr, R_ANAL_REF_TYPE_NULL, 1);
|
|
}
|
|
}
|
|
r_list_free (list);
|
|
}
|
|
|
|
static bool archIsThumbable(RCore *core) {
|
|
RAsm *as = core ? core->rasm : NULL;
|
|
if (as && as->cur && as->config->bits <= 32 && as->cur->name) {
|
|
return strstr (as->cur->name, "arm");
|
|
}
|
|
return false;
|
|
}
|
|
|
|
static void _CbInRangeAav(RCore *core, ut64 from, ut64 to, int vsize, void *user) {
|
|
bool asterisk = user;
|
|
int arch_align = r_anal_archinfo (core->anal, R_ANAL_ARCHINFO_ALIGN);
|
|
bool vinfun = r_config_get_i (core->config, "anal.vinfun");
|
|
int searchAlign = r_config_get_i (core->config, "search.align");
|
|
int align = (searchAlign > 0)? searchAlign: arch_align;
|
|
if (align > 1) {
|
|
if ((from % align) || (to % align)) {
|
|
bool itsFine = false;
|
|
if (archIsThumbable (core)) {
|
|
if ((from & 1) || (to & 1)) {
|
|
itsFine = true;
|
|
}
|
|
}
|
|
if (!itsFine) {
|
|
return;
|
|
}
|
|
if (core->anal->verbose) {
|
|
R_LOG_WARN ("aav: false positive in 0x%08"PFMT64x, from);
|
|
}
|
|
}
|
|
}
|
|
if (!vinfun) {
|
|
RAnalFunction *fcn = r_anal_get_fcn_in (core->anal, from, -1);
|
|
if (fcn) {
|
|
return;
|
|
}
|
|
}
|
|
if (asterisk) {
|
|
r_cons_printf ("ax 0x%"PFMT64x " 0x%"PFMT64x "\n", to, from);
|
|
r_cons_printf ("Cd %d @ 0x%"PFMT64x "\n", vsize, from);
|
|
r_cons_printf ("f+ aav.0x%08"PFMT64x "= 0x%08"PFMT64x, to, to);
|
|
} else {
|
|
r_anal_xrefs_set (core->anal, from, to, R_ANAL_REF_TYPE_NULL);
|
|
// r_meta_set (core->anal, 'd', from, from + vsize, NULL);
|
|
r_core_cmdf (core, "Cd %d @ 0x%"PFMT64x, vsize, from);
|
|
if (!r_flag_get_at (core->flags, to, false)) {
|
|
char *name = r_str_newf ("aav.0x%08"PFMT64x, to);
|
|
r_flag_set (core->flags, name, to, vsize);
|
|
free (name);
|
|
}
|
|
}
|
|
}
|
|
|
|
static void cmd_anal_aaw(RCore *core, const char *input) {
|
|
bool be = core->print->config->big_endian;
|
|
int bits = core->anal->config->bits;
|
|
RIntervalTreeIter it;
|
|
RAnalMetaItem *item;
|
|
r_interval_tree_foreach (&core->anal->meta, it, item) {
|
|
RIntervalNode *node = r_interval_tree_iter_get (&it);
|
|
ut64 size = r_meta_item_size (node->start, node->end);
|
|
if (item->type == R_META_TYPE_DATA && size == core->anal->config->bits / 8) {
|
|
ut8 buf[8] = {0};
|
|
r_io_read_at (core->io, node->start, buf, 8);
|
|
ut64 n = r_read_ble (buf, be, bits);
|
|
RFlagItem *fi = r_flag_get_at (core->flags, n, false);
|
|
if (fi) {
|
|
char *fn = r_str_newf ("r.%s", fi->name);
|
|
r_flag_set (core->flags, fn, node->start, true);
|
|
free (fn);
|
|
} else if (core->anal->verbose) {
|
|
R_LOG_WARN ("Unknown pointer 0x%"PFMT64x" at 0x%"PFMT64x, n, (ut64)node->start);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
static void cmd_anal_aav(RCore *core, const char *input) {
|
|
#define seti(x,y) r_config_set_i(core->config, x, y);
|
|
#define geti(x) r_config_get_i(core->config, x);
|
|
r_return_if_fail (*input == 'v');
|
|
bool relative = input[1] == 'r';
|
|
ut64 o_align = geti ("search.align");
|
|
const char *analin = r_config_get (core->config, "anal.in");
|
|
char *tmp = strdup (analin);
|
|
bool asterisk = strchr (input, '*');
|
|
const bool is_debug = r_config_get_b (core->config, "cfg.debug");
|
|
int archAlign = r_anal_archinfo (core->anal, R_ANAL_ARCHINFO_ALIGN);
|
|
seti ("search.align", archAlign);
|
|
r_config_set (core->config, "anal.in", "io.maps.x");
|
|
oldstr = r_print_rowlog (core->print, "Finding xrefs in noncode section (e anal.in=io.maps.x)");
|
|
r_print_rowlog_done (core->print, oldstr);
|
|
|
|
int vsize = 4; // 32bit dword
|
|
if (core->rasm->config->bits == 64) {
|
|
vsize = 8;
|
|
}
|
|
oldstr = r_print_rowlog (core->print, "Analyze value pointers (aav)");
|
|
r_print_rowlog_done (core->print, oldstr);
|
|
r_cons_break_push (NULL, NULL);
|
|
if (is_debug) {
|
|
RList *list = r_core_get_boundaries_prot (core, 0, "dbg.map", "anal");
|
|
RListIter *iter;
|
|
RIOMap *map;
|
|
if (!list) {
|
|
goto beach;
|
|
}
|
|
r_list_foreach (list, iter, map) {
|
|
if (r_cons_is_breaked ()) {
|
|
break;
|
|
}
|
|
(void)r_core_search_value_in_range (core, relative, map->itv,
|
|
r_io_map_begin (map), r_io_map_end (map), vsize, _CbInRangeAav, (void *)(size_t)asterisk);
|
|
}
|
|
r_list_free (list);
|
|
} else {
|
|
RList *list = r_core_get_boundaries_prot (core, 0, NULL, "anal");
|
|
if (!list) {
|
|
goto beach;
|
|
}
|
|
RListIter *iter, *iter2;
|
|
RIOMap *map, *map2;
|
|
ut64 from = UT64_MAX;
|
|
ut64 to = UT64_MAX;
|
|
// find values pointing to non-executable regions
|
|
r_list_foreach (list, iter2, map2) {
|
|
if (r_cons_is_breaked ()) {
|
|
break;
|
|
}
|
|
//TODO: Reduce multiple hits for same addr
|
|
from = r_itv_begin (map2->itv);
|
|
to = r_itv_end (map2->itv);
|
|
if ((to - from) > MAX_SCAN_SIZE) {
|
|
R_LOG_WARN ("Skipping large region");
|
|
continue;
|
|
}
|
|
r_list_foreach (list, iter, map) {
|
|
ut64 begin = r_io_map_begin (map);
|
|
ut64 end = r_io_map_end (map);
|
|
if (r_cons_is_breaked ()) {
|
|
break;
|
|
}
|
|
if (end - begin > UT32_MAX) {
|
|
oldstr = r_print_rowlog (core->print, "Skipping huge range");
|
|
r_print_rowlog_done (core->print, oldstr);
|
|
continue;
|
|
}
|
|
r_strf_var (msg2, 128, "aav: 0x%08"PFMT64x"-0x%08"PFMT64x" in 0x%"PFMT64x"-0x%"PFMT64x, from, to, begin, end);
|
|
oldstr = r_print_rowlog (core->print, msg2);
|
|
r_print_rowlog_done (core->print, oldstr);
|
|
(void)r_core_search_value_in_range (core, relative, map->itv, from, to, vsize, _CbInRangeAav, (void *)(size_t)asterisk);
|
|
}
|
|
}
|
|
r_list_free (list);
|
|
}
|
|
beach:
|
|
r_cons_break_pop ();
|
|
r_config_set (core->config, "anal.in", tmp);
|
|
free (tmp);
|
|
seti ("search.align", o_align);
|
|
}
|
|
|
|
static void cmd_anal_abt(RCore *core, const char *input) {
|
|
switch (*input) {
|
|
case 'e':
|
|
{
|
|
int n = 1;
|
|
char *p = strchr (input + 1, ' ');
|
|
if (!p) {
|
|
// TODO use r_cons_cmd_help_match () instead
|
|
r_core_cmd_help (core, help_msg_abt);
|
|
return;
|
|
}
|
|
ut64 addr = r_num_math (core->num, p + 1);
|
|
RList *paths = r_core_anal_graph_to (core, addr, n);
|
|
if (paths) {
|
|
RAnalBlock *bb;
|
|
RList *path;
|
|
RListIter *pathi;
|
|
RListIter *bbi;
|
|
r_cons_printf ("f orip=`dr?PC`\n");
|
|
r_list_foreach (paths, pathi, path) {
|
|
r_list_foreach (path, bbi, bb) {
|
|
r_cons_printf ("# 0x%08" PFMT64x "\n", bb->addr);
|
|
if (addr >= bb->addr && addr < bb->addr + bb->size) {
|
|
r_cons_printf ("aepc 0x%08"PFMT64x"\n", bb->addr);
|
|
r_cons_printf ("aesou 0x%08"PFMT64x"\n", addr);
|
|
} else {
|
|
r_cons_printf ("aepc 0x%08"PFMT64x"\n", bb->addr);
|
|
r_cons_printf ("aesou 0x%08"PFMT64x"\n", bb->addr + bb->size);
|
|
}
|
|
}
|
|
r_cons_newline ();
|
|
r_list_purge (path);
|
|
free (path);
|
|
}
|
|
r_list_purge (paths);
|
|
r_cons_printf ("aepc orip\n");
|
|
free (paths);
|
|
}
|
|
}
|
|
break;
|
|
case '?':
|
|
r_core_cmd_help (core, help_msg_abt);
|
|
break;
|
|
case 'j': {
|
|
ut64 addr = r_num_math (core->num, input + 1);
|
|
RAnalBlock *block = r_anal_get_block_at (core->anal, core->offset);
|
|
if (!block) {
|
|
break;
|
|
}
|
|
RList *path = r_anal_block_shortest_path (block, addr);
|
|
PJ *pj = r_core_pj_new (core);
|
|
if (pj) {
|
|
pj_a (pj);
|
|
if (path) {
|
|
RListIter *it;
|
|
r_list_foreach (path, it, block) {
|
|
pj_n (pj, block->addr);
|
|
}
|
|
}
|
|
pj_end (pj);
|
|
r_cons_println (pj_string (pj));
|
|
pj_free (pj);
|
|
}
|
|
r_list_free (path);
|
|
break;
|
|
}
|
|
case ' ': {
|
|
ut64 addr = r_num_math (core->num, input + 1);
|
|
if (addr == UT64_MAX || addr == 0) {
|
|
R_LOG_ERROR ("Invalid or missing address passed as argument");
|
|
} else {
|
|
RAnalBlock *block = r_anal_get_block_at (core->anal, core->offset);
|
|
if (!block) {
|
|
R_LOG_ERROR ("No basic block at");
|
|
break;
|
|
}
|
|
RList *path = r_anal_block_shortest_path (block, addr);
|
|
if (path) {
|
|
RListIter *it;
|
|
r_list_foreach (path, it, block) {
|
|
r_cons_printf ("0x%08" PFMT64x "\n", block->addr);
|
|
}
|
|
r_list_free (path);
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
case '\0':
|
|
r_core_cmdf (core, "abl, addr/eq/0x%08"PFMT64x, core->offset);
|
|
break;
|
|
}
|
|
}
|
|
|
|
static bool is_unknown_file(RCore *core) {
|
|
if (core->bin->cur && core->bin->cur->o) {
|
|
return (r_list_empty (core->bin->cur->o->sections));
|
|
}
|
|
return true;
|
|
}
|
|
|
|
static bool is_apple_target(RCore *core) {
|
|
const char *arch = r_config_get (core->config, "asm.arch");
|
|
if (!strstr (arch, "ppc") && !strstr (arch, "arm") && !strstr (arch, "x86")) {
|
|
return false;
|
|
}
|
|
RBinObject *bo = r_bin_cur_object (core->bin);
|
|
r_return_val_if_fail (!bo || (bo->plugin && bo->plugin->name), false);
|
|
return bo? strstr (bo->plugin->name, "mach"): false;
|
|
}
|
|
|
|
static int cmd_anal_all(RCore *core, const char *input) {
|
|
switch (*input) {
|
|
case '?':
|
|
r_core_cmd_help (core, help_msg_aa);
|
|
break;
|
|
case 'b': // "aab"
|
|
cmd_anal_blocks (core, input + 1);
|
|
break;
|
|
case 'f':
|
|
if (input[1] == 'e') { // "aafe"
|
|
r_core_cmd0 (core, "aef@@F");
|
|
} else if (input[1] == 'r') {
|
|
ut64 cur = core->offset;
|
|
bool hasnext = r_config_get_i (core->config, "anal.hasnext");
|
|
RListIter *iter;
|
|
RIOMap *map;
|
|
RList *list = r_core_get_boundaries_prot (core, R_PERM_X, NULL, "anal");
|
|
if (!list) {
|
|
break;
|
|
}
|
|
r_list_foreach (list, iter, map) {
|
|
r_core_seek (core, r_io_map_begin (map), true);
|
|
r_config_set_i (core->config, "anal.hasnext", 1);
|
|
r_core_cmd0 (core, "afr");
|
|
r_config_set_i (core->config, "anal.hasnext", hasnext);
|
|
}
|
|
r_list_free (list);
|
|
r_core_seek (core, cur, true);
|
|
} else if (input[1] == 't') { // "aaft"
|
|
cmd_anal_aaft (core);
|
|
} else if (input[1] == 'f') { // "aaff"
|
|
flag_every_function (core);
|
|
} else if (input[1] == 's') { // "aafs"
|
|
single_block_analysis (core);
|
|
} else if (input[1] == 0) { // "aaf"
|
|
const bool analHasnext = r_config_get_i (core->config, "anal.hasnext");
|
|
r_config_set_i (core->config, "anal.hasnext", true);
|
|
r_core_cmd0 (core, "afr@@c:isq");
|
|
r_config_set_i (core->config, "anal.hasnext", analHasnext);
|
|
} else {
|
|
r_core_cmd_help (core, help_msg_aaf);
|
|
}
|
|
break;
|
|
case 'c': // "aac"
|
|
switch (input[1]) {
|
|
case '*': // "aac*"
|
|
cmd_anal_calls (core, input + 1, true, false);
|
|
break;
|
|
case 'i': // "aaci"
|
|
cmd_anal_calls (core, input + 1, input[2] == '*', true);
|
|
break;
|
|
case '?': // "aac?"
|
|
r_core_cmd_help_match (core, help_msg_aa, "aac", false);
|
|
break;
|
|
default: // "aac"
|
|
cmd_anal_calls (core, input + 1, false, false);
|
|
break;
|
|
}
|
|
break;
|
|
case 'j': // "aaj"
|
|
if (input[1] == '?') {
|
|
r_core_cmd_help_match (core, help_msg_aa, "aaj", true);
|
|
} else {
|
|
cmd_anal_jumps (core, input + 1);
|
|
}
|
|
break;
|
|
case 'd': // "aad"
|
|
if (input[1] == '?') {
|
|
r_core_cmd_help_match (core, help_msg_aa, "aad", true);
|
|
} else {
|
|
cmd_anal_aad (core, input);
|
|
}
|
|
break;
|
|
case 'v': // "aav"
|
|
if (strchr (input + 1, '?')) {
|
|
r_core_cmd_help (core, help_msg_aav);
|
|
} else {
|
|
cmd_anal_aav (core, input);
|
|
}
|
|
break;
|
|
case 'w': // "aaw"
|
|
if (input[1] == '?') {
|
|
r_core_cmd_help_match (core, help_msg_aa, "aaw", true);
|
|
} else {
|
|
cmd_anal_aaw (core, input);
|
|
}
|
|
break;
|
|
case 'u': // "aau" - print areas not covered by functions
|
|
if (input[1] == '?') {
|
|
r_core_cmd_help_match (core, help_msg_aa, "aau", true);
|
|
} else {
|
|
r_core_anal_nofunclist (core, input + 1);
|
|
}
|
|
break;
|
|
case 'i': // "aai"
|
|
if (input[1] == '?') {
|
|
r_core_cmd_help_match (core, help_msg_aa, "aai", true);
|
|
} else {
|
|
r_core_anal_info (core, input + 1);
|
|
}
|
|
break;
|
|
case 's': // "aas"
|
|
if (input[1] == '?') {
|
|
r_core_cmd_help_match (core, help_msg_aa, "aas", true);
|
|
} else {
|
|
r_core_cmd0 (core, "af @@= `isq~[0]`");
|
|
r_core_cmd0 (core, "af @@f:entry*");
|
|
}
|
|
break;
|
|
case 'S': // "aaS"
|
|
if (input[1] == '?') {
|
|
r_core_cmd_help_match (core, help_msg_aa, "aaS", true);
|
|
} else {
|
|
r_core_cmd0 (core, "af @@f:sym.*");
|
|
r_core_cmd0 (core, "af @@f:entry*");
|
|
}
|
|
break;
|
|
case 'F': // "aaF" "aaFa"
|
|
if (!input[1] || input[1] == ' ' || input[1] == 'a') {
|
|
r_core_anal_inflags (core, input + 1);
|
|
} else {
|
|
eprintf ("Usage: aaF[a] - analyze functions in flag bounds (aaFa uses af/a2f instead of af+/afb+)\n");
|
|
}
|
|
break;
|
|
case 'n': // "aan"
|
|
switch (input[1]) {
|
|
case 'r': // "aanr" // all noreturn propagation
|
|
r_core_anal_propagate_noreturn (core, UT64_MAX);
|
|
break;
|
|
case 'g': // "aang"
|
|
r_core_anal_autoname_all_golang_fcns (core);
|
|
break;
|
|
case '?':
|
|
r_core_cmd_help (core, help_msg_aan);
|
|
break;
|
|
default: // "aan"
|
|
r_core_anal_autoname_all_fcns (core);
|
|
}
|
|
break;
|
|
case 'p': // "aap"
|
|
if (input[1] == '?') {
|
|
r_core_cmd_help_match (core, help_msg_aa, "aap", true);
|
|
} else {
|
|
r_core_search_preludes (core, true);
|
|
}
|
|
break;
|
|
case '\0': // "aa"
|
|
case 'a':
|
|
if (strchr (input, '?')) {
|
|
r_core_cmd_help (core, help_msg_aaa);
|
|
} else {
|
|
bool didAap = false;
|
|
char *dh_orig = NULL;
|
|
if (!strncmp (input, "aaaaa", 5)) {
|
|
eprintf ("An r2 developer is coming to your place to manually analyze this program. Please wait for it\n");
|
|
if (r_cons_is_interactive ()) {
|
|
r_cons_any_key (NULL);
|
|
}
|
|
goto jacuzzi;
|
|
}
|
|
ut64 curseek = core->offset;
|
|
oldstr = r_print_rowlog (core->print, "Analyze all flags starting with sym. and entry0 (aa)");
|
|
r_cons_break_push (NULL, NULL);
|
|
r_cons_break_timeout (r_config_get_i (core->config, "anal.timeout"));
|
|
r_core_anal_all (core);
|
|
r_print_rowlog_done (core->print, oldstr);
|
|
r_core_task_yield (&core->tasks);
|
|
if (r_cons_is_breaked ()) {
|
|
goto jacuzzi;
|
|
}
|
|
|
|
// Run afvn in all fcns
|
|
if (r_config_get_b (core->config, "anal.vars")) {
|
|
oldstr = r_print_rowlog (core->print, "Analyze all functions arguments/locals");
|
|
// r_core_cmd0 (core, "afva@@f");
|
|
r_core_cmd0 (core, "afva@@@F");
|
|
r_print_rowlog_done (core->print, oldstr);
|
|
}
|
|
|
|
// Run pending analysis immediately after analysis
|
|
// Usefull when running commands with ";" or via r2 -c,-i
|
|
dh_orig = core->dbg->h
|
|
? strdup (core->dbg->h->name)
|
|
: strdup ("esil");
|
|
if (core->io->desc && core->io->desc->plugin && !core->io->desc->plugin->isdbg) {
|
|
//use dh_origin if we are debugging
|
|
R_FREE (dh_orig);
|
|
}
|
|
if (r_cons_is_breaked ()) {
|
|
goto jacuzzi;
|
|
}
|
|
r_cons_clear_line (1);
|
|
bool cfg_debug = r_config_get_b (core->config, "cfg.debug");
|
|
if (*input == 'a') { // "aaa" .. which is checked just in the case above
|
|
if (r_str_startswith (r_config_get (core->config, "bin.lang"), "go")) {
|
|
oldstr = r_print_rowlog (core->print, "Find function and symbol names from golang binaries (aang)");
|
|
r_print_rowlog_done (core->print, oldstr);
|
|
r_core_anal_autoname_all_golang_fcns (core);
|
|
oldstr = r_print_rowlog (core->print, "Analyze all flags starting with sym.go. (aF @@f:sym.go.*)");
|
|
r_core_cmd0 (core, "aF @@@F:sym.go.*");
|
|
r_print_rowlog_done (core->print, oldstr);
|
|
}
|
|
r_core_task_yield (&core->tasks);
|
|
if (!cfg_debug) {
|
|
if (dh_orig && strcmp (dh_orig, "esil")) {
|
|
r_core_cmd0 (core, "dL esil");
|
|
r_core_task_yield (&core->tasks);
|
|
}
|
|
}
|
|
int c = r_config_get_i (core->config, "anal.calls");
|
|
r_config_set_i (core->config, "anal.calls", 1);
|
|
r_core_cmd0 (core, "s $S");
|
|
if (r_cons_is_breaked ()) {
|
|
goto jacuzzi;
|
|
}
|
|
|
|
oldstr = r_print_rowlog (core->print, "Analyze function calls (aac)");
|
|
(void)cmd_anal_calls (core, "", false, false); // "aac"
|
|
r_core_seek (core, curseek, true);
|
|
// oldstr = r_print_rowlog (core->print, "Analyze data refs as code (LEA)");
|
|
// (void) cmd_anal_aad (core, NULL); // "aad"
|
|
r_print_rowlog_done (core->print, oldstr);
|
|
r_core_task_yield (&core->tasks);
|
|
if (r_cons_is_breaked ()) {
|
|
goto jacuzzi;
|
|
}
|
|
|
|
if (is_unknown_file (core)) {
|
|
oldstr = r_print_rowlog (core->print, "find and analyze function preludes (aap)");
|
|
(void)r_core_search_preludes (core, false); // "aap"
|
|
didAap = true;
|
|
r_print_rowlog_done (core->print, oldstr);
|
|
r_core_task_yield (&core->tasks);
|
|
if (r_cons_is_breaked ()) {
|
|
goto jacuzzi;
|
|
}
|
|
}
|
|
|
|
oldstr = r_print_rowlog (core->print, "Analyze len bytes of instructions for references (aar)");
|
|
(void)r_core_anal_refs (core, ""); // "aar"
|
|
r_print_rowlog_done (core->print, oldstr);
|
|
r_core_task_yield (&core->tasks);
|
|
if (r_cons_is_breaked ()) {
|
|
goto jacuzzi;
|
|
}
|
|
if (is_apple_target (core)) {
|
|
oldstr = r_print_rowlog (core->print, "Check for objc references (aao)");
|
|
r_print_rowlog_done (core->print, oldstr);
|
|
cmd_anal_objc (core, input + 1, true);
|
|
}
|
|
r_core_task_yield (&core->tasks);
|
|
oldstr = r_print_rowlog (core->print, "Finding and parsing C++ vtables (avrr)");
|
|
r_core_cmd0 (core, "avrr");
|
|
r_print_rowlog_done (core->print, oldstr);
|
|
r_core_task_yield (&core->tasks);
|
|
r_config_set_i (core->config, "anal.calls", c);
|
|
r_core_task_yield (&core->tasks);
|
|
if (r_cons_is_breaked ()) {
|
|
goto jacuzzi;
|
|
}
|
|
bool isPreludableArch = core->rasm->config->bits == 64 && r_str_startswith (r_config_get (core->config, "asm.arch"), "arm");
|
|
|
|
if (!didAap && isPreludableArch) {
|
|
didAap = true;
|
|
oldstr = r_print_rowlog (core->print, "Finding function preludes");
|
|
(void)r_core_search_preludes (core, false); // "aap"
|
|
r_print_rowlog_done (core->print, oldstr);
|
|
r_core_task_yield (&core->tasks);
|
|
}
|
|
if (!r_str_startswith (r_config_get (core->config, "asm.arch"), "x86")) {
|
|
r_core_cmd0 (core, "aav");
|
|
r_core_task_yield (&core->tasks);
|
|
if (cfg_debug) {
|
|
oldstr = r_print_rowlog (core->print, "Skipping function emulation in debugger mode (aaef)");
|
|
// nothing to do
|
|
r_print_rowlog_done (core->print, oldstr);
|
|
} else {
|
|
bool ioCache = r_config_get_i (core->config, "io.pcache");
|
|
r_config_set_i (core->config, "io.pcache", 1);
|
|
oldstr = r_print_rowlog (core->print, "Emulate functions to find computed references (aaef)");
|
|
r_core_cmd0 (core, "aaef");
|
|
r_print_rowlog_done (core->print, oldstr);
|
|
r_core_task_yield (&core->tasks);
|
|
r_config_set_i (core->config, "io.pcache", ioCache);
|
|
}
|
|
if (r_cons_is_breaked ()) {
|
|
goto jacuzzi;
|
|
}
|
|
}
|
|
if (r_config_get_i (core->config, "anal.autoname")) {
|
|
oldstr = r_print_rowlog (core->print,
|
|
"Speculatively constructing a function name "
|
|
"for fcn.* and sym.func.* functions (aan)");
|
|
r_core_anal_autoname_all_fcns (core);
|
|
r_print_rowlog_done (core->print, oldstr);
|
|
r_core_task_yield (&core->tasks);
|
|
}
|
|
if (core->anal->opt.vars) {
|
|
RAnalFunction *fcni;
|
|
RListIter *iter;
|
|
r_list_foreach (core->anal->fcns, iter, fcni) {
|
|
if (r_cons_is_breaked ()) {
|
|
break;
|
|
}
|
|
RList *list = r_anal_var_list (core->anal, fcni, 'r');
|
|
if (!r_list_empty (list)) {
|
|
r_list_free (list);
|
|
continue;
|
|
}
|
|
//extract only reg based var here
|
|
r_core_recover_vars (core, fcni, true);
|
|
r_list_free (list);
|
|
}
|
|
r_core_task_yield (&core->tasks);
|
|
}
|
|
if (!sdb_isempty (core->anal->sdb_zigns)) {
|
|
oldstr = r_print_rowlog (core->print, "Check for zignature from zigns folder (z/)");
|
|
r_core_cmd0 (core, "z/");
|
|
r_print_rowlog_done (core->print, oldstr);
|
|
r_core_task_yield (&core->tasks);
|
|
}
|
|
if (cfg_debug) {
|
|
oldstr = r_print_rowlog (core->print, "Skipping type matching analysis in debugger mode (aaft)");
|
|
// nothing to do
|
|
r_print_rowlog_done (core->print, oldstr);
|
|
} else {
|
|
oldstr = r_print_rowlog (core->print, "Type matching analysis for all functions (aaft)");
|
|
r_core_cmd0 (core, "aaft");
|
|
r_print_rowlog_done (core->print, oldstr);
|
|
}
|
|
r_core_task_yield (&core->tasks);
|
|
|
|
oldstr = r_print_rowlog (core->print, "Propagate noreturn information (aanr)");
|
|
r_core_anal_propagate_noreturn (core, UT64_MAX);
|
|
r_print_rowlog_done (core->print, oldstr);
|
|
r_core_task_yield (&core->tasks);
|
|
|
|
// apply dwarf function information
|
|
Sdb *dwarf_sdb = sdb_ns (core->anal->sdb, "dwarf", 0);
|
|
if (dwarf_sdb) {
|
|
oldstr = r_print_rowlog (core->print, "Integrate dwarf function information.");
|
|
r_anal_dwarf_integrate_functions (core->anal, core->flags, dwarf_sdb);
|
|
r_print_rowlog_done (core->print, oldstr);
|
|
}
|
|
|
|
if (input[1] == 'a') { // "aaaa"
|
|
if (!didAap) {
|
|
didAap = true;
|
|
oldstr = r_print_rowlog (core->print, "Finding function preludes");
|
|
(void)r_core_search_preludes (core, false); // "aap"
|
|
r_print_rowlog_done (core->print, oldstr);
|
|
r_core_task_yield (&core->tasks);
|
|
}
|
|
oldstr = r_print_rowlog (core->print, "Enable constraint types analysis for variables");
|
|
r_config_set (core->config, "anal.types.constraint", "true");
|
|
r_print_rowlog_done (core->print, oldstr);
|
|
} else {
|
|
oldstr = r_print_rowlog (core->print, "Use -AA or aaaa to perform additional experimental analysis.");
|
|
r_print_rowlog_done (core->print, oldstr);
|
|
}
|
|
r_core_cmd0 (core, "s-");
|
|
if (dh_orig) {
|
|
r_core_cmdf (core, "dL %s", dh_orig);
|
|
r_core_task_yield (&core->tasks);
|
|
}
|
|
}
|
|
r_core_seek (core, curseek, true);
|
|
jacuzzi:
|
|
// XXX this shouldnt be called. flags muts be created wheen the function is registered
|
|
flag_every_function (core);
|
|
r_core_anal_propagate_noreturn (core, UT64_MAX);
|
|
r_cons_break_pop ();
|
|
R_FREE (dh_orig);
|
|
}
|
|
break;
|
|
case 't': // "aat"
|
|
if (input[1] == '?') {
|
|
r_core_cmd_help_match (core, help_msg_aa, "aat", true);
|
|
} else {
|
|
char *off = input[1]? r_str_trim_dup (input + 2): NULL;
|
|
RAnalFunction *fcn;
|
|
RListIter *it;
|
|
if (off && *off) {
|
|
ut64 addr = r_num_math (NULL, off);
|
|
fcn = r_anal_get_function_at (core->anal, core->offset);
|
|
if (fcn) {
|
|
r_core_link_stroff (core, fcn);
|
|
} else {
|
|
R_LOG_ERROR ("Cannot find function at %08" PFMT64x, addr);
|
|
}
|
|
} else {
|
|
if (r_list_empty (core->anal->fcns)) {
|
|
R_LOG_ERROR ("Couldn't find any functions");
|
|
break;
|
|
}
|
|
r_list_foreach (core->anal->fcns, it, fcn) {
|
|
if (r_cons_is_breaked ()) {
|
|
break;
|
|
}
|
|
r_core_link_stroff (core, fcn);
|
|
}
|
|
}
|
|
free (off);
|
|
}
|
|
break;
|
|
case 'T': // "aaT"
|
|
cmd_anal_aftertraps (core, input + 1);
|
|
break;
|
|
case 'o': // "aao"
|
|
if (input[1] == '?') { // "aao?"
|
|
r_core_cmd_help_match (core, help_msg_aa, "aao", true);
|
|
} else {
|
|
cmd_anal_objc (core, input + 1, false);
|
|
}
|
|
break;
|
|
case 'e': // "aae"
|
|
if (input[1] == 'f') { // "aaef"
|
|
if (input[2] == '?') {
|
|
r_core_cmd_help_match (core, help_msg_aae, "aaef", true);
|
|
} else {
|
|
RListIter *it;
|
|
RAnalFunction *fcn;
|
|
ut64 cur_seek = core->offset;
|
|
r_list_foreach (core->anal->fcns, it, fcn) {
|
|
r_core_seek (core, fcn->addr, true);
|
|
r_core_anal_esil (core, "f", NULL);
|
|
// __anal_esil_function (core, fcn->addr);
|
|
}
|
|
r_core_seek (core, cur_seek, true);
|
|
}
|
|
} else if (input[1] == '?') { // "aae?"
|
|
r_core_cmd_help (core, help_msg_aae);
|
|
} else if (input[1] == 'p') { // "aaep" // auto define all esil pins
|
|
if (input[2] == '?') {
|
|
r_core_cmd_help_match (core, help_msg_aae, "aaef", true);
|
|
} else {
|
|
r_core_cmd0 (core, "aep ret0@@@i");
|
|
r_core_cmd0 (core, "aepa@@@i");
|
|
}
|
|
} else if (input[1] == ' ') { // "aae "
|
|
char *arg = r_str_trim_dup (input + 1);
|
|
const char *len = (char *)arg;
|
|
char *addr = strchr (arg, ' ');
|
|
if (addr) {
|
|
*addr = 0;
|
|
addr = (char *)r_str_trim_head_ro (addr + 1);
|
|
}
|
|
r_core_anal_esil (core, len, addr);
|
|
free (arg);
|
|
} else {
|
|
ut64 at = core->offset;
|
|
RIOMap *map;
|
|
RListIter *iter;
|
|
RList *list = r_core_get_boundaries_prot (core, -1, NULL, "anal");
|
|
if (!list) {
|
|
break;
|
|
}
|
|
if (!strcmp ("range", r_config_get (core->config, "anal.in"))) {
|
|
ut64 from = r_config_get_i (core->config, "anal.from");
|
|
ut64 to = r_config_get_i (core->config, "anal.to");
|
|
if (to > from) {
|
|
char *len = r_str_newf (" 0x%"PFMT64x, to - from);
|
|
r_core_seek (core, from, true);
|
|
r_core_anal_esil (core, len, NULL);
|
|
free (len);
|
|
} else {
|
|
R_LOG_ERROR ("Assert: anal.from > anal.to");
|
|
}
|
|
} else {
|
|
r_list_foreach (list, iter, map) {
|
|
if (map->perm & R_PERM_X) {
|
|
char *ss = r_str_newf (" 0x%"PFMT64x, r_io_map_size (map));
|
|
r_core_seek (core, r_io_map_begin (map), true);
|
|
r_core_anal_esil (core, ss, NULL);
|
|
free (ss);
|
|
}
|
|
}
|
|
r_list_free (list);
|
|
}
|
|
r_core_seek (core, at, true);
|
|
}
|
|
break;
|
|
case 'r':
|
|
if (input[1] == '?') {
|
|
r_core_cmd_help (core, help_msg_aar);
|
|
} else {
|
|
(void)r_core_anal_refs (core, input + 1);
|
|
}
|
|
break;
|
|
default:
|
|
r_core_cmd_help (core, help_msg_aa);
|
|
break;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
static bool anal_fcn_data(RCore *core, const char *input) {
|
|
RAnalFunction *fcn = r_anal_get_fcn_in (core->anal, core->offset, R_ANAL_FCN_TYPE_ANY);
|
|
if (fcn) {
|
|
int i;
|
|
bool gap = false;
|
|
ut64 gap_addr = UT64_MAX;
|
|
ut32 fcn_size = r_anal_function_size_from_entry (fcn);
|
|
char *bitmap = calloc (1, fcn_size);
|
|
if (bitmap) {
|
|
RAnalBlock *b;
|
|
RListIter *iter;
|
|
r_list_foreach (fcn->bbs, iter, b) {
|
|
int f = b->addr - fcn->addr;
|
|
int t = R_MIN (f + b->size, fcn_size);
|
|
if (f >= 0) {
|
|
while (f < t) {
|
|
bitmap[f++] = 1;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
for (i = 0; i < fcn_size; i++) {
|
|
ut64 here = fcn->addr + i;
|
|
if (bitmap && bitmap[i]) {
|
|
if (gap) {
|
|
r_cons_printf ("Cd %" PFMT64u " @ 0x%08"PFMT64x"\n", here - gap_addr, gap_addr);
|
|
gap = false;
|
|
}
|
|
gap_addr = UT64_MAX;
|
|
} else {
|
|
if (!gap) {
|
|
gap = true;
|
|
gap_addr = here;
|
|
}
|
|
}
|
|
}
|
|
if (gap) {
|
|
r_cons_printf ("Cd %" PFMT64u " @ 0x%08" PFMT64x "\n", fcn->addr + fcn_size - gap_addr, gap_addr);
|
|
}
|
|
free (bitmap);
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
static bool anal_fcn_data_gaps(RCore *core, const char *input) {
|
|
ut64 end = UT64_MAX;
|
|
RAnalFunction *fcn;
|
|
RListIter *iter;
|
|
int i, wordsize = (core->rasm->config->bits == 64)? 8: 4;
|
|
r_list_sort (core->anal->fcns, cmpaddr);
|
|
r_list_foreach (core->anal->fcns, iter, fcn) {
|
|
if (end != UT64_MAX) {
|
|
int range = fcn->addr - end;
|
|
if (range > 0) {
|
|
for (i = 0; i + wordsize < range; i+= wordsize) {
|
|
r_cons_printf ("Cd %d @ 0x%08"PFMT64x"\n", wordsize, end + i);
|
|
}
|
|
r_cons_printf ("Cd %d @ 0x%08"PFMT64x"\n", range - i, end + i);
|
|
//r_cons_printf ("Cd %d @ 0x%08"PFMT64x"\n", range, end);
|
|
}
|
|
}
|
|
end = fcn->addr + r_anal_function_size_from_entry (fcn);
|
|
}
|
|
return true;
|
|
}
|
|
|
|
static void cmd_anal_rtti(RCore *core, const char *input) {
|
|
switch (input[0]) {
|
|
case '\0': // "avr"
|
|
case 'j': // "avrj"
|
|
r_anal_rtti_print_at_vtable (core->anal, core->offset, input[0]);
|
|
break;
|
|
case 'a': // "avra"
|
|
r_anal_rtti_print_all (core->anal, input[1]);
|
|
break;
|
|
case 'r': // "avrr"
|
|
r_anal_rtti_recover_all (core->anal);
|
|
break;
|
|
case 'D': { // "avrD"
|
|
char *name = r_str_trim_dup (input + 1);
|
|
char *demangled = r_anal_rtti_demangle_class_name (core->anal, name);
|
|
free (name);
|
|
if (demangled) {
|
|
r_cons_println (demangled);
|
|
free (demangled);
|
|
}
|
|
break;
|
|
}
|
|
default :
|
|
r_core_cmd_help (core, help_msg_av);
|
|
break;
|
|
}
|
|
}
|
|
|
|
static void cmd_avg(RCore *core, const char* input) {
|
|
switch (input[0]) {
|
|
case ' ':
|
|
if (strchr (input + 1, ' ')) {
|
|
char *a = r_str_trim_dup (input + 1);
|
|
char *b = strchr (a, ' ');
|
|
if (b) {
|
|
*b++ = 0;
|
|
r_anal_global_add (core->anal, core->offset, a, b);
|
|
} else {
|
|
RFlagItem *fi = r_anal_global_get (core->anal, core->offset);
|
|
if (fi) {
|
|
R_LOG_INFO ("type %s", fi->type);
|
|
}
|
|
}
|
|
free (a);
|
|
}
|
|
break;
|
|
case '-':
|
|
r_anal_global_del (core->anal, core->offset);
|
|
break;
|
|
case '\0': // "av"
|
|
r_core_cmd0 (core, "fs+globals;f;fs-");
|
|
break;
|
|
default :
|
|
r_core_cmd_help (core, help_msg_avg);
|
|
break;
|
|
}
|
|
}
|
|
static void cmd_anal_virtual_functions(RCore *core, const char* input) {
|
|
switch (input[0]) {
|
|
case 'g':
|
|
cmd_avg (core, input + 1);
|
|
break;
|
|
case '\0': // "av"
|
|
case '*': // "av*"
|
|
case 'j': // "avj"
|
|
r_anal_list_vtables (core->anal, input[0]);
|
|
break;
|
|
case 'r': // "avr"
|
|
cmd_anal_rtti (core, input + 1);
|
|
break;
|
|
default :
|
|
r_core_cmd_help (core, help_msg_av);
|
|
break;
|
|
}
|
|
}
|
|
|
|
static void cmd_anal_class_method(RCore *core, const char *input) {
|
|
RAnalClassErr err = R_ANAL_CLASS_ERR_SUCCESS;
|
|
char c = input[0];
|
|
switch (c) {
|
|
case ' ': // "acm"
|
|
case '-': // "acm-"
|
|
case 'n': { // "acmn"
|
|
const char *str = r_str_trim_head_ro (input + 1);
|
|
if (!*str) {
|
|
R_LOG_ERROR ("No class name given");
|
|
break;
|
|
}
|
|
char *cstr = strdup (str);
|
|
if (!cstr) {
|
|
break;
|
|
}
|
|
char *end = strchr (cstr, ' ');
|
|
if (!end) {
|
|
R_LOG_ERROR ("No method name given");
|
|
free (cstr);
|
|
break;
|
|
}
|
|
*end = '\0';
|
|
char *name_str = end + 1;
|
|
|
|
if (c == ' ' || c == 'n') {
|
|
end = strchr (name_str, ' ');
|
|
if (!end) {
|
|
if (c == ' ') {
|
|
R_LOG_ERROR ("No offset given");
|
|
} else if (c == 'n') {
|
|
R_LOG_ERROR ("No new method name given");
|
|
}
|
|
free (cstr);
|
|
break;
|
|
}
|
|
*end = '\0';
|
|
}
|
|
|
|
if (c == ' ') {
|
|
char *addr_str = end + 1;
|
|
end = strchr (addr_str, ' ');
|
|
if (end) {
|
|
*end = '\0';
|
|
}
|
|
|
|
RAnalMethod meth;
|
|
meth.name = name_str;
|
|
meth.addr = r_num_get (core->num, addr_str);
|
|
meth.vtable_offset = -1;
|
|
if (end) {
|
|
meth.vtable_offset = (int)r_num_get (core->num, end + 1);
|
|
}
|
|
err = r_anal_class_method_set (core->anal, cstr, &meth);
|
|
} else if (c == 'n') {
|
|
char *new_name_str = end + 1;
|
|
end = strchr (new_name_str, ' ');
|
|
if (end) {
|
|
*end = '\0';
|
|
}
|
|
|
|
err = r_anal_class_method_rename (core->anal, cstr, name_str, new_name_str);
|
|
} else if (c == '-') {
|
|
err = r_anal_class_method_delete (core->anal, cstr, name_str);
|
|
}
|
|
|
|
free (cstr);
|
|
break;
|
|
}
|
|
default:
|
|
r_core_cmd_help (core, help_msg_ac);
|
|
break;
|
|
}
|
|
|
|
switch (err) {
|
|
case R_ANAL_CLASS_ERR_NONEXISTENT_CLASS:
|
|
R_LOG_ERROR ("Class does not exist");
|
|
break;
|
|
case R_ANAL_CLASS_ERR_NONEXISTENT_ATTR:
|
|
R_LOG_ERROR ("Method does not exist");
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
static void cmd_anal_class_base(RCore *core, const char *input) {
|
|
RAnalClassErr err = R_ANAL_CLASS_ERR_SUCCESS;
|
|
char c = input[0];
|
|
switch (c) {
|
|
case ' ': // "acb"
|
|
case '-': { // "acb-"
|
|
const char *str = r_str_trim_head_ro (input + 1);
|
|
if (!*str) {
|
|
R_LOG_ERROR ("No class name given");
|
|
return;
|
|
}
|
|
char *cstr = strdup (str);
|
|
if (!cstr) {
|
|
break;
|
|
}
|
|
char *end = strchr (cstr, ' ');
|
|
if (end) {
|
|
*end = '\0';
|
|
end++;
|
|
}
|
|
|
|
if (!end || *end == '\0') {
|
|
if (c == ' ') {
|
|
r_anal_class_list_bases (core->anal, cstr);
|
|
} else /*if (c == '-')*/ {
|
|
R_LOG_ERROR ("No base id given");
|
|
}
|
|
free (cstr);
|
|
break;
|
|
}
|
|
|
|
char *base_str = end;
|
|
end = strchr (base_str, ' ');
|
|
if (end) {
|
|
*end = '\0';
|
|
}
|
|
|
|
if (c == '-') {
|
|
err = r_anal_class_base_delete (core->anal, cstr, base_str);
|
|
free (cstr);
|
|
break;
|
|
}
|
|
|
|
RAnalBaseClass base;
|
|
base.id = NULL;
|
|
base.offset = 0;
|
|
base.class_name = base_str;
|
|
|
|
if (end) {
|
|
base.offset = r_num_get (core->num, end + 1);
|
|
}
|
|
|
|
err = r_anal_class_base_set (core->anal, cstr, &base);
|
|
free (base.id);
|
|
free (cstr);
|
|
break;
|
|
}
|
|
default:
|
|
r_core_cmd_help (core, help_msg_ac);
|
|
break;
|
|
}
|
|
|
|
if (err == R_ANAL_CLASS_ERR_NONEXISTENT_CLASS) {
|
|
R_LOG_ERROR ("Class does not exist");
|
|
}
|
|
}
|
|
|
|
static void cmd_anal_class_vtable(RCore *core, const char *input) {
|
|
RAnalClassErr err = R_ANAL_CLASS_ERR_SUCCESS;
|
|
char c = input[0];
|
|
switch (c) {
|
|
case 'f': {// "acvf" [offset] ([class_name])
|
|
const char *str = r_str_trim_head_ro (input + 1);
|
|
if (!*str) {
|
|
R_LOG_ERROR ("No offset given");
|
|
return;
|
|
}
|
|
char *cstr = strdup (str);
|
|
if (!cstr) {
|
|
break;
|
|
}
|
|
char *end = strchr (cstr, ' ');
|
|
if (end) {
|
|
*end = '\0';
|
|
end++;
|
|
}
|
|
ut64 offset_arg = r_num_get (core->num, cstr); // Should I allow negative offset?
|
|
char *class_arg = NULL;
|
|
if (end) {
|
|
class_arg = (char *)r_str_trim_head_ro (end);
|
|
}
|
|
|
|
if (class_arg) {
|
|
end = (char *)r_str_trim_head_wp (class_arg); // in case of extra unwanted stuff at the cmd end
|
|
*end = '\0';
|
|
}
|
|
r_anal_class_list_vtable_offset_functions (core->anal, class_arg, offset_arg);
|
|
|
|
free (cstr);
|
|
break;
|
|
}
|
|
case ' ': // "acv"
|
|
case '-': { // "acv-"
|
|
const char *str = r_str_trim_head_ro (input + 1);
|
|
if (!*str) {
|
|
R_LOG_ERROR ("No class name given");
|
|
return;
|
|
}
|
|
char *cstr = strdup (str);
|
|
if (!cstr) {
|
|
break;
|
|
}
|
|
char *end = strchr (cstr, ' ');
|
|
if (end) {
|
|
*end = '\0';
|
|
end++;
|
|
}
|
|
|
|
if (!end || *end == '\0') {
|
|
if (c == ' ') {
|
|
r_anal_class_list_vtables (core->anal, cstr);
|
|
} else /*if (c == '-')*/ {
|
|
R_LOG_ERROR ("No vtable id given. See acv [class name]");
|
|
}
|
|
free (cstr);
|
|
break;
|
|
}
|
|
|
|
char *arg1_str = end;
|
|
|
|
if (c == '-') {
|
|
err = r_anal_class_vtable_delete (core->anal, cstr, arg1_str);
|
|
free (cstr);
|
|
break;
|
|
}
|
|
|
|
end = strchr (arg1_str, ' ');
|
|
if (end) {
|
|
*end = '\0';
|
|
}
|
|
|
|
RAnalVTable vtable;
|
|
vtable.id = NULL;
|
|
vtable.addr = r_num_get (core->num, arg1_str);
|
|
vtable.offset = 0;
|
|
vtable.size = 0;
|
|
|
|
char *arg3_str = NULL;
|
|
if (end) {
|
|
vtable.offset = r_num_get (core->num, end + 1);
|
|
// end + 1 won't work on extra whitespace between arguments, TODO
|
|
arg3_str = strchr (end + 1, ' ');
|
|
}
|
|
|
|
if (arg3_str) {
|
|
vtable.size = r_num_get (core->num, arg3_str + 1);
|
|
}
|
|
|
|
err = r_anal_class_vtable_set (core->anal, cstr, &vtable);
|
|
free (vtable.id);
|
|
free (cstr);
|
|
break;
|
|
}
|
|
default:
|
|
r_core_cmd_help (core, help_msg_ac);
|
|
break;
|
|
}
|
|
if (err == R_ANAL_CLASS_ERR_NONEXISTENT_CLASS) {
|
|
R_LOG_ERROR ("Class does not exist");
|
|
}
|
|
}
|
|
|
|
static void cmd_anal_classes(RCore *core, const char *input) {
|
|
switch (input[0]) {
|
|
case 'l': // "acl"
|
|
if (input[1] == 'l') { // "acll" (name)
|
|
char mode = 0;
|
|
int arg_offset = 2;
|
|
if (input[2] == 'j') {
|
|
arg_offset++;
|
|
mode = 'j';
|
|
}
|
|
const char *arg = r_str_trim_head_ro (input + arg_offset);
|
|
if (*arg) { // if there is an argument
|
|
char *class_name = strdup (arg);
|
|
if (!class_name) {
|
|
break;
|
|
}
|
|
char *name_end = (char *)r_str_trim_head_wp (class_name);
|
|
*name_end = 0; // trim the whitespace around the name
|
|
if (mode == 'j') {
|
|
PJ *pj = pj_new ();
|
|
r_anal_class_json (core->anal, pj, class_name);
|
|
r_cons_printf ("%s\n", pj_string (pj));
|
|
pj_free (pj);
|
|
} else {
|
|
r_anal_class_print (core->anal, class_name, true);
|
|
}
|
|
free (class_name);
|
|
break;
|
|
}
|
|
}
|
|
r_anal_class_list (core->anal, input[1]);
|
|
break;
|
|
case ' ': // "ac"
|
|
case '-': // "ac-"
|
|
case 'n': { // "acn"
|
|
const char *str = r_str_trim_head_ro (input + 1);
|
|
if (!*str) {
|
|
break;
|
|
}
|
|
char *cstr = strdup (str);
|
|
if (!cstr) {
|
|
break;
|
|
}
|
|
char *end = strchr (cstr, ' ');
|
|
if (end) {
|
|
*end = '\0';
|
|
}
|
|
if (input[0] == '-') {
|
|
r_anal_class_delete (core->anal, cstr);
|
|
} else if (input[0] == 'n') {
|
|
if (!end) {
|
|
R_LOG_ERROR ("No new class name given");
|
|
} else {
|
|
char *new_name = end + 1;
|
|
end = strchr (new_name, ' ');
|
|
if (end) {
|
|
*end = '\0';
|
|
}
|
|
RAnalClassErr err = r_anal_class_rename (core->anal, cstr, new_name);
|
|
if (err == R_ANAL_CLASS_ERR_NONEXISTENT_CLASS) {
|
|
R_LOG_ERROR ("Class does not exist");
|
|
} else if (err == R_ANAL_CLASS_ERR_CLASH) {
|
|
R_LOG_ERROR ("A class with this name already exists");
|
|
}
|
|
}
|
|
} else {
|
|
r_anal_class_create (core->anal, cstr);
|
|
}
|
|
free (cstr);
|
|
break;
|
|
}
|
|
case 'v':
|
|
cmd_anal_class_vtable (core, input + 1);
|
|
break;
|
|
case 'b': // "acb"
|
|
cmd_anal_class_base (core, input + 1);
|
|
break;
|
|
case 'm': // "acm"
|
|
cmd_anal_class_method (core, input + 1);
|
|
break;
|
|
case 'g': { // "acg"
|
|
RGraph *graph = r_anal_class_get_inheritance_graph (core->anal);
|
|
if (!graph) {
|
|
R_LOG_ERROR ("Couldn't create graph");
|
|
break;
|
|
}
|
|
r_core_graph_print (core, graph, -1, false, input + 1);
|
|
r_graph_free (graph);
|
|
} break;
|
|
default: // "ac?"
|
|
r_core_cmd_help (core, help_msg_ac);
|
|
break;
|
|
}
|
|
}
|
|
|
|
static void show_reg_args(RCore *core, int nargs, RStrBuf *sb) {
|
|
int i;
|
|
char regname[16];
|
|
if (nargs < 0) {
|
|
nargs = 4; // default args if not defined
|
|
}
|
|
for (i = 0; i < nargs; i++) {
|
|
snprintf (regname, sizeof (regname), "A%d", i);
|
|
ut64 v = r_reg_getv (core->anal->reg, regname);
|
|
if (sb) {
|
|
r_strbuf_appendf (sb, "%s0x%08"PFMT64x, i? ", ": "", v);
|
|
} else {
|
|
r_cons_printf ("A%d 0x%08"PFMT64x"\n", i, v);
|
|
}
|
|
}
|
|
}
|
|
|
|
// ripped from disasm.c: dupe code from there
|
|
// TODO: Implement aC* and aCj
|
|
static void cmd_anal_aC(RCore *core, const char *input) {
|
|
bool is_aCer = false;
|
|
const char *cc = r_anal_cc_default (core->anal);
|
|
RAnalFuncArg *arg;
|
|
RListIter *iter;
|
|
RListIter *nextele;
|
|
const char *iarg = strchr (input, ' ');
|
|
if (input[0] == 'e' && input[1] == 'f') { // "aCf"
|
|
// hacky :D
|
|
r_core_cmdf (core, ".aCe* $$ @@=`pdr~call`");
|
|
return;
|
|
}
|
|
if (iarg) {
|
|
iarg++;
|
|
}
|
|
if (!iarg) {
|
|
r_core_cmd_help (core, help_msg_aC);
|
|
return;
|
|
}
|
|
RStrBuf *sb = r_strbuf_new ("");
|
|
ut64 pcv = r_num_math (core->num, iarg);
|
|
if (input[0] == 'e') { // "aCe"
|
|
is_aCer = (input[1] == '*');
|
|
r_core_cmdf (core, ".abte 0x%08"PFMT64x, pcv);
|
|
}
|
|
RAnalOp* op = r_core_anal_op (core, pcv, -1);
|
|
if (!op) {
|
|
r_strbuf_free (sb);
|
|
return;
|
|
}
|
|
bool go_on = true;
|
|
if (op->type != R_ANAL_OP_TYPE_CALL) {
|
|
show_reg_args (core, -1, sb);
|
|
go_on = false;
|
|
}
|
|
const char *fcn_name = NULL;
|
|
RAnalFunction *fcn;
|
|
if (go_on) {
|
|
fcn = r_anal_get_function_at (core->anal, pcv);
|
|
if (fcn) {
|
|
fcn_name = fcn->name;
|
|
} else {
|
|
RFlagItem *item = r_flag_get_i (core->flags, op->jump);
|
|
if (item) {
|
|
fcn_name = item->name;
|
|
}
|
|
}
|
|
char *key = (fcn_name)? resolve_fcn_name (core->anal, fcn_name): NULL;
|
|
if (key) {
|
|
const char *fcn_type = r_type_func_ret (core->anal->sdb_types, key);
|
|
int nargs = r_type_func_args_count (core->anal->sdb_types, key);
|
|
// remove other comments
|
|
if (fcn_type) {
|
|
r_strbuf_appendf (sb, "%s%s%s(", r_str_getf (fcn_type),
|
|
(*fcn_type && fcn_type[strlen (fcn_type) - 1] == '*') ? "" : " ",
|
|
r_str_getf (key));
|
|
if (!nargs) {
|
|
r_strbuf_append (sb, "void)\n");
|
|
}
|
|
} else {
|
|
R_LOG_ERROR ("Cannot find any function signature");
|
|
}
|
|
free (key);
|
|
} else {
|
|
if (is_aCer) {
|
|
show_reg_args (core, -1, sb);
|
|
go_on = true;
|
|
} else {
|
|
show_reg_args (core, -1, NULL);
|
|
go_on = false;
|
|
}
|
|
}
|
|
}
|
|
if (go_on) {
|
|
ut64 s_width = (core->anal->config->bits == 64)? 8: 4;
|
|
const char *sp = r_reg_get_name (core->anal->reg, R_REG_NAME_SP);
|
|
ut64 spv = r_reg_getv (core->anal->reg, sp);
|
|
r_reg_setv (core->anal->reg, sp, spv + s_width); // temporarily set stack ptr to sync with carg.c
|
|
RList *list = r_core_get_func_args (core, fcn_name);
|
|
if (!r_list_empty (list)) {
|
|
#if 0
|
|
bool warning = false;
|
|
bool on_stack = false;
|
|
r_list_foreach (list, iter, arg) {
|
|
if (r_str_startswith (arg->cc_source, "stack")) {
|
|
on_stack = true;
|
|
}
|
|
if (!arg->size) {
|
|
r_cons_printf ("%s: unk_size", arg->c_type);
|
|
warning = true;
|
|
}
|
|
#endif
|
|
r_list_foreach (list, iter, arg) {
|
|
nextele = r_list_iter_get_next (iter);
|
|
if (!arg->fmt) {
|
|
r_strbuf_appendf (sb, "?%s", nextele? ", ": "");
|
|
} else {
|
|
// print_fcn_arg (core, arg->orig_c_type, arg->name, arg->fmt, arg->src, on_stack, 0);
|
|
// const char *fmt = arg->orig_c_type;
|
|
ut64 addr = arg->src;
|
|
char *res = r_core_cmd_strf (core, "pfq %s @ 0x%08" PFMT64x, arg->fmt, addr);
|
|
// r_cons_printf ("pfq *%s @ 0x%08" PFMT64x"\n", arg->fmt, addr);
|
|
r_str_trim (res);
|
|
r_strbuf_appendf (sb, "%s", res);
|
|
free (res);
|
|
}
|
|
}
|
|
r_strbuf_append (sb, ")");
|
|
} else {
|
|
// function name not resolved
|
|
int i, nargs = 4; // DEFAULT_NARGS;
|
|
if (fcn) {
|
|
// @TODO: fcn->nargs should be updated somewhere and used here instead
|
|
nargs = r_anal_var_count_args (fcn);
|
|
}
|
|
if (nargs > 0) {
|
|
if (fcn_name) {
|
|
r_strbuf_appendf (sb, "; %s(", fcn_name);
|
|
} else {
|
|
r_strbuf_appendf (sb, "; 0x%"PFMT64x"(", pcv);
|
|
}
|
|
for (i = 0; i < nargs; i++) {
|
|
ut64 v = r_debug_arg_get (core->dbg, cc, i);
|
|
r_strbuf_appendf (sb, "%s0x%"PFMT64x, i?", ":"", v);
|
|
}
|
|
r_strbuf_append (sb, ")");
|
|
}
|
|
}
|
|
r_list_free (list);
|
|
r_reg_setv (core->anal->reg, sp, spv); // reset stack ptr
|
|
}
|
|
r_anal_op_free (op);
|
|
char *s = r_strbuf_drain (sb);
|
|
if (is_aCer) {
|
|
char *u = r_base64_encode_dyn (s, -1);
|
|
if (u) {
|
|
r_cons_printf ("CCu base64:%s\n", u);
|
|
free (u);
|
|
}
|
|
} else {
|
|
r_cons_println (s);
|
|
}
|
|
free (s);
|
|
}
|
|
|
|
static bool core_anal_abf(RCore *core, const char* input) {
|
|
if (strchr (input, '?')) {
|
|
r_core_cmd_help_match (core, help_msg_ab, "abf", true);
|
|
return false;
|
|
}
|
|
ut64 addr = r_num_math (core->num, input);
|
|
if (!addr || addr == UT64_MAX) {
|
|
addr = core->offset;
|
|
}
|
|
|
|
RAnalBlock *bb, *bb2;
|
|
RListIter *iter, *iter2, *bbiter;
|
|
RAnalFunction *fcn;
|
|
RList *bbs = r_anal_get_blocks_in (core->anal, addr);
|
|
r_list_foreach (bbs, bbiter, bb) {
|
|
if (!bb) {
|
|
R_LOG_ERROR ("Cannot find basic block");
|
|
return false;
|
|
}
|
|
r_list_foreach (bb->fcns, iter, fcn) {
|
|
r_list_foreach (fcn->bbs, iter2, bb2) {
|
|
if (bb == bb2) {
|
|
continue;
|
|
}
|
|
if (bb2->jump != UT64_MAX && bb2->jump == bb->addr) {
|
|
r_cons_printf ("0x%"PFMT64x"\n", bb2->addr);
|
|
}
|
|
if (bb2->fail != UT64_MAX && bb2->fail == bb->addr) {
|
|
r_cons_printf ("0x%"PFMT64x"\n", bb2->addr);
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
static int cmd_anal(void *data, const char *input) {
|
|
const char *r;
|
|
RCore *core = (RCore *)data;
|
|
ut32 tbs = core->blocksize;
|
|
switch (input[0]) {
|
|
case 'p': // "ap"
|
|
if (input[1] == '?') {
|
|
r_core_cmd_help (core, help_msg_ap);
|
|
} else {
|
|
const ut8 *prelude = (const ut8*)"\xe9\x2d"; //:fffff000";
|
|
const int prelude_sz = 2;
|
|
const int bufsz = 4096;
|
|
ut8 *buf = calloc (1, bufsz);
|
|
ut64 off = core->offset;
|
|
if (input[1] == ' ') {
|
|
off = r_num_math (core->num, input + 1);
|
|
}
|
|
r_io_read_at (core->io, off - bufsz + prelude_sz, buf, bufsz);
|
|
//const char *prelude = "\x2d\xe9\xf0\x47"; //:fffff000";
|
|
r_mem_reverse (buf, bufsz);
|
|
//r_print_hexdump (NULL, off, buf, bufsz, 16, -16);
|
|
const ut8 *pos = r_mem_mem (buf, bufsz, prelude, prelude_sz);
|
|
if (pos) {
|
|
int delta = (size_t)(pos - buf);
|
|
// R_LOG_DEBUG ("POS = %d", delta);
|
|
// R_LOG_DEBUG ("HIT = 0x%"PFMT64x, off - delta);
|
|
r_cons_printf ("0x%08"PFMT64x"\n", off - delta);
|
|
} else {
|
|
R_LOG_ERROR ("Cannot find prelude");
|
|
}
|
|
free (buf);
|
|
}
|
|
break;
|
|
case '8': // "a8"
|
|
if (input[1] == '?') {
|
|
r_core_cmd_help (core, help_msg_a8);
|
|
break;
|
|
}
|
|
ut8 *buf = malloc (strlen (input) + 1);
|
|
if (buf) {
|
|
int len = r_hex_str2bin (input + 1, buf);
|
|
if (len > 0) {
|
|
core_anal_bytes (core, buf, len, 0, input[1]);
|
|
}
|
|
free (buf);
|
|
}
|
|
break;
|
|
case 'b': // "ab"
|
|
switch (input[1]) {
|
|
case '.': // "ab."
|
|
r_core_cmd0 (core, "ab $$");
|
|
break;
|
|
case 'a': // "aba"
|
|
r_core_cmdf (core, "aeab%s", input + 1);
|
|
break;
|
|
case 'e': // "aeb"
|
|
r_core_cmdf (core, "aeb%s", input + 2);
|
|
break;
|
|
case 'b': // "abb"
|
|
core_anal_bbs (core, input + 2);
|
|
break;
|
|
case 'f': // "abf"
|
|
core_anal_abf (core, input + 2);
|
|
break;
|
|
case 'r': // "abr"
|
|
core_anal_bbs_range (core, input + 2);
|
|
break;
|
|
case ',': // "ab,"
|
|
case 't': // "abt"
|
|
cmd_anal_abt (core, input + 2);
|
|
break;
|
|
case 'l': // "abl"
|
|
if (input[2] == '?') {
|
|
r_core_cmd_help (core, help_msg_abl);
|
|
} else {
|
|
anal_bb_list (core, input + 2);
|
|
}
|
|
break;
|
|
case 'j': { // "abj"
|
|
ut64 addr = core->offset;
|
|
if (input[2] && input[2] != '.') {
|
|
addr = r_num_math (core->num, input + 2);
|
|
}
|
|
r_core_cmdf (core, "afbij @ 0x%"PFMT64x, addr);
|
|
break;
|
|
}
|
|
case '-': // "ab-"
|
|
if (input[2] == '*') {
|
|
r_anal_block_reset (core->anal);
|
|
} else {
|
|
ut64 addr = core->offset;
|
|
if (input[2] == ' ') {
|
|
addr = r_num_math (core->num, input + 1);
|
|
}
|
|
r_anal_delete_block_at (core->anal, addr);
|
|
}
|
|
break;
|
|
case 0:
|
|
case ' ': { // "ab "
|
|
// find block
|
|
ut64 addr = core->offset;
|
|
if (input[1] && input[1] != '.') {
|
|
addr = r_num_math (core->num, input + 1);
|
|
}
|
|
r_core_cmdf (core, "afbi @ 0x%"PFMT64x, addr);
|
|
break;
|
|
}
|
|
default:
|
|
r_core_cmd_help (core, help_msg_ab);
|
|
break;
|
|
}
|
|
break;
|
|
case 'c': // "ac"
|
|
cmd_anal_classes (core, input + 1);
|
|
break;
|
|
case 'C': // "aC"
|
|
cmd_anal_aC (core, input + 1);
|
|
break;
|
|
case 'i': cmd_anal_info (core, input + 1); break; // "ai"
|
|
case 'r': cmd_anal_reg (core, input + 1); break; // "ar"
|
|
case 'e': cmd_anal_esil (core, input + 1, true); break; // "ae"
|
|
case 'L':
|
|
switch (input[1]) {
|
|
case 'j':
|
|
case 'q':
|
|
case 0:
|
|
rasm2_list (core, NULL, input[1]);
|
|
break;
|
|
default:
|
|
// help
|
|
r_core_cmd_help (core, help_msg_La);
|
|
break;
|
|
}
|
|
break;
|
|
case 'o': cmd_anal_opcode (core, input + 1); break; // "ao"
|
|
case 'O': cmd_anal_bytes (core, input + 1); break; // "aO"
|
|
case 'F': // "aF"
|
|
if (input[1] == '?') {
|
|
r_core_cmd_help (core, help_msg_aF);
|
|
break;
|
|
}
|
|
r_core_anal_fcn (core, core->offset, UT64_MAX, R_ANAL_REF_TYPE_NULL, 1);
|
|
break;
|
|
case 'l':
|
|
{
|
|
RList *l = r_asm_cpus (core->rasm);
|
|
RListIter *iter;
|
|
char *c;
|
|
r_list_foreach (l, iter, c) {
|
|
r_cons_printf ("- %s\n", c);
|
|
}
|
|
r_list_free (l);
|
|
}
|
|
break;
|
|
case 'f': // "af"
|
|
if (!cmd_af (core, input)) {
|
|
return false;
|
|
}
|
|
break;
|
|
case 'n': // "an"
|
|
{
|
|
const char *name = "";
|
|
int mode = 0;
|
|
switch (input[1]) {
|
|
case '?':
|
|
r_core_cmd_help (core, help_msg_an);
|
|
mode = -1;
|
|
break;
|
|
case 'j':
|
|
case '*':
|
|
mode = input[1];
|
|
input++;
|
|
break;
|
|
}
|
|
if (mode >= 0) {
|
|
if (input[1] == ' ') {
|
|
name = input + 1;
|
|
while (name[0] == ' ') {
|
|
name++;
|
|
}
|
|
char *end = strchr (name, ' ');
|
|
if (end) {
|
|
*end = '\0';
|
|
}
|
|
}
|
|
if (R_STR_ISEMPTY (name)) {
|
|
name = NULL;
|
|
}
|
|
cmd_an (core, name, mode);
|
|
}
|
|
}
|
|
break;
|
|
case 'g': // "ag"
|
|
cmd_anal_graph (core, input + 1);
|
|
break;
|
|
case 's': // "as"
|
|
cmd_anal_syscall (core, input + 1);
|
|
break;
|
|
case 'v': // "av"
|
|
cmd_anal_virtual_functions (core, input + 1);
|
|
break;
|
|
case 'x': // "ax"
|
|
if (!cmd_anal_refs (core, input + 1)) {
|
|
return false;
|
|
}
|
|
break;
|
|
case '*': // "a*"
|
|
r_core_cmd0 (core, "afl*");
|
|
r_core_cmd0 (core, "ah*");
|
|
r_core_cmd0 (core, "ax*");
|
|
break;
|
|
case 'a': // "aa"
|
|
if (!cmd_anal_all (core, input + 1)) {
|
|
return false;
|
|
}
|
|
break;
|
|
case 'd': // "ad"
|
|
switch (input[1]) {
|
|
case 'f': // "adf"
|
|
if (input[2] == 'g') {
|
|
anal_fcn_data_gaps (core, r_str_trim_head_ro (input + 1));
|
|
} else {
|
|
anal_fcn_data (core, input + 1);
|
|
}
|
|
break;
|
|
case 't': // "adt"
|
|
cmd_anal_trampoline (core, input + 2);
|
|
break;
|
|
case ' ': { // "ad"
|
|
const int default_depth = 1;
|
|
const char *p;
|
|
int a, b;
|
|
a = r_num_math (core->num, input + 2);
|
|
p = strchr (input + 2, ' ');
|
|
b = p? r_num_math (core->num, p + 1): default_depth;
|
|
if (a < 1) {
|
|
a = 1;
|
|
}
|
|
if (b < 1) {
|
|
b = 1;
|
|
}
|
|
r_core_anal_data (core, core->offset, a, b, 0);
|
|
} break;
|
|
case 'k': // "adk"
|
|
r = r_anal_data_kind (core->anal,
|
|
core->offset, core->block, core->blocksize);
|
|
r_cons_println (r);
|
|
break;
|
|
case '\0': // "ad"
|
|
r_core_anal_data (core, core->offset, 2 + (core->blocksize / 4), 1, 0);
|
|
break;
|
|
case '4': // "ad4"
|
|
r_core_anal_data (core, core->offset, 2 + (core->blocksize / 4), 1, 4);
|
|
break;
|
|
case '8': // "ad8"
|
|
r_core_anal_data (core, core->offset, 2 + (core->blocksize / 4), 1, 8);
|
|
break;
|
|
default:
|
|
r_core_cmd_help (core, help_msg_ad);
|
|
break;
|
|
}
|
|
break;
|
|
case 'h': // "ah"
|
|
cmd_anal_hint (core, input + 1);
|
|
break;
|
|
case '!': // "a!"
|
|
if (core->anal && core->anal->cur && core->anal->cur->cmd_ext) {
|
|
return core->anal->cur->cmd_ext (core->anal, input + 1);
|
|
} else {
|
|
r_cons_printf ("No plugins for this analysis plugin\n");
|
|
}
|
|
break;
|
|
case 'j': // "aj"
|
|
r_core_cmd0 (core, "aflj");
|
|
break;
|
|
case 0: // "a"
|
|
r_core_cmd0 (core, "aai");
|
|
break;
|
|
case '?':
|
|
if (input[1] == 'j') {
|
|
r_cons_cmd_help_json (help_msg_a);
|
|
} else {
|
|
r_core_cmd_help (core, help_msg_a);
|
|
}
|
|
break;
|
|
default:
|
|
r_core_cmd_help (core, help_msg_a);
|
|
break;
|
|
}
|
|
if (tbs != core->blocksize) {
|
|
r_core_block_size (core, tbs);
|
|
}
|
|
if (r_cons_is_breaked ()) {
|
|
r_cons_clear_line (1);
|
|
}
|
|
return 0;
|
|
}
|