/* $Id: gdeasm.c,v 1.5 2011/11/30 06:05:22 khorben Exp $ */ /* Copyright (c) 2011 Pierre Pronchery */ /* Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the authors nor the names of the contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * THIS SOFTWARE IS PROVIDED BY ITS AUTHORS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ /* TODO: * - add a preferences structure * - complete the function list * - add a check box to open in raw mode * - also display strings (calls...) */ #include #include #include #include #include #include /* gdeasm */ /* private */ /* types */ typedef struct _GDeasm { int raw; char * arch; char * format; /* widgets */ GtkTreeStore * func_store; GtkTreeStore * asm_store; } GDeasm; /* prototypes */ static GDeasm * _gdeasm_new(int raw, char const * arch, char const * format, char const * filename); static void _gdeasm_delete(GDeasm * gdeasm); static int _gdeasm_open(GDeasm * gdeasm, char const * filename, int raw); /* callbacks */ static gboolean _gdeasm_on_closex(void); static void _gdeasm_on_comment_edited(GtkCellRendererText * renderer, gchar * arg1, gchar * arg2, gpointer data); static void _gdeasm_on_open(gpointer data); static int _usage(void); /* functions */ /* gdeasm_new */ static GDeasm * _gdeasm_new(int raw, char const * arch, char const * format, char const * filename) { GDeasm * gdeasm; GtkWidget * window; GtkWidget * vbox; GtkWidget * toolbar; GtkToolItem * toolitem; GtkWidget * hpaned; GtkWidget * scrolled; GtkWidget * treeview; GtkCellRenderer * renderer; GtkTreeViewColumn * column; char const * headers1[] = { "Functions" }; char const * headers2[] = { "Offset", "Instruction", "Operand", "Operand", "Operand", "Operand", "Operand", "Comment" }; size_t i; if((gdeasm = malloc(sizeof(*gdeasm))) == NULL) return NULL; gdeasm->raw = raw; gdeasm->arch = (arch != NULL) ? strdup(arch) : NULL; gdeasm->format = (format != NULL) ? strdup(format) : NULL; gdeasm->func_store = gtk_tree_store_new(2, G_TYPE_STRING, G_TYPE_UINT); gdeasm->asm_store = gtk_tree_store_new(8, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING); window = gtk_window_new(GTK_WINDOW_TOPLEVEL); gtk_window_set_default_size(GTK_WINDOW(window), 640, 480); g_signal_connect_swapped(window, "delete-event", G_CALLBACK( _gdeasm_on_closex), NULL); vbox = gtk_vbox_new(FALSE, 0); /* toolbar */ toolbar = gtk_toolbar_new(); toolitem = gtk_tool_button_new_from_stock(GTK_STOCK_OPEN); g_signal_connect_swapped(toolitem, "clicked", G_CALLBACK( _gdeasm_on_open), gdeasm); gtk_toolbar_insert(GTK_TOOLBAR(toolbar), toolitem, -1); gtk_box_pack_start(GTK_BOX(vbox), toolbar, FALSE, TRUE, 0); /* view */ hpaned = gtk_hpaned_new(); /* functions */ scrolled = gtk_scrolled_window_new(NULL, NULL); gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC); treeview = gtk_tree_view_new_with_model(GTK_TREE_MODEL( gdeasm->func_store)); gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(treeview), TRUE); gtk_tree_view_set_reorderable(GTK_TREE_VIEW(treeview), FALSE); for(i = 0; i < sizeof(headers1) / sizeof(*headers1); i++) { column = gtk_tree_view_column_new_with_attributes(headers1[i], gtk_cell_renderer_text_new(), "text", i, NULL); gtk_tree_view_append_column(GTK_TREE_VIEW(treeview), column); } gtk_container_add(GTK_CONTAINER(scrolled), treeview); gtk_paned_add1(GTK_PANED(hpaned), scrolled); /* assembly */ scrolled = gtk_scrolled_window_new(NULL, NULL); gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC); treeview = gtk_tree_view_new_with_model(GTK_TREE_MODEL( gdeasm->asm_store)); gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(treeview), FALSE); gtk_tree_view_set_reorderable(GTK_TREE_VIEW(treeview), FALSE); gtk_tree_view_set_rules_hint(GTK_TREE_VIEW(treeview), TRUE); for(i = 0; i < sizeof(headers2) / sizeof(*headers2); i++) { renderer = gtk_cell_renderer_text_new(); column = gtk_tree_view_column_new_with_attributes(headers2[i], renderer, "text", i, NULL); gtk_tree_view_append_column(GTK_TREE_VIEW(treeview), column); } /* XXX the last column is editable */ g_object_set(renderer, "editable", TRUE, "style", PANGO_STYLE_ITALIC, NULL); g_signal_connect(renderer, "edited", G_CALLBACK( _gdeasm_on_comment_edited), gdeasm); gtk_container_add(GTK_CONTAINER(scrolled), treeview); gtk_paned_add2(GTK_PANED(hpaned), scrolled); gtk_box_pack_start(GTK_BOX(vbox), hpaned, TRUE, TRUE, 0); gtk_container_add(GTK_CONTAINER(window), vbox); gtk_widget_show_all(window); if(filename != NULL) _gdeasm_open(gdeasm, filename, 0); return gdeasm; } /* gdeasm_delete */ static void _gdeasm_delete(GDeasm * gdeasm) { free(gdeasm->arch); free(gdeasm->format); free(gdeasm); } /* gdeasm_open */ static int _open_code(GDeasm * gdeasm, AsmCode * af); static int _open_code_section(GDeasm * gdeasm, AsmCode * code, AsmSection * section); static void _open_instruction(GDeasm * gdeasm, GtkTreeIter * parent, ArchInstructionCall * call); static void _open_parse_dregister(char * buf, size_t size, ArchOperand * ao); static void _open_parse_dregister2(char * buf, size_t size, ArchOperand * ao); static void _open_parse_immediate(char * buf, size_t size, ArchOperand * ao); static int _gdeasm_open(GDeasm * gdeasm, char const * filename, int raw) { int ret = -1; Asm * a; AsmCode * code; if((a = asm_new(gdeasm->arch, gdeasm->format)) == NULL) return -1; if((code = asm_open_deassemble(a, filename, raw)) != NULL) ret = _open_code(gdeasm, code); asm_close(a); asm_delete(a); return ret; } static int _open_code(GDeasm * gdeasm, AsmCode * code) { int ret; AsmSection * sections; size_t sections_cnt; size_t i; asmcode_get_sections(code, §ions, §ions_cnt); for(i = 0; i < sections_cnt; i++) if((ret = _open_code_section(gdeasm, code, §ions[i])) != 0) break; return ret; } static int _open_code_section(GDeasm * gdeasm, AsmCode * code, AsmSection * section) { GtkTreeIter iter; ArchInstructionCall * calls = NULL; size_t calls_cnt = 0; size_t i; gtk_tree_store_append(gdeasm->asm_store, &iter, NULL); gtk_tree_store_set(gdeasm->asm_store, &iter, 0, section->name, -1); if(asmcode_decode_section(code, section, &calls, &calls_cnt) != 0) return -1; for(i = 0; i < calls_cnt; i++) _open_instruction(gdeasm, &iter, &calls[i]); free(calls); return 0; } static void _open_instruction(GDeasm * gdeasm, GtkTreeIter * parent, ArchInstructionCall * call) { GtkTreeIter iter; char buf[32]; size_t i; ArchOperand * ao; char const * name; gtk_tree_store_append(gdeasm->asm_store, &iter, parent); snprintf(buf, sizeof(buf), "%08lx", call->base); gtk_tree_store_set(gdeasm->asm_store, &iter, 0, buf, 1, call->name, -1); for(i = 0; i < call->operands_cnt; i++) { ao = &call->operands[i]; switch(AO_GET_TYPE(ao->definition)) { case AOT_DREGISTER: _open_parse_dregister(buf, sizeof(buf), ao); break; case AOT_DREGISTER2: _open_parse_dregister2(buf, sizeof(buf), ao); break; case AOT_IMMEDIATE: _open_parse_immediate(buf, sizeof(buf), ao); break; case AOT_REGISTER: name = call->operands[i].value._register.name; snprintf(buf, sizeof(buf), "%%%s", name); break; default: buf[0] = '\0'; break; } gtk_tree_store_set(gdeasm->asm_store, &iter, i + 2, buf, -1); } } static void _open_parse_dregister(char * buf, size_t size, ArchOperand * ao) { char const * name; name = ao->value.dregister.name; if(ao->value.dregister.offset == 0) snprintf(buf, size, "[%%%s]", name); else snprintf(buf, size, "[%%%s + $0x%lx]", name, (unsigned long)ao->value.dregister.offset); } static void _open_parse_dregister2(char * buf, size_t size, ArchOperand * ao) { snprintf(buf, size, "[%%%s + %%%s]", ao->value.dregister2.name, ao->value.dregister2.name2); } static void _open_parse_immediate(char * buf, size_t size, ArchOperand * ao) { snprintf(buf, size, "%s$0x%lx", ao->value.immediate.negative ? "-" : "", (unsigned long)ao->value.immediate.value); } /* callbacks */ /* gdeasm_on_closex */ static gboolean _gdeasm_on_closex(void) { gtk_main_quit(); return TRUE; } /* gdeasm_on_comment_edited */ static void _gdeasm_on_comment_edited(GtkCellRendererText * renderer, gchar * arg1, gchar * arg2, gpointer data) { GDeasm * gdeasm = data; GtkTreeModel * model = GTK_TREE_MODEL(gdeasm->asm_store); GtkTreeIter iter; if(gtk_tree_model_get_iter_from_string(model, &iter, arg1) == TRUE) gtk_tree_store_set(gdeasm->asm_store, &iter, 7, arg2, -1); } /* gdeasm_on_open */ static void _gdeasm_on_open(gpointer data) { GDeasm * gdeasm = data; GtkWidget * widget; char * filename = NULL; widget = gtk_file_chooser_dialog_new("Open file...", NULL, GTK_FILE_CHOOSER_ACTION_OPEN, GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT, GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, NULL); if(gtk_dialog_run(GTK_DIALOG(widget)) == GTK_RESPONSE_ACCEPT) filename = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER( widget)); gtk_widget_destroy(widget); if(filename == NULL) return; gtk_tree_store_clear(gdeasm->func_store); gtk_tree_store_clear(gdeasm->asm_store); _gdeasm_open(gdeasm, filename, gdeasm->raw); g_free(filename); } /* usage */ static int _usage(void) { fputs("Usage: gdeasm [-D][-a arch][-f format] filename\n", stderr); return 1; } /* public */ /* functions */ /* main */ int main(int argc, char * argv[]) { int o; GDeasm * gdeasm; int raw = 0; char const * arch = NULL; char const * format = NULL; gtk_init(&argc, &argv); while((o = getopt(argc, argv, "Da:f:")) != -1) switch(o) { case 'D': raw = 1; break; case 'a': arch = optarg; break; case 'f': format = optarg; break; default: return _usage(); } if(optind != argc && optind + 1 != argc) return _usage(); if((gdeasm = _gdeasm_new(raw, arch, format, argv[optind])) == NULL) return 2; gtk_main(); _gdeasm_delete(gdeasm); return 0; }