/* Copyright radare2 2014-2015 - Author: pancake */ // pls move the typedefs into roons and rename it -> RConsPanel #include typedef struct { int x; int y; int w; int h; int depth; int type; ut64 addr; char *cmd; char *text; } Panel; #define PANEL_TYPE_FRAME 0 #define PANEL_TYPE_DIALOG 1 #define PANEL_TYPE_FLOAT 2 static int COLW = 80; static RCore *_core; static int n_panels = 0; static void reloadPanels(RCore *core) ; static int menu_pos = 0; #define LIMIT 256 struct { int panels[LIMIT]; int size; } ostack; static RConsCanvas *can; static Panel *panels = NULL; static int callgraph = 0; static int menu_x = 0; static int menu_y = 0; static const char *menus[] = { "File", "Edit", "View", "Tools", "Search", "Debug", "Analyze", "Help", NULL }; static const char *menus_File[] = { "New", "Open", "Close", "--", "Sections", "Strings", "Symbols", "Imports", "Info", "Database", "Quit", NULL }; static const char *menus_Edit[] = { "Copy", "Paste", "Clipboard", "Write String", "Write Hex", "Write Value", "Assemble", "Fill", "io.cache", NULL }; static const char *menus_View[] = { "Hexdump", "Disassembly", "Graph", "FcnInfo", "Functions", "Comments", "Entropy", "Colors", NULL }; static const char *menus_Tools[] = { "Assembler", "Calculator", "Shell", "System Shell", NULL }; static const char *menus_Search[] = { "String", "ROP", "Code", "Hexpairs", NULL }; static const char *menus_Debug[] = { "Registers", "DRX", "Breakpoints", "Watchpoints", "Maps", "Continue", "Cont until.", "Backtrace", NULL }; static const char *menus_Analyze[] = { "Function", "Program", "Calls", "References", NULL }; static const char *menus_Help[] = { "Fortune", "Commands", "License", "About", NULL }; static const char **menus_sub[] = { menus_File, menus_Edit, menus_View, menus_Tools, menus_Search, menus_Debug, menus_Analyze, menus_Help, NULL }; // TODO: handle mouse wheel static int curnode = 0; #define G(x,y) r_cons_canvas_gotoxy (can, x, y) #define W(x) r_cons_canvas_write (can, x) #define B(x,y,w,h) r_cons_canvas_box(can, x,y,w,h,NULL) #define B1(x,y,w,h) r_cons_canvas_box(can, x,y,w,h,Color_BLUE) #define B2(x,y,w,h) r_cons_canvas_box(can, x,y,w,h,Color_MAGENTA) #define L(x,y,x2,y2) r_cons_canvas_line(can, x,y,x2,y2,0) #define L1(x,y,x2,y2) r_cons_canvas_line(can, x,y,x2,y2,1) #define L2(x,y,x2,y2) r_cons_canvas_line(can, x,y,x2,y2,2) #define F(x,y,x2,y2,c) r_cons_canvas_fill(can, x,y,x2,y2,c,0) static void Panel_print(RConsCanvas *can, Panel *n, int cur) { char title[128]; int delta_x = 0; int delta_y = 0; if (!can) return; // clear F(n->x, n->y, n->w, n->h, ' '); if (n->type == PANEL_TYPE_FRAME) { if (cur) { //F (n->x,n->y, n->w, n->h, '.'); snprintf (title, sizeof (title)-1, Color_BLUE"[[ %s ]] "Color_RESET, n->text); } else { snprintf (title, sizeof (title)-1, " %s ", n->text); } if (G (n->x+1, n->y+1)) W (title); // delta_x } (void)G (n->x+2+delta_x, n->y+2); //if ( // TODO: only refresh if n->refresh is set // TODO: temporary crop depending on out of screen offsets if (n->cmd && *n->cmd) { char *foo = r_core_cmd_str (_core, n->cmd); char *text = r_str_crop (foo, delta_x, delta_y, n->w, n->h-2); if (text) { W (text); free (text); } else { W (n->text); } free (foo); } else { char *text = r_str_crop (n->text, delta_x, delta_y, n->w+5, n->h); if (text) { W (text); free (text); } else { W (n->text); } } // glitch in menubar // if (G (n->x+1, n->y+1)) // W (title); // TODO: check if node is traced or not and hsow proper color // This info must be stored inside Panel* from RCore* if (cur) { B1 (n->x, n->y, n->w, n->h); } else { B (n->x, n->y, n->w, n->h); } } static void Layout_run(Panel *panels) { int h, w = r_cons_get_size (&h); int i, j; int colpos = w - COLW; can->sx = 0; can->sy = 0; for (i=j=0; panels[i].text; i++) { switch (panels[i].type) { case PANEL_TYPE_FLOAT: panels[i].w = r_str_bounds ( panels[i].text, &panels[i].h); panels[i].h += 4; break; case PANEL_TYPE_FRAME: if (j == 0) { panels[i].x = 0; panels[i].y = 1; if (panels[j+1].text) { panels[i].w = colpos; } else { panels[i].w = w; } panels[i].h = h-1; } else { int ph = ((h-1)/(n_panels-2)); panels[i].x = colpos; panels[i].y = 1 + (ph*(j-1)); panels[i].w = w-colpos; if (panels[i].w<0) panels[i].w = 0; panels[i].h = ph; if (!panels[i+1].text) { panels[i].h = h - panels[i].y; } } j++; } } } static void delcurpanel() { if (curnode>0 && n_panels>3) { int i; for (i=curnode; i<(n_panels-1); i++) { panels[i] = panels[i+1]; } panels[i].text = 0; n_panels--; if (curnode>=n_panels) { curnode = n_panels-1; } } } static void zoom() { if (n_panels>2) { if (curnode <2) { curnode = 2; } Panel ocurnode = panels[curnode]; panels[curnode] = panels[1]; panels[1] = ocurnode; } } static void addPanelFrame (const char *title, const char *cmd, ut64 addr) { int i = n_panels; if (!panels) { panels = calloc (sizeof (Panel), LIMIT); panels[0].text = strdup (""); panels[0].addr = addr; panels[0].type = PANEL_TYPE_FLOAT; i = n_panels = 1; menu_pos = 0; } panels[i].text = strdup (title); panels[i].cmd = r_str_newf (cmd); panels[i].addr = addr; panels[i].type = PANEL_TYPE_FRAME; panels[i+1].text = NULL; n_panels++; } static int bbPanels (RCore *core, Panel **n) { //panels = NULL; addPanelFrame ("Disassembly", "pd 128", 0); addPanelFrame ("Symbols", "isq", 0); addPanelFrame ("Stack", "px 256@r:sp", 0); addPanelFrame ("Registers", "dr=", 0); curnode = 0; Layout_run (panels); return n_panels; } // damn singletons.. there should be only one screen and therefor // only one visual instance of the graph view. refactoring this // into a struct makes the code to reference pointers unnecesarily // we can look for a non-global solution here in the future if // necessary static void r_core_panels_refresh(RCore *core) { char title[1024]; const char *color = Color_BLUE; char str[1024]; int i, j, h, w = r_cons_get_size (&h); r_cons_clear00 (); if (!can) { return; } r_cons_canvas_resize (can, w, h); r_cons_canvas_clear (can); if (panels) { if (menu_y>0) { panels[menu_pos].x = menu_x * 6; } else { panels[menu_pos].x = w; } panels[menu_pos].y = 1; free (panels[menu_pos].text); panels[menu_pos].text = calloc (1, 1024); //r_str_newf ("%d", menu_y); int maxsub = 0; for (i=0; menus_sub[i]; i++) { maxsub = i; } #if 1 if (menu_x >= 0 && menu_x <= maxsub && menus_sub[menu_x]) { for (j = 0; menus_sub[menu_x][j]; j++) { if (menu_y-1 == j) { strcat (panels[menu_pos].text, "> "); } else { strcat (panels[menu_pos].text, " "); } strcat (panels[menu_pos].text, menus_sub[menu_x][j]); strcat (panels[menu_pos].text, " \n"); } } #endif for (i=0; panels[i].text; i++) { if (i != curnode) { Panel_print (can, &panels[i], i==curnode); } } } if (menu_y) { curnode = menu_pos; } // redraw current node to make it appear on top if (curnode >= 0) { Panel_print (can, &panels[curnode], 1); } Panel_print (can, &panels[menu_pos], menu_y); (void)G (-can->sx, -can->sy); title[0] = 0; if (curnode == 0) strcpy (title, "> "); for (i=0; menus[i]; i++) { if (menu_x == i) { snprintf (str, sizeof (title)-1, "%s[%s]"Color_RESET, color, menus[i]); } else { snprintf (str, sizeof (title)-1, "%s %s "Color_RESET, color, menus[i]); } strcat (title, str); } if (curnode==0) { W (Color_BLUE); W (title); W (Color_RESET); } else { W (Color_RESET); W (title); } snprintf (title, sizeof (title)-1, "[0x%08"PFMT64x"]", core->offset); (void)G (-can->sx + w-strlen (title), -can->sy); W (title); r_cons_canvas_print (can); r_cons_flush (); } static void reloadPanels(RCore *core) { //W("HELLO WORLD"); Layout_run (panels); } static int havePanel(const char *s) { int i; if (!panels || !panels[0].text) return 0; // add new panel for testing for (i=1; panels[i].text; i++) { if (!strcmp (panels[i].text , s)) { return 1; } } return 0; } R_API int r_core_visual_panels(RCore *core) { #define OS_INIT() ostack.size = 0; ostack.panels[0] = 0; #define OS_PUSH(x) if (ostack.size0)? ostack.panels[--ostack.size]:0) int okey, key, wheel; int w, h; int asm_comments = 0; int asm_bytes = 0; n_panels = 0; panels = NULL; callgraph = 0; _core = core; OS_INIT(); w = r_cons_get_size (&h); can = r_cons_canvas_new (w, h); can->linemode = 1; can->color = r_config_get_i (core->config, "scr.color"); // disable colors in disasm because canvas doesnt supports ansi text yet r_config_set_i (core->config, "scr.color", 0); //can->color = 0; if (!can) { eprintf ("Cannot create RCons.canvas context\n"); return R_FALSE; } n_panels = bbPanels (core, NULL);//&panels); if (!panels) { r_config_set_i (core->config, "scr.color", can->color); free (can); return R_FALSE; } if (w<140) { COLW = w/3; } reloadPanels (core); asm_comments = r_config_get_i (core->config, "asm.comments"); r_config_set_i (core->config, "asm.comments", 0); asm_bytes = r_config_get_i (core->config, "asm.bytes"); r_config_set_i (core->config, "asm.bytes", 0); repeat: core->cons->event_data = core; core->cons->event_resize = \ (RConsEvent)r_core_panels_refresh; w = r_cons_get_size (&h); Layout_run (panels); r_core_panels_refresh (core); wheel = r_config_get_i (core->config, "scr.wheel"); if (wheel) r_cons_enable_mouse (R_TRUE); // r_core_graph_inputhandle() okey = r_cons_readchar (); key = r_cons_arrow_to_hjkl (okey); if (okey == 27) { key = 'K'; } switch (key) { case 'u': r_core_cmd0 (core, "s-"); break; case 'U': r_core_cmd0 (core, "s+"); break; case 'n': r_core_cmd0 (core, "sn"); break; case 'p': r_core_cmd0 (core, "sp"); break; case '.': if (r_config_get_i (core->config, "cfg.debug")) { r_core_cmd0 (core, "sr pc"); //r_core_seek (core, r_num_math (core->num, "entry0"), 1); } else { r_core_cmd0 (core, "s entry0; px"); } break; case ' ': case '\r': case '\n': if (curnode == 0 && menu_y) { const char *action = menus_sub[menu_x][menu_y-1]; if (strstr (action, "New")) { addPanelFrame ("New files", "o", 0); } else if (strstr (action, "Open")) { char *res = r_cons_input ("open file: "); if (res) { if (*res) r_core_cmdf (core, "o %s", res); free (res); } } else if (strstr (action, "Info")) { addPanelFrame ("Info", "i", 0); } else if (strstr (action, "Database")) { addPanelFrame ("Database", "k ***", 0); } else if (strstr (action, "Registers")) { if (!havePanel ("Registers")) { addPanelFrame ("Registers", "dr=", core->offset); } } else if (strstr (action, "About")) { char *s = r_core_cmd_str (core, "?V"); r_cons_message (s); free (s); } else if (strstr (action, "Hexdump")) { addPanelFrame ("Hexdump", "px 512", core->offset); } else if (strstr (action, "Disassembly")) { addPanelFrame ("Disassembly", "pd 128", core->offset); } else if (strstr (action, "Functions")) { addPanelFrame ("Functions", "afl", core->offset); } else if (strstr (action, "Comments")) { addPanelFrame ("Comments", "CC", core->offset); } else if (strstr (action, "Entropy")) { addPanelFrame ("Entropy", "p=e", core->offset); } else if (strstr (action, "Function")) { r_core_cmdf (core, "af"); } else if (strstr (action, "DRX")) { addPanelFrame ("DRX", "drx", core->offset); } else if (strstr (action, "Program")) { r_core_cmdf (core, "aaa"); } else if (strstr (action, "Calls")) { r_core_cmdf (core, "aac"); } else if (strstr (action, "ROP")) { char *res = r_cons_input ("rop grep: "); if (res) { r_core_cmdf (core, "/R %s", res); free (res); } } else if (strstr (action, "String")) { char *res = r_cons_input ("search string: "); if (res) { r_core_cmdf (core, "/ %s", res); free (res); } } else if (strstr (action, "Hexpairs")) { char *res = r_cons_input ("search hexpairs: "); if (res) { r_core_cmdf (core, "/x %s", res); free (res); } } else if (strstr (action, "Code")) { char *res = r_cons_input ("search code: "); if (res) { r_core_cmdf (core, "/c %s", res); free (res); } } else if (strstr (action, "Copy")) { char *res = r_cons_input ("How many bytes? "); if (res) { r_core_cmdf (core, "y %s", res); free (res); } } else if (strstr (action, "Write String")) { char *res = r_cons_input ("insert string: "); if (res) { r_core_cmdf (core, "w %s", res); free (res); } } else if (strstr (action, "Write Value")) { char *res = r_cons_input ("insert number: "); if (res) { r_core_cmdf (core, "wv %s", res); free (res); } } else if (strstr (action, "Write Hex")) { char *res = r_cons_input ("insert hexpairs: "); if (res) { r_core_cmdf (core, "wx %s", res); free (res); } } else if (strstr (action, "Calculator")) { for (;;) { char *s = r_cons_input ("> "); if (!s || !*s) { free (s); break; } r_core_cmdf (core, "? %s", s); r_cons_flush (); free (s); } } else if (strstr (action, "Assemble")) { r_core_visual_asm (core, core->offset); } else if (strstr (action, "Sections")) { addPanelFrame ("Sections", "iSq", 0); } else if (strstr (action, "Close")) { r_core_cmd0 (core, "o-*"); } else if (strstr (action, "Strings")) { addPanelFrame ("Strings", "izq", 0); } else if (strstr (action, "Maps")) { addPanelFrame ("Maps", "dm", 0); } else if (strstr (action, "Backtrace")) { addPanelFrame ("Backtrace", "dbt", 0); } else if (strstr (action, "Breakpoints")) { addPanelFrame ("Breakpoints", "db", 0); } else if (strstr (action, "Symbols")) { addPanelFrame ("Symbols", "isq", 0); } else if (strstr (action, "Imports")) { addPanelFrame ("Imports", "iiq", 0); } else if (strstr (action, "Paste")) { r_core_cmd0 (core, "yy"); } else if (strstr (action, "Clipboard")) { addPanelFrame ("Clipboard", "yx", 0); } else if (strstr (action, "io.cache")) { r_core_cmd0 (core, "e!io.cache"); } else if (strstr (action, "Fill")) { char *s = r_cons_input ("Fill with: "); r_core_cmdf (core, "wow %s", s); free (s); } else if (strstr (action, "References")) { r_core_cmdf (core, "aar"); } else if (strstr (action, "FcnInfo")) { addPanelFrame ("FcnInfo", "afi", 0); } else if (strstr (action, "Graph")) { RAnalFunction *fun = r_anal_get_fcn_in (core->anal, core->offset, R_ANAL_FCN_TYPE_NULL); if (fun) { int ocolor = r_config_get_i (core->config, "scr.color"); r_core_visual_graph (core, NULL, R_TRUE); r_config_set_i (core->config, "scr.color", ocolor); } else { r_cons_message("Not in a function. Type 'df' to define it here"); } } else if (strstr (action, "System Shell")) { r_cons_set_raw (0); r_sys_cmd ("$SHELL"); } else if (strstr (action, "Shell")) { core->vmode = R_FALSE; r_core_visual_prompt_input (core); core->vmode = R_TRUE; } else if (strstr (action, "License")) { r_cons_message ("Copyright 2006-2015 - pancake - LGPL"); } else if (strstr (action, "Fortune")) { char *s = r_core_cmd_str (core, "fo"); r_cons_message (s); free (s); } else if (strstr (action, "Commands")) { r_core_cmd0 (core, "?;?@?;?$?;???"); r_cons_any_key(NULL); } else if (strstr (action, "Colors")) { r_core_cmd0 (core, "e!scr.color"); } else if (strstr (action, "Quit")) { goto beach; } } else { if (curnode>0) { zoom(); } else { menu_y = 1; } } break; case '?': r_cons_clear00 (); r_cons_printf ("Visual Ascii Art Panels:\n" " ! - run r2048 game\n" " . - seek to PC or entrypoint\n" " : - run r2 command in prompt\n" " _ - start the hud input mode\n" " ? - show this help\n" " x - close current panel\n" " m - open menubar\n" " M - open new custom frame\n" " hl - toggle scr.color\n" " HL - move vertical column split\n" " JK - select prev/next panels (same as TAB)\n" " jk - scroll/select menu\n" " sS - step in / step over\n" " uU - undo / redo seek\n" " np - seek to next or previous scr.nkey\n" " q - quit, back to visual mode\n" ); r_cons_flush (); r_cons_any_key (NULL); break; case 's': if (r_config_get_i (core->config, "cfg.debug")) { r_core_cmd0 (core, "ds;.dr*"); //;sr pc"); } else { r_core_cmd0 (core, "aes;.ar*"); } break; case 'S': if (r_config_get_i (core->config, "cfg.debug")) { r_core_cmd0 (core, "dso;.dr*"); //;sr pc"); } else { r_core_cmd0 (core, "aeso;.ar*"); } break; case ':': core->vmode = R_FALSE; r_core_visual_prompt_input (core); core->vmode = R_TRUE; break; case 'C': can->color = !!!can->color; //WTF //r_config_swap (core->config, "scr.color"); // refresh graph // reloadPanels (core); break; case '!': r_cons_2048 (); break; case 'j': if (curnode==0) { if (panels[curnode].type == PANEL_TYPE_FLOAT) { if (menus_sub[menu_x][menu_y]) menu_y ++; } } else { r_core_cmd0 (core, "s+4"); } break; case 'k': if (curnode==0) { if (panels[curnode].type == PANEL_TYPE_FLOAT) { menu_y --; if (menu_y < 0) menu_y = 0; } } else { r_core_cmd0 (core, "s-4"); } break; case '_': r_core_visual_hud (core); break; case 'x': delcurpanel(); break; case 9: // TAB case 'J': menu_y = 0; menu_x = -1; curnode++; if (!panels[curnode].text) { curnode = 0; menu_x = 0; } break; case 'K': menu_y = 0; menu_x = -1; curnode--; if (curnode<0) { curnode = n_panels - 1; } if (!curnode) { menu_x = 0; } break; case 'M': { char *name = r_cons_input ("Name: "); char *cmd = r_cons_input ("Command: "); if (name && *name && cmd && *cmd) { addPanelFrame (name, cmd, 0); } free (name); free (cmd); } break; case 'm': curnode = 0; if (menu_x<0) { menu_x = 0; r_core_panels_refresh (core); } menu_y = 1; break; case 'H': COLW += 4; break; case 'L': COLW -= 4; if (COLW<0) COLW = 0; break; case 'h': if (curnode == 0) { if (menu_x) { menu_x --; menu_y = menu_y?1:0; r_core_panels_refresh (core); } } break; case 'l': if (curnode == 0) { if (menus[menu_x + 1]) { menu_x ++; menu_y = menu_y?1:0; r_core_panels_refresh (core); } } break; case 'q': case -1: // EOF if (menu_y>0) { menu_y = 0; } else { goto beach; } break; #if 0 case 27: // ESC if (r_cons_readchar () == 91) { if (r_cons_readchar () == 90) { } } break; #endif default: //eprintf ("Key %d\n", key); //sleep (1); break; } goto repeat; beach: free (panels); r_config_set_i (core->config, "scr.color", can->color); free (can); r_config_set_i (core->config, "asm.comments", asm_comments); r_config_set_i (core->config, "asm.bytes", asm_bytes); return R_TRUE; }