diff --git a/Makefile b/Makefile index 3bc1bce..c6d4a06 100644 --- a/Makefile +++ b/Makefile @@ -125,7 +125,7 @@ CORE_SRCS = kernel/main.c kernel/task.c kernel/sync.c kernel/timer.c kernel/prin kernel/panic.c kernel/trace.c kernel/sched.c kernel/cron.c kernel/syscall.c \ kernel/syslog.c kernel/module.c kernel/kobject.c kernel/metrics.c kernel/namespace.c \ kernel/signal.c kernel/workqueue.c kernel/tsc.c kernel/vt_art.c \ - kernel/sub/sub_runtime.c kernel/sub/sub_vm.c kernel/nt/ob.c kernel/nt/reg.c \ + kernel/sub/sub_runtime.c kernel/sub/sub_vm.c kernel/nt/ob.c kernel/nt/reg.c kernel/nt/io.c \ kernel/wait.c kernel/futex.c kernel/rcu.c kernel/ktest.c \ kernel/elf.c kernel/exec.c \ lib/string.c lib/vsprintf.c lib/bitmap.c lib/rbtree.c lib/kfifo.c lib/hashtable.c lib/font8x8.c \ diff --git a/gui/gui_apps.c b/gui/gui_apps.c index c99507d..3480a2a 100644 --- a/gui/gui_apps.c +++ b/gui/gui_apps.c @@ -10,6 +10,7 @@ #include #include #include +#include #include #include @@ -768,3 +769,122 @@ void gui_app_objbrowse_launch(int x, int y, int w, int h) { win->handle_event = objbrowse_event; } } + +// ================================================================= +// 10. Registry Editor -- navigate the NT \Registry hive tree +// ================================================================= +typedef struct { + reg_key_t* current; + int row_h; + int list_top_rel; // list start, relative to window content top + int sub_count; // subkeys shown this frame (for click mapping) + bool has_up; // a ".." row is present +} regedit_data_t; + +static void regedit_build_path(reg_key_t* key, char* out, size_t len) { + // Walk parents into a reversed segment stack, then join with backslashes. + const char* segs[16]; + int n = 0; + reg_key_t* k = key; + while (k && n < 16) { segs[n++] = k->name; k = k->parent; } + size_t pos = 0; + out[0] = '\0'; + for (int i = n - 1; i >= 0; i--) { + int wrote = snprintf(out + pos, (pos < len) ? len - pos : 0, "\\%s", segs[i]); + if (wrote > 0) pos += (size_t)wrote; + if (pos >= len) break; + } +} + +static void regedit_paint(gui_window_t* win) { + regedit_data_t* rd = (regedit_data_t*)win->user_data; + if (!rd) return; + if (!rd->current) rd->current = reg_root(); + + int cx = win->x + 12; + int top = win->y + GUI_TITLEBAR_HEIGHT; + + char path[192]; + regedit_build_path(rd->current, path, sizeof(path)); + gui_gfx_draw_string_16_shadow(cx, top + 8, "Registry Editor", GUI_THEME_PRIMARY, GUI_COLOR_BLACK); + gui_gfx_draw_string(cx, top + 28, path, GUI_THEME_TEXT_MUTED); + + rd->row_h = 13; + rd->list_top_rel = GUI_TITLEBAR_HEIGHT + 46; + int y = top + 46; + + rd->has_up = (rd->current->parent != NULL); + if (rd->has_up) { + gui_gfx_draw_string(cx, y, "..", GUI_THEME_ACCENT); + y += rd->row_h; + } + + // Subkeys (navigable) + int kc = reg_enum_key_count(rd->current); + rd->sub_count = kc; + for (int i = 0; i < kc; i++) { + reg_key_t* sub = reg_enum_key(rd->current, i); + char line[64]; + snprintf(line, sizeof(line), "[%s]", sub->name); + gui_gfx_draw_string(cx, y, line, GUI_THEME_SUCCESS); + y += rd->row_h; + } + + // Values pane + y += 4; + gui_gfx_draw_line(cx, y, win->x + win->width - 12, y, GUI_THEME_BORDER); + y += 6; + int vc = reg_enum_value_count(rd->current); + if (vc == 0) { + gui_gfx_draw_string(cx, y, "(no values)", GUI_THEME_TEXT_DIM); + } + for (int i = 0; i < vc; i++) { + reg_value_t* v = reg_enum_value(rd->current, i); + char rendered[REG_STR_MAX + 8]; + reg_value_to_string(v, rendered, sizeof(rendered)); + char line[160]; + snprintf(line, sizeof(line), "%-18s %-10s %s", v->name, reg_type_name(v->type), rendered); + gui_gfx_draw_string(cx, y, line, GUI_THEME_TEXT_MAIN); + y += rd->row_h; + if (y > win->y + win->height - 12) break; + } +} + +static void regedit_event(gui_window_t* win, const gui_event_t* ev) { + regedit_data_t* rd = (regedit_data_t*)win->user_data; + if (!rd) return; + + if (ev->type == GUI_EVENT_CLOSE) { + if (win->user_data) { kfree(win->user_data); win->user_data = NULL; } + return; + } + if (ev->type != GUI_EVENT_MOUSE_DOWN) return; + if (!rd->current) return; + + int rel = ev->rel_y - rd->list_top_rel; + if (rel < 0) return; + int row = rel / (rd->row_h > 0 ? rd->row_h : 13); + + int idx = 0; + if (rd->has_up) { + if (row == 0) { rd->current = rd->current->parent; return; } + idx = row - 1; + } else { + idx = row; + } + if (idx >= 0 && idx < rd->sub_count) { + reg_key_t* sub = reg_enum_key(rd->current, idx); + if (sub) rd->current = sub; + } +} + +void gui_app_regedit_launch(int x, int y, int w, int h) { + gui_window_t* win = gui_wm_create_window("Registry Editor", x, y, (w > 0) ? w : 480, (h > 0) ? h : 360); + if (win) { + regedit_data_t* rd = (regedit_data_t*)kzalloc(sizeof(regedit_data_t)); + if (rd) rd->current = reg_root(); + win->user_data = rd; + win->paint = regedit_paint; + win->handle_event = regedit_event; + } +} diff --git a/gui/gui_desktop.c b/gui/gui_desktop.c index d33ab0a..3b236cc 100644 --- a/gui/gui_desktop.c +++ b/gui/gui_desktop.c @@ -59,6 +59,7 @@ static const desktop_app_t g_apps[] = { { "Life", GUI_ICON_FLASK, gui_app_life_launch, 240, 70, 480, 380 }, { "Rust Lab", GUI_ICON_INFO, gui_app_rustlab_launch, 300, 80, 420, 300 }, { "Objects", GUI_ICON_TASKS, gui_app_objbrowse_launch,280, 70, 460, 340 }, + { "RegEdit", GUI_ICON_SETTINGS, gui_app_regedit_launch, 300, 90, 480, 360 }, { "Clock", GUI_ICON_CLOCK, gui_app_clock_launch, 460, 120, 280, 260 }, { "Settings", GUI_ICON_SETTINGS, gui_app_settings_launch, 160, 60, 400, 320 }, { "About", GUI_ICON_INFO, gui_app_about_launch, 240, 140, 330, 170 }, @@ -67,8 +68,8 @@ static const desktop_app_t g_apps[] = { // Which of the above appear on the desktop itself. // Indices into g_apps[]: Terminal, Files, Monitor, Kernel Log, KTest, Life, -// Rust Lab, Settings. (Objects lives in the start menu.) -static const int g_desktop_icons[] = { 0, 1, 2, 4, 5, 9, 10, 13 }; +// Rust Lab, Settings. (Objects/RegEdit live in the start menu.) +static const int g_desktop_icons[] = { 0, 1, 2, 4, 5, 9, 10, 14 }; #define DESKTOP_ICON_COUNT ((int)(sizeof(g_desktop_icons) / sizeof(g_desktop_icons[0]))) static bool g_start_menu_open = false; diff --git a/include/gui/gui_apps.h b/include/gui/gui_apps.h index a779936..12f8d98 100644 --- a/include/gui/gui_apps.h +++ b/include/gui/gui_apps.h @@ -12,5 +12,6 @@ void gui_app_about_launch(int x, int y, int w, int h); void gui_app_life_launch(int x, int y, int w, int h); void gui_app_rustlab_launch(int x, int y, int w, int h); void gui_app_objbrowse_launch(int x, int y, int w, int h); +void gui_app_regedit_launch(int x, int y, int w, int h); #endif // _GUI_APPS_H diff --git a/include/kernel/nt/io.h b/include/kernel/nt/io.h new file mode 100644 index 0000000..435ee9f --- /dev/null +++ b/include/kernel/nt/io.h @@ -0,0 +1,87 @@ +#ifndef _KERNEL_NT_IO_H +#define _KERNEL_NT_IO_H + +// ----------------------------------------------------------------------------- +// SUB-OS NT-Inspired I/O Manager +// +// Models the Windows NT I/O subsystem: DRIVER_OBJECTs export a MajorFunction +// dispatch table, DEVICE_OBJECTs are created by drivers and can be stacked +// (attached) into a device stack, and I/O flows as IRPs (I/O Request Packets) +// pushed down the stack with IoCallDriver / completed with IoCompleteRequest. +// ----------------------------------------------------------------------------- + +#include +#include +#include +#include // NTSTATUS, STATUS_* codes + +typedef enum { + IRP_MJ_CREATE = 0, + IRP_MJ_CLOSE, + IRP_MJ_READ, + IRP_MJ_WRITE, + IRP_MJ_DEVICE_CONTROL, + IRP_MJ_MAXIMUM +} irp_major_t; + +#define IO_NAME_MAX 48 +#define IO_MAX_DRIVERS 16 +#define IO_MAX_DEVICES 32 + +typedef struct _DEVICE_OBJECT DEVICE_OBJECT; +typedef struct _DRIVER_OBJECT DRIVER_OBJECT; +typedef struct _IRP IRP; + +typedef NTSTATUS (*driver_dispatch_t)(DEVICE_OBJECT* dev, IRP* irp); +typedef NTSTATUS (*driver_entry_t)(DRIVER_OBJECT* drv); + +struct _DRIVER_OBJECT { + char name[IO_NAME_MAX]; // e.g. "\Driver\Null" + driver_dispatch_t major_function[IRP_MJ_MAXIMUM]; + DEVICE_OBJECT* device_list; // devices this driver owns + uint64_t irp_count; +}; + +struct _DEVICE_OBJECT { + char name[IO_NAME_MAX]; // e.g. "\Device\Null" + DRIVER_OBJECT* driver; + DEVICE_OBJECT* next_on_driver; // next in the driver's device_list + DEVICE_OBJECT* attached_to; // lower device in the stack (or NULL) + void* device_extension; + uint64_t read_count; + uint64_t write_count; + uint64_t ioctl_count; +}; + +struct _IRP { + irp_major_t major_function; + void* buffer; + uint32_t length; + uint32_t io_control_code; + NTSTATUS status; + uint32_t information; // bytes transferred on completion + DEVICE_OBJECT* current_device; +}; + +void io_init(void); +const char* io_major_name(irp_major_t mj); + +DRIVER_OBJECT* io_create_driver(const char* name, driver_entry_t entry); +NTSTATUS io_create_device(DRIVER_OBJECT* drv, const char* name, + size_t ext_size, DEVICE_OBJECT** out); +DEVICE_OBJECT* io_attach_device(DEVICE_OBJECT* source, DEVICE_OBJECT* target); +DEVICE_OBJECT* io_lookup_device(const char* name); + +IRP* io_build_irp(irp_major_t mj, void* buffer, uint32_t length); +NTSTATUS io_call_driver(DEVICE_OBJECT* dev, IRP* irp); +void io_complete_request(IRP* irp, NTSTATUS status); +void io_free_irp(IRP* irp); + +// Enumeration / stats. +int io_driver_count(void); +DRIVER_OBJECT* io_driver_at(int index); +int io_device_count(void); +DEVICE_OBJECT* io_device_at(int index); +void io_get_stats(uint32_t* out_drivers, uint32_t* out_devices, uint64_t* out_irps); + +#endif // _KERNEL_NT_IO_H diff --git a/kernel/main.c b/kernel/main.c index d46d52b..0ffb95a 100644 --- a/kernel/main.c +++ b/kernel/main.c @@ -9,6 +9,7 @@ #include #include #include +#include #include #include #include @@ -243,6 +244,7 @@ static void subos_modular_core_boot(void) { cron_init(); ob_init(); reg_init(); + io_init(); service_manager_init(); printk(ANSI_BRIGHT_GREEN "OK\n" ANSI_RESET); diff --git a/kernel/nt/io.c b/kernel/nt/io.c new file mode 100644 index 0000000..111e7ef --- /dev/null +++ b/kernel/nt/io.c @@ -0,0 +1,242 @@ +// ----------------------------------------------------------------------------- +// SUB-OS NT-Inspired I/O Manager (Io) +// +// Drivers register a MajorFunction dispatch table and create named device +// objects. Devices can be stacked (a filter attached on top of a lower device); +// io_call_driver invokes the top device's dispatch, which may forward the IRP +// down to dev->attached_to. Ships three demo drivers: Null, Zero, and a Monitor +// filter stacked above Null that counts and forwards every IRP. +// ----------------------------------------------------------------------------- + +#include +#include +#include +#include +#include + +static DRIVER_OBJECT* g_drivers[IO_MAX_DRIVERS]; +static int g_driver_count = 0; +static DEVICE_OBJECT* g_devices[IO_MAX_DEVICES]; +static int g_device_count = 0; +static uint64_t g_total_irps = 0; +static spinlock_t g_io_lock = SPINLOCK_INIT; + +const char* io_major_name(irp_major_t mj) { + switch (mj) { + case IRP_MJ_CREATE: return "CREATE"; + case IRP_MJ_CLOSE: return "CLOSE"; + case IRP_MJ_READ: return "READ"; + case IRP_MJ_WRITE: return "WRITE"; + case IRP_MJ_DEVICE_CONTROL: return "DEVICE_CONTROL"; + default: return "UNKNOWN"; + } +} + +DRIVER_OBJECT* io_create_driver(const char* name, driver_entry_t entry) { + spin_lock(&g_io_lock); + if (g_driver_count >= IO_MAX_DRIVERS) { + spin_unlock(&g_io_lock); + return NULL; + } + DRIVER_OBJECT* drv = (DRIVER_OBJECT*)kzalloc(sizeof(DRIVER_OBJECT)); + if (!drv) { + spin_unlock(&g_io_lock); + return NULL; + } + strncpy(drv->name, name ? name : "", IO_NAME_MAX - 1); + g_drivers[g_driver_count++] = drv; + spin_unlock(&g_io_lock); + + if (entry) { + entry(drv); // DriverEntry fills the MajorFunction table and creates devices + } + return drv; +} + +NTSTATUS io_create_device(DRIVER_OBJECT* drv, const char* name, + size_t ext_size, DEVICE_OBJECT** out) { + if (!drv) return STATUS_UNSUCCESSFUL; + spin_lock(&g_io_lock); + if (g_device_count >= IO_MAX_DEVICES) { + spin_unlock(&g_io_lock); + return STATUS_INSUFFICIENT_RES; + } + DEVICE_OBJECT* dev = (DEVICE_OBJECT*)kzalloc(sizeof(DEVICE_OBJECT)); + if (!dev) { + spin_unlock(&g_io_lock); + return STATUS_INSUFFICIENT_RES; + } + strncpy(dev->name, name ? name : "", IO_NAME_MAX - 1); + dev->driver = drv; + if (ext_size > 0) { + dev->device_extension = kzalloc(ext_size); + } + // Link into the driver's device list and the global table. + dev->next_on_driver = drv->device_list; + drv->device_list = dev; + g_devices[g_device_count++] = dev; + spin_unlock(&g_io_lock); + + if (out) *out = dev; + return STATUS_SUCCESS; +} + +DEVICE_OBJECT* io_attach_device(DEVICE_OBJECT* source, DEVICE_OBJECT* target) { + if (!source || !target) return NULL; + spin_lock(&g_io_lock); + source->attached_to = target; // source now sits above target in the stack + spin_unlock(&g_io_lock); + return target; +} + +DEVICE_OBJECT* io_lookup_device(const char* name) { + if (!name) return NULL; + spin_lock(&g_io_lock); + DEVICE_OBJECT* found = NULL; + for (int i = 0; i < g_device_count; i++) { + if (strcmp(g_devices[i]->name, name) == 0) { found = g_devices[i]; break; } + } + spin_unlock(&g_io_lock); + return found; +} + +IRP* io_build_irp(irp_major_t mj, void* buffer, uint32_t length) { + IRP* irp = (IRP*)kzalloc(sizeof(IRP)); + if (!irp) return NULL; + irp->major_function = mj; + irp->buffer = buffer; + irp->length = length; + irp->status = STATUS_UNSUCCESSFUL; + irp->information = 0; + return irp; +} + +NTSTATUS io_call_driver(DEVICE_OBJECT* dev, IRP* irp) { + if (!dev || !irp) return STATUS_UNSUCCESSFUL; + irp->current_device = dev; + + spin_lock(&g_io_lock); + g_total_irps++; + if (dev->driver) dev->driver->irp_count++; + switch (irp->major_function) { + case IRP_MJ_READ: dev->read_count++; break; + case IRP_MJ_WRITE: dev->write_count++; break; + case IRP_MJ_DEVICE_CONTROL: dev->ioctl_count++; break; + default: break; + } + driver_dispatch_t fn = NULL; + if (dev->driver && irp->major_function < IRP_MJ_MAXIMUM) { + fn = dev->driver->major_function[irp->major_function]; + } + spin_unlock(&g_io_lock); + + if (!fn) { + irp->status = STATUS_UNSUCCESSFUL; + return irp->status; + } + return fn(dev, irp); +} + +void io_complete_request(IRP* irp, NTSTATUS status) { + if (!irp) return; + irp->status = status; +} + +void io_free_irp(IRP* irp) { + if (irp) kfree(irp); +} + +int io_driver_count(void) { return g_driver_count; } +DRIVER_OBJECT* io_driver_at(int index) { + return (index >= 0 && index < g_driver_count) ? g_drivers[index] : NULL; +} +int io_device_count(void) { return g_device_count; } +DEVICE_OBJECT* io_device_at(int index) { + return (index >= 0 && index < g_device_count) ? g_devices[index] : NULL; +} +void io_get_stats(uint32_t* out_drivers, uint32_t* out_devices, uint64_t* out_irps) { + if (out_drivers) *out_drivers = (uint32_t)g_driver_count; + if (out_devices) *out_devices = (uint32_t)g_device_count; + if (out_irps) *out_irps = g_total_irps; +} + +// --------------------------------------------------------------------------- +// Demo drivers +// --------------------------------------------------------------------------- + +// \Driver\Null : sink writes, return EOF on reads (like NT's \Device\Null). +static NTSTATUS null_read(DEVICE_OBJECT* dev, IRP* irp) { + (void)dev; + irp->information = 0; // 0 bytes == end of file + io_complete_request(irp, STATUS_SUCCESS); + return STATUS_SUCCESS; +} +static NTSTATUS null_write(DEVICE_OBJECT* dev, IRP* irp) { + (void)dev; + irp->information = irp->length; // swallow everything + io_complete_request(irp, STATUS_SUCCESS); + return STATUS_SUCCESS; +} +static NTSTATUS null_entry(DRIVER_OBJECT* drv) { + drv->major_function[IRP_MJ_READ] = null_read; + drv->major_function[IRP_MJ_WRITE] = null_write; + io_create_device(drv, "\\Device\\Null", 0, NULL); + return STATUS_SUCCESS; +} + +// \Driver\Zero : reads yield a buffer full of zero bytes. +static NTSTATUS zero_read(DEVICE_OBJECT* dev, IRP* irp) { + (void)dev; + if (irp->buffer && irp->length) { + memset(irp->buffer, 0, irp->length); + } + irp->information = irp->length; + io_complete_request(irp, STATUS_SUCCESS); + return STATUS_SUCCESS; +} +static NTSTATUS zero_entry(DRIVER_OBJECT* drv) { + drv->major_function[IRP_MJ_READ] = zero_read; + io_create_device(drv, "\\Device\\Zero", 0, NULL); + return STATUS_SUCCESS; +} + +// \Driver\Monitor : a filter stacked above \Device\Null. It counts each IRP in +// its device extension and forwards to the lower device, demonstrating a stack. +typedef struct { uint64_t forwarded; } monitor_ext_t; + +static NTSTATUS monitor_passthrough(DEVICE_OBJECT* dev, IRP* irp) { + monitor_ext_t* ext = (monitor_ext_t*)dev->device_extension; + if (ext) ext->forwarded++; + if (dev->attached_to) { + return io_call_driver(dev->attached_to, irp); // pass down the stack + } + io_complete_request(irp, STATUS_SUCCESS); + return STATUS_SUCCESS; +} +static NTSTATUS monitor_entry(DRIVER_OBJECT* drv) { + drv->major_function[IRP_MJ_READ] = monitor_passthrough; + drv->major_function[IRP_MJ_WRITE] = monitor_passthrough; + drv->major_function[IRP_MJ_DEVICE_CONTROL] = monitor_passthrough; + + DEVICE_OBJECT* filter = NULL; + io_create_device(drv, "\\Device\\NullMonitor", sizeof(monitor_ext_t), &filter); + DEVICE_OBJECT* lower = io_lookup_device("\\Device\\Null"); + if (filter && lower) { + io_attach_device(filter, lower); + } + return STATUS_SUCCESS; +} + +void io_init(void) { + g_driver_count = 0; + g_device_count = 0; + g_total_irps = 0; + + io_create_driver("\\Driver\\Null", null_entry); + io_create_driver("\\Driver\\Zero", zero_entry); + io_create_driver("\\Driver\\Monitor", monitor_entry); + + printk(ANSI_BRIGHT_GREEN "IO: " ANSI_RESET + "NT I/O Manager online (%d drivers, %d devices, IRP-based dispatch)\n", + g_driver_count, g_device_count); +} diff --git a/userland/lazybox/lazybox.c b/userland/lazybox/lazybox.c index 1f87d52..1b7544d 100644 --- a/userland/lazybox/lazybox.c +++ b/userland/lazybox/lazybox.c @@ -31,6 +31,7 @@ #include #include #include +#include #include #include #include @@ -659,6 +660,61 @@ static int applet_reg(int argc, char** argv) { return 1; } +static int applet_iodev(int argc, char** argv) { + (void)argc; (void)argv; + uint32_t drivers = 0, devices = 0; + uint64_t irps = 0; + io_get_stats(&drivers, &devices, &irps); + + printk(ANSI_BRIGHT_CYAN "NT I/O Manager:\n" ANSI_RESET); + printk(" Drivers: %u Devices: %u IRPs dispatched: %llu\n", + drivers, devices, (unsigned long long)irps); + + int dc = io_device_count(); + for (int i = 0; i < dc; i++) { + DEVICE_OBJECT* dev = io_device_at(i); + if (!dev) continue; + printk(" " ANSI_YELLOW "%-22s" ANSI_RESET " drv=%-16s r=%llu w=%llu ioctl=%llu", + dev->name, dev->driver ? dev->driver->name : "?", + (unsigned long long)dev->read_count, + (unsigned long long)dev->write_count, + (unsigned long long)dev->ioctl_count); + if (dev->attached_to) { + printk(" -> %s", dev->attached_to->name); + } + printk("\n"); + } + + // Live demo: push a WRITE IRP through the filtered Null stack and a READ to + // the Zero device, then report what came back. + printk(ANSI_BRIGHT_CYAN "IRP demo:\n" ANSI_RESET); + DEVICE_OBJECT* mon = io_lookup_device("\\Device\\NullMonitor"); + if (mon) { + char payload[16]; + memset(payload, 'X', sizeof(payload)); + IRP* irp = io_build_irp(IRP_MJ_WRITE, payload, sizeof(payload)); + if (irp) { + NTSTATUS st = io_call_driver(mon, irp); + printk(" WRITE %u bytes to %s -> status=0x%X, %u bytes sunk (via filter)\n", + (unsigned)sizeof(payload), mon->name, st, irp->information); + io_free_irp(irp); + } + } + DEVICE_OBJECT* zero = io_lookup_device("\\Device\\Zero"); + if (zero) { + uint8_t buf[8]; + memset(buf, 0xEE, sizeof(buf)); + IRP* irp = io_build_irp(IRP_MJ_READ, buf, sizeof(buf)); + if (irp) { + io_call_driver(zero, irp); + printk(" READ %u bytes from %s -> first bytes %02X %02X %02X (zeroed)\n", + irp->information, zero->name, buf[0], buf[1], buf[2]); + io_free_irp(irp); + } + } + return 0; +} + static int applet_rand(int argc, char** argv) { int count = (argc >= 2) ? atoi(argv[1]) : 4; printk("Random Integers: "); @@ -2371,6 +2427,7 @@ static const lazybox_applet_t applets[] = { {"objdir", applet_objdir, "objdir [\\path]", "List the NT object namespace","Kernel"}, {"ntobj", applet_ntobj, "ntobj", "NT Object Manager stats/demo","Kernel"}, {"reg", applet_reg, "reg query|add|set|stats", "NT configuration registry", "Kernel"}, + {"iodev", applet_iodev, "iodev", "NT I/O Manager devices & IRP","Kernel"}, {"rand", applet_rand, "rand [count]", "Generate random numbers", "Crypto"}, {"certcheck", applet_certcheck, "certcheck", "View X.509 kernel keyring", "Security"}, {"capsh", applet_capsh, "capsh", "View process capabilities", "Security"},