mirror of
https://github.com/mirkobrombin/raven.git
synced 2026-11-05 21:56:33 +00:00
328 lines
12 KiB
C++
328 lines
12 KiB
C++
#include "kernel_context.h"
|
|
#include "nls.h"
|
|
#include "registry.h"
|
|
#include "text.h"
|
|
|
|
#include <algorithm>
|
|
#include <cstring>
|
|
|
|
namespace raven {
|
|
|
|
namespace {
|
|
|
|
constexpr uint32_t error_more_data = 234, error_no_more_items = 259, error_invalid_parameter = 87, error_file_not_found = 2;
|
|
constexpr uint32_t type_sz = 1, type_expand_sz = 2, type_multi_sz = 7, type_dword = 4, type_qword = 11, type_binary = 3;
|
|
|
|
bool is_text(uint32_t type)
|
|
{
|
|
return type == type_sz || type == type_expand_sz || type == type_multi_sz;
|
|
}
|
|
|
|
// Registry text is stored as UTF-16; ANSI callers receive the process code page, always terminated.
|
|
std::vector<uint8_t> to_ansi_bytes(const std::vector<uint8_t> &wide, uint32_t code_page)
|
|
{
|
|
std::u16string text;
|
|
for (size_t index = 0; index + 1 < wide.size(); index += 2)
|
|
text.push_back(char16_t(wide[index] | wide[index + 1] << 8));
|
|
auto ansi = encode_ansi(text, code_page == 1252 ? 1252 : 1252);
|
|
if (ansi.empty() || ansi.back() != '\0')
|
|
ansi.push_back('\0');
|
|
return std::vector<uint8_t>(ansi.begin(), ansi.end());
|
|
}
|
|
|
|
std::vector<uint8_t> from_ansi_bytes(std::span<const uint8_t> ansi, uint32_t code_page)
|
|
{
|
|
const auto text = from_ansi(std::string_view(reinterpret_cast<const char *>(ansi.data()), ansi.size()), code_page);
|
|
std::vector<uint8_t> bytes(text.size() * 2);
|
|
std::memcpy(bytes.data(), text.data(), bytes.size());
|
|
return bytes;
|
|
}
|
|
|
|
}
|
|
|
|
void register_registry_api(ApiRegistry ®istry, KernelContext &context, NativeRegistry &hive)
|
|
{
|
|
const uint32_t ansi_page = context.launch.ansi_code_page;
|
|
const size_t handle_width = context.process.mode() == NativeMode::x86 ? 4 : 8;
|
|
const auto text_of = [](ApiCall &call, uint64_t address, bool wide) {
|
|
if (!address)
|
|
return std::u16string();
|
|
return wide ? call.wide(address, 16384) : from_ansi(call.narrow(address, 16384), 1252);
|
|
};
|
|
const auto put_handle = [handle_width](ApiCall &call, uint64_t address, uint64_t value) {
|
|
std::vector<uint8_t> bytes(handle_width);
|
|
std::memcpy(bytes.data(), &value, handle_width);
|
|
call.write(address, bytes);
|
|
};
|
|
// A 32-bit program sees HKEY_LOCAL_MACHINE\SOFTWARE as the Wow6432Node subkey of it, except for the keys both views share.
|
|
const bool wow64 = context.process.mode() == NativeMode::x86;
|
|
const auto view = [wow64](uint64_t parent, std::u16string path) {
|
|
if (!wow64 || parent != 0x80000002ULL)
|
|
return path;
|
|
const auto fold = [](std::u16string text) {
|
|
for (auto &character : text)
|
|
if (character >= u'a' && character <= u'z')
|
|
character = char16_t(character - (u'a' - u'A'));
|
|
return text;
|
|
};
|
|
while (!path.empty() && path.front() == u'\\')
|
|
path.erase(0, 1);
|
|
const auto first = path.find(u'\\');
|
|
if (fold(path.substr(0, first)) != u"SOFTWARE")
|
|
return path;
|
|
if (first == std::u16string::npos)
|
|
return path;
|
|
const auto second_end = path.find(u'\\', first + 1);
|
|
const auto second = fold(path.substr(first + 1, second_end == std::u16string::npos ? std::u16string::npos : second_end - first - 1));
|
|
if (second == u"CLASSES" || second == u"WOW6432NODE")
|
|
return path;
|
|
return path.substr(0, first + 1) + u"Wow6432Node\\" + path.substr(first + 1);
|
|
};
|
|
for (const bool wide : {true, false}) {
|
|
const std::string suffix = wide ? "W" : "A";
|
|
registry.add("RegOpenKeyEx" + suffix, 5, [&hive, text_of, put_handle, wide, view](ApiCall &call) {
|
|
if (!call[4]) {
|
|
call.ret(error_invalid_parameter);
|
|
return;
|
|
}
|
|
uint64_t key = 0;
|
|
const auto status = hive.open(call[0], view(call[0], text_of(call, call[1], wide)), key);
|
|
if (!status)
|
|
put_handle(call, call[4], key);
|
|
call.ret(status);
|
|
});
|
|
registry.add("RegOpenKey" + suffix, 3, [&hive, text_of, put_handle, wide, view](ApiCall &call) {
|
|
if (!call[2]) {
|
|
call.ret(error_invalid_parameter);
|
|
return;
|
|
}
|
|
uint64_t key = 0;
|
|
const auto status = hive.open(call[0], view(call[0], text_of(call, call[1], wide)), key);
|
|
if (!status)
|
|
put_handle(call, call[2], key);
|
|
call.ret(status);
|
|
});
|
|
registry.add("RegCreateKeyEx" + suffix, 9, [&hive, text_of, put_handle, wide, view](ApiCall &call) {
|
|
if (!call[7]) {
|
|
call.ret(error_invalid_parameter);
|
|
return;
|
|
}
|
|
uint64_t key = 0;
|
|
uint32_t disposition = 0;
|
|
const auto status = hive.create(call[0], view(call[0], text_of(call, call[1], wide)), key, disposition);
|
|
if (!status) {
|
|
put_handle(call, call[7], key);
|
|
if (call[8]) call.store<uint32_t>(call[8], disposition);
|
|
}
|
|
call.ret(status);
|
|
});
|
|
registry.add("RegSetValueEx" + suffix, 6, [&hive, text_of, wide, ansi_page](ApiCall &call) {
|
|
const auto name = text_of(call, call[1], wide);
|
|
const uint32_t type = call.u32(3), size = call.u32(5);
|
|
if (!call[4] && size) {
|
|
call.ret(error_invalid_parameter);
|
|
return;
|
|
}
|
|
auto data = size ? call.read(call[4], size) : std::vector<uint8_t>{};
|
|
if (!wide && is_text(type))
|
|
data = from_ansi_bytes(data, ansi_page);
|
|
call.ret(hive.set(call[0], name, type, data));
|
|
});
|
|
registry.add("RegQueryValueEx" + suffix, 6, [&hive, text_of, wide, ansi_page](ApiCall &call) {
|
|
const auto name = text_of(call, call[1], wide);
|
|
if (call[4] && !call[5]) {
|
|
call.ret(error_invalid_parameter);
|
|
return;
|
|
}
|
|
uint32_t type = 0;
|
|
std::vector<uint8_t> data;
|
|
const auto status = hive.query(call[0], name, type, data);
|
|
if (status) {
|
|
call.ret(status);
|
|
return;
|
|
}
|
|
if (!wide && is_text(type))
|
|
data = to_ansi_bytes(data, ansi_page);
|
|
if (call[3]) call.store<uint32_t>(call[3], type);
|
|
if (!call[4]) {
|
|
if (call[5]) call.store<uint32_t>(call[5], uint32_t(data.size()));
|
|
call.ret(0);
|
|
return;
|
|
}
|
|
const uint32_t capacity = call.load<uint32_t>(call[5]);
|
|
call.store<uint32_t>(call[5], uint32_t(data.size()));
|
|
if (data.size() > capacity) {
|
|
call.ret(error_more_data);
|
|
return;
|
|
}
|
|
call.write(call[4], data);
|
|
call.ret(0);
|
|
});
|
|
registry.add("RegDeleteValue" + suffix, 2, [&hive, text_of, wide](ApiCall &call) { call.ret(hive.remove_value(call[0], text_of(call, call[1], wide))); });
|
|
registry.add("RegDeleteKey" + suffix, 2, [&hive, text_of, wide, view](ApiCall &call) { call.ret(hive.remove_key(call[0], view(call[0], text_of(call, call[1], wide)))); });
|
|
// The older form: the buffer size is a plain count of characters, not a pointer to one.
|
|
registry.add("RegEnumKey" + suffix, 4, [&hive, wide](ApiCall &call) {
|
|
std::u16string name;
|
|
const auto status = hive.enum_key(call[0], call.u32(1), name);
|
|
if (status) {
|
|
call.ret(status);
|
|
return;
|
|
}
|
|
const uint32_t capacity = call.u32(3);
|
|
const auto narrow = wide ? std::string() : encode_ansi(name, 1252);
|
|
const size_t length = wide ? name.size() : narrow.size();
|
|
if (!call[2] || capacity <= length) {
|
|
call.ret(call[2] ? error_more_data : error_invalid_parameter);
|
|
return;
|
|
}
|
|
if (wide) call.put_wide(call[2], name, capacity);
|
|
else call.put_narrow(call[2], narrow, capacity);
|
|
call.ret(0);
|
|
});
|
|
registry.add("RegEnumKeyEx" + suffix, 8, [&hive, wide](ApiCall &call) {
|
|
std::u16string name;
|
|
const auto status = hive.enum_key(call[0], call.u32(1), name);
|
|
if (status) {
|
|
call.ret(status);
|
|
return;
|
|
}
|
|
const uint32_t capacity = call.load<uint32_t>(call[3]);
|
|
const auto narrow = wide ? std::string() : encode_ansi(name, 1252);
|
|
const size_t length = wide ? name.size() : narrow.size();
|
|
if (capacity <= length) {
|
|
call.ret(error_more_data);
|
|
return;
|
|
}
|
|
if (wide) call.put_wide(call[2], name, capacity);
|
|
else call.put_narrow(call[2], narrow, capacity);
|
|
call.store<uint32_t>(call[3], uint32_t(length));
|
|
if (call[5] && call[6]) {
|
|
if (call.load<uint32_t>(call[6]) > 0)
|
|
call.store<uint16_t>(call[5], 0);
|
|
call.store<uint32_t>(call[6], 0);
|
|
}
|
|
if (call[7]) call.store<uint64_t>(call[7], 0);
|
|
call.ret(0);
|
|
});
|
|
registry.add("RegEnumValue" + suffix, 8, [&hive, wide, ansi_page](ApiCall &call) {
|
|
std::u16string name;
|
|
uint32_t type = 0;
|
|
std::vector<uint8_t> data;
|
|
const auto status = hive.enum_value(call[0], call.u32(1), name, type, data);
|
|
if (status) {
|
|
call.ret(status);
|
|
return;
|
|
}
|
|
if (!wide && is_text(type))
|
|
data = to_ansi_bytes(data, ansi_page);
|
|
const uint32_t name_capacity = call.load<uint32_t>(call[3]);
|
|
const auto narrow = wide ? std::string() : encode_ansi(name, 1252);
|
|
const size_t length = wide ? name.size() : narrow.size();
|
|
if (name_capacity <= length) {
|
|
call.ret(error_more_data);
|
|
return;
|
|
}
|
|
if (call[4]) call.ret(error_invalid_parameter);
|
|
if (call[5]) call.store<uint32_t>(call[5], type);
|
|
if (call[6]) {
|
|
if (!call[7]) {
|
|
call.ret(error_invalid_parameter);
|
|
return;
|
|
}
|
|
const uint32_t capacity = call.load<uint32_t>(call[7]);
|
|
call.store<uint32_t>(call[7], uint32_t(data.size()));
|
|
if (data.size() > capacity) {
|
|
call.ret(error_more_data);
|
|
return;
|
|
}
|
|
call.write(call[6], data);
|
|
} else if (call[7]) {
|
|
call.store<uint32_t>(call[7], uint32_t(data.size()));
|
|
}
|
|
if (wide) call.put_wide(call[2], name, name_capacity);
|
|
else call.put_narrow(call[2], narrow, name_capacity);
|
|
call.store<uint32_t>(call[3], uint32_t(length));
|
|
call.ret(0);
|
|
});
|
|
}
|
|
registry.add("RegDeleteKeyExW", 4, [&hive, text_of, view](ApiCall &call) { call.ret(hive.remove_key(call[0], view(call[0], text_of(call, call[1], true)))); });
|
|
registry.add("RegCloseKey", 1, [&hive](ApiCall &call) { call.ret(hive.close(call[0])); });
|
|
registry.add("RegFlushKey", 1, [](ApiCall &call) { call.ret(0); });
|
|
registry.add("RegNotifyChangeKeyValue", 5, [](ApiCall &call) { call.ret(0); });
|
|
registry.add("RegOpenCurrentUser", 2, [put_handle](ApiCall &call) {
|
|
put_handle(call, call[1], 0x80000001ULL);
|
|
call.ret(0);
|
|
});
|
|
registry.add("RegQueryInfoKeyW", 12, [&hive](ApiCall &call) {
|
|
NativeRegistry::Info info;
|
|
const auto status = hive.info(call[0], info);
|
|
if (status) {
|
|
call.ret(status);
|
|
return;
|
|
}
|
|
if (call[2]) call.store<uint32_t>(call[2], 0);
|
|
if (call[4]) call.store<uint32_t>(call[4], info.subkeys);
|
|
if (call[5]) call.store<uint32_t>(call[5], info.longest_subkey);
|
|
if (call[6]) call.store<uint32_t>(call[6], 0);
|
|
if (call[7]) call.store<uint32_t>(call[7], info.values);
|
|
if (call[8]) call.store<uint32_t>(call[8], info.longest_name);
|
|
if (call[9]) call.store<uint32_t>(call[9], info.longest_data);
|
|
if (call[10]) call.store<uint32_t>(call[10], 0);
|
|
if (call[11]) call.store<uint64_t>(call[11], 0);
|
|
call.ret(0);
|
|
});
|
|
// RegGetValue filters by type (RRF_RT_*), opens the subkey itself and expands REG_EXPAND_SZ unless told not to.
|
|
registry.add("RegGetValueW", 7, [&hive, text_of](ApiCall &call) {
|
|
const uint32_t flags = call.u32(3);
|
|
uint64_t key = call[0];
|
|
uint64_t opened = 0;
|
|
const auto subkey = text_of(call, call[1], true);
|
|
if (!subkey.empty()) {
|
|
const auto status = hive.open(key, subkey, opened);
|
|
if (status) {
|
|
call.ret(status);
|
|
return;
|
|
}
|
|
key = opened;
|
|
}
|
|
uint32_t type = 0;
|
|
std::vector<uint8_t> data;
|
|
auto status = hive.query(key, text_of(call, call[2], true), type, data);
|
|
if (opened)
|
|
hive.close(opened);
|
|
if (status) {
|
|
call.ret(status);
|
|
return;
|
|
}
|
|
const auto allowed = [&](uint32_t value) {
|
|
switch (value) {
|
|
case type_sz: return flags & 0x2; case type_expand_sz: return flags & 0x4; case type_multi_sz: return flags & 0x20;
|
|
case type_dword: return flags & 0x10; case type_qword: return flags & 0x40; case type_binary: return flags & 0x8;
|
|
default: return 0U;
|
|
}
|
|
};
|
|
if ((flags & 0xffff) && !allowed(type) && !(flags & 1)) {
|
|
call.ret(1629);
|
|
return;
|
|
}
|
|
if (call[4]) call.store<uint32_t>(call[4], type);
|
|
if (!call[5]) {
|
|
if (call[6]) call.store<uint32_t>(call[6], uint32_t(data.size()));
|
|
call.ret(0);
|
|
return;
|
|
}
|
|
const uint32_t capacity = call.load<uint32_t>(call[6]);
|
|
call.store<uint32_t>(call[6], uint32_t(data.size()));
|
|
if (data.size() > capacity) {
|
|
call.ret(error_more_data);
|
|
return;
|
|
}
|
|
call.write(call[5], data);
|
|
call.ret(0);
|
|
});
|
|
(void)error_no_more_items;
|
|
(void)error_file_not_found;
|
|
}
|
|
|
|
}
|