208 lines
5.5 KiB
Perl

package Parse::Win32Registry::Win95::Key;
use strict;
use warnings;
use base qw(Parse::Win32Registry::Key);
use Carp;
use Parse::Win32Registry::Base qw(:all);
use Parse::Win32Registry::Win95::Value;
use constant RGKN_ENTRY_LENGTH => 0x1c;
use constant OFFSET_TO_RGKN_BLOCK => 0x20;
sub new {
my $class = shift;
my $regfile = shift;
my $offset = shift; # offset to RGKN key entry relative to start of RGKN
my $parent_key_path = shift; # parent key path (optional)
croak 'Missing registry file' if !defined $regfile;
croak 'Missing offset' if !defined $offset;
my $fh = $regfile->get_filehandle;
# RGKN Key Entry
# 0x00 dword
# 0x04 dword
# 0x08 dword
# 0x0c dword = offset to parent RGKN entry
# 0x10 dword = offset to first child RGKN entry
# 0x14 dword = offset to next sibling RGKN entry
# 0x18 dword = entry id of RGDB entry
# Extracted offsets are relative to the start of the RGKN block
# Any offset of 0xffffffff marks the end of a list.
# An entry id of 0xffffffff means the RGKN entry has no RGDB entry.
# This occurs for the root key of the registry file.
sysseek($fh, $offset, 0);
my $bytes_read = sysread($fh, my $rgkn_entry, RGKN_ENTRY_LENGTH);
if ($bytes_read != RGKN_ENTRY_LENGTH) {
warnf('Could not read RGKN key at 0x%x', $offset);
return;
}
my ($offset_to_parent,
$offset_to_first_child,
$offset_to_next_sibling,
$key_id) = unpack('x12VVVV', $rgkn_entry);
$offset_to_parent += OFFSET_TO_RGKN_BLOCK
if $offset_to_parent != 0xffffffff;
$offset_to_first_child += OFFSET_TO_RGKN_BLOCK
if $offset_to_first_child != 0xffffffff;
$offset_to_next_sibling += OFFSET_TO_RGKN_BLOCK
if $offset_to_next_sibling != 0xffffffff;
my $self = {};
$self->{_regfile} = $regfile;
$self->{_offset} = $offset;
$self->{_length} = RGKN_ENTRY_LENGTH;
$self->{_allocated} = 1;
$self->{_tag} = 'rgkn key';
$self->{_offset_to_parent} = $offset_to_parent;
$self->{_offset_to_first_child} = $offset_to_first_child;
$self->{_offset_to_next_sibling} = $offset_to_next_sibling;
$self->{_id} = $key_id;
bless $self, $class;
# Look up corresponding rgdb entry
my $index = $regfile->{_rgdb_index};
croak 'Missing rgdb index' if !defined $index;
if (exists $index->{$key_id}) {
my $rgdb_key = $index->{$key_id};
$self->{_rgdb_key} = $rgdb_key;
$self->{_name} = $rgdb_key->get_name;
}
else {
$self->{_name} = '';
# Only the root key should have no matching RGDB entry
if (!$self->is_root) {
warnf('Could not find RGDB entry for RGKN key at 0x%x', $offset);
}
}
my $name = $self->{_name};
$self->{_key_path} = defined($parent_key_path)
? "$parent_key_path\\$name"
: $name;
return $self;
}
sub get_timestamp {
return undef;
}
sub get_timestamp_as_string {
return iso8601(undef);
}
sub get_class_name {
return undef;
}
sub is_root {
my $self = shift;
my $offset = $self->{_offset};
my $regfile = $self->{_regfile};
my $rgkn_block = $regfile->get_rgkn;
my $offset_to_root_key = $rgkn_block->{_offset_to_root_key};
# This gives better results than checking id == 0xffffffff
return $offset == $offset_to_root_key;
}
sub get_parent {
my $self = shift;
my $regfile = $self->{_regfile};
my $offset_to_parent = $self->{_offset_to_parent};
my $key_path = $self->{_key_path};
return if $self->is_root;
my $grandparent_key_path;
my @keys = split(/\\/, $key_path, -1);
if (@keys > 2) {
$grandparent_key_path = join("\\", @keys[0..$#keys-2]);
}
return Parse::Win32Registry::Win95::Key->new($regfile,
$offset_to_parent,
$grandparent_key_path);
}
sub get_security {
return undef;
}
sub as_string {
my $self = shift;
return $self->get_path;
}
sub parse_info {
my $self = shift;
my $info = sprintf '0x%x rgkn key len=0x%x par=0x%x,child=0x%x,next=0x%x id=0x%x',
$self->{_offset},
$self->{_length},
$self->{_offset_to_parent},
$self->{_offset_to_first_child},
$self->{_offset_to_next_sibling},
$self->{_id};
return $info;
}
sub get_subkey_iterator {
my $self = shift;
my $regfile = $self->{_regfile};
my $key_path = $self->{_key_path};
my $offset_to_next_key = $self->{_offset_to_first_child};
my $end_of_file = $regfile->get_length;
my $rgkn_block = $regfile->get_rgkn;
my $end_of_rgkn_block = $rgkn_block->get_offset + $rgkn_block->get_length;
return Parse::Win32Registry::Iterator->new(sub {
if ($offset_to_next_key == 0xffffffff) {
return; # no more subkeys
}
if ($offset_to_next_key > $end_of_rgkn_block) {
return;
}
if (my $key = Parse::Win32Registry::Win95::Key->new($regfile,
$offset_to_next_key, $key_path))
{
$offset_to_next_key = $key->{_offset_to_next_sibling};
return $key;
}
else {
return; # no more subkeys
}
});
}
sub get_value_iterator {
my $self = shift;
my $rgdb_key = $self->{_rgdb_key};
if (defined $rgdb_key) {
return $rgdb_key->get_value_iterator;
}
else {
return Parse::Win32Registry::Iterator->new(sub {});
}
}
1;