/* Patchdiff2 Portions (C) 2010 - 2011 Nicolas Pouvesle Portions (C) 2007 - 2009 Tenable Network Security, Inc. Portions (c) 2018, Chris Eagle (Updates for IDA versions >= 6.7) This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License version 2 as published by the Free Software Foundation. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #include "precomp.h" #include "diff.h" #include "display.h" #include "os.h" #include "parser.h" #include "pgraph.h" #include "options.h" #include "system.h" #include "actions.h" #include "plugin.h" static uint32 idaapi sizer_dlist(slist_t *sl) { if (sl) { return sl->num; } return 0; } static uint32 idaapi sizer_match(void *obj) { deng_t *d = (deng_t *)obj; return sizer_dlist(d ? d->mlist : NULL); } static uint32 idaapi sizer_identical(void *obj) { deng_t *d = (deng_t *)obj; return sizer_dlist(d ? d->ilist : NULL); } static uint32 idaapi sizer_unmatch(void *obj) { deng_t *d = (deng_t *)obj; return sizer_dlist(d ? d->ulist : NULL); } static void idaapi close_window(void *obj) { deng_t *d = (deng_t *)obj; d->wnum--; if (!d->wnum) { ipc_close(); } return; } /*------------------------------------------------*/ /* function : ui_access_sig */ /* description: Compensates for the zero index */ /* indicating the header row and performs */ /* bounds checking in debug */ /*------------------------------------------------*/ static sig_t *ui_access_sig(slist_t *sl, uint32 n) { #ifdef _DEBUG if (!sl || n == 0 || n > sl->num) { error("ui attempted to access siglist out-of-bounds: %p %x\n", sl, n - 1); return NULL; } #endif return sl->sigs[n - 1]; } #if IDA_SDK_VERSION <= 695 static void idaapi desc_dlist(slist_t *sl, uint32 n, char *const *arrptr) { int i; /* header */ if (n == 0) { for (i = 0; i < qnumber (header_match); i++) { qsnprintf(arrptr[i], MAXSTR, "%s", header_match[i]); } } else { sig_t *sig = ui_access_sig(sl, n); qsnprintf(arrptr[0], MAXSTR, "%u", sig->mtype); qsnprintf(arrptr[1], MAXSTR, "%s", sig->name.c_str()); qsnprintf(arrptr[2], MAXSTR, "%s", sig->msig->name.c_str()); qsnprintf(arrptr[3], MAXSTR, "%a", sig->startEA); qsnprintf(arrptr[4], MAXSTR, "%a", sig->msig->startEA); qsnprintf(arrptr[5], MAXSTR, "%c", sig->id_crc ? '+' : '-'); qsnprintf(arrptr[6], MAXSTR, "%lx", sig->crc_hash); qsnprintf(arrptr[7], MAXSTR, "%lx", sig->msig->crc_hash); } } /*------------------------------------------------*/ /* function : desc_match */ /* description: Fills matched list */ /*------------------------------------------------*/ static void idaapi desc_match(void *obj, uint32 n, char *const *arrptr) { deng_t *d = (deng_t *)obj; desc_dlist(d ? d->mlist : NULL, n, arrptr); } /*------------------------------------------------*/ /* function : desc_identical */ /* description: Fills identical list */ /*------------------------------------------------*/ static void idaapi desc_identical(void *obj, uint32 n, char *const *arrptr) { deng_t *d = (deng_t *)obj; desc_dlist(d ? d->ilist : NULL, n, arrptr); } /*------------------------------------------------*/ /* function : desc_unmatch */ /* description: Fills unmatched list */ /*------------------------------------------------*/ static void idaapi desc_unmatch(void *obj, uint32 n, char *const *arrptr) { int i; /* header */ if (n == 0) { for (i = 0; i < qnumber (header_unmatch); i++) { qsnprintf(arrptr[i], MAXSTR, "%s", header_unmatch[i]); } } else { sig_t *sig = ui_access_sig(((deng_t *)obj)->ulist, n); qsnprintf(arrptr[0], MAXSTR, "%u", sig->nfile); qsnprintf(arrptr[1], MAXSTR, "%s", sig->name.c_str()); qsnprintf(arrptr[2], MAXSTR, "%a", sig->startEA); qsnprintf(arrptr[3], MAXSTR, "%.8lX", sig->sig); qsnprintf(arrptr[4], MAXSTR, "%.8lX", sig->hash); qsnprintf(arrptr[5], MAXSTR, "%.8lX", sig->crc_hash); } } #endif static void idaapi enter_list(slist_t *sl, uint32 n) { jumpto(ui_access_sig(sl, n)->startEA); os_copy_to_clipboard(NULL); } /*------------------------------------------------*/ /* function : enter_match */ /* description: Jumps to code for element n in */ /* matched list */ /*------------------------------------------------*/ static void idaapi enter_match(void *obj, uint32 n) { enter_list(((deng_t *)obj)->mlist, n); } /*------------------------------------------------*/ /* function : enter_identical */ /* description: Jumps to code for element n in */ /* identical list */ /*------------------------------------------------*/ static void idaapi enter_identical(void *obj, uint32 n) { enter_list(((deng_t *)obj)->ilist, n); } /*------------------------------------------------*/ /* function : enter_unmatch */ /* description: Jumps to code for element n in */ /* unmatched list */ /*------------------------------------------------*/ static void idaapi enter_unmatch(void *obj, uint32 n) { sig_t *sig = ui_access_sig(((deng_t *)obj)->ulist, n); if (sig->nfile == 1) { jumpto(sig->startEA); } else { os_copy_to_clipboard(NULL); } } static uint32 idaapi graph_list(slist_t *sl, uint32 n, options_t *opt) { slist_t *sl1 = NULL; slist_t *sl2 = NULL; msg ("parsing second function...\n"); sl2 = parse_second_fct(ui_access_sig(sl, n)->msig->startEA, sl->file, opt); if (!sl2) { msg("Error: FCT2 parsing failed.\n"); return 0; } msg ("parsing first function...\n"); #if IDA_SDK_VERSION < 700 sl1 = parse_fct(ui_access_sig(sl, n)->startEA, dto.graph.s_showpref); #else // dto went away in 7.0, not clear how to replicate above sl1 = parse_fct(ui_access_sig(sl, n)->startEA, 0); #endif if (!sl1) { msg("Error: FCT1 parsing failed.\n"); sl2->free_sigs(); delete sl2; return 0; } sl1->sigs[0]->nfile = 1; sl2->sigs[0]->nfile = 2; msg ("diffing functions...\n"); generate_diff(NULL, sl1, sl2, NULL, NULL); pgraph_display(sl1, sl2); msg ("done!\n"); return 1; } /*------------------------------------------------*/ /* function : graph_match */ /* description: Draws graph from element n in */ /* matched list */ /*------------------------------------------------*/ static void idaapi graph_match(void *obj, uint32 n) { slist_t *sl = ((deng_t *)obj)->mlist; options_t *opt = ((deng_t *)obj)->opt; graph_list(sl, n, opt); return; } /*------------------------------------------------*/ /* function : graph_identical */ /* description: Draws graph from element n in */ /* identical list */ /*------------------------------------------------*/ static void idaapi graph_identical(void *obj, uint32 n) { slist_t *sl = ((deng_t *)obj)->ilist; options_t *opt = ((deng_t *)obj)->opt; graph_list(sl, n, opt); return; } /*------------------------------------------------*/ /* function : graph_unmatch */ /* description: Draws graph from element n in */ /* unmatched list */ /*------------------------------------------------*/ static void idaapi graph_unmatch(void *obj, uint32 n) { slist_t *sl = NULL; slist_t *tmp = ((deng_t *)obj)->ulist; if (ui_access_sig(tmp, n)->nfile == 2) { msg ("parsing second function...\n"); sl = parse_second_fct(ui_access_sig(tmp, n)->startEA, tmp->file, ((deng_t *)obj)->opt); if (!sl) { msg("Error: FCT2 parsing failed.\n"); return; } sl->sigs[0]->nfile = 2; } else { msg ("parsing first function...\n"); #if IDA_SDK_VERSION < 700 sl = parse_fct(ui_access_sig(tmp, n)->startEA, dto.graph.s_showpref); #else // dto went away in 7.0, not clear how to replicate above sl = parse_fct(ui_access_sig(tmp, n)->startEA, 0); #endif if (!sl) { msg("Error: FCT1 parsing failed.\n"); return; } sl->sigs[0]->nfile = 1; } pgraph_display_one(sl); msg ("done!\n"); return; } static uint32 idaapi res_unmatch(deng_t *d, uint32 n, int type) { slist_t *sl; if (type == 0) { sl = d->ilist; } else { sl = d->mlist; } sig_t *sig = ui_access_sig(sl, n); sig->nfile = 1; sig->msig->nfile = 2; d->ulist->add(sig); d->ulist->add(sig->msig); sig->msig->msig = NULL; sig->msig = NULL; sl->remove(n - 1); // added entries to the unmatched list refresh_chooser(title_unmatch); // removed an entry from one of the following lists if (type == 0) { refresh_chooser(title_identical); } else { refresh_chooser(title_match); } return 1; } /*------------------------------------------------*/ /* function : res_iunmatch */ /* description: Unmatches element n from identical*/ /* list */ /*------------------------------------------------*/ static uint32 idaapi res_iunmatch(void *obj, uint32 n) { return res_unmatch((deng_t *)obj, n, 0); } /*------------------------------------------------*/ /* function : res_munmatch */ /* description: Unmatches element n from matched */ /* list */ /*------------------------------------------------*/ static uint32 idaapi res_munmatch(void *obj, uint32 n) { return res_unmatch((deng_t *)obj, n, 1); } /*------------------------------------------------*/ /* function : propagate_match */ /* description: Propagates new matched result if */ /* option is set in dialog box */ /*------------------------------------------------*/ void propagate_match(deng_t *eng, sig_t *s1, sig_t *s2, int options) { size_t i; if (options) { show_wait_box ("PatchDiff is in progress ..."); slist_t *l1 = new slist_t(eng->ulist->num, eng->ulist->file); slist_t *l2 = new slist_t(eng->ulist->num, eng->ulist->file); for (i = 0; i < eng->ulist->num; i++) { if (!eng->ulist->sigs[i]->msig) { if (eng->ulist->sigs[i]->nfile == 1) { l1->add(eng->ulist->sigs[i]); } else { l2->add(eng->ulist->sigs[i]); } } } generate_diff(NULL, l1, l2, eng->ulist->file, NULL); delete l1; delete l2; hide_wait_box(); } i = 0; while (i < eng->ulist->num) { s1 = eng->ulist->sigs[i]; s2 = s1->msig; if (!s2) { i++; } else { if (s1->nfile == 1) { if (sig_equal(s1, s2, DIFF_EQUAL_SIG_HASH)) { eng->ilist->add(s1); } else { eng->mlist->add(s1); } } eng->ulist->remove(i); } } } /*------------------------------------------------*/ /* function : res_match */ /* description: Matches 2 elements from unmatched */ /* list */ /*------------------------------------------------*/ static uint32 idaapi res_match(void *obj, uint32 n) { deng_t *eng = (deng_t *)obj; sig_t *s1; sig_t *s2; int option; ea_t ea = BADADDR; size_t i; const char format[] = "STARTITEM 0\n" "Set Match\n" "\n\n" "Options :\n" ">\n\n" ; option = 1; if (AskUsingForm_c(format, &ea, &option)) { s1 = ui_access_sig(eng->ulist, n); for (i = 0; i < eng->ulist->num; i++) { s2 = eng->ulist->sigs[i]; if (s2->startEA != ea || (s2->nfile == s1->nfile)) { continue; } s1->set_matched_sig(s2, DIFF_MANUAL); propagate_match(eng, s1, s2, option); // adding a match refresh_chooser(title_match); // removing a pair of unmatched functions refresh_chooser(title_unmatch); return 1; } warning("Address '%a' is not valid.", ea); return 0; } return 1; } /*------------------------------------------------*/ /* function : res_mtoi */ /* description: Switches element n from matched */ /* to identical list */ /*------------------------------------------------*/ static uint32 idaapi res_mtoi(void *obj, uint32 n) { deng_t *d = (deng_t *)obj; sig_t *sig = ui_access_sig(d->mlist, n); sig->mtype = sig->msig->mtype = DIFF_MANUAL; d->ilist->add(sig); d->mlist->remove(n - 1); // adding to identical refresh_chooser(title_identical); // removing from matched refresh_chooser(title_match); return 1; } /*------------------------------------------------*/ /* function : res_itom */ /* description: Switches element n from identical */ /* to matched list */ /*------------------------------------------------*/ static uint32 idaapi res_itom(void *obj, uint32 n) { deng_t *d = (deng_t *)obj; sig_t *sig = ui_access_sig(d->ilist, n); sig->mtype = sig->msig->mtype = DIFF_MANUAL; d->mlist->add(sig); d->ilist->remove(n - 1); // removing from identical refresh_chooser(title_identical); // adding to matched refresh_chooser(title_match); return 1; } /*------------------------------------------------*/ /* function : res_flagged */ /* description: Sets element as flagged/unflagged */ /*------------------------------------------------*/ static uint32 idaapi res_flagged(void *obj, uint32 n) { sig_t *sig = ui_access_sig(((deng_t *)obj)->mlist, n); sig->flag = !sig->flag; // flagging only happens on the matched list refresh_chooser(title_match); return 1; } static void transfer_sym(sig_t *sig) { sig_t *rhs = sig->msig; sig->set_name(rhs->name); set_name(sig->startEA, rhs->name.c_str(), SN_NOCHECK | SN_NON_AUTO); } static uint32 idaapi transfer_sym_match(void *obj, uint32 n) { sig_t *sig = ui_access_sig(((deng_t *)obj)->mlist, n); transfer_sym(sig); return 1; } static uint32 idaapi transfer_sym_identical(void *obj, uint32 n) { sig_t *sig = ui_access_sig(((deng_t *)obj)->ilist, n); transfer_sym(sig); return 1; } #if IDA_SDK_VERSION >= 670 //------------------------------------------------------------------------- int idaapi munmatch_action_handler_t::activate(action_activation_ctx_t *ctx) { uint32 n = ctx->chooser_selection.size(); if (n == 1) { n = ctx->chooser_selection[0]; #if IDA_SDK_VERSION < 700 return res_munmatch(plugin->d_engine, n); #else return res_munmatch(plugin->d_engine, n + 1); //hack because pre-7.0 choosers index from 1 #endif } return 0; } action_state_t idaapi munmatch_action_handler_t::update(action_update_ctx_t *ctx) { bool ok = ctx->form_type == BWN_CHOOSER; if (ok) { //it's a chooser, now make sure it's the correct form #if IDA_SDK_VERSION < 700 char name[MAXSTR]; ok = get_tform_title(ctx->form, name, sizeof(name)) && strneq(name, title_match, qstrlen(title_match)); #else qstring title; ok = get_widget_title(&title, ctx->widget) && title == title_match; #endif } return ok ? AST_ENABLE_FOR_FORM : AST_DISABLE_FOR_FORM; } //------------------------------------------------------------------------- int idaapi identical_action_handler_t::activate(action_activation_ctx_t *ctx) { uint32 n = ctx->chooser_selection.size(); if (n == 1) { n = ctx->chooser_selection[0]; #if IDA_SDK_VERSION < 700 return res_mtoi(plugin->d_engine, n); #else return res_mtoi(plugin->d_engine, n + 1); //hack because pre-7.0 choosers index from 1 #endif } return 0; } action_state_t idaapi identical_action_handler_t::update(action_update_ctx_t *ctx) { bool ok = ctx->form_type == BWN_CHOOSER; if (ok) { //it's a chooser, now make sure it's the correct form #if IDA_SDK_VERSION < 700 char name[MAXSTR]; ok = get_tform_title(ctx->form, name, sizeof(name)) && strneq(name, title_match, qstrlen(title_match)); #else qstring title; ok = get_widget_title(&title, ctx->widget) && title == title_match; #endif } return ok ? AST_ENABLE_FOR_FORM : AST_DISABLE_FOR_FORM; } //------------------------------------------------------------------------- int idaapi flagunflag_action_handler_t::activate(action_activation_ctx_t *ctx) { uint32 n = ctx->chooser_selection.size(); if (n == 1) { n = ctx->chooser_selection[0]; #if IDA_SDK_VERSION < 700 return res_flagged(plugin->d_engine, n); #else return res_flagged(plugin->d_engine, n + 1); //hack because pre-7.0 choosers index from 1 #endif } return 0; } action_state_t idaapi flagunflag_action_handler_t::update(action_update_ctx_t *ctx) { bool ok = ctx->form_type == BWN_CHOOSER; if (ok) { //it's a chooser, now make sure it's the correct form #if IDA_SDK_VERSION < 700 char name[MAXSTR]; ok = get_tform_title(ctx->form, name, sizeof(name)) && strneq(name, title_match, qstrlen(title_match)); #else qstring title; ok = get_widget_title(&title, ctx->widget) && title == title_match; #endif } return ok ? AST_ENABLE_FOR_FORM : AST_DISABLE_FOR_FORM; } //------------------------------------------------------------------------- int idaapi msym_action_handler_t::activate(action_activation_ctx_t *ctx) { uint32 n = ctx->chooser_selection.size(); if (n == 1) { n = ctx->chooser_selection[0]; #if IDA_SDK_VERSION < 700 return transfer_sym_match(plugin->d_engine, n); #else return transfer_sym_match(plugin->d_engine, n + 1); //hack because pre-7.0 choosers index from 1 #endif } return 0; } action_state_t idaapi msym_action_handler_t::update(action_update_ctx_t *ctx) { bool ok = ctx->form_type == BWN_CHOOSER; if (ok) { //it's a chooser, now make sure it's the correct form #if IDA_SDK_VERSION < 700 char name[MAXSTR]; ok = get_tform_title(ctx->form, name, sizeof(name)) && strneq(name, title_match, qstrlen(title_match)); #else qstring title; ok = get_widget_title(&title, ctx->widget) && title == title_match; #endif } return ok ? AST_ENABLE_FOR_FORM : AST_DISABLE_FOR_FORM; } //------------------------------------------------------------------------- int idaapi iunmatch_action_handler_t::activate(action_activation_ctx_t *ctx) { uint32 n = ctx->chooser_selection.size(); if (n == 1) { n = ctx->chooser_selection[0]; #if IDA_SDK_VERSION < 700 return res_iunmatch(plugin->d_engine, n); #else return res_iunmatch(plugin->d_engine, n + 1); //hack because pre-7.0 choosers index from 1 #endif } return 0; } action_state_t idaapi iunmatch_action_handler_t::update(action_update_ctx_t *ctx) { bool ok = ctx->form_type == BWN_CHOOSER; if (ok) { #if IDA_SDK_VERSION < 700 char name[MAXSTR]; ok = get_tform_title(ctx->form, name, sizeof(name)) && strneq(name, title_identical, qstrlen(title_match)); #else qstring title; ok = get_widget_title(&title, ctx->widget) && title == title_identical; #endif } return ok ? AST_ENABLE_FOR_FORM : AST_DISABLE_FOR_FORM; } //------------------------------------------------------------------------- int idaapi itom_action_handler_t::activate(action_activation_ctx_t *ctx) { uint32 n = ctx->chooser_selection.size(); if (n == 1) { n = ctx->chooser_selection[0]; #if IDA_SDK_VERSION < 700 return res_itom(plugin->d_engine, n); #else return res_itom(plugin->d_engine, n + 1); //hack because pre-7.0 choosers index from 1 #endif } return 0; } action_state_t idaapi itom_action_handler_t::update(action_update_ctx_t *ctx) { bool ok = ctx->form_type == BWN_CHOOSER; if (ok) { //it's a chooser, now make sure it's the correct form #if IDA_SDK_VERSION < 700 char name[MAXSTR]; ok = get_tform_title(ctx->form, name, sizeof(name)) && strneq(name, title_identical, qstrlen(title_match)); #else qstring title; ok = get_widget_title(&title, ctx->widget) && title == title_identical; #endif } return ok ? AST_ENABLE_FOR_FORM : AST_DISABLE_FOR_FORM; } //------------------------------------------------------------------------- int idaapi isym_action_handler_t::activate(action_activation_ctx_t *ctx) { uint32 n = ctx->chooser_selection.size(); if (n == 1) { n = ctx->chooser_selection[0]; #if IDA_SDK_VERSION < 700 return transfer_sym_identical(plugin->d_engine, n); #else return transfer_sym_identical(plugin->d_engine, n + 1); //hack because pre-7.0 choosers index from 1 #endif } return 0; } action_state_t idaapi isym_action_handler_t::update(action_update_ctx_t *ctx) { bool ok = ctx->form_type == BWN_CHOOSER; if (ok) { //it's a chooser, now make sure it's the correct form #if IDA_SDK_VERSION < 700 char name[MAXSTR]; ok = get_tform_title(ctx->form, name, sizeof(name)) && strneq(name, title_identical, qstrlen(title_match)); #else qstring title; ok = get_widget_title(&title, ctx->widget) && title == title_identical; #endif } return ok ? AST_ENABLE_FOR_FORM : AST_DISABLE_FOR_FORM; } //------------------------------------------------------------------------- int idaapi match_action_handler_t::activate(action_activation_ctx_t *ctx) { uint32 n = ctx->chooser_selection.size(); if (n == 1) { n = ctx->chooser_selection[0]; #if IDA_SDK_VERSION < 700 return res_match(plugin->d_engine, n); #else return res_match(plugin->d_engine, n + 1); //hack because pre-7.0 choosers index from 1 #endif } return 0; } action_state_t idaapi match_action_handler_t::update(action_update_ctx_t *ctx) { bool ok = ctx->form_type == BWN_CHOOSER; if (ok) { //it's a chooser, now make sure it's the correct form #if IDA_SDK_VERSION < 700 char name[MAXSTR]; ok = get_tform_title(ctx->form, name, sizeof(name)) && strneq(name, title_unmatch, qstrlen(title_match)); #else qstring title; ok = get_widget_title(&title, ctx->widget) && title == title_unmatch; #endif } return ok ? AST_ENABLE_FOR_FORM : AST_DISABLE_FOR_FORM; } #endif #if IDA_SDK_VERSION >= 700 static void idaapi desc_dlist(slist_t *sl, uint32 n, qstrvec_t *cols_) { qstrvec_t &cols = *cols_; sig_t *sig = ui_access_sig(sl, n + 1); //hack because pre-7.0 choosers index from 1 cols[0].sprnt("%u", sig->mtype); cols[1].sprnt("%s", sig->name.c_str()); cols[2].sprnt("%s", sig->msig->name.c_str()); cols[3].sprnt("%a", sig->startEA); cols[4].sprnt("%a", sig->msig->startEA); cols[5].sprnt("%c", sig->id_crc ? '+' : '-'); cols[6].sprnt("%lx", sig->crc_hash); cols[7].sprnt("%lx", sig->msig->crc_hash); } //------------------------------------------------------------------------- struct matched_chooser_t : public chooser_t { private: deng_t *eng; public: // this object must be allocated using `new` matched_chooser_t(deng_t *eng_); // function that is used to decide whether a new chooser should be opened // or we can use the existing one. // The contents of the window are completely determined by its title virtual const void *get_obj_id(size_t *len) const { *len = strlen(title); return title; } // function that returns number of lines in the list virtual size_t idaapi get_count() const { return sizer_dlist(eng ? eng->mlist : NULL); } // function that generates the list line virtual void idaapi get_row(qstrvec_t *cols, int *icon_, chooser_item_attrs_t *attrs, size_t n) const; // function that is called when the user hits Enter virtual cbret_t idaapi enter(size_t n) { enter_list(eng->mlist, n + 1); //hack because pre-7.0 choosers index from 1 return cbret_t(); // nothing changed } virtual cbret_t idaapi edit(size_t n) { graph_match(eng, n + 1); //hack because pre-7.0 choosers index from 1 return cbret_t(); // nothing changed } virtual void idaapi closed(); }; inline matched_chooser_t::matched_chooser_t(deng_t *eng_) : chooser_t(CH_ATTRS | CH_CAN_EDIT, qnumber(widths_match), widths_match, header_match, title_match) { eng = eng_; popup_names[POPUP_EDIT] = "Display Graphs"; } void idaapi matched_chooser_t::get_row(qstrvec_t *cols_, int *, chooser_item_attrs_t *, size_t n) const { desc_dlist(eng ? eng->mlist : NULL, n, cols_); } void idaapi matched_chooser_t::closed() { close_window(eng); } static matched_chooser_t *matched_chooser; //------------------------------------------------------------------------- struct identical_chooser_t : public chooser_t { private: deng_t *eng; public: // this object must be allocated using `new` identical_chooser_t(deng_t *eng_); // function that is used to decide whether a new chooser should be opened // or we can use the existing one. // The contents of the window are completely determined by its title virtual const void *get_obj_id(size_t *len) const { *len = strlen(title); return title; } // function that returns number of lines in the list virtual size_t idaapi get_count() const { return sizer_dlist(eng ? eng->ilist : NULL); } // function that generates the list line virtual void idaapi get_row(qstrvec_t *cols, int *icon_, chooser_item_attrs_t *attrs, size_t n) const; // function that is called when the user hits Enter virtual cbret_t idaapi enter(size_t n) { enter_list(eng->ilist, n + 1); //hack because pre-7.0 choosers index from 1 return cbret_t(); // nothing changed } virtual cbret_t idaapi edit(size_t n) { graph_identical(eng, n + 1); //hack because pre-7.0 choosers index from 1 return cbret_t(); // nothing changed } virtual void idaapi closed() { close_window(eng); } }; inline identical_chooser_t::identical_chooser_t(deng_t *eng_) : chooser_t(CH_ATTRS | CH_CAN_EDIT, qnumber(widths_match), widths_match, header_match, title_identical) { eng = eng_; popup_names[POPUP_EDIT] = "Display Graphs"; } void idaapi identical_chooser_t::get_row(qstrvec_t *cols_, int *, chooser_item_attrs_t *, size_t n) const { desc_dlist(eng ? eng->ilist : NULL, n, cols_); } static identical_chooser_t *identical_chooser; //------------------------------------------------------------------------- struct unmatched_chooser_t : public chooser_t { private: deng_t *eng; public: // this object must be allocated using `new` unmatched_chooser_t(deng_t *eng_); // function that is used to decide whether a new chooser should be opened // or we can use the existing one. // The contents of the window are completely determined by its title virtual const void *get_obj_id(size_t *len) const { *len = strlen(title); return title; } // function that returns number of lines in the list virtual size_t idaapi get_count() const { return sizer_dlist(eng ? eng->ulist : NULL); } // function that generates the list line virtual void idaapi get_row(qstrvec_t *cols, int *icon_, chooser_item_attrs_t *attrs, size_t n) const; // function that is called when the user hits Enter virtual cbret_t idaapi enter(size_t n) { sig_t *sig = ui_access_sig(eng->ulist, n + 1); //hack because pre-7.0 choosers index from 1 if (sig->nfile == 1) { jumpto(sig->startEA); } else { os_copy_to_clipboard(NULL); } return cbret_t(); // nothing changed } virtual cbret_t idaapi edit(size_t n) { graph_unmatch(eng, n + 1); //hack because pre-7.0 choosers index from 1 return cbret_t(); // nothing changed } virtual void idaapi closed() { close_window(eng); } }; inline unmatched_chooser_t::unmatched_chooser_t(deng_t *eng_) : chooser_t(CH_ATTRS | CH_CAN_EDIT, qnumber(widths_unmatch), widths_unmatch, header_unmatch, title_unmatch) { eng = eng_; popup_names[POPUP_EDIT] = "Display Graph"; } void idaapi unmatched_chooser_t::get_row(qstrvec_t *cols_, int *, chooser_item_attrs_t *, size_t n) const { qstrvec_t &cols = *cols_; sig_t *sig = ui_access_sig(eng->ulist, n + 1); //hack because pre-7.0 choosers index from 1 cols[0].sprnt("%u", sig->nfile); cols[1].sprnt("%s", sig->name.c_str()); cols[2].sprnt("%a", sig->startEA); cols[3].sprnt("%.8lX", sig->sig); cols[4].sprnt("%.8lX", sig->hash); cols[5].sprnt("%.8lX", sig->crc_hash); } static unmatched_chooser_t *unmatched_chooser; #endif /*------------------------------------------------*/ /* function : display_matched */ /* description: Displays matched list */ /*------------------------------------------------*/ static void display_matched(deng_t *eng) { #if IDA_SDK_VERSION <= 695 choose2(CH_ATTRS, -1, -1, -1, -1, // position is determined by Windows eng, // pass the created function list to the window qnumber(header_match),// number of columns widths_match, // widths of columns sizer_match, // function that returns number of lines desc_match, // function that generates a line title_match, // window title -1, // use the default icon for the window 1, // position the cursor on the first line NULL, // "kill" callback NULL, // "new" callback NULL, // "update" callback graph_match, // "edit" callback enter_match, // function to call when the user pressed Enter close_window, // function to call when the window is closed popup_match, // use default popup menu items NULL); #else if (matched_chooser == NULL) { matched_chooser = new matched_chooser_t(eng); matched_chooser->choose(chooser_t::NO_SELECTION); } #endif eng->wnum++; #if IDA_SDK_VERSION <= 660 add_chooser_command(title_match, "Unmatch", res_munmatch, 0, -1, CHOOSER_POPUP_MENU | CHOOSER_MENU_EDIT); add_chooser_command(title_match, "Set as identical", res_mtoi, 0, -1, CHOOSER_POPUP_MENU | CHOOSER_MENU_EDIT); add_chooser_command(title_match, "Flag/unflag", res_flagged, 0, -1, CHOOSER_POPUP_MENU | CHOOSER_MENU_EDIT); add_chooser_command(title_match, "Import Symbol", transfer_sym_match, 0, -1, CHOOSER_POPUP_MENU | CHOOSER_MENU_EDIT); #else auto_wait(); #if IDA_SDK_VERSION <= 695 TForm *form = find_tform(title_match); #else TWidget *form = find_widget(title_match); #endif if (form) { attach_action_to_popup(form, NULL, MUNMATCH_NAME); attach_action_to_popup(form, NULL, IDENTICAL_NAME); attach_action_to_popup(form, NULL, FLAGUNFLAG_NAME); attach_action_to_popup(form, NULL, MSYM_NAME); } else { msg("Failed to lookup form %s\n", title_match); } #endif } /*------------------------------------------------*/ /* function : display_identical */ /* description: Displays identical list */ /*------------------------------------------------*/ static void display_identical(deng_t *eng) { #if IDA_SDK_VERSION <= 695 choose2(0, -1, -1, -1, -1, // position is determined by Windows eng, // pass the created function list to the window qnumber(header_match),// number of columns widths_match, // widths of columns sizer_identical, // function that returns number of lines desc_identical, // function that generates a line title_identical, // window title -1, // use the default icon for the window 1, // position the cursor on the first line NULL, // "kill" callback NULL, // "new" callback NULL, // "update" callback graph_identical, // "edit" callback enter_identical, // function to call when the user pressed Enter close_window, // function to call when the window is closed popup_match, // use default popup menu items NULL); #else if (identical_chooser == NULL) { identical_chooser = new identical_chooser_t(eng); identical_chooser->choose(chooser_t::NO_SELECTION); } #endif eng->wnum++; #if IDA_SDK_VERSION <= 660 add_chooser_command(title_identical, "Unmatch", res_iunmatch, 0, -1, CHOOSER_POPUP_MENU | CHOOSER_MENU_EDIT); add_chooser_command(title_identical, "Set as matched", res_itom, 0, -1, CHOOSER_POPUP_MENU | CHOOSER_MENU_EDIT); add_chooser_command(title_identical, "Import Symbol", transfer_sym_identical, 0, -1, CHOOSER_POPUP_MENU | CHOOSER_MENU_EDIT); #else #if IDA_SDK_VERSION <= 695 TForm *form = find_tform(title_identical); #else auto_wait(); TWidget *form = find_widget(title_identical); #endif if (form) { attach_action_to_popup(form, NULL, IUNMATCH_NAME); attach_action_to_popup(form, NULL, ITOM_NAME); attach_action_to_popup(form, NULL, ISYM_NAME); } else { msg("Failed to lookup form %s\n", title_identical); } #endif } /*------------------------------------------------*/ /* function : display_unmatched */ /* description: Displays unmatched list */ /*------------------------------------------------*/ static void display_unmatched(deng_t *eng) { #if IDA_SDK_VERSION <= 695 choose2(0, -1, -1, -1, -1, // position is determined by Windows eng, // pass the created function list to the window qnumber(header_unmatch),// number of columns widths_unmatch, // widths of columns sizer_unmatch, // function that returns number of lines desc_unmatch, // function that generates a line title_unmatch, // window title -1, // use the default icon for the window 1, // position the cursor on the first line NULL, // "kill" callback NULL, // "new" callback NULL, // "update" callback graph_unmatch, // "edit" callback enter_unmatch, // function to call when the user pressed Enter close_window, // function to call when the window is closed popup_unmatch, // use default popup menu items NULL); // use the same icon for all lines #else if (unmatched_chooser == NULL) { unmatched_chooser = new unmatched_chooser_t(eng); unmatched_chooser->choose(chooser_t::NO_SELECTION); } #endif eng->wnum++; #if IDA_SDK_VERSION <= 660 add_chooser_command(title_unmatch, "Set match", res_match, 0, -1, CHOOSER_POPUP_MENU | CHOOSER_MENU_EDIT); #else auto_wait(); #if IDA_SDK_VERSION <= 695 TForm *form = find_tform(title_unmatch); #else TWidget *form = find_widget(title_unmatch); #endif if (form) { attach_action_to_popup(form, NULL, MATCH_NAME); } else { msg("Failed to lookup form %s\n", title_unmatch); } #endif } /*------------------------------------------------*/ /* function : ui_callback */ /* description: Catchs lists to change bg color */ /*------------------------------------------------*/ #if IDA_SDK_VERSION < 700 int idaapi ui_callback(void *data, int event_id, va_list va) { #else ssize_t idaapi ui_callback(void *data, int event_id, va_list va) { #endif if (event_id == ui_get_chooser_item_attrs) { void *co = va_arg(va, void *); uint32 n = va_arg(va, uint32); #if IDA_SDK_VERSION >= 700 n += 1; //hack because pre-7.0 choosers index from 1 #endif chooser_item_attrs_t *attrs = va_arg(va, chooser_item_attrs_t *); if (attrs != NULL) { deng_t *d = (deng_t *)co; if (d && d->magic == 0x0BADF00D && n > 0) { if (ui_access_sig(d->mlist, n)->flag == 1) { attrs->color = 0x908070; } } } } return 0; } /*------------------------------------------------*/ /* function : display_results */ /* description: Displays diff results */ /*------------------------------------------------*/ void display_results(pd_plugmod_t *plugin) { #if IDA_SDK_VERSION >= 670 register_action(plugin->munmatch_action); register_action(plugin->identical_action); register_action(plugin->flagunflag_action); register_action(plugin->msym_action); register_action(plugin->iunmatch_action); register_action(plugin->itom_action); register_action(plugin->isym_action); register_action(plugin->match_action); #endif hook_to_notification_point(HT_UI, ui_callback, NULL); display_matched(plugin->d_engine); display_unmatched(plugin->d_engine); display_identical(plugin->d_engine); }