From d9eb4398773cbda1dc185f4cf7b1b0e4cb9fb135 Mon Sep 17 00:00:00 2001 From: norly Date: Fri, 25 Jan 2013 15:24:36 +0000 Subject: [PATCH] Print ELF header/segments/sections --- Makefile | 46 ++++++++++++++ include/elfhandle.h | 15 +++++ include/libelfu/analysis.h | 11 ++++ include/libelfu/libelfu.h | 11 ++++ include/libelfu/lookup.h | 14 +++++ include/libelfu/types.h | 10 +++ include/options.h | 16 +++++ include/printing.h | 18 ++++++ src/analysis/segment-contains-section.c | 26 ++++++++ src/elfhandle.c | 74 ++++++++++++++++++++++ src/lookup/first-section-in-segment.c | 34 +++++++++++ src/lookup/last-section-in-segment.c | 49 +++++++++++++++ src/lookup/section-by-name.c | 39 ++++++++++++ src/lookup/section-name.c | 22 +++++++ src/main.c | 74 ++++++++++++++++++++++ src/options.c | 81 +++++++++++++++++++++++++ src/printing/header.c | 62 +++++++++++++++++++ src/printing/sections.c | 60 ++++++++++++++++++ src/printing/segments.c | 76 +++++++++++++++++++++++ 19 files changed, 738 insertions(+) create mode 100644 Makefile create mode 100644 include/elfhandle.h create mode 100644 include/libelfu/analysis.h create mode 100644 include/libelfu/libelfu.h create mode 100644 include/libelfu/lookup.h create mode 100644 include/libelfu/types.h create mode 100644 include/options.h create mode 100644 include/printing.h create mode 100644 src/analysis/segment-contains-section.c create mode 100644 src/elfhandle.c create mode 100644 src/lookup/first-section-in-segment.c create mode 100644 src/lookup/last-section-in-segment.c create mode 100644 src/lookup/section-by-name.c create mode 100644 src/lookup/section-name.c create mode 100644 src/main.c create mode 100644 src/options.c create mode 100644 src/printing/header.c create mode 100644 src/printing/sections.c create mode 100644 src/printing/segments.c diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..df5f59f --- /dev/null +++ b/Makefile @@ -0,0 +1,46 @@ +PROJ := elfedit + +BUILDDIR := build +INCLUDEDIR := include +SRCDIR := src + +EXE := $(BUILDDIR)/$(PROJ) +HEADERS := $(shell find $(INCLUDEDIR)/ -iname "*.h") +HEADERS += $(shell find $(SRCDIR)/ -iname "*.h") + +SOURCES := $(shell find $(SRCDIR)/ -iname "*.c") +OBJS := $(patsubst %.c, $(BUILDDIR)/%.o, $(SOURCES)) + +INCLUDES := $(patsubst %, -I%, $(INCLUDEDIR) $(SRCDIR)) -I /usr/include/libelf +CFLAGS := -g -Wall +LDFLAGS := -lelf + + + +.PHONY: default +default: $(EXE) + + + +$(EXE): $(OBJS) + @if [ ! -d $(BUILDDIR) ] ; then echo "Error: Build dir '$(BUILDDIR)' does not exist." ; false ; fi + gcc $(LDFLAGS) -o $@ $^ + + +$(BUILDDIR)/$(SRCDIR)/%.o: $(SRCDIR)/%.c $(HEADERS) + @if [ ! -d $(dir $@) ] ; then mkdir -p $(dir $@) ; fi + gcc $(INCLUDES) $(CFLAGS) -c -o $@ $< + + +.PHONY: clean +clean: + rm -f $(STATICLIB) + rm -f $(OBJS) + rm -f $(TESTEXES) + rm -rf $(BUILDDIR)/ + + +.PHONY: distclean +distclean: clean + find . -xdev -name "*~" -exec rm {} \; + find . -xdev -name "core" -exec rm {} \; diff --git a/include/elfhandle.h b/include/elfhandle.h new file mode 100644 index 0000000..b1a92ea --- /dev/null +++ b/include/elfhandle.h @@ -0,0 +1,15 @@ +#ifndef __ELFHANDLE_H__ +#define __ELFHANDLE_H__ + +#include + +typedef struct { + int fd; + Elf *e; +} ELFHandles; + + +void openElf(ELFHandles *h, char *fn, Elf_Cmd elfmode); +void closeElf(ELFHandles *h); + +#endif diff --git a/include/libelfu/analysis.h b/include/libelfu/analysis.h new file mode 100644 index 0000000..80ec251 --- /dev/null +++ b/include/libelfu/analysis.h @@ -0,0 +1,11 @@ +#ifndef __LIBELFU_ANALYSIS_H_ +#define __LIBELFU_ANALYSIS_H_ + +#include +#include + +#include + +ELFU_BOOL elfu_segmentContainsSection(GElf_Phdr *phdr, Elf_Scn *scn); + +#endif diff --git a/include/libelfu/libelfu.h b/include/libelfu/libelfu.h new file mode 100644 index 0000000..1c40541 --- /dev/null +++ b/include/libelfu/libelfu.h @@ -0,0 +1,11 @@ +#ifndef __LIBELFU_LIBELFU_H__ +#define __LIBELFU_LIBELFU_H__ + + +#include + +#include +#include + + +#endif diff --git a/include/libelfu/lookup.h b/include/libelfu/lookup.h new file mode 100644 index 0000000..2cca5df --- /dev/null +++ b/include/libelfu/lookup.h @@ -0,0 +1,14 @@ +#ifndef __LIBELFU_LOOKUP_H_ +#define __LIBELFU_LOOKUP_H_ + +#include +#include + +#include + +char* elfu_sectionName(Elf *e, Elf_Scn *scn); +Elf_Scn* elfu_sectionByName(Elf *e, char *name); +Elf_Scn* elfu_firstSectionInSegment(Elf *e, GElf_Phdr *phdr); +Elf_Scn* elfu_lastSectionInSegment(Elf *e, GElf_Phdr *phdr); + +#endif diff --git a/include/libelfu/types.h b/include/libelfu/types.h new file mode 100644 index 0000000..6ffdd84 --- /dev/null +++ b/include/libelfu/types.h @@ -0,0 +1,10 @@ +#ifndef __LIBELFU_TYPES_H__ +#define __LIBELFU_TYPES_H__ + +typedef enum { + ELFU_ERROR = -1, + ELFU_FALSE = 0, + ELFU_TRUE = 1 +} ELFU_BOOL; + +#endif diff --git a/include/options.h b/include/options.h new file mode 100644 index 0000000..f39f01e --- /dev/null +++ b/include/options.h @@ -0,0 +1,16 @@ +#ifndef __OPTIONS_H__ +#define __OPTIONS_H__ + + +typedef struct { + char *fnInput; + char *fnOutput; + int printHeader; + int printSegments; + int printSections; +} CLIOpts; + + +void parseOptions(CLIOpts *opts, int argc, char **argv); + +#endif diff --git a/include/printing.h b/include/printing.h new file mode 100644 index 0000000..bda2fc1 --- /dev/null +++ b/include/printing.h @@ -0,0 +1,18 @@ +#ifndef __PRINTING_H__ +#define __PRINTING_H__ + +#include + + +void printHeader(Elf *e); + +void printSegmentsWithSection(Elf *e, Elf_Scn *scn); +void printSection(Elf *e, Elf_Scn *scn); +void printSections(Elf *e); + +char* segmentTypeStr(size_t pt); +void printSectionsInSegment(Elf *e, GElf_Phdr *phdr); +void printSegments(Elf *e); + + +#endif 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 +#include + +#include + + +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 +#include +#include +#include +#include +#include +#include + +#include + +#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 +#include + +#include + + +/* + * 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 +#include + +#include + + +/* + * 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 + +#include +#include + +#include + + +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 +#include + +#include + + +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 +#include + +#include +#include +#include + +#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 +#include +#include +#include + +#include "options.h" + + +static void printUsage(char *progname) +{ + printf("Usage: %s [OPTIONS] \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 + +#include +#include + + +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 + +#include +#include + +#include +#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 + +#include +#include + +#include + + +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"); +} -- 2.30.2