#include <kernel/kernel.h> #include <kernel/proc.h> #include <kernel/ramdisk.h> #include <kernel/vmm.h> #include <kernel/handle.h> #include <kernel/thread.h> #include <kernel/fs.h> #include <os/pe.h> #include <wchar.h> #include <stdlib.h> #include <string.h> #include <errno.h> module_t* peLoad(process_t* proc, const wchar_t* file, dword base) { file_t *fd; module_t* mod; for (mod = proc->mod_first; mod; mod = mod->next) if (wcsicmp(mod->name, file) == 0) return mod; fd = fsOpen(file); if (!fd) return NULL; return peLoadMemory(proc, file, fd, base); } module_t* peLoadMemory(process_t* proc, const wchar_t* file, struct file_t *fd, dword base) { module_t *mod; IMAGE_DOS_HEADER dos; IMAGE_PE_HEADERS pe; vm_area_t *area; addr_t new_base; size_t size; fsSeek(fd, 0); size = fsRead(fd, &dos, sizeof(dos)); if (size < sizeof(dos)) { wprintf(L"%s: only %d bytes read (%d)\n", file, size, errno); return NULL; } if (dos.e_magic != IMAGE_DOS_SIGNATURE) { wprintf(L"%s: not an executable (%S)\n", file, &dos); return NULL; } fsSeek(fd, dos.e_lfanew); fsRead(fd, &pe, sizeof(pe)); if (pe.Signature != IMAGE_NT_SIGNATURE) { char *c = (char*) &pe.Signature; wprintf(L"%s: not PE format (%c%c%c%c)\n", file, c[0], c[1], c[2], c[3]); return NULL; } mod = hndAlloc(sizeof(module_t), proc); mod->name = wcsdup(file); if (base == NULL) base = pe.OptionalHeader.ImageBase; new_base = base; while ((area = vmmArea(proc, (const void*) new_base))) { new_base = (addr_t) area->start + area->pages * PAGE_SIZE; wprintf(L"%s: clashed with %08x, adjusting base to %08x\n", file, area->start, new_base); } assert(new_base == base); mod->base = base; mod->length = pe.OptionalHeader.SizeOfImage; mod->prev = proc->mod_last; mod->next = NULL; mod->entry = base + pe.OptionalHeader.AddressOfEntryPoint; mod->file = fd; mod->sizeof_headers = pe.OptionalHeader.SizeOfHeaders; mod->imported = false; //mod->raw_data = vmmMapFile(proc, NULL, (size_t) -1, fd, MEM_READ | MEM_WRITE); /*wprintf(L"%s: %x..%x (pbase = %x)\n", file, mod->base, mod->base + mod->length, pe->OptionalHeader.ImageBase);*/ if (proc->mod_last) proc->mod_last->next = mod; if (proc->mod_first == NULL) proc->mod_first = mod; proc->mod_last = mod; /*area = vmmAlloc(proc, mod->length / PAGE_SIZE, base, 0); if (area == NULL || (addr_t) area->start != base) { vmmFree(proc, area); return NULL; }*/ return mod; } static IMAGE_PE_HEADERS* peGetHeaders(addr_t base) { IMAGE_DOS_HEADER *dos = (IMAGE_DOS_HEADER*) base; return (IMAGE_PE_HEADERS*) (base + dos->e_lfanew); } static bool peDoImports(process_t* proc, module_t* mod, const IMAGE_DATA_DIRECTORY *directories) { IMAGE_IMPORT_DESCRIPTOR * imp; IMAGE_THUNK_DATA *import_entry; IMAGE_THUNK_DATA *mapped_entry; const char* name; enum { bound_none, bound_old, bound_new } bound; wchar_t temp[256]; module_t* newmod; dword args[3]; if (directories[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress == 0) return true; imp = (IMAGE_IMPORT_DESCRIPTOR*) (mod->base + directories[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress); mod->imported = true; for (; imp->Name; imp++) { if (imp->Name >= mod->length) { //wprintf(L"%s: name %d is invalid (%x)\n", //mod->name, imp - (IMAGE_IMPORT_DESCRIPTOR*) (mod->base + //directories[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress), //imp->Name); continue; } name = (const char*) (mod->base + (addr_t) imp->Name); if (imp->TimeDateStamp == ~0UL) bound = bound_new; else if (imp->TimeDateStamp) bound = bound_old; else bound = bound_none; if (imp->u.OriginalFirstThunk) { import_entry = (IMAGE_THUNK_DATA*) (mod->base + (addr_t) imp->u.OriginalFirstThunk); mapped_entry = (IMAGE_THUNK_DATA*) (mod->base + (addr_t) imp->FirstThunk); } else { _cputws(L"\t(hint table missing, probably Borland bug)"); import_entry = (IMAGE_THUNK_DATA*) (mod->base + (addr_t) imp->FirstThunk); mapped_entry = 0; bound = bound_none; } //wprintf(L"base = %x, name = %x\n", mod->base, imp->Name); mbstowcs(temp, name, countof(temp)); //wprintf(L"Loading library %s...", temp); newmod = peLoad(proc, temp, 0); //if (newmod) //wprintf(L"done\n"); if (!newmod) { wprintf(L"Failed to load %s\n", temp); mod->imported = false; exception(current, NULL, EXCEPTION_MISSING_DLL, (dword) name); return true; //return E_FAIL; } args[0] = newmod->base; args[1] = 0; args[2] = 0; thrCall(current, (void*) newmod->entry, args, sizeof(args)); { int count, nextforwarder = bound==bound_old ? imp->ForwarderChain : -1; for (count = 0; import_entry->u1.Ordinal; count++, import_entry++, /*bound ?*/ mapped_entry++/* : 0*/) // xxx - what is this? { if (IMAGE_SNAP_BY_ORDINAL(import_entry->u1.Ordinal)) wprintf(L"\t%6lu %20S", IMAGE_ORDINAL(import_entry->u1.Ordinal),"<ordinal>"); else { IMAGE_IMPORT_BY_NAME *name_import = (IMAGE_IMPORT_BY_NAME*) (mod->base + (addr_t) import_entry->u1.AddressOfData); //wprintf(L"\t%6u %20S\n", name_import->Hint, name_import->Name); mapped_entry->u1.Function = (dword*) peGetExport(newmod, name_import->Name); if (mapped_entry->u1.Function == NULL) { wprintf(L"Failed to access %S from %s\n", name_import->Name, temp); mod->imported = false; exception(current, NULL, EXCEPTION_MISSING_IMPORT, (dword) name_import->Name); return true; //return E_FAIL; } //wprintf(L"GetProcAddress returns %p\n", mapped_entry->u1.Function); } if (bound) { if (count != nextforwarder) wprintf(L"\t0x%12lx\n", (unsigned long)mapped_entry->u1.Function); else { wprintf(L"\t%12S\n", L" --> forward"); nextforwarder = (int)mapped_entry->u1.ForwarderString; } //else //_cputws(L"\n"); } } } } return true; } bool pePageFault(process_t* proc, module_t* mod, addr_t addr) { IMAGE_PE_HEADERS *pe; size_t size, raw_size; void *scn_base; dword priv; addr_t raw_offset; //if (wcsicmp(mod->name, L"kdebug.dll") == 0) //wprintf(L"%s: base = %08x length = %08x\n", mod->name, mod->base, mod->length); if (addr < mod->base || addr >= mod->base + mod->length) return false; //wprintf(L"pePageFault: %s at %p\n", mod->name, addr); if (addr >= mod->base && addr < mod->base + mod->sizeof_headers) { //wprintf(L"%x: headers: %d bytes\n", addr, mod->sizeof_headers); /* Map headers as a special case */ size = (mod->sizeof_headers + PAGE_SIZE - 1) & -PAGE_SIZE; raw_size = mod->sizeof_headers; priv = proc->level | MEM_READ | MEM_COMMIT | MEM_ZERO; scn_base = (void*) mod->base; //raw_data = mod->raw_data; raw_offset = 0; pe = NULL; } else { IMAGE_SECTION_HEADER *first_scn, *scn; int i; pe = peGetHeaders(mod->base); first_scn = IMAGE_FIRST_SECTION(pe); scn = NULL; for (i = 0; i < pe->FileHeader.NumberOfSections; i++) { if ((first_scn[i].Characteristics & IMAGE_SCN_CNT_UNINITIALIZED_DATA) && scn == NULL) scn = first_scn + i; else if (addr >= mod->base + first_scn[i].VirtualAddress && addr < mod->base + first_scn[i].VirtualAddress + first_scn[i].Misc.VirtualSize) { scn = first_scn + i; break; } } if (scn == NULL) { wprintf(L"%x: section not found\n", addr); return false; } scn_base = (void*) (mod->base + scn->VirtualAddress); size = (scn->Misc.VirtualSize + PAGE_SIZE - 1) & -PAGE_SIZE; //wprintf(L"%x: section %d: %8S %d bytes\n", addr, i, scn->Name, size); priv = proc->level | MEM_COMMIT | MEM_ZERO; if (scn->Characteristics & IMAGE_SCN_MEM_READ) priv |= MEM_READ; if (scn->Characteristics & IMAGE_SCN_MEM_WRITE) priv |= MEM_WRITE; if ((scn->Characteristics & IMAGE_SCN_CNT_UNINITIALIZED_DATA) == 0) //raw_data = (byte*) mod->raw_data + scn->PointerToRawData; raw_offset = scn->PointerToRawData; else //raw_data = NULL; raw_offset = -1; raw_size = scn->SizeOfRawData; } if (vmmArea(proc, scn_base)) { wprintf(L"Section already mapped\n"); return false; } //wprintf(L"%s: 0x%x bytes at 0x%x\n", mod->name, size, scn_base); if (!vmmAlloc(proc, size / PAGE_SIZE, (addr_t) scn_base, priv)) { wprintf(L"%s: failed to allocate section\n", mod->name); return false; } if (raw_offset != (addr_t) -1) { //wprintf(L"Copying %d bytes from %x\n", raw_size, raw_data); //memcpy(scn_base, raw_data, raw_size); fsSeek(mod->file, raw_offset); fsRead(mod->file, scn_base, raw_size); } //else //wprintf(L"Uninitialized data\n"); if (!pe) pe = peGetHeaders(mod->base); if (!mod->imported && !peDoImports(proc, mod, pe->OptionalHeader.DataDirectory)) { wprintf(L"%s: imports failed\n", mod->name); return false; } return true; } addr_t peGetExport(module_t* mod, const char* name) { IMAGE_PE_HEADERS* header; IMAGE_DATA_DIRECTORY* directories; IMAGE_EXPORT_DIRECTORY* exp; word *ordinal_table; dword *function_table; dword *name_table; size_t i; unsigned unused_slots = 0; /* functions with an RVA of 0 seem to be unused */ header = peGetHeaders(mod->base); directories = header->OptionalHeader.DataDirectory; if (directories[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress == 0) return NULL; exp = (IMAGE_EXPORT_DIRECTORY*) (mod->base + directories[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress); ordinal_table = (word*) (mod->base + (addr_t) exp->AddressOfNameOrdinals); function_table = (dword*) (mod->base + (addr_t) exp->AddressOfFunctions); name_table = exp->AddressOfNames ? (dword*) (mod->base + (addr_t) exp->AddressOfNames) : 0; for (i = 0; i < exp->NumberOfFunctions; i++) { dword addr = function_table[i]; if (!addr) /* skip unused slots */ { ++unused_slots; continue; } /* if names provided, list all names of this * export ordinal */ if (name_table) { size_t n; int found = 0; for (n = 0; n < exp->NumberOfNames; n++) { if (ordinal_table[n] == i) { if (stricmp((const char*) (mod->base + name_table[n]), name) == 0) return addr + mod->base; ++found; } } } /* entry point */ //if (addr >= section_base_virtual && addr < section_base_virtual + section_length) /* forwarder */ //wprintf(L"%S", adr(addr)); //else /* normal export */ } if (unused_slots) wprintf(L"\t-- there are %u unused slots --\n", unused_slots); return NULL; } void peUnload(process_t* proc, module_t* mod) { //vm_area_t *area; if (mod->next) mod->next->prev = mod->prev; if (mod->prev) mod->prev->next = mod->next; if (proc->mod_last == mod) proc->mod_last = mod->prev; if (proc->mod_first == mod) proc->mod_first = mod->next; /* xxx - free all areas allocated */ //area = vmmArea(proc, (const void*) mod->base); //vmmFree(proc, area); free(mod->name); hndFree(mod); }