VMProtect/runtime/registry_manager.cc
VNGhostMans 5ec92ee05e first commit
Version 3.x.x
2023-05-14 20:21:09 +07:00

1390 lines
46 KiB
C++
Raw Blame History

#ifdef WIN_DRIVER
#else
#include "common.h"
#include "utils.h"
#include "objects.h"
#include "crypto.h"
#include "core.h"
#include "file_manager.h"
#include "registry_manager.h"
#include "hook_manager.h"
// should be commented out in release builds
// #define CHECKED
#ifdef CHECKED
void XTrace0(LPCTSTR lpszText)
{
::OutputDebugString(lpszText);
}
void XTrace(LPCTSTR lpszFormat, ...)
{
va_list args;
va_start(args, lpszFormat);
int nBuf;
TCHAR szBuffer[16384]; // get rid of this hard-coded buffer
nBuf = _snwprintf_s(szBuffer, 16383, L"%p ", GetCurrentThreadId());
nBuf += _vsnwprintf_s(szBuffer + nBuf, 16383 - nBuf, _TRUNCATE, lpszFormat, args);
::OutputDebugString(szBuffer);
va_end(args);
}
#endif
/**
* hooked functions
*/
NTSTATUS WINAPI HookedNtSetValueKey(HANDLE KeyHandle, PUNICODE_STRING ValueName, ULONG TitleIndex, ULONG Type, PVOID Data, ULONG DataSize)
{
RegistryManager *registry_manager = Core::Instance()->registry_manager();
return registry_manager->NtSetValueKey(KeyHandle, ValueName, TitleIndex, Type, Data, DataSize);
}
NTSTATUS WINAPI HookedNtDeleteValueKey(HANDLE KeyHandle, PUNICODE_STRING ValueName)
{
RegistryManager *registry_manager = Core::Instance()->registry_manager();
return registry_manager->NtDeleteValueKey(KeyHandle, ValueName);
}
NTSTATUS WINAPI HookedNtCreateKey(PHANDLE KeyHandle, ACCESS_MASK DesiredAccess, POBJECT_ATTRIBUTES ObjectAttributes, ULONG TitleIndex, PUNICODE_STRING Class, ULONG CreateOptions, PULONG Disposition)
{
RegistryManager *registry_manager = Core::Instance()->registry_manager();
return registry_manager->NtCreateKey(KeyHandle, DesiredAccess, ObjectAttributes, TitleIndex, Class, CreateOptions, Disposition);
}
NTSTATUS WINAPI HookedNtQueryValueKey(HANDLE KeyHandle, PUNICODE_STRING ValueName, KEY_VALUE_INFORMATION_CLASS KeyValueInformationClass, PVOID KeyValueInformation, ULONG Length, PULONG ResultLength)
{
RegistryManager *registry_manager = Core::Instance()->registry_manager();
return registry_manager->NtQueryValueKey(KeyHandle, ValueName, KeyValueInformationClass, KeyValueInformation, Length, ResultLength);
}
NTSTATUS WINAPI HookedNtOpenKey(PHANDLE KeyHandle, ACCESS_MASK DesiredAccess, POBJECT_ATTRIBUTES ObjectAttributes)
{
RegistryManager *registry_manager = Core::Instance()->registry_manager();
return registry_manager->NtOpenKey(KeyHandle, DesiredAccess, ObjectAttributes);
}
NTSTATUS WINAPI HookedNtOpenKeyEx(PHANDLE KeyHandle, ACCESS_MASK DesiredAccess, POBJECT_ATTRIBUTES ObjectAttributes, ULONG OpenOptions)
{
RegistryManager *registry_manager = Core::Instance()->registry_manager();
return registry_manager->NtOpenKeyEx(KeyHandle, DesiredAccess, ObjectAttributes, OpenOptions);
}
NTSTATUS WINAPI HookedNtDeleteKey(HANDLE KeyHandle)
{
RegistryManager *registry_manager = Core::Instance()->registry_manager();
return registry_manager->NtDeleteKey(KeyHandle);
}
NTSTATUS WINAPI HookedNtQueryKey(HANDLE KeyHandle, KEY_INFORMATION_CLASS KeyInformationClass, PVOID KeyInformation, ULONG Length, PULONG ResultLength)
{
RegistryManager *registry_manager = Core::Instance()->registry_manager();
return registry_manager->NtQueryKey(KeyHandle, KeyInformationClass, KeyInformation, Length, ResultLength);
}
NTSTATUS WINAPI HookedNtEnumerateValueKey(HANDLE KeyHandle, ULONG Index, KEY_VALUE_INFORMATION_CLASS KeyValueInformationClass, PVOID KeyValueInformation, ULONG Length, PULONG ResultLength)
{
RegistryManager *registry_manager = Core::Instance()->registry_manager();
return registry_manager->NtEnumerateValueKey(KeyHandle, Index, KeyValueInformationClass, KeyValueInformation, Length, ResultLength);
}
NTSTATUS WINAPI HookedNtEnumerateKey(HANDLE KeyHandle, ULONG Index, KEY_INFORMATION_CLASS KeyInformationClass, PVOID KeyInformation, ULONG Length, PULONG ResultLength)
{
RegistryManager *registry_manager = Core::Instance()->registry_manager();
return registry_manager->NtEnumerateKey(KeyHandle, Index, KeyInformationClass, KeyInformation, Length, ResultLength);
}
/**
* RegistryValue
*/
RegistryValue::RegistryValue(const wchar_t *name)
: name_(NULL), type_(REG_NONE), data_(NULL), size_(0)
{
size_t size = wcslen(name) + 1;
name_ = new wchar_t[size];
memcpy(name_, name, size * sizeof(wchar_t));
}
RegistryValue::~RegistryValue()
{
delete [] name_;
delete [] data_;
}
void RegistryValue::SetValue(uint32_t type, void *data, uint32_t size)
{
type_ = type;
size_ = size;
delete [] data_;
if (size) {
data_ = new uint8_t[size_];
memcpy(data_, data, size_);
} else {
data_ = NULL;
}
}
/**
* RegistryKey
*/
RegistryKey::RegistryKey()
: name_(NULL), owner_(NULL), is_real_(false), is_wow_(false), last_write_time_(0)
{
}
RegistryKey::RegistryKey(RegistryKey *owner, const wchar_t *name, bool is_real)
: name_(NULL), owner_(owner), is_real_(is_real), is_wow_(false), last_write_time_(0)
{
size_t size = wcslen(name) + 1;
name_ = new wchar_t[size];
memcpy(name_, name, size * sizeof(wchar_t));
}
RegistryKey::~RegistryKey()
{
for (size_t i = 0; i < values_.size(); i++) {
RegistryValue *value = values_[i];
delete value;
}
values_.clear();
for (size_t i = 0; i < keys_.size(); i++) {
RegistryKey *key = keys_[i];
delete key;
}
keys_.clear();
delete [] name_;
}
RegistryValue *RegistryKey::GetValue(const wchar_t *value_name) const
{
if (!value_name)
value_name = L"";
for (size_t i = 0; i < values_.size(); i++) {
RegistryValue *value = values_[i];
if (_wcsicmp(value_name, value->name()) == 0)
return value;
}
return NULL;
}
RegistryValue *RegistryKey::AddValue(wchar_t *value_name)
{
if (!value_name)
value_name = L"";
RegistryValue *value = new RegistryValue(value_name);
values_.push_back(value);
return value;
}
void RegistryKey::SetValue(wchar_t *value_name, uint32_t type, void *data, uint32_t size)
{
RegistryValue *value = GetValue(value_name);
if (!value)
value = AddValue(value_name);
value->SetValue(type, data, size);
FILETIME system_time;
GetSystemTimeAsFileTime(&system_time);
last_write_time_ = static_cast<uint64_t>(system_time.dwHighDateTime) << 32 | static_cast<uint64_t>(system_time.dwLowDateTime);
}
bool RegistryKey::DeleteValue(wchar_t *value_name)
{
if (!value_name)
value_name = L"";
for (size_t i = 0; i < values_.size(); i++) {
RegistryValue *value = values_[i];
if (_wcsicmp(value_name, value->name()) == 0) {
delete value;
values_.erase(i);
return true;
}
}
return false;
}
bool RegistryKey::is_wow_node(const wchar_t *name) const
{
bool ret = (is_wow_ && _wcsicmp(L"Wow6432Node", name) == 0);
#ifdef CHECKED
XTrace(L"is_wow_node: %s = %d\n", name, ret ? 1 : 0);
#endif
return ret;
}
RegistryKey *RegistryKey::GetChild(const wchar_t *name) const
{
if (!name || *name == 0)
return NULL;
if (is_wow_node(name))
return const_cast<RegistryKey *>(this);
for (size_t i = 0; i < keys_.size(); i++) {
RegistryKey *key = keys_[i];
if (_wcsicmp(name, key->name()) == 0)
return key;
}
return NULL;
}
RegistryKey *RegistryKey::GetKey(const wchar_t *name) const
{
if (!name || *name == 0)
return NULL;
if (*name == L'\\')
name++;
const wchar_t *sub_name = wcschr(name, L'\\');
if (!sub_name)
return GetChild(name);
RegistryKey *key = GetChild(UnicodeString(name, sub_name - name).c_str());
return key ? key->GetKey(sub_name) : NULL;
}
RegistryKey *RegistryKey::AddChild(const wchar_t *name, bool is_real, bool *is_exists)
{
if (!name || *name == 0)
return NULL;
RegistryKey *key = is_wow_node(name) ? this : GetChild(name);
if (is_exists)
*is_exists = (key != NULL);
if (!key) {
key = new RegistryKey(this, name, is_real);
keys_.push_back(key);
}
return key;
}
RegistryKey *RegistryKey::AddKey(const wchar_t *name, bool is_real, bool *is_exists)
{
if (!name || *name == 0)
return NULL;
if (*name == L'\\')
name++;
const wchar_t *sub_name = wcschr(name, L'\\');
if (!sub_name)
return AddChild(name, is_real, is_exists);
RegistryKey *key = AddChild(UnicodeString(name, sub_name - name).c_str(), is_real, is_exists);
return key ? key->AddKey(sub_name, is_real, is_exists) : NULL;
}
bool RegistryKey::DeleteKey(RegistryKey *key)
{
size_t index = keys_.find(key);
if (index != -1) {
keys_.erase(index);
delete key;
return true;
}
return false;
}
UnicodeString RegistryKey::full_name() const
{
if (!owner_)
return UnicodeString();
return owner_->full_name() + L'\\' + name_;
}
/**
* RegistryManager
*/
RegistryManager::RegistryManager(const uint8_t *data, HMODULE instance, const uint8_t *key, VirtualObjectList *objects)
: instance_(instance)
, data_(data)
, objects_(objects)
, nt_set_value_key_(NULL)
, nt_delete_value_key_(NULL)
, nt_create_key_(NULL)
, nt_open_key_(NULL)
, nt_open_key_ex_(NULL)
, nt_query_value_key_(NULL)
, nt_delete_key_(NULL)
, nt_query_key_(NULL)
, nt_enumerate_value_key_(NULL)
, nt_enumerate_key_(NULL)
, append_mode_(false)
{
key_ = *(reinterpret_cast<const uint32_t *>(key));
//#ifndef _WIN64
static const wchar_t *wow_keys[] = {
L"\\REGISTRY\\MACHINE\\SOFTWARE\\Classes"
};
for (size_t i = 0; i < _countof(wow_keys); i++) {
RegistryKey *key = cache_.AddKey(wow_keys[i], true, NULL);
if (key)
key->set_is_wow(true);
}
//#endif
}
void RegistryManager::HookAPIs(HookManager &hook_manager)
{
hook_manager.Begin();
HMODULE dll = GetModuleHandleA(VMProtectDecryptStringA("ntdll.dll"));
nt_set_value_key_ = hook_manager.HookAPI(dll, VMProtectDecryptStringA("NtSetValueKey"), &HookedNtSetValueKey);
nt_delete_value_key_ = hook_manager.HookAPI(dll, VMProtectDecryptStringA("NtDeleteValueKey"), &HookedNtDeleteValueKey);
nt_create_key_ = hook_manager.HookAPI(dll, VMProtectDecryptStringA("NtCreateKey"), &HookedNtCreateKey);
nt_open_key_ = hook_manager.HookAPI(dll, VMProtectDecryptStringA("NtOpenKey"), &HookedNtOpenKey);
nt_open_key_ex_ = hook_manager.HookAPI(dll, VMProtectDecryptStringA("NtOpenKeyEx"), &HookedNtOpenKeyEx, false);
nt_query_value_key_ = hook_manager.HookAPI(dll, VMProtectDecryptStringA("NtQueryValueKey"), &HookedNtQueryValueKey);
nt_delete_key_ = hook_manager.HookAPI(dll, VMProtectDecryptStringA("NtDeleteKey"), &HookedNtDeleteKey);
nt_query_key_ = hook_manager.HookAPI(dll, VMProtectDecryptStringA("NtQueryKey"), &HookedNtQueryKey);
nt_enumerate_value_key_ = hook_manager.HookAPI(dll, VMProtectDecryptStringA("NtEnumerateValueKey"), &HookedNtEnumerateValueKey);
nt_enumerate_key_ = hook_manager.HookAPI(dll, VMProtectDecryptStringA("NtEnumerateKey"), &HookedNtEnumerateKey);
hook_manager.End();
}
void RegistryManager::UnhookAPIs(HookManager &hook_manager)
{
hook_manager.Begin();
hook_manager.UnhookAPI(nt_set_value_key_);
hook_manager.UnhookAPI(nt_delete_value_key_);
hook_manager.UnhookAPI(nt_create_key_);
hook_manager.UnhookAPI(nt_open_key_);
hook_manager.UnhookAPI(nt_open_key_ex_);
hook_manager.UnhookAPI(nt_query_value_key_);
hook_manager.UnhookAPI(nt_delete_key_);
hook_manager.UnhookAPI(nt_query_key_);
hook_manager.UnhookAPI(nt_enumerate_value_key_);
hook_manager.UnhookAPI(nt_enumerate_key_);
hook_manager.End();
}
REGISTRY_DIRECTORY RegistryManager::DecryptDirectory(const REGISTRY_DIRECTORY *directory_enc) const
{
REGISTRY_DIRECTORY res;
res.Reserved1 = directory_enc->Reserved1 ^ key_;
res.Reserved2 = directory_enc->Reserved2 ^ key_;
return res;
}
void RegistryManager::BeginRegisterServer()
{
append_mode_ = true;
}
void RegistryManager::EndRegisterServer()
{
append_mode_ = false;
}
NTSTATUS __forceinline RegistryManager::TrueNtSetValueKey(HANDLE KeyHandle, PUNICODE_STRING ValueName, ULONG TitleIndex, ULONG Type, PVOID Data, ULONG DataSize)
{
typedef NTSTATUS (WINAPI tNtSetValueKey)(HANDLE KeyHandle, PUNICODE_STRING ValueName, ULONG TitleIndex, ULONG Type, PVOID Data, ULONG DataSize);
return reinterpret_cast<tNtSetValueKey *>(nt_set_value_key_)(KeyHandle, ValueName, TitleIndex, Type, Data, DataSize);
}
NTSTATUS __forceinline RegistryManager::TrueNtDeleteValueKey(HANDLE KeyHandle, PUNICODE_STRING ValueName)
{
typedef NTSTATUS (WINAPI tNtDeleteValueKey)(HANDLE KeyHandle, PUNICODE_STRING ValueName);
return reinterpret_cast<tNtDeleteValueKey *>(nt_delete_value_key_)(KeyHandle, ValueName);
}
NTSTATUS __forceinline RegistryManager::TrueNtCreateKey(PHANDLE KeyHandle, ACCESS_MASK DesiredAccess, POBJECT_ATTRIBUTES ObjectAttributes, ULONG TitleIndex, PUNICODE_STRING Class, ULONG CreateOptions, PULONG Disposition)
{
typedef NTSTATUS (WINAPI tNtCreateKey)(PHANDLE KeyHandle, ACCESS_MASK DesiredAccess, POBJECT_ATTRIBUTES ObjectAttributes, ULONG TitleIndex, PUNICODE_STRING Class, ULONG CreateOptions, PULONG Disposition);
return reinterpret_cast<tNtCreateKey *>(nt_create_key_)(KeyHandle, DesiredAccess, ObjectAttributes, TitleIndex, Class, CreateOptions, Disposition);
}
NTSTATUS __forceinline RegistryManager::TrueNtQueryValueKey(HANDLE KeyHandle, PUNICODE_STRING ValueName, KEY_VALUE_INFORMATION_CLASS KeyValueInformationClass, PVOID KeyValueInformation, ULONG Length, PULONG ResultLength)
{
typedef NTSTATUS (WINAPI tNtQueryValueKey)(HANDLE KeyHandle, PUNICODE_STRING ValueName, KEY_VALUE_INFORMATION_CLASS KeyValueInformationClass, PVOID KeyValueInformation, ULONG Length, PULONG ResultLength);
return reinterpret_cast<tNtQueryValueKey *>(nt_query_value_key_)(KeyHandle, ValueName, KeyValueInformationClass, KeyValueInformation, Length, ResultLength);
}
NTSTATUS __forceinline RegistryManager::TrueNtOpenKey(PHANDLE KeyHandle, ACCESS_MASK DesiredAccess, POBJECT_ATTRIBUTES ObjectAttributes)
{
typedef NTSTATUS (WINAPI tNtOpenKey)(PHANDLE KeyHandle, ACCESS_MASK DesiredAccess, POBJECT_ATTRIBUTES ObjectAttributes);
return reinterpret_cast<tNtOpenKey *>(nt_open_key_)(KeyHandle, DesiredAccess, ObjectAttributes);
}
NTSTATUS __forceinline RegistryManager::TrueNtOpenKeyEx(PHANDLE KeyHandle, ACCESS_MASK DesiredAccess, POBJECT_ATTRIBUTES ObjectAttributes, ULONG OpenOptions)
{
typedef NTSTATUS (WINAPI tNtOpenKeyEx)(PHANDLE KeyHandle, ACCESS_MASK DesiredAccess, POBJECT_ATTRIBUTES ObjectAttributes, ULONG OpenOptions);
return reinterpret_cast<tNtOpenKeyEx *>(nt_open_key_ex_)(KeyHandle, DesiredAccess, ObjectAttributes, OpenOptions);
}
NTSTATUS __forceinline RegistryManager::TrueNtDeleteKey(HANDLE KeyHandle)
{
typedef NTSTATUS (WINAPI tNtDeleteKey)(HANDLE KeyHandle);
return reinterpret_cast<tNtDeleteKey *>(nt_delete_key_)(KeyHandle);
}
NTSTATUS __forceinline RegistryManager::TrueNtQueryKey(HANDLE KeyHandle, KEY_INFORMATION_CLASS KeyInformationClass, PVOID KeyInformation, ULONG Length, PULONG ResultLength)
{
typedef NTSTATUS (WINAPI tNtQueryKey)(HANDLE KeyHandle, KEY_INFORMATION_CLASS KeyInformationClass, PVOID KeyInformation, ULONG Length, PULONG ResultLength);
return reinterpret_cast<tNtQueryKey *>(nt_query_key_)(KeyHandle, KeyInformationClass, KeyInformation, Length, ResultLength);
}
NTSTATUS __forceinline RegistryManager::TrueNtEnumerateValueKey(HANDLE KeyHandle, ULONG Index, KEY_VALUE_INFORMATION_CLASS KeyValueInformationClass, PVOID KeyValueInformation, ULONG Length, PULONG ResultLength)
{
typedef NTSTATUS (WINAPI tNtEnumerateValueKey)(HANDLE KeyHandle, ULONG Index, KEY_VALUE_INFORMATION_CLASS KeyValueInformationClass, PVOID KeyValueInformation, ULONG Length, PULONG ResultLength);
return reinterpret_cast<tNtEnumerateValueKey *>(nt_enumerate_value_key_)(KeyHandle, Index, KeyValueInformationClass, KeyValueInformation, Length, ResultLength);
}
NTSTATUS __forceinline RegistryManager::TrueNtEnumerateKey(HANDLE KeyHandle, ULONG Index, KEY_INFORMATION_CLASS KeyInformationClass, PVOID KeyInformation, ULONG Length, PULONG ResultLength)
{
typedef NTSTATUS (WINAPI tNtEnumerateKey)(HANDLE KeyHandle, ULONG Index, KEY_INFORMATION_CLASS KeyInformationClass, PVOID KeyInformation, ULONG Length, PULONG ResultLength);
return reinterpret_cast<tNtEnumerateKey *>(nt_enumerate_key_)(KeyHandle, Index, KeyInformationClass, KeyInformation, Length, ResultLength);
}
NTSTATUS __forceinline RegistryManager::TrueNtQueryObject(HANDLE Handle, OBJECT_INFORMATION_CLASS ObjectInformationClass, PVOID ObjectInformation, ULONG ObjectInformationLength, PULONG ReturnLength)
{
return Core::Instance()->TrueNtQueryObject(Handle, ObjectInformationClass, ObjectInformation, ObjectInformationLength, ReturnLength);
}
NTSTATUS RegistryManager::NtSetValueKey(HANDLE KeyHandle, PUNICODE_STRING ValueName, ULONG TitleIndex, ULONG Type, PVOID Data, ULONG DataSize)
{
#ifdef CHECKED
UnicodeString s(ValueName->Buffer, ValueName->Length / sizeof(wchar_t));
XTrace(L"->NtSetValueKey %p %s\n", KeyHandle, s.c_str());
#endif
{
CriticalSection cs(objects_->critical_section());
VirtualObject *object = objects_->GetKey(KeyHandle);
if (object && object->ref()) {
if ((object->access() & KEY_SET_VALUE) == 0)
{
#ifdef CHECKED
XTrace(L"NtSetValueKey STATUS_ACCESS_DENIED\n");
#endif
return STATUS_ACCESS_DENIED;
}
RegistryKey *key = static_cast<RegistryKey *>(object->ref());
try {
key->SetValue(ValueName->Buffer, Type, Data, DataSize);
#ifdef CHECKED
XTrace(L"NtSetValueKey STATUS_SUCCESS\n");
#endif
return STATUS_SUCCESS;
} catch(...) {
#ifdef CHECKED
XTrace(L"NtSetValueKey STATUS_ACCESS_VIOLATION\n");
#endif
return STATUS_ACCESS_VIOLATION;
}
}
}
NTSTATUS ret = TrueNtSetValueKey(KeyHandle, ValueName, TitleIndex, Type, Data, DataSize);
#ifdef CHECKED
XTrace(L"TrueNtSetValueKey %p\n", ret);
#endif
return ret;
}
NTSTATUS RegistryManager::NtDeleteValueKey(HANDLE KeyHandle, PUNICODE_STRING ValueName)
{
#ifdef CHECKED
UnicodeString s(ValueName->Buffer, ValueName->Length / sizeof(wchar_t));
XTrace(L"->NtDeleteValueKey %p %s\n", KeyHandle, s.c_str());
#endif
{
CriticalSection cs(objects_->critical_section());
VirtualObject *object = objects_->GetKey(KeyHandle);
if (object && object->ref()) {
if ((object->access() & KEY_SET_VALUE) == 0)
{
#ifdef CHECKED
XTrace(L"NtDeleteValueKey STATUS_ACCESS_DENIED\n");
#endif
return STATUS_ACCESS_DENIED;
}
RegistryKey *key = static_cast<RegistryKey *>(object->ref());
try {
NTSTATUS ret = key->DeleteValue(ValueName->Buffer) ? STATUS_SUCCESS : STATUS_OBJECT_NAME_NOT_FOUND;
#ifdef CHECKED
XTrace(L"NtDeleteValueKey STATUS_SUCCESS\n");
#endif
return ret;
} catch(...) {
#ifdef CHECKED
XTrace(L"NtSetValueKey STATUS_ACCESS_VIOLATION\n");
#endif
return STATUS_ACCESS_VIOLATION;
}
}
}
NTSTATUS ret = TrueNtDeleteValueKey(KeyHandle, ValueName);
#ifdef CHECKED
XTrace(L"TrueNtDeleteValueKey %p\n", ret);
#endif
return ret;
}
RegistryKey *RegistryManager::GetRootKey(HANDLE root, uint32_t *access, bool can_create)
{
RegistryKey *res = NULL;
if (root) {
VirtualObject *object = objects_->GetKey(root);
if (object && object->ref()) {
// root is a virtual key
res = static_cast<RegistryKey *>(object->ref());
if (access)
*access = object->access();
} else {
// root is a real key
KEY_NAME_INFORMATION info;
DWORD size;
if (TrueNtQueryKey(root, KeyNameInformation, &info, sizeof(info), &size) == STATUS_BUFFER_OVERFLOW) {
KEY_NAME_INFORMATION *data = reinterpret_cast<KEY_NAME_INFORMATION *>(new uint8_t[size]);
if (NT_SUCCESS(TrueNtQueryKey(root, KeyNameInformation, data, size, &size))) {
UnicodeString str(data->Name, data->NameLength / sizeof(wchar_t));
res = can_create ? cache_.AddKey(str.c_str(), true, NULL) : cache_.GetKey(str.c_str());
if (access) {
if (object)
*access = object->access();
else {
PUBLIC_OBJECT_BASIC_INFORMATION info;
*access = (NT_SUCCESS(TrueNtQueryObject(root, ObjectBasicInformation, &info, sizeof(info), NULL))) ? info.GrantedAccess : 0;
}
}
}
delete [] data;
}
}
} else {
res = &cache_;
if (access)
*access = KEY_CREATE_SUB_KEY;
}
return res;
}
NTSTATUS RegistryManager::NtCreateKey(PHANDLE KeyHandle, ACCESS_MASK DesiredAccess, POBJECT_ATTRIBUTES ObjectAttributes, ULONG TitleIndex, PUNICODE_STRING Class, ULONG CreateOptions, PULONG Disposition)
{
#ifdef CHECKED
UnicodeString s(ObjectAttributes->ObjectName->Buffer, ObjectAttributes->ObjectName->Length / sizeof(wchar_t));
XTrace(L"->NtCreateKey %p %s DesiredAccess=%p\n", ObjectAttributes->RootDirectory, s.c_str(), DesiredAccess);
#endif
{
CriticalSection cs(objects_->critical_section());
try {
uint32_t root_access = 0;
RegistryKey *root_key = GetRootKey(ObjectAttributes->RootDirectory, &root_access, append_mode_);
if (root_key) {
bool is_exists = true;
RegistryKey *key;
if (ObjectAttributes->ObjectName->Buffer) {
UnicodeString str(ObjectAttributes->ObjectName->Buffer, ObjectAttributes->ObjectName->Length / sizeof(wchar_t));
key = ((root_access & KEY_CREATE_SUB_KEY) != 0 && (append_mode_ || !root_key->is_real())) ? root_key->AddKey(str.c_str(), false, &is_exists) : root_key->GetKey(str.c_str());
} else {
key = root_key;
}
if (key) {
if (!key->is_real()) {
HANDLE newHandle = ::CreateEventA(NULL, false, false, NULL);
#ifdef CHECKED
if(objects_->GetKey(newHandle))
{
XTrace(L"NtCreateKey HANDLE REUSE DETECTED\n");
}
#endif
VirtualObject *object = objects_->Add(OBJECT_KEY, key, newHandle, DesiredAccess);
*KeyHandle = object->handle();
if (Disposition)
*Disposition = is_exists ? REG_OPENED_EXISTING_KEY : REG_CREATED_NEW_KEY;
#ifdef CHECKED
XTrace(L"NtCreateKey STATUS_SUCCESS, is_exists=%d, h=%p\n", is_exists?1:0, *KeyHandle);
#endif
return STATUS_SUCCESS;
}
} else if (ObjectAttributes->RootDirectory) {
VirtualObject *object = objects_->GetKey(ObjectAttributes->RootDirectory);
if (object && object->ref())
{
NTSTATUS ret = ((root_access & KEY_CREATE_SUB_KEY) == 0) ? STATUS_ACCESS_DENIED : STATUS_INVALID_PARAMETER;
#ifdef CHECKED
XTrace(L"NtCreateKey re=%d\n", ret);
#endif
return ret;
}
}
}
} catch(...) {
#ifdef CHECKED
XTrace(L"NtCreateKey STATUS_ACCESS_VIOLATION\n");
#endif
return STATUS_ACCESS_VIOLATION;
}
}
NTSTATUS ret = TrueNtCreateKey(KeyHandle, DesiredAccess, ObjectAttributes, TitleIndex, Class, CreateOptions, Disposition);
#ifdef CHECKED
XTrace(L"TrueNtCreateKey %p h=%p\n", ret, *KeyHandle);
#endif
return ret;
}
NTSTATUS RegistryManager::NtOpenKey(PHANDLE KeyHandle, ACCESS_MASK DesiredAccess, POBJECT_ATTRIBUTES ObjectAttributes)
{
#ifdef CHECKED
UnicodeString s(ObjectAttributes->ObjectName->Buffer, ObjectAttributes->ObjectName->Length / sizeof(wchar_t));
XTrace(L"->NtOpenKey %p %s DesiredAccess=%p\n", ObjectAttributes->RootDirectory, s.c_str(), DesiredAccess);
#endif
{
CriticalSection cs(objects_->critical_section());
try {
RegistryKey *root_key = GetRootKey(ObjectAttributes->RootDirectory, NULL, false);
if (root_key) {
RegistryKey *key = ObjectAttributes->ObjectName->Buffer ? root_key->GetKey(UnicodeString(ObjectAttributes->ObjectName->Buffer, ObjectAttributes->ObjectName->Length / sizeof(wchar_t)).c_str()) : root_key;
if (key) {
if (!key->is_real()) {
HANDLE newHandle = ::CreateEventA(NULL, false, false, NULL);
#ifdef CHECKED
if(objects_->GetKey(newHandle))
{
XTrace(L"NtOpenKey HANDLE REUSE DETECTED\n");
}
#endif
VirtualObject *object = objects_->Add(OBJECT_KEY, key, newHandle, DesiredAccess);
*KeyHandle = object->handle();
#ifdef CHECKED
XTrace(L"NtOpenKey STATUS_SUCCESS, h=%p\n", *KeyHandle);
#endif
return STATUS_SUCCESS;
}
} else if (ObjectAttributes->RootDirectory) {
VirtualObject *object = objects_->GetKey(ObjectAttributes->RootDirectory);
if (object && object->ref())
{
#ifdef CHECKED
XTrace(L"NtOpenKey STATUS_OBJECT_NAME_NOT_FOUND\n");
#endif
return STATUS_OBJECT_NAME_NOT_FOUND;
}
}
}
} catch(...) {
#ifdef CHECKED
XTrace(L"NtOpenKey STATUS_ACCESS_VIOLATION\n");
#endif
return STATUS_ACCESS_VIOLATION;
}
if (append_mode_ && (DesiredAccess & (MAXIMUM_ALLOWED | KEY_SET_VALUE | KEY_CREATE_SUB_KEY))) {
NTSTATUS status = TrueNtOpenKey(KeyHandle, KEY_READ | (DesiredAccess & KEY_WOW64_RES), ObjectAttributes);
#ifdef CHECKED
XTrace(L"TrueNtOpenKey for MAXIMUM_ALLOWED %p h=%p\n", status, *KeyHandle);
#endif
if (NT_SUCCESS(status)) {
RegistryKey *root_key = GetRootKey(ObjectAttributes->RootDirectory, NULL, true);
if (root_key) {
bool is_exists = true;
RegistryKey *key = ObjectAttributes->ObjectName->Buffer ? root_key->AddKey(UnicodeString(ObjectAttributes->ObjectName->Buffer, ObjectAttributes->ObjectName->Length / sizeof(wchar_t)).c_str(), false, &is_exists) : root_key;
if (key)
objects_->Add(OBJECT_KEY, key, *KeyHandle, DesiredAccess);
}
}
return status;
}
}
NTSTATUS ret = TrueNtOpenKey(KeyHandle, DesiredAccess, ObjectAttributes);
#ifdef CHECKED
XTrace(L"TrueNtOpenKey %p h=%p\n", ret, *KeyHandle);
#endif
return ret;
}
NTSTATUS RegistryManager::NtOpenKeyEx(PHANDLE KeyHandle, ACCESS_MASK DesiredAccess, POBJECT_ATTRIBUTES ObjectAttributes, ULONG OpenOptions)
{
#ifdef CHECKED
UnicodeString s(ObjectAttributes->ObjectName->Buffer, ObjectAttributes->ObjectName->Length / sizeof(wchar_t));
XTrace(L"->NtOpenKeyEx %p %s DesiredAccess=%p\n", ObjectAttributes->RootDirectory, s.c_str(), DesiredAccess);
#endif
{
CriticalSection cs(objects_->critical_section());
try {
RegistryKey *root_key = GetRootKey(ObjectAttributes->RootDirectory, NULL, false);
if (root_key) {
RegistryKey *key = ObjectAttributes->ObjectName->Buffer ? root_key->GetKey(UnicodeString(ObjectAttributes->ObjectName->Buffer, ObjectAttributes->ObjectName->Length / sizeof(wchar_t)).c_str()) : root_key;
if (key) {
if (!key->is_real()) {
VirtualObject *object = objects_->Add(OBJECT_KEY, key, ::CreateEventA(NULL, false, false, NULL), DesiredAccess);
*KeyHandle = object->handle();
#ifdef CHECKED
XTrace(L"NtOpenKeyEx STATUS_SUCCESS, h=%p\n", *KeyHandle);
#endif
return STATUS_SUCCESS;
}
} else if (ObjectAttributes->RootDirectory) {
VirtualObject *object = objects_->GetKey(ObjectAttributes->RootDirectory);
if (object && object->ref())
{
#ifdef CHECKED
XTrace(L"NtOpenKeyEx STATUS_OBJECT_NAME_NOT_FOUND\n");
#endif
return STATUS_OBJECT_NAME_NOT_FOUND;
}
}
}
} catch(...) {
#ifdef CHECKED
XTrace(L"NtOpenKeyEx STATUS_ACCESS_VIOLATION\n");
#endif
return STATUS_ACCESS_VIOLATION;
}
if (append_mode_ && (DesiredAccess & (MAXIMUM_ALLOWED | KEY_SET_VALUE | KEY_CREATE_SUB_KEY))) {
NTSTATUS status = TrueNtOpenKey(KeyHandle, KEY_READ | (DesiredAccess & KEY_WOW64_RES), ObjectAttributes);
#ifdef CHECKED
XTrace(L"TrueNtOpenKey for MAXIMUM_ALLOWED %p h=%p\n", status, *KeyHandle);
#endif
if (NT_SUCCESS(status)) {
RegistryKey *root_key = GetRootKey(ObjectAttributes->RootDirectory, NULL, true);
if (root_key) {
bool is_exists = true;
RegistryKey *key = ObjectAttributes->ObjectName->Buffer ? root_key->AddKey(UnicodeString(ObjectAttributes->ObjectName->Buffer, ObjectAttributes->ObjectName->Length / sizeof(wchar_t)).c_str(), false, &is_exists) : root_key;
if (key)
objects_->Add(OBJECT_KEY, key, *KeyHandle, DesiredAccess);
}
}
return status;
}
}
NTSTATUS ret = TrueNtOpenKeyEx(KeyHandle, DesiredAccess, ObjectAttributes, OpenOptions);
#ifdef CHECKED
XTrace(L"TrueNtOpenKeyEx %p h=%p\n", ret, *KeyHandle);
#endif
return ret;
}
NTSTATUS RegistryManager::QueryValueKey(RegistryValue *value, KEY_VALUE_INFORMATION_CLASS KeyValueInformationClass, PVOID KeyValueInformation, ULONG Length, PULONG ResultLength)
{
if (!value)
{
#ifdef CHECKED
XTrace(L"QueryValueKey STATUS_INVALID_PARAMETER\n");
#endif
return STATUS_INVALID_PARAMETER;
}
switch (KeyValueInformationClass) {
case KeyValueBasicInformation:
{
uint32_t name_size = static_cast<ULONG>(wcslen(value->name()) * sizeof(wchar_t));
uint32_t min_size = offsetof(KEY_VALUE_BASIC_INFORMATION, Name);
uint32_t result_size = min_size + name_size;
if (ResultLength)
*ResultLength = result_size;
if (Length < min_size)
return STATUS_BUFFER_TOO_SMALL;
KEY_VALUE_BASIC_INFORMATION *info = static_cast<KEY_VALUE_BASIC_INFORMATION *>(KeyValueInformation);
info->TitleIndex = 0;
info->Type = value->type();
info->NameLength = name_size;
if (Length < result_size)
return STATUS_BUFFER_OVERFLOW;
memcpy(info->Name, value->name(), name_size);
}
return STATUS_SUCCESS;
case KeyValueFullInformation:
case KeyValueFullInformationAlign64:
{
uint32_t align = 0;
if (KeyValueInformationClass == KeyValueFullInformationAlign64) {
align = 8 - ULONG(INT_PTR(KeyValueInformation)) % 8; //-V221
KeyValueInformation = static_cast<uint8_t *>(KeyValueInformation) + align;
}
uint32_t name_size = static_cast<ULONG>(wcslen(value->name()) * sizeof(wchar_t));
uint32_t data_size = value->size();
uint32_t min_size = offsetof(KEY_VALUE_FULL_INFORMATION, Name) + align;
uint32_t result_size = min_size + name_size + data_size;
if (ResultLength)
*ResultLength = result_size;
if (Length < min_size)
return STATUS_BUFFER_TOO_SMALL;
KEY_VALUE_FULL_INFORMATION *info = static_cast<KEY_VALUE_FULL_INFORMATION *>(KeyValueInformation);
info->TitleIndex = 0;
info->Type = value->type();
info->NameLength = name_size;
info->DataLength = data_size;
info->DataOffset = min_size + name_size;
if (Length < result_size)
return STATUS_BUFFER_OVERFLOW;
memcpy(info->Name, value->name(), name_size);
memcpy(reinterpret_cast<uint8_t*>(info) + info->DataOffset, value->data(), data_size);
}
return STATUS_SUCCESS;
case KeyValuePartialInformation:
case KeyValuePartialInformationAlign64:
{
uint32_t align = 0;
if (KeyValueInformationClass == KeyValuePartialInformationAlign64) {
align = 8 - ULONG(INT_PTR(KeyValueInformation)) % 8; //-V221
KeyValueInformation = static_cast<uint8_t *>(KeyValueInformation) + align;
}
uint32_t data_size = value->size();
uint32_t min_size = offsetof(KEY_VALUE_PARTIAL_INFORMATION, Data) + align;
uint32_t result_size = min_size + data_size;
if (ResultLength)
*ResultLength = result_size;
if (Length < min_size)
{
#ifdef CHECKED
XTrace(L"QueryValueKey STATUS_BUFFER_TOO_SMALL\n");
#endif
return STATUS_BUFFER_TOO_SMALL;
}
KEY_VALUE_PARTIAL_INFORMATION *info = static_cast<KEY_VALUE_PARTIAL_INFORMATION *>(KeyValueInformation);
info->TitleIndex = 0;
info->Type = value->type();
info->DataLength = data_size;
if (Length < result_size)
{
#ifdef CHECKED
XTrace(L"QueryValueKey STATUS_BUFFER_OVERFLOW\n");
#endif
return STATUS_BUFFER_OVERFLOW;
}
#ifdef CHECKED
XTrace(L"QueryValueKey %s %d\n", value->data(), data_size);
#endif
memcpy(info->Data, value->data(), data_size);
}
return STATUS_SUCCESS;
default:
#ifdef CHECKED
XTrace(L"QueryValueKey STATUS_INVALID_PARAMETER\n");
#endif
return STATUS_INVALID_PARAMETER;
}
}
NTSTATUS RegistryManager::NtQueryValueKey(HANDLE KeyHandle, PUNICODE_STRING ValueName, KEY_VALUE_INFORMATION_CLASS KeyValueInformationClass, PVOID KeyValueInformation, ULONG Length, PULONG ResultLength)
{
#ifdef CHECKED
UnicodeString s(ValueName->Buffer, ValueName->Length / sizeof(wchar_t));
XTrace(L"->NtQueryValueKey %p %s KeyValueInformationClass=%d Length=%d\n", KeyHandle, s.c_str(), KeyValueInformationClass, Length);
#endif
{
CriticalSection cs(objects_->critical_section());
VirtualObject *object = objects_->GetKey(KeyHandle);
if (object && object->ref()) {
if ((object->access() & KEY_QUERY_VALUE) == 0)
{
#ifdef CHECKED
XTrace(L"NtQueryValueKey STATUS_ACCESS_DENIED\n");
#endif
return STATUS_ACCESS_DENIED;
}
RegistryKey *key = static_cast<RegistryKey *>(object->ref());
RegistryValue *value = key->GetValue(ValueName->Buffer);
if (value == NULL && append_mode_ == false) {
// \Registry\Machine\Software\Classes\... to
UnicodeString hklm = key->full_name();
if (_wcsnicmp(hklm.c_str(), L"\\Registry\\Machine\\Software\\Classes\\", 35 /* SIZE */) == 0) {
// \REGISTRY\USER\<SID>_Classes\...
RegistryKey *ru = cache_.GetKey(L"\\REGISTRY\\USER");
if (ru != NULL && ru->keys_size() == 1) {
ru = ru->key(0)->GetKey(hklm.c_str() + 35 /* SIZE */);
}
if (ru) {
value = ru->GetValue(ValueName->Buffer);
#ifdef CHECKED
XTrace(L"NtQueryValueKey map to hkCU %p\n", value);
#endif
}
}
}
if (value) {
try {
NTSTATUS ret = QueryValueKey(value, KeyValueInformationClass, KeyValueInformation, Length, ResultLength);
#ifdef CHECKED
XTrace(L"NtQueryValueKey ret=%p\n", ret);
#endif
return ret;
} catch(...) {
#ifdef CHECKED
XTrace(L"NtQueryValueKey STATUS_ACCESS_VIOLATION\n");
#endif
return STATUS_ACCESS_VIOLATION;
}
} else if (!key->is_real()) {
#ifdef CHECKED
XTrace(L"NtQueryValueKey STATUS_OBJECT_NAME_NOT_FOUND\n");
#endif
return STATUS_OBJECT_NAME_NOT_FOUND;
}
}
}
NTSTATUS ret = TrueNtQueryValueKey(KeyHandle, ValueName, KeyValueInformationClass, KeyValueInformation, Length, ResultLength);
#ifdef CHECKED
XTrace(L"TrueNtQueryValueKey %p\n", ret);
#endif
return ret;
}
NTSTATUS RegistryManager::NtDeleteKey(HANDLE KeyHandle)
{
#ifdef CHECKED
XTrace(L"->NtDeleteKey %p\n", KeyHandle);
#endif
{
CriticalSection cs(objects_->critical_section());
VirtualObject *object = objects_->GetKey(KeyHandle);
if (object && object->ref()) {
RegistryKey *key = static_cast<RegistryKey *>(object->ref());
objects_->DeleteRef(key);
if (key->owner()->DeleteKey(key))
return STATUS_SUCCESS;
}
}
if (append_mode_) {
// FIXME
#ifdef CHECKED
XTrace(L"TrueNtDeleteKey substed with STATUS_SUCCESS (append mode ON)\n");
#endif
return STATUS_SUCCESS;
}
NTSTATUS ret = TrueNtDeleteKey(KeyHandle);
#ifdef CHECKED
XTrace(L"TrueNtDeleteKey %p\n", ret);
#endif
return ret;
}
NTSTATUS RegistryManager::QueryKey(RegistryKey *key, KEY_INFORMATION_CLASS KeyInformationClass, PVOID KeyInformation, ULONG Length, PULONG ResultLength)
{
if (!key)
return STATUS_INVALID_PARAMETER;
switch (KeyInformationClass) {
case KeyBasicInformation:
{
UnicodeString name = key->full_name();
uint32_t min_size = offsetof(KEY_BASIC_INFORMATION, Name);
uint32_t name_size = static_cast<uint32_t>(name.size() * sizeof(wchar_t));
uint32_t result_size = min_size + name_size;
if (ResultLength)
*ResultLength = result_size;
if (Length < min_size)
return STATUS_BUFFER_TOO_SMALL;
KEY_BASIC_INFORMATION *info = static_cast<KEY_BASIC_INFORMATION *>(KeyInformation);
info->LastWriteTime.QuadPart = key->last_write_time();
info->TitleIndex = 0;
info->NameLength = name_size;
if (Length < result_size)
return STATUS_BUFFER_OVERFLOW;
memcpy(info->Name, name.c_str(), name_size);
}
return STATUS_SUCCESS;
case KeyNodeInformation:
{
UnicodeString name = key->full_name();
uint32_t min_size = offsetof(KEY_NODE_INFORMATION, Name);
uint32_t name_size = static_cast<uint32_t>(name.size() * sizeof(wchar_t));
uint32_t result_size = min_size + name_size;
if (ResultLength)
*ResultLength = result_size;
if (Length < min_size)
return STATUS_BUFFER_TOO_SMALL;
KEY_NODE_INFORMATION *info = static_cast<KEY_NODE_INFORMATION *>(KeyInformation);
info->LastWriteTime.QuadPart = key->last_write_time();
info->TitleIndex = 0;
info->ClassOffset = -1;
info->ClassLength = 0;
info->NameLength = name_size;
if (Length < result_size)
return STATUS_BUFFER_OVERFLOW;
memcpy(info->Name, name.c_str(), name_size);
}
return STATUS_SUCCESS;
case KeyFullInformation:
{
uint32_t min_size = offsetof(KEY_FULL_INFORMATION, Class);
uint32_t result_size = min_size;
if (ResultLength)
*ResultLength = result_size;
if (Length < min_size)
return STATUS_BUFFER_TOO_SMALL;
KEY_FULL_INFORMATION *info = static_cast<KEY_FULL_INFORMATION *>(KeyInformation);
info->LastWriteTime.QuadPart = key->last_write_time();
info->TitleIndex = 0;
info->ClassOffset = -1;
info->ClassLength = 0;
info->SubKeys = static_cast<uint32_t>(key->keys_size());
info->MaxNameLen = 0;
for (size_t i = 0; i < key->keys_size(); i++) {
uint32_t name_size = static_cast<uint32_t>(wcslen(key->key(i)->name()) * sizeof(wchar_t));
if (info->MaxNameLen < name_size)
info->MaxNameLen = name_size;
}
info->MaxClassLen = 0;
info->Values = static_cast<uint32_t>(key->values_size());
info->MaxValueNameLen = 0;
info->MaxValueDataLen = 0;
for (size_t i = 0; i < key->values_size(); i++) {
uint32_t name_size = static_cast<uint32_t>(wcslen(key->value(i)->name()) * sizeof(wchar_t));
uint32_t data_size = key->value(i)->size();
if (info->MaxValueNameLen < name_size)
info->MaxValueNameLen = name_size;
if (info->MaxValueDataLen < data_size)
info->MaxValueDataLen = data_size;
}
if (Length < result_size)
return STATUS_BUFFER_OVERFLOW;
}
return STATUS_SUCCESS;
case KeyNameInformation:
{
UnicodeString name = key->full_name();
uint32_t min_size = offsetof(KEY_NAME_INFORMATION, Name);
uint32_t name_size = static_cast<uint32_t>(name.size() * sizeof(wchar_t));
uint32_t result_size = min_size + name_size;
if (ResultLength)
*ResultLength = result_size;
if (Length < min_size)
return STATUS_BUFFER_TOO_SMALL;
KEY_NAME_INFORMATION *info = static_cast<KEY_NAME_INFORMATION *>(KeyInformation);
info->NameLength = name_size;
if (Length < result_size)
return STATUS_BUFFER_OVERFLOW;
memcpy(info->Name, name.c_str(), name_size);
}
return STATUS_SUCCESS;
case KeyCachedInformation:
{
UnicodeString name = key->full_name();
uint32_t min_size = sizeof(KEY_CACHED_INFORMATION);
uint32_t result_size = min_size;
if (ResultLength)
*ResultLength = result_size;
if (Length < min_size)
return STATUS_BUFFER_TOO_SMALL;
KEY_CACHED_INFORMATION *info = static_cast<KEY_CACHED_INFORMATION *>(KeyInformation);
info->LastWriteTime.QuadPart = key->last_write_time();
info->TitleIndex = 0;
info->SubKeys = static_cast<uint32_t>(key->keys_size());
info->MaxNameLen = 0;
for (size_t i = 0; i < key->keys_size(); i++) {
ULONG name_size = static_cast<ULONG>(wcslen(key->key(i)->name()) * sizeof(wchar_t));
if (info->MaxNameLen < name_size)
info->MaxNameLen = name_size;
}
info->Values = static_cast<ULONG>(key->values_size());
info->MaxValueNameLen = 0;
info->MaxValueDataLen = 0;
for (size_t i = 0; i < key->values_size(); i++) {
uint32_t name_size = static_cast<uint32_t>(wcslen(key->value(i)->name()) * sizeof(wchar_t));
uint32_t data_size = static_cast<uint32_t>(key->value(i)->size());
if (info->MaxValueNameLen < name_size)
info->MaxValueNameLen = name_size;
if (info->MaxValueDataLen < data_size)
info->MaxValueDataLen = data_size;
}
info->NameLength = static_cast<uint32_t>(name.size() * sizeof(wchar_t));
if (Length < result_size)
return STATUS_BUFFER_OVERFLOW;
}
return STATUS_SUCCESS;
case KeyHandleTagsInformation:
{
uint32_t min_size = sizeof(KEY_HANDLE_TAGS_INFORMATION);
uint32_t result_size = min_size;
if (ResultLength)
*ResultLength = result_size;
if (Length < min_size)
return STATUS_BUFFER_TOO_SMALL;
KEY_HANDLE_TAGS_INFORMATION *info = static_cast<KEY_HANDLE_TAGS_INFORMATION *>(KeyInformation);
info->HandleTags = 0;
}
return STATUS_SUCCESS;
case KeyFlagsInformation:
case KeyVirtualizationInformation:
return STATUS_NOT_IMPLEMENTED;
default:
return STATUS_INVALID_PARAMETER;
}
}
static void ApplyDirtyHack(PVOID KeyInformation)
{
KEY_HANDLE_TAGS_INFORMATION *info = static_cast<KEY_HANDLE_TAGS_INFORMATION *>(KeyInformation);
info->HandleTags = 0x601;
#ifdef CHECKED
XTrace(L"ApplyDirtyHack info->HandleTags=%d\n", info->HandleTags);
#endif
}
NTSTATUS RegistryManager::NtQueryKey(HANDLE KeyHandle, KEY_INFORMATION_CLASS KeyInformationClass, PVOID KeyInformation, ULONG Length, PULONG ResultLength)
{
#ifdef CHECKED
XTrace(L"->NtQueryKey %p KeyInformationClass=%d\n", KeyHandle, KeyInformationClass);
#endif
{
CriticalSection cs(objects_->critical_section());
VirtualObject *object = objects_->GetKey(KeyHandle);
if (object && object->ref()) {
if ((object->access() & KEY_QUERY_VALUE) == 0)
{
// Special case (<28><><EFBFBD><EFBFBD> <20><> win2k/private/ntos/config/ntapi.c:NtQueryKey)
if(KeyInformationClass != KeyNameInformation || object->access() == 0) {
#ifdef CHECKED
XTrace(L"NtQueryKey STATUS_ACCESS_DENIED %p\n", object->access());
#endif
return STATUS_ACCESS_DENIED;
}
}
RegistryKey *key = static_cast<RegistryKey *>(object->ref());
try {
NTSTATUS st = QueryKey(key, KeyInformationClass, KeyInformation, Length, ResultLength);
#ifdef CHECKED
XTrace(L"NtQueryKey st=%p, %d=%d\n", st, Length, ResultLength ? *ResultLength : 0);
#endif
if (KeyInformationClass == KeyHandleTagsInformation && st == S_OK && (object->access() & KEY_WOW64_32KEY) && append_mode_ && KeyInformation)
ApplyDirtyHack(KeyInformation);
return st;
} catch(...) {
#ifdef CHECKED
XTrace(L"NtQueryKey STATUS_ACCESS_VIOLATION\n");
#endif
return STATUS_ACCESS_VIOLATION;
}
}
}
NTSTATUS ret = TrueNtQueryKey(KeyHandle, KeyInformationClass, KeyInformation, Length, ResultLength);
if (KeyInformationClass == KeyHandleTagsInformation && ret == S_OK && append_mode_ && KeyInformation)
ApplyDirtyHack(KeyInformation);
#ifdef CHECKED
if(KeyInformationClass == 7)
{
KEY_HANDLE_TAGS_INFORMATION *info = static_cast<KEY_HANDLE_TAGS_INFORMATION *>(KeyInformation);
if (info)
XTrace(L"TrueNtQueryKey %p HandleTags=%p\n", ret, info->HandleTags);
} else if(KeyInformationClass == 3)
{
KEY_NAME_INFORMATION *info = static_cast<KEY_NAME_INFORMATION *>(KeyInformation);
if (info)
XTrace(L"TrueNtQueryKey %p info->Name=%s\n", ret, (UnicodeString(info->Name, info->NameLength)).c_str());
} else
XTrace(L"TrueNtQueryKey %p\n", ret);
#endif
return ret;
}
NTSTATUS RegistryManager::NtEnumerateValueKey(HANDLE KeyHandle, ULONG Index, KEY_VALUE_INFORMATION_CLASS KeyValueInformationClass, PVOID KeyValueInformation, ULONG Length, PULONG ResultLength)
{
#ifdef CHECKED
XTrace(L"->NtEnumerateValueKey %p\n", KeyHandle);
#endif
{
CriticalSection cs(objects_->critical_section());
VirtualObject *object = objects_->GetKey(KeyHandle);
if (object && object->ref()) {
if ((object->access() & KEY_QUERY_VALUE) == 0)
{
#ifdef CHECKED
XTrace(L"NtEnumerateValueKey STATUS_ACCESS_DENIED\n");
#endif
return STATUS_ACCESS_DENIED;
}
RegistryKey *key = static_cast<RegistryKey *>(object->ref());
if (Index >= key->values_size())
#ifdef CHECKED
XTrace(L"NtEnumerateValueKey STATUS_NO_MORE_ENTRIES\n");
#endif
return STATUS_NO_MORE_ENTRIES;
try {
NTSTATUS vret = QueryValueKey(key->value(Index), KeyValueInformationClass, KeyValueInformation, Length, ResultLength);
#ifdef CHECKED
XTrace(L"QueryValueKey %p\n", vret);
#endif
return vret;
} catch(...) {
#ifdef CHECKED
XTrace(L"NtEnumerateValueKey STATUS_ACCESS_VIOLATION\n");
#endif
return STATUS_ACCESS_VIOLATION;
}
}
}
NTSTATUS ret = TrueNtEnumerateValueKey(KeyHandle, Index, KeyValueInformationClass, KeyValueInformation, Length, ResultLength);
#ifdef CHECKED
XTrace(L"TrueNtEnumerateValueKey %p\n", ret);
#endif
return ret;
}
NTSTATUS RegistryManager::NtEnumerateKey(HANDLE KeyHandle, ULONG Index, KEY_INFORMATION_CLASS KeyInformationClass, PVOID KeyInformation, ULONG Length, PULONG ResultLength)
{
#ifdef CHECKED
XTrace(L"->NtEnumerateKey %p\n", KeyHandle);
#endif
{
CriticalSection cs(objects_->critical_section());
VirtualObject *object = objects_->GetKey(KeyHandle);
if (object && object->ref()) {
// a virtual key
if ((object->access() & KEY_ENUMERATE_SUB_KEYS) == 0)
{
#ifdef CHECKED
XTrace(L"NtEnumerateKey STATUS_ACCESS_DENIED\n");
#endif
return STATUS_ACCESS_DENIED;
}
RegistryKey *key = static_cast<RegistryKey *>(object->ref());
if (Index >= key->keys_size())
#ifdef CHECKED
XTrace(L"NtEnumerateKey STATUS_NO_MORE_ENTRIES\n");
#endif
return STATUS_NO_MORE_ENTRIES;
try {
NTSTATUS vret = QueryKey(key->key(Index), KeyInformationClass, KeyInformation, Length, ResultLength);
#ifdef CHECKED
XTrace(L"QueryKey %p\n", vret);
#endif
return vret;
} catch(...) {
#ifdef CHECKED
XTrace(L"NtEnumerateKey STATUS_ACCESS_VIOLATION\n");
#endif
return STATUS_ACCESS_VIOLATION;
}
} else {
// a real key
#ifdef CHECKED
XTrace(L"NtEnumerateKey real\n");
#endif
uint32_t access;
if (object)
access = object->access();
else {
PUBLIC_OBJECT_BASIC_INFORMATION info;
access = (NT_SUCCESS(TrueNtQueryObject(KeyHandle, ObjectBasicInformation, &info, sizeof(info), NULL))) ? info.GrantedAccess : 0;
#ifdef CHECKED
XTrace(L"TrueNtQueryObject.Access = %p\n", access);
#endif
}
if (access & KEY_ENUMERATE_SUB_KEYS) {
RegistryKey *key = NULL;
{
KEY_NAME_INFORMATION info;
DWORD size;
if (TrueNtQueryKey(KeyHandle, KeyNameInformation, &info, sizeof(info), &size) == STATUS_BUFFER_OVERFLOW) {
KEY_NAME_INFORMATION *data = reinterpret_cast<KEY_NAME_INFORMATION *>(new uint8_t[size]);
if (NT_SUCCESS(TrueNtQueryKey(KeyHandle, KeyNameInformation, data, size, &size)))
{
key = cache_.GetKey(UnicodeString(data->Name, data->NameLength / sizeof(wchar_t)).c_str());
#ifdef CHECKED
XTrace(L"Cache used\n");
#endif
}
#ifdef CHECKED
else
{
XTrace(L"Cache NOT used\n");
}
#endif
delete [] data;
}
}
if (key) {
vector<RegistryKey *> children;
for (size_t i = 0; i < key->keys_size(); i++) {
RegistryKey *child = key->key(i);
if (!child->is_real())
children.push_back(child);
}
if (Index < children.size()) {
try {
NTSTATUS vret = QueryKey(children[Index], KeyInformationClass, KeyInformation, Length, ResultLength);
#ifdef CHECKED
XTrace(L"QueryKey %p\n", vret);
#endif
return vret;
} catch(...) {
#ifdef CHECKED
XTrace(L"NtEnumerateKey STATUS_ACCESS_VIOLATION\n");
#endif
return STATUS_ACCESS_VIOLATION;
}
} else {
Index -= static_cast<uint32_t>(children.size());
}
}
}
}
}
NTSTATUS ret = TrueNtEnumerateKey(KeyHandle, Index, KeyInformationClass, KeyInformation, Length, ResultLength);
#ifdef CHECKED
XTrace(L"TrueNtEnumerateKey %p\n", ret);
#endif
return ret;
}
#endif // WIN_DRIVER