Add new tests and some syscall fixes (#234)

This PR aims to:
- [Add new
tests](9d0de32cde),
specifically tests for the new registry features were added, as well as
a check for the GetSystemDirectory (this breaks if the SharedSection is
incorrectly configured). This already helped me [fix a
bug](0474eef373)
in the `WOW6432Node` redirection under Linux!
- [Handle TokenGroups in
NtQueryInformationToken](7fef4ebc24),
[Stub SystemFirmwareTableInformation in
NtQuerySystemInformation](3b918f2d5c).
These two changes allow executables protected with Themida to reach
their OEP while running on the emulator.
- [Add c_437.nls to
create-root.bat](21af0de2c8),
because looks like 850 was my system's code page but the playground
emulator uses 437.
This commit is contained in:
Maurice Heumann
2025-04-24 16:07:15 +02:00
committed by GitHub
6 changed files with 207 additions and 5 deletions

View File

@@ -770,6 +770,12 @@ struct TOKEN_USER64
SID_AND_ATTRIBUTES64 User;
};
struct TOKEN_GROUPS64
{
ULONG GroupCount;
SID_AND_ATTRIBUTES64 Groups[1];
};
struct TOKEN_OWNER64
{
EMULATOR_CAST(EmulatorTraits<Emu64>::PVOID, PSID) Owner;

View File

@@ -273,16 +273,189 @@ namespace
return {std::string(data, std::min(static_cast<size_t>(length - 1), sizeof(data)))};
}
std::optional<std::vector<std::string>> get_all_registry_keys(const HKEY root, const char* path)
{
HKEY key{};
if (RegOpenKeyExA(root, path, 0, KEY_READ | KEY_ENUMERATE_SUB_KEYS | KEY_QUERY_VALUE, &key) != ERROR_SUCCESS)
{
return std::nullopt;
}
std::vector<std::string> keys;
std::vector<char> name_buffer(MAX_PATH + 1);
for (DWORD i = 0;; ++i)
{
auto name_buffer_len = static_cast<DWORD>(name_buffer.size());
const LSTATUS status =
RegEnumKeyExA(key, i, name_buffer.data(), &name_buffer_len, nullptr, nullptr, nullptr, nullptr);
if (status == ERROR_SUCCESS)
{
keys.emplace_back(name_buffer.data(), name_buffer_len);
}
else if (status == ERROR_NO_MORE_ITEMS)
{
break;
}
else
{
keys.clear();
break;
}
}
if (keys.empty())
{
RegCloseKey(key);
return std::nullopt;
}
if (RegCloseKey(key) != ERROR_SUCCESS)
{
return std::nullopt;
}
return keys;
}
std::optional<std::vector<std::string>> get_all_registry_values(const HKEY root, const char* path)
{
HKEY key{};
if (RegOpenKeyExA(root, path, 0, KEY_READ | KEY_ENUMERATE_SUB_KEYS | KEY_QUERY_VALUE, &key) != ERROR_SUCCESS)
{
return std::nullopt;
}
std::vector<std::string> values;
std::vector<char> name_buffer(MAX_PATH + 1);
for (DWORD i = 0;; ++i)
{
auto name_buffer_len = static_cast<DWORD>(name_buffer.size());
const auto status =
RegEnumValueA(key, i, name_buffer.data(), &name_buffer_len, nullptr, nullptr, nullptr, nullptr);
if (status == ERROR_SUCCESS)
{
values.emplace_back(name_buffer.data(), name_buffer_len);
}
else if (status == ERROR_NO_MORE_ITEMS)
{
break;
}
else
{
values.clear();
break;
}
}
if (values.empty())
{
RegCloseKey(key);
return std::nullopt;
}
if (RegCloseKey(key) != ERROR_SUCCESS)
{
return std::nullopt;
}
return values;
}
bool test_registry()
{
const auto val =
// Basic Reading Test
const auto prog_files_dir =
read_registry_string(HKEY_LOCAL_MACHINE, R"(SOFTWARE\Microsoft\Windows\CurrentVersion)", "ProgramFilesDir");
if (!val)
if (!prog_files_dir || *prog_files_dir != "C:\\Program Files")
{
return false;
}
return *val == "C:\\Program Files";
// WOW64 Redirection Test
const auto pst_display = read_registry_string(
HKEY_LOCAL_MACHINE,
R"(SOFTWARE\WOW6432Node\Microsoft\Windows NT\CurrentVersion\Time Zones\Pacific Standard Time)", "Display");
if (!pst_display || pst_display->empty())
{
return false;
}
// Key Sub-keys Enumeration Test
const auto subkeys_opt =
get_all_registry_keys(HKEY_LOCAL_MACHINE, R"(SOFTWARE\Microsoft\Windows NT\CurrentVersion)");
if (!subkeys_opt)
{
return false;
}
bool found_fonts = false;
for (const auto& key_name : *subkeys_opt)
{
if (key_name == "Fonts")
{
found_fonts = true;
break;
}
}
if (!found_fonts)
{
return false;
}
// Key Values Enumeration Test
const auto values_opt =
get_all_registry_values(HKEY_LOCAL_MACHINE, R"(SOFTWARE\Microsoft\Windows NT\CurrentVersion)");
if (!values_opt)
{
return false;
}
bool found_product_name = false;
for (const auto& val_name : *values_opt)
{
if (val_name == "ProductName")
{
found_product_name = true;
break;
}
}
if (!found_product_name)
{
return false;
}
return true;
}
bool test_system_info()
{
char sys_dir[MAX_PATH];
if (GetSystemDirectoryA(sys_dir, sizeof(sys_dir)) == 0)
{
return false;
}
if (strlen(sys_dir) != 19)
{
return false;
}
// TODO: This currently doesn't work.
/*
char username[256];
DWORD username_len = sizeof(username);
if (!GetUserNameA(username, &username_len))
{
return false;
}
if (username_len <= 1)
{
return false;
}
*/
return true;
}
void throw_exception()
@@ -477,6 +650,7 @@ int main(const int argc, const char* argv[])
RUN_TEST(test_dir_io, "Dir I/O")
RUN_TEST(test_working_directory, "Working Directory")
RUN_TEST(test_registry, "Registry")
RUN_TEST(test_system_info, "System Info")
RUN_TEST(test_threads, "Threads")
RUN_TEST(test_env, "Environment")
RUN_TEST(test_exceptions, "Exceptions")

View File

@@ -130,6 +130,7 @@ CALL :collect msacm32.dll
CALL :collect locale.nls
CALL :collect c_1252.nls
CALL :collect c_850.nls
CALL :collect c_437.nls
EXIT /B 0

View File

@@ -110,12 +110,12 @@ std::optional<registry_key> registry_manager::get_key(const utils::path_key& key
if (!entry)
{
constexpr std::wstring_view wowPrefix = L"wow6432node\\";
constexpr std::wstring_view wowPrefix = L"wow6432node";
const auto pathStr = path.wstring();
if (pathStr.starts_with(wowPrefix))
{
path = pathStr.substr(wowPrefix.size());
path = pathStr.substr(wowPrefix.size() + 1);
reg_key.path = path;
entry = iterator->second->get_sub_key(path);
}

View File

@@ -105,6 +105,7 @@ namespace syscalls
case SystemFeatureConfigurationInformation:
case SystemSupportedProcessorArchitectures2:
case SystemFeatureConfigurationSectionInformation:
case SystemFirmwareTableInformation:
return STATUS_NOT_SUPPORTED;
case SystemControlFlowTransition:

View File

@@ -75,6 +75,26 @@ namespace syscalls
return STATUS_SUCCESS;
}
if (token_information_class == TokenGroups)
{
constexpr auto required_size = sizeof(TOKEN_GROUPS64) + sizeof(sid);
return_length.write(required_size);
if (required_size > token_information_length)
{
return STATUS_BUFFER_TOO_SMALL;
}
TOKEN_GROUPS64 groups{};
groups.GroupCount = 1;
groups.Groups[0].Attributes = 0;
groups.Groups[0].Sid = token_information + sizeof(TOKEN_GROUPS64);
emulator_object<TOKEN_GROUPS64>{c.emu, token_information}.write(groups);
c.emu.write_memory(token_information + sizeof(TOKEN_GROUPS64), sid, sizeof(sid));
return STATUS_SUCCESS;
}
if (token_information_class == TokenOwner)
{
constexpr auto required_size = sizeof(sid) + sizeof(TOKEN_OWNER64);