mirror of
https://github.com/mirkobrombin/raven.git
synced 2026-11-03 21:46:32 +00:00
166 lines
5.6 KiB
C++
166 lines
5.6 KiB
C++
#include "msvcrt_api.h"
|
|
|
|
#include <algorithm>
|
|
#include <array>
|
|
#include <stdexcept>
|
|
#include <sys/mman.h>
|
|
|
|
namespace raven {
|
|
|
|
namespace {
|
|
|
|
constexpr std::array<std::string_view, 17> names{"_XcptFilter", "__getmainargs", "__p__commode", "__p__fmode",
|
|
"__set_app_type", "__setusermatherr", "_acmdln", "_adjust_fdiv", "_c_exit", "_cexit", "_controlfp",
|
|
"_except_handler3", "_exit", "_initterm", "exit", "rand", "srand"};
|
|
constexpr std::array<std::string_view, 2> variables{"_acmdln", "_adjust_fdiv"};
|
|
|
|
}
|
|
|
|
NativeCRT::NativeCRT(NativeProcess &process, NativeEnvironment &environment) : process(process), environment(environment)
|
|
{
|
|
if (process.mode() != NativeMode::x86)
|
|
throw std::runtime_error("the native CRT provider is x86 only");
|
|
}
|
|
|
|
std::span<const std::string_view> NativeCRT::exports()
|
|
{
|
|
static const auto sorted = [] {
|
|
auto copy = names;
|
|
std::sort(copy.begin(), copy.end());
|
|
return copy;
|
|
}();
|
|
return sorted;
|
|
}
|
|
|
|
std::span<const std::string_view> NativeCRT::data_exports()
|
|
{
|
|
static const auto sorted = [] {
|
|
auto copy = variables;
|
|
std::sort(copy.begin(), copy.end());
|
|
return copy;
|
|
}();
|
|
return sorted;
|
|
}
|
|
|
|
void NativeCRT::set_call(std::function<NativeResult(uint64_t, std::span<const uint64_t>)> caller)
|
|
{
|
|
invoke = std::move(caller);
|
|
}
|
|
|
|
void NativeCRT::write32(uint64_t address, uint32_t value)
|
|
{
|
|
const uint8_t bytes[4] = {uint8_t(value), uint8_t(value >> 8), uint8_t(value >> 16), uint8_t(value >> 24)};
|
|
process.write_memory(address, bytes);
|
|
}
|
|
|
|
void NativeCRT::initialize(const std::function<uint64_t(std::string_view)> &address)
|
|
{
|
|
globals = process.map_memory(4096, PROT_READ | PROT_WRITE);
|
|
// _acmdln points at the process ANSI command line; _adjust_fdiv reports a correct FDIV unit.
|
|
write32(address("_acmdln"), uint32_t(environment.command_line()));
|
|
write32(address("_adjust_fdiv"), 0);
|
|
}
|
|
|
|
bool NativeCRT::dispatch(std::string_view name)
|
|
{
|
|
if (std::find(names.begin(), names.end(), name) == names.end() || std::find(variables.begin(), variables.end(), name) != variables.end())
|
|
return false;
|
|
if (name == "__set_app_type" || name == "__setusermatherr") {
|
|
process.api_arguments(1);
|
|
process.return_api(0);
|
|
} else if (name == "__p__fmode" || name == "__p__commode") {
|
|
process.api_arguments(0);
|
|
process.return_api(globals + (name == "__p__fmode" ? 0 : 4));
|
|
} else if (name == "_controlfp") {
|
|
const auto args = process.api_arguments(2);
|
|
const auto mask = uint32_t(args[1]);
|
|
control_word = (control_word & ~mask) | (uint32_t(args[0]) & mask);
|
|
process.return_api(control_word);
|
|
} else if (name == "_initterm") {
|
|
const auto args = process.api_arguments(2);
|
|
if (args[0] > args[1] || (args[1] - args[0]) / 4 > 4096)
|
|
throw std::runtime_error("native _initterm table is invalid");
|
|
if (!invoke)
|
|
throw std::runtime_error("native CRT callbacks are not connected");
|
|
for (uint64_t cursor = args[0]; cursor < args[1]; cursor += 4) {
|
|
const auto bytes = process.read_memory(cursor, 4);
|
|
const uint64_t target = uint64_t(bytes[0]) | uint64_t(bytes[1]) << 8 | uint64_t(bytes[2]) << 16 | uint64_t(bytes[3]) << 24;
|
|
if (!target)
|
|
continue;
|
|
const auto result = invoke(target, {});
|
|
if (result.stop != NativeStop::returned)
|
|
throw result;
|
|
}
|
|
process.return_api(0);
|
|
} else if (name == "__getmainargs") {
|
|
const auto args = process.api_arguments(5);
|
|
// argv holds one pointer per space-separated token of the ANSI command line, honoring double quotes.
|
|
const auto command = environment.command_line();
|
|
std::string line;
|
|
for (uint64_t at = command;; ++at) {
|
|
const auto byte = process.read_memory(at, 1)[0];
|
|
if (!byte || line.size() > 32768)
|
|
break;
|
|
line.push_back(char(byte));
|
|
}
|
|
std::vector<std::string> tokens;
|
|
for (size_t at = 0; at < line.size();) {
|
|
while (at < line.size() && (line[at] == ' ' || line[at] == '\t'))
|
|
++at;
|
|
if (at >= line.size())
|
|
break;
|
|
std::string token;
|
|
bool quoted = false;
|
|
for (; at < line.size() && (quoted || (line[at] != ' ' && line[at] != '\t')); ++at) {
|
|
if (line[at] == '"')
|
|
quoted = !quoted;
|
|
else
|
|
token.push_back(line[at]);
|
|
}
|
|
tokens.push_back(token);
|
|
}
|
|
size_t size = (tokens.size() + 2) * 4 + 4;
|
|
for (const auto &token : tokens)
|
|
size += token.size() + 1;
|
|
const auto block = process.map_memory((size + 4095) & ~size_t(4095), PROT_READ | PROT_WRITE);
|
|
std::vector<uint8_t> bytes((size + 4095) & ~size_t(4095));
|
|
size_t strings = (tokens.size() + 2) * 4;
|
|
for (size_t index = 0; index < tokens.size(); ++index) {
|
|
const auto pointer = uint32_t(block + strings);
|
|
for (size_t part = 0; part < 4; ++part)
|
|
bytes[index * 4 + part] = uint8_t(pointer >> (part * 8));
|
|
std::copy(tokens[index].begin(), tokens[index].end(), bytes.begin() + ptrdiff_t(strings));
|
|
strings += tokens[index].size() + 1;
|
|
}
|
|
// env is an empty NULL-terminated array placed right after argv's terminator.
|
|
process.write_memory(block, bytes);
|
|
write32(args[0], uint32_t(tokens.size()));
|
|
write32(args[1], uint32_t(block));
|
|
write32(args[2], uint32_t(block + (tokens.size() + 1) * 4));
|
|
arguments_block = block;
|
|
process.return_api(0);
|
|
} else if (name == "_cexit" || name == "_c_exit") {
|
|
process.api_arguments(0);
|
|
process.return_api(0);
|
|
} else if (name == "rand") {
|
|
process.api_arguments(0);
|
|
seed = seed * 214013U + 2531011U;
|
|
process.return_api((seed >> 16) & 0x7fff);
|
|
} else if (name == "srand") {
|
|
seed = uint32_t(process.api_arguments(1)[0]);
|
|
process.return_api(0);
|
|
} else if (name == "_XcptFilter") {
|
|
process.api_arguments(2);
|
|
process.return_api(0);
|
|
} else if (name == "_except_handler3") {
|
|
// ExceptionContinueSearch until structured exception dispatch exists for x86 guests.
|
|
process.api_arguments(4);
|
|
process.return_api(1);
|
|
} else {
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
}
|