-
Notifications
You must be signed in to change notification settings - Fork 1
/
Main.cpp
282 lines (262 loc) · 8.11 KB
/
Main.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
/*
* Hex-Rays Decompiler project
* Copyright (c) 2007-2008 by Hex-Rays, support@hex-rays.com
* ALL RIGHTS RESERVED.
*
* Sample plugin for Hex-Rays Decompiler.
* It introduces a new command for the user: invert if-statement
* For example, a statement like
*
* if ( cond )
* {
* statements1;
* }
* else
* {
* statements2;
* }
*
* will be displayed as
*
* if ( !cond )
* {
* statements2;
* }
* else
* {
* statements1;
* }
*
* Please note that the plugin can not directly modify the current ctree.
* If the ctree is recreated, the changes will be lost.
* To make them persistent, we need to save information about the inverted
* if statements in the database and automatically reapply them
* for each new build. This approach makes all modifications
* persistent. The user can quit IDA and restart the session:
* his changes will be intact.
*
*/
#include "StdAfx.h"
// Hex-Rays API pointer
hexdsp_t *hexdsp = NULL;
static bool inited = false;
// The node to keep inverted-if information.
static const char nodename[] = "$ hexrays inverted-if";
static netnode node;
// Cached copy of inverted if-statement addresses
static eavec_t inverted_ifs;
#define ACTION_NAME "sample3:invertif"
//--------------------------------------------------------------------------
// The user has selected to invert the if statement. Update ctree
// and refresh the view.
static void do_invert_if(cinsn_t *i)
{
QASSERT(30198, i->op == cit_if);
cif_t &cif = *i->cif;
// create an inverted condition and swap it with the if-condition
cexpr_t *notcond = lnot(new cexpr_t(cif.expr));
notcond->swap(cif.expr);
delete notcond;
// swap if branches
qswap(cif.ielse, cif.ithen);
}
//--------------------------------------------------------------------------
static void add_inverted_if(ea_t ea)
{
eavec_t::iterator p = inverted_ifs.find(ea);
if (p != inverted_ifs.end()) // already present?
inverted_ifs.erase(p); // delete the mark
else
inverted_ifs.push_back(ea); // remember if-statement address
// immediately save data into the database
eavec_t copy = inverted_ifs;
for (int i = 0; i < copy.size(); i++)
copy[i] = ea2node(copy[i]);
node.setblob(copy.begin(), copy.size() * sizeof(ea_t), 0, 'I');
}
//--------------------------------------------------------------------------
// Check if the item under the cursor is 'if' or 'else' keyword
// If yes, return pointer to the corresponding ctree item
static cinsn_t *find_if_statement(vdui_t &vu)
{
// 'if' keyword: straightforward check
if (vu.item.is_citem())
{
cinsn_t *i = vu.item.i;
// we can handle only if-then-else statements, so check that the 'else'
// clause exists
if (i->op == cit_if && i->cif->ielse != NULL)
return i;
}
// check for 'else' line. The else lines do not correspond
// to any ctree item. That's why we have to check for them separately.
// we could extract the corresponding text line but this would be a bad approach
// a line with single 'else' would not give us enough information to locate
// the corresponding 'if'. That's why we use the line tail marks.
// All 'else' line will have the ITP_ELSE mark
if (vu.tail.citype == VDI_TAIL && vu.tail.loc.itp == ITP_ELSE)
{
// for tail marks, we know only the corresponding ea,
// not the pointer to if-statement
// find it by walking the whole ctree
struct ida_local if_finder_t : public ctree_visitor_t
{
ea_t ea;
cinsn_t *found;
if_finder_t(ea_t e) : ctree_visitor_t(CV_FAST | CV_INSNS), ea(e) {}
int idaapi visit_insn(cinsn_t *i)
{
if (i->op == cit_if && i->ea == ea)
{
found = i;
return 1; // stop enumeration
}
return 0;
}
};
if_finder_t iff(vu.tail.loc.ea);
if (iff.apply_to(&vu.cfunc->body, NULL))
return iff.found;
}
return NULL;
}
//--------------------------------------------------------------------------
static void convert_marked_ifs(cfunc_t *cfunc)
{
// we walk the ctree and for each if-statement check if has to be inverted
struct ida_local if_inverter_t : public ctree_visitor_t
{
if_inverter_t(void) : ctree_visitor_t(CV_FAST | CV_INSNS) {}
int idaapi visit_insn(cinsn_t *i)
{
if (i->op == cit_if && inverted_ifs.has(i->ea))
do_invert_if(i);
return 0; // continue enumeration
}
};
if_inverter_t ifi;
ifi.apply_to(&cfunc->body, NULL); // go!
}
//--------------------------------------------------------------------------
// This callback handles various hexrays events.
static int idaapi callback(void *, hexrays_event_t event, va_list va)
{
switch (event)
{
case hxe_populating_popup:
{ // If the current item is an if-statement, then add the menu item
TWidget *widget = va_arg(va, TWidget *);
TPopupMenu *popup = va_arg(va, TPopupMenu *);
vdui_t &vu = *va_arg(va, vdui_t *);
if (find_if_statement(vu) != NULL)
attach_action_to_popup(widget, popup, ACTION_NAME);
}
break;
case hxe_maturity:
if (!inverted_ifs.empty())
{ // If the ctree is ready, invert marked ifs
cfunc_t *cfunc = va_arg(va, cfunc_t *);
ctree_maturity_t new_maturity = va_argi(va, ctree_maturity_t);
if (new_maturity == CMAT_FINAL) // ctree is ready
convert_marked_ifs(cfunc);
}
break;
default:
break;
}
return 0;
}
//-------------------------------------------------------------------------
struct invert_if_ah_t : public action_handler_t
{
virtual int idaapi activate(action_activation_ctx_t *ctx)
{
vdui_t &vu = *get_widget_vdui(ctx->widget);
cinsn_t *i = find_if_statement(vu);
add_inverted_if(i->ea);
// we manually invert this if and recreate text.
// this is faster than rebuilding ctree from scratch.
do_invert_if(i);
vu.refresh_ctext();
return 1;
}
virtual action_state_t idaapi update(action_update_ctx_t *ctx)
{
vdui_t *vu = get_widget_vdui(ctx->widget);
if (vu == NULL)
return AST_DISABLE_FOR_WIDGET;
return find_if_statement(*vu) == NULL ? AST_DISABLE : AST_ENABLE;
}
};
static invert_if_ah_t invert_if_ah;
static const action_desc_t invert_if_action =
ACTION_DESC_LITERAL(
ACTION_NAME,
"Invert if-statement",
&invert_if_ah,
NULL,
NULL,
-1
);
//--------------------------------------------------------------------------
// Initialize the plugin.
int idaapi init(void)
{
if (!init_hexrays_plugin())
return PLUGIN_SKIP; // no decompiler
if (!node.create(nodename)) // create failed -> node existed
{
size_t n;
void *blob = node.getblob(NULL, &n, 0, 'I');
if (blob != NULL)
{
inverted_ifs.clear();
inverted_ifs.inject((ea_t *)blob, n / sizeof(ea_t));
for (int i = 0; i < inverted_ifs.size(); i++)
inverted_ifs[i] = node2ea(inverted_ifs[i]);
}
}
install_hexrays_callback(callback, NULL);
const char *hxver = get_hexrays_version();
msg("Hex-rays version %s has been detected, %s ready to use\n", hxver, PLUGIN.wanted_name);
register_action(invert_if_action);
inited = true;
return PLUGIN_KEEP;
}
//--------------------------------------------------------------------------
void idaapi term(void)
{
if (inited)
{
// clean up
remove_hexrays_callback(callback, NULL);
term_hexrays_plugin();
}
}
//--------------------------------------------------------------------------
bool idaapi run(size_t)
{
// should not be called because of PLUGIN_HIDE
return false;
}
//--------------------------------------------------------------------------
static char comment[] = "Sample3 plugin for Hex-Rays decompiler";
//--------------------------------------------------------------------------
//
// PLUGIN DESCRIPTION BLOCK
//
//--------------------------------------------------------------------------
plugin_t PLUGIN =
{
IDP_INTERFACE_VERSION,
PLUGIN_HIDE, // plugin flags
init, // initialize
term, // terminate. this pointer may be NULL.
run, // invoke plugin
comment, // long comment about the plugin
// it could appear in the status line
// or as a hint
"", // multiline help about the plugin
"Hex-Rays if-inverter", // the preferred short name of the plugin
"Shift-I" // the preferred hotkey to run the plugin
};