mirror of
https://github.com/mirkobrombin/raven.git
synced 2026-10-28 21:25:06 +00:00
165 lines
6.1 KiB
C++
165 lines
6.1 KiB
C++
#include "native_cpu.h"
|
|
|
|
#include <algorithm>
|
|
#include <cerrno>
|
|
#include <functional>
|
|
#include <iostream>
|
|
#include <signal.h>
|
|
#include <stdexcept>
|
|
#include <sys/mman.h>
|
|
#include <sys/wait.h>
|
|
|
|
static void check(bool condition, const char *message)
|
|
{
|
|
if (!condition)
|
|
throw std::runtime_error(message);
|
|
}
|
|
|
|
static void put(std::span<uint8_t> bytes, size_t offset, uint64_t value, size_t width = 4)
|
|
{
|
|
for (size_t i = 0; i < width; ++i)
|
|
bytes[offset + i] = static_cast<uint8_t>(value >> (i * 8));
|
|
}
|
|
|
|
constexpr uint32_t image_base = 0x400000;
|
|
constexpr uint32_t trap_rva = 0x1100;
|
|
|
|
// One PE32 executable whose code section holds each probe at a fixed 0x20 byte slot.
|
|
static std::vector<uint8_t> fixture()
|
|
{
|
|
std::vector<uint8_t> bytes(0x600);
|
|
put(bytes, 0, 0x5a4d, 2);
|
|
put(bytes, 0x3c, 0x80);
|
|
put(bytes, 0x80, 0x4550);
|
|
put(bytes, 0x84, 0x14c, 2);
|
|
put(bytes, 0x86, 2, 2);
|
|
put(bytes, 0x94, 224, 2);
|
|
put(bytes, 0x96, 2, 2);
|
|
put(bytes, 0x98, 0x10b, 2);
|
|
put(bytes, 0xa8, 0x1000);
|
|
put(bytes, 0xb4, image_base);
|
|
put(bytes, 0xb8, 0x1000);
|
|
put(bytes, 0xbc, 0x200);
|
|
put(bytes, 0xd0, 0x5000);
|
|
put(bytes, 0xd4, 0x200);
|
|
put(bytes, 0xf4, 16);
|
|
for (size_t i = 0; i < 2; ++i) {
|
|
const size_t section = 0x178 + i * 40;
|
|
bytes[section] = '.';
|
|
bytes[section + 1] = i == 0 ? 't' : 'd';
|
|
put(bytes, section + 8, 0x400);
|
|
put(bytes, section + 12, i == 0 ? 0x1000 : 0x3000);
|
|
put(bytes, section + 16, 0x200);
|
|
put(bytes, section + 20, 0x200 + i * 0x200);
|
|
put(bytes, section + 36, i == 0 ? 0x60000020 : 0xc0000040);
|
|
}
|
|
const auto place = [&](size_t slot, std::initializer_list<uint8_t> code) {
|
|
std::copy(code.begin(), code.end(), bytes.begin() + 0x200 + slot * 0x20);
|
|
};
|
|
place(0, {0xb8, 0x34, 0x12, 0x00, 0x00, 0xc3});
|
|
place(1, {0x8b, 0x44, 0x24, 0x04, 0x03, 0x44, 0x24, 0x08, 0xc2, 0x08, 0x00});
|
|
place(2, {0x64, 0xa1, 0x18, 0x00, 0x00, 0x00, 0xc3});
|
|
place(3, {0x6a, 0x07, 0x6a, 0x05, 0xb8, 0x00, 0x11, 0x40, 0x00, 0xff, 0xd0, 0xc3});
|
|
place(4, {0x8b, 0x44, 0x24, 0x04, 0xc3});
|
|
bytes[0x200 + (trap_rva - 0x1000)] = 0xcc;
|
|
return bytes;
|
|
}
|
|
|
|
static uint64_t slot(size_t index)
|
|
{
|
|
return image_base + 0x1000 + index * 0x20;
|
|
}
|
|
|
|
static void rejected(const std::function<void()> &operation)
|
|
{
|
|
try {
|
|
operation();
|
|
} catch (const std::runtime_error &) {
|
|
return;
|
|
}
|
|
throw std::runtime_error("unsupported x86 setup was accepted");
|
|
}
|
|
|
|
static void no_children()
|
|
{
|
|
int status = 0;
|
|
errno = 0;
|
|
check(waitpid(-1, &status, WNOHANG) == -1 && errno == ECHILD, "x86 guest leaked or was not reaped");
|
|
}
|
|
|
|
int main(int argc, char **argv)
|
|
{
|
|
try {
|
|
if (argc != 2)
|
|
throw std::runtime_error("bootstrap path required");
|
|
const auto timeout = std::chrono::seconds(2);
|
|
{
|
|
const auto fixture_bytes = fixture();
|
|
const raven::PEImage image(fixture_bytes);
|
|
check(!image.is_64, "fixture is not PE32");
|
|
raven::LoadedImage loaded(image, image.image_base);
|
|
raven::LoadedImage *images[] = {&loaded};
|
|
raven::NativeProcess process(images, argv[1], timeout, raven::NativeMode::x86);
|
|
check(process.mode() == raven::NativeMode::x86, "process lost its x86 mode");
|
|
|
|
const auto constant = process.call(slot(0), {}, timeout);
|
|
check(constant.stop == raven::NativeStop::returned && constant.value == 0x1234, "x86 return value differs");
|
|
|
|
const uint64_t add[] = {20, 22};
|
|
const auto sum = process.call(slot(1), add, timeout);
|
|
check(sum.stop == raven::NativeStop::returned && sum.value == 42, "x86 stdcall arguments differ");
|
|
const uint64_t wide[] = {0x100000000ULL};
|
|
rejected([&] { process.call(slot(4), wide, timeout); });
|
|
|
|
const auto teb = process.map_memory(4096, PROT_READ | PROT_WRITE);
|
|
check(teb < 0x100000000ULL, "x86 allocation left the 32-bit address space");
|
|
const uint8_t self[] = {uint8_t(teb), uint8_t(teb >> 8), uint8_t(teb >> 16), uint8_t(teb >> 24)};
|
|
process.write_memory(teb + 0x18, self);
|
|
process.set_teb(teb);
|
|
const auto linear = process.call(slot(2), {}, timeout);
|
|
check(linear.stop == raven::NativeStop::returned && linear.value == teb, "FS:[0x18] does not reach the TEB self pointer");
|
|
|
|
process.add_api_trap(image_base + trap_rva, 8);
|
|
rejected([&] { process.add_api_trap(slot(0), 3); });
|
|
const auto stopped = process.call(slot(3), {}, timeout);
|
|
check(stopped.stop == raven::NativeStop::api && stopped.value == image_base + trap_rva, "x86 API trap was not captured");
|
|
check(process.api_arguments(2) == std::vector<uint64_t>{5, 7}, "x86 API arguments differ");
|
|
const auto before = process.context();
|
|
const auto scratch = process.map_memory(4096, PROT_READ | PROT_WRITE);
|
|
check(scratch < 0x100000000ULL && process.context().registers.cs == before.registers.cs &&
|
|
process.context().registers.rsp == before.registers.rsp, "host memory operation disturbed the paused x86 guest");
|
|
process.unmap_memory(scratch);
|
|
rejected([&] { process.map_memory(4096, PROT_READ, 0x100000000ULL); });
|
|
process.return_api(99);
|
|
const auto finished = process.resume(timeout);
|
|
check(finished.stop == raven::NativeStop::returned && finished.value == 99, "x86 API return or callee cleanup differs");
|
|
|
|
rejected([&] { process.enter_nt_service(0, 0); });
|
|
rejected([&] { process.complete_debug_print(0); });
|
|
}
|
|
{
|
|
const auto fixture_bytes = fixture();
|
|
const raven::PEImage image(fixture_bytes);
|
|
raven::LoadedImage loaded(image, image.image_base);
|
|
raven::LoadedImage *images[] = {&loaded};
|
|
raven::NativeProcess process(images, argv[1], timeout);
|
|
rejected([&] { process.set_teb(0x10000); });
|
|
}
|
|
{
|
|
const auto fixture_bytes = fixture();
|
|
const raven::PEImage image(fixture_bytes);
|
|
raven::LoadedImage loaded(image, image.image_base);
|
|
raven::LoadedImage *images[] = {&loaded};
|
|
raven::NativeProcess process(images, argv[1], timeout, raven::NativeMode::x86);
|
|
const auto unset = process.call(slot(2), {}, timeout);
|
|
check(unset.stop == raven::NativeStop::fault && unset.signal == SIGSEGV, "FS access succeeded before a TEB descriptor existed");
|
|
}
|
|
no_children();
|
|
std::cout << "PE32 compatibility-mode calls, stdcall frames, FS TEB, API traps and host memory operations verified\n";
|
|
return 0;
|
|
} catch (const std::exception &error) {
|
|
std::cerr << error.what() << '\n';
|
|
return 1;
|
|
}
|
|
}
|