mirror of
https://github.com/mirkobrombin/raven.git
synced 2026-11-03 21:46:32 +00:00
A copy in the program's folder now loads for d3d8, d3d9, d3d10core, d3d11, dxgi, ddraw, dinput, dinput8, winmm, version, xinput1_3 and xinput1_4, the libraries graphics wrappers and mod loaders ship. Every other system library stays the runtime's whatever the program ships, as before this series, so a bundled dbghelp.dll or winhttp.dll is still answered by Raven. RAVEN_PREFER_LOCAL_DLLS=1 loads the program's copy of any system library outside the core list instead. The program's folder is no longer read file by file at each start: it is listed once, on the first listed name the runtime asks about, and only a file that matches such a name is mapped to check that it is a DLL of the program's width. The C runtime ordering change in main.cpp is dropped, as msvcrt.dll and ucrtbase.dll are again never looked for there. Assisted-by: Claude Code:claude-opus-5-5 AI-Scope: Reworked the change to the maintainer's review: the allowlist, the RAVEN_PREFER_LOCAL_DLLS setting, the per-name ProgramLibraries lookup shared by the x64 and x86 paths, and the dependencies_test case and CTest entry for both modes.
138 lines
7.5 KiB
C++
138 lines
7.5 KiB
C++
#include <string>
|
|
#include "loader.h"
|
|
#include "providers.h"
|
|
|
|
#include <algorithm>
|
|
#include <array>
|
|
#include <cstdlib>
|
|
#include <filesystem>
|
|
#include <fstream>
|
|
#include <iostream>
|
|
#include <stdexcept>
|
|
|
|
static void check(bool value,const std::string &message)
|
|
{
|
|
if (!value) throw std::runtime_error(message);
|
|
}
|
|
|
|
static std::vector<uint8_t> read_file(const std::filesystem::path &path)
|
|
{
|
|
std::ifstream file(path,std::ios::binary|std::ios::ate);
|
|
check(bool(file) && file.tellg()>0 && file.tellg()<1024*1024,"invalid startup fixture");
|
|
std::vector<uint8_t> bytes(size_t(file.tellg()));file.seekg(0);
|
|
check(bool(file.read(reinterpret_cast<char *>(bytes.data()),std::streamsize(bytes.size()))),"short startup fixture");return bytes;
|
|
}
|
|
|
|
int main(int argc,char **argv)
|
|
{
|
|
try {
|
|
check(argc==3 || argc==4,"bootstrap, lifecycle directory and optional PE32 fixture required");
|
|
const std::filesystem::path directory=argv[2];
|
|
const auto client=read_file(directory/"client.exe"),base=read_file(directory/"base.dll"),dependent=read_file(directory/"dependent.dll");
|
|
const std::array<std::string_view,1> native{"kernel32.dll"};
|
|
for (const bool supplied:{false,true}) for (const bool mapped:{false,true}) {
|
|
raven::ModuleSet modules;auto &entry=modules.add("client.exe",client);
|
|
raven::PEModule *existing=nullptr;if (supplied) existing=&modules.add("base.dll",base);
|
|
std::vector<std::string> queries;
|
|
const auto source=[&](std::string_view name)->std::optional<raven::ModuleBytes> {
|
|
queries.emplace_back(name);
|
|
if (mapped && (name=="base.dll" || name=="dependent.dll")) return raven::ModuleBytes::map(directory/name);
|
|
if (name=="base.dll") return base;
|
|
if (name=="dependent.dll") return dependent;
|
|
throw std::runtime_error("startup queried a native or unrelated module");
|
|
};
|
|
const auto result=raven::run_native_process(modules,entry,argv[1],std::chrono::seconds(3),source);
|
|
check(result.stop==raven::NativeStop::returned && !result.value,"implicit startup or dependency initialization order differs");
|
|
check(queries.size()==(supplied ? 1 : 2) && modules.all().contains("base.dll") && modules.all().contains("dependent.dll"),"startup queried a dependency more than once");
|
|
if (supplied) check(modules.all().at("base.dll").get()==existing && std::ranges::equal(std::span<const uint8_t>(existing->bytes),base),"startup replaced an explicitly supplied module");
|
|
}
|
|
for (const std::string_view mode:{"missing","malformed","executable","clr","symbol","pe32"}) {
|
|
if (mode=="pe32" && argc!=4) continue;
|
|
raven::ModuleSet modules;auto &entry=modules.add("client.exe",client);const auto original=std::vector<uint8_t>(entry.loaded.data().begin(),entry.loaded.data().end());
|
|
auto bad=dependent;
|
|
if (mode=="malformed") bad.resize(10);
|
|
if (mode=="executable") bad=client;
|
|
if (mode=="pe32") bad=read_file(argv[3]);
|
|
if (mode=="clr") {
|
|
uint32_t header=0;for(unsigned i=0;i<4;++i)header|=uint32_t(bad[0x3c+i])<<(i*8);
|
|
bad[header+24+112+14*8]=1;
|
|
}
|
|
if (mode=="symbol") {
|
|
entry.imports.push_back({"dependent.dll","UnavailableExport",0,false,0});
|
|
}
|
|
std::string error;
|
|
try {
|
|
raven::load_initial_dependencies(modules,[&](std::string_view name)->std::optional<std::vector<uint8_t>> {
|
|
if (name=="base.dll") return base;
|
|
if (name=="dependent.dll" && mode!="missing") return bad;
|
|
return std::nullopt;
|
|
},native);
|
|
} catch (const std::runtime_error &caught) { error=caught.what(); }
|
|
check(!error.empty() && modules.all().size()==1 && std::equal(original.begin(),original.end(),entry.loaded.data().begin()),"failed initial acquisition retained images or changed the entry ("+std::string(mode)+": "+(error.empty() ? "no error" : error)+")");
|
|
if (mode=="missing") check(error=="missing PE module: dependent.dll","missing startup failed for another reason");
|
|
if (mode=="symbol") check(error.find("UnavailableExport")!=error.npos,"symbol startup failed for another reason");
|
|
if (mode=="pe32") check(error.find("PE32")!=error.npos,"PE32 startup failed for another reason");
|
|
if (mode=="clr") check(error.find("runtime header")!=error.npos,"CLR startup failed for another reason");
|
|
}
|
|
{
|
|
raven::ModuleSet modules;auto &entry=modules.add("forward-client.exe",read_file(directory/"forward-client.exe"));
|
|
std::vector<std::string> queries;
|
|
const auto result=raven::run_native_process(modules,entry,argv[1],std::chrono::seconds(3),
|
|
[&](std::string_view name)->std::optional<std::vector<uint8_t>> {
|
|
queries.emplace_back(name);
|
|
if (name=="forward.dll") return read_file(directory/"forward.dll");
|
|
if (name=="base.dll") return base;
|
|
throw std::runtime_error("inactive startup forwarder was loaded");
|
|
});
|
|
check(result.stop==raven::NativeStop::returned && !result.value && queries==std::vector<std::string>({"forward.dll","base.dll"}),"active forwarder acquisition differs");
|
|
}
|
|
{
|
|
raven::ModuleSet modules;auto &entry=modules.add("forward-client.exe",read_file(directory/"forward-client.exe"));
|
|
for (auto &symbol : entry.imports)
|
|
if (symbol.module=="forward.dll") symbol.module="winmm.dll";
|
|
std::vector<std::string> queries;
|
|
const auto result=raven::run_native_process(modules,entry,argv[1],std::chrono::seconds(3),
|
|
[&](std::string_view name)->std::optional<std::vector<uint8_t>> {
|
|
queries.emplace_back(name);
|
|
if (name=="winmm.dll") return read_file(directory/"forward.dll");
|
|
if (name=="base.dll") return base;
|
|
return std::nullopt;
|
|
});
|
|
check(result.stop==raven::NativeStop::returned && !result.value && queries==std::vector<std::string>({"winmm.dll","base.dll"}) &&
|
|
modules.all().contains("winmm.dll") && !modules.all().at("winmm.dll")->runtime_api &&
|
|
modules.all().contains("system32-winmm.dll") && modules.all().at("system32-winmm.dll")->runtime_api,
|
|
"program's own winmm.dll or the system32 fallback failed");
|
|
}
|
|
// A system library off the list stays the runtime's whatever copy the program ships, unless RAVEN_PREFER_LOCAL_DLLS=1 asks for the program's.
|
|
{
|
|
const char *prefer=std::getenv("RAVEN_PREFER_LOCAL_DLLS");
|
|
const bool prefer_local=prefer && std::string_view(prefer)=="1";
|
|
raven::ModuleSet modules;auto &entry=modules.add("forward-client.exe",read_file(directory/"forward-client.exe"));
|
|
for (auto &symbol : entry.imports)
|
|
if (symbol.module=="forward.dll") symbol.module="winhttp.dll";
|
|
std::vector<std::string> queries;
|
|
bool returned=false;
|
|
try {
|
|
const auto result=raven::run_native_process(modules,entry,argv[1],std::chrono::seconds(3),
|
|
[&](std::string_view name)->std::optional<std::vector<uint8_t>> {
|
|
queries.emplace_back(name);
|
|
if (name=="winhttp.dll") return read_file(directory/"forward.dll");
|
|
if (name=="base.dll") return base;
|
|
return std::nullopt;
|
|
});
|
|
returned=result.stop==raven::NativeStop::returned && !result.value;
|
|
} catch (const std::exception &) {}
|
|
if (prefer_local)
|
|
check(returned && queries==std::vector<std::string>({"winhttp.dll","base.dll"}) &&
|
|
modules.all().contains("winhttp.dll") && !modules.all().at("winhttp.dll")->runtime_api &&
|
|
modules.all().contains("system32-winhttp.dll") && modules.all().at("system32-winhttp.dll")->runtime_api,
|
|
"RAVEN_PREFER_LOCAL_DLLS=1 did not load the program's own winhttp.dll");
|
|
else
|
|
check(!returned && std::find(queries.begin(),queries.end(),"winhttp.dll")==queries.end(),
|
|
"the program's own winhttp.dll replaced the runtime's");
|
|
}
|
|
std::cout<<"Implicit initial dependencies, explicit-module preservation, active forwarders and pre-execution rollback passed\n";
|
|
} catch (const std::exception &error) { std::cerr<<error.what()<<'\n';return 1; }
|
|
return 0;
|
|
}
|