Skip to content

Commit 4f1d0b2

Browse files
committed
feat: implement registry hive
Signed-off-by: Theo Paris <theo@theoparis.com>
1 parent 3c0a64e commit 4f1d0b2

File tree

10 files changed

+539
-45
lines changed

10 files changed

+539
-45
lines changed

kernel/BUCK

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ rust_binary(
1010
deps = [
1111
"//lib/crabfs:crabfs",
1212
"//lib/loader:loader",
13+
"//lib/regf:regf",
1314
"third-party//:bitflags",
1415
"third-party//:goblin",
1516
"third-party//:limine",

kernel/nt.rs

Lines changed: 126 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@ use crate::vfs;
44
use alloc::collections::BTreeMap;
55
use alloc::format;
66
use alloc::string::{String, ToString};
7+
use alloc::vec::Vec;
8+
use regf::Hive;
79
use spin::{Lazy, Mutex};
810

911
pub type NtStatus = i32;
@@ -370,6 +372,7 @@ pub enum ObjectType {
370372
Directory,
371373
SymbolicLink,
372374
File,
375+
Key,
373376
Section,
374377
Process,
375378
Thread,
@@ -382,6 +385,11 @@ pub struct FileObject {
382385
pub vfs_handle: i32,
383386
}
384387

388+
#[derive(Debug, Clone)]
389+
pub struct KeyObject {
390+
pub path: String,
391+
}
392+
385393
#[derive(Debug, Clone, Copy)]
386394
pub struct EventObject {
387395
pub signaled: bool,
@@ -417,6 +425,7 @@ pub enum ObjectData {
417425
Directory,
418426
SymbolicLink { target: String },
419427
File(FileObject),
428+
Key(KeyObject),
420429
Section(SectionObject),
421430
Process(ProcessObject),
422431
Thread(ThreadObject),
@@ -445,6 +454,14 @@ static OBJECTS: Lazy<Mutex<ObjectManager>> = Lazy::new(|| {
445454
})
446455
});
447456

457+
#[derive(Default)]
458+
struct RegistryState {
459+
loaded: bool,
460+
system_hive: Option<Hive>,
461+
}
462+
463+
static REGISTRY: Lazy<Mutex<RegistryState>> = Lazy::new(|| Mutex::new(RegistryState::default()));
464+
448465
pub fn init_namespace() {
449466
let mut objects = OBJECTS.lock();
450467
if !objects.named.is_empty() {
@@ -454,6 +471,8 @@ pub fn init_namespace() {
454471
let _ = create_named(&mut objects, "\\", ObjectData::Directory);
455472
let _ = create_named(&mut objects, "\\Device", ObjectData::Directory);
456473
let _ = create_named(&mut objects, "\\??", ObjectData::Directory);
474+
let _ = create_named(&mut objects, "\\Registry", ObjectData::Directory);
475+
let _ = create_named(&mut objects, "\\Registry\\Machine", ObjectData::Directory);
457476
let _ = create_named(&mut objects, "\\KnownDlls", ObjectData::Directory);
458477
let _ = create_named(&mut objects, "\\Device\\Crabfs0", ObjectData::Directory);
459478
let _ = create_named(
@@ -517,13 +536,120 @@ fn object_type_of(data: &ObjectData) -> ObjectType {
517536
ObjectData::Directory => ObjectType::Directory,
518537
ObjectData::SymbolicLink { .. } => ObjectType::SymbolicLink,
519538
ObjectData::File(_) => ObjectType::File,
539+
ObjectData::Key(_) => ObjectType::Key,
520540
ObjectData::Section(_) => ObjectType::Section,
521541
ObjectData::Process(_) => ObjectType::Process,
522542
ObjectData::Thread(_) => ObjectType::Thread,
523543
ObjectData::Event(_) => ObjectType::Event,
524544
}
525545
}
526546

547+
fn read_file_all(path: &str) -> Result<Vec<u8>, NtStatus> {
548+
let fd = vfs::open(path, 0).map_err(|_| STATUS_OBJECT_NAME_NOT_FOUND)?;
549+
let mut data = Vec::new();
550+
let mut buf = [0u8; 4096];
551+
loop {
552+
let n = vfs::read(fd, &mut buf).map_err(|_| STATUS_UNSUCCESSFUL)?;
553+
if n == 0 {
554+
break;
555+
}
556+
data.extend_from_slice(&buf[..n]);
557+
}
558+
let _ = vfs::close(fd);
559+
Ok(data)
560+
}
561+
562+
fn ensure_system_hive_loaded() -> Result<(), NtStatus> {
563+
let mut reg = REGISTRY.lock();
564+
if reg.loaded {
565+
return Ok(());
566+
}
567+
let data = read_file_all("/Windows/System32/config/SYSTEM")?;
568+
let hive = Hive::parse(&data).map_err(|_| STATUS_INVALID_IMAGE_FORMAT)?;
569+
reg.system_hive = Some(hive);
570+
reg.loaded = true;
571+
Ok(())
572+
}
573+
574+
fn system_hive_subkey(path: &str) -> Option<String> {
575+
let canonical = canonicalize_nt_path(path);
576+
let lower = canonical.to_ascii_lowercase();
577+
let prefix = "\\registry\\machine\\system";
578+
if lower == prefix {
579+
return Some(String::new());
580+
}
581+
if !lower.starts_with(prefix) {
582+
return None;
583+
}
584+
let mut rest = &canonical[prefix.len()..];
585+
while rest.starts_with('\\') {
586+
rest = &rest[1..];
587+
}
588+
Some(rest.to_string())
589+
}
590+
591+
pub fn open_key(path: &str) -> Result<u32, NtStatus> {
592+
init_namespace();
593+
ensure_system_hive_loaded()?;
594+
let Some(subkey) = system_hive_subkey(path) else {
595+
return Err(STATUS_OBJECT_NAME_NOT_FOUND);
596+
};
597+
let reg = REGISTRY.lock();
598+
let Some(hive) = reg.system_hive.as_ref() else {
599+
return Err(STATUS_OBJECT_NAME_NOT_FOUND);
600+
};
601+
if !hive.has_key(&subkey) {
602+
return Err(STATUS_OBJECT_NAME_NOT_FOUND);
603+
}
604+
drop(reg);
605+
let mut objects = OBJECTS.lock();
606+
Ok(insert_unnamed(
607+
&mut objects,
608+
ObjectData::Key(KeyObject {
609+
path: canonicalize_nt_path(path),
610+
}),
611+
))
612+
}
613+
614+
pub fn query_key_value(object_id: u32, value_name: &str) -> Result<(u32, Vec<u8>), NtStatus> {
615+
ensure_system_hive_loaded()?;
616+
let key_path = {
617+
let objects = OBJECTS.lock();
618+
let Some(record) = objects.objects.get(&object_id) else {
619+
return Err(STATUS_INVALID_HANDLE);
620+
};
621+
match &record.data {
622+
ObjectData::Key(key) => key.path.clone(),
623+
_ => return Err(STATUS_OBJECT_TYPE_MISMATCH),
624+
}
625+
};
626+
let Some(subkey) = system_hive_subkey(&key_path) else {
627+
return Err(STATUS_OBJECT_NAME_NOT_FOUND);
628+
};
629+
let reg = REGISTRY.lock();
630+
let Some(hive) = reg.system_hive.as_ref() else {
631+
return Err(STATUS_OBJECT_NAME_NOT_FOUND);
632+
};
633+
let Some(value) = hive.query_value(&subkey, value_name) else {
634+
return Err(STATUS_OBJECT_NAME_NOT_FOUND);
635+
};
636+
Ok((value.ty, value.data.clone()))
637+
}
638+
639+
pub fn object_name(object_id: u32) -> Result<String, NtStatus> {
640+
let objects = OBJECTS.lock();
641+
let Some(record) = objects.objects.get(&object_id) else {
642+
return Err(STATUS_INVALID_HANDLE);
643+
};
644+
if let Some(name) = &record.name {
645+
return Ok(name.clone());
646+
}
647+
match &record.data {
648+
ObjectData::Key(key) => Ok(key.path.clone()),
649+
_ => Err(STATUS_OBJECT_NAME_NOT_FOUND),
650+
}
651+
}
652+
527653
pub fn create_file(path: String, vfs_handle: i32) -> u32 {
528654
let mut objects = OBJECTS.lock();
529655
insert_unnamed(

kernel/syscall.rs

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -206,6 +206,19 @@ extern "sysv64" fn syscall_dispatch(frame: *mut SyscallFrame) -> usize {
206206
frame.r9 as u32,
207207
stack_arg(frame, 0) as u32,
208208
) as usize,
209+
18 => user::open_key(
210+
frame.r10 as *mut usize,
211+
frame.rdx as u32,
212+
frame.r8 as *const nt::ObjectAttributes,
213+
) as usize,
214+
23 => user::query_value_key(
215+
frame.r10,
216+
frame.rdx as *const nt::UnicodeString,
217+
frame.r8 as u32,
218+
frame.r9 as *mut u8,
219+
stack_arg(frame, 0) as u32,
220+
stack_arg(frame, 1) as *mut u32,
221+
) as usize,
209222
24 if frame.rdx != 0 && frame.r9 != 0 => user::allocate_virtual_memory(
210223
frame.r10,
211224
frame.rdx as *mut usize,

kernel/user.rs

Lines changed: 105 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -995,7 +995,17 @@ fn path_from_object_attributes(attributes: *const ObjectAttributes) -> Result<St
995995
if attrs.object_name.is_null() {
996996
return Err(STATUS_INVALID_PARAMETER);
997997
}
998-
read_utf16_string(attrs.object_name)
998+
let path = read_utf16_string(attrs.object_name)?;
999+
if attrs.root_directory == 0 || path.starts_with('\\') {
1000+
return Ok(path);
1001+
}
1002+
let root_entry = resolve_handle_entry(attrs.root_directory)?;
1003+
let root_path = nt::object_name(root_entry.object_id)?;
1004+
if root_path.ends_with('\\') {
1005+
Ok(format!("{root_path}{path}"))
1006+
} else {
1007+
Ok(format!("{root_path}\\{path}"))
1008+
}
9991009
}
10001010

10011011
fn nt_path_from_vfs_path(path: &str) -> Option<String> {
@@ -1052,6 +1062,100 @@ pub fn create_file(
10521062
STATUS_SUCCESS
10531063
}
10541064

1065+
pub fn open_key(
1066+
out_handle: *mut Handle,
1067+
desired_access: AccessMask,
1068+
object_attributes: *const ObjectAttributes,
1069+
) -> NtStatus {
1070+
if out_handle.is_null() {
1071+
return STATUS_INVALID_PARAMETER;
1072+
}
1073+
let nt_path = match path_from_object_attributes(object_attributes) {
1074+
Ok(path) => nt::canonicalize_nt_path(&path),
1075+
Err(status) => return status,
1076+
};
1077+
let object_id = match nt::open_key(&nt_path) {
1078+
Ok(id) => id,
1079+
Err(status) => return status,
1080+
};
1081+
let access = if desired_access == 0 {
1082+
nt::PROCESS_ALL_ACCESS
1083+
} else {
1084+
desired_access
1085+
};
1086+
let handle = match install_handle(object_id, access) {
1087+
Ok(handle) => handle,
1088+
Err(status) => {
1089+
let _ = nt::release(object_id);
1090+
return status;
1091+
}
1092+
};
1093+
let _ = nt::release(object_id);
1094+
unsafe { *out_handle = handle };
1095+
STATUS_SUCCESS
1096+
}
1097+
1098+
pub fn query_value_key(
1099+
handle: Handle,
1100+
value_name: *const UnicodeString,
1101+
key_value_information_class: u32,
1102+
key_value_information: *mut u8,
1103+
length: u32,
1104+
result_length: *mut u32,
1105+
) -> NtStatus {
1106+
const KEY_VALUE_BASIC_INFORMATION_CLASS: u32 = 0;
1107+
const KEY_VALUE_FULL_INFORMATION_CLASS: u32 = 1;
1108+
const KEY_VALUE_PARTIAL_INFORMATION_CLASS: u32 = 2;
1109+
1110+
#[repr(C)]
1111+
struct KeyValuePartialInformationHeader {
1112+
title_index: u32,
1113+
type_: u32,
1114+
data_length: u32,
1115+
}
1116+
1117+
let _ = KEY_VALUE_BASIC_INFORMATION_CLASS;
1118+
let _ = KEY_VALUE_FULL_INFORMATION_CLASS;
1119+
if key_value_information_class != KEY_VALUE_PARTIAL_INFORMATION_CLASS {
1120+
return STATUS_NOT_SUPPORTED;
1121+
}
1122+
if value_name.is_null() {
1123+
return STATUS_INVALID_PARAMETER;
1124+
}
1125+
let value_name = match read_utf16_string(value_name) {
1126+
Ok(name) => name,
1127+
Err(status) => return status,
1128+
};
1129+
let entry = match resolve_handle_entry(handle) {
1130+
Ok(entry) => entry,
1131+
Err(status) => return status,
1132+
};
1133+
let (value_type, data) = match nt::query_key_value(entry.object_id, &value_name) {
1134+
Ok(value) => value,
1135+
Err(status) => return status,
1136+
};
1137+
let header_len = core::mem::size_of::<KeyValuePartialInformationHeader>();
1138+
let needed = header_len + data.len();
1139+
if !result_length.is_null() {
1140+
unsafe { *result_length = needed as u32 };
1141+
}
1142+
if key_value_information.is_null() || (length as usize) < needed {
1143+
return STATUS_BUFFER_TOO_SMALL;
1144+
}
1145+
unsafe {
1146+
let header = key_value_information as *mut KeyValuePartialInformationHeader;
1147+
(*header).title_index = 0;
1148+
(*header).type_ = value_type;
1149+
(*header).data_length = data.len() as u32;
1150+
core::ptr::copy_nonoverlapping(
1151+
data.as_ptr(),
1152+
key_value_information.add(header_len),
1153+
data.len(),
1154+
);
1155+
}
1156+
STATUS_SUCCESS
1157+
}
1158+
10551159
pub fn read_file(
10561160
handle: Handle,
10571161
io_status: *mut IoStatusBlock,

lib/crabfs/Cargo.toml

Lines changed: 0 additions & 19 deletions
This file was deleted.

lib/regf/BUCK

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
rust_library(
2+
name = "regf",
3+
srcs = glob(["**/*.rs"]),
4+
crate_root = "lib.rs",
5+
crate = "regf",
6+
edition = "2024",
7+
visibility = ["PUBLIC"],
8+
)

0 commit comments

Comments
 (0)