mirror of
https://github.com/momo5502/emulator.git
synced 2026-01-25 06:31:02 +00:00
Fix thread support (#640)
This PR fixes several things related to threads emulation:
1. Support `SameTebFlags.InitialThread`
This flag is needed to support emulation of .net executables (not yet
fully supported) that don't have an entry point set in `PE` header. This
applies to both `PE32` and `PE64` executables. If `InitialThread` is set
the loader substitutes an entry point of the .net executable with
`mscoree.dll!_CorExeMain`.
2. Fix static thread local storage for `WOW64`
This fix resolves `shell32.dll` initialization on `WOW64`. This fix also
uses correct structure and field names that are obtained from the
corresponding `.pdb` files.
3. Fix dynamic thread local storage for `WOW64`
4. Fix setting argument of a `WOW64` thread start proc
5. Fix creating suspended thread and parse create_flags
Currently creating suspended thread doesn't work because
`NtCreateThreadEx` handler uses invalid flag `CREATE_SUSPENDED`. This PR
fixes that, and moreover it carefully parses create_flags of the
`NtCreateThreadEx` call.
6. Fix `FS` and `GS` handling
This PR fixes several problems with `GS` and `FS` segments:
* Wrong GDT descriptor for selector 0x53
* Update GDT descriptor for selector 0x53 for a `WOW64` process every
context switch like Windows does
* Set `GS` base when `GS` segment register is updated in 64-bit code
(code selector is `0x33`). When `GS` segment register is loaded with
correct selector (`0x2b`) `GS` base is set to 0. So, when the code
accesses something like `gs:[0]`, a page fault occurs. `KiPageFault`
handles this situation and sets correct `GS` base.
Also, take into account that `teb64.ExceptionList` initially contains
`teb32` address for `WOW64` process. This is used to setup `FS` base
when `wrfsbase` instruction is available. We can enable this instruction
using `kusd.ProcessorFeatures.arr[PF_RDWRFSGSBASE_AVAILABLE] = 1;` and
this work perfectly with `unicorn` backend. Unfortunately `icicle`
backend does not support `wrfsbase`, so I don't enable this instruction
by default.
This commit is contained in:
@@ -178,6 +178,21 @@ namespace icicle
|
||||
}
|
||||
}
|
||||
|
||||
pointer_type get_segment_base(const x86_register base) override
|
||||
{
|
||||
switch (base)
|
||||
{
|
||||
case x86_register::fs:
|
||||
case x86_register::fs_base:
|
||||
return this->reg(x86_register::fs_base);
|
||||
case x86_register::gs:
|
||||
case x86_register::gs_base:
|
||||
return this->reg(x86_register::gs_base);
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
size_t write_raw_register(const int reg, const void* value, const size_t size) override
|
||||
{
|
||||
return icicle_write_register(this->emu_, reg, value, size);
|
||||
@@ -348,7 +363,9 @@ namespace icicle
|
||||
|
||||
const auto& func = *static_cast<decltype(ptr)>(user);
|
||||
const auto res = func(address, 1, static_cast<memory_operation>(operation), violation_type);
|
||||
return res == memory_violation_continuation::resume ? 1 : 0;
|
||||
const auto restart = res == memory_violation_continuation::restart;
|
||||
const auto resume = res == memory_violation_continuation::resume || restart;
|
||||
return resume ? 1 : 0;
|
||||
};
|
||||
|
||||
const auto id = icicle_add_violation_hook(this->emu_, wrapper, ptr);
|
||||
|
||||
@@ -21,6 +21,15 @@ namespace unicorn
|
||||
|
||||
static_assert(static_cast<uint32_t>(x86_register::end) == UC_X86_REG_ENDING);
|
||||
|
||||
constexpr auto IA32_FS_BASE_MSR = 0xC0000100;
|
||||
constexpr auto IA32_GS_BASE_MSR = 0xC0000101;
|
||||
|
||||
struct msr_value
|
||||
{
|
||||
uint64_t id{};
|
||||
uint64_t value{};
|
||||
};
|
||||
|
||||
uc_x86_insn map_hookable_instruction(const x86_hookable_instructions instruction)
|
||||
{
|
||||
switch (instruction)
|
||||
@@ -253,15 +262,6 @@ namespace unicorn
|
||||
|
||||
void set_segment_base(const x86_register base, const pointer_type value) override
|
||||
{
|
||||
constexpr auto IA32_FS_BASE_MSR = 0xC0000100;
|
||||
constexpr auto IA32_GS_BASE_MSR = 0xC0000101;
|
||||
|
||||
struct msr_value
|
||||
{
|
||||
uint64_t id{};
|
||||
uint64_t value{};
|
||||
};
|
||||
|
||||
msr_value msr_val{
|
||||
.id = 0,
|
||||
.value = value,
|
||||
@@ -272,12 +272,10 @@ namespace unicorn
|
||||
case x86_register::fs:
|
||||
case x86_register::fs_base:
|
||||
msr_val.id = IA32_FS_BASE_MSR;
|
||||
preserved_fs_base_ = static_cast<uint64_t>(value);
|
||||
break;
|
||||
case x86_register::gs:
|
||||
case x86_register::gs_base:
|
||||
msr_val.id = IA32_GS_BASE_MSR;
|
||||
preserved_gs_base_ = static_cast<uint64_t>(value);
|
||||
break;
|
||||
default:
|
||||
return;
|
||||
@@ -286,6 +284,30 @@ namespace unicorn
|
||||
this->write_register(x86_register::msr, &msr_val, sizeof(msr_val));
|
||||
}
|
||||
|
||||
pointer_type get_segment_base(const x86_register base) override
|
||||
{
|
||||
msr_value msr_val{};
|
||||
|
||||
switch (base)
|
||||
{
|
||||
case x86_register::fs:
|
||||
case x86_register::fs_base:
|
||||
msr_val.id = IA32_FS_BASE_MSR;
|
||||
break;
|
||||
case x86_register::gs:
|
||||
case x86_register::gs_base:
|
||||
msr_val.id = IA32_GS_BASE_MSR;
|
||||
break;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
|
||||
size_t result_size = sizeof(msr_value);
|
||||
uce(uc_reg_read2(*this, (int)x86_register::msr, &msr_val, &result_size));
|
||||
|
||||
return msr_val.value;
|
||||
}
|
||||
|
||||
size_t write_raw_register(const int reg, const void* value, const size_t size) override
|
||||
{
|
||||
auto result_size = size;
|
||||
@@ -495,18 +517,19 @@ namespace unicorn
|
||||
const auto operation = map_memory_operation(type);
|
||||
const auto violation = map_memory_violation_type(type);
|
||||
|
||||
const auto resume =
|
||||
c(address, static_cast<uint64_t>(size), operation, violation) == memory_violation_continuation::resume;
|
||||
const auto result = c(address, static_cast<uint64_t>(size), operation, violation);
|
||||
const auto restart = result == memory_violation_continuation::restart;
|
||||
const auto resume = result == memory_violation_continuation::resume || restart;
|
||||
|
||||
const auto new_ip = this->read_instruction_pointer();
|
||||
const auto has_ip_changed = ip != new_ip;
|
||||
const auto set_ip = ip != new_ip || restart;
|
||||
|
||||
if (!resume)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (resume && has_ip_changed)
|
||||
if (resume && set_ip)
|
||||
{
|
||||
this->violation_ip_ = new_ip;
|
||||
}
|
||||
@@ -515,7 +538,7 @@ namespace unicorn
|
||||
this->violation_ip_ = std::nullopt;
|
||||
}
|
||||
|
||||
if (has_ip_changed)
|
||||
if (set_ip)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
@@ -538,17 +561,8 @@ namespace unicorn
|
||||
|
||||
emulator_hook* hook_memory_execution(const uint64_t address, const uint64_t size, memory_execution_hook_callback callback)
|
||||
{
|
||||
auto exec_wrapper = [c = std::move(callback), this](uc_engine*, const uint64_t address, const uint32_t /*size*/) {
|
||||
auto exec_wrapper = [c = std::move(callback)](uc_engine*, const uint64_t address, const uint32_t /*size*/) {
|
||||
c(address); //
|
||||
|
||||
// Fix unicorn bug?
|
||||
const auto cs_current = this->reg<uint16_t>(x86_register::cs);
|
||||
if (this->current_reg_cs_ != cs_current)
|
||||
{
|
||||
this->set_segment_base(x86_register::gs, preserved_gs_base_);
|
||||
this->set_segment_base(x86_register::fs, preserved_fs_base_);
|
||||
this->current_reg_cs_ = cs_current;
|
||||
}
|
||||
};
|
||||
|
||||
function_wrapper<void, uc_engine*, uint64_t, uint32_t> wrapper(std::move(exec_wrapper));
|
||||
@@ -657,11 +671,6 @@ namespace unicorn
|
||||
|
||||
const uc_context_serializer serializer(this->uc_, is_snapshot);
|
||||
serializer.serialize(buffer);
|
||||
|
||||
// Serialize unicorn bug workaround state
|
||||
buffer.write(this->preserved_gs_base_);
|
||||
buffer.write(this->preserved_fs_base_);
|
||||
buffer.write(this->current_reg_cs_);
|
||||
}
|
||||
|
||||
void deserialize_state(utils::buffer_deserializer& buffer, const bool is_snapshot) override
|
||||
@@ -674,10 +683,6 @@ namespace unicorn
|
||||
|
||||
const uc_context_serializer serializer(this->uc_, is_snapshot);
|
||||
serializer.deserialize(buffer);
|
||||
// Deserialize unicorn bug workaround state
|
||||
buffer.read(this->preserved_gs_base_);
|
||||
buffer.read(this->preserved_fs_base_);
|
||||
buffer.read(this->current_reg_cs_);
|
||||
}
|
||||
|
||||
std::vector<std::byte> save_registers() const override
|
||||
@@ -712,11 +717,6 @@ namespace unicorn
|
||||
std::vector<std::unique_ptr<hook_object>> hooks_{};
|
||||
std::unordered_map<uint64_t, mmio_callbacks> mmio_{};
|
||||
|
||||
// gs & fs base (Fix unicorn Bug?)
|
||||
mutable uint64_t preserved_gs_base_{0};
|
||||
mutable uint64_t preserved_fs_base_{0};
|
||||
mutable uint16_t current_reg_cs_{0x33};
|
||||
|
||||
static uint64_t calc_end_address(const uint64_t address, uint64_t size)
|
||||
{
|
||||
if (size == 0)
|
||||
|
||||
Reference in New Issue
Block a user