📄 pe_linker.c
字号:
/* * Copyright (C) 2003-2005 Pontus Fuchs, Giridhar Pemmasani * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * */#ifdef TEST_LOADER#include <stdio.h>#include <stdint.h>#include <string.h>#include <linux/types.h>#include <asm/errno.h>#else#include <linux/types.h>#include <asm/errno.h>#include "ntoskernel.h"#endifstruct pe_exports { char *dll; char *name; WRAP_EXPORT_FUNC addr;};static struct pe_exports pe_exports[40];static int num_pe_exports;#define RVA2VA(image, rva, type) (type)(ULONG_PTR)((void *)image + rva)#define CHECK_SZ(a,b) { if (sizeof(a) != b) { \ ERROR("%s is bad, got %zd, expected %d", \ #a , sizeof(a), (b)); return -EINVAL; } }#ifdef TEST_LOADER#define WRAP_EXPORT_FUNC char *static WRAP_EXPORT_FUNC get_export(char *name){ return name;}#elseextern struct wrap_export ntoskernel_exports[], ndis_exports[], misc_funcs_exports[], hal_exports[];#ifdef CONFIG_USBextern struct wrap_export usb_exports[];#endifstatic char *get_export(char *name){ int i; for (i = 0 ; ntoskernel_exports[i].name != NULL; i++) if (strcmp(ntoskernel_exports[i].name, name) == 0) return (char *)ntoskernel_exports[i].func; for (i = 0 ; ndis_exports[i].name != NULL; i++) if (strcmp(ndis_exports[i].name, name) == 0) return (char *)ndis_exports[i].func; for (i = 0 ; misc_funcs_exports[i].name != NULL; i++) if (strcmp(misc_funcs_exports[i].name, name) == 0) return (char *)misc_funcs_exports[i].func; for (i = 0 ; hal_exports[i].name != NULL; i++) if (strcmp(hal_exports[i].name, name) == 0) return (char *)hal_exports[i].func;#ifdef CONFIG_USB for (i = 0 ; usb_exports[i].name != NULL; i++) if (strcmp(usb_exports[i].name, name) == 0) return (char *)usb_exports[i].func;#endif for (i = 0; i < num_pe_exports; i++) if (strcmp(pe_exports[i].name, name) == 0) return (char *)pe_exports[i].addr; return NULL;}#endif // TEST_LOADERstatic void *get_dll_init(char *name){ int i; for (i = 0; i < num_pe_exports; i++) if ((strcmp(pe_exports[i].dll, name) == 0) && (strcmp(pe_exports[i].name, "DllInitialize") == 0)) return (void *)pe_exports[i].addr; return NULL;}#if DEBUG >= 3static const char *image_directory_name[] = { "EXPORT", "IMPORT", "RESOURCE", "EXCEPTION", "SECURITY", "BASERELOC", "DEBUG", "COPYRIGHT", "GLOBALPTR", "TLS", "LOAD_CONFIG", "BOUND_IMPORT", "IAT", "DELAY_IMPORT", "COM_DESCRIPTOR" };#endif/* * Find and validate the coff header * */static int check_nt_hdr(IMAGE_NT_HEADERS *nt_hdr){ int i; WORD attr; PIMAGE_OPTIONAL_HEADER opt_hdr; /* Validate the "PE\0\0" signature */ if (nt_hdr->Signature != IMAGE_NT_SIGNATURE) { ERROR("is this driver file? bad signature %08x", nt_hdr->Signature); return -EINVAL; } opt_hdr = &nt_hdr->OptionalHeader; /* Make sure Image is PE32 or PE32+ */#ifdef CONFIG_X86_64 if (opt_hdr->Magic != IMAGE_NT_OPTIONAL_HDR64_MAGIC) { ERROR("Windows driver is not 64-bit; bad magic: %04X", opt_hdr->Magic); return -EINVAL; }#else if (opt_hdr->Magic != IMAGE_NT_OPTIONAL_HDR32_MAGIC) { ERROR("Windows driver is not 32-bit; bad magic: %04X", opt_hdr->Magic); return -EINVAL; }#endif /* Validate the image for the current architecture. */ if (nt_hdr->FileHeader.Machine !=#ifdef CONFIG_X86_64 IMAGE_FILE_MACHINE_AMD64#else IMAGE_FILE_MACHINE_I386#endif ) { ERROR("driver is not for current architecture " " (PE signature is %04X)", nt_hdr->FileHeader.Machine); return -EINVAL; } /* Must have attributes */#ifdef CONFIG_X86_64 attr = IMAGE_FILE_EXECUTABLE_IMAGE | IMAGE_FILE_LARGE_ADDRESS_AWARE;#else attr = IMAGE_FILE_EXECUTABLE_IMAGE | IMAGE_FILE_32BIT_MACHINE;#endif if ((nt_hdr->FileHeader.Characteristics & attr) != attr) return -EINVAL; /* Must be relocatable */ attr = IMAGE_FILE_RELOCS_STRIPPED; if ((nt_hdr->FileHeader.Characteristics & attr)) return -EINVAL; /* Make sure we have at least one section */ if (nt_hdr->FileHeader.NumberOfSections == 0) return -EINVAL; if (opt_hdr->SectionAlignment < opt_hdr->FileAlignment) { ERROR("alignment mismatch: secion: 0x%x, file: 0x%x", opt_hdr->SectionAlignment, opt_hdr->FileAlignment); return -EINVAL; } DBGTRACE1("number of datadictionary entries %d", opt_hdr->NumberOfRvaAndSizes); for (i = 0; i < opt_hdr->NumberOfRvaAndSizes; i++) { DBGTRACE3("datadirectory %s RVA:%X Size:%d", (i<=IMAGE_DIRECTORY_ENTRY_COM_DESCRIPTOR)? image_directory_name[i] : "unknown", opt_hdr->DataDirectory[i].VirtualAddress, opt_hdr->DataDirectory[i].Size); } if ((nt_hdr->FileHeader.Characteristics & IMAGE_FILE_DLL)) return IMAGE_FILE_DLL; if ((nt_hdr->FileHeader.Characteristics & IMAGE_FILE_EXECUTABLE_IMAGE)) return IMAGE_FILE_EXECUTABLE_IMAGE; return -EINVAL;}static int import(void *image, IMAGE_IMPORT_DESCRIPTOR *dirent, char *dll){ ULONG_PTR *lookup_tbl, *address_tbl; char *symname = NULL; int i; int ret = 0; void *adr; lookup_tbl = RVA2VA(image, dirent->u.OriginalFirstThunk, ULONG_PTR *); address_tbl = RVA2VA(image, dirent->FirstThunk, ULONG_PTR *); for (i = 0; lookup_tbl[i]; i++) { if (IMAGE_SNAP_BY_ORDINAL(lookup_tbl[i])) { ERROR("ordinal import not supported: %Lu", (uint64_t)lookup_tbl[i]); return -1; } else { symname = RVA2VA(image, ((lookup_tbl[i] & ~IMAGE_ORDINAL_FLAG) + 2), char *); } adr = get_export(symname); if (adr != NULL) DBGTRACE1("found symbol: %s:%s, rva = %Lu", dll, symname, (uint64_t)address_tbl[i]); if (adr == NULL) { ERROR("unknown symbol: %s:%s", dll, symname); ret = -1; } DBGTRACE1("importing rva: %p, %p: %s : %s", (void *)(address_tbl[i]), adr, dll, symname); address_tbl[i] = (ULONG_PTR)adr; } return ret;}static int read_exports(struct pe_image *pe){ IMAGE_EXPORT_DIRECTORY *export_dir_table; uint32_t *export_addr_table; int i; uint32_t *name_table; PIMAGE_OPTIONAL_HEADER opt_hdr; IMAGE_DATA_DIRECTORY *export_data_dir; opt_hdr = &pe->nt_hdr->OptionalHeader; export_data_dir = &opt_hdr->DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT]; if (export_data_dir->Size == 0) { DBGTRACE1("no exports"); return 0; } export_dir_table = RVA2VA(pe->image, export_data_dir->VirtualAddress, IMAGE_EXPORT_DIRECTORY *); name_table = (unsigned int *)(pe->image + export_dir_table->AddressOfNames); export_addr_table = (uint32_t *) (pe->image + export_dir_table->AddressOfFunctions); for (i = 0; i < export_dir_table->NumberOfNames; i++) { if (export_data_dir->VirtualAddress <= *export_addr_table || *export_addr_table >= (export_data_dir->VirtualAddress + export_data_dir->Size)) DBGTRACE1("forwarder rva"); DBGTRACE1("export symbol: %s, at %p", (char *)(pe->image + *name_table), pe->image + *export_addr_table); pe_exports[num_pe_exports].dll = pe->name; pe_exports[num_pe_exports].name = pe->image + *name_table; pe_exports[num_pe_exports].addr = pe->image + *export_addr_table; num_pe_exports++; name_table++; export_addr_table++; } return 0;}static int fixup_imports(void *image, IMAGE_NT_HEADERS *nt_hdr){ int i; char *name; int ret = 0; IMAGE_IMPORT_DESCRIPTOR *dirent; IMAGE_DATA_DIRECTORY *import_data_dir; PIMAGE_OPTIONAL_HEADER opt_hdr; opt_hdr = &nt_hdr->OptionalHeader; import_data_dir = &opt_hdr->DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT]; dirent = RVA2VA(image, import_data_dir->VirtualAddress, IMAGE_IMPORT_DESCRIPTOR *); for (i = 0; dirent[i].Name; i++) { name = RVA2VA(image, dirent[i].Name, char*); DBGTRACE1("imports from dll: %s\n", name); ret += import(image, &dirent[i], name); } return ret;}static int fixup_reloc(void *image, IMAGE_NT_HEADERS *nt_hdr){ ULONG_PTR base; ULONG_PTR size; IMAGE_BASE_RELOCATION *fixup_block; IMAGE_DATA_DIRECTORY *base_reloc_data_dir; PIMAGE_OPTIONAL_HEADER opt_hdr; opt_hdr = &nt_hdr->OptionalHeader; base = opt_hdr->ImageBase; base_reloc_data_dir = &opt_hdr->DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC]; if (base_reloc_data_dir->Size == 0) return 0; fixup_block = RVA2VA(image, base_reloc_data_dir->VirtualAddress, IMAGE_BASE_RELOCATION *); DBGTRACE3("fixup_block=%p, image=%p", fixup_block, image); DBGTRACE3("fixup_block info: %x %d", fixup_block->VirtualAddress, fixup_block->SizeOfBlock); while (fixup_block->SizeOfBlock) { int i; WORD fixup, offset; size = (fixup_block->SizeOfBlock - sizeof(IMAGE_BASE_RELOCATION)) / sizeof(WORD); DBGTRACE3("found %Lu relocations in this block", (uint64_t)size); for (i = 0; i < size; i++) { fixup = fixup_block->TypeOffset[i]; offset = fixup & 0xfff; switch ((fixup >> 12) & 0x0f) { case IMAGE_REL_BASED_ABSOLUTE: break; case IMAGE_REL_BASED_HIGHLOW: { uint32_t addr; uint32_t *loc = RVA2VA(image, fixup_block->VirtualAddress + offset, uint32_t *); addr = RVA2VA(image, (*loc - base), uint32_t); DBGTRACE3("relocation: *%p (Val:%X)= %X", loc, *loc, addr); *loc = addr; } break; case IMAGE_REL_BASED_DIR64: { uint64_t addr; uint64_t *loc = RVA2VA(image, fixup_block->VirtualAddress + offset, uint64_t *); addr = RVA2VA(image, (*loc - base), uint64_t); DBGTRACE3("relocation: *%p (Val:%llX)= %llx", loc, *loc, addr); *loc = addr; } break; default: ERROR("unknown fixup: %08X", (fixup >> 12) & 0x0f); return -EOPNOTSUPP; break; } } DBGTRACE1("finished relocating block"); fixup_block = (IMAGE_BASE_RELOCATION *) ((void *)fixup_block + fixup_block->SizeOfBlock); }; DBGTRACE1("done relocating all"); return 0;}/* Expand the image in memroy if necessary. The image on disk does not * necessarily maps the image of the driver in memory, so we have to * re-write it in order to fullfill the sections alignements. The * advantage to do that is that rva_to_va becomes a simple * addition. */static int fix_pe_image(struct pe_image *pe){ void *image; IMAGE_SECTION_HEADER *sect_hdr; int i, sections; int image_size; if (pe->size == pe->opt_hdr->SizeOfImage) { /* Nothing to do */ return 0; } image_size = pe->opt_hdr->SizeOfImage;#ifdef CONFIG_X86_64#ifdef PAGE_KERNEL_EXECUTABLE image = __vmalloc(image_size, GFP_KERNEL | __GFP_HIGHMEM, PAGE_KERNEL_EXECUTABLE);#elif defined PAGE_KERNEL_EXEC image = __vmalloc(image_size, GFP_KERNEL | __GFP_HIGHMEM, PAGE_KERNEL_EXEC);#else#error x86_64 should have either PAGE_KERNEL_EXECUTABLE or PAGE_KERNEL_EXEC#endif#else image = vmalloc(image_size);#endif if (image == NULL) { ERROR("failed to allocate enough space for new image:" " %d bytes", image_size); return -ENOMEM; } /* Copy all the headers, ie everything before the first section. */ sections = pe->nt_hdr->FileHeader.NumberOfSections; sect_hdr = IMAGE_FIRST_SECTION(pe->nt_hdr); DBGTRACE3("copying headers: %u bytes", sect_hdr->PointerToRawData); memcpy(image, pe->image, sect_hdr->PointerToRawData); /* Copy all the sections */ for (i = 0; i < sections; i++) { DBGTRACE3("Copy section %s from %x to %x", sect_hdr->Name, sect_hdr->PointerToRawData, sect_hdr->VirtualAddress); if (sect_hdr->VirtualAddress+sect_hdr->SizeOfRawData > image_size) { ERROR("Invalid section %s in driver", sect_hdr->Name); vfree(image); return -EINVAL; } memcpy(image+sect_hdr->VirtualAddress, pe->image + sect_hdr->PointerToRawData, sect_hdr->SizeOfRawData); sect_hdr++; } vfree(pe->image); pe->image = image; pe->size = image_size; /* Update our internal pointers */ pe->nt_hdr = (IMAGE_NT_HEADERS *) (pe->image + ((IMAGE_DOS_HEADER *)pe->image)->e_lfanew); pe->opt_hdr = &pe->nt_hdr->OptionalHeader; DBGTRACE3("set nt headers: nt_hdr=%p, opt_hdr=%p, image=%p", pe->nt_hdr, pe->opt_hdr, pe->image); return 0;}int load_pe_images(struct pe_image *pe_image, int n){ int i; struct pe_image *pe;#ifdef DEBUG /* Sanity checkings */ CHECK_SZ(IMAGE_SECTION_HEADER, IMAGE_SIZEOF_SECTION_HEADER); CHECK_SZ(IMAGE_FILE_HEADER, IMAGE_SIZEOF_FILE_HEADER); CHECK_SZ(IMAGE_OPTIONAL_HEADER, IMAGE_SIZEOF_NT_OPTIONAL_HEADER); CHECK_SZ(IMAGE_NT_HEADERS, 4 + IMAGE_SIZEOF_FILE_HEADER + IMAGE_SIZEOF_NT_OPTIONAL_HEADER); CHECK_SZ(IMAGE_DOS_HEADER, 0x40); CHECK_SZ(IMAGE_EXPORT_DIRECTORY, 40); CHECK_SZ(IMAGE_BASE_RELOCATION, 8); CHECK_SZ(IMAGE_IMPORT_DESCRIPTOR, 20);#endif for (i = 0; i < n; i++) { IMAGE_DOS_HEADER *dos_hdr; pe = &pe_image[i]; dos_hdr = pe->image; if (pe->size < sizeof(IMAGE_DOS_HEADER)) { DBGTRACE1("image too small: %d", pe->size); return -EINVAL; } pe->nt_hdr = (IMAGE_NT_HEADERS *)(pe->image + dos_hdr->e_lfanew); pe->opt_hdr = &pe->nt_hdr->OptionalHeader; pe->type = check_nt_hdr(pe->nt_hdr); if (pe->type <= 0) { DBGTRACE1("type <= 0"); return -EINVAL; } if (fix_pe_image(pe)) { DBGTRACE1("bad PE image"); return -EINVAL; } if (read_exports(pe)) { DBGTRACE1("read exports failed"); return -EINVAL; } } for (i = 0; i < n; i++) { pe = &pe_image[i]; if (fixup_reloc(pe->image, pe->nt_hdr)) { DBGTRACE1("fixup reloc failed"); return -EINVAL; } if (fixup_imports(pe->image, pe->nt_hdr)) { DBGTRACE1("fixup imports failed"); return -EINVAL; } flush_icache_range(pe->image, pe->size); pe->entry = RVA2VA(pe->image, pe->opt_hdr->AddressOfEntryPoint, void *); DBGTRACE1("entry is at %p, rva at %08X", pe->entry, pe->opt_hdr->AddressOfEntryPoint); } for (i = 0; i < n; i++) { pe = &pe_image[i]; if (pe->type == IMAGE_FILE_DLL) { struct unicode_string ustring; char *buf = "0/0t0m0p00"; int (*dll_entry)(struct unicode_string *ustring) STDCALL; memset(&ustring, 0, sizeof(ustring)); ustring.buf = (wchar_t *)buf; dll_entry = (void *)get_dll_init(pe->name); DBGTRACE1("calling dll_init at %p", dll_entry); if (!dll_entry || dll_entry(&ustring)) ERROR("DLL initialize failed for %s", pe->name); } else if (pe->type != IMAGE_FILE_EXECUTABLE_IMAGE) ERROR("illegal image type: %d", pe->type); } return 0;}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -