diff options
Diffstat (limited to 'libpe')
| -rw-r--r-- | libpe/Makefile | 32 | ||||
| -rw-r--r-- | libpe/_libpe.h | 213 | ||||
| -rw-r--r-- | libpe/libpe.h | 121 | ||||
| -rw-r--r-- | libpe/libpe_buffer.c | 185 | ||||
| -rw-r--r-- | libpe/libpe_coff.c | 535 | ||||
| -rw-r--r-- | libpe/libpe_dos.c | 403 | ||||
| -rw-r--r-- | libpe/libpe_init.c | 145 | ||||
| -rw-r--r-- | libpe/libpe_rich.c | 128 | ||||
| -rw-r--r-- | libpe/libpe_section.c | 518 | ||||
| -rw-r--r-- | libpe/libpe_utils.c | 69 | ||||
| -rw-r--r-- | libpe/os.Linux.mk | 6 | ||||
| -rw-r--r-- | libpe/os.NetBSD.mk | 2 | ||||
| -rw-r--r-- | libpe/pe.h | 292 | ||||
| -rw-r--r-- | libpe/pe_buffer.c | 100 | ||||
| -rw-r--r-- | libpe/pe_cntl.c | 62 | ||||
| -rw-r--r-- | libpe/pe_coff.c | 157 | ||||
| -rw-r--r-- | libpe/pe_dos.c | 119 | ||||
| -rw-r--r-- | libpe/pe_flag.c | 187 | ||||
| -rw-r--r-- | libpe/pe_init.c | 95 | ||||
| -rw-r--r-- | libpe/pe_rich.c | 107 | ||||
| -rw-r--r-- | libpe/pe_section.c | 213 | ||||
| -rw-r--r-- | libpe/pe_symtab.c | 86 | ||||
| -rw-r--r-- | libpe/pe_update.c | 86 |
23 files changed, 3861 insertions, 0 deletions
diff --git a/libpe/Makefile b/libpe/Makefile new file mode 100644 index 000000000000..d02fb5080ef6 --- /dev/null +++ b/libpe/Makefile @@ -0,0 +1,32 @@ +# $Id: Makefile 3349 2016-01-18 21:09:16Z jkoshy $ + +TOP= ${.CURDIR}/.. + +LIB= pe + +SRCS= libpe_buffer.c \ + libpe_coff.c \ + libpe_dos.c \ + libpe_init.c \ + libpe_rich.c \ + libpe_section.c \ + libpe_utils.c \ + pe_buffer.c \ + pe_cntl.c \ + pe_coff.c \ + pe_dos.c \ + pe_flag.c \ + pe_init.c \ + pe_rich.c \ + pe_section.c \ + pe_symtab.c \ + pe_update.c + +INCS= libpe.h pe.h +INCSDIR= /usr/include + +SHLIB_MAJOR= 1 + +WARNS?= 6 + +.include "${TOP}/mk/elftoolchain.lib.mk" diff --git a/libpe/_libpe.h b/libpe/_libpe.h new file mode 100644 index 000000000000..1a83a674194a --- /dev/null +++ b/libpe/_libpe.h @@ -0,0 +1,213 @@ +/*- + * Copyright (c) 2015 Kai Wang + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $Id: _libpe.h 3312 2016-01-10 09:23:51Z kaiwang27 $ + */ + +#ifndef __LIBPE_H_ +#define __LIBPE_H_ + +#include <sys/types.h> +#include <sys/queue.h> + +#include "libpe.h" + +#include "_elftc.h" + +typedef struct _PE_SecBuf { + PE_Buffer sb_pb; /* application buffer */ + PE_Scn *sb_ps; /* PE_Scn pointer */ + unsigned int sb_flags; /* buffer flags */ + STAILQ_ENTRY(_PE_SecBuf) sb_next; +} PE_SecBuf; + +struct _PE_Scn { + PE *ps_pe; /* PE descriptor */ + PE_SecHdr ps_sh; /* section header */ + unsigned int ps_ndx; /* 1-based section index */ + unsigned int ps_flags; /* section flags */ + unsigned int ps_falign; /* section file alignment */ + STAILQ_HEAD(, _PE_SecBuf) ps_b; /* buffer list */ + STAILQ_ENTRY(_PE_Scn) ps_next; +}; + +struct _PE { + int pe_fd; /* file descriptor */ + PE_Cmd pe_cmd; /* open mode */ + PE_Object pe_obj; /* PE32/PE32+/COFF */ + size_t pe_fsize; /* file size */ + unsigned int pe_flags; /* library flags */ + PE_DosHdr *pe_dh; /* MS-DOS header */ + char *pe_stub; /* MS-DOS stub */ + size_t pe_stub_ex; /* MS-DOS stub len (exclude hdr) */ + char *pe_stub_app; /* MS-DOS stub (app supplied) */ + size_t pe_stub_app_sz; /* MS-DOS stub len (app supplied) */ + PE_RichHdr *pe_rh; /* rich header */ + char *pe_rh_start; /* pointer to rich header */ + PE_CoffHdr *pe_ch; /* COFF header */ + PE_OptHdr *pe_oh; /* optional header */ + PE_DataDir *pe_dd; /* data directories */ + unsigned int pe_nscn; /* num. of sections */ + char *pe_symtab; /* COFF symbol table */ + size_t pe_symbtab_sz; /* size of symbol table */ + unsigned int pe_nsym; /* num. of symbols */ + unsigned int pe_rvamax; /* maximum RVA */ + STAILQ_HEAD(, _PE_Scn) pe_scn; /* section list */ +}; + +/* Library internal flags */ +#define LIBPE_F_API_MASK 0x000FFFU +#define LIBPE_F_SPECIAL_FILE 0x001000U +#define LIBPE_F_BAD_DOS_HEADER 0x002000U +#define LIBPE_F_BAD_PE_HEADER 0x004000U +#define LIBPE_F_BAD_COFF_HEADER 0x008000U +#define LIBPE_F_BAD_OPT_HEADER 0x010000U +#define LIBPE_F_BAD_SEC_HEADER 0x020000U +#define LIBPE_F_LOAD_DOS_STUB 0x040000U +#define LIBPE_F_FD_DONE 0x080000U +#define LIBPE_F_DIRTY_DOS_HEADER 0x100000U +#define LIBPE_F_DIRTY_COFF_HEADER 0x200000U +#define LIBPE_F_DIRTY_OPT_HEADER 0x400000U +#define LIBPE_F_DIRTY_SEC_HEADER 0x800000U + +/* Internal section flags */ +#define LIBPE_F_LOAD_SECTION 0x1000U +#define LIBPE_F_STRIP_SECTION 0x2000U + +/* Internal buffer flags */ +#define LIBPE_F_BUFFER_MALLOCED 0x1000U + +/* Library internal defines */ +#define PE_DOS_MAGIC 0x5a4dU +#define PE_RICH_TEXT "Rich" +#define PE_RICH_HIDDEN 0x536e6144U /* DanS */ +#define PE_SIGNATURE 0x4550U /* PE\0\0 */ +#define PE_COFF_OPT_SIZE_32 224 +#define PE_COFF_OPT_SIZE_32P 240 +#define PE_SYM_ENTRY_SIZE 18 + +/* Encode/Decode macros */ +#if defined(ELFTC_NEED_BYTEORDER_EXTENSIONS) +static __inline uint16_t +le16dec(const void *pp) +{ + unsigned char const *p = (unsigned char const *)pp; + + return ((p[1] << 8) | p[0]); +} + +static __inline uint32_t +le32dec(const void *pp) +{ + unsigned char const *p = (unsigned char const *)pp; + + return ((p[3] << 24) | (p[2] << 16) | (p[1] << 8) | p[0]); +} + +static __inline uint64_t +le64dec(const void *pp) +{ + unsigned char const *p = (unsigned char const *)pp; + + return (((uint64_t)le32dec(p + 4) << 32) | le32dec(p)); +} + +static __inline void +le16enc(void *pp, uint16_t u) +{ + unsigned char *p = (unsigned char *)pp; + + p[0] = u & 0xff; + p[1] = (u >> 8) & 0xff; +} + +static __inline void +le32enc(void *pp, uint32_t u) +{ + unsigned char *p = (unsigned char *)pp; + + p[0] = u & 0xff; + p[1] = (u >> 8) & 0xff; + p[2] = (u >> 16) & 0xff; + p[3] = (u >> 24) & 0xff; +} + +static __inline void +le64enc(void *pp, uint64_t u) +{ + unsigned char *p = (unsigned char *)pp; + + le32enc(p, (uint32_t)(u & 0xffffffffU)); + le32enc(p + 4, (uint32_t)(u >> 32)); +} +#endif /* ELFTC_NEED_BYTEORDER_EXTENSIONS */ + +#define PE_READ16(p,v) do { \ + (v) = le16dec((p)); \ + (p) += 2; \ +} while(0) + +#define PE_READ32(p,v) do { \ + (v) = le32dec((p)); \ + (p) += 4; \ +} while(0) + +#define PE_WRITE16(p,v) do { \ + le16enc((p), (v)); \ + (p) += 2; \ +} while(0) + +#define PE_WRITE32(p,v) do { \ + le32enc((p), (v)); \ + (p) += 4; \ +} while(0) + + +/* Internal function declarations */ +off_t libpe_align(PE *, off_t, size_t); +PE_SecBuf *libpe_alloc_buffer(PE_Scn *, size_t); +PE_Scn *libpe_alloc_scn(PE *); +int libpe_load_all_sections(PE *); +int libpe_load_section(PE *, PE_Scn *); +int libpe_open_object(PE *); +int libpe_pad(PE *, size_t); +int libpe_parse_msdos_header(PE *, char *); +int libpe_parse_coff_header(PE *, char *); +int libpe_parse_rich_header(PE *); +int libpe_parse_section_headers(PE *); +int libpe_read_msdos_stub(PE *); +void libpe_release_buffer(PE_SecBuf *); +void libpe_release_object(PE *); +void libpe_release_scn(PE_Scn *); +size_t libpe_resync_buffers(PE_Scn *); +int libpe_resync_sections(PE *, off_t); +int libpe_write_buffers(PE_Scn *); +off_t libpe_write_coff_header(PE *, off_t); +off_t libpe_write_msdos_stub(PE *, off_t); +off_t libpe_write_pe_header(PE *, off_t); +off_t libpe_write_sections(PE *, off_t); +off_t libpe_write_section_headers(PE *, off_t); + +#endif /* !__LIBPE_H_ */ diff --git a/libpe/libpe.h b/libpe/libpe.h new file mode 100644 index 000000000000..3cec39a8f522 --- /dev/null +++ b/libpe/libpe.h @@ -0,0 +1,121 @@ +/*- + * Copyright (c) 2015 Kai Wang + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $Id: libpe.h 3312 2016-01-10 09:23:51Z kaiwang27 $ + */ + +#ifndef _LIBPE_H_ +#define _LIBPE_H_ + +#include <sys/types.h> + +#include "pe.h" + +/* Library private data structures */ +typedef struct _PE PE; +typedef struct _PE_Scn PE_Scn; + +/* Section buffers */ +typedef struct PE_Buffer { + unsigned int pb_align; + off_t pb_off; + size_t pb_size; + void *pb_buf; +} PE_Buffer; + +/* Object types */ +typedef enum { + PE_O_UNKNOWN = 0, + PE_O_PE32, + PE_O_PE32P, + PE_O_COFF, +} PE_Object; + +/* Commands */ +typedef enum { + PE_C_NULL = 0, + PE_C_CLR, + PE_C_FDDONE, + PE_C_FDREAD, + PE_C_RDWR, + PE_C_READ, + PE_C_SET, + PE_C_WRITE, + PE_C_NUM +} PE_Cmd; + +/* Flags defined by the API. */ +#define PE_F_DIRTY 0x001U +#define PE_F_STRIP_DOS_STUB 0x002U +#define PE_F_STRIP_RICH_HEADER 0x004U +#define PE_F_STRIP_SYMTAB 0x008U +#define PE_F_STRIP_DEBUG 0x010U +#define PE_F_STRIP_SECTION 0x020U + +#ifdef __cplusplus +extern "C" { +#endif + +PE_CoffHdr *pe_coff_header(PE *); +int pe_cntl(PE *, PE_Cmd); +PE_DataDir *pe_data_dir(PE *); +void pe_finish(PE *); +int pe_flag(PE *, PE_Cmd, unsigned int); +int pe_flag_buffer(PE_Buffer *, PE_Cmd, unsigned int); +int pe_flag_coff_header(PE *, PE_Cmd, unsigned int); +int pe_flag_data_dir(PE *, PE_Cmd, unsigned int); +int pe_flag_dos_header(PE *, PE_Cmd, unsigned int); +int pe_flag_opt_header(PE *, PE_Cmd, unsigned int); +int pe_flag_section_header(PE_Scn *, PE_Cmd, unsigned int); +int pe_flag_scn(PE_Scn *, PE_Cmd, unsigned int); +PE_Buffer *pe_getbuffer(PE_Scn *, PE_Buffer *); +PE_Scn *pe_getscn(PE *, size_t); +PE *pe_init(int, PE_Cmd, PE_Object); +PE_Scn *pe_insertscn(PE *, size_t); +PE_DosHdr *pe_msdos_header(PE *); +char *pe_msdos_stub(PE *, size_t *); +size_t pe_ndxscn(PE_Scn *); +PE_Buffer *pe_newbuffer(PE_Scn *); +PE_Scn *pe_newscn(PE *); +PE_Scn *pe_nextscn(PE *, PE_Scn *); +PE_Object pe_object(PE *); +PE_OptHdr *pe_opt_header(PE *); +PE_RichHdr *pe_rich_header(PE *); +int pe_rich_header_validate(PE *); +PE_SecHdr *pe_section_header(PE_Scn *); +off_t pe_update(PE *); +int pe_update_coff_header(PE *, PE_CoffHdr *); +int pe_update_opt_header(PE *, PE_OptHdr *); +int pe_update_data_dir(PE *, PE_DataDir *); +int ps_update_msdos_header(PE *, PE_DosHdr *); +int ps_update_msdos_stub(PE *, char *, size_t); +int pe_update_section_header(PE_Scn *, PE_SecHdr *); +int pe_update_symtab(PE *, char *, size_t, unsigned int); + +#ifdef __cplusplus +} +#endif + +#endif /* !_LIBPE_H_ */ diff --git a/libpe/libpe_buffer.c b/libpe/libpe_buffer.c new file mode 100644 index 000000000000..cc633dd04ee9 --- /dev/null +++ b/libpe/libpe_buffer.c @@ -0,0 +1,185 @@ +/*- + * Copyright (c) 2016 Kai Wang + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include <sys/param.h> +#include <assert.h> +#include <errno.h> +#include <stdlib.h> +#include <unistd.h> + +#include "_libpe.h" + +ELFTC_VCSID("$Id: libpe_buffer.c 3312 2016-01-10 09:23:51Z kaiwang27 $"); + +PE_SecBuf * +libpe_alloc_buffer(PE_Scn *ps, size_t sz) +{ + PE_SecBuf *sb; + + if ((sb = malloc(sizeof(PE_SecBuf))) == NULL) { + errno = ENOMEM; + return (NULL); + } + + sb->sb_ps = ps; + sb->sb_flags = 0; + sb->sb_pb.pb_align = 1; + sb->sb_pb.pb_off = 0; + sb->sb_pb.pb_size = sz; + if (sz > 0) { + if ((sb->sb_pb.pb_buf = malloc(sz)) == NULL) { + free(sb); + errno = ENOMEM; + return (NULL); + } + sb->sb_flags |= LIBPE_F_BUFFER_MALLOCED; + } else + sb->sb_pb.pb_buf = NULL; + + STAILQ_INSERT_TAIL(&ps->ps_b, sb, sb_next); + + return (sb); +} + +void +libpe_release_buffer(PE_SecBuf *sb) +{ + PE_Scn *ps; + + assert(sb != NULL); + + ps = sb->sb_ps; + + STAILQ_REMOVE(&ps->ps_b, sb, _PE_SecBuf, sb_next); + + if (sb->sb_flags & LIBPE_F_BUFFER_MALLOCED) + free(sb->sb_pb.pb_buf); + + free(sb); +} + +static int +cmp_sb(PE_SecBuf *a, PE_SecBuf *b) +{ + + if (a->sb_pb.pb_off < b->sb_pb.pb_off) + return (-1); + else if (a->sb_pb.pb_off == b->sb_pb.pb_off) + return (0); + else + return (1); +} + +static void +sort_buffers(PE_Scn *ps) +{ + + if (STAILQ_EMPTY(&ps->ps_b)) + return; + + STAILQ_SORT(&ps->ps_b, _PE_SecBuf, sb_next, cmp_sb); +} + +size_t +libpe_resync_buffers(PE_Scn *ps) +{ + PE_SecBuf *sb; + PE_Buffer *pb; + size_t sz; + + assert(ps->ps_flags & LIBPE_F_LOAD_SECTION); + + sort_buffers(ps); + + sz = 0; + STAILQ_FOREACH(sb, &ps->ps_b, sb_next) { + if (ps->ps_flags & PE_F_DIRTY) + sb->sb_flags |= PE_F_DIRTY; + + pb = (PE_Buffer *) sb; + if (pb->pb_align > ps->ps_falign) + pb->pb_align = ps->ps_falign; + if (pb->pb_buf == NULL || pb->pb_size == 0) + continue; + + sz = roundup(sz, pb->pb_align); + + if (pb->pb_off != (off_t) sz) { + pb->pb_off = sz; + sb->sb_flags |= PE_F_DIRTY; + } + sz += pb->pb_size; + } + + return (sz); +} + +int +libpe_write_buffers(PE_Scn *ps) +{ + PE *pe; + PE_SecBuf *sb; + PE_Buffer *pb; + off_t off; + + assert(ps->ps_flags & LIBPE_F_LOAD_SECTION); + + pe = ps->ps_pe; + + off = 0; + STAILQ_FOREACH(sb, &ps->ps_b, sb_next) { + pb = &sb->sb_pb; + if (pb->pb_buf == NULL || pb->pb_size == 0) + continue; + + if ((sb->sb_flags & PE_F_DIRTY) == 0) { + assert((pe->pe_flags & LIBPE_F_SPECIAL_FILE) == 0); + if (lseek(pe->pe_fd, (off_t) pb->pb_size, SEEK_CUR) < + 0) { + errno = EIO; + return (-1); + } + goto next_buf; + } + + if (pb->pb_off > off) { + if (libpe_pad(pe, pb->pb_off - off) < 0) + return (-1); + off = pb->pb_off; + } + + if (write(pe->pe_fd, pb->pb_buf, pb->pb_size) != + (ssize_t) pb->pb_size) { + errno = EIO; + return (-1); + } + + next_buf: + off += pb->pb_size; + } + + return (0); +} diff --git a/libpe/libpe_coff.c b/libpe/libpe_coff.c new file mode 100644 index 000000000000..e161f7c71864 --- /dev/null +++ b/libpe/libpe_coff.c @@ -0,0 +1,535 @@ +/*- + * Copyright (c) 2015 Kai Wang + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include <sys/param.h> +#include <assert.h> +#include <errno.h> +#include <stdlib.h> +#include <string.h> +#include <time.h> +#include <unistd.h> + +#include "_libpe.h" + +ELFTC_VCSID("$Id: libpe_coff.c 3326 2016-01-16 17:46:17Z kaiwang27 $"); + +int +libpe_parse_coff_header(PE *pe, char *hdr) +{ + char tmp[128]; + PE_CoffHdr *ch; + PE_OptHdr *oh; + PE_DataDir *dd; + unsigned p, r, s; + int i; + + if ((ch = malloc(sizeof(PE_CoffHdr))) == NULL) { + errno = ENOMEM; + return (-1); + } + + PE_READ16(hdr, ch->ch_machine); + PE_READ16(hdr, ch->ch_nsec); + PE_READ32(hdr, ch->ch_timestamp); + PE_READ32(hdr, ch->ch_symptr); + PE_READ32(hdr, ch->ch_nsym); + PE_READ16(hdr, ch->ch_optsize); + PE_READ16(hdr, ch->ch_char); + + pe->pe_ch = ch; + + /* + * The Optional header is omitted for object files. + */ + if (ch->ch_optsize == 0) + return (libpe_parse_section_headers(pe)); + + if ((oh = calloc(1, sizeof(PE_OptHdr))) == NULL) { + errno = ENOMEM; + return (-1); + } + pe->pe_oh = oh; + +#define READ_OPT(n) \ + do { \ + /* \ + * Since the Optional Header size is variable, we must \ + * check if the requested read size will overrun the \ + * remaining header bytes. \ + */ \ + if (p + (n) > ch->ch_optsize) { \ + /* Consume the "extra" bytes */ \ + r = ch->ch_optsize - p; \ + if (read(pe->pe_fd, tmp, r) != (ssize_t) r) { \ + pe->pe_flags |= LIBPE_F_BAD_SEC_HEADER;\ + return (0); \ + } \ + return (libpe_parse_section_headers(pe)); \ + } \ + if (read(pe->pe_fd, tmp, (n)) != (ssize_t) (n)) { \ + pe->pe_flags |= LIBPE_F_BAD_OPT_HEADER; \ + return (0); \ + } \ + p += (n); \ + } while (0) +#define READ_OPT8(v) do { READ_OPT(1); (v) = *tmp; } while(0) +#define READ_OPT16(v) do { READ_OPT(2); (v) = le16dec(tmp); } while(0) +#define READ_OPT32(v) do { READ_OPT(4); (v) = le32dec(tmp); } while(0) +#define READ_OPT64(v) do { READ_OPT(8); (v) = le64dec(tmp); } while(0) + + /* + * Read in the Optional header. Size of some fields are depending + * on the PE format specified by the oh_magic field. (PE32 or PE32+) + */ + + p = 0; + READ_OPT16(oh->oh_magic); + if (oh->oh_magic == PE_FORMAT_32P) + pe->pe_obj = PE_O_PE32P; + READ_OPT8(oh->oh_ldvermajor); + READ_OPT8(oh->oh_ldverminor); + READ_OPT32(oh->oh_textsize); + READ_OPT32(oh->oh_datasize); + READ_OPT32(oh->oh_bsssize); + READ_OPT32(oh->oh_entry); + READ_OPT32(oh->oh_textbase); + if (oh->oh_magic != PE_FORMAT_32P) { + READ_OPT32(oh->oh_database); + READ_OPT32(oh->oh_imgbase); + } else + READ_OPT64(oh->oh_imgbase); + READ_OPT32(oh->oh_secalign); + READ_OPT32(oh->oh_filealign); + READ_OPT16(oh->oh_osvermajor); + READ_OPT16(oh->oh_osverminor); + READ_OPT16(oh->oh_imgvermajor); + READ_OPT16(oh->oh_imgverminor); + READ_OPT16(oh->oh_subvermajor); + READ_OPT16(oh->oh_subverminor); + READ_OPT32(oh->oh_win32ver); + READ_OPT32(oh->oh_imgsize); + READ_OPT32(oh->oh_hdrsize); + READ_OPT32(oh->oh_checksum); + READ_OPT16(oh->oh_subsystem); + READ_OPT16(oh->oh_dllchar); + if (oh->oh_magic != PE_FORMAT_32P) { + READ_OPT32(oh->oh_stacksizer); + READ_OPT32(oh->oh_stacksizec); + READ_OPT32(oh->oh_heapsizer); + READ_OPT32(oh->oh_heapsizec); + } else { + READ_OPT64(oh->oh_stacksizer); + READ_OPT64(oh->oh_stacksizec); + READ_OPT64(oh->oh_heapsizer); + READ_OPT64(oh->oh_heapsizec); + } + READ_OPT32(oh->oh_ldrflags); + READ_OPT32(oh->oh_ndatadir); + + /* + * Read in the Data Directories. + */ + + if (oh->oh_ndatadir > 0) { + if ((dd = calloc(1, sizeof(PE_DataDir))) == NULL) { + errno = ENOMEM; + return (-1); + } + pe->pe_dd = dd; + + dd->dd_total = oh->oh_ndatadir < PE_DD_MAX ? oh->oh_ndatadir : + PE_DD_MAX; + + for (i = 0; (uint32_t) i < dd->dd_total; i++) { + READ_OPT32(dd->dd_e[i].de_addr); + READ_OPT32(dd->dd_e[i].de_size); + } + } + + /* Consume the remaining bytes in the Optional header, if any. */ + if (ch->ch_optsize > p) { + r = ch->ch_optsize - p; + for (; r > 0; r -= s) { + s = r > sizeof(tmp) ? sizeof(tmp) : r; + if (read(pe->pe_fd, tmp, s) != (ssize_t) s) { + pe->pe_flags |= LIBPE_F_BAD_SEC_HEADER; + return (0); + } + } + } + + return (libpe_parse_section_headers(pe)); +} + +off_t +libpe_write_pe_header(PE *pe, off_t off) +{ + char tmp[4]; + + if (pe->pe_cmd == PE_C_RDWR && + (pe->pe_flags & LIBPE_F_BAD_PE_HEADER) == 0) { + assert(pe->pe_dh != NULL); + off = lseek(pe->pe_fd, (off_t) pe->pe_dh->dh_lfanew + 4, + SEEK_SET); + return (off); + } + + /* + * PE Header should to be aligned on 8-byte boundary according to + * the PE/COFF specification. + */ + if ((off = libpe_align(pe, off, 8)) < 0) + return (-1); + + le32enc(tmp, PE_SIGNATURE); + if (write(pe->pe_fd, tmp, sizeof(tmp)) != (ssize_t) sizeof(tmp)) { + errno = EIO; + return (-1); + } + + off += 4; + + pe->pe_flags &= ~LIBPE_F_BAD_PE_HEADER; + + /* Trigger rewrite for the following headers. */ + pe->pe_flags |= LIBPE_F_DIRTY_COFF_HEADER; + pe->pe_flags |= LIBPE_F_DIRTY_OPT_HEADER; + + return (off); +} + +off_t +libpe_write_coff_header(PE *pe, off_t off) +{ + char tmp[128], *hdr; + PE_CoffHdr *ch; + PE_DataDir *dd; + PE_OptHdr *oh; + PE_Scn *ps; + PE_SecHdr *sh; + unsigned p; + uint32_t reloc_rva, reloc_sz; + int i, reloc; + + reloc = 0; + reloc_rva = reloc_sz = 0; + + if (pe->pe_cmd == PE_C_RDWR) { + assert((pe->pe_flags & LIBPE_F_SPECIAL_FILE) == 0); + + if ((pe->pe_flags & LIBPE_F_DIRTY_COFF_HEADER) == 0 && + (pe->pe_flags & LIBPE_F_BAD_COFF_HEADER) == 0) { + if (lseek(pe->pe_fd, (off_t) sizeof(PE_CoffHdr), + SEEK_CUR) < 0) { + errno = EIO; + return (-1); + } + off += sizeof(PE_CoffHdr); + assert(pe->pe_ch != NULL); + ch = pe->pe_ch; + goto coff_done; + } + + /* lseek(2) to the offset of the COFF header. */ + if (lseek(pe->pe_fd, off, SEEK_SET) < 0) { + errno = EIO; + return (-1); + } + } + + if (pe->pe_ch == NULL) { + if ((ch = calloc(1, sizeof(PE_CoffHdr))) == NULL) { + errno = ENOMEM; + return (-1); + } + pe->pe_ch = ch; + + /* + * Default value for ch_machine if not provided by the + * application. + */ + if (pe->pe_obj == PE_O_PE32P) + ch->ch_machine = IMAGE_FILE_MACHINE_AMD64; + else + ch->ch_machine = IMAGE_FILE_MACHINE_I386; + + } else + ch = pe->pe_ch; + + if (!ch->ch_timestamp) + ch->ch_timestamp = time(NULL); + + if (pe->pe_obj == PE_O_PE32) { + if (!ch->ch_optsize) + ch->ch_optsize = PE_COFF_OPT_SIZE_32; + ch->ch_char |= IMAGE_FILE_EXECUTABLE_IMAGE | + IMAGE_FILE_32BIT_MACHINE; + } else if (pe->pe_obj == PE_O_PE32P) { + if (!ch->ch_optsize) + ch->ch_optsize = PE_COFF_OPT_SIZE_32P; + ch->ch_char |= IMAGE_FILE_EXECUTABLE_IMAGE | + IMAGE_FILE_LARGE_ADDRESS_AWARE; + } else + ch->ch_optsize = 0; + + /* + * COFF line number is deprecated by the PE/COFF + * specification. COFF symbol table is deprecated + * for executables. + */ + ch->ch_char |= IMAGE_FILE_LINE_NUMS_STRIPPED; + if (pe->pe_obj == PE_O_PE32 || pe->pe_obj == PE_O_PE32P) + ch->ch_char |= IMAGE_FILE_LOCAL_SYMS_STRIPPED; + + ch->ch_nsec = pe->pe_nscn; + + STAILQ_FOREACH(ps, &pe->pe_scn, ps_next) { + sh = &ps->ps_sh; + + if (ps->ps_ndx == 0xFFFFFFFFU) { + ch->ch_symptr = sh->sh_rawptr; + ch->ch_nsym = pe->pe_nsym; + } + + if (pe->pe_obj == PE_O_PE32 || pe->pe_obj == PE_O_PE32P) { + if (ps->ps_ndx == (0xFFFF0000 | PE_DD_BASERELOC) || + strncmp(sh->sh_name, ".reloc", strlen(".reloc")) == + 0) { + reloc = 1; + reloc_rva = sh->sh_addr; + reloc_sz = sh->sh_virtsize; + } + } + } + + if (!reloc) + ch->ch_char |= IMAGE_FILE_RELOCS_STRIPPED; + + if (pe->pe_flags & LIBPE_F_BAD_OPT_HEADER) { + if (pe->pe_obj == PE_O_PE32) + ch->ch_optsize = PE_COFF_OPT_SIZE_32; + else if (pe->pe_obj == PE_O_PE32P) + ch->ch_optsize = PE_COFF_OPT_SIZE_32P; + else + ch->ch_optsize = 0; + } + + /* + * Write the COFF header. + */ + hdr = tmp; + PE_WRITE16(hdr, ch->ch_machine); + PE_WRITE16(hdr, ch->ch_nsec); + PE_WRITE32(hdr, ch->ch_timestamp); + PE_WRITE32(hdr, ch->ch_symptr); + PE_WRITE32(hdr, ch->ch_nsym); + PE_WRITE16(hdr, ch->ch_optsize); + PE_WRITE16(hdr, ch->ch_char); + if (write(pe->pe_fd, tmp, sizeof(PE_CoffHdr)) != + (ssize_t) sizeof(PE_CoffHdr)) { + errno = EIO; + return (-1); + } + +coff_done: + off += sizeof(PE_CoffHdr); + pe->pe_flags &= ~LIBPE_F_DIRTY_COFF_HEADER; + pe->pe_flags &= ~LIBPE_F_BAD_COFF_HEADER; + pe->pe_flags |= LIBPE_F_DIRTY_SEC_HEADER; + + if (ch->ch_optsize == 0) + return (off); + + /* + * Write the Optional header. + */ + + if (pe->pe_cmd == PE_C_RDWR) { + if ((pe->pe_flags & LIBPE_F_DIRTY_OPT_HEADER) == 0 && + (pe->pe_flags & LIBPE_F_BAD_OPT_HEADER) == 0) { + if (lseek(pe->pe_fd, (off_t) ch->ch_optsize, + SEEK_CUR) < 0) { + errno = EIO; + return (-1); + } + off += ch->ch_optsize; + return (off); + } + + } + + if (pe->pe_oh == NULL) { + if ((oh = calloc(1, sizeof(PE_OptHdr))) == NULL) { + errno = ENOMEM; + return (-1); + } + pe->pe_oh = oh; + } else + oh = pe->pe_oh; + + if (pe->pe_obj == PE_O_PE32) + oh->oh_magic = PE_FORMAT_32; + else + oh->oh_magic = PE_FORMAT_32P; + + /* + * LinkerVersion should not be less than 2.5, which will cause + * Windows to complain the executable is invalid in some case. + * By default we set LinkerVersion to 2.22 (binutils 2.22) + */ + if (!oh->oh_ldvermajor && !oh->oh_ldverminor) { + oh->oh_ldvermajor = 2; + oh->oh_ldverminor = 22; + } + + /* + * The library always tries to write out all 16 data directories + * but the actual data dir written will depend on ch_optsize. + */ + oh->oh_ndatadir = PE_DD_MAX; + + if (!oh->oh_filealign) + oh->oh_filealign = 0x200; + if (!oh->oh_secalign) + oh->oh_secalign = 0x1000; + oh->oh_hdrsize = roundup(off + ch->ch_optsize + pe->pe_nscn * + sizeof(PE_SecHdr), oh->oh_filealign); + oh->oh_imgsize = roundup(pe->pe_rvamax, oh->oh_secalign); + +#define WRITE_OPT(n) \ + do { \ + /* \ + * Since the Optional Header size is variable, we must \ + * check if the requested write size will overrun the \ + * remaining header bytes. \ + */ \ + if (p + (n) > ch->ch_optsize) { \ + /* Pad the "extra" bytes */ \ + if (libpe_pad(pe, ch->ch_optsize - p) < 0) { \ + errno = EIO; \ + return (-1); \ + } \ + goto opt_done; \ + } \ + if (write(pe->pe_fd, tmp, (n)) != (ssize_t) (n)) { \ + errno = EIO; \ + return (-1); \ + } \ + p += (n); \ + } while (0) +#define WRITE_OPT8(v) do { *tmp = (v); WRITE_OPT(1); } while(0) +#define WRITE_OPT16(v) do { le16enc(tmp, (v)); WRITE_OPT(2); } while(0) +#define WRITE_OPT32(v) do { le32enc(tmp, (v)); WRITE_OPT(4); } while(0) +#define WRITE_OPT64(v) do { le64enc(tmp, (v)); WRITE_OPT(8); } while(0) + + p = 0; + WRITE_OPT16(oh->oh_magic); + if (oh->oh_magic == PE_FORMAT_32P) + pe->pe_obj = PE_O_PE32P; + WRITE_OPT8(oh->oh_ldvermajor); + WRITE_OPT8(oh->oh_ldverminor); + WRITE_OPT32(oh->oh_textsize); + WRITE_OPT32(oh->oh_datasize); + WRITE_OPT32(oh->oh_bsssize); + WRITE_OPT32(oh->oh_entry); + WRITE_OPT32(oh->oh_textbase); + if (oh->oh_magic != PE_FORMAT_32P) { + WRITE_OPT32(oh->oh_database); + WRITE_OPT32(oh->oh_imgbase); + } else + WRITE_OPT64(oh->oh_imgbase); + WRITE_OPT32(oh->oh_secalign); + WRITE_OPT32(oh->oh_filealign); + WRITE_OPT16(oh->oh_osvermajor); + WRITE_OPT16(oh->oh_osverminor); + WRITE_OPT16(oh->oh_imgvermajor); + WRITE_OPT16(oh->oh_imgverminor); + WRITE_OPT16(oh->oh_subvermajor); + WRITE_OPT16(oh->oh_subverminor); + WRITE_OPT32(oh->oh_win32ver); + WRITE_OPT32(oh->oh_imgsize); + WRITE_OPT32(oh->oh_hdrsize); + WRITE_OPT32(oh->oh_checksum); + WRITE_OPT16(oh->oh_subsystem); + WRITE_OPT16(oh->oh_dllchar); + if (oh->oh_magic != PE_FORMAT_32P) { + WRITE_OPT32(oh->oh_stacksizer); + WRITE_OPT32(oh->oh_stacksizec); + WRITE_OPT32(oh->oh_heapsizer); + WRITE_OPT32(oh->oh_heapsizec); + } else { + WRITE_OPT64(oh->oh_stacksizer); + WRITE_OPT64(oh->oh_stacksizec); + WRITE_OPT64(oh->oh_heapsizer); + WRITE_OPT64(oh->oh_heapsizec); + } + WRITE_OPT32(oh->oh_ldrflags); + WRITE_OPT32(oh->oh_ndatadir); + + /* + * Write the Data Directories. + */ + + if (oh->oh_ndatadir > 0) { + if (pe->pe_dd == NULL) { + if ((dd = calloc(1, sizeof(PE_DataDir))) == NULL) { + errno = ENOMEM; + return (-1); + } + pe->pe_dd = dd; + dd->dd_total = PE_DD_MAX; + } else + dd = pe->pe_dd; + + assert(oh->oh_ndatadir <= PE_DD_MAX); + + if (reloc) { + dd->dd_e[PE_DD_BASERELOC].de_addr = reloc_rva; + dd->dd_e[PE_DD_BASERELOC].de_size = reloc_sz; + } + + for (i = 0; (uint32_t) i < dd->dd_total; i++) { + WRITE_OPT32(dd->dd_e[i].de_addr); + WRITE_OPT32(dd->dd_e[i].de_size); + } + } + + /* Pad the remaining bytes in the Optional header, if any. */ + if (ch->ch_optsize > p) { + if (libpe_pad(pe, ch->ch_optsize - p) < 0) { + errno = EIO; + return (-1); + } + } + +opt_done: + off += ch->ch_optsize; + pe->pe_flags &= ~LIBPE_F_DIRTY_OPT_HEADER; + pe->pe_flags &= ~LIBPE_F_BAD_OPT_HEADER; + pe->pe_flags |= LIBPE_F_DIRTY_SEC_HEADER; + + return (off); +} diff --git a/libpe/libpe_dos.c b/libpe/libpe_dos.c new file mode 100644 index 000000000000..a48ad1241597 --- /dev/null +++ b/libpe/libpe_dos.c @@ -0,0 +1,403 @@ +/*- + * Copyright (c) 2015 Kai Wang + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include <sys/param.h> +#include <sys/types.h> +#include <assert.h> +#include <errno.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +#include "_libpe.h" + +ELFTC_VCSID("$Id: libpe_dos.c 3312 2016-01-10 09:23:51Z kaiwang27 $"); + +int +libpe_parse_msdos_header(PE *pe, char *hdr) +{ + PE_DosHdr *dh; + char coff[sizeof(PE_CoffHdr)]; + uint32_t pe_magic; + int i; + + if ((pe->pe_stub = malloc(sizeof(PE_DosHdr))) == NULL) { + errno = ENOMEM; + return (-1); + } + memcpy(pe->pe_stub, hdr, sizeof(PE_DosHdr)); + + if ((dh = malloc(sizeof(*dh))) == NULL) { + errno = ENOMEM; + return (-1); + } + pe->pe_dh = dh; + + /* Read the conventional MS-DOS EXE header. */ + memcpy(dh->dh_magic, hdr, 2); + hdr += 2; + PE_READ16(hdr, dh->dh_lastsize); + PE_READ16(hdr, dh->dh_nblock); + PE_READ16(hdr, dh->dh_nreloc); + PE_READ16(hdr, dh->dh_hdrsize); + PE_READ16(hdr, dh->dh_minalloc); + PE_READ16(hdr, dh->dh_maxalloc); + PE_READ16(hdr, dh->dh_ss); + PE_READ16(hdr, dh->dh_sp); + PE_READ16(hdr, dh->dh_checksum); + PE_READ16(hdr, dh->dh_ip); + PE_READ16(hdr, dh->dh_cs); + PE_READ16(hdr, dh->dh_relocpos); + PE_READ16(hdr, dh->dh_noverlay); + + /* Do not continue if the EXE is not a PE/NE/... (new executable) */ + if (dh->dh_relocpos != 0x40) { + pe->pe_flags |= LIBPE_F_BAD_DOS_HEADER; + return (0); + } + + for (i = 0; i < 4; i++) + PE_READ16(hdr, dh->dh_reserved1[i]); + PE_READ16(hdr, dh->dh_oemid); + PE_READ16(hdr, dh->dh_oeminfo); + for (i = 0; i < 10; i++) + PE_READ16(hdr, dh->dh_reserved2[i]); + PE_READ32(hdr, dh->dh_lfanew); + + /* Check if the e_lfanew pointer is valid. */ + if (dh->dh_lfanew > pe->pe_fsize - 4) { + pe->pe_flags |= LIBPE_F_BAD_DOS_HEADER; + return (0); + } + + if (dh->dh_lfanew < sizeof(PE_DosHdr) && + (pe->pe_flags & LIBPE_F_SPECIAL_FILE)) { + pe->pe_flags |= LIBPE_F_BAD_DOS_HEADER; + return (0); + } + + if (dh->dh_lfanew > sizeof(PE_DosHdr)) { + pe->pe_stub_ex = dh->dh_lfanew - sizeof(PE_DosHdr); + if (pe->pe_flags & LIBPE_F_SPECIAL_FILE) { + /* Read in DOS stub now. */ + if (libpe_read_msdos_stub(pe) < 0) { + pe->pe_flags |= LIBPE_F_BAD_DOS_HEADER; + return (0); + } + } + } + + if ((pe->pe_flags & LIBPE_F_SPECIAL_FILE) == 0) { + /* Jump to the PE header. */ + if (lseek(pe->pe_fd, (off_t) dh->dh_lfanew, SEEK_SET) < 0) { + pe->pe_flags |= LIBPE_F_BAD_PE_HEADER; + return (0); + } + } + + if (read(pe->pe_fd, &pe_magic, 4) != 4 || + htole32(pe_magic) != PE_SIGNATURE) { + pe->pe_flags |= LIBPE_F_BAD_PE_HEADER; + return (0); + } + + if (read(pe->pe_fd, coff, sizeof(coff)) != (ssize_t) sizeof(coff)) { + pe->pe_flags |= LIBPE_F_BAD_COFF_HEADER; + return (0); + } + + return (libpe_parse_coff_header(pe, coff)); +} + +int +libpe_read_msdos_stub(PE *pe) +{ + void *m; + + assert(pe->pe_stub_ex > 0 && + (pe->pe_flags & LIBPE_F_LOAD_DOS_STUB) == 0); + + if ((pe->pe_flags & LIBPE_F_SPECIAL_FILE) == 0) { + if (lseek(pe->pe_fd, (off_t) sizeof(PE_DosHdr), SEEK_SET) < + 0) { + errno = EIO; + goto fail; + } + } + + if ((m = realloc(pe->pe_stub, sizeof(PE_DosHdr) + pe->pe_stub_ex)) == + NULL) { + errno = ENOMEM; + goto fail; + } + pe->pe_stub = m; + + if (read(pe->pe_fd, pe->pe_stub + sizeof(PE_DosHdr), pe->pe_stub_ex) != + (ssize_t) pe->pe_stub_ex) { + errno = EIO; + goto fail; + } + + pe->pe_flags |= LIBPE_F_LOAD_DOS_STUB; + + /* Search for the Rich header embedded just before the PE header. */ + (void) libpe_parse_rich_header(pe); + + return (0); + +fail: + pe->pe_stub_ex = 0; + + return (-1); +} + +/* + * The "standard" MS-DOS stub displaying "This program cannot be run in + * DOS mode". + */ +static const char msdos_stub[] = { + '\x0e','\x1f','\xba','\x0e','\x00','\xb4','\x09','\xcd', + '\x21','\xb8','\x01','\x4c','\xcd','\x21','\x54','\x68', + '\x69','\x73','\x20','\x70','\x72','\x6f','\x67','\x72', + '\x61','\x6d','\x20','\x63','\x61','\x6e','\x6e','\x6f', + '\x74','\x20','\x62','\x65','\x20','\x72','\x75','\x6e', + '\x20','\x69','\x6e','\x20','\x44','\x4f','\x53','\x20', + '\x6d','\x6f','\x64','\x65','\x2e','\x0d','\x0d','\x0a', + '\x24','\x00','\x00','\x00','\x00','\x00','\x00','\x00', +}; + +static void +init_dos_header(PE_DosHdr *dh) +{ + + dh->dh_magic[0] = 'M'; + dh->dh_magic[1] = 'Z'; + dh->dh_lastsize = 144; + dh->dh_nblock = 3; + dh->dh_hdrsize = 4; + dh->dh_maxalloc = 65535; + dh->dh_sp = 184; + dh->dh_relocpos = 0x40; + dh->dh_lfanew = 0x80; +} + +off_t +libpe_write_msdos_stub(PE *pe, off_t off) +{ + PE_DosHdr *dh; + char tmp[sizeof(PE_DosHdr)], *hdr; + off_t d; + int i, strip_rich; + + strip_rich = 0; + + if (pe->pe_cmd == PE_C_RDWR) { + assert((pe->pe_flags & LIBPE_F_SPECIAL_FILE) == 0); + + if (pe->pe_dh != NULL && + (pe->pe_flags & PE_F_STRIP_DOS_STUB)) { + /* + * If we strip MS-DOS stub, everything after it + * needs rewritten. + */ + pe->pe_flags |= LIBPE_F_BAD_PE_HEADER; + goto done; + } + + /* + * lseek(2) to the PE signature if MS-DOS stub is not + * modified. + */ + if (pe->pe_dh != NULL && + (pe->pe_flags & LIBPE_F_DIRTY_DOS_HEADER) == 0 && + (pe->pe_flags & LIBPE_F_BAD_DOS_HEADER) == 0 && + (pe->pe_flags & PE_F_STRIP_RICH_HEADER) == 0) { + if (lseek(pe->pe_fd, + (off_t) (sizeof(PE_DosHdr) + pe->pe_stub_ex), + SEEK_CUR) < 0) { + errno = EIO; + return (-1); + } + off = sizeof(PE_DosHdr) + pe->pe_stub_ex; + goto done; + } + + /* Check if we should strip the Rich header. */ + if (pe->pe_dh != NULL && pe->pe_stub_app == NULL && + (pe->pe_flags & LIBPE_F_BAD_DOS_HEADER) == 0 && + (pe->pe_flags & PE_F_STRIP_RICH_HEADER)) { + if ((pe->pe_flags & LIBPE_F_LOAD_DOS_STUB) == 0) { + (void) libpe_read_msdos_stub(pe); + if (lseek(pe->pe_fd, off, SEEK_SET) < 0) { + errno = EIO; + return (-1); + } + } + if (pe->pe_rh != NULL) { + strip_rich = 1; + pe->pe_flags |= LIBPE_F_DIRTY_DOS_HEADER; + } + } + + /* + * If length of MS-DOS stub will change, Mark the PE + * signature is broken so that the PE signature and the + * headers follow it will be rewritten. + * + * The sections should be loaded now since the stub might + * overwrite the section data. + */ + if ((pe->pe_flags & LIBPE_F_BAD_DOS_HEADER) || + (pe->pe_stub_app != NULL && pe->pe_stub_app_sz != + sizeof(PE_DosHdr) + pe->pe_stub_ex) || strip_rich) { + if (libpe_load_all_sections(pe) < 0) + return (-1); + if (lseek(pe->pe_fd, off, SEEK_SET) < 0) { + errno = EIO; + return (-1); + } + pe->pe_flags |= LIBPE_F_BAD_PE_HEADER; + } + } + + if (pe->pe_flags & PE_F_STRIP_DOS_STUB) + goto done; + + /* Always use application supplied MS-DOS stub, if exists. */ + if (pe->pe_stub_app != NULL && pe->pe_stub_app_sz > 0) { + if (write(pe->pe_fd, pe->pe_stub_app, pe->pe_stub_app_sz) != + (ssize_t) pe->pe_stub_app_sz) { + errno = EIO; + return (-1); + } + off = pe->pe_stub_app_sz; + goto done; + } + + /* + * Write MS-DOS header. + */ + + if (pe->pe_dh == NULL) { + if ((dh = calloc(1, sizeof(PE_DosHdr))) == NULL) { + errno = ENOMEM; + return (-1); + } + pe->pe_dh = dh; + + init_dos_header(dh); + + pe->pe_flags |= LIBPE_F_DIRTY_DOS_HEADER; + } else + dh = pe->pe_dh; + + if (pe->pe_flags & LIBPE_F_BAD_DOS_HEADER) + init_dos_header(dh); + + if (strip_rich) { + d = pe->pe_rh_start - pe->pe_stub; + dh->dh_lfanew = roundup(d, 8); + } + + if ((pe->pe_flags & LIBPE_F_DIRTY_DOS_HEADER) || + (pe->pe_flags & LIBPE_F_BAD_DOS_HEADER)) { + memcpy(tmp, dh->dh_magic, 2); + hdr = tmp + 2; + PE_WRITE16(hdr, dh->dh_lastsize); + PE_WRITE16(hdr, dh->dh_nblock); + PE_WRITE16(hdr, dh->dh_nreloc); + PE_WRITE16(hdr, dh->dh_hdrsize); + PE_WRITE16(hdr, dh->dh_minalloc); + PE_WRITE16(hdr, dh->dh_maxalloc); + PE_WRITE16(hdr, dh->dh_ss); + PE_WRITE16(hdr, dh->dh_sp); + PE_WRITE16(hdr, dh->dh_checksum); + PE_WRITE16(hdr, dh->dh_ip); + PE_WRITE16(hdr, dh->dh_cs); + PE_WRITE16(hdr, dh->dh_relocpos); + PE_WRITE16(hdr, dh->dh_noverlay); + for (i = 0; i < 4; i++) + PE_WRITE16(hdr, dh->dh_reserved1[i]); + PE_WRITE16(hdr, dh->dh_oemid); + PE_WRITE16(hdr, dh->dh_oeminfo); + for (i = 0; i < 10; i++) + PE_WRITE16(hdr, dh->dh_reserved2[i]); + PE_WRITE32(hdr, dh->dh_lfanew); + + if (write(pe->pe_fd, tmp, sizeof(tmp)) != + (ssize_t) sizeof(tmp)) { + errno = EIO; + return (-1); + } + } else { + assert((pe->pe_flags & LIBPE_F_SPECIAL_FILE) == 0); + if (lseek(pe->pe_fd, (off_t) sizeof(PE_DosHdr), SEEK_CUR) < + 0) { + errno = EIO; + return (-1); + } + } + + off = sizeof(PE_DosHdr); + + /* + * Write the MS-DOS stub. + */ + + if (strip_rich) { + assert((pe->pe_flags & LIBPE_F_SPECIAL_FILE) == 0); + assert(pe->pe_stub != NULL && pe->pe_rh_start != NULL); + d = pe->pe_rh_start - pe->pe_stub; + if (lseek(pe->pe_fd, d, SEEK_SET) < 0) { + errno = EIO; + return (-1); + } + off = d; + goto done; + } + + if (pe->pe_cmd == PE_C_RDWR) { + if (lseek(pe->pe_fd, (off_t) pe->pe_stub_ex, SEEK_CUR) < 0) { + errno = EIO; + return (-1); + } + off += pe->pe_stub_ex; + goto done; + } + + if (write(pe->pe_fd, msdos_stub, sizeof(msdos_stub)) != + (ssize_t) sizeof(msdos_stub)) { + errno = EIO; + return (-1); + } + off += sizeof(msdos_stub); + +done: + pe->pe_flags &= ~LIBPE_F_DIRTY_DOS_HEADER; + pe->pe_flags &= ~LIBPE_F_BAD_DOS_HEADER; + + return (off); +} diff --git a/libpe/libpe_init.c b/libpe/libpe_init.c new file mode 100644 index 000000000000..2579774bf0c5 --- /dev/null +++ b/libpe/libpe_init.c @@ -0,0 +1,145 @@ +/*- + * Copyright (c) 2015 Kai Wang + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include <sys/stat.h> +#include <assert.h> +#include <errno.h> +#include <stdlib.h> +#include <unistd.h> + +#include "_libpe.h" + +ELFTC_VCSID("$Id: libpe_init.c 3312 2016-01-10 09:23:51Z kaiwang27 $"); + +int +libpe_open_object(PE *pe) +{ + struct stat sb; + mode_t mode; + char magic[sizeof(PE_DosHdr)]; + + if (fstat(pe->pe_fd, &sb) < 0) + return (-1); + + mode = sb.st_mode; + pe->pe_fsize = (size_t) sb.st_size; + + /* Reject unsupported file types. */ + if (!S_ISREG(mode) && !S_ISCHR(mode) && !S_ISFIFO(mode) && + !S_ISSOCK(mode)) { + errno = EINVAL; + return (-1); + } + + /* Read/Write mode is not supported for non-regular file. */ + if (pe->pe_cmd == PE_C_RDWR && !S_ISREG(mode)) { + errno = EINVAL; + return (-1); + } + + /* The minimal file should at least contain a COFF header. */ + if (S_ISREG(mode) && pe->pe_fsize < sizeof(PE_CoffHdr)) { + errno = ENOENT; + return (-1); + } + + /* + * Search for MS-DOS header or COFF header. + */ + + if (read(pe->pe_fd, magic, 2) != 2) { + errno = EIO; + return (-1); + } + + if (magic[0] == 'M' && magic[1] == 'Z') { + pe->pe_obj = PE_O_PE32; + if (read(pe->pe_fd, &magic[2], sizeof(PE_DosHdr) - 2) != + (ssize_t) sizeof(PE_DosHdr) - 2) { + errno = EIO; + return (-1); + } + return (libpe_parse_msdos_header(pe, magic)); + + } else if (magic[0] == 'P' && magic[1] == 'E') { + if (read(pe->pe_fd, magic, 2) != 2) { + errno = EIO; + return (-1); + } + if (magic[0] == '\0' && magic[1] == '\0') { + pe->pe_obj = PE_O_PE32; + if (read(pe->pe_fd, magic, sizeof(PE_CoffHdr)) != + (ssize_t) sizeof(PE_CoffHdr)) { + errno = EIO; + return (-1); + } + return (libpe_parse_coff_header(pe, magic)); + } + errno = ENOENT; + return (-1); + + } else { + pe->pe_obj = PE_O_COFF; + if (read(pe->pe_fd, &magic[2], sizeof(PE_CoffHdr) - 2) != + (ssize_t) sizeof(PE_CoffHdr) - 2) { + errno = EIO; + return (-1); + } + return (libpe_parse_coff_header(pe, magic)); + } +} + +void +libpe_release_object(PE *pe) +{ + PE_Scn *ps, *_ps; + + if (pe->pe_dh) + free(pe->pe_dh); + + if (pe->pe_rh) { + free(pe->pe_rh->rh_compid); + free(pe->pe_rh->rh_cnt); + free(pe->pe_rh); + } + + if (pe->pe_ch) + free(pe->pe_ch); + + if (pe->pe_oh) + free(pe->pe_oh); + + if (pe->pe_dd) + free(pe->pe_dd); + + if (pe->pe_stub) + free(pe->pe_stub); + + STAILQ_FOREACH_SAFE(ps, &pe->pe_scn, ps_next, _ps) + libpe_release_scn(ps); + + free(pe); +} diff --git a/libpe/libpe_rich.c b/libpe/libpe_rich.c new file mode 100644 index 000000000000..4669a224d004 --- /dev/null +++ b/libpe/libpe_rich.c @@ -0,0 +1,128 @@ +/*- + * Copyright (c) 2015 Kai Wang + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include <assert.h> +#include <errno.h> +#include <stdlib.h> +#include <string.h> + +#include "_libpe.h" + +ELFTC_VCSID("$Id: libpe_rich.c 3312 2016-01-10 09:23:51Z kaiwang27 $"); + +static char * +memfind(char *s, const char *find, size_t slen, size_t flen) +{ + int i; + + if (slen == 0 || flen == 0 || flen > slen) + return (NULL); + + for (i = 0; (size_t) i <= slen - flen; i++) { + if (s[i] != find[0]) + continue; + if (flen == 1) + return (&s[i]); + if (memcmp(&s[i + 1], &find[1], flen - 1) == 0) + return (&s[i]); + } + + return (NULL); +} + +int +libpe_parse_rich_header(PE *pe) +{ + PE_RichHdr *rh; + char *p, *r, *s; + uint32_t x; + int found, i; + + assert(pe->pe_stub != NULL && pe->pe_stub_ex > 0); + + /* Search for the "Rich" keyword to locate the Rich header. */ + s = pe->pe_stub + sizeof(PE_DosHdr); + r = memfind(s, PE_RICH_TEXT, pe->pe_stub_ex, 4); + if (r == NULL || r + 8 > s + pe->pe_stub_ex) { + errno = ENOENT; + return (-1); + } + + if ((rh = calloc(1, sizeof(*rh))) == NULL) { + errno = ENOMEM; + return (-1); + } + + rh->rh_xor = le32dec(r + 4); /* Retrieve the "XOR mask" */ + + /* + * Search for the hidden keyword "DanS" by XOR the dwords before + * the "Rich" keyword with the XOR mask. + */ + found = 0; + for (p = r - 4; p >= s; p -= 4) { + x = le32dec(p) ^ rh->rh_xor; + if (x == PE_RICH_HIDDEN) { + found = 1; + break; + } + } + if (!found) { + free(rh); + errno = ENOENT; + return (-1); + } + + /* + * Found the "DanS" keyword, which is the start of the Rich header. + * The next step is to skip the first 16 bytes (DanS, XOR mask, + * XOR mask, XOR mask) and read the (compid,cnt) tuples. + */ + pe->pe_rh_start = p; + p += 16; + rh->rh_total = (r - p) / 8; + if ((rh->rh_compid = malloc(rh->rh_total * sizeof(*rh->rh_compid))) == + NULL) { + free(rh); + errno = ENOMEM; + return (-1); + } + if ((rh->rh_cnt = malloc(rh->rh_total * sizeof(*rh->rh_cnt))) == + NULL) { + free(rh->rh_compid); + free(rh); + errno = ENOMEM; + return (-1); + } + for (i = 0; (uint32_t) i < rh->rh_total; i++, p += 8) { + rh->rh_compid[i] = le32dec(p) ^ rh->rh_xor; + rh->rh_cnt[i] = le32dec(p + 4) ^ rh->rh_xor; + } + + pe->pe_rh = rh; + + return (0); +} diff --git a/libpe/libpe_section.c b/libpe/libpe_section.c new file mode 100644 index 000000000000..7ff63fbef60f --- /dev/null +++ b/libpe/libpe_section.c @@ -0,0 +1,518 @@ +/*- + * Copyright (c) 2016 Kai Wang + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include <sys/param.h> +#include <assert.h> +#include <errno.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +#include "_libpe.h" + +ELFTC_VCSID("$Id: libpe_section.c 3312 2016-01-10 09:23:51Z kaiwang27 $"); + +PE_Scn * +libpe_alloc_scn(PE *pe) +{ + PE_Scn *ps; + + if ((ps = calloc(1, sizeof(PE_Scn))) == NULL) { + errno = ENOMEM; + return (NULL); + } + STAILQ_INIT(&ps->ps_b); + ps->ps_pe = pe; + + return (ps); +} + +void +libpe_release_scn(PE_Scn *ps) +{ + PE *pe; + PE_SecBuf *sb, *_sb; + + assert(ps != NULL); + + pe = ps->ps_pe; + + STAILQ_REMOVE(&pe->pe_scn, ps, _PE_Scn, ps_next); + + STAILQ_FOREACH_SAFE(sb, &ps->ps_b, sb_next, _sb) + libpe_release_buffer(sb); + + free(ps); +} + +static int +cmp_scn(PE_Scn *a, PE_Scn *b) +{ + + if (a->ps_sh.sh_addr < b->ps_sh.sh_addr) + return (-1); + else if (a->ps_sh.sh_addr == b->ps_sh.sh_addr) + return (0); + else + return (1); +} + +static void +sort_sections(PE *pe) +{ + + if (STAILQ_EMPTY(&pe->pe_scn)) + return; + + /* Sort the list of Scn by RVA in ascending order. */ + STAILQ_SORT(&pe->pe_scn, _PE_Scn, ps_next, cmp_scn); +} + +int +libpe_parse_section_headers(PE *pe) +{ + char tmp[sizeof(PE_SecHdr)], *hdr; + PE_Scn *ps; + PE_SecHdr *sh; + PE_CoffHdr *ch; + PE_DataDir *dd; + int found, i; + + assert(pe->pe_ch != NULL); + + for (i = 0; (uint16_t) i < pe->pe_ch->ch_nsec; i++) { + if (read(pe->pe_fd, tmp, sizeof(PE_SecHdr)) != + (ssize_t) sizeof(PE_SecHdr)) { + pe->pe_flags |= LIBPE_F_BAD_SEC_HEADER; + return (0); + } + + if ((ps = libpe_alloc_scn(pe)) == NULL) + return (-1); + STAILQ_INSERT_TAIL(&pe->pe_scn, ps, ps_next); + ps->ps_ndx = ++pe->pe_nscn; /* Setion index is 1-based */ + sh = &ps->ps_sh; + + /* + * Note that the section name won't be NUL-terminated if + * its length happens to be 8. + */ + memcpy(sh->sh_name, tmp, sizeof(sh->sh_name)); + hdr = tmp + 8; + PE_READ32(hdr, sh->sh_virtsize); + PE_READ32(hdr, sh->sh_addr); + PE_READ32(hdr, sh->sh_rawsize); + PE_READ32(hdr, sh->sh_rawptr); + PE_READ32(hdr, sh->sh_relocptr); + PE_READ32(hdr, sh->sh_lineptr); + PE_READ16(hdr, sh->sh_nreloc); + PE_READ16(hdr, sh->sh_nline); + PE_READ32(hdr, sh->sh_char); + } + + /* + * For all the data directories that don't belong to any section, + * we create pseudo sections for them to make layout easier. + */ + dd = pe->pe_dd; + if (dd != NULL && dd->dd_total > 0) { + for (i = 0; (uint32_t) i < pe->pe_dd->dd_total; i++) { + if (dd->dd_e[i].de_size == 0) + continue; + found = 0; + STAILQ_FOREACH(ps, &pe->pe_scn, ps_next) { + sh = &ps->ps_sh; + if (dd->dd_e[i].de_addr >= sh->sh_addr && + dd->dd_e[i].de_addr + dd->dd_e[i].de_size <= + sh->sh_addr + sh->sh_virtsize) { + found = 1; + break; + } + } + if (found) + continue; + + if ((ps = libpe_alloc_scn(pe)) == NULL) + return (-1); + STAILQ_INSERT_TAIL(&pe->pe_scn, ps, ps_next); + ps->ps_ndx = 0xFFFF0000U | i; + sh = &ps->ps_sh; + sh->sh_rawptr = dd->dd_e[i].de_addr; /* FIXME */ + sh->sh_rawsize = dd->dd_e[i].de_size; + } + } + + /* + * Also consider the COFF symbol table as a pseudo section. + */ + ch = pe->pe_ch; + if (ch->ch_nsym > 0) { + if ((ps = libpe_alloc_scn(pe)) == NULL) + return (-1); + STAILQ_INSERT_TAIL(&pe->pe_scn, ps, ps_next); + ps->ps_ndx = 0xFFFFFFFFU; + sh = &ps->ps_sh; + sh->sh_rawptr = ch->ch_symptr; + sh->sh_rawsize = ch->ch_nsym * PE_SYM_ENTRY_SIZE; + pe->pe_nsym = ch->ch_nsym; + } + + /* PE file headers initialization is complete if we reach here. */ + return (0); +} + +int +libpe_load_section(PE *pe, PE_Scn *ps) +{ + PE_SecHdr *sh; + PE_SecBuf *sb; + size_t sz; + char tmp[4]; + + assert(pe != NULL && ps != NULL); + assert((ps->ps_flags & LIBPE_F_LOAD_SECTION) == 0); + + sh = &ps->ps_sh; + + /* Allocate a PE_SecBuf struct without buffer for empty sections. */ + if (sh->sh_rawsize == 0) { + (void) libpe_alloc_buffer(ps, 0); + ps->ps_flags |= LIBPE_F_LOAD_SECTION; + return (0); + } + + if ((pe->pe_flags & LIBPE_F_SPECIAL_FILE) == 0) { + if (lseek(pe->pe_fd, (off_t) sh->sh_rawptr, SEEK_SET) < 0) { + errno = EIO; + return (-1); + } + } + + if ((sb = libpe_alloc_buffer(ps, sh->sh_rawsize)) == NULL) + return (-1); + + if (read(pe->pe_fd, sb->sb_pb.pb_buf, sh->sh_rawsize) != + (ssize_t) sh->sh_rawsize) { + errno = EIO; + return (-1); + } + + if (ps->ps_ndx == 0xFFFFFFFFU) { + /* + * Index 0xFFFFFFFF indicates this section is a pseudo + * section that contains the COFF symbol table. We should + * read in the string table right after it. + */ + if (read(pe->pe_fd, tmp, sizeof(tmp)) != + (ssize_t) sizeof(tmp)) { + errno = EIO; + return (-1); + } + sz = le32dec(tmp); + + /* + * The minimum value for the size field is 4, which indicates + * there is no string table. + */ + if (sz > 4) { + sz -= 4; + if ((sb = libpe_alloc_buffer(ps, sz)) == NULL) + return (-1); + if (read(pe->pe_fd, sb->sb_pb.pb_buf, sz) != + (ssize_t) sz) { + errno = EIO; + return (-1); + } + } + } + + ps->ps_flags |= LIBPE_F_LOAD_SECTION; + + return (0); +} + +int +libpe_load_all_sections(PE *pe) +{ + PE_Scn *ps; + PE_SecHdr *sh; + unsigned r, s; + off_t off; + char tmp[256]; + + /* Calculate the current offset into the file. */ + off = 0; + if (pe->pe_dh != NULL) + off += pe->pe_dh->dh_lfanew + 4; + if (pe->pe_ch != NULL) + off += sizeof(PE_CoffHdr) + pe->pe_ch->ch_optsize; + + STAILQ_FOREACH(ps, &pe->pe_scn, ps_next) { + if (ps->ps_flags & LIBPE_F_LOAD_SECTION) + continue; + sh = &ps->ps_sh; + + /* + * For special files, we consume the padding in between + * and advance to the section offset. + */ + if (pe->pe_flags & LIBPE_F_SPECIAL_FILE) { + /* Can't go backwards. */ + if (off > sh->sh_rawptr) { + errno = EIO; + return (-1); + } + if (off < sh->sh_rawptr) { + r = sh->sh_rawptr - off; + for (; r > 0; r -= s) { + s = r > sizeof(tmp) ? sizeof(tmp) : r; + if (read(pe->pe_fd, tmp, s) != + (ssize_t) s) { + errno = EIO; + return (-1); + } + } + } + } + + /* Load the section content. */ + if (libpe_load_section(pe, ps) < 0) + return (-1); + } + + return (0); +} + +int +libpe_resync_sections(PE *pe, off_t off) +{ + PE_Scn *ps; + PE_SecHdr *sh; + size_t falign, nsec; + + /* Firstly, sort all sections by their file offsets. */ + sort_sections(pe); + + /* Count the number of sections. */ + nsec = 0; + STAILQ_FOREACH(ps, &pe->pe_scn, ps_next) { + if (ps->ps_flags & LIBPE_F_STRIP_SECTION) + continue; + if (ps->ps_ndx & 0xFFFF0000U) + continue; + nsec++; + } + pe->pe_nscn = nsec; + + /* + * Calculate the file offset for the first section. (`off' is + * currently pointing to the COFF header.) + */ + off += sizeof(PE_CoffHdr); + if (pe->pe_ch != NULL && pe->pe_ch->ch_optsize > 0) + off += pe->pe_ch->ch_optsize; + else { + switch (pe->pe_obj) { + case PE_O_PE32: + off += PE_COFF_OPT_SIZE_32; + break; + case PE_O_PE32P: + off += PE_COFF_OPT_SIZE_32P; + break; + case PE_O_COFF: + default: + break; + } + } + off += nsec * sizeof(PE_SecHdr); + + /* + * Determine the file alignment for sections. + */ + if (pe->pe_oh != NULL && pe->pe_oh->oh_filealign > 0) + falign = pe->pe_oh->oh_filealign; + else { + /* + * Use the default file alignment defined by the + * PE/COFF specification. + */ + if (pe->pe_obj == PE_O_COFF) + falign = 4; + else + falign = 512; + } + + /* + * Step through each section (and pseduo section) and verify + * alignment constraint and overlapping, make adjustment if need. + */ + pe->pe_rvamax = 0; + STAILQ_FOREACH(ps, &pe->pe_scn, ps_next) { + if (ps->ps_flags & LIBPE_F_STRIP_SECTION) + continue; + + sh = &ps->ps_sh; + + if (sh->sh_addr + sh->sh_virtsize > pe->pe_rvamax) + pe->pe_rvamax = sh->sh_addr + sh->sh_virtsize; + + if (ps->ps_ndx & 0xFFFF0000U) + ps->ps_falign = 4; + else + ps->ps_falign = falign; + + off = roundup(off, ps->ps_falign); + + if (off != sh->sh_rawptr) + ps->ps_flags |= PE_F_DIRTY; + + if (ps->ps_flags & PE_F_DIRTY) { + if ((ps->ps_flags & LIBPE_F_LOAD_SECTION) == 0) { + if (libpe_load_section(pe, ps) < 0) + return (-1); + } + sh->sh_rawsize = libpe_resync_buffers(ps); + } + + /* + * Sections only contains uninitialized data should set + * PointerToRawData to zero according to the PE/COFF + * specification. + */ + if (sh->sh_rawsize == 0) + sh->sh_rawptr = 0; + else + sh->sh_rawptr = off; + + off += sh->sh_rawsize; + } + + return (0); +} + +off_t +libpe_write_section_headers(PE *pe, off_t off) +{ + char tmp[sizeof(PE_SecHdr)], *hdr; + PE_Scn *ps; + PE_SecHdr *sh; + + if (pe->pe_flags & LIBPE_F_BAD_SEC_HEADER || pe->pe_nscn == 0) + return (off); + + if ((pe->pe_flags & LIBPE_F_DIRTY_SEC_HEADER) == 0) { + off += sizeof(PE_SecHdr) * pe->pe_ch->ch_nsec; + return (off); + } + + STAILQ_FOREACH(ps, &pe->pe_scn, ps_next) { + if (ps->ps_flags & LIBPE_F_STRIP_SECTION) + continue; + if (ps->ps_ndx & 0xFFFF0000U) + continue; + if ((pe->pe_flags & LIBPE_F_DIRTY_SEC_HEADER) == 0 && + (ps->ps_flags & PE_F_DIRTY) == 0) + goto next_header; + + sh = &ps->ps_sh; + + memcpy(tmp, sh->sh_name, sizeof(sh->sh_name)); + hdr = tmp + 8; + PE_WRITE32(hdr, sh->sh_virtsize); + PE_WRITE32(hdr, sh->sh_addr); + PE_WRITE32(hdr, sh->sh_rawsize); + PE_WRITE32(hdr, sh->sh_rawptr); + PE_WRITE32(hdr, sh->sh_relocptr); + PE_WRITE32(hdr, sh->sh_lineptr); + PE_WRITE16(hdr, sh->sh_nreloc); + PE_WRITE16(hdr, sh->sh_nline); + PE_WRITE32(hdr, sh->sh_char); + + if (write(pe->pe_fd, tmp, sizeof(PE_SecHdr)) != + (ssize_t) sizeof(PE_SecHdr)) { + errno = EIO; + return (-1); + } + + next_header: + off += sizeof(PE_SecHdr); + } + + return (off); +} + +off_t +libpe_write_sections(PE *pe, off_t off) +{ + PE_Scn *ps; + PE_SecHdr *sh; + + if (pe->pe_flags & LIBPE_F_BAD_SEC_HEADER) + return (off); + + STAILQ_FOREACH(ps, &pe->pe_scn, ps_next) { + sh = &ps->ps_sh; + + if (ps->ps_flags & LIBPE_F_STRIP_SECTION) + continue; + + /* Skip empty sections. */ + if (sh->sh_rawptr == 0 || sh->sh_rawsize == 0) + continue; + + /* + * Padding between sections. (padding always written + * in case the the section headers or sections are + * moved or shrinked.) + */ + assert(off <= sh->sh_rawptr); + if (off < sh->sh_rawptr) + libpe_pad(pe, sh->sh_rawptr - off); + + if ((ps->ps_flags & PE_F_DIRTY) == 0) { + assert((pe->pe_flags & LIBPE_F_SPECIAL_FILE) == 0); + if (lseek(pe->pe_fd, + (off_t) (sh->sh_rawptr + sh->sh_rawsize), + SEEK_SET) < 0) { + errno = EIO; + return (-1); + } + off = sh->sh_rawptr + sh->sh_rawsize; + continue; + } + + off = sh->sh_rawptr; + + if (libpe_write_buffers(ps) < 0) + return (-1); + + off += sh->sh_rawsize; + + ps->ps_flags &= ~PE_F_DIRTY; + } + + return (off); +} diff --git a/libpe/libpe_utils.c b/libpe/libpe_utils.c new file mode 100644 index 000000000000..9bc9a54bc4ea --- /dev/null +++ b/libpe/libpe_utils.c @@ -0,0 +1,69 @@ +/*- + * Copyright (c) 2016 Kai Wang + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include <sys/param.h> +#include <assert.h> +#include <errno.h> +#include <string.h> +#include <unistd.h> + +#include "_libpe.h" + +ELFTC_VCSID("$Id: libpe_utils.c 3312 2016-01-10 09:23:51Z kaiwang27 $"); + +off_t +libpe_align(PE *pe, off_t off, size_t align) +{ + off_t n; + + assert(align > 0 && (align & (align - 1)) == 0); + + n = roundup(off, align); + if (n > off) { + if (libpe_pad(pe, n - off) < 0) + return (-1); + } + + return (n); +} + +int +libpe_pad(PE *pe, size_t pad) +{ + char tmp[128]; + size_t s; + + memset(tmp, 0, sizeof(tmp)); + for (; pad > 0; pad -= s) { + s = pad > sizeof(tmp) ? sizeof(tmp) : pad; + if (write(pe->pe_fd, tmp, s) != (ssize_t) s) { + errno = EIO; + return (-1); + } + } + + return (0); +} diff --git a/libpe/os.Linux.mk b/libpe/os.Linux.mk new file mode 100644 index 000000000000..ed5bdf00e835 --- /dev/null +++ b/libpe/os.Linux.mk @@ -0,0 +1,6 @@ +# $Id: os.Linux.mk 3312 2016-01-10 09:23:51Z kaiwang27 $ + +CFLAGS+= -Wall -Wno-unused-parameter -Wstrict-prototypes \ + -Wmissing-prototypes -Wpointer-arith -Wreturn-type \ + -Wcast-qual -Wwrite-strings -Wswitch -Wshadow \ + -Wcast-align -Wunused-parameter diff --git a/libpe/os.NetBSD.mk b/libpe/os.NetBSD.mk new file mode 100644 index 000000000000..ae214e3115c0 --- /dev/null +++ b/libpe/os.NetBSD.mk @@ -0,0 +1,2 @@ +# TODO(#511): Revert after the source tree is -Wconversion clean. +WARNS=5 diff --git a/libpe/pe.h b/libpe/pe.h new file mode 100644 index 000000000000..5b6130e47a7f --- /dev/null +++ b/libpe/pe.h @@ -0,0 +1,292 @@ +/*- + * Copyright (c) 2015 Kai Wang + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $Id: pe.h 3312 2016-01-10 09:23:51Z kaiwang27 $ + */ + +#ifndef _PE_H_ +#define _PE_H_ + +#include <stdint.h> + +/* + * MS-DOS header. + */ + +typedef struct _PE_DosHdr { + char dh_magic[2]; + uint16_t dh_lastsize; + uint16_t dh_nblock; + uint16_t dh_nreloc; + uint16_t dh_hdrsize; + uint16_t dh_minalloc; + uint16_t dh_maxalloc; + uint16_t dh_ss; + uint16_t dh_sp; + uint16_t dh_checksum; + uint16_t dh_ip; + uint16_t dh_cs; + uint16_t dh_relocpos; + uint16_t dh_noverlay; + uint16_t dh_reserved1[4]; + uint16_t dh_oemid; + uint16_t dh_oeminfo; + uint16_t dh_reserved2[10]; + uint32_t dh_lfanew; +} PE_DosHdr; + +/* + * Rich header. + */ + +typedef struct _PE_RichHdr { + uint32_t rh_xor; + uint32_t rh_total; + uint32_t *rh_compid; + uint32_t *rh_cnt; +} PE_RichHdr; + +/* + * COFF header: Machine Types. + */ + +#define IMAGE_FILE_MACHINE_UNKNOWN 0x0 /* not specified */ +#define IMAGE_FILE_MACHINE_AM33 0x1d3 /* Matsushita AM33 */ +#define IMAGE_FILE_MACHINE_AMD64 0x8664 /* x86-64 */ +#define IMAGE_FILE_MACHINE_ARM 0x1c0 /* ARM LE */ +#define IMAGE_FILE_MACHINE_ARMNT 0x1c4 /* ARMv7(or higher) Thumb */ +#define IMAGE_FILE_MACHINE_ARM64 0xaa64 /* ARMv8 64-bit */ +#define IMAGE_FILE_MACHINE_EBC 0xebc /* EFI byte code */ +#define IMAGE_FILE_MACHINE_I386 0x14c /* x86 */ +#define IMAGE_FILE_MACHINE_IA64 0x200 /* IA64 */ +#define IMAGE_FILE_MACHINE_M32R 0x9041 /* Mitsubishi M32R LE */ +#define IMAGE_FILE_MACHINE_MIPS16 0x266 /* MIPS16 */ +#define IMAGE_FILE_MACHINE_MIPSFPU 0x366 /* MIPS with FPU */ +#define IMAGE_FILE_MACHINE_MIPSFPU16 0x466 /* MIPS16 with FPU */ +#define IMAGE_FILE_MACHINE_POWERPC 0x1f0 /* Power PC LE */ +#define IMAGE_FILE_MACHINE_POWERPCFP 0x1f1 /* Power PC floating point */ +#define IMAGE_FILE_MACHINE_R4000 0x166 /* MIPS R4000 LE */ +#define IMAGE_FILE_MACHINE_SH3 0x1a2 /* Hitachi SH3 */ +#define IMAGE_FILE_MACHINE_SH3DSP 0x1a3 /* Hitachi SH3 DSP */ +#define IMAGE_FILE_MACHINE_SH4 0x1a6 /* Hitachi SH4 */ +#define IMAGE_FILE_MACHINE_SH5 0x1a8 /* Hitachi SH5 */ +#define IMAGE_FILE_MACHINE_THUMB 0x1c2 /* ARM or Thumb interworking */ +#define IMAGE_FILE_MACHINE_WCEMIPSV2 0x169 /* MIPS LE WCE v2 */ + +/* + * COFF header: Characteristics + */ + +#define IMAGE_FILE_RELOCS_STRIPPED 0x0001 +#define IMAGE_FILE_EXECUTABLE_IMAGE 0x0002 +#define IMAGE_FILE_LINE_NUMS_STRIPPED 0x0004 +#define IMAGE_FILE_LOCAL_SYMS_STRIPPED 0x0008 +#define IMAGE_FILE_AGGRESSIVE_WS_TRIM 0x0010 +#define IMAGE_FILE_LARGE_ADDRESS_AWARE 0x0020 +#define IMAGE_FILE_BYTES_REVERSED_LO 0x0080 +#define IMAGE_FILE_32BIT_MACHINE 0x0100 +#define IMAGE_FILE_DEBUG_STRIPPED 0x0200 +#define IMAGE_FILE_REMOVABLE_RUN_FROM_SWAP 0x0400 +#define IMAGE_FILE_NET_RUN_FROM_SWAP 0x0800 +#define IMAGE_FILE_SYSTEM 0x1000 +#define IMAGE_FILE_DLL 0x2000 +#define IMAGE_FILE_UP_SYSTEM_ONLY 0x4000 +#define IMAGE_FILE_BYTES_REVERSED_HI 0x8000 + +/* + * COFF Header. + */ + +typedef struct _PE_CoffHdr { + uint16_t ch_machine; + uint16_t ch_nsec; + uint32_t ch_timestamp; + uint32_t ch_symptr; + uint32_t ch_nsym; + uint16_t ch_optsize; + uint16_t ch_char; +} PE_CoffHdr; + + +/* + * Optional Header: Subsystem. + */ + +#define IMAGE_SUBSYSTEM_UNKNOWN 0 +#define IMAGE_SUBSYSTEM_NATIVE 1 +#define IMAGE_SUBSYSTEM_WINDOWS_GUI 2 +#define IMAGE_SUBSYSTEM_WINDOWS_CUI 3 +#define IMAGE_SUBSYSTEM_POSIX_CUI 7 +#define IMAGE_SUBSYSTEM_WINDOWS_CE_GUI 9 +#define IMAGE_SUBSYSTEM_EFI_APPLICATION 10 +#define IMAGE_SUBSYSTEM_EFI_BOOT_SERVICE_DRIVER 11 +#define IMAGE_SUBSYSTEM_EFI_RUNTIME_DRIVER 12 +#define IMAGE_SUBSYSTEM_EFI_ROM 13 +#define IMAGE_SUBSYSTEM_XBOX 14 + +/* + * Optional Header: DLL Characteristics + */ + +#define IMAGE_DLL_CHARACTERISTICS_DYNAMIC_BASE 0x0040 +#define IMAGE_DLL_CHARACTERISTICS_FORCE_INTEGRITY 0x0080 +#define IMAGE_DLL_CHARACTERISTICS_NX_COMPAT 0x0100 +#define IMAGE_DLL_CHARACTERISTICS_NO_ISOLATION 0x0200 +#define IMAGE_DLL_CHARACTERISTICS_NO_SEH 0x0400 +#define IMAGE_DLL_CHARACTERISTICS_NO_BIND 0x0800 +#define IMAGE_DLL_CHARACTERISTICS_WDM_DRIVER 0x2000 +#define IMAGE_DLL_CHARACTERISTICS_TERMINAL_SERVER_AWARE 0x8000 + +/* + * Optional Header. + */ + +#define PE_FORMAT_ROM 0x107 +#define PE_FORMAT_32 0x10b +#define PE_FORMAT_32P 0x20b + +typedef struct _PE_OptHdr { + uint16_t oh_magic; + uint8_t oh_ldvermajor; + uint8_t oh_ldverminor; + uint32_t oh_textsize; + uint32_t oh_datasize; + uint32_t oh_bsssize; + uint32_t oh_entry; + uint32_t oh_textbase; + uint32_t oh_database; + uint64_t oh_imgbase; + uint32_t oh_secalign; + uint32_t oh_filealign; + uint16_t oh_osvermajor; + uint16_t oh_osverminor; + uint16_t oh_imgvermajor; + uint16_t oh_imgverminor; + uint16_t oh_subvermajor; + uint16_t oh_subverminor; + uint32_t oh_win32ver; + uint32_t oh_imgsize; + uint32_t oh_hdrsize; + uint32_t oh_checksum; + uint16_t oh_subsystem; + uint16_t oh_dllchar; + uint64_t oh_stacksizer; + uint64_t oh_stacksizec; + uint64_t oh_heapsizer; + uint64_t oh_heapsizec; + uint32_t oh_ldrflags; + uint32_t oh_ndatadir; +} PE_OptHdr; + +/* + * Optional Header: Data Directories. + */ + +#define PE_DD_EXPORT 0 +#define PE_DD_IMPORT 1 +#define PE_DD_RESROUCE 2 +#define PE_DD_EXCEPTION 3 +#define PE_DD_CERTIFICATE 4 +#define PE_DD_BASERELOC 5 +#define PE_DD_DEBUG 6 +#define PE_DD_ARCH 7 +#define PE_DD_GLOBALPTR 8 +#define PE_DD_TLS 9 +#define PE_DD_LOADCONFIG 10 +#define PE_DD_BOUNDIMPORT 11 +#define PE_DD_IAT 12 +#define PE_DD_DELAYIMPORT 13 +#define PE_DD_CLRRUNTIME 14 +#define PE_DD_RESERVED 15 +#define PE_DD_MAX 16 + +typedef struct _PE_DataDirEntry { + uint32_t de_addr; + uint32_t de_size; +} PE_DataDirEntry; + +typedef struct _PE_DataDir { + PE_DataDirEntry dd_e[PE_DD_MAX]; + uint32_t dd_total; +} PE_DataDir; + +/* + * Section Headers: Section flags. + */ + +#define IMAGE_SCN_TYPE_NO_PAD 0x00000008 +#define IMAGE_SCN_CNT_CODE 0x00000020 +#define IMAGE_SCN_CNT_INITIALIZED_DATA 0x00000040 +#define IMAGE_SCN_CNT_UNINITIALIZED_DATA 0x00000080 +#define IMAGE_SCN_LNK_OTHER 0x00000100 +#define IMAGE_SCN_LNK_INFO 0x00000200 +#define IMAGE_SCN_LNK_REMOVE 0x00000800 +#define IMAGE_SCN_LNK_COMDAT 0x00001000 +#define IMAGE_SCN_GPREL 0x00008000 +#define IMAGE_SCN_MEM_PURGEABLE 0x00020000 +#define IMAGE_SCN_MEM_16BIT 0x00020000 +#define IMAGE_SCN_MEM_LOCKED 0x00040000 +#define IMAGE_SCN_MEM_PRELOAD 0x00080000 +#define IMAGE_SCN_ALIGN_1BYTES 0x00100000 +#define IMAGE_SCN_ALIGN_2BYTES 0x00200000 +#define IMAGE_SCN_ALIGN_4BYTES 0x00300000 +#define IMAGE_SCN_ALIGN_8BYTES 0x00400000 +#define IMAGE_SCN_ALIGN_16BYTES 0x00500000 +#define IMAGE_SCN_ALIGN_32BYTES 0x00600000 +#define IMAGE_SCN_ALIGN_64BYTES 0x00700000 +#define IMAGE_SCN_ALIGN_128BYTES 0x00800000 +#define IMAGE_SCN_ALIGN_256BYTES 0x00900000 +#define IMAGE_SCN_ALIGN_512BYTES 0x00A00000 +#define IMAGE_SCN_ALIGN_1024BYTES 0x00B00000 +#define IMAGE_SCN_ALIGN_2048BYTES 0x00C00000 +#define IMAGE_SCN_ALIGN_4096BYTES 0x00D00000 +#define IMAGE_SCN_ALIGN_8192BYTES 0x00E00000 +#define IMAGE_SCN_LNK_NRELOC_OVFL 0x01000000 +#define IMAGE_SCN_MEM_DISCARDABLE 0x02000000 +#define IMAGE_SCN_MEM_NOT_CACHED 0x04000000 +#define IMAGE_SCN_MEM_NOT_PAGED 0x08000000 +#define IMAGE_SCN_MEM_SHARED 0x10000000 +#define IMAGE_SCN_MEM_EXECUTE 0x20000000 +#define IMAGE_SCN_MEM_READ 0x40000000 +#define IMAGE_SCN_MEM_WRITE 0x80000000 + +/* + * Section Headers. + */ + +typedef struct _PE_SecHdr { + char sh_name[8]; + uint32_t sh_virtsize; + uint32_t sh_addr; + uint32_t sh_rawsize; + uint32_t sh_rawptr; + uint32_t sh_relocptr; + uint32_t sh_lineptr; + uint16_t sh_nreloc; + uint16_t sh_nline; + uint32_t sh_char; +} PE_SecHdr; + +#endif /* !_PE_H_ */ diff --git a/libpe/pe_buffer.c b/libpe/pe_buffer.c new file mode 100644 index 000000000000..e4ac19fa04b8 --- /dev/null +++ b/libpe/pe_buffer.c @@ -0,0 +1,100 @@ +/*- + * Copyright (c) 2016 Kai Wang + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include <errno.h> + +#include "_libpe.h" + +ELFTC_VCSID("$Id: pe_buffer.c 3312 2016-01-10 09:23:51Z kaiwang27 $"); + +PE_Buffer * +pe_getbuffer(PE_Scn *ps, PE_Buffer *pb) +{ + PE *pe; + PE_SecBuf *sb; + + if (ps == NULL) { + errno = EINVAL; + return (NULL); + } + + pe = ps->ps_pe; + + if ((ps->ps_flags & LIBPE_F_LOAD_SECTION) == 0) { + if (pe->pe_flags & LIBPE_F_FD_DONE) { + errno = EACCES; + return (NULL); + } + if (pe->pe_flags & LIBPE_F_SPECIAL_FILE) { + if (libpe_load_all_sections(pe) < 0) + return (NULL); + } else { + if (libpe_load_section(pe, ps) < 0) + return (NULL); + } + } + + sb = (PE_SecBuf *) pb; + + if (sb == NULL) + sb = STAILQ_FIRST(&ps->ps_b); + else + sb = STAILQ_NEXT(sb, sb_next); + + return ((PE_Buffer *) sb); +} + +PE_Buffer * +pe_newbuffer(PE_Scn *ps) +{ + PE *pe; + PE_SecBuf *sb; + + if (ps == NULL) { + errno = EINVAL; + return (NULL); + } + + pe = ps->ps_pe; + + if (pe->pe_flags & LIBPE_F_FD_DONE) { + errno = EACCES; + return (NULL); + } + + if ((ps->ps_flags & LIBPE_F_LOAD_SECTION) == 0) { + if (libpe_load_section(pe, ps) < 0) + return (NULL); + } + + if ((sb = libpe_alloc_buffer(ps, 0)) == NULL) + return (NULL); + + sb->sb_flags |= PE_F_DIRTY; + ps->ps_flags |= PE_F_DIRTY; + + return ((PE_Buffer *) sb); +} diff --git a/libpe/pe_cntl.c b/libpe/pe_cntl.c new file mode 100644 index 000000000000..1fc8c474015e --- /dev/null +++ b/libpe/pe_cntl.c @@ -0,0 +1,62 @@ +/*- + * Copyright (c) 2016 Kai Wang + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include <errno.h> + +#include "_libpe.h" + +ELFTC_VCSID("$Id: pe_cntl.c 3312 2016-01-10 09:23:51Z kaiwang27 $"); + +int +pe_cntl(PE *pe, PE_Cmd cmd) +{ + + if (pe == NULL) { + errno = EINVAL; + return (-1); + } + + switch (cmd) { + case PE_C_FDDONE: + pe->pe_flags |= LIBPE_F_FD_DONE; + break; + + case PE_C_FDREAD: + if (pe->pe_cmd == PE_C_WRITE) { + errno = EACCES; + return (-1); + } + if (libpe_load_all_sections(pe) < 0) + return (-1); + break; + + default: + errno = EINVAL; + return (-1); + } + + return (0); +} diff --git a/libpe/pe_coff.c b/libpe/pe_coff.c new file mode 100644 index 000000000000..d5cd83311a58 --- /dev/null +++ b/libpe/pe_coff.c @@ -0,0 +1,157 @@ +/*- + * Copyright (c) 2015 Kai Wang + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include <errno.h> +#include <stdlib.h> +#include <string.h> + +#include "_libpe.h" + +ELFTC_VCSID("$Id: pe_coff.c 3312 2016-01-10 09:23:51Z kaiwang27 $"); + +PE_CoffHdr * +pe_coff_header(PE *pe) +{ + + if (pe->pe_ch == NULL) { + errno = ENOENT; + return (NULL); + } + + return (pe->pe_ch); +} + +PE_OptHdr * +pe_opt_header(PE *pe) +{ + + if (pe->pe_oh == NULL) { + errno = ENOENT; + return (NULL); + } + + return (pe->pe_oh); +} + +PE_DataDir * +pe_data_dir(PE *pe) +{ + + if (pe->pe_dd == NULL) { + errno = ENOENT; + return (NULL); + } + + return (pe->pe_dd); +} + +int +pe_update_coff_header(PE *pe, PE_CoffHdr *ch) +{ + + if (pe == NULL || ch == NULL) { + errno = EINVAL; + return (-1); + } + + if (pe->pe_cmd == PE_C_READ || pe->pe_flags & LIBPE_F_FD_DONE) { + errno = EACCES; + return (-1); + } + + if (pe->pe_ch == NULL) { + if ((pe->pe_ch = malloc(sizeof(PE_CoffHdr))) == NULL) { + errno = ENOMEM; + return (-1); + } + } else { + /* Rewrite optional header if `optsize' field changed. */ + if (pe->pe_ch->ch_optsize != ch->ch_optsize) + pe->pe_flags |= LIBPE_F_DIRTY_OPT_HEADER; + } + + *pe->pe_ch = *ch; + + pe->pe_flags |= LIBPE_F_DIRTY_COFF_HEADER; + + return (0); +} + +int +pe_update_opt_header(PE *pe, PE_OptHdr *oh) +{ + + if (pe == NULL || oh == NULL) { + errno = EINVAL; + return (-1); + } + + if (pe->pe_cmd == PE_C_READ || pe->pe_flags & LIBPE_F_FD_DONE) { + errno = EACCES; + return (-1); + } + + if (pe->pe_oh == NULL) { + if ((pe->pe_oh = malloc(sizeof(PE_OptHdr))) == NULL) { + errno = ENOMEM; + return (-1); + } + } + + *pe->pe_oh = *oh; + + pe->pe_flags |= LIBPE_F_DIRTY_OPT_HEADER; + + return (0); +} + +int +pe_update_data_dir(PE *pe, PE_DataDir *dd) +{ + + if (pe == NULL || dd == NULL) { + errno = EINVAL; + return (-1); + } + + if (pe->pe_cmd == PE_C_READ || pe->pe_flags & LIBPE_F_FD_DONE) { + errno = EACCES; + return (-1); + } + + if (pe->pe_dd == NULL) { + if ((pe->pe_dd = malloc(sizeof(PE_DataDir))) == NULL) { + errno = ENOMEM; + return (-1); + } + } + + *pe->pe_dd = *dd; + + pe->pe_flags |= LIBPE_F_DIRTY_OPT_HEADER; + + return (0); +} diff --git a/libpe/pe_dos.c b/libpe/pe_dos.c new file mode 100644 index 000000000000..01ba42f44aae --- /dev/null +++ b/libpe/pe_dos.c @@ -0,0 +1,119 @@ +/*- + * Copyright (c) 2015 Kai Wang + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include <assert.h> +#include <errno.h> +#include <stdlib.h> +#include <string.h> + +#include "_libpe.h" + +ELFTC_VCSID("$Id: pe_dos.c 3312 2016-01-10 09:23:51Z kaiwang27 $"); + +PE_DosHdr * +pe_msdos_header(PE *pe) +{ + + if (pe == NULL) { + errno = EINVAL; + return (NULL); + } + + if (pe->pe_dh == NULL) { + errno = ENOENT; + return (NULL); + } + + return (pe->pe_dh); +} + +char * +pe_msdos_stub(PE *pe, size_t *len) +{ + + if (pe == NULL || len == NULL) { + errno = EINVAL; + return (NULL); + } + + if (pe->pe_stub_ex > 0 && + (pe->pe_flags & LIBPE_F_LOAD_DOS_STUB) == 0) { + assert((pe->pe_flags & LIBPE_F_SPECIAL_FILE) == 0); + (void) libpe_read_msdos_stub(pe); + } + + *len = sizeof(PE_DosHdr) + pe->pe_stub_ex; + + return (pe->pe_stub); +} + +int +ps_update_msdos_header(PE *pe, PE_DosHdr *dh) +{ + + if (pe == NULL || dh == NULL) { + errno = EINVAL; + return (-1); + } + + if (pe->pe_cmd == PE_C_READ || pe->pe_flags & LIBPE_F_FD_DONE) { + errno = EACCES; + return (-1); + } + + if (pe->pe_dh == NULL) { + if ((pe->pe_dh = malloc(sizeof(PE_DosHdr))) == NULL) { + errno = ENOMEM; + return (-1); + } + } + + *pe->pe_dh = *dh; + + pe->pe_flags |= LIBPE_F_DIRTY_DOS_HEADER; + + return (0); +} + +int +ps_update_msdos_stub(PE *pe, char *dos_stub, size_t sz) +{ + + if (pe == NULL || dos_stub == NULL || sz == 0) { + errno = EINVAL; + return (-1); + } + + if (pe->pe_cmd == PE_C_READ || pe->pe_flags & LIBPE_F_FD_DONE) { + errno = EACCES; + return (-1); + } + + pe->pe_stub_app = dos_stub; + pe->pe_stub_app_sz = sz; + + return (0); +} diff --git a/libpe/pe_flag.c b/libpe/pe_flag.c new file mode 100644 index 000000000000..c392a4d2eeaf --- /dev/null +++ b/libpe/pe_flag.c @@ -0,0 +1,187 @@ +/*- + * Copyright (c) 2016 Kai Wang + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include <errno.h> + +#include "_libpe.h" + +ELFTC_VCSID("$Id: pe_flag.c 3312 2016-01-10 09:23:51Z kaiwang27 $"); + +int +pe_flag(PE *pe, PE_Cmd c, unsigned int flags) +{ + + if (pe == NULL || (c != PE_C_SET && c != PE_C_CLR)) { + errno = EINVAL; + return (-1); + } + + if ((flags & ~(PE_F_STRIP_DOS_STUB | PE_F_STRIP_RICH_HEADER | + PE_F_STRIP_SYMTAB | PE_F_STRIP_DEBUG)) != 0) { + errno = EINVAL; + return (-1); + } + + if (c == PE_C_SET) + pe->pe_flags |= flags; + else + pe->pe_flags &= ~flags; + + return (0); +} + +int +pe_flag_dos_header(PE *pe, PE_Cmd c, unsigned int flags) +{ + + if (pe == NULL || (c != PE_C_SET && c != PE_C_CLR) || + (flags & ~PE_F_DIRTY) != 0) { + errno = EINVAL; + return (-1); + } + + if (c == PE_C_SET) + pe->pe_flags |= LIBPE_F_DIRTY_DOS_HEADER; + else + pe->pe_flags &= ~LIBPE_F_DIRTY_DOS_HEADER; + + return (0); +} + +int +pe_flag_coff_header(PE *pe, PE_Cmd c, unsigned int flags) +{ + + if (pe == NULL || (c != PE_C_SET && c != PE_C_CLR) || + (flags & ~PE_F_DIRTY) != 0) { + errno = EINVAL; + return (-1); + } + + if (c == PE_C_SET) + pe->pe_flags |= LIBPE_F_DIRTY_COFF_HEADER; + else + pe->pe_flags &= ~LIBPE_F_DIRTY_COFF_HEADER; + + return (0); +} + +int +pe_flag_opt_header(PE *pe, PE_Cmd c, unsigned int flags) +{ + + if (pe == NULL || (c != PE_C_SET && c != PE_C_CLR) || + (flags & ~PE_F_DIRTY) != 0) { + errno = EINVAL; + return (-1); + } + + if (c == PE_C_SET) + pe->pe_flags |= LIBPE_F_DIRTY_OPT_HEADER; + else + pe->pe_flags &= ~LIBPE_F_DIRTY_OPT_HEADER; + + return (0); +} + +int +pe_flag_data_dir(PE *pe, PE_Cmd c, unsigned int flags) +{ + + if (pe == NULL || (c != PE_C_SET && c != PE_C_CLR) || + (flags & ~PE_F_DIRTY) != 0) { + errno = EINVAL; + return (-1); + } + + if (c == PE_C_SET) + pe->pe_flags |= LIBPE_F_DIRTY_OPT_HEADER; + else + pe->pe_flags &= ~LIBPE_F_DIRTY_OPT_HEADER; + + return (0); +} + +int +pe_flag_scn(PE_Scn *ps, PE_Cmd c, unsigned int flags) +{ + + if (ps == NULL || (c != PE_C_SET && c != PE_C_CLR) || + (flags & ~(PE_F_DIRTY | PE_F_STRIP_SECTION)) == 0) { + errno = EINVAL; + return (-1); + } + + if (c == PE_C_SET) + ps->ps_flags |= flags; + else + ps->ps_flags &= ~flags; + + return (0); +} + +int +pe_flag_section_header(PE_Scn *ps, PE_Cmd c, unsigned int flags) +{ + PE *pe; + + if (ps == NULL || (c != PE_C_SET && c != PE_C_CLR) || + (flags & ~PE_F_DIRTY) != 0) { + errno = EINVAL; + return (-1); + } + + pe = ps->ps_pe; + + /* The library doesn't support per section header dirty flag. */ + if (c == PE_C_SET) + pe->pe_flags |= LIBPE_F_DIRTY_SEC_HEADER; + else + pe->pe_flags &= ~LIBPE_F_DIRTY_SEC_HEADER; + + return (0); +} + +int +pe_flag_buffer(PE_Buffer *pb, PE_Cmd c, unsigned int flags) +{ + PE_SecBuf *sb; + + if (pb == NULL || (c != PE_C_SET && c != PE_C_CLR) || + (flags & ~PE_F_DIRTY) != 0) { + errno = EINVAL; + return (-1); + } + + sb = (PE_SecBuf *) pb; + + if (c == PE_C_SET) + sb->sb_flags |= flags; + else + sb->sb_flags &= ~flags; + + return (0); +} diff --git a/libpe/pe_init.c b/libpe/pe_init.c new file mode 100644 index 000000000000..4e2f22a260d2 --- /dev/null +++ b/libpe/pe_init.c @@ -0,0 +1,95 @@ +/*- + * Copyright (c) 2015 Kai Wang + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include <sys/queue.h> +#include <errno.h> +#include <stdlib.h> + +#include "_libpe.h" + +ELFTC_VCSID("$Id: pe_init.c 3312 2016-01-10 09:23:51Z kaiwang27 $"); + +PE * +pe_init(int fd, PE_Cmd c, PE_Object o) +{ + PE *pe; + + if ((pe = calloc(1, sizeof(*pe))) == NULL) { + errno = ENOMEM; + return (NULL); + } + pe->pe_fd = fd; + pe->pe_cmd = c; + pe->pe_obj = o; + STAILQ_INIT(&pe->pe_scn); + + switch (c) { + case PE_C_READ: + case PE_C_RDWR: + if (libpe_open_object(pe) < 0) + goto init_fail; + break; + + case PE_C_WRITE: + if (o < PE_O_PE32 || o > PE_O_COFF) { + errno = EINVAL; + goto init_fail; + } + break; + + default: + errno = EINVAL; + goto init_fail; + } + + return (pe); + +init_fail: + pe_finish(pe); + return (NULL); +} + +void +pe_finish(PE *pe) +{ + + if (pe == NULL) + return; + + libpe_release_object(pe); +} + +PE_Object +pe_object(PE *pe) +{ + + if (pe == NULL) { + errno = EINVAL; + return (PE_O_UNKNOWN); + } + + return (pe->pe_obj); +} diff --git a/libpe/pe_rich.c b/libpe/pe_rich.c new file mode 100644 index 000000000000..ea1029e58f5f --- /dev/null +++ b/libpe/pe_rich.c @@ -0,0 +1,107 @@ +/*- + * Copyright (c) 2016 Kai Wang + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include <assert.h> +#include <errno.h> + +#include "_libpe.h" + +ELFTC_VCSID("$Id: pe_rich.c 3312 2016-01-10 09:23:51Z kaiwang27 $"); + +PE_RichHdr * +pe_rich_header(PE *pe) +{ + + if (pe == NULL) { + errno = EINVAL; + return (NULL); + } + + if (pe->pe_rh == NULL && pe->pe_stub_ex > 0 && + (pe->pe_flags & LIBPE_F_LOAD_DOS_STUB) == 0) { + assert((pe->pe_flags & LIBPE_F_SPECIAL_FILE) == 0); + (void) libpe_read_msdos_stub(pe); + } + + if (pe->pe_rh == NULL) { + errno = ENOENT; + return (NULL); + } + + return (pe->pe_rh); +} + +static uint32_t +rol32(uint32_t n, int c) +{ + + c &= 0x1f; + + return ((n << c) | (n >> (0x20 - c))); +} + +int +pe_rich_header_validate(PE *pe) +{ + PE_RichHdr *rh; + uint32_t cksum; + char *p; + int i, off; + + if (pe_rich_header(pe) == NULL) + return (-1); + + assert(pe->pe_rh_start != NULL); + + /* + * Initial value of the checksum is the offset to the begin of + * the Rich header. + */ + cksum = pe->pe_rh_start - pe->pe_stub; + + /* + * Add the bytes before the Rich header to the checksum, rotated + * left by the offset. + */ + for (p = pe->pe_stub; p < pe->pe_rh_start; p++) { + /* Skip dh_lfanew. */ + off = p - pe->pe_stub; + if (off >= 0x3c && off < 0x40) + continue; + cksum += rol32((unsigned char) *p, off); + } + + /* Add each compid rotated left by its count to the checksum. */ + rh = pe->pe_rh; + for (i = 0; (uint32_t) i < rh->rh_total; i++) + cksum += rol32(rh->rh_compid[i], rh->rh_cnt[i]); + + /* Validate the checksum with the XOR mask stored after "Rich". */ + if (cksum == rh->rh_xor) + return (1); + + return (0); +} diff --git a/libpe/pe_section.c b/libpe/pe_section.c new file mode 100644 index 000000000000..3e82d8480596 --- /dev/null +++ b/libpe/pe_section.c @@ -0,0 +1,213 @@ +/*- + * Copyright (c) 2016 Kai Wang + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include <errno.h> +#include <string.h> + +#include "_libpe.h" + +ELFTC_VCSID("$Id: pe_section.c 3312 2016-01-10 09:23:51Z kaiwang27 $"); + +PE_Scn * +pe_getscn(PE *pe, size_t ndx) +{ + PE_Scn *ps; + + if (pe == NULL || ndx < 1 || ndx > 0xFFFFU) { + errno = EINVAL; + return (NULL); + } + + STAILQ_FOREACH(ps, &pe->pe_scn, ps_next) { + if (ps->ps_ndx == ndx) + return (ps); + } + + errno = ENOENT; + + return (NULL); +} + +size_t +pe_ndxscn(PE_Scn *ps) +{ + + if (ps == NULL) { + errno = EINVAL; + return (0); + } + + return (ps->ps_ndx); +} + +PE_Scn * +pe_nextscn(PE *pe, PE_Scn *ps) +{ + + if (pe == NULL) { + errno = EINVAL; + return (NULL); + } + + if (ps == NULL) + ps = STAILQ_FIRST(&pe->pe_scn); + else + ps = STAILQ_NEXT(ps, ps_next); + + while (ps != NULL) { + if (ps->ps_ndx >= 1 && ps->ps_ndx <= 0xFFFFU) + return (ps); + ps = STAILQ_NEXT(ps, ps_next); + } + + return (NULL); +} + +PE_Scn * +pe_newscn(PE *pe) +{ + PE_Scn *ps, *tps, *_tps; + + if (pe == NULL) { + errno = EINVAL; + return (NULL); + } + + if (pe->pe_cmd == PE_C_READ || pe->pe_flags & LIBPE_F_FD_DONE) { + errno = EACCES; + return (NULL); + } + + if ((ps = libpe_alloc_scn(pe)) == NULL) + return (NULL); + + if (pe->pe_flags & LIBPE_F_BAD_SEC_HEADER) { + STAILQ_FOREACH_SAFE(tps, &pe->pe_scn, ps_next, _tps) + libpe_release_scn(tps); + pe->pe_flags &= ~LIBPE_F_BAD_SEC_HEADER; + } + + STAILQ_INSERT_TAIL(&pe->pe_scn, ps, ps_next); + + ps->ps_flags |= PE_F_DIRTY | LIBPE_F_LOAD_SECTION; + pe->pe_flags |= LIBPE_F_DIRTY_SEC_HEADER; + + return (ps); +} + +PE_Scn * +pe_insertscn(PE *pe, size_t ndx) +{ + PE_Scn *ps, *a, *b; + + if (pe == NULL || ndx < 1 || ndx > 0xFFFFU) { + errno = EINVAL; + return (NULL); + } + + if (pe->pe_cmd == PE_C_READ || pe->pe_flags & LIBPE_F_FD_DONE) { + errno = EACCES; + return (NULL); + } + + if ((ps = libpe_alloc_scn(pe)) == NULL) + return (NULL); + + if (pe->pe_flags & LIBPE_F_BAD_SEC_HEADER) { + STAILQ_FOREACH_SAFE(a, &pe->pe_scn, ps_next, b) + libpe_release_scn(a); + pe->pe_flags &= ~LIBPE_F_BAD_SEC_HEADER; + } + + b = NULL; + STAILQ_FOREACH(a, &pe->pe_scn, ps_next) { + if (a->ps_ndx & 0xFFFF0000U) + continue; + if (a->ps_ndx == ndx) + break; + b = a; + } + + if (a == NULL) { + STAILQ_INSERT_TAIL(&pe->pe_scn, ps, ps_next); + if (b == NULL) + ps->ps_ndx = 1; + else + ps->ps_ndx = b->ps_ndx + 1; + } else if (b == NULL) { + STAILQ_INSERT_HEAD(&pe->pe_scn, ps, ps_next); + ps->ps_ndx = 1; + } else { + STAILQ_INSERT_AFTER(&pe->pe_scn, b, ps, ps_next); + ps->ps_ndx = ndx; + } + + a = ps; + while ((a = STAILQ_NEXT(a, ps_next)) != NULL) { + if ((a->ps_ndx & 0xFFFF0000U) == 0) + a->ps_ndx++; + } + + ps->ps_flags |= PE_F_DIRTY | LIBPE_F_LOAD_SECTION; + pe->pe_flags |= LIBPE_F_DIRTY_SEC_HEADER; + + return (ps); +} + +PE_SecHdr * +pe_section_header(PE_Scn *ps) +{ + + if (ps == NULL) { + errno = EINVAL; + return (NULL); + } + + return (&ps->ps_sh); +} + +int +pe_update_section_header(PE_Scn *ps, PE_SecHdr *sh) +{ + PE *pe; + + if (ps == NULL || sh == NULL) { + errno = EINVAL; + return (-1); + } + + pe = ps->ps_pe; + + if (pe->pe_cmd == PE_C_READ || pe->pe_flags & LIBPE_F_FD_DONE) { + errno = EACCES; + return (-1); + } + + ps->ps_sh = *sh; + pe->pe_flags |= LIBPE_F_DIRTY_SEC_HEADER; + + return (0); +} diff --git a/libpe/pe_symtab.c b/libpe/pe_symtab.c new file mode 100644 index 000000000000..d0e90d14ad3e --- /dev/null +++ b/libpe/pe_symtab.c @@ -0,0 +1,86 @@ +/*- + * Copyright (c) 2016 Kai Wang + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include <errno.h> + +#include "_libpe.h" + +ELFTC_VCSID("$Id: pe_symtab.c 3312 2016-01-10 09:23:51Z kaiwang27 $"); + +int +pe_update_symtab(PE *pe, char *symtab, size_t sz, unsigned int nsym) +{ + PE_Scn *ps; + PE_SecBuf *sb; + PE_SecHdr *sh; + + if (pe == NULL || symtab == NULL || sz == 0) { + errno = EINVAL; + return (-1); + } + + if (pe->pe_cmd == PE_C_READ || pe->pe_flags & LIBPE_F_FD_DONE) { + errno = EACCES; + return (-1); + } + + /* Remove the old symbol table. */ + STAILQ_FOREACH(ps, &pe->pe_scn, ps_next) { + if (ps->ps_ndx == 0xFFFFFFFFU) + libpe_release_scn(ps); + } + + /* + * Insert the new symbol table. + */ + + if ((ps = libpe_alloc_scn(pe)) == NULL) + return (-1); + + STAILQ_INSERT_TAIL(&pe->pe_scn, ps, ps_next); + ps->ps_ndx = 0xFFFFFFFFU; + ps->ps_flags |= PE_F_DIRTY; + + /* + * Set the symbol table section offset to the maximum to make sure + * that it will be placed in the end of the file during section + * layout. + */ + sh = &ps->ps_sh; + sh->sh_rawptr = 0xFFFFFFFFU; + sh->sh_rawsize = sz; + + /* Allocate the buffer. */ + if ((sb = libpe_alloc_buffer(ps, 0)) == NULL) + return (-1); + sb->sb_flags |= PE_F_DIRTY; + sb->sb_pb.pb_size = sz; + sb->sb_pb.pb_buf = symtab; + + pe->pe_nsym = nsym; + + return (0); +} diff --git a/libpe/pe_update.c b/libpe/pe_update.c new file mode 100644 index 000000000000..ec2b2e52492d --- /dev/null +++ b/libpe/pe_update.c @@ -0,0 +1,86 @@ +/*- + * Copyright (c) 2016 Kai Wang + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include <assert.h> +#include <errno.h> +#include <unistd.h> + +#include "_libpe.h" + +ELFTC_VCSID("$Id: pe_update.c 3312 2016-01-10 09:23:51Z kaiwang27 $"); + +off_t +pe_update(PE *pe) +{ + off_t off; + + if (pe == NULL) { + errno = EINVAL; + return (-1); + } + + if (pe->pe_cmd == PE_C_READ || pe->pe_flags & LIBPE_F_FD_DONE) { + errno = EACCES; + return (-1); + } + + if (pe->pe_cmd == PE_C_RDWR || (pe->pe_cmd == PE_C_WRITE && + (pe->pe_flags & LIBPE_F_SPECIAL_FILE) == 0)) { + if (lseek(pe->pe_fd, 0, SEEK_SET) < 0) { + errno = EIO; + return (-1); + } + } + + off = 0; + + if (pe->pe_obj == PE_O_PE32 || pe->pe_obj == PE_O_PE32P) { + if ((off = libpe_write_msdos_stub(pe, off)) < 0) + return (-1); + + if ((off = libpe_write_pe_header(pe, off)) < 0) + return (-1); + } + + if (libpe_resync_sections(pe, off) < 0) + return (-1); + + if ((off = libpe_write_coff_header(pe, off)) < 0) + return (-1); + + if ((off = libpe_write_section_headers(pe, off)) < 0) + return (-1); + + if ((off = libpe_write_sections(pe, off)) < 0) + return (-1); + + if (ftruncate(pe->pe_fd, off) < 0) { + errno = EIO; + return (-1); + } + + return (off); +} |
