/*
 * Copyright (C) Feb. 1999, Matt Conover & w00w00
 *
 * Demonstrates modifying opcodes in binary files. (Broken)
 * To compile: gcc -o bfd bfd.c -lbfd -liberty
 */

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <stdarg.h>
#include <errno.h>
#include <ctype.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <bfd.h>

#if defined(SUN) || defined(SOLARIS)
#include <inttypes.h>
#endif

#define ERROR -1
#define OFFSET  0x0 /* offset to preferred location */

#define CALL 0xe8 /* opcode for CALL on i386 */
#define JMP 0xeb  /* opcode for JMP on i386  */

bfd_format bfdformat;
enum bfd_flavour bfdflavour;
bfd *abfd = NULL, *abfd1 = NULL;

u_long *entaddr = NULL;
asection *asect = NULL;

int offset = OFFSET;

char ops[] = { (char)CALL, (char)JMP, '\0' };

/* --------------------------------------- */

/* ------- function declarations ------- */
void setup();
void showStats();
void modSection(char *outfile, char *sectname);

void bfderror(char *fmt, ...);
/* ------------------------------------- */

int main(int argc, char **argv)
{   
   if (argc <= 2)
   {
      fprintf(stderr, "Usage: %s <infile> <outfile> [addr offset]\n",
              argv[0]);

      exit(ERROR); 
   }

   else if (argc > 3) offset = atoi(argv[3]);

   bfd_init();
   bfd_set_error_handler((bfd_error_handler_type)bfderror);

   abfd = bfd_openr(argv[1], NULL);

   setup(), showStats();
   modSection(argv[2], ".text");

   bfd_close(abfd);
   return 0;
}


void setup()
{
   bfdformat = bfd_object;
   if (bfd_check_format(abfd, bfdformat) == false)
   {
      (void)fprintf(stderr, "error: only object files are supported\n\n");
      exit(ERROR);
   }

   bfdflavour = bfd_get_flavour(abfd);
   if (bfdflavour == ERROR)
   {
      bfderror("[bfd_get_flavour] error");
      exit(ERROR);
   }

   /* ---------------------------------------------------- */

   if ((bfdflavour == bfd_target_unknown_flavour) ||
       ((bfdflavour != bfd_target_aout_flavour) &&
        (bfdflavour != bfd_target_coff_flavour) &&
        (bfdflavour != bfd_target_elf_flavour)))
   {
      (void)fprintf(stderr, "Only current supported formats (flavours): "
                            "ELF, COFF, and AOUT\n");

      exit(ERROR);
   }
}


void showStats()
{   
   (void)printf("Filename: %s\n\n", bfd_get_filename(abfd));
   (void)printf("File's target: %s\n", bfd_get_target(abfd));

   entaddr = (u_long *)bfd_get_start_address(abfd);
   (void)printf("Start address: %p\n\n", entaddr);
}


void modSection(char *outfile, char *sectname)
{
   register int i;
   int sectmod = 0, sectsize = 0;
   char *sectbuf, *sectptr = NULL;

   asect = bfd_get_section_by_name(abfd, sectname);
   if (asect == NULL) 
   {
      bfderror("[bfd_get_section_by_name] error opening %s\n", sectname);
      exit(ERROR);
   }

   sectsize = bfd_get_section_size_before_reloc(asect);

   sectbuf = (char *)malloc(sectsize+1);
   memset(sectbuf, 0, sectsize+1);

   if (bfd_get_section_contents(abfd, asect, sectbuf, 0, sectsize) == false)
   {
      bfderror("[bfd_get_section_contents] error opening %s section", 
               sectname);

      exit(ERROR);
   }

   printf("%s size = 0x%x (%d) bytes\n\n", sectname, sectsize, sectsize);
   printf("Start (%p) - End (%p)\n", sectbuf, &sectbuf[sectsize]);
   sleep(2);

   for (sectptr = sectbuf; sectptr < &sectbuf[sectsize]; sectptr++)
   {
      for (i = 0; ops[i]; i++)
         if (*sectptr == ops[i])
         {
            printf("0x%02x found at [%s] + 0x%x\n",
                   ops[i], sectname, (u_long)sectptr - (u_long)sectbuf, 
                   (u_long)sectptr - (u_long)sectbuf);

            sectptr += 1; /* 4-byte address after jmp/call opcode */

            /* ----------------------------------- */

/*
            if (!bfd_little_endian(abfd))
               *((u_long *)sectptr) = (u_long)(entaddr + offset);

            else *((u_long *)sectptr) = ntohl((u_long)(entaddr + offset));
*/

            sectmod = 1;
         }
   }

   if (sectmod)
   {
      abfd1 = bfd_openw(outfile, NULL);

      if (abfd1 == NULL)
      {
         bfderror("[bfd_openw] error with opening %s", outfile);
         exit(ERROR);
      }

      bfd_copy_private_bfd_data(abfd, abfd1);
      bfd_set_section_contents(abfd1, asect, sectbuf, 0, sectsize);

      bfd_close(abfd1);
   }
}


/* our error message handler */
void bfderror(char *fmt, ...)
{
   va_list va;
   char errbuf[512] = {0};

   va_start(va, fmt);
   vsnprintf(errbuf, sizeof(errbuf)-1, fmt, va);
   va_end(va);

   bfd_perror(errbuf);
}


