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
libpe_parse_msdos_header(PE * pe,char * hdr)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
libpe_read_msdos_stub(PE * pe)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
init_dos_header(PE_DosHdr * dh)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
libpe_write_msdos_stub(PE * pe,off_t off)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