yet more stuff for storm
[ia32rtools.git] / plugin / saveasm.cpp
CommitLineData
d8891fcc 1#define NO_OBSOLETE_FUNCS
2#include <ida.hpp>
3#include <idp.hpp>
4#include <bytes.hpp>
5#include <loader.hpp>
6#include <kernwin.hpp>
7
8#include <name.hpp>
9#include <frame.hpp>
10#include <struct.hpp>
11#include <auto.hpp>
15c7b2a4 12#include <intel.hpp>
d8891fcc 13
14#define IS_START(w, y) !strncmp(w, y, strlen(y))
15#define ARRAY_SIZE(x) (sizeof(x) / sizeof(x[0]))
16
15c7b2a4 17// non-local branch targets
18static ea_t *nonlocal_bt;
19static int nonlocal_bt_alloc;
20static int nonlocal_bt_cnt;
21
d8891fcc 22//--------------------------------------------------------------------------
23static int idaapi init(void)
24{
25 return PLUGIN_OK;
26}
27
28//--------------------------------------------------------------------------
29static void idaapi term(void)
30{
15c7b2a4 31 if (nonlocal_bt != NULL) {
32 free(nonlocal_bt);
33 nonlocal_bt = NULL;
34 }
35 nonlocal_bt_alloc = 0;
d8891fcc 36}
37
38//--------------------------------------------------------------------------
39
40static const char *reserved_names[] = {
41 "name",
b587e6ae 42 "type",
d8891fcc 43 "offset",
b587e6ae 44 "aam",
1402b79d 45 "text",
d8891fcc 46};
47
48static int is_name_reserved(const char *name)
49{
50 int i;
51 for (i = 0; i < ARRAY_SIZE(reserved_names); i++)
52 if (strcasecmp(name, reserved_names[i]) == 0)
53 return 1;
54
55 return 0;
56}
57
15c7b2a4 58static int nonlocal_bt_cmp(const void *p1, const void *p2)
59{
60 const ea_t *e1 = (const ea_t *)p1, *e2 = (const ea_t *)p2;
61 return *e1 - *e2;
62}
63
64static void nonlocal_add(ea_t ea)
65{
66 if (nonlocal_bt_cnt >= nonlocal_bt_alloc) {
67 nonlocal_bt_alloc += nonlocal_bt_alloc * 2 + 64;
68 nonlocal_bt = (ea_t *)realloc(nonlocal_bt,
69 nonlocal_bt_alloc * sizeof(nonlocal_bt[0]));
70 if (nonlocal_bt == NULL) {
71 msg("OOM\n");
72 return;
73 }
74 }
75 nonlocal_bt[nonlocal_bt_cnt++] = ea;
76}
77
b587e6ae 78// is instruction a (un)conditional jump (not call)?
79static int is_insn_jmp(uint16 itype)
80{
81 return itype == NN_jmp || (NN_ja <= itype && itype <= NN_jz);
82}
83
d53d4cc7 84static void do_def_line(char *buf, size_t buf_size, const char *line,
85 ea_t ea)
d8891fcc 86{
d53d4cc7 87 ea_t *ea_ret;
88 char *p;
d8891fcc 89 int len;
90
91 tag_remove(line, buf, buf_size); // remove color codes
92 len = strlen(buf);
93 if (len < 9) {
94 buf[0] = 0;
95 return;
96 }
97 memmove(buf, buf + 9, len - 9 + 1); // rm address
15c7b2a4 98
d53d4cc7 99 p = buf;
100 while (*p && *p != ' ' && *p != ':')
101 p++;
102 if (*p == ':') {
103 ea_ret = (ea_t *)bsearch(&ea, nonlocal_bt, nonlocal_bt_cnt,
104 sizeof(nonlocal_bt[0]), nonlocal_bt_cmp);
105 if (ea_ret != 0) {
106 if (p[1] != ' ')
107 msg("no trailing blank in '%s'\n", buf);
108 else
109 p[1] = ':';
15c7b2a4 110 }
111 }
d8891fcc 112}
113
114static void idaapi run(int /*arg*/)
115{
15c7b2a4 116 // isEnabled(ea) // address belongs to disassembly
d8891fcc 117 // ea_t ea = get_screen_ea();
15c7b2a4 118 // foo = DecodeInstruction(ScreenEA());
d8891fcc 119 FILE *fout = NULL;
120 int fout_line = 0;
121 char buf[MAXSTR];
1caf86bb 122 char buf2[MAXSTR];
b587e6ae 123 const char *name;
d8891fcc 124 struc_t *frame;
125 func_t *func;
15c7b2a4 126 ea_t ui_ea_block = 0, ea_size;
127 ea_t tmp_ea, target_ea;
d8891fcc 128 ea_t ea;
b587e6ae 129 flags_t ea_flags;
1caf86bb 130 uval_t idx;
d8891fcc 131 int i, o, m, n;
132 int ret;
133 char *p;
134
15c7b2a4 135 nonlocal_bt_cnt = 0;
136
1caf86bb 137 // get rid of structs, masm doesn't understand them
138 idx = get_first_struc_idx();
139 while (idx != BADNODE) {
140 tid_t tid = get_struc_by_idx(idx);
141 struc_t *struc = get_struc(tid);
142 get_struc_name(tid, buf, sizeof(buf));
143 msg("removing struct '%s'\n", buf);
144 //del_struc_members(struc, 0, get_max_offset(struc));
145 del_struc(struc);
146
147 idx = get_first_struc_idx();
148 }
149
15c7b2a4 150 // 1st pass: walk through all funcs
151 func = get_func(inf.minEA);
d8891fcc 152 while (func != NULL)
153 {
15c7b2a4 154 func_tail_iterator_t fti(func);
155 if (!fti.main()) {
156 msg("%x: func_tail_iterator_t main failed\n", ea);
157 return;
158 }
159 const area_t &f_area = fti.chunk();
160 ea = f_area.startEA;
161
162 // rename global syms which conflict with frame member names
d8891fcc 163 frame = get_frame(func);
164 if (frame != NULL)
165 {
166 for (m = 0; m < (int)frame->memqty; m++)
167 {
168 ret = get_member_name(frame->members[m].id, buf, sizeof(buf));
169 if (ret <= 0) {
170 msg("%x: member has no name?\n", ea);
171 return;
172 }
173 if (buf[0] == ' ') // what's this?
174 continue;
175 if (IS_START(buf, "arg_") || IS_START(buf, "var_"))
176 continue;
177
1caf86bb 178 // check for dupe names
179 int m1, dupe = 0;
180 for (m1 = 0; m1 < m; m1++) {
181 get_member_name(frame->members[m1].id, buf2, sizeof(buf2));
182 if (stricmp(buf, buf2) == 0)
183 dupe = 1;
184 }
185
186 if (is_name_reserved(buf) || dupe) {
d8891fcc 187 msg("%x: renaming '%s'\n", ea, buf);
188 qstrncat(buf, "_", sizeof(buf));
189 ret = set_member_name(frame, frame->members[m].soff, buf);
190 if (!ret) {
191 msg("%x: renaming failed\n", ea);
192 return;
193 }
194 }
195
196 tmp_ea = get_name_ea(ea, buf);
197 if (tmp_ea == 0 || tmp_ea == ~0)
198 continue;
199
200 msg("%x: from %x: renaming '%s'\n", tmp_ea, ea, buf);
201 qstrncat(buf, "_g", sizeof(buf));
202 set_name(tmp_ea, buf);
203 }
204 }
205
206 func = get_next_func(ea);
15c7b2a4 207 }
208
b587e6ae 209 // 2nd pass over whole .text and .(ro)data segments
15c7b2a4 210 for (ea = inf.minEA; ea != BADADDR; ea = next_head(ea, inf.maxEA))
211 {
212 segment_t *seg = getseg(ea);
b587e6ae 213 if (!seg)
214 break;
215 if (seg->type == SEG_XTRN)
216 continue;
217 if (seg->type != SEG_CODE && seg->type != SEG_DATA)
15c7b2a4 218 break;
219
b587e6ae 220 ea_flags = get_flags_novalue(ea);
15c7b2a4 221 func = get_func(ea);
222 if (isCode(ea_flags))
223 {
224 if (!decode_insn(ea)) {
225 msg("%x: decode_insn() failed\n", ea);
226 continue;
227 }
228
b587e6ae 229 // masm doesn't understand IDA's float/xmm types
230 if (cmd.itype == NN_fld || cmd.itype == NN_fst
231 || cmd.itype == NN_movapd || cmd.itype == NN_movlpd)
232 {
233 for (o = 0; o < UA_MAXOP; o++) {
234 if (cmd.Operands[o].type == o_void)
235 break;
236
237 if (cmd.Operands[o].type == o_mem) {
238 tmp_ea = cmd.Operands[o].addr;
239 flags_t tmp_ea_flags = get_flags_novalue(tmp_ea);
240 if (!isUnknown(tmp_ea_flags)) {
241 buf[0] = 0;
242 get_name(ea, tmp_ea, buf, sizeof(buf));
243 msg("%x: undefining %x '%s'\n", ea, tmp_ea, buf);
244 do_unknown(tmp_ea, DOUNK_EXPAND);
245 }
246 }
247 }
248 }
1caf86bb 249 // detect code alignment
250 else if (cmd.itype == NN_lea) {
251 if (cmd.Operands[0].reg == cmd.Operands[1].reg
252 && cmd.Operands[1].type == o_displ
253 && cmd.Operands[1].addr == 0)
254 {
255 tmp_ea = next_head(ea, inf.maxEA);
256 if ((tmp_ea & 0x03) == 0) {
257 n = calc_max_align(tmp_ea);
258 if (n > 4) // masm doesn't like more..
259 n = 4;
260 msg("%x: align %d\n", ea, 1 << n);
261 do_unknown(ea, DOUNK_SIMPLE);
262 doAlign(ea, tmp_ea - ea, n);
263 }
264 }
265 }
b587e6ae 266
15c7b2a4 267 // find non-local branches
b587e6ae 268 if (is_insn_jmp(cmd.itype) && cmd.Operands[0].type == o_near)
15c7b2a4 269 {
270 target_ea = cmd.Operands[0].addr;
271 if (func == NULL)
272 nonlocal_add(target_ea);
273 else {
274 ret = get_func_chunknum(func, target_ea);
275 if (ret != 0) {
276 // a jump to another func or chunk
277 // check if it lands on func start
278 if (!isFunc(get_flags_novalue(target_ea)))
279 nonlocal_add(target_ea);
280 }
281 }
282 }
283 }
284 else { // not code
1caf86bb 285 int do_undef = 0;
286 ea_size = get_item_size(ea);
287
15c7b2a4 288 if (func == NULL && isOff0(ea_flags)) {
15c7b2a4 289 for (tmp_ea = 0; tmp_ea < ea_size; tmp_ea += 4)
290 nonlocal_add(get_long(ea + tmp_ea));
291 }
b587e6ae 292
293 // IDA vs masm float/mmx/xmm type incompatibility
294 if (isDouble(ea_flags) || isTbyt(ea_flags)
295 || isPackReal(ea_flags))
296 {
1caf86bb 297 do_undef = 1;
298 }
299 else if (isOwrd(ea_flags)) {
b587e6ae 300 buf[0] = 0;
301 get_name(BADADDR, ea, buf, sizeof(buf));
1caf86bb 302 if (IS_START(buf, "xmm"))
303 do_undef = 1;
304 }
305 // masm doesn't understand IDA's unicode
306 else if (isASCII(ea_flags) && ea_size >= 4
307 && (get_long(ea) & 0xff00ff00) == 0) // lame..
308 {
309 do_undef = 1;
310 }
311 // masm doesn't understand large aligns
312 else if (isAlign(ea_flags) && ea_size > 0x10) {
313 msg("%x: undefining align %d\n", ea, ea_size);
b587e6ae 314 do_unknown(ea, DOUNK_EXPAND);
315 }
1caf86bb 316
317 if (do_undef) {
b587e6ae 318 buf[0] = 0;
319 get_name(BADADDR, ea, buf, sizeof(buf));
1caf86bb 320 msg("%x: undefining '%s'\n", ea, buf);
321 do_unknown(ea, DOUNK_EXPAND);
b587e6ae 322 }
323 }
324 }
325
326 // check namelist for reserved names
327 n = get_nlist_size();
328 for (i = 0; i < n; i++) {
329 ea = get_nlist_ea(i);
330 name = get_nlist_name(i);
331 if (name == NULL) {
332 msg("%x: null name?\n", ea);
333 continue;
334 }
335
b25f320a 336 // rename vars with '?@' (funcs are ok)
337 int change_qat = 0;
338 ea_flags = get_flags_novalue(ea);
339 if (!isCode(ea_flags) && strpbrk(name, "?@"))
340 change_qat = 1;
341
342 if (change_qat || is_name_reserved(name)) {
b587e6ae 343 msg("%x: renaming name '%s'\n", ea, name);
344 qsnprintf(buf, sizeof(buf), "%s_g", name);
b25f320a 345
346 if (change_qat) {
347 for (p = buf; *p != 0; p++) {
348 if (*p == '?' || *p == '@') {
349 qsnprintf(buf2, sizeof(buf2), "%02x", (unsigned char)*p);
350 memmove(p + 1, p, strlen(p) + 1);
351 memcpy(p, buf2, 2);
352 }
353 }
354 }
355
b587e6ae 356 set_name(ea, buf);
15c7b2a4 357 }
358 }
359
360 if (nonlocal_bt_cnt > 1) {
361 qsort(nonlocal_bt, nonlocal_bt_cnt,
362 sizeof(nonlocal_bt[0]), nonlocal_bt_cmp);
d8891fcc 363 }
364
365 char *fname = askfile_c(1, NULL, "Save asm file");
366 if (fname == NULL)
367 return;
368 fout = qfopen(fname, "w");
369 if (fout == NULL) {
370 msg("couldn't open '%s'\n", fname);
371 return;
372 }
373
374 show_wait_box("Saving..");
375
376 // deal with the beginning
377 ea = inf.minEA;
378 int flags = 0; // calc_default_idaplace_flags();
379 linearray_t ln(&flags);
380 idaplace_t pl;
381 pl.ea = ea;
382 pl.lnnum = 0;
383 ln.set_place(&pl);
384 n = ln.get_linecnt();
385 for (i = 0; i < n - 1; i++) {
d53d4cc7 386 do_def_line(buf, sizeof(buf), ln.down(), ea);
d8891fcc 387 if (strstr(buf, "include"))
388 continue;
a6e7d4e6 389 p = strstr(buf, "assume cs");
390 if (p != NULL) {
391 memmove(p + 1, p, strlen(p) + 1);
392 *p = ';';
393 }
d8891fcc 394
395 fout_line++;
396 qfprintf(fout, "%s\n", buf);
397 p = strstr(buf, ".mmx");
398 if (p != NULL) {
399 memcpy(p, ".xmm", 4);
400 fout_line++;
401 qfprintf(fout, "%s\n", buf);
1402b79d 402 continue;
403 }
404 p = strstr(buf, ".model");
405 if (p != NULL) {
406 qstrncpy(p, "include imports.inc", sizeof(buf) - (p - buf));
407 fout_line++;
408 qfprintf(fout, "\n%s\n", buf);
409 continue;
d8891fcc 410 }
411 }
b587e6ae 412 pl.lnnum = i;
d8891fcc 413
414 for (;;)
415 {
94cd6e34 416 int drop_large = 0, do_rva = 0, set_scale = 0, jmp_near = 0;
1caf86bb 417 int word_imm = 0, dword_imm = 0, do_pushf = 0;
15c7b2a4 418
d8891fcc 419 if ((ea >> 14) != ui_ea_block) {
420 ui_ea_block = ea >> 14;
421 showAddr(ea);
422 if (wasBreak())
423 break;
424 }
425
426 segment_t *seg = getseg(ea);
b587e6ae 427 if (!seg || (seg->type != SEG_CODE && seg->type != SEG_DATA))
d8891fcc 428 goto pass;
429
b587e6ae 430 ea_flags = get_flags_novalue(ea);
431 if (isCode(ea_flags))
432 {
433 if (!decode_insn(ea))
434 goto pass;
d8891fcc 435
1caf86bb 436 if (is_insn_jmp(cmd.itype) && cmd.Operands[0].type == o_near
437 && cmd.Operands[0].dtyp == dt_dword)
438 {
439 jmp_near = 1;
440 }
441 else if ((cmd.itype == NN_pushf || cmd.itype == NN_popf)
442 && natop())
443 {
444 do_pushf = 1;
445 }
446
b587e6ae 447 for (o = 0; o < UA_MAXOP; o++) {
1caf86bb 448 const op_t &opr = cmd.Operands[o];
449 if (opr.type == o_void)
b587e6ae 450 break;
d8891fcc 451
1caf86bb 452 // correct?
453 if (opr.type == o_mem && opr.specval_shorts.high == 0x21)
b587e6ae 454 drop_large = 1;
1caf86bb 455 if (opr.hasSIB && x86_scale(opr) == 0
456 && x86_index(opr) != INDEX_NONE)
457 {
458 set_scale = 1;
459 }
460 // annoying alignment variant..
461 if (opr.type == o_imm && opr.dtyp == dt_dword
462 && (opr.value < 0x80 || opr.value > 0xffffff80)
463 && cmd.size >= opr.offb + 4)
464 {
465 if (get_long(ea + opr.offb) == opr.value)
466 dword_imm = 1;
467 }
468 else if (opr.type == o_imm && opr.dtyp == dt_word
469 && (opr.value < 0x80 || opr.value > 0xff80)
470 && cmd.size >= opr.offb + 2)
471 {
472 if (get_word(ea + opr.offb) == (ushort)opr.value)
473 word_imm = 1;
15c7b2a4 474 }
15c7b2a4 475 }
b587e6ae 476 }
477 else { // not code
478 if (isOff0(ea_flags))
94cd6e34 479 do_rva = 1;
d8891fcc 480 }
481
482pass:
b587e6ae 483 n = ln.get_linecnt();
484 for (i = pl.lnnum; i < n; i++) {
d53d4cc7 485 do_def_line(buf, sizeof(buf), ln.down(), ea);
15c7b2a4 486
1402b79d 487 char *fw;
488 for (fw = buf; *fw != 0 && *fw == ' '; )
489 fw++;
490
1caf86bb 491 // patches..
b587e6ae 492 if (drop_large) {
1402b79d 493 p = strstr(fw, "large ");
b587e6ae 494 if (p != NULL)
495 memmove(p, p + 6, strlen(p + 6) + 1);
496 }
94cd6e34 497 while (do_rva) {
1402b79d 498 p = strstr(fw, " rva ");
b587e6ae 499 if (p == NULL)
500 break;
94cd6e34 501 memmove(p + 4 + 3, p + 4, strlen(p + 4) + 1);
502 memcpy(p + 1, "offset", 6);
b587e6ae 503 }
1caf86bb 504 if (set_scale) {
1402b79d 505 p = strchr(fw, '[');
1caf86bb 506 if (p != NULL)
507 p = strchr(p, '+');
508 if (p != NULL && p[1] == 'e') {
509 p += 4;
510 // scale is 1, must specify it explicitly so that
511 // masm chooses the right scaled reg
512 memmove(p + 2, p, strlen(p) + 1);
513 memcpy(p, "*1", 2);
514 }
515 }
516 else if (jmp_near) {
1402b79d 517 p = strchr(fw, 'j');
1caf86bb 518 while (p && *p != ' ')
519 p++;
520 while (p && *p == ' ')
521 p++;
522 if (p != NULL) {
523 memmove(p + 9, p, strlen(p) + 1);
524 memcpy(p, "near ptr ", 9);
525 }
526 }
527 if (word_imm) {
1402b79d 528 p = strstr(fw, ", ");
1caf86bb 529 if (p != NULL && '0' <= p[2] && p[2] <= '9') {
530 p += 2;
531 memmove(p + 9, p, strlen(p) + 1);
532 memcpy(p, "word ptr ", 9);
533 }
534 }
535 else if (dword_imm) {
1402b79d 536 p = strstr(fw, ", ");
1caf86bb 537 if (p != NULL && '0' <= p[2] && p[2] <= '9') {
538 p += 2;
539 memmove(p + 10, p, strlen(p) + 1);
540 memcpy(p, "dword ptr ", 10);
541 }
542 }
543 else if (do_pushf) {
1402b79d 544 p = strstr(fw, "pushf");
1caf86bb 545 if (p == NULL)
1402b79d 546 p = strstr(fw, "popf");
1caf86bb 547 if (p != NULL) {
548 p = strchr(p, 'f') + 1;
549 memmove(p + 1, p, strlen(p) + 1);
550 *p = 'd';
551 }
552 }
b587e6ae 553
1402b79d 554 if (fw[0] == 'e' && IS_START(fw, "end") && fw[3] == ' ') {
555 fout_line++;
556 qfprintf(fout, "include public.inc\n\n");
557
558 // kill entry point
559 fw[3] = 0;
560 }
561
b587e6ae 562 fout_line++;
563 qfprintf(fout, "%s\n", buf);
564 }
d8891fcc 565
15c7b2a4 566 // note: next_head skips some undefined stuff
d8891fcc 567 ea = next_not_tail(ea); // correct?
15c7b2a4 568 if (ea == BADADDR)
d8891fcc 569 break;
570
571 pl.ea = ea;
572 pl.lnnum = 0;
573 ln.set_place(&pl);
d8891fcc 574 }
575
576 if (fout != NULL)
577 qfclose(fout);
15c7b2a4 578 if (fname != NULL)
579 qfree(fname);
d8891fcc 580
581 hide_wait_box();
582 msg("%d lines saved.\n", fout_line);
583}
584
585//--------------------------------------------------------------------------
586
587static const char comment[] = "Generate disassembly lines for one address";
588static const char help[] = "Generate asm file\n";
589static const char wanted_name[] = "Save asm";
590static const char wanted_hotkey[] = "Ctrl-F6";
591
592//--------------------------------------------------------------------------
593//
594// PLUGIN DESCRIPTION BLOCK
595//
596//--------------------------------------------------------------------------
597plugin_t PLUGIN =
598{
599 IDP_INTERFACE_VERSION,
600 0, // plugin flags
601 init, // initialize
602 term, // terminate. this pointer may be NULL.
603 run, // invoke plugin
604 comment, // long comment about the plugin
605 // it could appear in the status line
606 // or as a hint
607 help, // multiline help about the plugin
608 wanted_name, // the preferred short name of the plugin
609 wanted_hotkey // the preferred hotkey to run the plugin
610};
611
612// vim:ts=2:shiftwidth=2:expandtab