Files
raven/native/nt/x86_thread_api.cpp
T

157 lines
5.3 KiB
C++

#include "x86_thread_api.h"
#include <algorithm>
#include <array>
#include <chrono>
namespace raven {
namespace {
constexpr std::array<std::pair<std::string_view, uint32_t>, 13> thread_exports{{
{"CreateThread", 6}, {"DeleteCriticalSection", 1}, {"EnterCriticalSection", 1}, {"ExitThread", 1}, {"GetExitCodeThread", 2},
{"InitializeCriticalSection", 1}, {"LeaveCriticalSection", 1}, {"ResumeThread", 1}, {"SetThreadPriority", 2},
{"Sleep", 1}, {"SwitchToThread", 0}, {"TryEnterCriticalSection", 1}, {"WaitForSingleObject", 2},
}};
constexpr uint32_t wait_timeout = 0x102, wait_failed = UINT32_MAX, still_active = 259;
}
NativeX86Threads::NativeX86Threads(NativeProcess &process, NativeEnvironment &environment, NativeObjects &objects, NativeThreads &threads)
: process(process), environment(environment), objects(objects), threads(threads)
{
}
std::span<const std::pair<std::string_view, uint32_t>> NativeX86Threads::exports()
{
return thread_exports;
}
uint32_t NativeX86Threads::wait(uint64_t handle, uint32_t milliseconds)
{
const uint32_t id = threads.current_id();
std::optional<std::chrono::steady_clock::time_point> wake;
if (milliseconds != UINT32_MAX)
wake = std::chrono::steady_clock::now() + std::chrono::milliseconds(milliseconds);
for (;;) {
const int state = objects.signaled(handle, id);
if (state < 0) {
environment.set_last_error(6);
return wait_failed;
}
if (state == 1) {
objects.consume(handle, id);
return 0;
}
if (milliseconds == 0 || (wake && std::chrono::steady_clock::now() >= *wake))
return wait_timeout;
threads.block([&] { return objects.signaled(handle, id) == 1; }, wake);
}
}
bool NativeX86Threads::dispatch(std::string_view name)
{
const auto known = std::find_if(thread_exports.begin(), thread_exports.end(), [&](const auto &item) { return item.first == name; });
if (known == thread_exports.end())
return false;
const auto args = process.api_arguments(known->second);
if (name == "CreateThread") {
const uint32_t flags = uint32_t(args[4]);
if (args[0] || (flags & ~uint32_t(0x4))) {
environment.set_last_error(87);
process.return_api(0);
return true;
}
std::shared_ptr<GuestThread> thread;
try {
thread = threads.create(args[2], args[3], args[1] ? args[1] : 1024 * 1024, flags & 4);
} catch (const std::runtime_error &) {
environment.set_last_error(8);
process.return_api(0);
return true;
}
auto object = std::make_shared<ThreadObject>(thread);
const auto made = objects.add_object(object);
if (made.status) {
threads.terminate(thread, 0);
environment.set_last_error(8);
process.return_api(0);
return true;
}
handles[made.value] = object;
if (args[5]) {
const uint32_t id = thread->id;
process.write_memory(args[5], std::span<const uint8_t>(reinterpret_cast<const uint8_t *>(&id), 4));
}
process.return_api(made.value);
} else if (name == "ExitThread") {
threads.exit_current(uint32_t(args[0]));
} else if (name == "GetExitCodeThread") {
const auto found = handles.find(args[0]);
if (found == handles.end()) {
environment.set_last_error(6);
process.return_api(0);
return true;
}
const auto &thread = *found->second->thread;
const uint32_t code = thread.state == GuestThread::State::done ? thread.exit_code : still_active;
process.write_memory(args[1], std::span<const uint8_t>(reinterpret_cast<const uint8_t *>(&code), 4));
process.return_api(1);
} else if (name == "ResumeThread") {
const auto found = handles.find(args[0]);
if (found == handles.end()) {
environment.set_last_error(6);
process.return_api(UINT32_MAX);
return true;
}
const auto previous = found->second->thread->suspend_count;
threads.resume(*found->second->thread);
process.return_api(uint32_t(previous));
} else if (name == "SetThreadPriority") {
const auto found = handles.find(args[0]);
if (found != handles.end())
found->second->thread->priority = int32_t(args[1]);
process.return_api(found != handles.end());
} else if (name == "Sleep") {
const uint32_t milliseconds = uint32_t(args[0]);
if (!milliseconds) {
threads.yield();
} else {
std::optional<std::chrono::steady_clock::time_point> wake;
if (milliseconds != UINT32_MAX)
wake = std::chrono::steady_clock::now() + std::chrono::milliseconds(milliseconds);
threads.block([] { return false; }, wake);
}
process.return_api(0);
} else if (name == "InitializeCriticalSection" || name == "DeleteCriticalSection") {
sections.erase(args[0]);
process.return_api(0);
} else if (name == "EnterCriticalSection" || name == "TryEnterCriticalSection") {
const uint32_t id = threads.current_id();
auto &section = sections[args[0]];
const bool free = section.owner == 0 || section.owner == id;
if (!free && name == "TryEnterCriticalSection") {
process.return_api(0);
return true;
}
if (!free)
threads.block([&section, id] { return section.owner == 0 || section.owner == id; }, std::nullopt);
section.owner = id;
++section.depth;
process.return_api(1);
} else if (name == "LeaveCriticalSection") {
auto &section = sections[args[0]];
if (section.depth > 0 && --section.depth == 0)
section.owner = 0;
process.return_api(0);
} else if (name == "SwitchToThread") {
process.return_api(threads.yield());
} else {
process.return_api(wait(args[0], uint32_t(args[1])));
}
return true;
}
}