1 /*- 2 * Copyright (c) 2015 Kai Wang 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 8 * 1. Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * 2. Redistributions in binary form must reproduce the above copyright 11 * notice, this list of conditions and the following disclaimer in the 12 * documentation and/or other materials provided with the distribution. 13 * 14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24 * SUCH DAMAGE. 25 */ 26 27 #include <sys/param.h> 28 #include <sys/types.h> 29 #include <assert.h> 30 #include <errno.h> 31 #include <stdlib.h> 32 #include <string.h> 33 #include <unistd.h> 34 35 #include "_libpe.h" 36 37 ELFTC_VCSID("$Id: libpe_dos.c 3312 2016-01-10 09:23:51Z kaiwang27 $"); 38 39 int 40 libpe_parse_msdos_header(PE *pe, char *hdr) 41 { 42 PE_DosHdr *dh; 43 char coff[sizeof(PE_CoffHdr)]; 44 uint32_t pe_magic; 45 int i; 46 47 if ((pe->pe_stub = malloc(sizeof(PE_DosHdr))) == NULL) { 48 errno = ENOMEM; 49 return (-1); 50 } 51 memcpy(pe->pe_stub, hdr, sizeof(PE_DosHdr)); 52 53 if ((dh = malloc(sizeof(*dh))) == NULL) { 54 errno = ENOMEM; 55 return (-1); 56 } 57 pe->pe_dh = dh; 58 59 /* Read the conventional MS-DOS EXE header. */ 60 memcpy(dh->dh_magic, hdr, 2); 61 hdr += 2; 62 PE_READ16(hdr, dh->dh_lastsize); 63 PE_READ16(hdr, dh->dh_nblock); 64 PE_READ16(hdr, dh->dh_nreloc); 65 PE_READ16(hdr, dh->dh_hdrsize); 66 PE_READ16(hdr, dh->dh_minalloc); 67 PE_READ16(hdr, dh->dh_maxalloc); 68 PE_READ16(hdr, dh->dh_ss); 69 PE_READ16(hdr, dh->dh_sp); 70 PE_READ16(hdr, dh->dh_checksum); 71 PE_READ16(hdr, dh->dh_ip); 72 PE_READ16(hdr, dh->dh_cs); 73 PE_READ16(hdr, dh->dh_relocpos); 74 PE_READ16(hdr, dh->dh_noverlay); 75 76 /* Do not continue if the EXE is not a PE/NE/... (new executable) */ 77 if (dh->dh_relocpos != 0x40) { 78 pe->pe_flags |= LIBPE_F_BAD_DOS_HEADER; 79 return (0); 80 } 81 82 for (i = 0; i < 4; i++) 83 PE_READ16(hdr, dh->dh_reserved1[i]); 84 PE_READ16(hdr, dh->dh_oemid); 85 PE_READ16(hdr, dh->dh_oeminfo); 86 for (i = 0; i < 10; i++) 87 PE_READ16(hdr, dh->dh_reserved2[i]); 88 PE_READ32(hdr, dh->dh_lfanew); 89 90 /* Check if the e_lfanew pointer is valid. */ 91 if (dh->dh_lfanew > pe->pe_fsize - 4) { 92 pe->pe_flags |= LIBPE_F_BAD_DOS_HEADER; 93 return (0); 94 } 95 96 if (dh->dh_lfanew < sizeof(PE_DosHdr) && 97 (pe->pe_flags & LIBPE_F_SPECIAL_FILE)) { 98 pe->pe_flags |= LIBPE_F_BAD_DOS_HEADER; 99 return (0); 100 } 101 102 if (dh->dh_lfanew > sizeof(PE_DosHdr)) { 103 pe->pe_stub_ex = dh->dh_lfanew - sizeof(PE_DosHdr); 104 if (pe->pe_flags & LIBPE_F_SPECIAL_FILE) { 105 /* Read in DOS stub now. */ 106 if (libpe_read_msdos_stub(pe) < 0) { 107 pe->pe_flags |= LIBPE_F_BAD_DOS_HEADER; 108 return (0); 109 } 110 } 111 } 112 113 if ((pe->pe_flags & LIBPE_F_SPECIAL_FILE) == 0) { 114 /* Jump to the PE header. */ 115 if (lseek(pe->pe_fd, (off_t) dh->dh_lfanew, SEEK_SET) < 0) { 116 pe->pe_flags |= LIBPE_F_BAD_PE_HEADER; 117 return (0); 118 } 119 } 120 121 if (read(pe->pe_fd, &pe_magic, 4) != 4 || 122 htole32(pe_magic) != PE_SIGNATURE) { 123 pe->pe_flags |= LIBPE_F_BAD_PE_HEADER; 124 return (0); 125 } 126 127 if (read(pe->pe_fd, coff, sizeof(coff)) != (ssize_t) sizeof(coff)) { 128 pe->pe_flags |= LIBPE_F_BAD_COFF_HEADER; 129 return (0); 130 } 131 132 return (libpe_parse_coff_header(pe, coff)); 133 } 134 135 int 136 libpe_read_msdos_stub(PE *pe) 137 { 138 void *m; 139 140 assert(pe->pe_stub_ex > 0 && 141 (pe->pe_flags & LIBPE_F_LOAD_DOS_STUB) == 0); 142 143 if ((pe->pe_flags & LIBPE_F_SPECIAL_FILE) == 0) { 144 if (lseek(pe->pe_fd, (off_t) sizeof(PE_DosHdr), SEEK_SET) < 145 0) { 146 errno = EIO; 147 goto fail; 148 } 149 } 150 151 if ((m = realloc(pe->pe_stub, sizeof(PE_DosHdr) + pe->pe_stub_ex)) == 152 NULL) { 153 errno = ENOMEM; 154 goto fail; 155 } 156 pe->pe_stub = m; 157 158 if (read(pe->pe_fd, pe->pe_stub + sizeof(PE_DosHdr), pe->pe_stub_ex) != 159 (ssize_t) pe->pe_stub_ex) { 160 errno = EIO; 161 goto fail; 162 } 163 164 pe->pe_flags |= LIBPE_F_LOAD_DOS_STUB; 165 166 /* Search for the Rich header embedded just before the PE header. */ 167 (void) libpe_parse_rich_header(pe); 168 169 return (0); 170 171 fail: 172 pe->pe_stub_ex = 0; 173 174 return (-1); 175 } 176 177 /* 178 * The "standard" MS-DOS stub displaying "This program cannot be run in 179 * DOS mode". 180 */ 181 static const char msdos_stub[] = { 182 '\x0e','\x1f','\xba','\x0e','\x00','\xb4','\x09','\xcd', 183 '\x21','\xb8','\x01','\x4c','\xcd','\x21','\x54','\x68', 184 '\x69','\x73','\x20','\x70','\x72','\x6f','\x67','\x72', 185 '\x61','\x6d','\x20','\x63','\x61','\x6e','\x6e','\x6f', 186 '\x74','\x20','\x62','\x65','\x20','\x72','\x75','\x6e', 187 '\x20','\x69','\x6e','\x20','\x44','\x4f','\x53','\x20', 188 '\x6d','\x6f','\x64','\x65','\x2e','\x0d','\x0d','\x0a', 189 '\x24','\x00','\x00','\x00','\x00','\x00','\x00','\x00', 190 }; 191 192 static void 193 init_dos_header(PE_DosHdr *dh) 194 { 195 196 dh->dh_magic[0] = 'M'; 197 dh->dh_magic[1] = 'Z'; 198 dh->dh_lastsize = 144; 199 dh->dh_nblock = 3; 200 dh->dh_hdrsize = 4; 201 dh->dh_maxalloc = 65535; 202 dh->dh_sp = 184; 203 dh->dh_relocpos = 0x40; 204 dh->dh_lfanew = 0x80; 205 } 206 207 off_t 208 libpe_write_msdos_stub(PE *pe, off_t off) 209 { 210 PE_DosHdr *dh; 211 char tmp[sizeof(PE_DosHdr)], *hdr; 212 off_t d; 213 int i, strip_rich; 214 215 strip_rich = 0; 216 217 if (pe->pe_cmd == PE_C_RDWR) { 218 assert((pe->pe_flags & LIBPE_F_SPECIAL_FILE) == 0); 219 220 if (pe->pe_dh != NULL && 221 (pe->pe_flags & PE_F_STRIP_DOS_STUB)) { 222 /* 223 * If we strip MS-DOS stub, everything after it 224 * needs rewritten. 225 */ 226 pe->pe_flags |= LIBPE_F_BAD_PE_HEADER; 227 goto done; 228 } 229 230 /* 231 * lseek(2) to the PE signature if MS-DOS stub is not 232 * modified. 233 */ 234 if (pe->pe_dh != NULL && 235 (pe->pe_flags & LIBPE_F_DIRTY_DOS_HEADER) == 0 && 236 (pe->pe_flags & LIBPE_F_BAD_DOS_HEADER) == 0 && 237 (pe->pe_flags & PE_F_STRIP_RICH_HEADER) == 0) { 238 if (lseek(pe->pe_fd, 239 (off_t) (sizeof(PE_DosHdr) + pe->pe_stub_ex), 240 SEEK_CUR) < 0) { 241 errno = EIO; 242 return (-1); 243 } 244 off = sizeof(PE_DosHdr) + pe->pe_stub_ex; 245 goto done; 246 } 247 248 /* Check if we should strip the Rich header. */ 249 if (pe->pe_dh != NULL && pe->pe_stub_app == NULL && 250 (pe->pe_flags & LIBPE_F_BAD_DOS_HEADER) == 0 && 251 (pe->pe_flags & PE_F_STRIP_RICH_HEADER)) { 252 if ((pe->pe_flags & LIBPE_F_LOAD_DOS_STUB) == 0) { 253 (void) libpe_read_msdos_stub(pe); 254 if (lseek(pe->pe_fd, off, SEEK_SET) < 0) { 255 errno = EIO; 256 return (-1); 257 } 258 } 259 if (pe->pe_rh != NULL) { 260 strip_rich = 1; 261 pe->pe_flags |= LIBPE_F_DIRTY_DOS_HEADER; 262 } 263 } 264 265 /* 266 * If length of MS-DOS stub will change, Mark the PE 267 * signature is broken so that the PE signature and the 268 * headers follow it will be rewritten. 269 * 270 * The sections should be loaded now since the stub might 271 * overwrite the section data. 272 */ 273 if ((pe->pe_flags & LIBPE_F_BAD_DOS_HEADER) || 274 (pe->pe_stub_app != NULL && pe->pe_stub_app_sz != 275 sizeof(PE_DosHdr) + pe->pe_stub_ex) || strip_rich) { 276 if (libpe_load_all_sections(pe) < 0) 277 return (-1); 278 if (lseek(pe->pe_fd, off, SEEK_SET) < 0) { 279 errno = EIO; 280 return (-1); 281 } 282 pe->pe_flags |= LIBPE_F_BAD_PE_HEADER; 283 } 284 } 285 286 if (pe->pe_flags & PE_F_STRIP_DOS_STUB) 287 goto done; 288 289 /* Always use application supplied MS-DOS stub, if exists. */ 290 if (pe->pe_stub_app != NULL && pe->pe_stub_app_sz > 0) { 291 if (write(pe->pe_fd, pe->pe_stub_app, pe->pe_stub_app_sz) != 292 (ssize_t) pe->pe_stub_app_sz) { 293 errno = EIO; 294 return (-1); 295 } 296 off = pe->pe_stub_app_sz; 297 goto done; 298 } 299 300 /* 301 * Write MS-DOS header. 302 */ 303 304 if (pe->pe_dh == NULL) { 305 if ((dh = calloc(1, sizeof(PE_DosHdr))) == NULL) { 306 errno = ENOMEM; 307 return (-1); 308 } 309 pe->pe_dh = dh; 310 311 init_dos_header(dh); 312 313 pe->pe_flags |= LIBPE_F_DIRTY_DOS_HEADER; 314 } else 315 dh = pe->pe_dh; 316 317 if (pe->pe_flags & LIBPE_F_BAD_DOS_HEADER) 318 init_dos_header(dh); 319 320 if (strip_rich) { 321 d = pe->pe_rh_start - pe->pe_stub; 322 dh->dh_lfanew = roundup(d, 8); 323 } 324 325 if ((pe->pe_flags & LIBPE_F_DIRTY_DOS_HEADER) || 326 (pe->pe_flags & LIBPE_F_BAD_DOS_HEADER)) { 327 memcpy(tmp, dh->dh_magic, 2); 328 hdr = tmp + 2; 329 PE_WRITE16(hdr, dh->dh_lastsize); 330 PE_WRITE16(hdr, dh->dh_nblock); 331 PE_WRITE16(hdr, dh->dh_nreloc); 332 PE_WRITE16(hdr, dh->dh_hdrsize); 333 PE_WRITE16(hdr, dh->dh_minalloc); 334 PE_WRITE16(hdr, dh->dh_maxalloc); 335 PE_WRITE16(hdr, dh->dh_ss); 336 PE_WRITE16(hdr, dh->dh_sp); 337 PE_WRITE16(hdr, dh->dh_checksum); 338 PE_WRITE16(hdr, dh->dh_ip); 339 PE_WRITE16(hdr, dh->dh_cs); 340 PE_WRITE16(hdr, dh->dh_relocpos); 341 PE_WRITE16(hdr, dh->dh_noverlay); 342 for (i = 0; i < 4; i++) 343 PE_WRITE16(hdr, dh->dh_reserved1[i]); 344 PE_WRITE16(hdr, dh->dh_oemid); 345 PE_WRITE16(hdr, dh->dh_oeminfo); 346 for (i = 0; i < 10; i++) 347 PE_WRITE16(hdr, dh->dh_reserved2[i]); 348 PE_WRITE32(hdr, dh->dh_lfanew); 349 350 if (write(pe->pe_fd, tmp, sizeof(tmp)) != 351 (ssize_t) sizeof(tmp)) { 352 errno = EIO; 353 return (-1); 354 } 355 } else { 356 assert((pe->pe_flags & LIBPE_F_SPECIAL_FILE) == 0); 357 if (lseek(pe->pe_fd, (off_t) sizeof(PE_DosHdr), SEEK_CUR) < 358 0) { 359 errno = EIO; 360 return (-1); 361 } 362 } 363 364 off = sizeof(PE_DosHdr); 365 366 /* 367 * Write the MS-DOS stub. 368 */ 369 370 if (strip_rich) { 371 assert((pe->pe_flags & LIBPE_F_SPECIAL_FILE) == 0); 372 assert(pe->pe_stub != NULL && pe->pe_rh_start != NULL); 373 d = pe->pe_rh_start - pe->pe_stub; 374 if (lseek(pe->pe_fd, d, SEEK_SET) < 0) { 375 errno = EIO; 376 return (-1); 377 } 378 off = d; 379 goto done; 380 } 381 382 if (pe->pe_cmd == PE_C_RDWR) { 383 if (lseek(pe->pe_fd, (off_t) pe->pe_stub_ex, SEEK_CUR) < 0) { 384 errno = EIO; 385 return (-1); 386 } 387 off += pe->pe_stub_ex; 388 goto done; 389 } 390 391 if (write(pe->pe_fd, msdos_stub, sizeof(msdos_stub)) != 392 (ssize_t) sizeof(msdos_stub)) { 393 errno = EIO; 394 return (-1); 395 } 396 off += sizeof(msdos_stub); 397 398 done: 399 pe->pe_flags &= ~LIBPE_F_DIRTY_DOS_HEADER; 400 pe->pe_flags &= ~LIBPE_F_BAD_DOS_HEADER; 401 402 return (off); 403 } 404