Reading the contents of an ELF file (programmatically)

11,307

You should probably be using the Pheaders, not the Sheaders. There's usually far fewer of them, and they correspond directly to pieces of the file that get mapped into memory by the kernel's ELF loader. I'm forced to use the contents of /usr/include/elf.h in the following, as you example doesn't include Elf64_Phdr definitions.

  Elf64_Off p_offset;       /* Segment file offset */
  Elf64_Xword   p_filesz;       /* Segment size in file */

Those two elements of the Elf64_Phdr for the PT_LOAD segments get you index into the file and the bytes that get mapped. The correspondence between .text segment header and the PT_LOAD Phdr always seemed a bit loose to me. If you want the executable code, you have to look at the p_flags element of Elf64_Phdr structs - the .text segment is marked PF_X, and the .data segment is not.

A while back, I updated the "userland exec". Looking at this code might help you understand, or it might just be confusing.

Share:
11,307

Related videos on Youtube

Sebi
Author by

Sebi

Updated on September 18, 2022

Comments

  • Sebi
    Sebi over 1 year

    I am trying to retrieve the contents of an additional section within an ELF binary. At this point, I'm using the following code to retrieve the name of each section:

    #include <stdio.h>
    #include <unistd.h>
    #include <stdint.h>
    #include <stdlib.h>
    
    #pragma pack(push,1)
    #pragma pack(pop)
    
    #define EI_NIDENT       16
    
    /* 32-bit ELF base types. */
    typedef unsigned int Elf32_Addr;
    typedef unsigned short Elf32_Half;
    typedef unsigned int Elf32_Off;
    typedef signed int Elf32_Sword;
    typedef unsigned int Elf32_Word;
    
    /* 64-bit ELF base types. */
    typedef unsigned long long Elf64_Addr;
    typedef unsigned short Elf64_Half;
    typedef signed short Elf64_SHalf;
    typedef unsigned long long Elf64_Off;
    typedef signed int Elf64_Sword;
    typedef unsigned int Elf64_Word;
    typedef unsigned long long Elf64_Xword;
    typedef signed long long Elf64_Sxword;
    
    typedef struct elf32_hdr{
      unsigned char e_ident[EI_NIDENT];
      Elf32_Half    e_type;
      Elf32_Half    e_machine;
      Elf32_Word    e_version;
      Elf32_Addr    e_entry;  /* Entry point */
      Elf32_Off e_phoff;
      Elf32_Off e_shoff;
      Elf32_Word    e_flags;
      Elf32_Half    e_ehsize;
      Elf32_Half    e_phentsize;
      Elf32_Half    e_phnum;
      Elf32_Half    e_shentsize;
      Elf32_Half    e_shnum;
      Elf32_Half    e_shstrndx;
    } Elf32_Ehdr;
    
    typedef struct elf32_shdr {
      Elf32_Word    sh_name;
      Elf32_Word    sh_type;
      Elf32_Word    sh_flags;
      Elf32_Addr    sh_addr;
      Elf32_Off sh_offset;
      Elf32_Word    sh_size;
      Elf32_Word    sh_link;
      Elf32_Word    sh_info;
      Elf32_Word    sh_addralign;
      Elf32_Word    sh_entsize;
    } Elf32_Shdr;
    
    typedef struct elf64_hdr {
      unsigned char e_ident[EI_NIDENT]; /* ELF "magic number" */
      Elf64_Half e_type;
      Elf64_Half e_machine;
      Elf64_Word e_version;
      Elf64_Addr e_entry;       /* Entry point virtual address */
      Elf64_Off e_phoff;        /* Program header table file offset */
      Elf64_Off e_shoff;        /* Section header table file offset */
      Elf64_Word e_flags;
      Elf64_Half e_ehsize;
      Elf64_Half e_phentsize;
      Elf64_Half e_phnum;
      Elf64_Half e_shentsize;
      Elf64_Half e_shnum;
      Elf64_Half e_shstrndx;
    } Elf64_Ehdr;
    
    typedef struct elf64_shdr {
      Elf64_Word sh_name;       /* Section name, index in string tbl */
      Elf64_Word sh_type;       /* Type of section */
      Elf64_Xword sh_flags;     /* Miscellaneous section attributes */
      Elf64_Addr sh_addr;       /* Section virtual addr at execution */
      Elf64_Off sh_offset;      /* Section file offset */
      Elf64_Xword sh_size;      /* Size of section in bytes */
      Elf64_Word sh_link;       /* Index of another section */
      Elf64_Word sh_info;       /* Additional section information */
      Elf64_Xword sh_addralign; /* Section alignment */
      Elf64_Xword sh_entsize;   /* Entry size if section holds table */
    } Elf64_Shdr;
    
    
    
    int main(int argc, char **argv) 
    {
      FILE* ElfFile = NULL;
      char* SectNames = NULL;
      Elf64_Ehdr elfHdr;
      Elf64_Shdr sectHdr;
      uint32_t idx;
    
      if(argc != 2) {
        printf("usage: %s <ELF_FILE>\n", argv[0]);
        exit(1);
      }
    
      if((ElfFile = fopen(argv[1], "r")) == NULL) {
        perror("[E] Error opening file:");
        exit(1);
      }
    
      // read ELF header, first thing in the file
      fread(&elfHdr, 1, sizeof(Elf64_Ehdr), ElfFile);
    
      // read section name string table
      // first, read its header. 
      /* 
       e_shoff         This member holds the section header table's file offset
                       in bytes.  If the file has no section header table, this
                       member holds zero.
    
       e_shstrndx      This member holds the section header table index of the
                   entry associated with the section name string table.  If
               the file has no section name string table, this member
               holds the value SHN_UNDEF.
    
               If the index of section name string table section is
               larger than or equal to SHN_LORESERVE (0xff00), this
               member holds SHN_XINDEX (0xffff) and the real index of
               the section name string table section is held in the
               sh_link member of the initial entry in section header
               table.  Otherwise, the sh_link member of the initial
               entry in section header table contains the value zero.
    
               SHN_UNDEF     This value marks an undefined, missing,
                             irrelevant, or otherwise meaningless
                             section reference.  For example, a symbol
                             "defined" relative to section number
                             SHN_UNDEF is an undefined symbol.
    
               SHN_LORESERVE This value specifies the lower bound of the
                             range of reserved indices.
    
               SHN_LOPROC    Values greater than or equal to SHN_HIPROC
                             are reserved for processor-specific
                             semantics.
    
               SHN_HIPROC    Values less than or equal to SHN_LOPROC are
                             reserved for processor-specific semantics.
    
               SHN_ABS       This value specifies absolute values for
                             the corresponding reference.  For example,
                             symbols defined relative to section number
                             SHN_ABS have absolute values and are not
                             affected by relocation.
    
               SHN_COMMON    Symbols defined relative to this section
                             are common symbols, such as Fortran COMMON
                             or unallocated C external variables.
    
               SHN_HIRESERVE This value specifies the upper bound of the
                             range of reserved indices between
                             SHN_LORESERVE and SHN_HIRESERVE, inclusive;
                             the values do not reference the section
                             header table.  That is, the section header
                             table does not contain entries for the
                             reserved indices.
      */
      fseek(ElfFile, elfHdr.e_shoff + elfHdr.e_shstrndx * sizeof(sectHdr), SEEK_SET);
      fread(&sectHdr, 1, sizeof(sectHdr), ElfFile);
      /*
       sh_size       This member holds the section's size in bytes.  Unless the
                     section type is SHT_NOBITS, the section occupies sh_size
                     bytes in the file.  A section of type SHT_NOBITS may have a
                     nonzero size, but it occupies no space in the file.
    
       sh_offset     This member's value holds the byte offset from the
                     beginning of the file to the first byte in the section.
                     One section type, SHT_NOBITS, occupies no space in the
                     file, and its sh_offset member locates the conceptual
                     placement in the file.
    
       e_shnum       This member holds the number of entries in the section
                     header table.  Thus the product of e_shentsize and
                     e_shnum gives the section header table's size in bytes.
                     If a file has no section header table, e_shnum holds the
                     value of zero.
    
                     If the number of entries in the section header table is
                     larger than or equal to SHN_LORESERVE (0xff00), e_shnum
                     holds the value zero and the real number of entries in
                     the section header table is held in the sh_size member of
                     the initial entry in section header table.  Otherwise,
                     the sh_size member of the initial entry in the section
                     header table holds the value zero.   
    
       sh_name       This member specifies the name of the section.  Its value
                     is an index into the section header string table section,
                     giving the location of a null-terminated string.
       */
      // next, read the section, string data
      // printf("sh_size = %llu\n", sectHdr.sh_size);
      SectNames = malloc(sectHdr.sh_size);
      fseek(ElfFile, sectHdr.sh_offset, SEEK_SET);
      fread(SectNames, 1, sectHdr.sh_size, ElfFile);
    
      // read all section headers
      for (idx = 0; idx < elfHdr.e_shnum; idx++)
      {
        const char* name = "";
    
        fseek(ElfFile, elfHdr.e_shoff + idx * sizeof(sectHdr), SEEK_SET);
        fread(&sectHdr, 1, sizeof(sectHdr), ElfFile);
    
        // print section name
        if (sectHdr.sh_name);
          name = SectNames + sectHdr.sh_name;
        printf("%2u %s\n", idx, name);
      }
    
      return 0;
    }
    

    Running readelf on a "hello world" binary produces the following output:

    $ readelf -S helloworld
    There are 30 section headers, starting at offset 0x1170:
    
    Section Headers:
      [Nr] Name              Type             Address           Offset
           Size              EntSize          Flags  Link  Info  Align
      [ 0]                   NULL             0000000000000000  00000000
           0000000000000000  0000000000000000           0     0     0
      [ 1] .interp           PROGBITS         0000000000400238  00000238
           000000000000001c  0000000000000000   A       0     0     1
      [ 2] .note.ABI-tag     NOTE             0000000000400254  00000254
           0000000000000020  0000000000000000   A       0     0     4
      [ 3] .note.gnu.build-i NOTE             0000000000400274  00000274
           0000000000000024  0000000000000000   A       0     0     4
      [ 4] .gnu.hash         GNU_HASH         0000000000400298  00000298
           000000000000001c  0000000000000000   A       5     0     8
      [ 5] .dynsym           DYNSYM           00000000004002b8  000002b8
           0000000000000060  0000000000000018   A       6     1     8
      [ 6] .dynstr           STRTAB           0000000000400318  00000318
           000000000000003d  0000000000000000   A       0     0     1
      [ 7] .gnu.version      VERSYM           0000000000400356  00000356
           0000000000000008  0000000000000002   A       5     0     2
      [ 8] .gnu.version_r    VERNEED          0000000000400360  00000360
           0000000000000020  0000000000000000   A       6     1     8
      [ 9] .rela.dyn         RELA             0000000000400380  00000380
           0000000000000018  0000000000000018   A       5     0     8
      [10] .rela.plt         RELA             0000000000400398  00000398
           0000000000000048  0000000000000018   A       5    12     8
      [11] .init             PROGBITS         00000000004003e0  000003e0
           000000000000001a  0000000000000000  AX       0     0     4
      [12] .plt              PROGBITS         0000000000400400  00000400
           0000000000000040  0000000000000010  AX       0     0     16
      [13] .text             PROGBITS         0000000000400440  00000440
           0000000000000182  0000000000000000  AX       0     0     16
      [14] .fini             PROGBITS         00000000004005c4  000005c4
           0000000000000009  0000000000000000  AX       0     0     4
      [15] .rodata           PROGBITS         00000000004005d0  000005d0
           0000000000000013  0000000000000000   A       0     0     4
      [16] .eh_frame_hdr     PROGBITS         00000000004005e4  000005e4
           0000000000000034  0000000000000000   A       0     0     4
      [17] .eh_frame         PROGBITS         0000000000400618  00000618
           00000000000000f4  0000000000000000   A       0     0     8
      [18] .init_array       INIT_ARRAY       0000000000600e10  00000e10
           0000000000000008  0000000000000000  WA       0     0     8
      [19] .fini_array       FINI_ARRAY       0000000000600e18  00000e18
           0000000000000008  0000000000000000  WA       0     0     8
      [20] .jcr              PROGBITS         0000000000600e20  00000e20
           0000000000000008  0000000000000000  WA       0     0     8
      [21] .dynamic          DYNAMIC          0000000000600e28  00000e28
           00000000000001d0  0000000000000010  WA       6     0     8
      [22] .got              PROGBITS         0000000000600ff8  00000ff8
           0000000000000008  0000000000000008  WA       0     0     8
      [23] .got.plt          PROGBITS         0000000000601000  00001000
           0000000000000030  0000000000000008  WA       0     0     8
      [24] .data             PROGBITS         0000000000601030  00001030
           0000000000000010  0000000000000000  WA       0     0     8
      [25] .bss              NOBITS           0000000000601040  00001040
           0000000000000008  0000000000000000  WA       0     0     1
      [26] .comment          PROGBITS         0000000000000000  00001040
           0000000000000024  0000000000000001  MS       0     0     1
      [27] .shstrtab         STRTAB           0000000000000000  00001064
           0000000000000108  0000000000000000           0     0     1
      [28] .symtab           SYMTAB           0000000000000000  000018f0
           0000000000000618  0000000000000018          29    45     8
      [29] .strtab           STRTAB           0000000000000000  00001f08
           000000000000023c  0000000000000000           0     0     1
    Key to Flags:
      W (write), A (alloc), X (execute), M (merge), S (strings), l (large)
      I (info), L (link order), G (group), T (TLS), E (exclude), x (unknown)
      O (extra OS processing required) o (OS specific), p (processor specific)
    

    Examining the following entry:

    [13] .text             PROGBITS         0000000000400440  00000440
               0000000000000182  0000000000000000  AX       0     0     16
    

    in order to retrieve the .text section entirely from the binary is it sufficient to read 0x182 to bytes from the address 0x400440 + 0x440 from the file, where 0x182 is the section size and 0x400440 is the address and 0x440 is the offset? Also, what is the role of the alignment(0x16) here?

    • Wyzard
      Wyzard about 9 years
      Consider using the BFD library instead of implementing the ELF format yourself.
    • peterh
      peterh about 9 years
      Your question was closed unfairly, unfortunately it is not uncommon here. I suggest a next try on stackoverflow.com, maybe people there won't be so hostile as here.
    • Sebi
      Sebi about 9 years
      @peterh I did. Here is the answer: stackoverflow.com/questions/29052125/…