1295484Semaste/*- 2295484Semaste * Copyright (c) 2015 Kai Wang 3295484Semaste * All rights reserved. 4295484Semaste * 5295484Semaste * Redistribution and use in source and binary forms, with or without 6295484Semaste * modification, are permitted provided that the following conditions 7295484Semaste * are met: 8295484Semaste * 1. Redistributions of source code must retain the above copyright 9295484Semaste * notice, this list of conditions and the following disclaimer. 10295484Semaste * 2. Redistributions in binary form must reproduce the above copyright 11295484Semaste * notice, this list of conditions and the following disclaimer in the 12295484Semaste * documentation and/or other materials provided with the distribution. 13295484Semaste * 14295484Semaste * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15295484Semaste * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16295484Semaste * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17295484Semaste * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 18295484Semaste * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19295484Semaste * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20295484Semaste * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21295484Semaste * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22295484Semaste * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23295484Semaste * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24295484Semaste * SUCH DAMAGE. 25295484Semaste */ 26295484Semaste 27295484Semaste#include <sys/param.h> 28295484Semaste#include <sys/types.h> 29295484Semaste#include <assert.h> 30295484Semaste#include <errno.h> 31295484Semaste#include <stdlib.h> 32295484Semaste#include <string.h> 33295484Semaste#include <unistd.h> 34295484Semaste 35295484Semaste#include "_libpe.h" 36295484Semaste 37295484SemasteELFTC_VCSID("$Id: libpe_dos.c 3312 2016-01-10 09:23:51Z kaiwang27 $"); 38295484Semaste 39295484Semasteint 40295484Semastelibpe_parse_msdos_header(PE *pe, char *hdr) 41295484Semaste{ 42295484Semaste PE_DosHdr *dh; 43295484Semaste char coff[sizeof(PE_CoffHdr)]; 44295484Semaste uint32_t pe_magic; 45295484Semaste int i; 46295484Semaste 47295484Semaste if ((pe->pe_stub = malloc(sizeof(PE_DosHdr))) == NULL) { 48295484Semaste errno = ENOMEM; 49295484Semaste return (-1); 50295484Semaste } 51295484Semaste memcpy(pe->pe_stub, hdr, sizeof(PE_DosHdr)); 52295484Semaste 53295484Semaste if ((dh = malloc(sizeof(*dh))) == NULL) { 54295484Semaste errno = ENOMEM; 55295484Semaste return (-1); 56295484Semaste } 57295484Semaste pe->pe_dh = dh; 58295484Semaste 59295484Semaste /* Read the conventional MS-DOS EXE header. */ 60295484Semaste memcpy(dh->dh_magic, hdr, 2); 61295484Semaste hdr += 2; 62295484Semaste PE_READ16(hdr, dh->dh_lastsize); 63295484Semaste PE_READ16(hdr, dh->dh_nblock); 64295484Semaste PE_READ16(hdr, dh->dh_nreloc); 65295484Semaste PE_READ16(hdr, dh->dh_hdrsize); 66295484Semaste PE_READ16(hdr, dh->dh_minalloc); 67295484Semaste PE_READ16(hdr, dh->dh_maxalloc); 68295484Semaste PE_READ16(hdr, dh->dh_ss); 69295484Semaste PE_READ16(hdr, dh->dh_sp); 70295484Semaste PE_READ16(hdr, dh->dh_checksum); 71295484Semaste PE_READ16(hdr, dh->dh_ip); 72295484Semaste PE_READ16(hdr, dh->dh_cs); 73295484Semaste PE_READ16(hdr, dh->dh_relocpos); 74295484Semaste PE_READ16(hdr, dh->dh_noverlay); 75295484Semaste 76295484Semaste /* Do not continue if the EXE is not a PE/NE/... (new executable) */ 77295484Semaste if (dh->dh_relocpos != 0x40) { 78295484Semaste pe->pe_flags |= LIBPE_F_BAD_DOS_HEADER; 79295484Semaste return (0); 80295484Semaste } 81295484Semaste 82295484Semaste for (i = 0; i < 4; i++) 83295484Semaste PE_READ16(hdr, dh->dh_reserved1[i]); 84295484Semaste PE_READ16(hdr, dh->dh_oemid); 85295484Semaste PE_READ16(hdr, dh->dh_oeminfo); 86295484Semaste for (i = 0; i < 10; i++) 87295484Semaste PE_READ16(hdr, dh->dh_reserved2[i]); 88295484Semaste PE_READ32(hdr, dh->dh_lfanew); 89295484Semaste 90295484Semaste /* Check if the e_lfanew pointer is valid. */ 91295484Semaste if (dh->dh_lfanew > pe->pe_fsize - 4) { 92295484Semaste pe->pe_flags |= LIBPE_F_BAD_DOS_HEADER; 93295484Semaste return (0); 94295484Semaste } 95295484Semaste 96295484Semaste if (dh->dh_lfanew < sizeof(PE_DosHdr) && 97295484Semaste (pe->pe_flags & LIBPE_F_SPECIAL_FILE)) { 98295484Semaste pe->pe_flags |= LIBPE_F_BAD_DOS_HEADER; 99295484Semaste return (0); 100295484Semaste } 101295484Semaste 102295484Semaste if (dh->dh_lfanew > sizeof(PE_DosHdr)) { 103295484Semaste pe->pe_stub_ex = dh->dh_lfanew - sizeof(PE_DosHdr); 104295484Semaste if (pe->pe_flags & LIBPE_F_SPECIAL_FILE) { 105295484Semaste /* Read in DOS stub now. */ 106295484Semaste if (libpe_read_msdos_stub(pe) < 0) { 107295484Semaste pe->pe_flags |= LIBPE_F_BAD_DOS_HEADER; 108295484Semaste return (0); 109295484Semaste } 110295484Semaste } 111295484Semaste } 112295484Semaste 113295484Semaste if ((pe->pe_flags & LIBPE_F_SPECIAL_FILE) == 0) { 114295484Semaste /* Jump to the PE header. */ 115295484Semaste if (lseek(pe->pe_fd, (off_t) dh->dh_lfanew, SEEK_SET) < 0) { 116295484Semaste pe->pe_flags |= LIBPE_F_BAD_PE_HEADER; 117295484Semaste return (0); 118295484Semaste } 119295484Semaste } 120295484Semaste 121295484Semaste if (read(pe->pe_fd, &pe_magic, 4) != 4 || 122295484Semaste htole32(pe_magic) != PE_SIGNATURE) { 123295484Semaste pe->pe_flags |= LIBPE_F_BAD_PE_HEADER; 124295484Semaste return (0); 125295484Semaste } 126295484Semaste 127295484Semaste if (read(pe->pe_fd, coff, sizeof(coff)) != (ssize_t) sizeof(coff)) { 128295484Semaste pe->pe_flags |= LIBPE_F_BAD_COFF_HEADER; 129295484Semaste return (0); 130295484Semaste } 131295484Semaste 132295484Semaste return (libpe_parse_coff_header(pe, coff)); 133295484Semaste} 134295484Semaste 135295484Semasteint 136295484Semastelibpe_read_msdos_stub(PE *pe) 137295484Semaste{ 138295484Semaste void *m; 139295484Semaste 140295484Semaste assert(pe->pe_stub_ex > 0 && 141295484Semaste (pe->pe_flags & LIBPE_F_LOAD_DOS_STUB) == 0); 142295484Semaste 143295484Semaste if ((pe->pe_flags & LIBPE_F_SPECIAL_FILE) == 0) { 144295484Semaste if (lseek(pe->pe_fd, (off_t) sizeof(PE_DosHdr), SEEK_SET) < 145295484Semaste 0) { 146295484Semaste errno = EIO; 147295484Semaste goto fail; 148295484Semaste } 149295484Semaste } 150295484Semaste 151295484Semaste if ((m = realloc(pe->pe_stub, sizeof(PE_DosHdr) + pe->pe_stub_ex)) == 152295484Semaste NULL) { 153295484Semaste errno = ENOMEM; 154295484Semaste goto fail; 155295484Semaste } 156295484Semaste pe->pe_stub = m; 157295484Semaste 158295484Semaste if (read(pe->pe_fd, pe->pe_stub + sizeof(PE_DosHdr), pe->pe_stub_ex) != 159295484Semaste (ssize_t) pe->pe_stub_ex) { 160295484Semaste errno = EIO; 161295484Semaste goto fail; 162295484Semaste } 163295484Semaste 164295484Semaste pe->pe_flags |= LIBPE_F_LOAD_DOS_STUB; 165295484Semaste 166295484Semaste /* Search for the Rich header embedded just before the PE header. */ 167295484Semaste (void) libpe_parse_rich_header(pe); 168295484Semaste 169295484Semaste return (0); 170295484Semaste 171295484Semastefail: 172295484Semaste pe->pe_stub_ex = 0; 173295484Semaste 174295484Semaste return (-1); 175295484Semaste} 176295484Semaste 177295484Semaste/* 178295484Semaste * The "standard" MS-DOS stub displaying "This program cannot be run in 179295484Semaste * DOS mode". 180295484Semaste */ 181295484Semastestatic const char msdos_stub[] = { 182295484Semaste '\x0e','\x1f','\xba','\x0e','\x00','\xb4','\x09','\xcd', 183295484Semaste '\x21','\xb8','\x01','\x4c','\xcd','\x21','\x54','\x68', 184295484Semaste '\x69','\x73','\x20','\x70','\x72','\x6f','\x67','\x72', 185295484Semaste '\x61','\x6d','\x20','\x63','\x61','\x6e','\x6e','\x6f', 186295484Semaste '\x74','\x20','\x62','\x65','\x20','\x72','\x75','\x6e', 187295484Semaste '\x20','\x69','\x6e','\x20','\x44','\x4f','\x53','\x20', 188295484Semaste '\x6d','\x6f','\x64','\x65','\x2e','\x0d','\x0d','\x0a', 189295484Semaste '\x24','\x00','\x00','\x00','\x00','\x00','\x00','\x00', 190295484Semaste}; 191295484Semaste 192295484Semastestatic void 193295484Semasteinit_dos_header(PE_DosHdr *dh) 194295484Semaste{ 195295484Semaste 196295484Semaste dh->dh_magic[0] = 'M'; 197295484Semaste dh->dh_magic[1] = 'Z'; 198295484Semaste dh->dh_lastsize = 144; 199295484Semaste dh->dh_nblock = 3; 200295484Semaste dh->dh_hdrsize = 4; 201295484Semaste dh->dh_maxalloc = 65535; 202295484Semaste dh->dh_sp = 184; 203295484Semaste dh->dh_relocpos = 0x40; 204295484Semaste dh->dh_lfanew = 0x80; 205295484Semaste} 206295484Semaste 207295484Semasteoff_t 208295484Semastelibpe_write_msdos_stub(PE *pe, off_t off) 209295484Semaste{ 210295484Semaste PE_DosHdr *dh; 211295484Semaste char tmp[sizeof(PE_DosHdr)], *hdr; 212295484Semaste off_t d; 213295484Semaste int i, strip_rich; 214295484Semaste 215295484Semaste strip_rich = 0; 216295484Semaste 217295484Semaste if (pe->pe_cmd == PE_C_RDWR) { 218295484Semaste assert((pe->pe_flags & LIBPE_F_SPECIAL_FILE) == 0); 219295484Semaste 220295484Semaste if (pe->pe_dh != NULL && 221295484Semaste (pe->pe_flags & PE_F_STRIP_DOS_STUB)) { 222295484Semaste /* 223295484Semaste * If we strip MS-DOS stub, everything after it 224295484Semaste * needs rewritten. 225295484Semaste */ 226295484Semaste pe->pe_flags |= LIBPE_F_BAD_PE_HEADER; 227295484Semaste goto done; 228295484Semaste } 229295484Semaste 230295484Semaste /* 231295484Semaste * lseek(2) to the PE signature if MS-DOS stub is not 232295484Semaste * modified. 233295484Semaste */ 234295484Semaste if (pe->pe_dh != NULL && 235295484Semaste (pe->pe_flags & LIBPE_F_DIRTY_DOS_HEADER) == 0 && 236295484Semaste (pe->pe_flags & LIBPE_F_BAD_DOS_HEADER) == 0 && 237295484Semaste (pe->pe_flags & PE_F_STRIP_RICH_HEADER) == 0) { 238295484Semaste if (lseek(pe->pe_fd, 239295484Semaste (off_t) (sizeof(PE_DosHdr) + pe->pe_stub_ex), 240295484Semaste SEEK_CUR) < 0) { 241295484Semaste errno = EIO; 242295484Semaste return (-1); 243295484Semaste } 244295484Semaste off = sizeof(PE_DosHdr) + pe->pe_stub_ex; 245295484Semaste goto done; 246295484Semaste } 247295484Semaste 248295484Semaste /* Check if we should strip the Rich header. */ 249295484Semaste if (pe->pe_dh != NULL && pe->pe_stub_app == NULL && 250295484Semaste (pe->pe_flags & LIBPE_F_BAD_DOS_HEADER) == 0 && 251295484Semaste (pe->pe_flags & PE_F_STRIP_RICH_HEADER)) { 252295484Semaste if ((pe->pe_flags & LIBPE_F_LOAD_DOS_STUB) == 0) { 253295484Semaste (void) libpe_read_msdos_stub(pe); 254295484Semaste if (lseek(pe->pe_fd, off, SEEK_SET) < 0) { 255295484Semaste errno = EIO; 256295484Semaste return (-1); 257295484Semaste } 258295484Semaste } 259295484Semaste if (pe->pe_rh != NULL) { 260295484Semaste strip_rich = 1; 261295484Semaste pe->pe_flags |= LIBPE_F_DIRTY_DOS_HEADER; 262295484Semaste } 263295484Semaste } 264295484Semaste 265295484Semaste /* 266295484Semaste * If length of MS-DOS stub will change, Mark the PE 267295484Semaste * signature is broken so that the PE signature and the 268295484Semaste * headers follow it will be rewritten. 269295484Semaste * 270295484Semaste * The sections should be loaded now since the stub might 271295484Semaste * overwrite the section data. 272295484Semaste */ 273295484Semaste if ((pe->pe_flags & LIBPE_F_BAD_DOS_HEADER) || 274295484Semaste (pe->pe_stub_app != NULL && pe->pe_stub_app_sz != 275295484Semaste sizeof(PE_DosHdr) + pe->pe_stub_ex) || strip_rich) { 276295484Semaste if (libpe_load_all_sections(pe) < 0) 277295484Semaste return (-1); 278295484Semaste if (lseek(pe->pe_fd, off, SEEK_SET) < 0) { 279295484Semaste errno = EIO; 280295484Semaste return (-1); 281295484Semaste } 282295484Semaste pe->pe_flags |= LIBPE_F_BAD_PE_HEADER; 283295484Semaste } 284295484Semaste } 285295484Semaste 286295484Semaste if (pe->pe_flags & PE_F_STRIP_DOS_STUB) 287295484Semaste goto done; 288295484Semaste 289295484Semaste /* Always use application supplied MS-DOS stub, if exists. */ 290295484Semaste if (pe->pe_stub_app != NULL && pe->pe_stub_app_sz > 0) { 291295484Semaste if (write(pe->pe_fd, pe->pe_stub_app, pe->pe_stub_app_sz) != 292295484Semaste (ssize_t) pe->pe_stub_app_sz) { 293295484Semaste errno = EIO; 294295484Semaste return (-1); 295295484Semaste } 296295484Semaste off = pe->pe_stub_app_sz; 297295484Semaste goto done; 298295484Semaste } 299295484Semaste 300295484Semaste /* 301295484Semaste * Write MS-DOS header. 302295484Semaste */ 303295484Semaste 304295484Semaste if (pe->pe_dh == NULL) { 305295484Semaste if ((dh = calloc(1, sizeof(PE_DosHdr))) == NULL) { 306295484Semaste errno = ENOMEM; 307295484Semaste return (-1); 308295484Semaste } 309295484Semaste pe->pe_dh = dh; 310295484Semaste 311295484Semaste init_dos_header(dh); 312295484Semaste 313295484Semaste pe->pe_flags |= LIBPE_F_DIRTY_DOS_HEADER; 314295484Semaste } else 315295484Semaste dh = pe->pe_dh; 316295484Semaste 317295484Semaste if (pe->pe_flags & LIBPE_F_BAD_DOS_HEADER) 318295484Semaste init_dos_header(dh); 319295484Semaste 320295484Semaste if (strip_rich) { 321295484Semaste d = pe->pe_rh_start - pe->pe_stub; 322295484Semaste dh->dh_lfanew = roundup(d, 8); 323295484Semaste } 324295484Semaste 325295484Semaste if ((pe->pe_flags & LIBPE_F_DIRTY_DOS_HEADER) || 326295484Semaste (pe->pe_flags & LIBPE_F_BAD_DOS_HEADER)) { 327295484Semaste memcpy(tmp, dh->dh_magic, 2); 328295484Semaste hdr = tmp + 2; 329295484Semaste PE_WRITE16(hdr, dh->dh_lastsize); 330295484Semaste PE_WRITE16(hdr, dh->dh_nblock); 331295484Semaste PE_WRITE16(hdr, dh->dh_nreloc); 332295484Semaste PE_WRITE16(hdr, dh->dh_hdrsize); 333295484Semaste PE_WRITE16(hdr, dh->dh_minalloc); 334295484Semaste PE_WRITE16(hdr, dh->dh_maxalloc); 335295484Semaste PE_WRITE16(hdr, dh->dh_ss); 336295484Semaste PE_WRITE16(hdr, dh->dh_sp); 337295484Semaste PE_WRITE16(hdr, dh->dh_checksum); 338295484Semaste PE_WRITE16(hdr, dh->dh_ip); 339295484Semaste PE_WRITE16(hdr, dh->dh_cs); 340295484Semaste PE_WRITE16(hdr, dh->dh_relocpos); 341295484Semaste PE_WRITE16(hdr, dh->dh_noverlay); 342295484Semaste for (i = 0; i < 4; i++) 343295484Semaste PE_WRITE16(hdr, dh->dh_reserved1[i]); 344295484Semaste PE_WRITE16(hdr, dh->dh_oemid); 345295484Semaste PE_WRITE16(hdr, dh->dh_oeminfo); 346295484Semaste for (i = 0; i < 10; i++) 347295484Semaste PE_WRITE16(hdr, dh->dh_reserved2[i]); 348295484Semaste PE_WRITE32(hdr, dh->dh_lfanew); 349295484Semaste 350295484Semaste if (write(pe->pe_fd, tmp, sizeof(tmp)) != 351295484Semaste (ssize_t) sizeof(tmp)) { 352295484Semaste errno = EIO; 353295484Semaste return (-1); 354295484Semaste } 355295484Semaste } else { 356295484Semaste assert((pe->pe_flags & LIBPE_F_SPECIAL_FILE) == 0); 357295484Semaste if (lseek(pe->pe_fd, (off_t) sizeof(PE_DosHdr), SEEK_CUR) < 358295484Semaste 0) { 359295484Semaste errno = EIO; 360295484Semaste return (-1); 361295484Semaste } 362295484Semaste } 363295484Semaste 364295484Semaste off = sizeof(PE_DosHdr); 365295484Semaste 366295484Semaste /* 367295484Semaste * Write the MS-DOS stub. 368295484Semaste */ 369295484Semaste 370295484Semaste if (strip_rich) { 371295484Semaste assert((pe->pe_flags & LIBPE_F_SPECIAL_FILE) == 0); 372295484Semaste assert(pe->pe_stub != NULL && pe->pe_rh_start != NULL); 373295484Semaste d = pe->pe_rh_start - pe->pe_stub; 374295484Semaste if (lseek(pe->pe_fd, d, SEEK_SET) < 0) { 375295484Semaste errno = EIO; 376295484Semaste return (-1); 377295484Semaste } 378295484Semaste off = d; 379295484Semaste goto done; 380295484Semaste } 381295484Semaste 382295484Semaste if (pe->pe_cmd == PE_C_RDWR) { 383295484Semaste if (lseek(pe->pe_fd, (off_t) pe->pe_stub_ex, SEEK_CUR) < 0) { 384295484Semaste errno = EIO; 385295484Semaste return (-1); 386295484Semaste } 387295484Semaste off += pe->pe_stub_ex; 388295484Semaste goto done; 389295484Semaste } 390295484Semaste 391295484Semaste if (write(pe->pe_fd, msdos_stub, sizeof(msdos_stub)) != 392295484Semaste (ssize_t) sizeof(msdos_stub)) { 393295484Semaste errno = EIO; 394295484Semaste return (-1); 395295484Semaste } 396295484Semaste off += sizeof(msdos_stub); 397295484Semaste 398295484Semastedone: 399295484Semaste pe->pe_flags &= ~LIBPE_F_DIRTY_DOS_HEADER; 400295484Semaste pe->pe_flags &= ~LIBPE_F_BAD_DOS_HEADER; 401295484Semaste 402295484Semaste return (off); 403295484Semaste} 404