/*
Patchdiff2
Portions (C) 2010 - 2011 Nicolas Pouvesle
Portions (C) 2007 - 2009 Tenable Network Security, Inc.
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.hpp"
#include "pgraph.hpp"
#include "sig.hpp"
#include "diff.hpp"
static int find_node(slist_t * sl, ea_t ea)
{
size_t i;
for(i=0; inum; i++)
if (sl->sigs[i]->startEA == ea)
return i;
return -1;
}
/*------------------------------------------------*/
/* function : menu_callback */
/* description: Menu callback */
/*------------------------------------------------*/
static bool idaapi menu_callback(void *ud)
{
int node;
slist_t * sl = (slist_t *)ud;
if (sl && sl->sigs && sl->sigs[0]->nfile == 1)
{
node = viewer_get_curnode(sl->gv);
if (node >= 0)
jumpto(sl->sigs[node]->startEA);
}
return true;
}
/*------------------------------------------------*/
/* function : graph_callback */
/* description: Graph callback */
/*------------------------------------------------*/
static int idaapi graph_callback(void * ud, int code, va_list va)
{
int result = 0;
switch ( code )
{
case grcode_layout_calculated:
{
mutable_graph_t *g = va_arg(va, mutable_graph_t *);
slist_t * sl = (slist_t *)ud;
if (sl->num != g->size())
{
warning("Graph layout is too complex to be displayed.\n");
g->reset();
}
}
break;
case grcode_changed_current:
{
graph_viewer_t *v = va_arg (va, graph_viewer_t *);
int node = va_argi(va, int);
if (node != -1)
{
slist_t * sl = (slist_t *)ud;
sl->dclk = true;
}
}
break;
case grcode_clicked:
{
slist_t * sl = (slist_t *)ud;
sl->dclk = false;
}
break;
case grcode_dblclicked:
{
graph_viewer_t *v = va_arg(va, graph_viewer_t *);
selection_item_t *s = va_arg(va, selection_item_t *);
slist_t * sl = (slist_t *)ud;
if ( s && s->is_node)
{
viewer_center_on(v, s->node);
if (sl->sigs[s->node]->msig != NULL && sl->msl->gv != NULL)
viewer_center_on(sl->msl->gv, find_node(sl->msl, sl->sigs[s->node]->matchedEA));
sl->dclk = false;
}
else if ( sl->dclk || s )
{
int node;
node = viewer_get_curnode(v);
if (sl->sigs[node]->msig != NULL && sl->msl->gv != NULL)
viewer_center_on(sl->msl->gv, find_node(sl->msl, sl->sigs[node]->matchedEA));
sl->dclk = false;
}
}
break;
case grcode_user_refresh:
{
mutable_graph_t *g = va_arg(va, mutable_graph_t *);
slist_t * sl = (slist_t *)ud;
if ( g->empty() )
g->resize(sl->num);
for(size_t i = 0; i< sl->num; i++)
{
fref_t * fref;
if (sl->sigs[i]->srefs)
{
fref = sl->sigs[i]->srefs->list;
while(fref)
{
int pos = find_node(sl, fref->ea);
if (pos != -1)
{
edge_info_t ed;
if (fref->type == 3)
ed.color = 0xff0000;
else if (fref->type == 2)
ed.color = 0x0000ff;
else
ed.color = 0x006400;
g->add_edge(i, pos, &ed);
}
fref = fref->next;
}
}
}
result = 1;
}
break;
case grcode_user_text:
{
mutable_graph_t *g = va_arg(va, mutable_graph_t *);
int node = va_arg(va, int);
const char **text = va_arg(va, const char **);
bgcolor_t *bgcolor = va_arg(va, bgcolor_t *);
slist_t * sl = (slist_t *)ud;
*text = sl->sigs[node]->dl.lines;
if ( bgcolor != NULL )
{
*bgcolor = 0xFFFFFFFF;
if (!sl->unique)
{
if (sl->sigs[node]->mtype == DIFF_UNMATCHED)
*bgcolor = 0xcccccc;
else if (sl->sigs[node]->sig != sl->sigs[node]->msig->sig)
*bgcolor = 0x33cc;
else if (sl->sigs[node]->id_crc)
*bgcolor = 0x8cb4d2;
}
}
result = 1;
qnotused(g);
}
break;
case grcode_destroyed:
{
slist_t * sl = (slist_t *)ud;
sl->gv = NULL;
}
break;
}
return result;
}
template
void create_form_name(char (&dst)[len], slist_t *sl, int num)
{
qsnprintf(dst, len, "IDB%d: %s", num, sl->sigs[0]->name);
}
/*------------------------------------------------*/
/* function : pgraph_create */
/* description: Creates s function graph */
/* returns: true if the form was newly created */
/*------------------------------------------------*/
static bool pgraph_create(slist_t *sl, int num)
{
HWND hwnd = NULL;
char form_name[512];
char node_name[512];
TForm *form;
bool form_is_new = true;
create_form_name(form_name, sl, num);
qsnprintf(node_name, sizeof(node_name), "$ %s", form_name);
form = find_tform(form_name);
netnode id;
bool already_existed = !id.create(node_name);
if (form && already_existed)
{
form_is_new = false;
switchto_tform(form, true);
sl->gv = get_graph_viewer(form);
}
else
{
form = create_tform(form_name, &hwnd);
if (hwnd)
{
sl->gv = create_graph_viewer(form, id, graph_callback, sl, 0);
open_tform(form, FORM_TAB|FORM_MENU|FORM_QWIDGET);
if (sl->gv)
{
viewer_fit_window(sl->gv);
viewer_add_menu_item(sl->gv, "Jump to code", menu_callback, sl, NULL, 0);
}
}
}
return form_is_new;
}
/*------------------------------------------------*/
/* function : pgraph_display */
/* description: Displays function graph */
/*------------------------------------------------*/
void pgraph_display(slist_t * sl1, slist_t * sl2)
{
graph_viewer_t *gv = NULL;
sl1->msl = sl2;
sl2->msl = sl1;
sl1->unique = sl2->unique = false;
bool sl1_new_form = pgraph_create(sl1, 1);
bool sl2_new_form = pgraph_create(sl2, 2);
if ((sl1_new_form || sl2_new_form) &&
sl1->gv && sl2->gv)
{
// Only perform the docking for forms that were just created
char buf[512], buf2[512];
create_form_name(buf, sl1, 1);
create_form_name(buf2, sl2, 2);
set_dock_pos(buf2, buf, DP_RIGHT);
}
}
void pgraph_display_one(slist_t * sl)
{
sl->msl = NULL;
sl->unique = true;
pgraph_create(sl, sl->sigs[0]->nfile);
}