diff --git a/src/emulator/memory_manager.hpp b/src/emulator/memory_manager.hpp index d4ec5021..11cf8d19 100644 --- a/src/emulator/memory_manager.hpp +++ b/src/emulator/memory_manager.hpp @@ -15,7 +15,7 @@ public: virtual void write_memory(uint64_t address, const void* data, size_t size) = 0; bool protect_memory(const uint64_t address, const size_t size, const memory_permission permissions, - memory_permission* old_permissions) + memory_permission* old_permissions = nullptr) { const auto entry = this->find_reserved_region(address); if (entry == this->reserved_regions_.end()) diff --git a/src/windows_emulator/main.cpp b/src/windows_emulator/main.cpp index d3035c7f..19ef20d3 100644 --- a/src/windows_emulator/main.cpp +++ b/src/windows_emulator/main.cpp @@ -95,6 +95,8 @@ namespace context.teb.access([&](TEB& teb) { + teb.ClientId.UniqueProcess = reinterpret_cast(1); + teb.ClientId.UniqueThread = reinterpret_cast(2); teb.NtTib.StackLimit = reinterpret_cast(STACK_ADDRESS); teb.NtTib.StackBase = reinterpret_cast((STACK_ADDRESS + STACK_SIZE)); teb.NtTib.Self = &context.teb.ptr()->NtTib; diff --git a/src/windows_emulator/module_mapper.cpp b/src/windows_emulator/module_mapper.cpp index 9525924b..8c73da15 100644 --- a/src/windows_emulator/module_mapper.cpp +++ b/src/windows_emulator/module_mapper.cpp @@ -3,6 +3,161 @@ namespace { + template + T offset_pointer(const void* data, const size_t offset) + { + return reinterpret_cast(static_cast(data) + offset); + } + + void collect_exports(mapped_binary& binary, const unsigned char* ptr, const IMAGE_OPTIONAL_HEADER& optional_header) + { + auto& export_directory_entry = optional_header.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT]; + if (export_directory_entry.VirtualAddress == 0 || export_directory_entry.Size == 0) + { + return; + } + + const auto* export_directory = reinterpret_cast(ptr + export_directory_entry. + VirtualAddress); + + //const auto function_count = export_directory->NumberOfFunctions; + const auto names_count = export_directory->NumberOfNames; + + const auto* names = reinterpret_cast(ptr + export_directory->AddressOfNames); + const auto* ordinals = reinterpret_cast(ptr + export_directory->AddressOfNameOrdinals); + const auto* functions = reinterpret_cast(ptr + export_directory->AddressOfFunctions); + + for (DWORD i = 0; i < names_count; i++) + { + exported_symbol symbol{}; + symbol.ordinal = ordinals[i]; + symbol.name = reinterpret_cast(ptr + names[i]); + symbol.rva = functions[symbol.ordinal]; + symbol.address = binary.image_base + symbol.rva; + + binary.exports.push_back(std::move(symbol)); + } + } + + void apply_relocations(x64_emulator& emu, const mapped_binary& binary, + const IMAGE_OPTIONAL_HEADER& optional_header) + { + const auto delta = binary.image_base - optional_header.ImageBase; + if (delta == 0) + { + return; + } + + const auto directory = &optional_header.DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC]; + if (directory->Size == 0) + { + return; + } + + + emulator_object relocation_object{emu, binary.image_base + directory->VirtualAddress}; + const auto end_address = relocation_object.value() + directory->Size; + + while (relocation_object.value() < end_address) + { + const auto relocation = relocation_object.read(); + if (relocation.VirtualAddress <= 0) + { + break; + } + + const auto dest = binary.image_base + relocation.VirtualAddress; + + const auto data_size = relocation.SizeOfBlock - sizeof(IMAGE_BASE_RELOCATION); + const auto entry_count = data_size / sizeof(uint16_t); + + std::vector relocations{}; + relocations.resize(entry_count); + + emu.read_memory(relocation_object.value() + relocation_object.size(), relocations.data(), data_size); + + for (const auto entry : relocations) + { + const int type = entry >> 12; + const int offset = entry & 0xfff; + + switch (type) + { + case IMAGE_REL_BASED_ABSOLUTE: + break; + + case IMAGE_REL_BASED_HIGHLOW: + { + emulator_object relocatable_object{emu, dest + offset}; + relocatable_object.access([&](DWORD& obj) + { + obj += static_cast(delta); + }); + break; + } + + case IMAGE_REL_BASED_DIR64: + { + emulator_object relocatable_object{emu, dest + offset}; + relocatable_object.access([&](ULONGLONG& obj) + { + obj += delta; + }); + + break; + } + + default: + throw std::runtime_error("Unknown relocation type: " + std::to_string(type)); + } + } + + relocation_object = emulator_object{ + emu, relocation_object.value() + relocation.SizeOfBlock + }; + } + } + + void map_sections(x64_emulator& emu, const mapped_binary& binary, const unsigned char* ptr, + const IMAGE_NT_HEADERS& nt_headers) + { + const std::span sections(IMAGE_FIRST_SECTION(&nt_headers), nt_headers.FileHeader.NumberOfSections); + + for (const auto& section : sections) + { + const auto target_ptr = binary.image_base + section.VirtualAddress; + + if (section.SizeOfRawData > 0) + { + const void* source_ptr = ptr + section.PointerToRawData; + + const auto size_of_data = std::min(section.SizeOfRawData, section.Misc.VirtualSize); + emu.write_memory(target_ptr, source_ptr, size_of_data); + } + + auto permissions = memory_permission::none; + + if (section.Characteristics & IMAGE_SCN_MEM_EXECUTE) + { + permissions |= memory_permission::exec; + } + + if (section.Characteristics & IMAGE_SCN_MEM_READ) + { + permissions |= memory_permission::read; + } + + if (section.Characteristics & IMAGE_SCN_MEM_WRITE) + { + permissions |= memory_permission::write; + } + + const auto size_of_section = page_align_up(std::max(section.SizeOfRawData, section.Misc.VirtualSize)); + + emu.protect_memory(target_ptr, size_of_section, permissions, nullptr); + } + } + mapped_binary map_module(x64_emulator& emu, const std::vector& module_data, const std::string& name) { @@ -33,67 +188,9 @@ namespace emu.write_memory(binary.image_base, ptr, optional_header.SizeOfHeaders); - const std::span sections(IMAGE_FIRST_SECTION(nt_headers), nt_headers->FileHeader.NumberOfSections); - - for (const auto& section : sections) - { - const auto target_ptr = binary.image_base + section.VirtualAddress; - - if (section.SizeOfRawData > 0) - { - const void* source_ptr = ptr + section.PointerToRawData; - - const auto size_of_data = std::min(section.SizeOfRawData, section.Misc.VirtualSize); - emu.write_memory(target_ptr, source_ptr, size_of_data); - } - auto permissions = memory_permission::none; - - if (section.Characteristics & IMAGE_SCN_MEM_EXECUTE) - { - permissions |= memory_permission::exec; - } - - if (section.Characteristics & IMAGE_SCN_MEM_READ) - { - permissions |= memory_permission::read; - } - - if (section.Characteristics & IMAGE_SCN_MEM_WRITE) - { - permissions |= memory_permission::write; - } - - const auto size_of_section = page_align_up(std::max(section.SizeOfRawData, section.Misc.VirtualSize)); - - emu.protect_memory(target_ptr, size_of_section, permissions, nullptr); - } - - auto& export_directory_entry = optional_header.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT]; - if (export_directory_entry.VirtualAddress == 0 || export_directory_entry.Size == 0) - { - return binary; - } - - const auto* export_directory = reinterpret_cast(ptr + export_directory_entry. - VirtualAddress); - - //const auto function_count = export_directory->NumberOfFunctions; - const auto names_count = export_directory->NumberOfNames; - - const auto* names = reinterpret_cast(ptr + export_directory->AddressOfNames); - const auto* ordinals = reinterpret_cast(ptr + export_directory->AddressOfNameOrdinals); - const auto* functions = reinterpret_cast(ptr + export_directory->AddressOfFunctions); - - for (DWORD i = 0; i < names_count; i++) - { - exported_symbol symbol{}; - symbol.ordinal = ordinals[i]; - symbol.name = reinterpret_cast(ptr + names[i]); - symbol.rva = functions[symbol.ordinal]; - symbol.address = binary.image_base + symbol.rva; - - binary.exports.push_back(std::move(symbol)); - } + map_sections(emu, binary, ptr, *nt_headers); + apply_relocations(emu, binary, optional_header); + collect_exports(binary, ptr, optional_header); return binary; }