Path: blob/main/contrib/elftoolchain/libpe/libpe_dos.c
39478 views
/*-1* Copyright (c) 2015 Kai Wang2* All rights reserved.3*4* Redistribution and use in source and binary forms, with or without5* modification, are permitted provided that the following conditions6* are met:7* 1. Redistributions of source code must retain the above copyright8* notice, this list of conditions and the following disclaimer.9* 2. Redistributions in binary form must reproduce the above copyright10* notice, this list of conditions and the following disclaimer in the11* documentation and/or other materials provided with the distribution.12*13* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND14* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE15* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE16* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE17* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL18* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS19* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)20* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT21* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY22* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF23* SUCH DAMAGE.24*/2526#include <sys/param.h>27#include <sys/types.h>28#include <assert.h>29#include <errno.h>30#include <stdlib.h>31#include <string.h>32#include <unistd.h>3334#include "_libpe.h"3536ELFTC_VCSID("$Id: libpe_dos.c 3312 2016-01-10 09:23:51Z kaiwang27 $");3738int39libpe_parse_msdos_header(PE *pe, char *hdr)40{41PE_DosHdr *dh;42char coff[sizeof(PE_CoffHdr)];43uint32_t pe_magic;44int i;4546if ((pe->pe_stub = malloc(sizeof(PE_DosHdr))) == NULL) {47errno = ENOMEM;48return (-1);49}50memcpy(pe->pe_stub, hdr, sizeof(PE_DosHdr));5152if ((dh = malloc(sizeof(*dh))) == NULL) {53errno = ENOMEM;54return (-1);55}56pe->pe_dh = dh;5758/* Read the conventional MS-DOS EXE header. */59memcpy(dh->dh_magic, hdr, 2);60hdr += 2;61PE_READ16(hdr, dh->dh_lastsize);62PE_READ16(hdr, dh->dh_nblock);63PE_READ16(hdr, dh->dh_nreloc);64PE_READ16(hdr, dh->dh_hdrsize);65PE_READ16(hdr, dh->dh_minalloc);66PE_READ16(hdr, dh->dh_maxalloc);67PE_READ16(hdr, dh->dh_ss);68PE_READ16(hdr, dh->dh_sp);69PE_READ16(hdr, dh->dh_checksum);70PE_READ16(hdr, dh->dh_ip);71PE_READ16(hdr, dh->dh_cs);72PE_READ16(hdr, dh->dh_relocpos);73PE_READ16(hdr, dh->dh_noverlay);7475/* Do not continue if the EXE is not a PE/NE/... (new executable) */76if (dh->dh_relocpos != 0x40) {77pe->pe_flags |= LIBPE_F_BAD_DOS_HEADER;78return (0);79}8081for (i = 0; i < 4; i++)82PE_READ16(hdr, dh->dh_reserved1[i]);83PE_READ16(hdr, dh->dh_oemid);84PE_READ16(hdr, dh->dh_oeminfo);85for (i = 0; i < 10; i++)86PE_READ16(hdr, dh->dh_reserved2[i]);87PE_READ32(hdr, dh->dh_lfanew);8889/* Check if the e_lfanew pointer is valid. */90if (dh->dh_lfanew > pe->pe_fsize - 4) {91pe->pe_flags |= LIBPE_F_BAD_DOS_HEADER;92return (0);93}9495if (dh->dh_lfanew < sizeof(PE_DosHdr) &&96(pe->pe_flags & LIBPE_F_SPECIAL_FILE)) {97pe->pe_flags |= LIBPE_F_BAD_DOS_HEADER;98return (0);99}100101if (dh->dh_lfanew > sizeof(PE_DosHdr)) {102pe->pe_stub_ex = dh->dh_lfanew - sizeof(PE_DosHdr);103if (pe->pe_flags & LIBPE_F_SPECIAL_FILE) {104/* Read in DOS stub now. */105if (libpe_read_msdos_stub(pe) < 0) {106pe->pe_flags |= LIBPE_F_BAD_DOS_HEADER;107return (0);108}109}110}111112if ((pe->pe_flags & LIBPE_F_SPECIAL_FILE) == 0) {113/* Jump to the PE header. */114if (lseek(pe->pe_fd, (off_t) dh->dh_lfanew, SEEK_SET) < 0) {115pe->pe_flags |= LIBPE_F_BAD_PE_HEADER;116return (0);117}118}119120if (read(pe->pe_fd, &pe_magic, 4) != 4 ||121htole32(pe_magic) != PE_SIGNATURE) {122pe->pe_flags |= LIBPE_F_BAD_PE_HEADER;123return (0);124}125126if (read(pe->pe_fd, coff, sizeof(coff)) != (ssize_t) sizeof(coff)) {127pe->pe_flags |= LIBPE_F_BAD_COFF_HEADER;128return (0);129}130131return (libpe_parse_coff_header(pe, coff));132}133134int135libpe_read_msdos_stub(PE *pe)136{137void *m;138139assert(pe->pe_stub_ex > 0 &&140(pe->pe_flags & LIBPE_F_LOAD_DOS_STUB) == 0);141142if ((pe->pe_flags & LIBPE_F_SPECIAL_FILE) == 0) {143if (lseek(pe->pe_fd, (off_t) sizeof(PE_DosHdr), SEEK_SET) <1440) {145errno = EIO;146goto fail;147}148}149150if ((m = realloc(pe->pe_stub, sizeof(PE_DosHdr) + pe->pe_stub_ex)) ==151NULL) {152errno = ENOMEM;153goto fail;154}155pe->pe_stub = m;156157if (read(pe->pe_fd, pe->pe_stub + sizeof(PE_DosHdr), pe->pe_stub_ex) !=158(ssize_t) pe->pe_stub_ex) {159errno = EIO;160goto fail;161}162163pe->pe_flags |= LIBPE_F_LOAD_DOS_STUB;164165/* Search for the Rich header embedded just before the PE header. */166(void) libpe_parse_rich_header(pe);167168return (0);169170fail:171pe->pe_stub_ex = 0;172173return (-1);174}175176/*177* The "standard" MS-DOS stub displaying "This program cannot be run in178* DOS mode".179*/180static const char msdos_stub[] = {181'\x0e','\x1f','\xba','\x0e','\x00','\xb4','\x09','\xcd',182'\x21','\xb8','\x01','\x4c','\xcd','\x21','\x54','\x68',183'\x69','\x73','\x20','\x70','\x72','\x6f','\x67','\x72',184'\x61','\x6d','\x20','\x63','\x61','\x6e','\x6e','\x6f',185'\x74','\x20','\x62','\x65','\x20','\x72','\x75','\x6e',186'\x20','\x69','\x6e','\x20','\x44','\x4f','\x53','\x20',187'\x6d','\x6f','\x64','\x65','\x2e','\x0d','\x0d','\x0a',188'\x24','\x00','\x00','\x00','\x00','\x00','\x00','\x00',189};190191static void192init_dos_header(PE_DosHdr *dh)193{194195dh->dh_magic[0] = 'M';196dh->dh_magic[1] = 'Z';197dh->dh_lastsize = 144;198dh->dh_nblock = 3;199dh->dh_hdrsize = 4;200dh->dh_maxalloc = 65535;201dh->dh_sp = 184;202dh->dh_relocpos = 0x40;203dh->dh_lfanew = 0x80;204}205206off_t207libpe_write_msdos_stub(PE *pe, off_t off)208{209PE_DosHdr *dh;210char tmp[sizeof(PE_DosHdr)], *hdr;211off_t d;212int i, strip_rich;213214strip_rich = 0;215216if (pe->pe_cmd == PE_C_RDWR) {217assert((pe->pe_flags & LIBPE_F_SPECIAL_FILE) == 0);218219if (pe->pe_dh != NULL &&220(pe->pe_flags & PE_F_STRIP_DOS_STUB)) {221/*222* If we strip MS-DOS stub, everything after it223* needs rewritten.224*/225pe->pe_flags |= LIBPE_F_BAD_PE_HEADER;226goto done;227}228229/*230* lseek(2) to the PE signature if MS-DOS stub is not231* modified.232*/233if (pe->pe_dh != NULL &&234(pe->pe_flags & LIBPE_F_DIRTY_DOS_HEADER) == 0 &&235(pe->pe_flags & LIBPE_F_BAD_DOS_HEADER) == 0 &&236(pe->pe_flags & PE_F_STRIP_RICH_HEADER) == 0) {237if (lseek(pe->pe_fd,238(off_t) (sizeof(PE_DosHdr) + pe->pe_stub_ex),239SEEK_CUR) < 0) {240errno = EIO;241return (-1);242}243off = sizeof(PE_DosHdr) + pe->pe_stub_ex;244goto done;245}246247/* Check if we should strip the Rich header. */248if (pe->pe_dh != NULL && pe->pe_stub_app == NULL &&249(pe->pe_flags & LIBPE_F_BAD_DOS_HEADER) == 0 &&250(pe->pe_flags & PE_F_STRIP_RICH_HEADER)) {251if ((pe->pe_flags & LIBPE_F_LOAD_DOS_STUB) == 0) {252(void) libpe_read_msdos_stub(pe);253if (lseek(pe->pe_fd, off, SEEK_SET) < 0) {254errno = EIO;255return (-1);256}257}258if (pe->pe_rh != NULL) {259strip_rich = 1;260pe->pe_flags |= LIBPE_F_DIRTY_DOS_HEADER;261}262}263264/*265* If length of MS-DOS stub will change, Mark the PE266* signature is broken so that the PE signature and the267* headers follow it will be rewritten.268*269* The sections should be loaded now since the stub might270* overwrite the section data.271*/272if ((pe->pe_flags & LIBPE_F_BAD_DOS_HEADER) ||273(pe->pe_stub_app != NULL && pe->pe_stub_app_sz !=274sizeof(PE_DosHdr) + pe->pe_stub_ex) || strip_rich) {275if (libpe_load_all_sections(pe) < 0)276return (-1);277if (lseek(pe->pe_fd, off, SEEK_SET) < 0) {278errno = EIO;279return (-1);280}281pe->pe_flags |= LIBPE_F_BAD_PE_HEADER;282}283}284285if (pe->pe_flags & PE_F_STRIP_DOS_STUB)286goto done;287288/* Always use application supplied MS-DOS stub, if exists. */289if (pe->pe_stub_app != NULL && pe->pe_stub_app_sz > 0) {290if (write(pe->pe_fd, pe->pe_stub_app, pe->pe_stub_app_sz) !=291(ssize_t) pe->pe_stub_app_sz) {292errno = EIO;293return (-1);294}295off = pe->pe_stub_app_sz;296goto done;297}298299/*300* Write MS-DOS header.301*/302303if (pe->pe_dh == NULL) {304if ((dh = calloc(1, sizeof(PE_DosHdr))) == NULL) {305errno = ENOMEM;306return (-1);307}308pe->pe_dh = dh;309310init_dos_header(dh);311312pe->pe_flags |= LIBPE_F_DIRTY_DOS_HEADER;313} else314dh = pe->pe_dh;315316if (pe->pe_flags & LIBPE_F_BAD_DOS_HEADER)317init_dos_header(dh);318319if (strip_rich) {320d = pe->pe_rh_start - pe->pe_stub;321dh->dh_lfanew = roundup(d, 8);322}323324if ((pe->pe_flags & LIBPE_F_DIRTY_DOS_HEADER) ||325(pe->pe_flags & LIBPE_F_BAD_DOS_HEADER)) {326memcpy(tmp, dh->dh_magic, 2);327hdr = tmp + 2;328PE_WRITE16(hdr, dh->dh_lastsize);329PE_WRITE16(hdr, dh->dh_nblock);330PE_WRITE16(hdr, dh->dh_nreloc);331PE_WRITE16(hdr, dh->dh_hdrsize);332PE_WRITE16(hdr, dh->dh_minalloc);333PE_WRITE16(hdr, dh->dh_maxalloc);334PE_WRITE16(hdr, dh->dh_ss);335PE_WRITE16(hdr, dh->dh_sp);336PE_WRITE16(hdr, dh->dh_checksum);337PE_WRITE16(hdr, dh->dh_ip);338PE_WRITE16(hdr, dh->dh_cs);339PE_WRITE16(hdr, dh->dh_relocpos);340PE_WRITE16(hdr, dh->dh_noverlay);341for (i = 0; i < 4; i++)342PE_WRITE16(hdr, dh->dh_reserved1[i]);343PE_WRITE16(hdr, dh->dh_oemid);344PE_WRITE16(hdr, dh->dh_oeminfo);345for (i = 0; i < 10; i++)346PE_WRITE16(hdr, dh->dh_reserved2[i]);347PE_WRITE32(hdr, dh->dh_lfanew);348349if (write(pe->pe_fd, tmp, sizeof(tmp)) !=350(ssize_t) sizeof(tmp)) {351errno = EIO;352return (-1);353}354} else {355assert((pe->pe_flags & LIBPE_F_SPECIAL_FILE) == 0);356if (lseek(pe->pe_fd, (off_t) sizeof(PE_DosHdr), SEEK_CUR) <3570) {358errno = EIO;359return (-1);360}361}362363off = sizeof(PE_DosHdr);364365/*366* Write the MS-DOS stub.367*/368369if (strip_rich) {370assert((pe->pe_flags & LIBPE_F_SPECIAL_FILE) == 0);371assert(pe->pe_stub != NULL && pe->pe_rh_start != NULL);372d = pe->pe_rh_start - pe->pe_stub;373if (lseek(pe->pe_fd, d, SEEK_SET) < 0) {374errno = EIO;375return (-1);376}377off = d;378goto done;379}380381if (pe->pe_cmd == PE_C_RDWR) {382if (lseek(pe->pe_fd, (off_t) pe->pe_stub_ex, SEEK_CUR) < 0) {383errno = EIO;384return (-1);385}386off += pe->pe_stub_ex;387goto done;388}389390if (write(pe->pe_fd, msdos_stub, sizeof(msdos_stub)) !=391(ssize_t) sizeof(msdos_stub)) {392errno = EIO;393return (-1);394}395off += sizeof(msdos_stub);396397done:398pe->pe_flags &= ~LIBPE_F_DIRTY_DOS_HEADER;399pe->pe_flags &= ~LIBPE_F_BAD_DOS_HEADER;400401return (off);402}403404405