mirror of
https://github.com/mirkobrombin/raven.git
synced 2026-11-03 21:46:32 +00:00
160 lines
7.6 KiB
C++
160 lines
7.6 KiB
C++
#include "apiset.h"
|
|
#include "loader.h"
|
|
#include "memory.h"
|
|
#include "system_exports.h"
|
|
#include "actctx.h"
|
|
|
|
#include <algorithm>
|
|
#include <filesystem>
|
|
#include <stdexcept>
|
|
|
|
namespace raven {
|
|
|
|
void validate_native_image(const PEImage &image,std::string_view name,bool is_64)
|
|
{
|
|
if (image.is_64!=is_64) throw std::runtime_error(is_64 ? "native PE32 execution is not implemented" : "native x86 process cannot mix PE64 modules: "+std::string(name));
|
|
// Managed code is the runtime's to start; a runtime header that does not hold IMAGE_COR20_HEADER is no image Windows would load.
|
|
const auto &runtime=image.directories[14];
|
|
if ((runtime.address || runtime.size) && (!runtime.address || runtime.size<72))
|
|
throw std::runtime_error("native image has a malformed runtime header: "+std::string(name));
|
|
if (image.directories[13].address || image.directories[13].size)
|
|
for (const auto &descriptor:image.delay_imports())
|
|
if (!(image.file_span(descriptor.descriptor_address,4)[0] & 1))
|
|
throw std::runtime_error("native legacy-VA delay helper execution is not implemented: "+std::string(name));
|
|
}
|
|
|
|
uint64_t program_start(const ModuleSet &modules, const PEModule &entry)
|
|
{
|
|
if (entry.image.directories[14].address && entry.image.directories[14].size)
|
|
return modules.resolve(PEImport{"mscoree.dll", "_CorExeMain", 0, false, 0});
|
|
return entry.loaded.base() + entry.image.entry;
|
|
}
|
|
|
|
void load_initial_dependencies(ModuleSet &modules,
|
|
const std::function<std::optional<ModuleBytes>(std::string_view)> &source,
|
|
std::span<const std::string_view> native_modules,bool is_64,bool tolerate_missing)
|
|
{
|
|
if (!source) return;
|
|
std::vector<PEModule *> pending,staged;
|
|
for (const auto &[name,module]:modules.all()) pending.push_back(module.get());
|
|
try {
|
|
for (size_t index=0;index<pending.size();++index) {
|
|
// A .NET image starts in the runtime's shim, which the loader brings in whether or not the image imports it, as Windows does
|
|
// for programs whose native imports it leaves out.
|
|
auto symbols=pending[index]->imports;
|
|
if (pending[index]->image.directories[14].address && pending[index]->image.directories[14].size)
|
|
symbols.push_back(PEImport{"mscoree.dll","_CorExeMain",0,false,0});
|
|
for (const auto &symbol:symbols)
|
|
for (;;) {
|
|
try { modules.resolve(symbol);break; }
|
|
catch (const std::runtime_error &error) {
|
|
const std::string_view message=error.what();
|
|
if (!message.starts_with("missing PE module: ")) throw;
|
|
const std::string identity(message.substr(19));
|
|
if (std::find(native_modules.begin(),native_modules.end(),identity)!=native_modules.end()) break;
|
|
// An API set name stands for the system library that implements it; that library is served natively, and alias_api_sets
|
|
// ties the name to it once the runtime has made it.
|
|
if (const auto host=apiset_host(identity); !host.empty() && host!="ucrtbase.dll") {
|
|
const auto system_names=system_library_names();
|
|
if (std::find(native_modules.begin(),native_modules.end(),host)!=native_modules.end() ||
|
|
std::find(system_names.begin(),system_names.end(),host)!=system_names.end()) break;
|
|
}
|
|
// A C runtime contract resolves through the runtime component, whichever contract version the module names.
|
|
if (apiset_host(identity)=="ucrtbase.dll") {
|
|
if (!modules.all().contains("ucrtbase.dll")) {
|
|
auto bytes=source("ucrtbase.dll");
|
|
if (!bytes) throw;
|
|
const PEImage image(*bytes);validate_native_image(image,"ucrtbase.dll",is_64);
|
|
staged.reserve(staged.size()+1);pending.reserve(pending.size()+1);
|
|
auto &module=modules.add("ucrtbase.dll",std::move(*bytes));
|
|
staged.push_back(&module);pending.push_back(&module);
|
|
}
|
|
modules.add_alias(identity,"ucrtbase.dll");
|
|
continue;
|
|
}
|
|
if (modules.all().size()>=10000) throw std::runtime_error("native DLL graph exceeds module limit");
|
|
// A library's own manifest names the side-by-side assemblies its imports come from (a private one beside the program
|
|
// among them): the loader looks there first, as Windows does.
|
|
std::optional<ModuleBytes> bytes;
|
|
std::filesystem::path redirected;
|
|
if (const auto own=activation_contexts().module_manifest(pending[index]->image))
|
|
if (const auto folder=activation_contexts().assembly_folder(*own,identity)) {
|
|
std::error_code error;
|
|
for (const auto &entry:std::filesystem::directory_iterator(*folder,error)) {
|
|
auto file=entry.path().filename().string();
|
|
for (auto &character:file) if (character>='A' && character<='Z') character=static_cast<char>(character+('a'-'A'));
|
|
if (file==identity && entry.is_regular_file(error)) { bytes=ModuleBytes::map(entry.path()); redirected=entry.path(); break; }
|
|
}
|
|
}
|
|
if (!bytes) bytes=source(identity);
|
|
if (!bytes) {
|
|
const auto system_names=system_library_names();
|
|
if (tolerate_missing || std::find(system_names.begin(),system_names.end(),identity)!=system_names.end()) break;
|
|
throw;
|
|
}
|
|
const PEImage image(*bytes);validate_native_image(image,identity,is_64);
|
|
if (!(image.characteristics & 0x2000)) throw std::runtime_error("native startup dependency must be a DLL: "+identity);
|
|
staged.reserve(staged.size()+1);pending.reserve(pending.size()+1);
|
|
auto &module=modules.add(identity,std::move(*bytes));
|
|
if (!redirected.empty()) module.file=redirected;
|
|
staged.push_back(&module);pending.push_back(&module);
|
|
}
|
|
}
|
|
}
|
|
} catch (...) {
|
|
for (auto item=staged.rbegin();item!=staged.rend();++item) modules.remove(**item);
|
|
throw;
|
|
}
|
|
}
|
|
|
|
void alias_api_sets(ModuleSet &modules)
|
|
{
|
|
std::vector<std::string> names;
|
|
for (const auto &[name,module]:modules.all())
|
|
for (const auto &symbol:module->imports) {
|
|
auto identity=symbol.module;
|
|
for (auto &character : identity) if (character>='A' && character<='Z') character=char(character+('a'-'A'));
|
|
if (identity.find('.')==identity.npos) identity+=".dll";
|
|
if (!modules.all().contains(identity)) names.push_back(identity);
|
|
}
|
|
for (const auto &identity:names) {
|
|
const auto host=apiset_host(identity);
|
|
if (!host.empty() && modules.all().contains(host) && !modules.all().contains(identity))
|
|
modules.add_alias(identity,host);
|
|
}
|
|
}
|
|
|
|
void forward_opengl_formats(ModuleSet &modules, PEModule &provider)
|
|
{
|
|
if (provider.name != "gdi32.dll" || !provider.runtime_api)
|
|
return;
|
|
const auto backend = modules.all().find("opengl32.dll");
|
|
if (backend == modules.all().end() || backend->second->runtime_api)
|
|
return;
|
|
for (const char *name : {"ChoosePixelFormat", "DescribePixelFormat", "GetPixelFormat", "SetPixelFormat", "SwapBuffers"}) {
|
|
const std::string target = std::string("wgl") + name;
|
|
const auto symbol = provider.export_names.find(name);
|
|
if (symbol != provider.export_names.end() && backend->second->export_names.contains(target))
|
|
provider.exports.at(symbol->second).forwarder = "opengl32." + target;
|
|
}
|
|
}
|
|
|
|
void register_image(NativeMemory &memory, const PEModule &module)
|
|
{
|
|
std::vector<std::pair<std::pair<uint64_t,uint64_t>,uint32_t>> sections;
|
|
sections.push_back({{0,module.image.headers_size},2});
|
|
for (const auto §ion:module.image.sections) {
|
|
const bool execute=section.characteristics & 0x20000000U, write=section.characteristics & 0x80000000U;
|
|
sections.push_back({{section.address,std::max<uint32_t>(section.virtual_size,section.file_size)},execute ? (write ? 0x40U : 0x20U) : write ? 0x04U : 0x02U});
|
|
}
|
|
memory.add_image(module.loaded.base(),module.image.image_size,sections);
|
|
}
|
|
|
|
void register_images(NativeMemory &memory, const ModuleSet &modules)
|
|
{
|
|
for (const auto &[name,module]:modules.all())
|
|
register_image(memory,*module);
|
|
}
|
|
|
|
}
|