/* decomp.c * Decompile an executable file. * * Copyright (C) 2001 Jonathan duSaint * * Started around 15 November 2001. */ #include #include #include #include #include #include /*#include */ #include "decomp.h" char pname[] = PACKAGE_NAME; char version[] = PACKAGE_VERSION; char author[] = AUTHOR; char author_email[] = AUTHOR_EMAIL; int debug = 0; int print_address = 0; int print_opcode = 0; int print_prefix = 0; enum syntax syntax = INTEL; int host_endian = ELITTLE, target_endian = ELITTLE; /* handy default */ void write_file_header (FILE *fp, const char *source_file, const char *file_name) { fprintf (fp, "\t; %s\n" "\t; Decompiled by Decomp from %s.\n" "\t; In case of an incorrect decompilation, let the author know.\n" "\n", file_name, source_file); } /* get_output_name * Called when an output file has not been specified with -o. Appends * DEFAULT_OUTPUT_EXTENSION on to the file name of the object file. The * returned memory must be freed. */ char * get_output_name (const char *file_name) { char *output_name; char output_extension[] = DEFAULT_OUTPUT_EXTENSION; output_name = xmalloc (strlen (file_name) + strlen (output_extension) + 1); sprintf (output_name, "%s%s", file_name, output_extension); return (output_name); } /* byte_reverse * Convert from big to little endian and vice-versa. */ void byte_reverse (void *ptr, size_t size, size_t nmemb) { uint8_t *buf = (uint8_t *)ptr; if (size == 1 || nmemb == 0) return; do { register size_t low = 0, high = size - 1; while (high > low) { register uint8_t tmp; tmp = buf[low]; buf[low] = buf[high]; buf[high] = tmp; high--; low++; } buf += size; } while (--nmemb); } /* d_read * Similar in functionality to fread, except ensures that > 0 bytes are * read and accounts for byte order. */ void d_read (void *ptr, size_t size, size_t nmemb, FILE *stream, int as_is) { if (fread (ptr, size, nmemb, stream) <= 0) error_out (ERR_FILE_READ); if (as_is) return; /* if (host_endian .xor. target_endian) */ if ((host_endian || target_endian) - (host_endian && target_endian)) byte_reverse (ptr, size, nmemb); } int get_host_endian (void) { union { char c[4]; uint32_t i; } x; memset (&x, 0, 4); /* just to be sure */ x.i = 1; if (x.c[0] != 0) return ELITTLE; else return EBIG; } void print_version (void) { printf ("%s %s\n" "Copyright (C) 2001 %s %s\n" "%s comes with NO WARRANTY, to the extent\n" "permitted by law. You may redistribute copies\n" "of %s under the terms of the GNU General\n" "Public License. For more information about\n" "these matters, see the file named COPYING.\n", pname, version, author, author_email, pname, pname); exit (EXIT_SUCCESS); } void print_help (void) { printf ("Usage: decomp [OPTION]... \n" "Decompile FILE into a complete assembly language file\n" "which can in turn be re-assembled.\n" "\n" " -o, --output Place output in file. The default is\n" " to append .S to the input file name.\n" "\n" " -h, --help Print this message.\n" " -v, --version Print version information.\n" "\n" " -a, --address Print the address of each instruction.\n" " -c, --opcode Print the opcode of each instruction.\n" " -p, --prefix Print any instruction prefixes.\n" "\n" " -d, --debug Print debugging information.\n" " -t, --trace Turn on memory tracing.\n" "\n" " -q, --queue-size Adjust the size of the output\n" " instruction queue. Only use this if\n" " you know what you are doing.\n" "IA-32 specific options:\n" " -s, --style Select assembly language style.\n" " Not yet implemented.\n" "\n" "Report bugs to %s %s\n", author, author_email); } int main (int argc, char *argv[]) { int k; char opt, *output_file_name = NULL; struct file_info fi; hash_t symtab; FILE *ofp; struct option options[] = { { "address", no_argument, NULL, 'a' }, { "debug", no_argument, NULL, 'd' }, { "help", no_argument, NULL, 'h' }, { "opcode", no_argument, NULL, 'c' }, { "output", required_argument, NULL, 'o' }, { "prefix", no_argument, NULL, 'p' }, { "queue-size", required_argument, NULL, 'q' }, { "style", required_argument, NULL, 's' }, { "trace", no_argument, NULL, 't' }, { "version", no_argument, NULL, 'v' }, { 0, 0, 0, 0 } }; /* parse through argv, looking for options */ while (1) { opt = getopt_long (argc, argv, "acdho:pq:s:tv", options, NULL); if (opt == EOF) break; switch (opt) { case 'a': print_address = 1; break; case 'c': print_opcode = 1; break; case 'd': debug = 1; break; case 'h': print_help (); exit (EXIT_SUCCESS); case 'o': output_file_name = optarg; break; case 'p': print_prefix = 1; break; case 'q': max_queue_size = strtol (optarg, NULL, 0); if (max_queue_size < 1) { fprintf (stderr, "DECOMP: invalid queue size - using default\n"); max_queue_size = MAX_OUTPUT_QUEUE_SIZE; } break; case 's': if (!strcmp (optarg, "att")) syntax = ATT; else if (!strcmp (optarg, "intel")) syntax = INTEL; else { fprintf (stderr, "DECOMP: unknown disassembly syntax `%s'\n", optarg); exit (1); } break; case 't': memory_trace = 1; break; case 'v': print_version (); exit (EXIT_SUCCESS); case '?': fprintf (stderr, "DECOMP: unknown option character `%c'\n", optopt); exit (EXIT_FAILURE); default: fprintf (stderr, "DECOMP: ?? getopt returned character code 0x%x (%c) ??\n", opt, opt); exit (EXIT_FAILURE); } } /* all that's left in argv should be a file name to disassemble */ if (argv[optind] == NULL) error_out (ERR_NEED_FILE_NAME); /* get host endian-ness and set target as the same for now */ target_endian = host_endian = get_host_endian (); /* open_files (); */ /* get_file_info (); */ if (get_file_info (&fi, argv[optind])) error_out (ERR_FILE_INFO); /* write_asm_header (); */ if (output_file_name == NULL) output_file_name = get_output_name (argv[optind]); ofp = fopen (output_file_name, "wt"); if (ofp == NULL) error_out (ERR_FILE_OPEN); if (debug) setvbuf (ofp, NULL, _IONBF, 0); write_file_header (ofp, argv[optind], output_file_name); /* read the symtab */ symtab = read_symtab (&fi); /* decode and write out the code sections */ k = -1; while (fi.code_sections[++k]) decode_code_section (&fi, fi.code_sections[k], symtab, ofp); /* decode and write out the data sections */ k = -1; while (fi.data_sections[++k]) decode_data_section (&fi, fi.data_sections[k], symtab, ofp); /* write_asm_footer (); */ /* clean up */ clean_symtab (symtab); fclose (ofp); if (clean_up_file (&fi)) error_out (ERR_CLEAN_UP); free_all (); return (0); }