xref: /NextBSD/sys/compat/ndis/subr_pe.c (revision eb1a5f8de9f7ea602c373a710f531abbf81141c4)
1 /*-
2  * Copyright (c) 2003
3  *	Bill Paul <wpaul@windriver.com>.  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  * 3. All advertising materials mentioning features or use of this software
14  *    must display the following acknowledgement:
15  *	This product includes software developed by Bill Paul.
16  * 4. Neither the name of the author nor the names of any co-contributors
17  *    may be used to endorse or promote products derived from this software
18  *    without specific prior written permission.
19  *
20  * THIS SOFTWARE IS PROVIDED BY Bill Paul AND CONTRIBUTORS ``AS IS'' AND
21  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23  * ARE DISCLAIMED.  IN NO EVENT SHALL Bill Paul OR THE VOICES IN HIS HEAD
24  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
25  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
26  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
27  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
28  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
29  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
30  * THE POSSIBILITY OF SUCH DAMAGE.
31  */
32 
33 #include <sys/cdefs.h>
34 __FBSDID("$FreeBSD$");
35 
36 /*
37  * This file contains routines for relocating and dynamically linking
38  * executable object code files in the Windows(r) PE (Portable Executable)
39  * format. In Windows, anything with a .EXE, .DLL or .SYS extention is
40  * considered an executable, and all such files have some structures in
41  * common. The PE format was apparently based largely on COFF but has
42  * mutated significantly over time. We are mainly concerned with .SYS files,
43  * so this module implements only enough routines to be able to parse the
44  * headers and sections of a .SYS object file and perform the necessary
45  * relocations and jump table patching to allow us to call into it
46  * (and to have it call back to us). Note that while this module
47  * can handle fixups for imported symbols, it knows nothing about
48  * exporting them.
49  */
50 
51 #include <sys/param.h>
52 #include <sys/types.h>
53 #include <sys/errno.h>
54 #ifdef _KERNEL
55 #include <sys/systm.h>
56 #else
57 #include <stdio.h>
58 #include <stddef.h>
59 #include <stdlib.h>
60 #include <unistd.h>
61 #include <string.h>
62 #endif
63 
64 #include <compat/ndis/pe_var.h>
65 
66 static vm_offset_t pe_functbl_match(image_patch_table *, char *);
67 
68 /*
69  * Check for an MS-DOS executable header. All Windows binaries
70  * have a small MS-DOS executable prepended to them to print out
71  * the "This program requires Windows" message. Even .SYS files
72  * have this header, in spite of the fact that you're can't actually
73  * run them directly.
74  */
75 
76 int
pe_get_dos_header(imgbase,hdr)77 pe_get_dos_header(imgbase, hdr)
78 	vm_offset_t		imgbase;
79 	image_dos_header	*hdr;
80 {
81 	uint16_t		signature;
82 
83 	if (imgbase == 0 || hdr == NULL)
84 		return (EINVAL);
85 
86 	signature = *(uint16_t *)imgbase;
87 	if (signature != IMAGE_DOS_SIGNATURE)
88 		return (ENOEXEC);
89 
90 	bcopy ((char *)imgbase, (char *)hdr, sizeof(image_dos_header));
91 
92 	return (0);
93 }
94 
95 /*
96  * Verify that this image has a Windows NT PE signature.
97  */
98 
99 int
pe_is_nt_image(imgbase)100 pe_is_nt_image(imgbase)
101 	vm_offset_t		imgbase;
102 {
103 	uint32_t		signature;
104 	image_dos_header	*dos_hdr;
105 
106 	if (imgbase == 0)
107 		return (EINVAL);
108 
109 	signature = *(uint16_t *)imgbase;
110 	if (signature == IMAGE_DOS_SIGNATURE) {
111 		dos_hdr = (image_dos_header *)imgbase;
112 		signature = *(uint32_t *)(imgbase + dos_hdr->idh_lfanew);
113 		if (signature == IMAGE_NT_SIGNATURE)
114 			return (0);
115 	}
116 
117 	return (ENOEXEC);
118 }
119 
120 /*
121  * Return a copy of the optional header. This contains the
122  * executable entry point and the directory listing which we
123  * need to find the relocations and imports later.
124  */
125 
126 int
pe_get_optional_header(imgbase,hdr)127 pe_get_optional_header(imgbase, hdr)
128 	vm_offset_t		imgbase;
129 	image_optional_header	*hdr;
130 {
131 	image_dos_header	*dos_hdr;
132 	image_nt_header		*nt_hdr;
133 
134 	if (imgbase == 0 || hdr == NULL)
135 		return (EINVAL);
136 
137 	if (pe_is_nt_image(imgbase))
138 		return (EINVAL);
139 
140 	dos_hdr = (image_dos_header *)(imgbase);
141 	nt_hdr = (image_nt_header *)(imgbase + dos_hdr->idh_lfanew);
142 
143 	bcopy ((char *)&nt_hdr->inh_optionalhdr, (char *)hdr,
144 	    nt_hdr->inh_filehdr.ifh_optionalhdrlen);
145 
146 	return (0);
147 }
148 
149 /*
150  * Return a copy of the file header. Contains the number of
151  * sections in this image.
152  */
153 
154 int
pe_get_file_header(imgbase,hdr)155 pe_get_file_header(imgbase, hdr)
156 	vm_offset_t		imgbase;
157 	image_file_header	*hdr;
158 {
159 	image_dos_header	*dos_hdr;
160 	image_nt_header		*nt_hdr;
161 
162 	if (imgbase == 0 || hdr == NULL)
163 		return (EINVAL);
164 
165 	if (pe_is_nt_image(imgbase))
166 		return (EINVAL);
167 
168 	dos_hdr = (image_dos_header *)imgbase;
169 	nt_hdr = (image_nt_header *)(imgbase + dos_hdr->idh_lfanew);
170 
171 	/*
172 	 * Note: the size of the nt_header is variable since it
173 	 * can contain optional fields, as indicated by ifh_optionalhdrlen.
174 	 * However it happens we're only interested in fields in the
175 	 * non-variant portion of the nt_header structure, so we don't
176 	 * bother copying the optional parts here.
177 	 */
178 
179 	bcopy ((char *)&nt_hdr->inh_filehdr, (char *)hdr,
180 	    sizeof(image_file_header));
181 
182 	return (0);
183 }
184 
185 /*
186  * Return the header of the first section in this image (usually
187  * .text).
188  */
189 
190 int
pe_get_section_header(imgbase,hdr)191 pe_get_section_header(imgbase, hdr)
192 	vm_offset_t		imgbase;
193 	image_section_header	*hdr;
194 {
195 	image_dos_header	*dos_hdr;
196 	image_nt_header		*nt_hdr;
197 	image_section_header	*sect_hdr;
198 
199 	if (imgbase == 0 || hdr == NULL)
200 		return (EINVAL);
201 
202 	if (pe_is_nt_image(imgbase))
203 		return (EINVAL);
204 
205 	dos_hdr = (image_dos_header *)imgbase;
206 	nt_hdr = (image_nt_header *)(imgbase + dos_hdr->idh_lfanew);
207 	sect_hdr = IMAGE_FIRST_SECTION(nt_hdr);
208 
209 	bcopy ((char *)sect_hdr, (char *)hdr, sizeof(image_section_header));
210 
211 	return (0);
212 }
213 
214 /*
215  * Return the number of sections in this executable, or 0 on error.
216  */
217 
218 int
pe_numsections(imgbase)219 pe_numsections(imgbase)
220 	vm_offset_t		imgbase;
221 {
222 	image_file_header	file_hdr;
223 
224 	if (pe_get_file_header(imgbase, &file_hdr))
225 		return (0);
226 
227 	return (file_hdr.ifh_numsections);
228 }
229 
230 /*
231  * Return the base address that this image was linked for.
232  * This helps us calculate relocation addresses later.
233  */
234 
235 vm_offset_t
pe_imagebase(imgbase)236 pe_imagebase(imgbase)
237 	vm_offset_t		imgbase;
238 {
239 	image_optional_header	optional_hdr;
240 
241 	if (pe_get_optional_header(imgbase, &optional_hdr))
242 		return (0);
243 
244 	return (optional_hdr.ioh_imagebase);
245 }
246 
247 /*
248  * Return the offset of a given directory structure within the
249  * image. Directories reside within sections.
250  */
251 
252 vm_offset_t
pe_directory_offset(imgbase,diridx)253 pe_directory_offset(imgbase, diridx)
254 	vm_offset_t		imgbase;
255 	uint32_t		diridx;
256 {
257 	image_optional_header	opt_hdr;
258 	vm_offset_t		dir;
259 
260 	if (pe_get_optional_header(imgbase, &opt_hdr))
261 		return (0);
262 
263 	if (diridx >= opt_hdr.ioh_rva_size_cnt)
264 		return (0);
265 
266 	dir = opt_hdr.ioh_datadir[diridx].idd_vaddr;
267 
268 	return (pe_translate_addr(imgbase, dir));
269 }
270 
271 vm_offset_t
pe_translate_addr(imgbase,rva)272 pe_translate_addr(imgbase, rva)
273 	vm_offset_t		imgbase;
274 	vm_offset_t		rva;
275 {
276 	image_optional_header	opt_hdr;
277 	image_section_header	*sect_hdr;
278 	image_dos_header	*dos_hdr;
279 	image_nt_header		*nt_hdr;
280 	int			i = 0, sections, fixedlen;
281 
282 	if (pe_get_optional_header(imgbase, &opt_hdr))
283 		return (0);
284 
285 	sections = pe_numsections(imgbase);
286 
287 	dos_hdr = (image_dos_header *)imgbase;
288 	nt_hdr = (image_nt_header *)(imgbase + dos_hdr->idh_lfanew);
289 	sect_hdr = IMAGE_FIRST_SECTION(nt_hdr);
290 
291 	/*
292 	 * The test here is to see if the RVA falls somewhere
293 	 * inside the section, based on the section's start RVA
294 	 * and its length. However it seems sometimes the
295 	 * virtual length isn't enough to cover the entire
296 	 * area of the section. We fudge by taking into account
297 	 * the section alignment and rounding the section length
298 	 * up to a page boundary.
299 	 */
300 	while (i++ < sections) {
301 		fixedlen = sect_hdr->ish_misc.ish_vsize;
302 		fixedlen += ((opt_hdr.ioh_sectalign - 1) -
303 		    sect_hdr->ish_misc.ish_vsize) &
304 		    (opt_hdr.ioh_sectalign - 1);
305 		if (sect_hdr->ish_vaddr <= (uint32_t)rva &&
306 		    (sect_hdr->ish_vaddr + fixedlen) >
307 		    (uint32_t)rva)
308 			break;
309 		sect_hdr++;
310 	}
311 
312 	if (i > sections)
313 		return (0);
314 
315 	return ((vm_offset_t)(imgbase + rva - sect_hdr->ish_vaddr +
316 	    sect_hdr->ish_rawdataaddr));
317 }
318 
319 /*
320  * Get the section header for a particular section. Note that
321  * section names can be anything, but there are some standard
322  * ones (.text, .data, .rdata, .reloc).
323  */
324 
325 int
pe_get_section(imgbase,hdr,name)326 pe_get_section(imgbase, hdr, name)
327 	vm_offset_t		imgbase;
328 	image_section_header	*hdr;
329 	const char		*name;
330 {
331 	image_dos_header	*dos_hdr;
332 	image_nt_header		*nt_hdr;
333 	image_section_header	*sect_hdr;
334 
335 	int			i, sections;
336 
337 	if (imgbase == 0 || hdr == NULL)
338 		return (EINVAL);
339 
340 	if (pe_is_nt_image(imgbase))
341 		return (EINVAL);
342 
343 	sections = pe_numsections(imgbase);
344 
345 	dos_hdr = (image_dos_header *)imgbase;
346 	nt_hdr = (image_nt_header *)(imgbase + dos_hdr->idh_lfanew);
347 	sect_hdr = IMAGE_FIRST_SECTION(nt_hdr);
348 
349 	for (i = 0; i < sections; i++) {
350 		if (!strcmp ((char *)&sect_hdr->ish_name, name)) {
351 			bcopy((char *)sect_hdr, (char *)hdr,
352 			    sizeof(image_section_header));
353 			return (0);
354 		} else
355 			sect_hdr++;
356 	}
357 
358 	return (ENOEXEC);
359 }
360 
361 /*
362  * Apply the base relocations to this image. The relocation table
363  * resides within the .reloc section. Relocations are specified in
364  * blocks which refer to a particular page. We apply the relocations
365  * one page block at a time.
366  */
367 
368 int
pe_relocate(imgbase)369 pe_relocate(imgbase)
370 	vm_offset_t		imgbase;
371 {
372 	image_section_header	sect;
373 	image_base_reloc	*relhdr;
374 	uint16_t		rel, *sloc;
375 	vm_offset_t		base;
376 	vm_size_t		delta;
377 	uint32_t		*lloc;
378 	uint64_t		*qloc;
379 	int			i, count;
380 	vm_offset_t		txt;
381 
382 	base = pe_imagebase(imgbase);
383 	pe_get_section(imgbase, &sect, ".text");
384 	txt = pe_translate_addr(imgbase, sect.ish_vaddr);
385 	delta = (uint32_t)(txt) - base - sect.ish_vaddr;
386 
387 	pe_get_section(imgbase, &sect, ".reloc");
388 
389 	relhdr = (image_base_reloc *)(imgbase + sect.ish_rawdataaddr);
390 
391 	do {
392 		count = (relhdr->ibr_blocksize -
393 		    (sizeof(uint32_t) * 2)) / sizeof(uint16_t);
394 		for (i = 0; i < count; i++) {
395 			rel = relhdr->ibr_rel[i];
396 			switch (IMR_RELTYPE(rel)) {
397 			case IMAGE_REL_BASED_ABSOLUTE:
398 				break;
399 			case IMAGE_REL_BASED_HIGHLOW:
400 				lloc = (uint32_t *)pe_translate_addr(imgbase,
401 				    relhdr->ibr_vaddr + IMR_RELOFFSET(rel));
402 				*lloc = pe_translate_addr(imgbase,
403 				    (*lloc - base));
404 				break;
405 			case IMAGE_REL_BASED_HIGH:
406 				sloc = (uint16_t *)pe_translate_addr(imgbase,
407 				    relhdr->ibr_vaddr + IMR_RELOFFSET(rel));
408 				*sloc += (delta & 0xFFFF0000) >> 16;
409 				break;
410 			case IMAGE_REL_BASED_LOW:
411 				sloc = (uint16_t *)pe_translate_addr(imgbase,
412 				    relhdr->ibr_vaddr + IMR_RELOFFSET(rel));
413 				*sloc += (delta & 0xFFFF);
414 				break;
415 			case IMAGE_REL_BASED_DIR64:
416 				qloc = (uint64_t *)pe_translate_addr(imgbase,
417 				    relhdr->ibr_vaddr + IMR_RELOFFSET(rel));
418 				*qloc = pe_translate_addr(imgbase,
419 				    (*qloc - base));
420 				break;
421 
422 			default:
423 				printf("[%d]reloc type: %d\n",i,
424 				    IMR_RELTYPE(rel));
425 				break;
426 			}
427 		}
428 		relhdr = (image_base_reloc *)((vm_offset_t)relhdr +
429 		    relhdr->ibr_blocksize);
430 	} while (relhdr->ibr_blocksize);
431 
432 	return (0);
433 }
434 
435 /*
436  * Return the import descriptor for a particular module. An image
437  * may be linked against several modules, typically HAL.dll, ntoskrnl.exe
438  * and NDIS.SYS. For each module, there is a list of imported function
439  * names and their addresses.
440  *
441  * Note: module names are case insensitive!
442  */
443 
444 int
pe_get_import_descriptor(imgbase,desc,module)445 pe_get_import_descriptor(imgbase, desc, module)
446 	vm_offset_t		imgbase;
447 	image_import_descriptor	*desc;
448 	char			*module;
449 {
450 	vm_offset_t		offset;
451 	image_import_descriptor	*imp_desc;
452 	char			*modname;
453 
454 	if (imgbase == 0 || module == NULL || desc == NULL)
455 		return (EINVAL);
456 
457 	offset = pe_directory_offset(imgbase, IMAGE_DIRECTORY_ENTRY_IMPORT);
458 	if (offset == 0)
459 		return (ENOENT);
460 
461 	imp_desc = (void *)offset;
462 
463 	while (imp_desc->iid_nameaddr) {
464 		modname = (char *)pe_translate_addr(imgbase,
465 		    imp_desc->iid_nameaddr);
466 		if (!strncasecmp(module, modname, strlen(module))) {
467 			bcopy((char *)imp_desc, (char *)desc,
468 			    sizeof(image_import_descriptor));
469 			return (0);
470 		}
471 		imp_desc++;
472 	}
473 
474 	return (ENOENT);
475 }
476 
477 int
pe_get_messagetable(imgbase,md)478 pe_get_messagetable(imgbase, md)
479 	vm_offset_t		imgbase;
480 	message_resource_data	**md;
481 {
482 	image_resource_directory	*rdir, *rtype;
483 	image_resource_directory_entry	*dent, *dent2;
484 	image_resource_data_entry	*rent;
485 	vm_offset_t		offset;
486 	int			i;
487 
488 	if (imgbase == 0)
489 		return (EINVAL);
490 
491 	offset = pe_directory_offset(imgbase, IMAGE_DIRECTORY_ENTRY_RESOURCE);
492 	if (offset == 0)
493 		return (ENOENT);
494 
495 	rdir = (image_resource_directory *)offset;
496 
497 	dent = (image_resource_directory_entry *)(offset +
498 	    sizeof(image_resource_directory));
499 
500 	for (i = 0; i < rdir->ird_id_entries; i++){
501 		if (dent->irde_name != RT_MESSAGETABLE)	{
502 			dent++;
503 			continue;
504 		}
505 		dent2 = dent;
506 		while (dent2->irde_dataoff & RESOURCE_DIR_FLAG) {
507 			rtype = (image_resource_directory *)(offset +
508 			    (dent2->irde_dataoff & ~RESOURCE_DIR_FLAG));
509 			dent2 = (image_resource_directory_entry *)
510 			    ((uintptr_t)rtype +
511 			     sizeof(image_resource_directory));
512 		}
513 		rent = (image_resource_data_entry *)(offset +
514 		    dent2->irde_dataoff);
515 		*md = (message_resource_data *)pe_translate_addr(imgbase,
516 		    rent->irde_offset);
517 		return (0);
518 	}
519 
520 	return (ENOENT);
521 }
522 
523 int
pe_get_message(imgbase,id,str,len,flags)524 pe_get_message(imgbase, id, str, len, flags)
525 	vm_offset_t		imgbase;
526 	uint32_t		id;
527 	char			**str;
528 	int			*len;
529 	uint16_t		*flags;
530 {
531 	message_resource_data	*md = NULL;
532 	message_resource_block	*mb;
533 	message_resource_entry	*me;
534 	uint32_t		i;
535 
536 	pe_get_messagetable(imgbase, &md);
537 
538 	if (md == NULL)
539 		return (ENOENT);
540 
541 	mb = (message_resource_block *)((uintptr_t)md +
542 	    sizeof(message_resource_data));
543 
544 	for (i = 0; i < md->mrd_numblocks; i++) {
545 		if (id >= mb->mrb_lowid && id <= mb->mrb_highid) {
546 			me = (message_resource_entry *)((uintptr_t)md +
547 			    mb->mrb_entryoff);
548 			for (i = id - mb->mrb_lowid; i > 0; i--)
549 				me = (message_resource_entry *)((uintptr_t)me +
550 				    me->mre_len);
551 			*str = me->mre_text;
552 			*len = me->mre_len;
553 			*flags = me->mre_flags;
554 			return (0);
555 		}
556 		mb++;
557 	}
558 
559 	return (ENOENT);
560 }
561 
562 /*
563  * Find the function that matches a particular name. This doesn't
564  * need to be particularly speedy since it's only run when loading
565  * a module for the first time.
566  */
567 
568 static vm_offset_t
pe_functbl_match(functbl,name)569 pe_functbl_match(functbl, name)
570 	image_patch_table	*functbl;
571 	char			*name;
572 {
573 	image_patch_table	*p;
574 
575 	if (functbl == NULL || name == NULL)
576 		return (0);
577 
578 	p = functbl;
579 
580 	while (p->ipt_name != NULL) {
581 		if (!strcmp(p->ipt_name, name))
582 			return ((vm_offset_t)p->ipt_wrap);
583 		p++;
584 	}
585 	printf("no match for %s\n", name);
586 
587 	/*
588 	 * Return the wrapper pointer for this routine.
589 	 * For x86, this is the same as the funcptr.
590 	 * For amd64, this points to a wrapper routine
591 	 * that does calling convention translation and
592 	 * then invokes the underlying routine.
593 	 */
594 	return ((vm_offset_t)p->ipt_wrap);
595 }
596 
597 /*
598  * Patch the imported function addresses for a given module.
599  * The caller must specify the module name and provide a table
600  * of function pointers that will be patched into the jump table.
601  * Note that there are actually two copies of the jump table: one
602  * copy is left alone. In a .SYS file, the jump tables are usually
603  * merged into the INIT segment.
604  */
605 
606 int
pe_patch_imports(imgbase,module,functbl)607 pe_patch_imports(imgbase, module, functbl)
608 	vm_offset_t		imgbase;
609 	char			*module;
610 	image_patch_table	*functbl;
611 {
612 	image_import_descriptor	imp_desc;
613 	char			*fname;
614 	vm_offset_t		*nptr, *fptr;
615 	vm_offset_t		func;
616 
617 	if (imgbase == 0 || module == NULL || functbl == NULL)
618 		return (EINVAL);
619 
620 	if (pe_get_import_descriptor(imgbase, &imp_desc, module))
621 		return (ENOEXEC);
622 
623 	nptr = (vm_offset_t *)pe_translate_addr(imgbase,
624 	    imp_desc.iid_import_name_table_addr);
625 	fptr = (vm_offset_t *)pe_translate_addr(imgbase,
626 	    imp_desc.iid_import_address_table_addr);
627 
628 	while (nptr != NULL && pe_translate_addr(imgbase, *nptr)) {
629 		fname = (char *)pe_translate_addr(imgbase, (*nptr) + 2);
630 		func = pe_functbl_match(functbl, fname);
631 		if (func)
632 			*fptr = func;
633 #ifdef notdef
634 		if (*fptr == 0)
635 			return (ENOENT);
636 #endif
637 		nptr++;
638 		fptr++;
639 	}
640 
641 	return (0);
642 }
643