/* uncripple.c - section header table fixer by [ByteRage] (byterage@yahoo.com)
 * http://www.byterage.cjb.net
 *
 * This program appends a section header table to an ELF file that hasn't got
 * one, this makes it easier to reverse engineer certain 'crippled' ELF files.
 * (older versions of IDA can deal with the ELF file, objdump works again,
 * gdb works again, etc...) This source compiles under Linux.
 *
 * example usage (together with burndump) :
 * # gcc -c burndump.c
 * # gcc uncripple.c -o uncripple
 * # insmod burndump
 * # ./burneye
 * # rmmod burndump
 * # ./uncripple burnout
 * # gdb burnout
 * gdb> run
 */

#include <stdio.h>
#include <elf.h>

char * et(e_type) {
  switch(e_type) {
    case 0 : return "ET_NONE";
    case 1 : return "ET_REL";
    case 2 : return "ET_EXEC";
    case 3 : return "ET_DYN";
    case 4 : return "ET_CORE";
    case 0xff00: return "ET_LOPROC";
    case 0xffff: return "ET_HIPROC";
    default : return "<unknown>";
  }
}

char * em(e_machine) {
  switch(e_machine) {
    case 0 : return "EM_NONE";
    case 1 : return "EM_32";
    case 2 : return "EM_SPARC";
    case 3 : return "EM_386";
    case 4 : return "EM_68K";
    case 5 : return "EM_88K";    
    case 6 : return "EM_486";
    case 7 : return "EM_860";
    case 8 : return "EM_MIPS";
    case 10: return "EM_MIPS_RS4_BE";    
    case 15: return "EM_PARISC";
    case 18: return "EM_SPARC32PLUS";
    case 20: return "EM_PPC";
    case 42: return "EM_SH";
    case 43: return "EM_SPARCV9";
    case 50: return "EM_IA_64";
    case 62: return "EM_X8664";
    case 76: return "EM_CRIS";
    default : return "<unknown>";
  }
}

char * pt(p_type) {
  switch(p_type) {
    case 0 : return "PT_NULL";
    case 1 : return "PT_LOAD";
    case 2 : return "PT_DYNAMIC";
    case 3 : return "PT_INTERP";
    case 4 : return "PT_NOTE";
    case 5 : return "PT_SHLIB";
    case 6 : return "PT_PHDR";
    case 0x70000000: return "PT_LOPROC / PT_MIPS_REGINFO";
    case 0x7fffffff: return "PT_HIPROC";
    default : return "<unknown>";
  }
}

char * sht(sh_type) {
  switch(sh_type) {
    case 0 : return "SHT_NULL";
    case 1 : return "SHT_PROGBITS";
    case 2 : return "SHT_SYMTAB";
    case 3 : return "SHT_STRTAB";
    case 4 : return "SHT_RELA";
    case 5 : return "SHT_HASH";
    case 6 : return "SHT_DYNAMIC";
    case 7 : return "SHT_NOTE";
    case 8 : return "SHT_NOBITS";
    case 9 : return "SHT_REL";
    case 10: return "SHT_SHLIB";
    case 11: return "SHT_DYNSYM";
    case 12: return "SHT_NUM";
    case 0x70000000: return "SHT_LOPROC / SHT_MIPS_LIST";
    case 0x7fffffff: return "SHT_HIPROC";
    case 0x80000000: return "SHT_LOUSER";
    case 0xffffffff: return "SHT_HIUSER";
    case 0x70000002: return "SHT_MIPS_CONFLICT";
    case 0x70000003: return "SHT_MIPS_GPTAB";
    case 0x70000004: return "SHT_MIPS_UCODE";
    default : return "<unknown>";
  }
}

char *shl(sh_link) {
  switch(sh_link) {
    case 0: return "SHN_UNDEF";
    case 0xff00: return "SHN_LORESERVE / SHN_LOPROC / SHN_MIPS_ACCOMON";
    case 0xff1f: return "SHN_HIPROC";
    case 0xfff1: return "SHN_ABS";
    case 0xfff2: return "SHN_COMMON";
    case 0xffff: return "SHN_HIRESERVE";
    default : return "<unknown>";
  }
}

void dumpelfhdr(Elf32_Ehdr ehdr) {
  int i;
  printf("e_ident     : ");
  for(i = 0; i < 16; i++)
    if ((ehdr.e_ident[i] > 0x20) && (ehdr.e_ident[i] < 0x7F))
      printf("%c", ehdr.e_ident[i]);
    else
      printf(" ");
  for(i = 0; i < 16; i++)
    printf(" %02x", ehdr.e_ident[i]);
  printf("\ne_type      : 0x%04x (%s)"
         "\ne_machine   : 0x%04x (%s)"
         "\ne_version   : 0x%08x (%s)"
         "\ne_entry     : 0x%08x"
         "\ne_phoff     : 0x%08x"
         "\ne_shoff     : 0x%08x"
         "\ne_flags     : 0x%08x"
	 "\ne_ehsize    : 0x%04x"
	 "\ne_phentsize : 0x%04x"
	 "\ne_phnum     : 0x%04x"
	 "\ne_shentsize : 0x%04x"
	 "\ne_shnum     : 0x%04x"
	 "\ne_shstrndx  : 0x%04x\n",
	 ehdr.e_type, et(ehdr.e_type), ehdr.e_machine, em(ehdr.e_machine),
	 ehdr.e_version, (ehdr.e_version == 1) ? "EV_CURRENT" : "EV_NONE",
	 ehdr.e_entry, ehdr.e_phoff, ehdr.e_shoff, ehdr.e_flags,
	 ehdr.e_ehsize, ehdr.e_phentsize, ehdr.e_phnum, ehdr.e_shentsize,
	 ehdr.e_shnum, ehdr.e_shstrndx);  
}

void dumpprogramhdr(Elf32_Phdr phdr) {
  int i;
  printf("p_type   : 0x%08x (%s)\n"
         "p_offset : 0x%08x\n"
 	 "p_vaddr  : 0x%08x\n"
	 "p_paddr  : 0x%08x\n"
	 "p_filesz : 0x%08x\n"
	 "p_memsz  : 0x%08x\n"
	 "p_flags  : 0x%08x (",
	 phdr.p_type, pt(phdr.p_type), phdr.p_offset, phdr.p_vaddr,
	 phdr.p_paddr, phdr.p_filesz, phdr.p_memsz, phdr.p_flags);
  i = 0;
  if (phdr.p_flags & PF_R) {
    printf("PF_R"); i = 1;
  }
  if (phdr.p_flags & PF_W) {
    if (i)
      printf("|");
    printf("PF_W"); i = 1;
  }
  if (phdr.p_flags & PF_X) {
    if (i)
      printf("|");
    printf("PF_X"); i = 1;
  }
  printf(")\np_align  : 0x%08x\n", phdr.p_align);
}

void dumpsectionhdr(Elf32_Shdr shdr) {
  int i = 0;
  printf("sh_name      : 0x%08x\n"
         "sh_type      : 0x%08x (%s)\n",
	 shdr.sh_name, shdr.sh_type, sht(shdr.sh_type));
  printf("sh_flags     : 0x%08x (", shdr.sh_flags);
  if (shdr.sh_flags & SHF_WRITE) {
    printf("SHF_WRITE"); i = 1;
  }
  if (shdr.sh_flags & SHF_ALLOC) {
    if (i)
      printf("|");
    printf("SHF_ALLOC"); i = 1;
  }
  if (shdr.sh_flags & SHF_EXECINSTR) {
    if (i)
      printf("|");
    printf("SHF_EXECINSTR"); i = 1;
  }
  if (shdr.sh_flags & SHF_MASKPROC) {
    if (i)
      printf("|");
    printf("SHF_MASKPROC"); i = 1;
  }
  printf(")\nsh_addr      : 0x%08x"
          "\nsh_offset    : 0x%08x"
	  "\nsh_size      : %d bytes (0x%08x)"
	  "\nsh_link      : 0x%08x (%s)"
	  "\nsh_info      : 0x%08x"
	  "\nsh_addralign : 0x%08x"
	  "\nsh_entsize   : 0x%08x\n",
          shdr.sh_addr, shdr.sh_offset, shdr.sh_size, shdr.sh_size,
	  shdr.sh_link, shl(shdr.sh_link), shdr.sh_info, shdr.sh_addralign,
	  shdr.sh_entsize);
}

int main(int argc, char ** argv) {

  int i;
  unsigned long origsize, savefpos;
  FILE *fh;
  Elf32_Ehdr elfhdr;
  Elf32_Phdr programhdr;
  Elf32_Shdr sectionhdr;

  if (argc < 2) {
    printf("usage: %s filename\n", argv[0]);
    return 1;
  }
  if ((fh = fopen(argv[1],"r+b")) != 0) {
    fread(&elfhdr, 1, sizeof(Elf32_Ehdr), fh);
    if (*(unsigned long *)&elfhdr.e_ident[0] != 0x464C457F) {
      printf("woops! ELF signature not found\n");
      fclose(fh);
      return 1;
    }
    if (elfhdr.e_ident[EI_CLASS] != 1) {
      printf("woops! there is no support for non 32-bit ELF files\n");
      fclose(fh);
      return 1;
    }
    printf("--- ELF header\n");
    dumpelfhdr(elfhdr);
    printf("---\n");
    
    if ((elfhdr.e_phoff == 0) || (elfhdr.e_phnum == 0) ||
        (elfhdr.e_phentsize == 0)) {
      printf("no valid program headers found\nyou are on your own :)\n");
      fclose(fh);
      return 1;
    }
    fseek(fh, elfhdr.e_phoff, SEEK_SET);
    for(i = 0; i < elfhdr.e_phnum; i++) {
      printf("--- program header %d\n", i, i);
      fread(&programhdr, 1, sizeof(Elf32_Phdr), fh);
      dumpprogramhdr(programhdr);
    }
    printf("---\n");    
    if ((elfhdr.e_shoff != 0) && (elfhdr.e_shnum != 0) &&
        (elfhdr.e_shentsize  != 0)) {
      printf("section header table present! (@0x%08x, shnum:%d)\n",
             elfhdr.e_shoff, elfhdr.e_shnum);
      fseek(fh, elfhdr.e_shoff, SEEK_SET);
      for(i = 0; i < elfhdr.e_shnum; i++) {
        printf("--- section header %d\n", i, i);
        fread(&sectionhdr, 1, sizeof(Elf32_Shdr), fh);
        dumpsectionhdr(sectionhdr);
      }
      printf("---\n");
      fclose(fh);
      return 0;
    }
    printf("section header table is crippled...\n");

    fseek(fh, 0, SEEK_END);
    origsize = ftell(fh);

    printf("--- appending section header table entry 0 : empty\n");
    memset(&sectionhdr, 0, sizeof(Elf32_Shdr));

    dumpsectionhdr(sectionhdr);
    fwrite(&sectionhdr, sizeof(Elf32_Shdr), 1, fh);

    printf("--- appending section header table entry 1 : .text\n");
    memset(&sectionhdr, 0, sizeof(Elf32_Shdr));

    sectionhdr.sh_name = 1;
    sectionhdr.sh_type = SHT_PROGBITS;
    sectionhdr.sh_flags = SHF_ALLOC|SHF_EXECINSTR;
    savefpos = ftell(fh);
    fseek(fh, elfhdr.e_phoff, SEEK_SET);
    for(i = 0; i < elfhdr.e_phnum; i++) {
      fread(&programhdr, 1, sizeof(Elf32_Phdr), fh);
      if ((programhdr.p_type == PT_LOAD) && (programhdr.p_flags & PF_R) &&
          (programhdr.p_flags & PF_X)) {
        if ((elfhdr.e_entry >= programhdr.p_vaddr) &&
	    (elfhdr.e_entry <= programhdr.p_vaddr+programhdr.p_memsz)) {
	  sectionhdr.sh_addr = programhdr.p_vaddr;
	  sectionhdr.sh_offset = programhdr.p_offset;
	  /* sectionhdr.sh_size = programhdr.p_filesz; */
	  sectionhdr.sh_size = origsize-programhdr.p_offset;
 	  break;
	}
      }
    }
    if (i == elfhdr.e_phnum) {
      printf("couldn't find the program header of a segment that "
             "contains the entry point code\n");
      return 1;
    }
    fseek(fh, savefpos, SEEK_SET);
    sectionhdr.sh_addralign = 1;

    dumpsectionhdr(sectionhdr);
    fwrite(&sectionhdr, sizeof(Elf32_Shdr), 1, fh);

    printf("--- appending section header table entry 2 : .shstrtab\n");    
    memset(&sectionhdr, 0, sizeof(Elf32_Shdr));

    sectionhdr.sh_name = 7;
    sectionhdr.sh_type = SHT_STRTAB;
    sectionhdr.sh_offset = origsize+(3*sizeof(Elf32_Shdr));
    sectionhdr.sh_size = 17;

    dumpsectionhdr(sectionhdr);
    fwrite(&sectionhdr, sizeof(Elf32_Shdr), 1, fh);

    printf("---\n");
    
    printf("appending string table...\n");
    fwrite("\x00.text\x00.shstrtab\x00", 17, 1, fh);

    printf("modifying ELF header...\n");
    printf("---\n");
    elfhdr.e_shoff = origsize;
    elfhdr.e_shentsize = 40;
    elfhdr.e_shnum = 3;
    elfhdr.e_shstrndx = 2;
    dumpelfhdr(elfhdr);
    fseek(fh, 0, SEEK_SET);
    fwrite(&elfhdr, sizeof(Elf32_Ehdr), 1, fh);
    printf("---\n");    

    fclose(fh);
  } else {
    printf("failed to open the file!\n");
  }
  return 0;
}