diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/analysis/segment-contains-section.c | 26 | ||||
-rw-r--r-- | src/elfhandle.c | 74 | ||||
-rw-r--r-- | src/lookup/first-section-in-segment.c | 34 | ||||
-rw-r--r-- | src/lookup/last-section-in-segment.c | 49 | ||||
-rw-r--r-- | src/lookup/section-by-name.c | 39 | ||||
-rw-r--r-- | src/lookup/section-name.c | 22 | ||||
-rw-r--r-- | src/main.c | 74 | ||||
-rw-r--r-- | src/options.c | 81 | ||||
-rw-r--r-- | src/printing/header.c | 62 | ||||
-rw-r--r-- | src/printing/sections.c | 60 | ||||
-rw-r--r-- | src/printing/segments.c | 76 |
11 files changed, 597 insertions, 0 deletions
diff --git a/src/analysis/segment-contains-section.c b/src/analysis/segment-contains-section.c new file mode 100644 index 0000000..dc3269d --- /dev/null +++ b/src/analysis/segment-contains-section.c @@ -0,0 +1,26 @@ +#include <libelf.h> +#include <gelf.h> + +#include <libelfu/libelfu.h> + + +ELFU_BOOL elfu_segmentContainsSection(GElf_Phdr *phdr, Elf_Scn *scn) +{ + GElf_Shdr shdr; + + + if (gelf_getshdr(scn, &shdr) != &shdr) { + return ELFU_ERROR; + } + + size_t secStart = shdr.sh_offset; + size_t secEnd = shdr.sh_offset + shdr.sh_size; + size_t segStart = phdr->p_offset; + size_t segEnd = phdr->p_offset + phdr->p_memsz; + + if (secStart < segStart || secEnd > segEnd) { + return ELFU_FALSE; + } + + return ELFU_TRUE; +} diff --git a/src/elfhandle.c b/src/elfhandle.c new file mode 100644 index 0000000..a17216d --- /dev/null +++ b/src/elfhandle.c @@ -0,0 +1,74 @@ +#include <errno.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <stdio.h> +#include <string.h> +#include <unistd.h> + +#include <libelf.h> + +#include "elfhandle.h" + + +void openElf(ELFHandles *h, char *fn, Elf_Cmd elfmode) +{ + int openflags = 0; + int openmode = 0; + + h->e = NULL; + + switch(elfmode) { + case ELF_C_READ: + openflags = O_RDONLY; + break; + case ELF_C_WRITE: + openflags = O_WRONLY | O_CREAT; + openmode = S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH; + break; + case ELF_C_RDWR: + openflags = O_RDWR; + break; + default: + return; + } + + + h->fd = open(fn, openflags, openmode); + if (h->fd < 0) { + fprintf(stderr, "Cannot open %s: %s\n", fn, strerror(errno)); + return; + } + + h->e = elf_begin(h->fd, elfmode, NULL); + if (!h->e) { + fprintf(stderr, "elf_begin() failed on %s: %s\n", fn, elf_errmsg(-1)); + goto ERR; + } + + if (elfmode == ELF_C_READ || elfmode == ELF_C_RDWR) { + if (elf_kind(h->e) != ELF_K_ELF) { + fprintf(stderr, "Not an ELF object: %s", fn); + goto ERR; + } + } + + return; + +ERR: + if (h->e) { + elf_end(h->e); + h->e = NULL; + } + close(h->fd); +} + + +void closeElf(ELFHandles *h) +{ + if (h->e) { + elf_end(h->e); + close(h->fd); + h->e = NULL; + } +} diff --git a/src/lookup/first-section-in-segment.c b/src/lookup/first-section-in-segment.c new file mode 100644 index 0000000..b66c518 --- /dev/null +++ b/src/lookup/first-section-in-segment.c @@ -0,0 +1,34 @@ +#include <libelf.h> +#include <gelf.h> + +#include <libelfu/libelfu.h> + + +/* + * Returns the section that starts at the same point in the file as + * the segment AND is wholly contained in the memory image. + * + * If no section fits, NULL is returned. + */ +Elf_Scn* elfu_firstSectionInSegment(Elf *e, GElf_Phdr *phdr) +{ + Elf_Scn *scn; + + scn = elf_getscn(e, 1); + while (scn) { + GElf_Shdr shdr; + + if (gelf_getshdr(scn, &shdr) != &shdr) { + return NULL; + } + + if (shdr.sh_offset == phdr->p_offset + && elfu_segmentContainsSection(phdr, scn) == ELFU_TRUE) { + return scn; + } + + scn = elf_nextscn(e, scn); + } + + return NULL; +} diff --git a/src/lookup/last-section-in-segment.c b/src/lookup/last-section-in-segment.c new file mode 100644 index 0000000..0e78263 --- /dev/null +++ b/src/lookup/last-section-in-segment.c @@ -0,0 +1,49 @@ +#include <libelf.h> +#include <gelf.h> + +#include <libelfu/libelfu.h> + + +/* + * Returns the first section that is contained in the segment and + * ends as close to its memory image of as possible (the "last" + * section in the segment). + * + * If no section fits, NULL is returned. + */ +Elf_Scn* elfu_lastSectionInSegment(Elf *e, GElf_Phdr *phdr) +{ + Elf_Scn *last = NULL; + Elf_Scn *scn; + + + scn = elf_getscn(e, 1); + while (scn) { + if (elfu_segmentContainsSection(phdr, scn) == ELFU_TRUE) { + if (!last) { + last = scn; + } else { + GElf_Shdr shdrOld; + GElf_Shdr shdrNew; + + if (gelf_getshdr(last, &shdrOld) != &shdrOld) { + continue; + } + + if (gelf_getshdr(scn, &shdrNew) != &shdrNew) { + continue; + } + + if (shdrNew.sh_offset + shdrNew.sh_size + > shdrOld.sh_offset + shdrOld.sh_size) { + // TODO: Check (leftover space in memory image) < (p_align) + last = scn; + } + } + } + + scn = elf_nextscn(e, scn); + } + + return last; +} diff --git a/src/lookup/section-by-name.c b/src/lookup/section-by-name.c new file mode 100644 index 0000000..1697140 --- /dev/null +++ b/src/lookup/section-by-name.c @@ -0,0 +1,39 @@ +#include <string.h> + +#include <libelf.h> +#include <gelf.h> + +#include <libelfu/libelfu.h> + + +Elf_Scn* elfu_sectionByName(Elf *e, char *name) +{ + size_t shstrndx; + Elf_Scn *scn; + + if (elf_getshdrstrndx(e, &shstrndx) != 0) { + return NULL; + } + + scn = elf_getscn(e, 1); + while (scn) { + GElf_Shdr shdr; + char *curname; + + if (gelf_getshdr(scn, &shdr) != &shdr) { + return NULL; + } + + /* elf_strptr returns NULL if there was an error */ + curname = elf_strptr(e, shstrndx, shdr.sh_name); + + /* strcmp... but we really have no bounds on the lengths here */ + if (!strcmp(curname, name)) { + return scn; + } + + scn = elf_nextscn(e, scn); + } + + return NULL; +} diff --git a/src/lookup/section-name.c b/src/lookup/section-name.c new file mode 100644 index 0000000..d3748f9 --- /dev/null +++ b/src/lookup/section-name.c @@ -0,0 +1,22 @@ +#include <libelf.h> +#include <gelf.h> + +#include <libelfu/libelfu.h> + + +char* elfu_sectionName(Elf *e, Elf_Scn *scn) +{ + size_t shstrndx; + GElf_Shdr shdr; + + if (elf_getshdrstrndx(e, &shstrndx) != 0) { + return NULL; + } + + if (gelf_getshdr(scn, &shdr) != &shdr) { + return NULL; + } + + /* elf_strptr returns NULL if there was an error */ + return elf_strptr(e, shstrndx, shdr.sh_name); +} diff --git a/src/main.c b/src/main.c new file mode 100644 index 0000000..9791feb --- /dev/null +++ b/src/main.c @@ -0,0 +1,74 @@ +#include <stdio.h> +#include <stdlib.h> + +#include <getopt.h> +#include <libelf.h> +#include <gelf.h> + +#include "elfhandle.h" +#include "options.h" +#include "printing.h" + + +int main(int argc, char **argv) +{ + CLIOpts opts = { 0 }; + ELFHandles hIn = { 0 }; + ELFHandles hOut = { 0 }; + int exitval = EXIT_SUCCESS; + + /* Is libelf alive and well? */ + if (elf_version(EV_CURRENT) == EV_NONE) { + fprintf(stderr, "libelf init error: %s\n", elf_errmsg(-1)); + } + + + /* Parse and validate user input */ + parseOptions(&opts, argc, argv); + + + /* Open input/output files */ + openElf(&hIn, opts.fnInput, ELF_C_READ); + if (!hIn.e) { + exitval = EXIT_FAILURE; + goto EXIT; + } + + if (opts.fnOutput) { + openElf(&hOut, opts.fnOutput, ELF_C_WRITE); + if (!hOut.e) { + exitval = EXIT_FAILURE; + goto EXIT; + } + } + + + /* Now that we have a (hopefully) sane environment, execute commands */ + if (opts.printHeader) { + printHeader(hIn.e); + } + + if (opts.printSegments) { + printSegments(hIn.e); + } + + if (opts.printSections) { + printSections(hIn.e); + } + + + +EXIT: + if (hOut.e) { + if (elf_update(hOut.e, ELF_C_WRITE) < 0) { + fprintf(stderr, "elf_update() failed: %s\n", elf_errmsg(-1)); + } + closeElf(&hOut); + } + + if (hIn.e) { + closeElf(&hIn); + } + + return (exitval); +} diff --git a/src/options.c b/src/options.c new file mode 100644 index 0000000..014a1f2 --- /dev/null +++ b/src/options.c @@ -0,0 +1,81 @@ +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <getopt.h> + +#include "options.h" + + +static void printUsage(char *progname) +{ + printf("Usage: %s [OPTIONS] <elf-file>\n", progname); + printf("\n" + "Options:\n" + " -h, --help Print this help message\n" + " -o, --output Where to write the modified ELF file to\n" + "\n" + " --print-header Print ELF header\n" + " --print-segments Print program headers\n" + " --print-sections Print sections\n" + "\n"); +} + + + +void parseOptions(CLIOpts *opts, int argc, char **argv) +{ + char *progname = argv[0]; + int c; + int option_index = 0; + + static struct option long_options[] = { + {"help", 0, 0, 'h'}, + {"output", 1, 0, 'o'}, + {"print-header", 0, 0, 10001}, + {"print-segments", 0, 0, 10002}, + {"print-sections", 0, 0, 10003}, + {NULL, 0, NULL, 0} + }; + + while ((c = getopt_long(argc, argv, "e:ho:", + long_options, &option_index)) != -1) { + switch (c) { + case 'h': + printUsage(progname); + exit(EXIT_SUCCESS); + case 'o': + opts->fnOutput = optarg; + break; + case 10001: + opts->printHeader = 1; + break; + case 10002: + opts->printSegments = 1; + break; + case 10003: + opts->printSections = 1; + break; + case '?': + default: + goto USAGE; + } + } + + while (optind < argc) { + if (!opts->fnInput) { + opts->fnInput = argv[optind]; + } + optind++; + } + + if (!opts->fnInput) { + fprintf(stderr, "Error: No input file specified.\n\n"); + goto USAGE; + } + + return; + +USAGE: + printUsage(progname); + exit(1); +} diff --git a/src/printing/header.c b/src/printing/header.c new file mode 100644 index 0000000..33f7bdb --- /dev/null +++ b/src/printing/header.c @@ -0,0 +1,62 @@ +#include <stdio.h> + +#include <libelf.h> +#include <gelf.h> + + +void printHeader(Elf *e) +{ + GElf_Ehdr ehdr; + int elfclass; + size_t shdrnum, shdrstrndx, phdrnum; + + + printf("ELF header:\n"); + + + elfclass = gelf_getclass(e); + if (elfclass == ELFCLASSNONE) { + fprintf(stderr, "getclass() failed: %s\n", elf_errmsg(-1)); + } + printf(" * %d-bit ELF object\n", elfclass == ELFCLASS32 ? 32 : 64); + + + if (!gelf_getehdr(e, &ehdr)) { + fprintf(stderr, "getehdr() failed: %s.", elf_errmsg(-1)); + return; + } + + printf(" * type machine version entry phoff shoff flags ehsize phentsize shentsize\n"); + printf(" %8jx %8jx %8jx %8jx %8jx %8jx %8jx %8jx %9jx %9jx\n", + (uintmax_t) ehdr.e_type, + (uintmax_t) ehdr.e_machine, + (uintmax_t) ehdr.e_version, + (uintmax_t) ehdr.e_entry, + (uintmax_t) ehdr.e_phoff, + (uintmax_t) ehdr.e_shoff, + (uintmax_t) ehdr.e_flags, + (uintmax_t) ehdr.e_ehsize, + (uintmax_t) ehdr.e_phentsize, + (uintmax_t) ehdr.e_shentsize); + + + if (elf_getshdrnum(e, &shdrnum) != 0) { + fprintf(stderr, "getshdrnum() failed: %s\n", elf_errmsg(-1)); + } else { + printf(" * (shnum): %u\n", shdrnum); + } + + if (elf_getshdrstrndx(e, &shdrstrndx) != 0) { + fprintf(stderr, "getshdrstrndx() failed: %s\n", elf_errmsg(-1)); + } else { + printf(" * (shstrndx): %u\n", shdrstrndx); + } + + if (elf_getphdrnum(e, &phdrnum) != 0) { + fprintf(stderr, "getphdrnum() failed: %s\n", elf_errmsg(-1)); + } else { + printf(" * (phnum): %u\n", phdrnum); + } + + printf("\n"); +} diff --git a/src/printing/sections.c b/src/printing/sections.c new file mode 100644 index 0000000..6241a73 --- /dev/null +++ b/src/printing/sections.c @@ -0,0 +1,60 @@ +#include <stdio.h> + +#include <libelf.h> +#include <gelf.h> + +#include <libelfu/libelfu.h> +#include "printing.h" + + +void printSegmentsWithSection(Elf *e, Elf_Scn *scn) +{ + GElf_Phdr phdr; + int i; + size_t n; + + + if (elf_getphdrnum(e, &n)) { + fprintf(stderr, "elf_getphdrnum() failed: %s\n", elf_errmsg(-1)); + return; + } + + for (i = 0; i < n; i++) { + ELFU_BOOL isInSeg; + + if (gelf_getphdr(e, i, &phdr) != &phdr) { + fprintf(stderr, "getphdr() failed for #%d: %s\n", i, elf_errmsg(-1)); + continue; + } + + isInSeg = elfu_segmentContainsSection(&phdr, scn); + if (isInSeg == ELFU_TRUE) { + printf(" %d %s\n", i, segmentTypeStr(phdr.p_type)); + } + } +} + + +void printSection(Elf *e, Elf_Scn *scn) +{ + printf(" %jd: %s\n", + (uintmax_t) elf_ndxscn(scn), + elfu_sectionName(e, scn)); +} + + +void printSections(Elf *e) +{ + Elf_Scn *scn; + + printf("Sections:\n"); + + scn = elf_getscn(e, 0); + + while (scn) { + printSection(e, scn); + //printSegmentsWithSection(e, scn); + + scn = elf_nextscn(e, scn); + } +} diff --git a/src/printing/segments.c b/src/printing/segments.c new file mode 100644 index 0000000..6d8cac7 --- /dev/null +++ b/src/printing/segments.c @@ -0,0 +1,76 @@ +#include <stdio.h> + +#include <libelf.h> +#include <gelf.h> + +#include <libelfu/libelfu.h> + + +static char *ptstr[] = {"NULL", "LOAD", "DYNAMIC", "INTERP", "NOTE", "SHLIB", "PHDR", "TLS", "NUM"}; + + +char* segmentTypeStr(size_t pt) +{ + if (pt >= 0 && pt <= PT_NUM) { + return ptstr[pt]; + } + + return "-?-"; +} + + +void printSectionsInSegment(Elf *e, GElf_Phdr *phdr) +{ + Elf_Scn *scn; + + scn = elf_getscn(e, 0); + + while (scn) { + ELFU_BOOL isInSeg; + + isInSeg = elfu_segmentContainsSection(phdr, scn); + if (isInSeg == ELFU_TRUE) { + printf(" %10u %s\n", elf_ndxscn(scn), elfu_sectionName(e, scn)); + } + + scn = elf_nextscn(e, scn); + } +} + + +void printSegments(Elf *e) +{ + size_t i, n; + + if (elf_getphdrnum(e, &n)) { + fprintf(stderr, "elf_getphdrnum() failed: %s.", elf_errmsg(-1)); + } + + printf("Segments:\n"); + printf(" # typeStr type offset vaddr paddr filesz memsz flags align\n"); + + for (i = 0; i < n; i++) { + GElf_Phdr phdr; + + if (gelf_getphdr(e, i, &phdr) != &phdr) { + fprintf(stderr, "getphdr() failed for #%d: %s.", i, elf_errmsg(-1)); + continue; + } + + printf(" * %4d: %8s ", i, segmentTypeStr(phdr.p_type)); + + printf("%8jx %8jx %8jx %8jx %8jx %8jx %8jx %8jx\n", + (uintmax_t) phdr.p_type, + (uintmax_t) phdr.p_offset, + (uintmax_t) phdr.p_vaddr, + (uintmax_t) phdr.p_paddr, + (uintmax_t) phdr.p_filesz, + (uintmax_t) phdr.p_memsz, + (uintmax_t) phdr.p_flags, + (uintmax_t) phdr.p_align); + + printSectionsInSegment(e, &phdr); + } + + printf("\n"); +} |