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:
Maurice Heumann
2025-12-30 17:56:30 +01:00
committed by GitHub
14 changed files with 241 additions and 159 deletions

View File

@@ -103,17 +103,44 @@ namespace syscalls
for (const auto& t : c.proc.threads | std::views::values)
{
t.teb64->access([&](TEB64& teb) {
if (tls_cell < TLS_MINIMUM_AVAILABLE)
if (tls_cell < TLS_MINIMUM_AVAILABLE)
{
if (c.proc.is_wow64_process)
{
teb.TlsSlots.arr[tls_cell] = 0;
if (t.teb32.has_value())
{
t.teb32->access([&](TEB32& teb32) { teb32.TlsSlots.arr[tls_cell] = 0; });
}
}
else if (teb.TlsExpansionSlots)
else
{
const emulator_object<emulator_pointer> expansion_slots(c.emu, teb.TlsExpansionSlots);
expansion_slots.write(0, tls_cell - TLS_MINIMUM_AVAILABLE);
t.teb64->access([&](TEB64& teb64) { teb64.TlsSlots.arr[tls_cell] = 0; });
}
});
}
else if (tls_cell < TLS_MINIMUM_AVAILABLE + TLS_EXPANSION_SLOTS)
{
if (c.proc.is_wow64_process)
{
if (t.teb32.has_value())
{
t.teb32->access([&](TEB32& teb32) {
if (teb32.TlsExpansionSlots)
{
c.emu.write_memory<uint32_t>(teb32.TlsExpansionSlots + (4 * tls_cell) - TLS_MINIMUM_AVAILABLE, 0);
}
});
}
}
else
{
t.teb64->access([&](TEB64& teb64) {
if (teb64.TlsExpansionSlots)
{
c.emu.write_memory<uint64_t>(teb64.TlsExpansionSlots + (8 * tls_cell) - TLS_MINIMUM_AVAILABLE, 0);
}
});
}
}
}
return STATUS_SUCCESS;
@@ -601,7 +628,7 @@ namespace syscalls
return STATUS_NOT_SUPPORTED;
}
const auto h = c.proc.create_thread(c.win_emu.memory, start_routine, argument, stack_size, create_flags & CREATE_SUSPENDED);
const auto h = c.proc.create_thread(c.win_emu.memory, start_routine, argument, stack_size, create_flags);
thread_handle.write(h);
if (!attribute_list)