Skip to content

Commit

Permalink
Fix threading to fully work
Browse files Browse the repository at this point in the history
  • Loading branch information
momo5502 committed Oct 21, 2024
1 parent 6a162fb commit 5f56216
Show file tree
Hide file tree
Showing 3 changed files with 164 additions and 95 deletions.
24 changes: 22 additions & 2 deletions src/windows-emulator/process_context.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -219,14 +219,26 @@ class emulator_thread : ref_counted_object

std::optional<uint32_t> exit_status{};
std::optional<handle> await_object{};
std::optional<bool> alerted{};
bool waiting_for_alert{false};
bool alerted{false};
std::optional<std::chrono::steady_clock::time_point> await_time{};

std::optional<NTSTATUS> pending_status{};

std::optional<emulator_allocator> gs_segment;
std::optional<emulator_object<TEB>> teb;

std::vector<std::byte> last_registers{};

void mark_as_ready(NTSTATUS status);

bool is_await_time_over() const
{
return this->await_time.has_value() && this->await_time.value() < std::chrono::steady_clock::now();
}

bool is_thread_ready(process_context& context);

void save(x64_emulator& emu)
{
this->last_registers = emu.save_registers();
Expand All @@ -237,12 +249,20 @@ class emulator_thread : ref_counted_object
emu.restore_registers(this->last_registers);
}

void setup_if_necessary(x64_emulator& emu, const process_context& context) const
void setup_if_necessary(x64_emulator& emu, const process_context& context)
{
if (!this->executed_instructions)
{
this->setup_registers(emu, context);
}

if (this->pending_status.has_value())
{
const auto status = *this->pending_status;
this->pending_status = {};

emu.reg<uint64_t>(x64_register::rax, static_cast<uint64_t>(status));
}
}

void serialize(utils::buffer_serializer&) const
Expand Down
84 changes: 5 additions & 79 deletions src/windows-emulator/syscalls.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -176,38 +176,6 @@ namespace
}
}

bool is_object_signaled(process_context& c, const handle h)
{
const auto type = h.value.type;

switch (type)
{
case handle_types::event:
{
const auto* e = c.events.get(h);
if (e)
{
return e->signaled;
}

break;
}

case handle_types::thread:
{
const auto* t = c.threads.get(h);
if (t)
{
return t->exit_status.has_value();
}

break;
}
}

throw std::runtime_error("Bad object");
}

std::chrono::steady_clock::time_point convert_delay_interval_to_time_point(const LARGE_INTEGER delay_interval)
{
constexpr auto HUNDRED_NANOSECONDS_IN_ONE_SECOND = 10000000LL;
Expand Down Expand Up @@ -2084,36 +2052,21 @@ namespace
handle h{};
h.bits = handle_value;

auto& t = c.win_emu.current_thread();

if (h.value.type != handle_types::thread && h.value.type != handle_types::event)
{
puts("Unsupported handle type for NtWaitForSingleObject!");
c.emu.stop();
return STATUS_NOT_SUPPORTED;
}

auto& t = c.win_emu.current_thread();
t.await_object = h;

if (timeout.value() && !t.await_time.has_value())
{
t.await_time = convert_delay_interval_to_time_point(timeout.read());
}

if (is_object_signaled(c.proc, h))
{
t.await_time = {};
t.await_object = {};
return STATUS_WAIT_0;
}

if (t.await_time.has_value() && *t.await_time < std::chrono::steady_clock::now())
{
t.await_time = {};
t.await_object = {};
return STATUS_TIMEOUT;
}

t.await_object = h;
c.retrigger_syscall = true;
c.win_emu.switch_thread = true;
c.emu.stop();

Expand Down Expand Up @@ -2153,18 +2106,8 @@ namespace
}

auto& t = c.win_emu.current_thread();
t.await_time = convert_delay_interval_to_time_point(delay_interval.read());

if (!t.await_time.has_value())
{
t.await_time = convert_delay_interval_to_time_point(delay_interval.read());
}
else if (*t.await_time < std::chrono::steady_clock::now())
{
t.await_time = {};
return STATUS_SUCCESS;
}

c.retrigger_syscall = true;
c.win_emu.switch_thread = true;
c.emu.stop();

Expand Down Expand Up @@ -2197,30 +2140,13 @@ namespace
const emulator_object<LARGE_INTEGER> timeout)
{
auto& t = c.win_emu.current_thread();
t.waiting_for_alert = true;

if (timeout.value() && !t.await_time.has_value())
{
t.await_time = convert_delay_interval_to_time_point(timeout.read());
}

if (!t.alerted.has_value())
{
t.alerted = false;
}
else if (*t.alerted)
{
t.alerted = {};
t.await_time = {};
return STATUS_ALERTED;
}
else if (t.await_time.has_value() && *t.await_time < std::chrono::steady_clock::now())
{
t.alerted = {};
t.await_time = {};
return STATUS_TIMEOUT;
}

c.retrigger_syscall = true;
c.win_emu.switch_thread = true;
c.emu.stop();

Expand Down
151 changes: 137 additions & 14 deletions src/windows-emulator/windows_emulator.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -417,9 +417,23 @@ namespace
dispatch_exception_pointers(emu, dispatcher, pointers);
}

void switch_to_thread(x64_emulator& emu, process_context& context, emulator_thread& thread)
bool switch_to_thread(const logger& logger, x64_emulator& emu, process_context& context, emulator_thread& thread)
{
if (thread.exit_status.has_value() || !thread.is_thread_ready(context))
{
return false;
}

auto* active_thread = context.active_thread;

if (active_thread == &thread)
{
return true;
}

logger.print(color::green, "Performing thread switch...\n");


if (active_thread)
{
active_thread->save(emu);
Expand All @@ -430,36 +444,35 @@ namespace

thread.restore(emu);
thread.setup_if_necessary(emu, context);

return true;
}

void switch_to_thread(x64_emulator& emu, process_context& context, const handle thread_handle)
bool switch_to_thread(const logger& logger, x64_emulator& emu, process_context& context, const handle thread_handle)
{
auto* thread = context.threads.get(thread_handle);
if (!thread)
{
throw std::runtime_error("Bad thread handle");
}

switch_to_thread(emu, context, *thread);
return switch_to_thread(logger, emu, context, *thread);
}

void switch_to_next_thread(x64_emulator& emu, process_context& context)
bool switch_to_next_thread(const logger& logger, x64_emulator& emu, process_context& context)
{
//cleanup_threads(context);

bool next_thread = false;

for (auto& thread : context.threads)
{
if (next_thread)
{
if (thread.second.exit_status.has_value())
if (switch_to_thread(logger, emu, context, thread.second))
{
continue;
return true;
}

switch_to_thread(emu, context, thread.second);
return;
continue;
}

if (&thread.second == context.active_thread)
Expand All @@ -468,7 +481,50 @@ namespace
}
}

switch_to_thread(emu, context, context.threads.begin()->second);
for (auto& thread : context.threads)
{
if (switch_to_thread(logger, emu, context, thread.second))
{
return true;
}
}

return false;
}

bool is_object_signaled(process_context& c, const handle h)
{
const auto type = h.value.type;

switch (type)
{
default:
break;

case handle_types::event:
{
const auto* e = c.events.get(h);
if (e)
{
return e->signaled;
}

break;
}

case handle_types::thread:
{
const auto* t = c.threads.get(h);
if (t)
{
return t->exit_status.has_value();
}

break;
}
}

throw std::runtime_error("Bad object");
}
}

Expand Down Expand Up @@ -504,6 +560,70 @@ emulator_thread::emulator_thread(x64_emulator& emu, const process_context& conte
});
}

void emulator_thread::mark_as_ready(const NTSTATUS status)
{
this->pending_status = status;
this->await_time = {};
this->await_object = {};

// TODO: Find out if this is correct
if (this->waiting_for_alert)
{
this->alerted = false;
}

this->waiting_for_alert = false;
}

bool emulator_thread::is_thread_ready(process_context& context)
{
if (this->waiting_for_alert)
{
if (this->alerted)
{
this->mark_as_ready(STATUS_ALERTED);
return true;
}
if (this->is_await_time_over())
{
this->mark_as_ready(STATUS_TIMEOUT);
return true;
}

return false;
}

if (this->await_object.has_value())
{
if (is_object_signaled(context, *this->await_object))
{
this->mark_as_ready(STATUS_WAIT_0);
return true;
}

if (this->is_await_time_over())
{
this->mark_as_ready(STATUS_TIMEOUT);
return true;
}

return false;
}

if (this->await_time.has_value())
{
if (this->is_await_time_over())
{
this->mark_as_ready(STATUS_SUCCESS);
return true;
}

return false;
}

return true;
}

void emulator_thread::setup_registers(x64_emulator& emu, const process_context& context) const
{
setup_stack(emu, this->stack_base, this->stack_size);
Expand Down Expand Up @@ -577,14 +697,17 @@ void windows_emulator::setup_process(const std::filesystem::path& application,
context.default_register_set = emu.save_registers();

const auto main_thread_id = context.create_thread(emu, context.executable->entry_point, 0, 0);
switch_to_thread(emu, context, main_thread_id);
switch_to_thread(this->logger, emu, context, main_thread_id);
}

void windows_emulator::perform_thread_switch()
{
this->logger.print(color::green, "Performing thread switch...\n");
switch_to_next_thread(this->emu(), this->process());
this->switch_thread = false;
while (!switch_to_next_thread(this->logger, this->emu(), this->process()))
{
// TODO: Optimize that
std::this_thread::sleep_for(1ms);
}
}

void windows_emulator::setup_hooks()
Expand Down

0 comments on commit 5f56216

Please sign in to comment.