Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/freebsd-src
Path: blob/main/contrib/elftoolchain/libpe/libpe_dos.c
39478 views
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
405