mirror of
https://github.com/overcuriousity/autopsy-flatpak.git
synced 2025-07-06 21:00:22 +00:00
modified extractregistry.java in recentactivity to make regripper work
This commit is contained in:
parent
3455aa61e0
commit
e60d5967a4
@ -76,7 +76,7 @@ class ExtractRegistry extends Extract {
|
||||
final private static UsbDeviceIdMapper USB_MAPPER = new UsbDeviceIdMapper();
|
||||
final private static String RIP_EXE = "rip.exe";
|
||||
final private static String RIP_PL = "rip.pl";
|
||||
final private static String PERL = "perl ";
|
||||
private static String PERL = "perl ";
|
||||
|
||||
ExtractRegistry() throws IngestModuleException {
|
||||
moduleName = NbBundle.getMessage(ExtractIE.class, "ExtractRegistry.moduleName.text");
|
||||
@ -107,8 +107,7 @@ class ExtractRegistry extends Extract {
|
||||
}
|
||||
|
||||
if (!PlatformUtil.isWindowsOS()) {
|
||||
RR_PATH = PERL + RR_PATH;
|
||||
RR_FULL_PATH = PERL + RR_FULL_PATH;
|
||||
PERL = "/usr/bin/perl";
|
||||
}
|
||||
}
|
||||
|
||||
@ -281,6 +280,7 @@ class ExtractRegistry extends Extract {
|
||||
private void executeRegRipper(String regRipperPath, Path regRipperHomeDir, String hiveFilePath, String hiveFileType, String outputFile, String errFile) {
|
||||
try {
|
||||
List<String> commandLine = new ArrayList<>();
|
||||
commandLine.add(PERL);
|
||||
commandLine.add(regRipperPath);
|
||||
commandLine.add("-r"); //NON-NLS
|
||||
commandLine.add(hiveFilePath);
|
||||
@ -288,6 +288,7 @@ class ExtractRegistry extends Extract {
|
||||
commandLine.add(hiveFileType);
|
||||
|
||||
ProcessBuilder processBuilder = new ProcessBuilder(commandLine);
|
||||
System.out.println(processBuilder.command());
|
||||
processBuilder.directory(regRipperHomeDir.toFile()); // RegRipper 2.8 has to be run from its own directory
|
||||
processBuilder.redirectOutput(new File(outputFile));
|
||||
processBuilder.redirectError(new File(errFile));
|
||||
|
1834
thirdparty/rr-full/Parse/Win32Registry.pm
vendored
Normal file
1834
thirdparty/rr-full/Parse/Win32Registry.pm
vendored
Normal file
File diff suppressed because it is too large
Load Diff
1107
thirdparty/rr-full/Parse/Win32Registry/Base.pm
vendored
Normal file
1107
thirdparty/rr-full/Parse/Win32Registry/Base.pm
vendored
Normal file
File diff suppressed because it is too large
Load Diff
151
thirdparty/rr-full/Parse/Win32Registry/Entry.pm
vendored
Normal file
151
thirdparty/rr-full/Parse/Win32Registry/Entry.pm
vendored
Normal file
@ -0,0 +1,151 @@
|
||||
package Parse::Win32Registry::Entry;
|
||||
|
||||
use strict;
|
||||
use warnings;
|
||||
|
||||
use Carp;
|
||||
use Parse::Win32Registry::Base qw(:all);
|
||||
|
||||
sub get_regfile {
|
||||
my $self = shift;
|
||||
|
||||
return $self->{_regfile};
|
||||
}
|
||||
|
||||
sub get_offset {
|
||||
my $self = shift;
|
||||
|
||||
return $self->{_offset};
|
||||
}
|
||||
|
||||
sub get_length {
|
||||
my $self = shift;
|
||||
|
||||
return $self->{_length};
|
||||
}
|
||||
|
||||
sub is_allocated {
|
||||
my $self = shift;
|
||||
|
||||
return $self->{_allocated};
|
||||
}
|
||||
|
||||
sub get_tag {
|
||||
my $self = shift;
|
||||
|
||||
return $self->{_tag};
|
||||
}
|
||||
|
||||
sub as_string {
|
||||
my $self = shift;
|
||||
|
||||
my $tag = $self->{_tag};
|
||||
$tag = 'unidentified entry' if !defined $tag;
|
||||
return "($tag)";
|
||||
}
|
||||
|
||||
sub parse_info {
|
||||
my $self = shift;
|
||||
|
||||
my $info = sprintf '0x%x %s len=0x%x',
|
||||
$self->{_offset},
|
||||
$self->{_tag},
|
||||
$self->{_length};
|
||||
|
||||
return $info;
|
||||
}
|
||||
|
||||
sub unparsed {
|
||||
my $self = shift;
|
||||
|
||||
return hexdump($self->get_raw_bytes, $self->get_offset);
|
||||
}
|
||||
|
||||
sub get_raw_bytes {
|
||||
my $self = shift;
|
||||
|
||||
my $regfile = $self->{_regfile};
|
||||
my $fh = $regfile->get_filehandle;
|
||||
my $offset = $self->{_offset};
|
||||
my $length = $self->{_length};
|
||||
|
||||
if (defined $self->{_header_length}) {
|
||||
$length = $self->{_header_length};
|
||||
}
|
||||
|
||||
sysseek($fh, $offset, 0);
|
||||
my $bytes_read = sysread($fh, my $buffer, $length);
|
||||
if ($bytes_read == $length) {
|
||||
return $buffer;
|
||||
}
|
||||
else {
|
||||
return '';
|
||||
}
|
||||
}
|
||||
|
||||
sub looks_like_key {
|
||||
return UNIVERSAL::isa($_[0], "Parse::Win32Registry::Key");
|
||||
}
|
||||
|
||||
sub looks_like_value {
|
||||
return UNIVERSAL::isa($_[0], "Parse::Win32Registry::Value");
|
||||
}
|
||||
|
||||
sub looks_like_security {
|
||||
return UNIVERSAL::isa($_[0], "Parse::Win32Registry::WinNT::Security");
|
||||
}
|
||||
|
||||
sub _dumpvar {
|
||||
my $self = shift;
|
||||
my $depth = shift || 1;
|
||||
|
||||
my $dumpvar = '';
|
||||
foreach (sort keys %$self) {
|
||||
$dumpvar .= ' ' x ($depth*2);
|
||||
$dumpvar .= "$_ => ";
|
||||
my $var = $self->{$_};
|
||||
if (!defined $var) {
|
||||
$dumpvar .= "undef\n";
|
||||
}
|
||||
elsif (/offset/ || /_id$/ || /^_unk/) {
|
||||
$dumpvar .= sprintf "0x%x\n", $var;
|
||||
}
|
||||
elsif (/_flags$/) {
|
||||
$dumpvar .= sprintf "0x%x (0b%b)\n", $var, $var;
|
||||
}
|
||||
elsif (/length/ || /bytes_used/) {
|
||||
$dumpvar .= sprintf "0x%x (%d)\n", $var, $var;
|
||||
}
|
||||
elsif (/_data$/) {
|
||||
if (length($var) == 0) {
|
||||
$dumpvar .= '(no data)';
|
||||
}
|
||||
else {
|
||||
$dumpvar .= join(' ', unpack('(H2)20', $var));
|
||||
if (length($var) > 20) {
|
||||
$dumpvar .= '...';
|
||||
}
|
||||
}
|
||||
$dumpvar .= "\n";
|
||||
}
|
||||
elsif (/timestamp$/) {
|
||||
$dumpvar .= $var . " (" . iso8601($var) . ")\n";
|
||||
}
|
||||
elsif ($var =~ /^\d+$/) {
|
||||
$dumpvar .= sprintf "%d\n", $var;
|
||||
}
|
||||
elsif (ref($var)) {
|
||||
$dumpvar .= "$var\n"; # stringify object ref
|
||||
}
|
||||
else {
|
||||
$dumpvar .= qq{"$var"};
|
||||
$dumpvar .= ' ';
|
||||
$dumpvar .= Encode::is_utf8($var) ? "(UTF8)" : "(BYTES)";
|
||||
$dumpvar .= "\n";
|
||||
}
|
||||
}
|
||||
|
||||
return $dumpvar;
|
||||
}
|
||||
|
||||
1;
|
66
thirdparty/rr-full/Parse/Win32Registry/File.pm
vendored
Normal file
66
thirdparty/rr-full/Parse/Win32Registry/File.pm
vendored
Normal file
@ -0,0 +1,66 @@
|
||||
package Parse::Win32Registry::File;
|
||||
|
||||
use strict;
|
||||
use warnings;
|
||||
|
||||
sub get_filehandle {
|
||||
my $self = shift;
|
||||
|
||||
return $self->{_filehandle};
|
||||
}
|
||||
|
||||
sub get_filename {
|
||||
my $self = shift;
|
||||
|
||||
return $self->{_filename};
|
||||
}
|
||||
|
||||
sub get_length {
|
||||
my $self = shift;
|
||||
|
||||
return $self->{_length};
|
||||
}
|
||||
|
||||
sub get_entry_iterator {
|
||||
my $self = shift;
|
||||
|
||||
my $entry_iter;
|
||||
my $block_iter = $self->get_block_iterator;
|
||||
|
||||
return Parse::Win32Registry::Iterator->new(sub {
|
||||
while (1) {
|
||||
if (defined $entry_iter) {
|
||||
my $entry = $entry_iter->();
|
||||
if (defined $entry) {
|
||||
return $entry;
|
||||
}
|
||||
}
|
||||
# entry iterator is undefined or finished
|
||||
my $block = $block_iter->();
|
||||
if (!defined $block) {
|
||||
return; # block iterator finished
|
||||
}
|
||||
$entry_iter = $block->get_entry_iterator;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
# method provided for backwards compatibility
|
||||
sub move_to_first_entry {
|
||||
my $self = shift;
|
||||
|
||||
$self->{_entry_iter} = undef;
|
||||
}
|
||||
|
||||
# method provided for backwards compatibility
|
||||
sub get_next_entry {
|
||||
my $self = shift;
|
||||
|
||||
my $entry_iter = $self->{_entry_iter};
|
||||
if (!defined $entry_iter) {
|
||||
$self->{_entry_iter} = $entry_iter = $self->get_entry_iterator;
|
||||
}
|
||||
return $entry_iter->();
|
||||
}
|
||||
|
||||
1;
|
245
thirdparty/rr-full/Parse/Win32Registry/Key.pm
vendored
Normal file
245
thirdparty/rr-full/Parse/Win32Registry/Key.pm
vendored
Normal file
@ -0,0 +1,245 @@
|
||||
package Parse::Win32Registry::Key;
|
||||
|
||||
use strict;
|
||||
use warnings;
|
||||
|
||||
use base qw(Parse::Win32Registry::Entry);
|
||||
|
||||
use Carp;
|
||||
|
||||
sub get_name {
|
||||
my $self = shift;
|
||||
|
||||
# the root key of a windows 95 registry has no defined name
|
||||
# but this should be set to '' when created
|
||||
return $self->{_name};
|
||||
}
|
||||
|
||||
sub get_path {
|
||||
my $self = shift;
|
||||
|
||||
return $self->{_key_path};
|
||||
}
|
||||
|
||||
sub _look_up_subkey {
|
||||
my $self = shift;
|
||||
my $subkey_name = shift;
|
||||
|
||||
croak 'Missing subkey name' if !defined $subkey_name;
|
||||
|
||||
foreach my $subkey ($self->get_list_of_subkeys) {
|
||||
if (uc $subkey_name eq uc $subkey->{_name}) {
|
||||
return $subkey;
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
sub get_subkey {
|
||||
my $self = shift;
|
||||
my $subkey_path = shift;
|
||||
|
||||
# check for definedness in case key name is '' or '0'
|
||||
croak "Usage: get_subkey('key name')" if !defined $subkey_path;
|
||||
|
||||
my $key = $self;
|
||||
|
||||
# Current path component separator is '\' to match that used in Windows.
|
||||
# split returns nothing if it is given an empty string,
|
||||
# and without a limit of -1 drops trailing empty fields.
|
||||
# The following returns a list with a single zero-length string ("")
|
||||
# for an empty string, as split(/\\/, $subkey_path, -1) returns (),
|
||||
# an empty list.
|
||||
my @path_components = index($subkey_path, "\\") == -1
|
||||
? ($subkey_path)
|
||||
: split(/\\/, $subkey_path, -1);
|
||||
|
||||
my %offsets_seen = ();
|
||||
$offsets_seen{$key->get_offset} = undef;
|
||||
|
||||
foreach my $subkey_name (@path_components) {
|
||||
if (my $subkey = $key->_look_up_subkey($subkey_name)) {
|
||||
if (exists $offsets_seen{$subkey->get_offset}) {
|
||||
return; # found loop
|
||||
}
|
||||
$key = $subkey;
|
||||
$offsets_seen{$key->get_offset} = undef;
|
||||
}
|
||||
else { # subkey name not found, abort look up
|
||||
return;
|
||||
}
|
||||
}
|
||||
return $key;
|
||||
}
|
||||
|
||||
sub get_value {
|
||||
my $self = shift;
|
||||
my $value_name = shift;
|
||||
|
||||
# check for definedness in case value name is '' or '0'
|
||||
croak "Usage: get_value('value name')" if !defined $value_name;
|
||||
|
||||
foreach my $value ($self->get_list_of_values) {
|
||||
if (uc $value_name eq uc $value->{_name}) {
|
||||
return $value;
|
||||
}
|
||||
}
|
||||
return undef;
|
||||
}
|
||||
|
||||
sub print_summary {
|
||||
my $self = shift;
|
||||
|
||||
print $self->as_string, "\n";
|
||||
}
|
||||
|
||||
sub as_regedit_export {
|
||||
my $self = shift;
|
||||
|
||||
return "[" . $self->{_key_path} . "]\n";
|
||||
}
|
||||
|
||||
sub regenerate_path {
|
||||
my $self = shift;
|
||||
|
||||
# ascend to the root
|
||||
my $key = $self;
|
||||
my @key_names = ($key->get_name);
|
||||
|
||||
my %offsets_seen = ();
|
||||
while (!$key->is_root) {
|
||||
$offsets_seen{$key->get_offset}++;
|
||||
$key = $key->get_parent;
|
||||
if (!defined $key) { # found an undefined parent key
|
||||
unshift @key_names, '(Invalid Parent Key)';
|
||||
last;
|
||||
}
|
||||
if (exists $offsets_seen{$key->get_offset}) { # found loop
|
||||
unshift @key_names, '(Invalid Parent Key)';
|
||||
last;
|
||||
}
|
||||
unshift @key_names, $key->get_name;
|
||||
}
|
||||
|
||||
my $key_path = join('\\', @key_names);
|
||||
$self->{_key_path} = $key_path;
|
||||
return $key_path;
|
||||
}
|
||||
|
||||
sub get_value_data {
|
||||
my $self = shift;
|
||||
my $value_name = shift;
|
||||
|
||||
croak "Usage: get_value_data('value name')" if !defined $value_name;
|
||||
|
||||
if (my $value = $self->get_value($value_name)) {
|
||||
return $value->get_data;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
sub get_mru_list_of_values {
|
||||
my $self = shift;
|
||||
|
||||
my @values = ();
|
||||
|
||||
if (my $mrulist = $self->get_value('MRUList')) {
|
||||
foreach my $ch (split(//, $mrulist->get_data)) {
|
||||
if (my $value = $self->get_value($ch)) {
|
||||
push @values, $value;
|
||||
}
|
||||
}
|
||||
}
|
||||
elsif (my $mrulistex = $self->get_value('MRUListEx')) {
|
||||
foreach my $item (unpack('V*', $mrulistex->get_data)) {
|
||||
last if $item == 0xffffffff;
|
||||
if (my $value = $self->get_value($item)) {
|
||||
push @values, $value;
|
||||
}
|
||||
}
|
||||
}
|
||||
return @values;
|
||||
}
|
||||
|
||||
sub get_list_of_subkeys {
|
||||
my $self = shift;
|
||||
|
||||
my $subkey_iter = $self->get_subkey_iterator;
|
||||
my @subkeys;
|
||||
while (my $subkey = $subkey_iter->()) {
|
||||
push @subkeys, $subkey;
|
||||
}
|
||||
return @subkeys;
|
||||
}
|
||||
|
||||
sub get_list_of_values {
|
||||
my $self = shift;
|
||||
|
||||
my $value_iter = $self->get_value_iterator;
|
||||
my @values;
|
||||
while (my $value = $value_iter->()) {
|
||||
push @values, $value;
|
||||
}
|
||||
return @values;
|
||||
}
|
||||
|
||||
sub get_subtree_iterator {
|
||||
my $self = shift;
|
||||
|
||||
my @start_keys = ($self);
|
||||
push my (@subkey_iters), Parse::Win32Registry::Iterator->new(sub {
|
||||
return shift @start_keys;
|
||||
});
|
||||
my $value_iter;
|
||||
my $key; # used to remember key while iterating values
|
||||
|
||||
return Parse::Win32Registry::Iterator->new(sub {
|
||||
if (defined $value_iter && wantarray) {
|
||||
my $value = $value_iter->();
|
||||
if (defined $value) {
|
||||
return ($key, $value);
|
||||
}
|
||||
# $value_iter finished, so fetch a new one
|
||||
# from the (current) $subkey_iter[-1]
|
||||
}
|
||||
while (@subkey_iters > 0) {
|
||||
$key = $subkey_iters[-1]->(); # depth-first
|
||||
if (defined $key) {
|
||||
push @subkey_iters, $key->get_subkey_iterator;
|
||||
$value_iter = $key->get_value_iterator;
|
||||
return $key;
|
||||
}
|
||||
pop @subkey_iters; # $subkey_iter finished, so remove it
|
||||
}
|
||||
return;
|
||||
});
|
||||
}
|
||||
|
||||
sub walk {
|
||||
my $self = shift;
|
||||
my $key_enter_func = shift;
|
||||
my $value_func = shift;
|
||||
my $key_leave_func = shift;
|
||||
|
||||
if (!defined $key_enter_func &&
|
||||
!defined $value_func &&
|
||||
!defined $key_leave_func) {
|
||||
$key_enter_func = sub { print "+ ", $_[0]->get_path, "\n"; };
|
||||
$value_func = sub { print " '", $_[0]->get_name, "'\n"; };
|
||||
$key_leave_func = sub { print "- ", $_[0]->get_path, "\n"; };
|
||||
}
|
||||
|
||||
$key_enter_func->($self) if ref $key_enter_func eq 'CODE';
|
||||
|
||||
foreach my $value ($self->get_list_of_values) {
|
||||
$value_func->($value) if ref $value_func eq 'CODE';
|
||||
}
|
||||
|
||||
foreach my $subkey ($self->get_list_of_subkeys) {
|
||||
$subkey->walk($key_enter_func, $value_func, $key_leave_func);
|
||||
}
|
||||
|
||||
$key_leave_func->($self) if ref $key_leave_func eq 'CODE';
|
||||
}
|
||||
|
||||
1;
|
101
thirdparty/rr-full/Parse/Win32Registry/Value.pm
vendored
Normal file
101
thirdparty/rr-full/Parse/Win32Registry/Value.pm
vendored
Normal file
@ -0,0 +1,101 @@
|
||||
package Parse::Win32Registry::Value;
|
||||
|
||||
use strict;
|
||||
use warnings;
|
||||
|
||||
use base qw(Parse::Win32Registry::Entry);
|
||||
|
||||
use Carp;
|
||||
use Parse::Win32Registry::Base qw(:all);
|
||||
|
||||
sub get_name {
|
||||
my $self = shift;
|
||||
|
||||
return $self->{_name};
|
||||
}
|
||||
|
||||
sub get_type {
|
||||
my $self = shift;
|
||||
|
||||
return $self->{_type};
|
||||
}
|
||||
|
||||
our @Types = qw(
|
||||
REG_NONE
|
||||
REG_SZ
|
||||
REG_EXPAND_SZ
|
||||
REG_BINARY
|
||||
REG_DWORD
|
||||
REG_DWORD_BIG_ENDIAN
|
||||
REG_LINK
|
||||
REG_MULTI_SZ
|
||||
REG_RESOURCE_LIST
|
||||
REG_FULL_RESOURCE_DESCRIPTOR
|
||||
REG_RESOURCE_REQUIREMENTS_LIST
|
||||
REG_QWORD
|
||||
);
|
||||
|
||||
sub get_type_as_string {
|
||||
my $self = shift;
|
||||
|
||||
my $type = $self->get_type;
|
||||
if (exists $Types[$type]) {
|
||||
return $Types[$type];
|
||||
}
|
||||
else {
|
||||
# Return unrecognised types as REG_<number>
|
||||
# REGEDIT displays them as formatted hex numbers, e.g. 0x1f4
|
||||
return "REG_$type";
|
||||
}
|
||||
}
|
||||
|
||||
sub get_data_as_string {
|
||||
my $self = shift;
|
||||
|
||||
my $type = $self->get_type;
|
||||
my $data = $self->get_data;
|
||||
if (!defined($data)) {
|
||||
return '(invalid data)';
|
||||
}
|
||||
elsif (length($data) == 0) {
|
||||
return '(no data)';
|
||||
}
|
||||
elsif ($type == REG_SZ || $type == REG_EXPAND_SZ) {
|
||||
return $data;
|
||||
}
|
||||
elsif ($type == REG_MULTI_SZ) {
|
||||
my @data = $self->get_data;
|
||||
my $i = 0;
|
||||
return join(' ', map { "[" . $i++ . "] $_" } @data);
|
||||
}
|
||||
elsif ($type == REG_DWORD || $type == REG_DWORD_BIG_ENDIAN) {
|
||||
return sprintf '0x%08x (%u)', $data, $data;
|
||||
}
|
||||
else {
|
||||
return join(' ', unpack('(H2)*', $data));
|
||||
}
|
||||
}
|
||||
|
||||
sub get_raw_data {
|
||||
my $self = shift;
|
||||
|
||||
return $self->{_data};
|
||||
}
|
||||
|
||||
sub as_string {
|
||||
my $self = shift;
|
||||
|
||||
my $name = $self->get_name;
|
||||
$name = '(Default)' if $name eq '';
|
||||
my $type_as_string = $self->get_type_as_string;
|
||||
my $data_as_string = $self->get_data_as_string;
|
||||
return "$name ($type_as_string) = $data_as_string";
|
||||
}
|
||||
|
||||
sub print_summary {
|
||||
my $self = shift;
|
||||
|
||||
print $self->as_string, "\n";
|
||||
}
|
||||
|
||||
1;
|
540
thirdparty/rr-full/Parse/Win32Registry/Win95/File.pm
vendored
Normal file
540
thirdparty/rr-full/Parse/Win32Registry/Win95/File.pm
vendored
Normal file
@ -0,0 +1,540 @@
|
||||
package Parse::Win32Registry::Win95::File;
|
||||
|
||||
use strict;
|
||||
use warnings;
|
||||
|
||||
use base qw(Parse::Win32Registry::File);
|
||||
|
||||
use Carp;
|
||||
use File::Basename;
|
||||
use Parse::Win32Registry::Base qw(:all);
|
||||
use Parse::Win32Registry::Win95::Key;
|
||||
|
||||
use constant CREG_HEADER_LENGTH => 0x20;
|
||||
use constant OFFSET_TO_RGKN_BLOCK => 0x20;
|
||||
|
||||
sub new {
|
||||
my $class = shift;
|
||||
my $filename = shift or croak 'No filename specified';
|
||||
|
||||
open my $fh, '<', $filename or croak "Unable to open '$filename': $!";
|
||||
|
||||
# CREG Header
|
||||
# 0x00 dword = 'CREG' signature
|
||||
# 0x04
|
||||
# 0x08 dword = offset to first rgdb block
|
||||
# 0x0c
|
||||
# 0x10 word = number of rgdb blocks
|
||||
|
||||
my $bytes_read = sysread($fh, my $creg_header, CREG_HEADER_LENGTH);
|
||||
if ($bytes_read != CREG_HEADER_LENGTH) {
|
||||
warnf('Could not read registry file header');
|
||||
return;
|
||||
}
|
||||
|
||||
my ($creg_sig,
|
||||
$offset_to_first_rgdb_block,
|
||||
$num_rgdb_blocks) = unpack('a4x4Vx4v', $creg_header);
|
||||
|
||||
if ($creg_sig ne 'CREG') {
|
||||
warnf('Invalid registry file signature');
|
||||
return;
|
||||
}
|
||||
|
||||
my $self = {};
|
||||
$self->{_filehandle} = $fh;
|
||||
$self->{_filename} = $filename;
|
||||
$self->{_length} = (stat $fh)[7];
|
||||
$self->{_offset_to_first_rgdb_block} = $offset_to_first_rgdb_block;
|
||||
$self->{_num_rgdb_blocks} = $num_rgdb_blocks;
|
||||
bless $self, $class;
|
||||
|
||||
# get_rgkn will cache the rgkn block for subsequent calls
|
||||
my $rgkn_block = $self->get_rgkn;
|
||||
return if !defined $rgkn_block; # warning will already have been made
|
||||
|
||||
# Index the rgdb entries by id for faster look up
|
||||
$self->_index_rgdb_entries;
|
||||
|
||||
return $self;
|
||||
}
|
||||
|
||||
sub get_timestamp {
|
||||
return undef;
|
||||
}
|
||||
|
||||
sub get_timestamp_as_string {
|
||||
return iso8601(undef);
|
||||
}
|
||||
|
||||
sub get_embedded_filename {
|
||||
return undef;
|
||||
}
|
||||
|
||||
sub get_root_key {
|
||||
my $self = shift;
|
||||
|
||||
return $self->get_rgkn->get_root_key;
|
||||
}
|
||||
|
||||
sub get_virtual_root_key {
|
||||
my $self = shift;
|
||||
my $fake_root = shift;
|
||||
|
||||
my $root_key = $self->get_root_key;
|
||||
return if !defined $root_key;
|
||||
|
||||
if (!defined $fake_root) {
|
||||
# guess virtual root from filename
|
||||
my $filename = basename $self->{_filename};
|
||||
|
||||
if ($filename =~ /USER/i) {
|
||||
$fake_root = 'HKEY_USERS';
|
||||
}
|
||||
elsif ($filename =~ /SYSTEM/i) {
|
||||
$fake_root = 'HKEY_LOCAL_MACHINE';
|
||||
}
|
||||
else {
|
||||
$fake_root = 'HKEY_UNKNOWN';
|
||||
}
|
||||
}
|
||||
|
||||
$root_key->{_name} = $fake_root;
|
||||
$root_key->{_key_path} = $fake_root;
|
||||
|
||||
return $root_key;
|
||||
}
|
||||
|
||||
sub _index_rgdb_entries {
|
||||
my $self = shift;
|
||||
|
||||
my %index = ();
|
||||
|
||||
# Build index of rgdb key entries
|
||||
# Entries are only included if $key_block_num matches $rgdb_block_num
|
||||
my $rgdb_block_num = 0;
|
||||
my $rgdb_iter = $self->get_rgdb_iterator;
|
||||
while (my $rgdb = $rgdb_iter->()) {
|
||||
my $rgdb_key_iter = $rgdb->get_key_iterator;
|
||||
while (my $rgdb_key = $rgdb_key_iter->()) {
|
||||
my $key_id = $rgdb_key->{_id};
|
||||
my $key_block_num = $key_id >> 16;
|
||||
if ($rgdb_block_num == $key_block_num) {
|
||||
$index{$key_id} = $rgdb_key;
|
||||
}
|
||||
}
|
||||
$rgdb_block_num++;
|
||||
}
|
||||
|
||||
$self->{_rgdb_index} = \%index;
|
||||
}
|
||||
|
||||
sub _dump_rgdb_index {
|
||||
my $self = shift;
|
||||
|
||||
my $rgdb_index = $self->{_rgdb_index};
|
||||
|
||||
foreach my $key_id (sort { $a <=> $b } keys %$rgdb_index) {
|
||||
my $rgdb_key = $rgdb_index->{$key_id};
|
||||
printf qq{id=0x%x 0x%x,%d/%d "%s" vals=%d\n},
|
||||
$key_id,
|
||||
$rgdb_key->{_offset},
|
||||
$rgdb_key->{_length_used},
|
||||
$rgdb_key->{_length},
|
||||
$rgdb_key->{_name},
|
||||
$rgdb_key->{_num_values};
|
||||
}
|
||||
}
|
||||
|
||||
sub get_rgkn {
|
||||
my $self = shift;
|
||||
|
||||
# Return cached rgkn block if present
|
||||
if (defined $self->{_rgkn}) {
|
||||
return $self->{_rgkn};
|
||||
}
|
||||
|
||||
my $offset = OFFSET_TO_RGKN_BLOCK;
|
||||
my $rgkn_block = Parse::Win32Registry::Win95::RGKN->new($self, $offset);
|
||||
$self->{_rgkn} = $rgkn_block;
|
||||
return $rgkn_block;
|
||||
}
|
||||
|
||||
sub get_rgdb_iterator {
|
||||
my $self = shift;
|
||||
|
||||
my $offset_to_next_rgdb_block = $self->{_offset_to_first_rgdb_block};
|
||||
my $num_rgdb_blocks = $self->{_num_rgdb_blocks};
|
||||
|
||||
my $end_of_file = $self->{_length};
|
||||
|
||||
my $rgdb_block_num = 0;
|
||||
|
||||
return Parse::Win32Registry::Iterator->new(sub {
|
||||
if ($offset_to_next_rgdb_block > $end_of_file) {
|
||||
return; # no more rgdb blocks
|
||||
}
|
||||
if ($rgdb_block_num >= $num_rgdb_blocks) {
|
||||
return; # no more rgdb blocks
|
||||
}
|
||||
$rgdb_block_num++;
|
||||
if (my $rgdb_block = Parse::Win32Registry::Win95::RGDB->new($self,
|
||||
$offset_to_next_rgdb_block))
|
||||
{
|
||||
return unless $rgdb_block->get_length > 0;
|
||||
$offset_to_next_rgdb_block += $rgdb_block->get_length;
|
||||
return $rgdb_block;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
sub get_block_iterator {
|
||||
my $self = shift;
|
||||
|
||||
my $rgdb_iter;
|
||||
|
||||
return Parse::Win32Registry::Iterator->new(sub {
|
||||
if (!defined $rgdb_iter) {
|
||||
$rgdb_iter = $self->get_rgdb_iterator;
|
||||
return $self->get_rgkn;
|
||||
}
|
||||
return $rgdb_iter->();
|
||||
});
|
||||
}
|
||||
|
||||
*get_hbin_iterator = \&get_block_iterator;
|
||||
|
||||
|
||||
package Parse::Win32Registry::Win95::RGKN;
|
||||
|
||||
use strict;
|
||||
use warnings;
|
||||
|
||||
use base qw(Parse::Win32Registry::Entry);
|
||||
|
||||
use Carp;
|
||||
use Parse::Win32Registry::Base qw(:all);
|
||||
|
||||
use constant RGKN_HEADER_LENGTH => 0x20;
|
||||
use constant OFFSET_TO_RGKN_BLOCK => 0x20;
|
||||
|
||||
sub new {
|
||||
my $class = shift;
|
||||
my $regfile = shift;
|
||||
my $offset = shift || OFFSET_TO_RGKN_BLOCK;
|
||||
|
||||
croak 'Missing registry file' if !defined $regfile;
|
||||
croak 'Missing offset' if !defined $offset;
|
||||
|
||||
my $fh = $regfile->get_filehandle;
|
||||
|
||||
# RGKN Block Header
|
||||
# 0x0 dword = 'RGKN' signature
|
||||
# 0x4 dword = length of rgkn block
|
||||
# 0x8 dword = offset to root key entry (relative to start of rgkn block)
|
||||
|
||||
sysseek($fh, $offset, 0);
|
||||
my $bytes_read = sysread($fh, my $rgkn_header, RGKN_HEADER_LENGTH);
|
||||
if ($bytes_read != RGKN_HEADER_LENGTH) {
|
||||
warnf('Could not read RGKN header at 0x%x', $offset);
|
||||
return;
|
||||
}
|
||||
|
||||
my ($sig,
|
||||
$rgkn_block_length,
|
||||
$offset_to_root_key) = unpack('a4VV', $rgkn_header);
|
||||
|
||||
if ($sig ne 'RGKN') {
|
||||
warnf('Invalid RGKN block signature at 0x%x', $offset);
|
||||
return;
|
||||
}
|
||||
|
||||
$offset_to_root_key += $offset;
|
||||
|
||||
my $self = {};
|
||||
$self->{_regfile} = $regfile;
|
||||
$self->{_offset} = $offset;
|
||||
$self->{_length} = $rgkn_block_length;
|
||||
$self->{_header_length} = RGKN_HEADER_LENGTH;
|
||||
$self->{_allocated} = 1;
|
||||
$self->{_tag} = 'rgkn block';
|
||||
$self->{_offset_to_root_key} = $offset_to_root_key;
|
||||
bless $self, $class;
|
||||
|
||||
return $self;
|
||||
}
|
||||
|
||||
sub get_root_key {
|
||||
my $self = shift;
|
||||
|
||||
my $regfile = $self->{_regfile};
|
||||
my $offset_to_root_key = $self->{_offset_to_root_key};
|
||||
|
||||
my $root_key = Parse::Win32Registry::Win95::Key->new($regfile,
|
||||
$offset_to_root_key);
|
||||
return $root_key;
|
||||
}
|
||||
|
||||
sub get_entry_iterator {
|
||||
my $self = shift;
|
||||
|
||||
my $root_key = $self->get_root_key;
|
||||
|
||||
# In the unlikely event there is no root key, return an empty iterator
|
||||
if (defined $root_key) {
|
||||
return $root_key->get_subtree_iterator;
|
||||
}
|
||||
else {
|
||||
return Parse::Win32Registry::Iterator->new(sub {});
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
package Parse::Win32Registry::Win95::RGDB;
|
||||
|
||||
use base qw(Parse::Win32Registry::Entry);
|
||||
|
||||
use Carp;
|
||||
use Parse::Win32Registry::Base qw(:all);
|
||||
|
||||
use constant RGDB_HEADER_LENGTH => 0x20;
|
||||
|
||||
sub new {
|
||||
my $class = shift;
|
||||
my $regfile = shift;
|
||||
my $offset = shift;
|
||||
|
||||
croak 'Missing registry file' if !defined $regfile;
|
||||
croak 'Missing offset' if !defined $offset;
|
||||
|
||||
my $fh = $regfile->get_filehandle;
|
||||
|
||||
# RGDB Block Header
|
||||
# 0x0 dword = 'RDGB' signature
|
||||
# 0x4 dword = length of rgdb block
|
||||
|
||||
sysseek($fh, $offset, 0);
|
||||
my $bytes_read = sysread($fh, my $rgdb_header, RGDB_HEADER_LENGTH);
|
||||
if ($bytes_read != RGDB_HEADER_LENGTH) {
|
||||
return;
|
||||
}
|
||||
|
||||
my ($sig,
|
||||
$rgdb_block_length) = unpack('a4V', $rgdb_header);
|
||||
|
||||
if ($sig ne 'RGDB') {
|
||||
return;
|
||||
}
|
||||
|
||||
my $self = {};
|
||||
$self->{_regfile} = $regfile;
|
||||
$self->{_offset} = $offset;
|
||||
$self->{_length} = $rgdb_block_length;
|
||||
$self->{_header_length} = RGDB_HEADER_LENGTH;
|
||||
$self->{_allocated} = 1;
|
||||
$self->{_tag} = 'rgdb block';
|
||||
bless $self, $class;
|
||||
|
||||
return $self;
|
||||
}
|
||||
|
||||
sub get_key_iterator {
|
||||
my $self = shift;
|
||||
|
||||
my $regfile = $self->{_regfile};
|
||||
my $offset = $self->{_offset};
|
||||
my $length = $self->{_length};
|
||||
|
||||
my $offset_to_next_rgdb_key = $offset + RGDB_HEADER_LENGTH;
|
||||
my $end_of_rgdb_block = $offset + $length;
|
||||
|
||||
return Parse::Win32Registry::Iterator->new(sub {
|
||||
if ($offset_to_next_rgdb_key >= $end_of_rgdb_block) {
|
||||
return;
|
||||
}
|
||||
if (my $rgdb_key = Parse::Win32Registry::Win95::RGDBKey->new($regfile,
|
||||
$offset_to_next_rgdb_key))
|
||||
{
|
||||
return unless $rgdb_key->get_length > 0;
|
||||
$offset_to_next_rgdb_key += $rgdb_key->get_length;
|
||||
|
||||
# Check rgdb key has not run past end of rgdb block
|
||||
if ($offset_to_next_rgdb_key > $end_of_rgdb_block) {
|
||||
return;
|
||||
}
|
||||
return $rgdb_key;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
sub get_entry_iterator {
|
||||
my $self = shift;
|
||||
|
||||
my $value_iter;
|
||||
my $key_iter = $self->get_key_iterator;
|
||||
|
||||
return Parse::Win32Registry::Iterator->new(sub {
|
||||
if (defined $value_iter) {
|
||||
my $value = $value_iter->();
|
||||
if (defined $value) {
|
||||
return $value;
|
||||
}
|
||||
}
|
||||
|
||||
my $key = $key_iter->();
|
||||
if (!defined $key) {
|
||||
return; # key iterator finished
|
||||
}
|
||||
|
||||
$value_iter = $key->get_value_iterator;
|
||||
return $key;
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
package Parse::Win32Registry::Win95::RGDBKey;
|
||||
|
||||
use base qw(Parse::Win32Registry::Entry);
|
||||
|
||||
use Carp;
|
||||
use Encode;
|
||||
use Parse::Win32Registry::Base qw(:all);
|
||||
|
||||
use constant RGDB_ENTRY_HEADER_LENGTH => 0x14;
|
||||
|
||||
sub new {
|
||||
my $class = shift;
|
||||
my $regfile = shift;
|
||||
my $offset = shift;
|
||||
|
||||
croak 'Missing registry file' if !defined $regfile;
|
||||
croak 'Missing offset' if !defined $offset;
|
||||
|
||||
my $fh = $regfile->get_filehandle;
|
||||
|
||||
# RGDB Key Entry
|
||||
# 0x00 dword = length of rgdb entry / offset to next rgdb entry
|
||||
# (this length includes any following value entries)
|
||||
# 0x04 dword = id (top word = block num, bottom word = id)
|
||||
# 0x08 dword = bytes used (unpacked, but not used)
|
||||
# 0x0c word = key name length
|
||||
# 0x0e word = number of values
|
||||
# 0x10 dword
|
||||
# 0x14 = key name [for key name length bytes]
|
||||
# followed immediately by any RGDB Value Entries belonging to this key
|
||||
|
||||
sysseek($fh, $offset, 0);
|
||||
my $bytes_read = sysread($fh, my $rgdb_key_entry, RGDB_ENTRY_HEADER_LENGTH);
|
||||
if ($bytes_read != RGDB_ENTRY_HEADER_LENGTH) {
|
||||
return;
|
||||
}
|
||||
|
||||
my ($length,
|
||||
$key_id,
|
||||
$length_used,
|
||||
$name_length,
|
||||
$num_values) = unpack('VVVvv', $rgdb_key_entry);
|
||||
|
||||
$bytes_read = sysread($fh, my $name, $name_length);
|
||||
if ($bytes_read != $name_length) {
|
||||
return;
|
||||
}
|
||||
$name = decode($Parse::Win32Registry::Base::CODEPAGE, $name);
|
||||
|
||||
# Calculate the length of the entry's key header
|
||||
my $header_length = RGDB_ENTRY_HEADER_LENGTH + $name_length;
|
||||
|
||||
# Check for invalid/unused entries
|
||||
if ($key_id == 0xffffffff || $length_used == 0xffffffff
|
||||
|| $header_length > $length)
|
||||
{
|
||||
$name = '';
|
||||
$header_length = RGDB_ENTRY_HEADER_LENGTH;
|
||||
}
|
||||
|
||||
my $self = {};
|
||||
$self->{_regfile} = $regfile;
|
||||
$self->{_offset} = $offset;
|
||||
$self->{_length} = $length;
|
||||
$self->{_length_used} = $length_used;
|
||||
$self->{_header_length} = $header_length;
|
||||
$self->{_allocated} = 1;
|
||||
$self->{_tag} = 'rgdb key';
|
||||
$self->{_id} = $key_id;
|
||||
$self->{_name} = $name;
|
||||
$self->{_name_length} = $name_length;
|
||||
$self->{_num_values} = $num_values;
|
||||
bless $self, $class;
|
||||
|
||||
return $self;
|
||||
}
|
||||
|
||||
sub get_name {
|
||||
my $self = shift;
|
||||
|
||||
return $self->{_name};
|
||||
}
|
||||
|
||||
sub parse_info {
|
||||
my $self = shift;
|
||||
|
||||
my $info = sprintf '0x%x rgdb key len=0x%x/0x%x "%s" id=0x%x vals=%d',
|
||||
$self->{_offset},
|
||||
$self->{_length_used},
|
||||
$self->{_length},
|
||||
$self->{_name},
|
||||
$self->{_id},
|
||||
$self->{_num_values};
|
||||
|
||||
return $info;
|
||||
}
|
||||
|
||||
sub get_value_iterator {
|
||||
my $self = shift;
|
||||
|
||||
my $regfile = $self->{_regfile};
|
||||
|
||||
my $num_values_remaining = $self->{_num_values};
|
||||
|
||||
my $offset = $self->{_offset};
|
||||
|
||||
# offset_to_next_rgdb_value can only be set to a valid offset
|
||||
# if num_values_remaining > 0
|
||||
my $offset_to_next_rgdb_value = 0xffffffff;
|
||||
if ($num_values_remaining > 0) {
|
||||
$offset_to_next_rgdb_value = $offset
|
||||
+ $self->{_header_length};
|
||||
}
|
||||
|
||||
my $end_of_rgdb_key = $offset + $self->{_length};
|
||||
|
||||
# don't attempt to return values if id is invalid...
|
||||
if ($self->{_id} == 0xffffffff) {
|
||||
$num_values_remaining = 0;
|
||||
}
|
||||
|
||||
return Parse::Win32Registry::Iterator->new(sub {
|
||||
if ($num_values_remaining-- <= 0) {
|
||||
return;
|
||||
}
|
||||
if ($offset_to_next_rgdb_value == 0xffffffff) {
|
||||
return;
|
||||
}
|
||||
if ($offset_to_next_rgdb_value > $end_of_rgdb_key) {
|
||||
return;
|
||||
}
|
||||
if (my $value = Parse::Win32Registry::Win95::Value->new($regfile,
|
||||
$offset_to_next_rgdb_value))
|
||||
{
|
||||
return unless $value->get_length > 0;
|
||||
$offset_to_next_rgdb_value += $value->get_length;
|
||||
return $value;
|
||||
}
|
||||
else {
|
||||
return; # no more values
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
1;
|
||||
|
207
thirdparty/rr-full/Parse/Win32Registry/Win95/Key.pm
vendored
Normal file
207
thirdparty/rr-full/Parse/Win32Registry/Win95/Key.pm
vendored
Normal file
@ -0,0 +1,207 @@
|
||||
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;
|
177
thirdparty/rr-full/Parse/Win32Registry/Win95/Value.pm
vendored
Normal file
177
thirdparty/rr-full/Parse/Win32Registry/Win95/Value.pm
vendored
Normal file
@ -0,0 +1,177 @@
|
||||
package Parse::Win32Registry::Win95::Value;
|
||||
|
||||
use strict;
|
||||
use warnings;
|
||||
|
||||
use base qw(Parse::Win32Registry::Value);
|
||||
|
||||
use Carp;
|
||||
use Encode;
|
||||
use Parse::Win32Registry::Base qw(:all);
|
||||
|
||||
use constant RGDB_VALUE_HEADER_LENGTH => 0xc;
|
||||
|
||||
sub new {
|
||||
my $class = shift;
|
||||
my $regfile = shift;
|
||||
my $offset = shift; # offset to RGDB value entry
|
||||
|
||||
croak 'Missing registry file' if !defined $regfile;
|
||||
croak 'Missing offset' if !defined $offset;
|
||||
|
||||
my $fh = $regfile->get_filehandle;
|
||||
|
||||
# RGDB Value Entry
|
||||
# 0x00 dword = value type
|
||||
# 0x04
|
||||
# 0x08 word = value name length
|
||||
# 0x0a word = value data length
|
||||
# 0x0c = value name [for name length bytes]
|
||||
# + value data [for data length bytes]
|
||||
# Value type may just be a word, not a dword;
|
||||
# following word always appears to be zero.
|
||||
|
||||
sysseek($fh, $offset, 0);
|
||||
my $bytes_read = sysread($fh, my $rgdb_value_entry,
|
||||
RGDB_VALUE_HEADER_LENGTH);
|
||||
if ($bytes_read != RGDB_VALUE_HEADER_LENGTH) {
|
||||
warnf('Could not read RGDB value at 0x%x', $offset);
|
||||
return;
|
||||
}
|
||||
|
||||
my ($type,
|
||||
$name_length,
|
||||
$data_length) = unpack('Vx4vv', $rgdb_value_entry);
|
||||
|
||||
$bytes_read = sysread($fh, my $name, $name_length);
|
||||
if ($bytes_read != $name_length) {
|
||||
warnf('Could not read name for RGDB value at 0x%x', $offset);
|
||||
return;
|
||||
}
|
||||
$name = decode($Parse::Win32Registry::Base::CODEPAGE, $name);
|
||||
|
||||
$bytes_read = sysread($fh, my $data, $data_length);
|
||||
if ($bytes_read != $data_length) {
|
||||
warnf('Could not read data for RGDB value at 0x%x', $offset);
|
||||
return;
|
||||
}
|
||||
|
||||
my $self = {};
|
||||
$self->{_regfile} = $regfile;
|
||||
$self->{_offset} = $offset;
|
||||
$self->{_length} = RGDB_VALUE_HEADER_LENGTH + $name_length + $data_length;
|
||||
$self->{_allocated} = 1;
|
||||
$self->{_tag} = 'rgdb value';
|
||||
$self->{_name} = $name;
|
||||
$self->{_name_length} = $name_length;
|
||||
$self->{_type} = $type;
|
||||
$self->{_data} = $data;
|
||||
$self->{_data_length} = $data_length;
|
||||
bless $self, $class;
|
||||
|
||||
return $self;
|
||||
}
|
||||
|
||||
sub get_data {
|
||||
my $self = shift;
|
||||
|
||||
my $type = $self->get_type;
|
||||
|
||||
my $data = $self->{_data};
|
||||
return if !defined $data; # actually, Win95 value data is always defined
|
||||
|
||||
# apply decoding to appropriate data types
|
||||
if ($type == REG_DWORD) {
|
||||
if (length($data) == 4) {
|
||||
$data = unpack('V', $data);
|
||||
}
|
||||
else {
|
||||
# incorrect length for dword data
|
||||
$data = undef;
|
||||
}
|
||||
}
|
||||
elsif ($type == REG_DWORD_BIG_ENDIAN) {
|
||||
if (length($data) == 4) {
|
||||
$data = unpack('N', $data);
|
||||
}
|
||||
else {
|
||||
# incorrect length for dword data
|
||||
$data = undef;
|
||||
}
|
||||
}
|
||||
elsif ($type == REG_SZ || $type == REG_EXPAND_SZ) {
|
||||
# Snip off any terminating null.
|
||||
# Typically, REG_SZ values will not have a terminating null,
|
||||
# while REG_EXPAND_SZ values will have a terminating null
|
||||
chop $data if substr($data, -1, 1) eq "\0";
|
||||
}
|
||||
elsif ($type == REG_MULTI_SZ) {
|
||||
# Snip off any terminating nulls
|
||||
chop $data if substr($data, -1, 1) eq "\0";
|
||||
chop $data if substr($data, -1, 1) eq "\0";
|
||||
my @multi_sz = split("\0", $data, -1);
|
||||
# Make sure there is at least one empty string
|
||||
@multi_sz = ('') if @multi_sz == 0;
|
||||
return wantarray ? @multi_sz : join($", @multi_sz);
|
||||
}
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
sub as_regedit_export {
|
||||
my $self = shift;
|
||||
my $version = shift || 5;
|
||||
|
||||
my $name = $self->get_name;
|
||||
my $export = $name eq '' ? '@=' : '"' . $name . '"=';
|
||||
|
||||
my $type = $self->get_type;
|
||||
|
||||
# XXX
|
||||
# if (!defined $self->{_data}) {
|
||||
# $name = $name eq '' ? '@' : qq{"$name"};
|
||||
# return qq{; $name=(invalid data)\n};
|
||||
# }
|
||||
|
||||
if ($type == REG_SZ) {
|
||||
$export .= '"' . $self->get_data . '"';
|
||||
$export .= "\n";
|
||||
}
|
||||
elsif ($type == REG_BINARY) {
|
||||
$export .= 'hex:';
|
||||
$export .= format_octets($self->{_data}, length($export));
|
||||
}
|
||||
elsif ($type == REG_DWORD) {
|
||||
my $data = $self->get_data;
|
||||
$export .= defined($data)
|
||||
? sprintf("dword:%08x", $data)
|
||||
: "dword:";
|
||||
$export .= "\n";
|
||||
}
|
||||
elsif ($type == REG_EXPAND_SZ || $type == REG_MULTI_SZ) {
|
||||
my $data = $version == 4
|
||||
? $self->{_data} # raw data
|
||||
: encode("UCS-2LE", $self->{_data}); # ansi->unicode
|
||||
$export .= sprintf("hex(%x):", $type);
|
||||
$export .= format_octets($data, length($export));
|
||||
}
|
||||
else {
|
||||
$export .= sprintf("hex(%x):", $type);
|
||||
$export .= format_octets($self->{_data}, length($export));
|
||||
}
|
||||
return $export;
|
||||
}
|
||||
|
||||
sub parse_info {
|
||||
my $self = shift;
|
||||
|
||||
my $info = sprintf '0x%x rgdb value len=0x%x "%s" type=%d data,len=0x%x',
|
||||
$self->{_offset},
|
||||
$self->{_length},
|
||||
$self->{_name},
|
||||
$self->{_type},
|
||||
$self->{_data_length};
|
||||
return $info;
|
||||
}
|
||||
|
||||
1;
|
109
thirdparty/rr-full/Parse/Win32Registry/WinNT/Entry.pm
vendored
Normal file
109
thirdparty/rr-full/Parse/Win32Registry/WinNT/Entry.pm
vendored
Normal file
@ -0,0 +1,109 @@
|
||||
package Parse::Win32Registry::WinNT::Entry;
|
||||
|
||||
use strict;
|
||||
use warnings;
|
||||
|
||||
use base qw(Parse::Win32Registry::Entry);
|
||||
|
||||
use Carp;
|
||||
use Parse::Win32Registry::Base qw(:all);
|
||||
use Parse::Win32Registry::WinNT::Key;
|
||||
use Parse::Win32Registry::WinNT::Value;
|
||||
use Parse::Win32Registry::WinNT::Security;
|
||||
|
||||
sub new {
|
||||
my $class = shift;
|
||||
my $regfile = shift;
|
||||
my $offset = shift;
|
||||
|
||||
croak 'Missing registry file' if !defined $regfile;
|
||||
croak 'Missing offset' if !defined $offset;
|
||||
|
||||
my $fh = $regfile->get_filehandle;
|
||||
|
||||
sysseek($fh, $offset, 0);
|
||||
my $bytes_read = sysread($fh, my $entry_header, 8);
|
||||
if ($bytes_read != 8) {
|
||||
return;
|
||||
}
|
||||
|
||||
my ($length,
|
||||
$tag) = unpack('Va2', $entry_header);
|
||||
|
||||
my $allocated = 0;
|
||||
if ($length > 0x7fffffff) {
|
||||
$allocated = 1;
|
||||
$length = (0xffffffff - $length) + 1;
|
||||
}
|
||||
|
||||
$tag = '' if $tag !~ /(nk|vk|lh|lf|li|ri|sk)/;
|
||||
|
||||
if ($tag eq 'nk') {
|
||||
if (my $key = Parse::Win32Registry::WinNT::Key->new($regfile,
|
||||
$offset))
|
||||
{
|
||||
$key->regenerate_path;
|
||||
return $key;
|
||||
}
|
||||
}
|
||||
elsif ($tag eq 'vk') {
|
||||
if (my $value = Parse::Win32Registry::WinNT::Value->new($regfile,
|
||||
$offset))
|
||||
{
|
||||
return $value;
|
||||
}
|
||||
}
|
||||
elsif ($tag eq 'sk') {
|
||||
if (my $value = Parse::Win32Registry::WinNT::Security->new($regfile,
|
||||
$offset))
|
||||
{
|
||||
return $value;
|
||||
}
|
||||
}
|
||||
|
||||
my $self = {};
|
||||
$self->{_regfile} = $regfile,
|
||||
$self->{_offset} = $offset,
|
||||
$self->{_length} = $length,
|
||||
$self->{_tag} = $tag,
|
||||
$self->{_allocated} = $allocated,
|
||||
bless $self, $class;
|
||||
|
||||
return $self;
|
||||
}
|
||||
|
||||
sub as_string {
|
||||
my $self = shift;
|
||||
|
||||
my $tag = $self->{_tag};
|
||||
if ($tag eq 'nk') {
|
||||
return '(key entry)';
|
||||
}
|
||||
elsif ($tag eq 'vk') {
|
||||
return '(value entry)';
|
||||
}
|
||||
elsif ($tag eq 'sk') {
|
||||
return '(security entry)';
|
||||
}
|
||||
elsif ($tag =~ /(lh|lf|li|ri)/) {
|
||||
return '(subkey list entry)';
|
||||
}
|
||||
return '(unidentified entry)';
|
||||
}
|
||||
|
||||
sub parse_info {
|
||||
my $self = shift;
|
||||
|
||||
my $tag = $self->{_tag};
|
||||
$tag = defined($tag) && $tag ne ''
|
||||
? $tag . ' '
|
||||
: '.. ';
|
||||
my $info = sprintf '0x%x %slen=0x%x alloc=%d',
|
||||
$self->{_offset},
|
||||
$tag,
|
||||
$self->{_length},
|
||||
$self->{_allocated};
|
||||
return $info;
|
||||
}
|
||||
|
||||
1;
|
297
thirdparty/rr-full/Parse/Win32Registry/WinNT/File.pm
vendored
Normal file
297
thirdparty/rr-full/Parse/Win32Registry/WinNT/File.pm
vendored
Normal file
@ -0,0 +1,297 @@
|
||||
package Parse::Win32Registry::WinNT::File;
|
||||
|
||||
use strict;
|
||||
use warnings;
|
||||
|
||||
use base qw(Parse::Win32Registry::File);
|
||||
|
||||
use Carp;
|
||||
use Encode;
|
||||
use File::Basename;
|
||||
use Parse::Win32Registry::Base qw(:all);
|
||||
use Parse::Win32Registry::WinNT::Key;
|
||||
|
||||
use constant REGF_HEADER_LENGTH => 0x200;
|
||||
use constant OFFSET_TO_FIRST_HBIN => 0x1000;
|
||||
|
||||
sub new {
|
||||
my $class = shift;
|
||||
my $filename = shift or croak "No filename specified";
|
||||
|
||||
open my $fh, '<', $filename or croak "Unable to open '$filename': $!";
|
||||
|
||||
# 0x00 dword = 'regf' signature
|
||||
# 0x04 dword = seq1
|
||||
# 0x08 dword = seq2
|
||||
# 0x0c qword = timestamp
|
||||
# 0x14 dword = major version
|
||||
# 0x18 dword = minor version
|
||||
# 0x1c dword = type (0 = registry file, 1 = log file)
|
||||
# 0x20 dword = (1)
|
||||
# 0x24 dword = offset to root key
|
||||
# 0x28 dword = total length of all hbins (excludes header)
|
||||
# 0x2c dword = (1)
|
||||
# 0x30 = embedded filename
|
||||
|
||||
# Extracted offsets are always relative to first hbin
|
||||
|
||||
my $bytes_read = sysread($fh, my $regf_header, REGF_HEADER_LENGTH);
|
||||
if ($bytes_read != REGF_HEADER_LENGTH) {
|
||||
warnf('Could not read registry file header');
|
||||
return;
|
||||
}
|
||||
|
||||
my ($regf_sig,
|
||||
$seq1,
|
||||
$seq2,
|
||||
$timestamp,
|
||||
$major_version,
|
||||
$minor_version,
|
||||
$type,
|
||||
$offset_to_root_key,
|
||||
$total_hbin_length,
|
||||
$embedded_filename,
|
||||
) = unpack('a4VVa8VVVx4VVx4a64', $regf_header);
|
||||
|
||||
$offset_to_root_key += OFFSET_TO_FIRST_HBIN;
|
||||
|
||||
if ($regf_sig ne 'regf') {
|
||||
warnf('Invalid registry file signature');
|
||||
return;
|
||||
}
|
||||
|
||||
$embedded_filename = unpack('Z*', decode('UCS-2LE', $embedded_filename));
|
||||
|
||||
# The header checksum is the xor of the first 127 dwords.
|
||||
# The checksum is stored in the 128th dword, at offset 0x1fc (508).
|
||||
my $checksum = 0;
|
||||
foreach my $x (unpack('V127', $regf_header)) {
|
||||
$checksum ^= $x;
|
||||
}
|
||||
my $embedded_checksum = unpack('x508V', $regf_header);
|
||||
if ($checksum != $embedded_checksum) {
|
||||
warnf('Invalid checksum for registry file header');
|
||||
}
|
||||
|
||||
my $self = {};
|
||||
$self->{_filehandle} = $fh;
|
||||
$self->{_filename} = $filename;
|
||||
$self->{_length} = (stat $fh)[7];
|
||||
$self->{_offset_to_root_key} = $offset_to_root_key;
|
||||
$self->{_timestamp} = unpack_windows_time($timestamp);
|
||||
$self->{_embedded_filename} = $embedded_filename;
|
||||
$self->{_seq1} = $seq1;
|
||||
$self->{_seq2} = $seq2;
|
||||
$self->{_version} = "$major_version.$minor_version";
|
||||
$self->{_type} = $type;
|
||||
$self->{_total_hbin_length} = $total_hbin_length;
|
||||
$self->{_embedded_checksum} = $embedded_checksum;
|
||||
$self->{_security_cache} = {}; # comment out to disable cache
|
||||
bless $self, $class;
|
||||
|
||||
return $self;
|
||||
}
|
||||
|
||||
sub get_root_key {
|
||||
my $self = shift;
|
||||
|
||||
my $offset_to_root_key = $self->{_offset_to_root_key};
|
||||
|
||||
my $root_key = Parse::Win32Registry::WinNT::Key->new($self,
|
||||
$offset_to_root_key);
|
||||
return $root_key;
|
||||
}
|
||||
|
||||
sub get_virtual_root_key {
|
||||
my $self = shift;
|
||||
my $fake_root = shift;
|
||||
|
||||
my $root_key = $self->get_root_key;
|
||||
return if !defined $root_key;
|
||||
|
||||
if (!defined $fake_root) {
|
||||
# guess virtual root from filename
|
||||
my $filename = basename $self->{_filename};
|
||||
|
||||
if ($filename =~ /NTUSER/i) {
|
||||
$fake_root = 'HKEY_CURRENT_USER';
|
||||
}
|
||||
elsif ($filename =~ /USRCLASS/i) {
|
||||
$fake_root = 'HKEY_CLASSES_ROOT';
|
||||
}
|
||||
elsif ($filename =~ /SOFTWARE/i) {
|
||||
$fake_root = 'HKEY_LOCAL_MACHINE\SOFTWARE';
|
||||
}
|
||||
elsif ($filename =~ /SYSTEM/i) {
|
||||
$fake_root = 'HKEY_LOCAL_MACHINE\SYSTEM';
|
||||
}
|
||||
elsif ($filename =~ /SAM/i) {
|
||||
$fake_root = 'HKEY_LOCAL_MACHINE\SAM';
|
||||
}
|
||||
elsif ($filename =~ /SECURITY/i) {
|
||||
$fake_root = 'HKEY_LOCAL_MACHINE\SECURITY';
|
||||
}
|
||||
else {
|
||||
$fake_root = 'HKEY_UNKNOWN';
|
||||
}
|
||||
}
|
||||
|
||||
$root_key->{_name} = $fake_root;
|
||||
$root_key->{_key_path} = $fake_root;
|
||||
|
||||
return $root_key;
|
||||
}
|
||||
|
||||
sub get_timestamp {
|
||||
my $self = shift;
|
||||
|
||||
return $self->{_timestamp};
|
||||
}
|
||||
|
||||
sub get_timestamp_as_string {
|
||||
my $self = shift;
|
||||
|
||||
return iso8601($self->{_timestamp});
|
||||
}
|
||||
|
||||
sub get_embedded_filename {
|
||||
my $self = shift;
|
||||
|
||||
return $self->{_embedded_filename};
|
||||
}
|
||||
|
||||
sub get_block_iterator {
|
||||
my $self = shift;
|
||||
|
||||
my $offset_to_next_hbin = OFFSET_TO_FIRST_HBIN;
|
||||
my $end_of_file = $self->{_length};
|
||||
|
||||
return Parse::Win32Registry::Iterator->new(sub {
|
||||
if ($offset_to_next_hbin > $end_of_file) {
|
||||
return; # no more hbins
|
||||
}
|
||||
if (my $hbin = Parse::Win32Registry::WinNT::Hbin->new($self,
|
||||
$offset_to_next_hbin))
|
||||
{
|
||||
return unless $hbin->get_length > 0;
|
||||
$offset_to_next_hbin += $hbin->get_length;
|
||||
return $hbin;
|
||||
}
|
||||
else {
|
||||
return; # no more hbins
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
*get_hbin_iterator = \&get_block_iterator;
|
||||
|
||||
sub _dump_security_cache {
|
||||
my $self = shift;
|
||||
|
||||
if (defined(my $cache = $self->{_security_cache})) {
|
||||
foreach my $offset (sort { $a <=> $b } keys %$cache) {
|
||||
my $security = $cache->{$offset};
|
||||
printf '0x%x %s\n', $offset, $security->as_string;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
package Parse::Win32Registry::WinNT::Hbin;
|
||||
|
||||
use strict;
|
||||
use warnings;
|
||||
|
||||
use base qw(Parse::Win32Registry::Entry);
|
||||
|
||||
use Carp;
|
||||
use Parse::Win32Registry::Base qw(:all);
|
||||
use Parse::Win32Registry::WinNT::Entry;
|
||||
|
||||
use constant HBIN_HEADER_LENGTH => 0x20;
|
||||
|
||||
sub new {
|
||||
my $class = shift;
|
||||
my $regfile = shift;
|
||||
my $offset = shift;
|
||||
|
||||
croak 'Missing registry file' if !defined $regfile;
|
||||
croak 'Missing offset' if !defined $offset;
|
||||
|
||||
my $fh = $regfile->get_filehandle;
|
||||
|
||||
# 0x00 dword = 'hbin' signature
|
||||
# 0x04 dword = offset from first hbin to this hbin
|
||||
# 0x08 dword = length of this hbin / relative offset to next hbin
|
||||
# 0x14 qword = timestamp (first hbin only)
|
||||
|
||||
# Extracted offsets are always relative to first hbin
|
||||
|
||||
sysseek($fh, $offset, 0);
|
||||
my $bytes_read = sysread($fh, my $hbin_header, HBIN_HEADER_LENGTH);
|
||||
if ($bytes_read != HBIN_HEADER_LENGTH) {
|
||||
return;
|
||||
}
|
||||
|
||||
my ($sig,
|
||||
$offset_to_hbin,
|
||||
$length,
|
||||
$timestamp) = unpack('a4VVx8a8x4', $hbin_header);
|
||||
|
||||
if ($sig ne 'hbin') {
|
||||
return;
|
||||
}
|
||||
|
||||
my $self = {};
|
||||
$self->{_regfile} = $regfile;
|
||||
$self->{_offset} = $offset;
|
||||
$self->{_length} = $length;
|
||||
$self->{_header_length} = HBIN_HEADER_LENGTH;
|
||||
$self->{_allocated} = 1;
|
||||
$self->{_tag} = $sig;
|
||||
$self->{_timestamp} = unpack_windows_time($timestamp);
|
||||
bless $self, $class;
|
||||
|
||||
return $self;
|
||||
}
|
||||
|
||||
sub get_timestamp {
|
||||
my $self = shift;
|
||||
|
||||
return $self->{_timestamp};
|
||||
}
|
||||
|
||||
sub get_timestamp_as_string {
|
||||
my $self = shift;
|
||||
|
||||
return iso8601($self->{_timestamp});
|
||||
}
|
||||
|
||||
sub get_entry_iterator {
|
||||
my $self = shift;
|
||||
|
||||
my $regfile = $self->{_regfile};
|
||||
my $offset = $self->{_offset};
|
||||
my $length = $self->{_length};
|
||||
|
||||
my $offset_to_next_entry = $offset + HBIN_HEADER_LENGTH;
|
||||
my $end_of_hbin = $offset + $length;
|
||||
|
||||
return Parse::Win32Registry::Iterator->new(sub {
|
||||
if ($offset_to_next_entry >= $end_of_hbin) {
|
||||
return; # no more entries
|
||||
}
|
||||
if (my $entry = Parse::Win32Registry::WinNT::Entry->new($regfile,
|
||||
$offset_to_next_entry))
|
||||
{
|
||||
return unless $entry->get_length > 0;
|
||||
$offset_to_next_entry += $entry->get_length;
|
||||
return $entry;
|
||||
}
|
||||
else {
|
||||
return; # no more entries
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
1;
|
444
thirdparty/rr-full/Parse/Win32Registry/WinNT/Key.pm
vendored
Normal file
444
thirdparty/rr-full/Parse/Win32Registry/WinNT/Key.pm
vendored
Normal file
@ -0,0 +1,444 @@
|
||||
package Parse::Win32Registry::WinNT::Key;
|
||||
|
||||
use strict;
|
||||
use warnings;
|
||||
|
||||
use base qw(Parse::Win32Registry::Key);
|
||||
|
||||
use Carp;
|
||||
use Encode;
|
||||
use Parse::Win32Registry::Base qw(:all);
|
||||
use Parse::Win32Registry::WinNT::Value;
|
||||
use Parse::Win32Registry::WinNT::Security;
|
||||
|
||||
use constant NK_HEADER_LENGTH => 0x50;
|
||||
use constant OFFSET_TO_FIRST_HBIN => 0x1000;
|
||||
|
||||
sub new {
|
||||
my $class = shift;
|
||||
my $regfile = shift;
|
||||
my $offset = shift; # offset to nk record relative to start of file
|
||||
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;
|
||||
|
||||
# 0x00 dword = key length (negative = allocated)
|
||||
# 0x04 word = 'nk' signature
|
||||
# 0x06 word = flags
|
||||
# 0x08 qword = timestamp
|
||||
# 0x10
|
||||
# 0x14 dword = offset to parent
|
||||
# 0x18 dword = number of subkeys
|
||||
# 0x1c
|
||||
# 0x20 dword = offset to subkey list (lf, lh, ri, li)
|
||||
# 0x24
|
||||
# 0x28 dword = number of values
|
||||
# 0x2c dword = offset to value list
|
||||
# 0x30 dword = offset to security
|
||||
# 0x34 dword = offset to class name
|
||||
# 0x38 dword = max subkey name length
|
||||
# 0x3c dword = max class name length
|
||||
# 0x40 dword = max value name length
|
||||
# 0x44 dword = max value data length
|
||||
# 0x48
|
||||
# 0x4c word = key name length
|
||||
# 0x4e word = class name length
|
||||
# 0x50 = key name [for key name length bytes]
|
||||
|
||||
# Extracted offsets are always relative to first hbin
|
||||
|
||||
sysseek($fh, $offset, 0);
|
||||
my $bytes_read = sysread($fh, my $nk_header, NK_HEADER_LENGTH);
|
||||
if ($bytes_read != NK_HEADER_LENGTH) {
|
||||
warnf('Could not read key at 0x%x', $offset);
|
||||
return;
|
||||
}
|
||||
|
||||
my ($length,
|
||||
$sig,
|
||||
$flags,
|
||||
$timestamp,
|
||||
$offset_to_parent,
|
||||
$num_subkeys,
|
||||
$offset_to_subkey_list,
|
||||
$num_values,
|
||||
$offset_to_value_list,
|
||||
$offset_to_security,
|
||||
$offset_to_class_name,
|
||||
$name_length,
|
||||
$class_name_length,
|
||||
) = unpack('Va2va8x4VVx4Vx4VVVVx20vv', $nk_header);
|
||||
|
||||
$offset_to_parent += OFFSET_TO_FIRST_HBIN
|
||||
if $offset_to_parent != 0xffffffff;
|
||||
$offset_to_subkey_list += OFFSET_TO_FIRST_HBIN
|
||||
if $offset_to_subkey_list != 0xffffffff;
|
||||
$offset_to_value_list += OFFSET_TO_FIRST_HBIN
|
||||
if $offset_to_value_list != 0xffffffff;
|
||||
$offset_to_security += OFFSET_TO_FIRST_HBIN
|
||||
if $offset_to_security != 0xffffffff;
|
||||
$offset_to_class_name += OFFSET_TO_FIRST_HBIN
|
||||
if $offset_to_class_name != 0xffffffff;
|
||||
|
||||
my $allocated = 0;
|
||||
if ($length > 0x7fffffff) {
|
||||
$allocated = 1;
|
||||
$length = (0xffffffff - $length) + 1;
|
||||
}
|
||||
# allocated should be true
|
||||
|
||||
if ($length < NK_HEADER_LENGTH) {
|
||||
warnf('Invalid value entry length at 0x%x', $offset);
|
||||
return;
|
||||
}
|
||||
|
||||
if ($sig ne 'nk') {
|
||||
warnf('Invalid signature for key at 0x%x', $offset);
|
||||
return;
|
||||
}
|
||||
|
||||
$bytes_read = sysread($fh, my $name, $name_length);
|
||||
if ($bytes_read != $name_length) {
|
||||
warnf('Could not read name for key at 0x%x', $offset);
|
||||
return;
|
||||
}
|
||||
|
||||
if ($flags & 0x20) {
|
||||
$name = decode($Parse::Win32Registry::Base::CODEPAGE, $name);
|
||||
}
|
||||
else {
|
||||
$name = decode('UCS-2LE', $name);
|
||||
}
|
||||
|
||||
my $key_path = (defined $parent_key_path)
|
||||
? "$parent_key_path\\$name"
|
||||
: "$name";
|
||||
|
||||
my $class_name;
|
||||
if ($offset_to_class_name != 0xffffffff) {
|
||||
sysseek($fh, $offset_to_class_name + 4, 0);
|
||||
$bytes_read = sysread($fh, $class_name, $class_name_length);
|
||||
if ($bytes_read != $class_name_length) {
|
||||
warnf('Could not read class name at 0x%x', $offset_to_class_name);
|
||||
$class_name = undef;
|
||||
}
|
||||
else {
|
||||
$class_name = decode('UCS-2LE', $class_name);
|
||||
}
|
||||
}
|
||||
|
||||
my $self = {};
|
||||
$self->{_regfile} = $regfile;
|
||||
$self->{_offset} = $offset;
|
||||
$self->{_length} = $length;
|
||||
$self->{_allocated} = $allocated;
|
||||
$self->{_tag} = $sig;
|
||||
$self->{_name} = $name;
|
||||
$self->{_name_length} = $name_length;
|
||||
$self->{_key_path} = $key_path;
|
||||
$self->{_flags} = $flags;
|
||||
$self->{_offset_to_parent} = $offset_to_parent;
|
||||
$self->{_num_subkeys} = $num_subkeys;
|
||||
$self->{_offset_to_subkey_list} = $offset_to_subkey_list;
|
||||
$self->{_num_values} = $num_values;
|
||||
$self->{_offset_to_value_list} = $offset_to_value_list;
|
||||
$self->{_timestamp} = unpack_windows_time($timestamp);
|
||||
$self->{_offset_to_security} = $offset_to_security;
|
||||
$self->{_offset_to_class_name} = $offset_to_class_name;
|
||||
$self->{_class_name_length} = $class_name_length;
|
||||
$self->{_class_name} = $class_name;
|
||||
bless $self, $class;
|
||||
|
||||
return $self;
|
||||
}
|
||||
|
||||
sub get_timestamp {
|
||||
my $self = shift;
|
||||
|
||||
return $self->{_timestamp};
|
||||
}
|
||||
|
||||
sub get_timestamp_as_string {
|
||||
my $self = shift;
|
||||
|
||||
return iso8601($self->get_timestamp);
|
||||
}
|
||||
|
||||
sub get_class_name {
|
||||
my $self = shift;
|
||||
|
||||
return $self->{_class_name};
|
||||
}
|
||||
|
||||
sub is_root {
|
||||
my $self = shift;
|
||||
|
||||
my $flags = $self->{_flags};
|
||||
return $flags & 4 || $flags & 8;
|
||||
}
|
||||
|
||||
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::WinNT::Key->new($regfile,
|
||||
$offset_to_parent,
|
||||
$grandparent_key_path);
|
||||
}
|
||||
|
||||
sub get_security {
|
||||
my $self = shift;
|
||||
|
||||
my $regfile = $self->{_regfile};
|
||||
my $offset_to_security = $self->{_offset_to_security};
|
||||
my $key_path = $self->{_key_path};
|
||||
|
||||
if ($offset_to_security == 0xffffffff) {
|
||||
return;
|
||||
}
|
||||
|
||||
return Parse::Win32Registry::WinNT::Security->new($regfile,
|
||||
$offset_to_security,
|
||||
$key_path);
|
||||
}
|
||||
|
||||
sub as_string {
|
||||
my $self = shift;
|
||||
|
||||
my $string = $self->get_path . ' [' . $self->get_timestamp_as_string . ']';
|
||||
return $string;
|
||||
}
|
||||
|
||||
sub parse_info {
|
||||
my $self = shift;
|
||||
|
||||
my $info = sprintf '0x%x nk len=0x%x alloc=%d "%s" par=0x%x keys=%d,0x%x vals=%d,0x%x sec=0x%x class=0x%x',
|
||||
$self->{_offset},
|
||||
$self->{_length},
|
||||
$self->{_allocated},
|
||||
$self->{_name},
|
||||
$self->{_offset_to_parent},
|
||||
$self->{_num_subkeys}, $self->{_offset_to_subkey_list},
|
||||
$self->{_num_values}, $self->{_offset_to_value_list},
|
||||
$self->{_offset_to_security},
|
||||
$self->{_offset_to_class_name};
|
||||
if (defined $self->{_class_name}) {
|
||||
$info .= sprintf ',len=0x%x', $self->{_class_name_length};
|
||||
}
|
||||
return $info;
|
||||
}
|
||||
|
||||
sub _get_offsets_to_subkeys {
|
||||
my $self = shift;
|
||||
|
||||
# Offset is passed as a parameter for recursive lists such as 'ri'
|
||||
my $offset_to_subkey_list = shift || $self->{_offset_to_subkey_list};
|
||||
|
||||
my $regfile = $self->{_regfile};
|
||||
my $fh = $regfile->get_filehandle;
|
||||
|
||||
return if $offset_to_subkey_list == 0xffffffff
|
||||
|| $self->{_num_subkeys} == 0;
|
||||
|
||||
sysseek($fh, $offset_to_subkey_list, 0);
|
||||
my $bytes_read = sysread($fh, my $subkey_list_header, 8);
|
||||
if ($bytes_read != 8) {
|
||||
warnf('Could not read subkey list header at 0x%x',
|
||||
$offset_to_subkey_list);
|
||||
return;
|
||||
}
|
||||
|
||||
# 0x00 dword = subkey list length (negative = allocated)
|
||||
# 0x04 word = 'lf' signature
|
||||
# 0x06 word = number of entries
|
||||
# 0x08 dword = offset to 1st subkey
|
||||
# 0x0c dword = first four characters of the key name
|
||||
# 0x10 dword = offset to 2nd subkey
|
||||
# 0x14 dword = first four characters of the key name
|
||||
# ...
|
||||
|
||||
# 0x00 dword = subkey list length (negative = allocated)
|
||||
# 0x04 word = 'lh' signature
|
||||
# 0x06 word = number of entries
|
||||
# 0x08 dword = offset to 1st subkey
|
||||
# 0x0c dword = hash of the key name
|
||||
# 0x10 dword = offset to 2nd subkey
|
||||
# 0x14 dword = hash of the key name
|
||||
# ...
|
||||
|
||||
# 0x00 dword = subkey list length (negative = allocated)
|
||||
# 0x04 word = 'ri' signature
|
||||
# 0x06 word = number of entries in ri list
|
||||
# 0x08 dword = offset to 1st lf/lh/li list
|
||||
# 0x0c dword = offset to 2nd lf/lh/li list
|
||||
# 0x10 dword = offset to 3rd lf/lh/li list
|
||||
# ...
|
||||
|
||||
# 0x00 dword = subkey list length (negative = allocated)
|
||||
# 0x04 word = 'li' signature
|
||||
# 0x06 word = number of entries in li list
|
||||
# 0x08 dword = offset to 1st subkey
|
||||
# 0x0c dword = offset to 2nd subkey
|
||||
# ...
|
||||
|
||||
# Extracted offsets are always relative to first hbin
|
||||
|
||||
my @offsets_to_subkeys = ();
|
||||
|
||||
my ($length,
|
||||
$sig,
|
||||
$num_entries,
|
||||
) = unpack('Va2v', $subkey_list_header);
|
||||
|
||||
my $subkey_list_length;
|
||||
if ($sig eq 'lf' || $sig eq 'lh') {
|
||||
$subkey_list_length = 2 * 4 * $num_entries;
|
||||
}
|
||||
elsif ($sig eq 'ri' || $sig eq 'li') {
|
||||
$subkey_list_length = 4 * $num_entries;
|
||||
}
|
||||
else {
|
||||
warnf('Invalid signature for subkey list at 0x%x',
|
||||
$offset_to_subkey_list);
|
||||
return;
|
||||
}
|
||||
|
||||
$bytes_read = sysread($fh, my $subkey_list, $subkey_list_length);
|
||||
if ($bytes_read != $subkey_list_length) {
|
||||
warnf('Could not read subkey list at 0x%x',
|
||||
$offset_to_subkey_list);
|
||||
return;
|
||||
}
|
||||
|
||||
if ($sig eq 'lf') {
|
||||
foreach my $offset (unpack("(Vx4)$num_entries", $subkey_list)) {
|
||||
push @offsets_to_subkeys, OFFSET_TO_FIRST_HBIN + $offset;
|
||||
}
|
||||
}
|
||||
elsif ($sig eq 'lh') {
|
||||
foreach my $offset (unpack("(Vx4)$num_entries", $subkey_list)) {
|
||||
push @offsets_to_subkeys, OFFSET_TO_FIRST_HBIN + $offset;
|
||||
}
|
||||
}
|
||||
elsif ($sig eq 'ri') {
|
||||
foreach my $offset (unpack("V$num_entries", $subkey_list)) {
|
||||
my $offsets_ref =
|
||||
$self->_get_offsets_to_subkeys(OFFSET_TO_FIRST_HBIN + $offset);
|
||||
if (defined $offsets_ref && ref $offsets_ref eq 'ARRAY') {
|
||||
push @offsets_to_subkeys, @{ $offsets_ref };
|
||||
}
|
||||
}
|
||||
}
|
||||
elsif ($sig eq 'li') {
|
||||
foreach my $offset (unpack("V$num_entries", $subkey_list)) {
|
||||
push @offsets_to_subkeys, OFFSET_TO_FIRST_HBIN + $offset;
|
||||
}
|
||||
}
|
||||
|
||||
return \@offsets_to_subkeys;
|
||||
}
|
||||
|
||||
sub get_subkey_iterator {
|
||||
my $self = shift;
|
||||
|
||||
my $regfile = $self->{_regfile};
|
||||
my $key_path = $self->{_key_path};
|
||||
|
||||
my @offsets_to_subkeys = ();
|
||||
if ($self->{_num_subkeys} > 0) {
|
||||
my $offsets_to_subkeys_ref = $self->_get_offsets_to_subkeys;
|
||||
if (defined $offsets_to_subkeys_ref) {
|
||||
@offsets_to_subkeys = @{$self->_get_offsets_to_subkeys};
|
||||
}
|
||||
}
|
||||
|
||||
return Parse::Win32Registry::Iterator->new(sub {
|
||||
while (defined(my $offset_to_subkey = shift @offsets_to_subkeys)) {
|
||||
my $subkey = Parse::Win32Registry::WinNT::Key->new($regfile,
|
||||
$offset_to_subkey, $key_path);
|
||||
if (defined $subkey) {
|
||||
return $subkey;
|
||||
}
|
||||
}
|
||||
return; # no more offsets to subkeys
|
||||
});
|
||||
}
|
||||
|
||||
sub _get_offsets_to_values {
|
||||
my $self = shift;
|
||||
|
||||
my $regfile = $self->{_regfile};
|
||||
my $fh = $regfile->get_filehandle;
|
||||
my $offset_to_value_list = $self->{_offset_to_value_list};
|
||||
|
||||
my $num_values = $self->{_num_values};
|
||||
return if $num_values == 0;
|
||||
# Actually, this could probably just fall through
|
||||
# as unpack("x4V0", ...) would return an empty array.
|
||||
|
||||
my @offsets_to_values = ();
|
||||
|
||||
# 0x00 dword = value list length (negative = allocated)
|
||||
# 0x04 dword = 1st offset
|
||||
# 0x08 dword = 2nd offset
|
||||
# ...
|
||||
|
||||
# Extracted offsets are always relative to first hbin
|
||||
|
||||
sysseek($fh, $offset_to_value_list, 0);
|
||||
my $value_list_length = 0x4 + $num_values * 4;
|
||||
my $bytes_read = sysread($fh, my $value_list, $value_list_length);
|
||||
if ($bytes_read != $value_list_length) {
|
||||
warnf("Could not read value list at 0x%x",
|
||||
$offset_to_value_list);
|
||||
return;
|
||||
}
|
||||
|
||||
foreach my $offset (unpack("x4V$num_values", $value_list)) {
|
||||
push @offsets_to_values, OFFSET_TO_FIRST_HBIN + $offset;
|
||||
}
|
||||
|
||||
return \@offsets_to_values;
|
||||
}
|
||||
|
||||
sub get_value_iterator {
|
||||
my $self = shift;
|
||||
|
||||
my $regfile = $self->{_regfile};
|
||||
my $key_path = $self->{_key_path};
|
||||
|
||||
my @offsets_to_values = ();
|
||||
if ($self->{_num_values} > 0) {
|
||||
my $offsets_to_values_ref = $self->_get_offsets_to_values;
|
||||
if (defined $offsets_to_values_ref) {
|
||||
@offsets_to_values = @{$self->_get_offsets_to_values};
|
||||
}
|
||||
}
|
||||
|
||||
return Parse::Win32Registry::Iterator->new(sub {
|
||||
while (defined(my $offset_to_value = shift @offsets_to_values)) {
|
||||
my $value = Parse::Win32Registry::WinNT::Value->new($regfile,
|
||||
$offset_to_value);
|
||||
if (defined $value) {
|
||||
return $value;
|
||||
}
|
||||
}
|
||||
return; # no more offsets to values
|
||||
});
|
||||
}
|
||||
|
||||
1;
|
157
thirdparty/rr-full/Parse/Win32Registry/WinNT/Security.pm
vendored
Normal file
157
thirdparty/rr-full/Parse/Win32Registry/WinNT/Security.pm
vendored
Normal file
@ -0,0 +1,157 @@
|
||||
package Parse::Win32Registry::WinNT::Security;
|
||||
|
||||
use strict;
|
||||
use warnings;
|
||||
|
||||
use base qw(Parse::Win32Registry::Entry);
|
||||
|
||||
use Carp;
|
||||
use Parse::Win32Registry::Base qw(:all);
|
||||
|
||||
use constant SK_HEADER_LENGTH => 0x18;
|
||||
use constant OFFSET_TO_FIRST_HBIN => 0x1000;
|
||||
|
||||
sub new {
|
||||
my $class = shift;
|
||||
my $regfile = shift;
|
||||
my $offset = shift; # offset to sk record relative to start of file
|
||||
|
||||
croak 'Missing registry file' if !defined $regfile;
|
||||
croak 'Missing offset' if !defined $offset;
|
||||
|
||||
if (defined(my $cache = $regfile->{_security_cache})) {
|
||||
if (exists $cache->{$offset}) {
|
||||
return $cache->{$offset};
|
||||
}
|
||||
}
|
||||
|
||||
my $fh = $regfile->get_filehandle;
|
||||
|
||||
# 0x00 dword = security length (negative = allocated)
|
||||
# 0x04 word = 'sk' signature
|
||||
# 0x08 dword = offset to previous sk
|
||||
# 0x0c dword = offset to next sk
|
||||
# 0x10 dword = ref count
|
||||
# 0x14 dword = length of security descriptor
|
||||
# 0x18 = start of security descriptor
|
||||
|
||||
# Extracted offsets are always relative to first hbin
|
||||
|
||||
sysseek($fh, $offset, 0);
|
||||
my $bytes_read = sysread($fh, my $sk_header, SK_HEADER_LENGTH);
|
||||
if ($bytes_read != SK_HEADER_LENGTH) {
|
||||
warnf('Could not read security at 0x%x', $offset);
|
||||
return;
|
||||
}
|
||||
|
||||
my ($length,
|
||||
$sig,
|
||||
$offset_to_previous,
|
||||
$offset_to_next,
|
||||
$ref_count,
|
||||
$sd_length,
|
||||
) = unpack('Va2x2VVVV', $sk_header);
|
||||
|
||||
$offset_to_previous += OFFSET_TO_FIRST_HBIN
|
||||
if $offset_to_previous != 0xffffffff;
|
||||
$offset_to_next += OFFSET_TO_FIRST_HBIN
|
||||
if $offset_to_next != 0xffffffff;
|
||||
|
||||
my $allocated = 0;
|
||||
if ($length > 0x7fffffff) {
|
||||
$allocated = 1;
|
||||
$length = (0xffffffff - $length) + 1;
|
||||
}
|
||||
# allocated should be true
|
||||
|
||||
if ($sig ne 'sk') {
|
||||
warnf('Invalid signature for security at 0x%x', $offset);
|
||||
return;
|
||||
}
|
||||
|
||||
$bytes_read = sysread($fh, my $sd_data, $sd_length);
|
||||
if ($bytes_read != $sd_length) {
|
||||
warnf('Could not read security descriptor for security at 0x%x',
|
||||
$offset);
|
||||
return;
|
||||
}
|
||||
|
||||
my $sd = unpack_security_descriptor($sd_data);
|
||||
if (!defined $sd) {
|
||||
warnf('Invalid security descriptor for security at 0x%x',
|
||||
$offset);
|
||||
# Abandon security object if security descriptor is invalid
|
||||
return;
|
||||
}
|
||||
|
||||
my $self = {};
|
||||
$self->{_regfile} = $regfile;
|
||||
$self->{_offset} = $offset;
|
||||
$self->{_length} = $length;
|
||||
$self->{_allocated} = $allocated;
|
||||
$self->{_tag} = $sig;
|
||||
$self->{_offset_to_previous} = $offset_to_previous;
|
||||
$self->{_offset_to_next} = $offset_to_next;
|
||||
$self->{_ref_count} = $ref_count;
|
||||
$self->{_security_descriptor_length} = $sd_length;
|
||||
$self->{_security_descriptor} = $sd;
|
||||
bless $self, $class;
|
||||
|
||||
if (defined(my $cache = $regfile->{_security_cache})) {
|
||||
$cache->{$offset} = $self;
|
||||
}
|
||||
|
||||
return $self;
|
||||
}
|
||||
|
||||
sub get_previous {
|
||||
my $self = shift;
|
||||
my $regfile = $self->{_regfile};
|
||||
my $offset_to_previous = $self->{_offset_to_previous};
|
||||
|
||||
return Parse::Win32Registry::WinNT::Security->new($regfile,
|
||||
$offset_to_previous);
|
||||
}
|
||||
|
||||
sub get_next {
|
||||
my $self = shift;
|
||||
my $regfile = $self->{_regfile};
|
||||
my $offset_to_next = $self->{_offset_to_next};
|
||||
|
||||
return Parse::Win32Registry::WinNT::Security->new($regfile,
|
||||
$offset_to_next);
|
||||
}
|
||||
|
||||
sub get_reference_count {
|
||||
my $self = shift;
|
||||
|
||||
return $self->{_ref_count};
|
||||
}
|
||||
|
||||
sub get_security_descriptor {
|
||||
my $self = shift;
|
||||
|
||||
return $self->{_security_descriptor};
|
||||
}
|
||||
|
||||
sub as_string {
|
||||
my $self = shift;
|
||||
|
||||
return '(security entry)';
|
||||
}
|
||||
|
||||
sub parse_info {
|
||||
my $self = shift;
|
||||
|
||||
my $info = sprintf '0x%x sk len=0x%x alloc=%d prev=0x%x,next=0x%x refs=%d',
|
||||
$self->{_offset},
|
||||
$self->{_length},
|
||||
$self->{_allocated},
|
||||
$self->{_offset_to_previous},
|
||||
$self->{_offset_to_next},
|
||||
$self->{_ref_count};
|
||||
|
||||
return $info;
|
||||
}
|
||||
|
||||
1;
|
332
thirdparty/rr-full/Parse/Win32Registry/WinNT/Value.pm
vendored
Normal file
332
thirdparty/rr-full/Parse/Win32Registry/WinNT/Value.pm
vendored
Normal file
@ -0,0 +1,332 @@
|
||||
package Parse::Win32Registry::WinNT::Value;
|
||||
|
||||
use strict;
|
||||
use warnings;
|
||||
|
||||
use base qw(Parse::Win32Registry::Value);
|
||||
|
||||
use Carp;
|
||||
use Encode;
|
||||
use Parse::Win32Registry::Base qw(:all);
|
||||
|
||||
use constant VK_HEADER_LENGTH => 0x18;
|
||||
use constant OFFSET_TO_FIRST_HBIN => 0x1000;
|
||||
|
||||
sub new {
|
||||
my $class = shift;
|
||||
my $regfile = shift;
|
||||
my $offset = shift; # offset to vk record relative to first hbin
|
||||
|
||||
croak 'Missing registry file' if !defined $regfile;
|
||||
croak 'Missing offset' if !defined $offset;
|
||||
|
||||
my $fh = $regfile->get_filehandle;
|
||||
|
||||
# 0x00 dword = value length (negative = allocated)
|
||||
# 0x04 word = 'vk' signature
|
||||
# 0x06 word = value name length
|
||||
# 0x08 dword = value data length (bit 31 set => data stored inline)
|
||||
# 0x0c dword = offset to data/inline data
|
||||
# 0x10 dword = value type
|
||||
# 0x14 word = flags (bit 1 set => compressed name)
|
||||
# 0x16 word
|
||||
# 0x18 = value name [for value name length bytes]
|
||||
|
||||
# Extracted offsets are always relative to first hbin
|
||||
|
||||
sysseek($fh, $offset, 0);
|
||||
my $bytes_read = sysread($fh, my $vk_header, VK_HEADER_LENGTH);
|
||||
if ($bytes_read != VK_HEADER_LENGTH) {
|
||||
warnf('Could not read value at 0x%x', $offset);
|
||||
return;
|
||||
}
|
||||
|
||||
my ($length,
|
||||
$sig,
|
||||
$name_length,
|
||||
$data_length,
|
||||
$offset_to_data,
|
||||
$type,
|
||||
$flags,
|
||||
) = unpack('Va2vVVVv', $vk_header);
|
||||
|
||||
my $allocated = 0;
|
||||
if ($length > 0x7fffffff) {
|
||||
$allocated = 1;
|
||||
$length = (0xffffffff - $length) + 1;
|
||||
}
|
||||
# allocated should be true
|
||||
|
||||
if ($length < VK_HEADER_LENGTH) {
|
||||
warnf('Invalid value entry length at 0x%x', $offset);
|
||||
return;
|
||||
}
|
||||
|
||||
if ($sig ne 'vk') {
|
||||
warnf('Invalid signature for value at 0x%x', $offset);
|
||||
return;
|
||||
}
|
||||
|
||||
$bytes_read = sysread($fh, my $name, $name_length);
|
||||
if ($bytes_read != $name_length) {
|
||||
warnf('Could not read name for value at 0x%x', $offset);
|
||||
return;
|
||||
}
|
||||
|
||||
if ($flags & 1) {
|
||||
$name = decode($Parse::Win32Registry::Base::CODEPAGE, $name);
|
||||
}
|
||||
else {
|
||||
$name = decode('UCS-2LE', $name);
|
||||
};
|
||||
|
||||
# If the top bit of the data_length is set, then
|
||||
# the value is inline and stored in the offset to data field (at 0xc).
|
||||
my $data;
|
||||
my $data_inline = $data_length >> 31;
|
||||
if ($data_inline) {
|
||||
# REG_DWORDs are always inline, but I've also seen
|
||||
# REG_SZ, REG_BINARY, REG_EXPAND_SZ, and REG_NONE inline
|
||||
$data_length &= 0x7fffffff;
|
||||
if ($data_length > 4) {
|
||||
warnf("Invalid inline data length for value '%s' at 0x%x",
|
||||
$name, $offset);
|
||||
$data = undef;
|
||||
}
|
||||
else {
|
||||
# unpack inline data from header
|
||||
$data = substr($vk_header, 0xc, $data_length);
|
||||
}
|
||||
}
|
||||
else {
|
||||
if ($offset_to_data != 0 && $offset_to_data != 0xffffffff) {
|
||||
$offset_to_data += OFFSET_TO_FIRST_HBIN;
|
||||
if ($offset_to_data < ($regfile->get_length - $data_length)) {
|
||||
$data = _extract_data($fh, $offset_to_data, $data_length);
|
||||
}
|
||||
else {
|
||||
warnf("Invalid offset to data for value '%s' at 0x%x",
|
||||
$name, $offset);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
my $self = {};
|
||||
$self->{_regfile} = $regfile;
|
||||
$self->{_offset} = $offset;
|
||||
$self->{_length} = $length;
|
||||
$self->{_allocated} = $allocated;
|
||||
$self->{_tag} = $sig;
|
||||
$self->{_name} = $name;
|
||||
$self->{_name_length} = $name_length;
|
||||
$self->{_type} = $type;
|
||||
$self->{_data} = $data;
|
||||
$self->{_data_length} = $data_length;
|
||||
$self->{_data_inline} = $data_inline;
|
||||
$self->{_offset_to_data} = $offset_to_data;
|
||||
$self->{_flags} = $flags;
|
||||
bless $self, $class;
|
||||
|
||||
return $self;
|
||||
}
|
||||
|
||||
sub _extract_data {
|
||||
my $fh = shift;
|
||||
my $offset_to_data = shift;
|
||||
my $data_length = shift;
|
||||
|
||||
if ($offset_to_data == 0 || $offset_to_data == 0xffffffff) {
|
||||
return undef;
|
||||
}
|
||||
|
||||
sysseek($fh, $offset_to_data, 0);
|
||||
my $bytes_read = sysread($fh, my $data_header, 4);
|
||||
if ($bytes_read != 4) {
|
||||
warnf('Could not read data at 0x%x', $offset_to_data);
|
||||
return undef;
|
||||
}
|
||||
|
||||
my ($max_data_length) = unpack('V', $data_header);
|
||||
|
||||
my $data_allocated = 0;
|
||||
if ($max_data_length > 0x7fffffff) {
|
||||
$data_allocated = 1;
|
||||
$max_data_length = (0xffffffff - $max_data_length) + 1;
|
||||
}
|
||||
# data_allocated should be true
|
||||
|
||||
my $data;
|
||||
|
||||
if ($data_length > $max_data_length) {
|
||||
$bytes_read = sysread($fh, my $db_entry, 8);
|
||||
if ($bytes_read != 8) {
|
||||
warnf('Could not read data at 0x%x', $offset_to_data);
|
||||
return undef;
|
||||
}
|
||||
|
||||
my ($sig, $num_data_blocks, $offset_to_data_block_list)
|
||||
= unpack('a2vV', $db_entry);
|
||||
if ($sig ne 'db') {
|
||||
warnf('Invalid signature for big data at 0x%x', $offset_to_data);
|
||||
return undef;
|
||||
}
|
||||
$offset_to_data_block_list += OFFSET_TO_FIRST_HBIN;
|
||||
|
||||
sysseek($fh, $offset_to_data_block_list + 4, 0);
|
||||
$bytes_read = sysread($fh, my $data_block_list, $num_data_blocks * 4);
|
||||
if ($bytes_read != $num_data_blocks * 4) {
|
||||
warnf('Could not read data block list at 0x%x',
|
||||
$offset_to_data_block_list);
|
||||
return undef;
|
||||
}
|
||||
|
||||
$data = "";
|
||||
my @offsets = map { OFFSET_TO_FIRST_HBIN + $_ }
|
||||
unpack("V$num_data_blocks", $data_block_list);
|
||||
foreach my $offset (@offsets) {
|
||||
sysseek($fh, $offset, 0);
|
||||
$bytes_read = sysread($fh, my $block_header, 4);
|
||||
if ($bytes_read != 4) {
|
||||
warnf('Could not read data block at 0x%x', $offset);
|
||||
return undef;
|
||||
}
|
||||
my ($block_length) = unpack('V', $block_header);
|
||||
if ($block_length > 0x7fffffff) {
|
||||
$block_length = (0xffffffff - $block_length) + 1;
|
||||
}
|
||||
$bytes_read = sysread($fh, my $block_data, $block_length - 8);
|
||||
if ($bytes_read != $block_length - 8) {
|
||||
warnf('Could not read data block at 0x%x', $offset);
|
||||
return undef;
|
||||
}
|
||||
$data .= $block_data;
|
||||
}
|
||||
if (length($data) < $data_length) {
|
||||
warnf("Insufficient data blocks for data at 0x%x", $offset_to_data);
|
||||
return undef;
|
||||
}
|
||||
$data = substr($data, 0, $data_length);
|
||||
return $data;
|
||||
}
|
||||
else {
|
||||
$bytes_read = sysread($fh, $data, $data_length);
|
||||
if ($bytes_read != $data_length) {
|
||||
warnf("Could not read data at 0x%x", $offset_to_data);
|
||||
return undef;
|
||||
}
|
||||
}
|
||||
return $data;
|
||||
}
|
||||
|
||||
sub get_data {
|
||||
my $self = shift;
|
||||
|
||||
my $type = $self->get_type;
|
||||
|
||||
my $data = $self->{_data};
|
||||
return if !defined $data;
|
||||
|
||||
# apply decoding to appropriate data types
|
||||
if ($type == REG_DWORD) {
|
||||
if (length($data) == 4) {
|
||||
$data = unpack('V', $data);
|
||||
}
|
||||
else {
|
||||
# incorrect length for dword data
|
||||
$data = undef;
|
||||
}
|
||||
}
|
||||
elsif ($type == REG_DWORD_BIG_ENDIAN) {
|
||||
if (length($data) == 4) {
|
||||
$data = unpack('N', $data);
|
||||
}
|
||||
else {
|
||||
# incorrect length for dword data
|
||||
$data = undef;
|
||||
}
|
||||
}
|
||||
elsif ($type == REG_SZ || $type == REG_EXPAND_SZ) {
|
||||
$data = decode('UCS-2LE', $data);
|
||||
# snip off any terminating null
|
||||
chop $data if substr($data, -1, 1) eq "\0";
|
||||
}
|
||||
elsif ($type == REG_MULTI_SZ) {
|
||||
$data = decode('UCS-2LE', $data);
|
||||
# snip off any terminating nulls
|
||||
chop $data if substr($data, -1, 1) eq "\0";
|
||||
chop $data if substr($data, -1, 1) eq "\0";
|
||||
my @multi_sz = split("\0", $data, -1);
|
||||
# make sure there is at least one empty string
|
||||
@multi_sz = ('') if @multi_sz == 0;
|
||||
return wantarray ? @multi_sz : join($", @multi_sz);
|
||||
}
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
sub as_regedit_export {
|
||||
my $self = shift;
|
||||
my $version = shift || 5;
|
||||
|
||||
my $name = $self->get_name;
|
||||
my $export = $name eq '' ? '@=' : '"' . $name . '"=';
|
||||
|
||||
my $type = $self->get_type;
|
||||
|
||||
# XXX
|
||||
# if (!defined $self->{_data}) {
|
||||
# $name = $name eq '' ? '@' : qq{"$name"};
|
||||
# return qq{; $name=(invalid data)\n};
|
||||
# }
|
||||
|
||||
if ($type == REG_SZ) {
|
||||
$export .= '"' . $self->get_data . '"';
|
||||
$export .= "\n";
|
||||
}
|
||||
elsif ($type == REG_BINARY) {
|
||||
$export .= "hex:";
|
||||
$export .= format_octets($self->{_data}, length($export));
|
||||
}
|
||||
elsif ($type == REG_DWORD) {
|
||||
my $data = $self->get_data;
|
||||
$export .= defined($data)
|
||||
? sprintf("dword:%08x", $data)
|
||||
: "dword:";
|
||||
$export .= "\n";
|
||||
}
|
||||
elsif ($type == REG_EXPAND_SZ || $type == REG_MULTI_SZ) {
|
||||
my $data = $version == 4
|
||||
? encode("ascii", $self->{_data}) # unicode->ascii
|
||||
: $self->{_data}; # raw data
|
||||
$export .= sprintf("hex(%x):", $type);
|
||||
$export .= format_octets($data, length($export));
|
||||
}
|
||||
else {
|
||||
$export .= sprintf("hex(%x):", $type);
|
||||
$export .= format_octets($self->{_data}, length($export));
|
||||
}
|
||||
return $export;
|
||||
}
|
||||
|
||||
sub parse_info {
|
||||
my $self = shift;
|
||||
|
||||
my $info = sprintf '0x%x vk len=0x%x alloc=%d "%s" type=%d',
|
||||
$self->{_offset},
|
||||
$self->{_length},
|
||||
$self->{_allocated},
|
||||
$self->{_name},
|
||||
$self->{_type};
|
||||
if ($self->{_data_inline}) {
|
||||
$info .= sprintf ' data=inline,len=0x%x',
|
||||
$self->{_data_length};
|
||||
}
|
||||
else {
|
||||
$info .= sprintf ' data=0x%x,len=0x%x',
|
||||
$self->{_offset_to_data},
|
||||
$self->{_data_length};
|
||||
}
|
||||
return $info;
|
||||
}
|
||||
|
||||
1;
|
2
thirdparty/rr-full/rip.pl
vendored
2
thirdparty/rr-full/rip.pl
vendored
@ -1,4 +1,4 @@
|
||||
#! c:\perl\bin\perl.exe
|
||||
#! /usr/bin/perl
|
||||
#-------------------------------------------------------------------------
|
||||
# Rip - RegRipper, CLI version
|
||||
# Use this utility to run a plugins file or a single plugin against a Reg
|
||||
|
2
thirdparty/rr-full/rr.pl
vendored
2
thirdparty/rr-full/rr.pl
vendored
@ -1,4 +1,4 @@
|
||||
#! c:\perl\bin\perl.exe
|
||||
#! /usr/bin/perl
|
||||
#-----------------------------------------------------------
|
||||
# Registry Ripper
|
||||
# Parse a Registry hive file for data pertinent to an investigation
|
||||
|
1834
thirdparty/rr/Parse/Win32Registry.pm
vendored
Normal file
1834
thirdparty/rr/Parse/Win32Registry.pm
vendored
Normal file
File diff suppressed because it is too large
Load Diff
1107
thirdparty/rr/Parse/Win32Registry/Base.pm
vendored
Normal file
1107
thirdparty/rr/Parse/Win32Registry/Base.pm
vendored
Normal file
File diff suppressed because it is too large
Load Diff
151
thirdparty/rr/Parse/Win32Registry/Entry.pm
vendored
Normal file
151
thirdparty/rr/Parse/Win32Registry/Entry.pm
vendored
Normal file
@ -0,0 +1,151 @@
|
||||
package Parse::Win32Registry::Entry;
|
||||
|
||||
use strict;
|
||||
use warnings;
|
||||
|
||||
use Carp;
|
||||
use Parse::Win32Registry::Base qw(:all);
|
||||
|
||||
sub get_regfile {
|
||||
my $self = shift;
|
||||
|
||||
return $self->{_regfile};
|
||||
}
|
||||
|
||||
sub get_offset {
|
||||
my $self = shift;
|
||||
|
||||
return $self->{_offset};
|
||||
}
|
||||
|
||||
sub get_length {
|
||||
my $self = shift;
|
||||
|
||||
return $self->{_length};
|
||||
}
|
||||
|
||||
sub is_allocated {
|
||||
my $self = shift;
|
||||
|
||||
return $self->{_allocated};
|
||||
}
|
||||
|
||||
sub get_tag {
|
||||
my $self = shift;
|
||||
|
||||
return $self->{_tag};
|
||||
}
|
||||
|
||||
sub as_string {
|
||||
my $self = shift;
|
||||
|
||||
my $tag = $self->{_tag};
|
||||
$tag = 'unidentified entry' if !defined $tag;
|
||||
return "($tag)";
|
||||
}
|
||||
|
||||
sub parse_info {
|
||||
my $self = shift;
|
||||
|
||||
my $info = sprintf '0x%x %s len=0x%x',
|
||||
$self->{_offset},
|
||||
$self->{_tag},
|
||||
$self->{_length};
|
||||
|
||||
return $info;
|
||||
}
|
||||
|
||||
sub unparsed {
|
||||
my $self = shift;
|
||||
|
||||
return hexdump($self->get_raw_bytes, $self->get_offset);
|
||||
}
|
||||
|
||||
sub get_raw_bytes {
|
||||
my $self = shift;
|
||||
|
||||
my $regfile = $self->{_regfile};
|
||||
my $fh = $regfile->get_filehandle;
|
||||
my $offset = $self->{_offset};
|
||||
my $length = $self->{_length};
|
||||
|
||||
if (defined $self->{_header_length}) {
|
||||
$length = $self->{_header_length};
|
||||
}
|
||||
|
||||
sysseek($fh, $offset, 0);
|
||||
my $bytes_read = sysread($fh, my $buffer, $length);
|
||||
if ($bytes_read == $length) {
|
||||
return $buffer;
|
||||
}
|
||||
else {
|
||||
return '';
|
||||
}
|
||||
}
|
||||
|
||||
sub looks_like_key {
|
||||
return UNIVERSAL::isa($_[0], "Parse::Win32Registry::Key");
|
||||
}
|
||||
|
||||
sub looks_like_value {
|
||||
return UNIVERSAL::isa($_[0], "Parse::Win32Registry::Value");
|
||||
}
|
||||
|
||||
sub looks_like_security {
|
||||
return UNIVERSAL::isa($_[0], "Parse::Win32Registry::WinNT::Security");
|
||||
}
|
||||
|
||||
sub _dumpvar {
|
||||
my $self = shift;
|
||||
my $depth = shift || 1;
|
||||
|
||||
my $dumpvar = '';
|
||||
foreach (sort keys %$self) {
|
||||
$dumpvar .= ' ' x ($depth*2);
|
||||
$dumpvar .= "$_ => ";
|
||||
my $var = $self->{$_};
|
||||
if (!defined $var) {
|
||||
$dumpvar .= "undef\n";
|
||||
}
|
||||
elsif (/offset/ || /_id$/ || /^_unk/) {
|
||||
$dumpvar .= sprintf "0x%x\n", $var;
|
||||
}
|
||||
elsif (/_flags$/) {
|
||||
$dumpvar .= sprintf "0x%x (0b%b)\n", $var, $var;
|
||||
}
|
||||
elsif (/length/ || /bytes_used/) {
|
||||
$dumpvar .= sprintf "0x%x (%d)\n", $var, $var;
|
||||
}
|
||||
elsif (/_data$/) {
|
||||
if (length($var) == 0) {
|
||||
$dumpvar .= '(no data)';
|
||||
}
|
||||
else {
|
||||
$dumpvar .= join(' ', unpack('(H2)20', $var));
|
||||
if (length($var) > 20) {
|
||||
$dumpvar .= '...';
|
||||
}
|
||||
}
|
||||
$dumpvar .= "\n";
|
||||
}
|
||||
elsif (/timestamp$/) {
|
||||
$dumpvar .= $var . " (" . iso8601($var) . ")\n";
|
||||
}
|
||||
elsif ($var =~ /^\d+$/) {
|
||||
$dumpvar .= sprintf "%d\n", $var;
|
||||
}
|
||||
elsif (ref($var)) {
|
||||
$dumpvar .= "$var\n"; # stringify object ref
|
||||
}
|
||||
else {
|
||||
$dumpvar .= qq{"$var"};
|
||||
$dumpvar .= ' ';
|
||||
$dumpvar .= Encode::is_utf8($var) ? "(UTF8)" : "(BYTES)";
|
||||
$dumpvar .= "\n";
|
||||
}
|
||||
}
|
||||
|
||||
return $dumpvar;
|
||||
}
|
||||
|
||||
1;
|
66
thirdparty/rr/Parse/Win32Registry/File.pm
vendored
Normal file
66
thirdparty/rr/Parse/Win32Registry/File.pm
vendored
Normal file
@ -0,0 +1,66 @@
|
||||
package Parse::Win32Registry::File;
|
||||
|
||||
use strict;
|
||||
use warnings;
|
||||
|
||||
sub get_filehandle {
|
||||
my $self = shift;
|
||||
|
||||
return $self->{_filehandle};
|
||||
}
|
||||
|
||||
sub get_filename {
|
||||
my $self = shift;
|
||||
|
||||
return $self->{_filename};
|
||||
}
|
||||
|
||||
sub get_length {
|
||||
my $self = shift;
|
||||
|
||||
return $self->{_length};
|
||||
}
|
||||
|
||||
sub get_entry_iterator {
|
||||
my $self = shift;
|
||||
|
||||
my $entry_iter;
|
||||
my $block_iter = $self->get_block_iterator;
|
||||
|
||||
return Parse::Win32Registry::Iterator->new(sub {
|
||||
while (1) {
|
||||
if (defined $entry_iter) {
|
||||
my $entry = $entry_iter->();
|
||||
if (defined $entry) {
|
||||
return $entry;
|
||||
}
|
||||
}
|
||||
# entry iterator is undefined or finished
|
||||
my $block = $block_iter->();
|
||||
if (!defined $block) {
|
||||
return; # block iterator finished
|
||||
}
|
||||
$entry_iter = $block->get_entry_iterator;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
# method provided for backwards compatibility
|
||||
sub move_to_first_entry {
|
||||
my $self = shift;
|
||||
|
||||
$self->{_entry_iter} = undef;
|
||||
}
|
||||
|
||||
# method provided for backwards compatibility
|
||||
sub get_next_entry {
|
||||
my $self = shift;
|
||||
|
||||
my $entry_iter = $self->{_entry_iter};
|
||||
if (!defined $entry_iter) {
|
||||
$self->{_entry_iter} = $entry_iter = $self->get_entry_iterator;
|
||||
}
|
||||
return $entry_iter->();
|
||||
}
|
||||
|
||||
1;
|
245
thirdparty/rr/Parse/Win32Registry/Key.pm
vendored
Normal file
245
thirdparty/rr/Parse/Win32Registry/Key.pm
vendored
Normal file
@ -0,0 +1,245 @@
|
||||
package Parse::Win32Registry::Key;
|
||||
|
||||
use strict;
|
||||
use warnings;
|
||||
|
||||
use base qw(Parse::Win32Registry::Entry);
|
||||
|
||||
use Carp;
|
||||
|
||||
sub get_name {
|
||||
my $self = shift;
|
||||
|
||||
# the root key of a windows 95 registry has no defined name
|
||||
# but this should be set to '' when created
|
||||
return $self->{_name};
|
||||
}
|
||||
|
||||
sub get_path {
|
||||
my $self = shift;
|
||||
|
||||
return $self->{_key_path};
|
||||
}
|
||||
|
||||
sub _look_up_subkey {
|
||||
my $self = shift;
|
||||
my $subkey_name = shift;
|
||||
|
||||
croak 'Missing subkey name' if !defined $subkey_name;
|
||||
|
||||
foreach my $subkey ($self->get_list_of_subkeys) {
|
||||
if (uc $subkey_name eq uc $subkey->{_name}) {
|
||||
return $subkey;
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
sub get_subkey {
|
||||
my $self = shift;
|
||||
my $subkey_path = shift;
|
||||
|
||||
# check for definedness in case key name is '' or '0'
|
||||
croak "Usage: get_subkey('key name')" if !defined $subkey_path;
|
||||
|
||||
my $key = $self;
|
||||
|
||||
# Current path component separator is '\' to match that used in Windows.
|
||||
# split returns nothing if it is given an empty string,
|
||||
# and without a limit of -1 drops trailing empty fields.
|
||||
# The following returns a list with a single zero-length string ("")
|
||||
# for an empty string, as split(/\\/, $subkey_path, -1) returns (),
|
||||
# an empty list.
|
||||
my @path_components = index($subkey_path, "\\") == -1
|
||||
? ($subkey_path)
|
||||
: split(/\\/, $subkey_path, -1);
|
||||
|
||||
my %offsets_seen = ();
|
||||
$offsets_seen{$key->get_offset} = undef;
|
||||
|
||||
foreach my $subkey_name (@path_components) {
|
||||
if (my $subkey = $key->_look_up_subkey($subkey_name)) {
|
||||
if (exists $offsets_seen{$subkey->get_offset}) {
|
||||
return; # found loop
|
||||
}
|
||||
$key = $subkey;
|
||||
$offsets_seen{$key->get_offset} = undef;
|
||||
}
|
||||
else { # subkey name not found, abort look up
|
||||
return;
|
||||
}
|
||||
}
|
||||
return $key;
|
||||
}
|
||||
|
||||
sub get_value {
|
||||
my $self = shift;
|
||||
my $value_name = shift;
|
||||
|
||||
# check for definedness in case value name is '' or '0'
|
||||
croak "Usage: get_value('value name')" if !defined $value_name;
|
||||
|
||||
foreach my $value ($self->get_list_of_values) {
|
||||
if (uc $value_name eq uc $value->{_name}) {
|
||||
return $value;
|
||||
}
|
||||
}
|
||||
return undef;
|
||||
}
|
||||
|
||||
sub print_summary {
|
||||
my $self = shift;
|
||||
|
||||
print $self->as_string, "\n";
|
||||
}
|
||||
|
||||
sub as_regedit_export {
|
||||
my $self = shift;
|
||||
|
||||
return "[" . $self->{_key_path} . "]\n";
|
||||
}
|
||||
|
||||
sub regenerate_path {
|
||||
my $self = shift;
|
||||
|
||||
# ascend to the root
|
||||
my $key = $self;
|
||||
my @key_names = ($key->get_name);
|
||||
|
||||
my %offsets_seen = ();
|
||||
while (!$key->is_root) {
|
||||
$offsets_seen{$key->get_offset}++;
|
||||
$key = $key->get_parent;
|
||||
if (!defined $key) { # found an undefined parent key
|
||||
unshift @key_names, '(Invalid Parent Key)';
|
||||
last;
|
||||
}
|
||||
if (exists $offsets_seen{$key->get_offset}) { # found loop
|
||||
unshift @key_names, '(Invalid Parent Key)';
|
||||
last;
|
||||
}
|
||||
unshift @key_names, $key->get_name;
|
||||
}
|
||||
|
||||
my $key_path = join('\\', @key_names);
|
||||
$self->{_key_path} = $key_path;
|
||||
return $key_path;
|
||||
}
|
||||
|
||||
sub get_value_data {
|
||||
my $self = shift;
|
||||
my $value_name = shift;
|
||||
|
||||
croak "Usage: get_value_data('value name')" if !defined $value_name;
|
||||
|
||||
if (my $value = $self->get_value($value_name)) {
|
||||
return $value->get_data;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
sub get_mru_list_of_values {
|
||||
my $self = shift;
|
||||
|
||||
my @values = ();
|
||||
|
||||
if (my $mrulist = $self->get_value('MRUList')) {
|
||||
foreach my $ch (split(//, $mrulist->get_data)) {
|
||||
if (my $value = $self->get_value($ch)) {
|
||||
push @values, $value;
|
||||
}
|
||||
}
|
||||
}
|
||||
elsif (my $mrulistex = $self->get_value('MRUListEx')) {
|
||||
foreach my $item (unpack('V*', $mrulistex->get_data)) {
|
||||
last if $item == 0xffffffff;
|
||||
if (my $value = $self->get_value($item)) {
|
||||
push @values, $value;
|
||||
}
|
||||
}
|
||||
}
|
||||
return @values;
|
||||
}
|
||||
|
||||
sub get_list_of_subkeys {
|
||||
my $self = shift;
|
||||
|
||||
my $subkey_iter = $self->get_subkey_iterator;
|
||||
my @subkeys;
|
||||
while (my $subkey = $subkey_iter->()) {
|
||||
push @subkeys, $subkey;
|
||||
}
|
||||
return @subkeys;
|
||||
}
|
||||
|
||||
sub get_list_of_values {
|
||||
my $self = shift;
|
||||
|
||||
my $value_iter = $self->get_value_iterator;
|
||||
my @values;
|
||||
while (my $value = $value_iter->()) {
|
||||
push @values, $value;
|
||||
}
|
||||
return @values;
|
||||
}
|
||||
|
||||
sub get_subtree_iterator {
|
||||
my $self = shift;
|
||||
|
||||
my @start_keys = ($self);
|
||||
push my (@subkey_iters), Parse::Win32Registry::Iterator->new(sub {
|
||||
return shift @start_keys;
|
||||
});
|
||||
my $value_iter;
|
||||
my $key; # used to remember key while iterating values
|
||||
|
||||
return Parse::Win32Registry::Iterator->new(sub {
|
||||
if (defined $value_iter && wantarray) {
|
||||
my $value = $value_iter->();
|
||||
if (defined $value) {
|
||||
return ($key, $value);
|
||||
}
|
||||
# $value_iter finished, so fetch a new one
|
||||
# from the (current) $subkey_iter[-1]
|
||||
}
|
||||
while (@subkey_iters > 0) {
|
||||
$key = $subkey_iters[-1]->(); # depth-first
|
||||
if (defined $key) {
|
||||
push @subkey_iters, $key->get_subkey_iterator;
|
||||
$value_iter = $key->get_value_iterator;
|
||||
return $key;
|
||||
}
|
||||
pop @subkey_iters; # $subkey_iter finished, so remove it
|
||||
}
|
||||
return;
|
||||
});
|
||||
}
|
||||
|
||||
sub walk {
|
||||
my $self = shift;
|
||||
my $key_enter_func = shift;
|
||||
my $value_func = shift;
|
||||
my $key_leave_func = shift;
|
||||
|
||||
if (!defined $key_enter_func &&
|
||||
!defined $value_func &&
|
||||
!defined $key_leave_func) {
|
||||
$key_enter_func = sub { print "+ ", $_[0]->get_path, "\n"; };
|
||||
$value_func = sub { print " '", $_[0]->get_name, "'\n"; };
|
||||
$key_leave_func = sub { print "- ", $_[0]->get_path, "\n"; };
|
||||
}
|
||||
|
||||
$key_enter_func->($self) if ref $key_enter_func eq 'CODE';
|
||||
|
||||
foreach my $value ($self->get_list_of_values) {
|
||||
$value_func->($value) if ref $value_func eq 'CODE';
|
||||
}
|
||||
|
||||
foreach my $subkey ($self->get_list_of_subkeys) {
|
||||
$subkey->walk($key_enter_func, $value_func, $key_leave_func);
|
||||
}
|
||||
|
||||
$key_leave_func->($self) if ref $key_leave_func eq 'CODE';
|
||||
}
|
||||
|
||||
1;
|
101
thirdparty/rr/Parse/Win32Registry/Value.pm
vendored
Normal file
101
thirdparty/rr/Parse/Win32Registry/Value.pm
vendored
Normal file
@ -0,0 +1,101 @@
|
||||
package Parse::Win32Registry::Value;
|
||||
|
||||
use strict;
|
||||
use warnings;
|
||||
|
||||
use base qw(Parse::Win32Registry::Entry);
|
||||
|
||||
use Carp;
|
||||
use Parse::Win32Registry::Base qw(:all);
|
||||
|
||||
sub get_name {
|
||||
my $self = shift;
|
||||
|
||||
return $self->{_name};
|
||||
}
|
||||
|
||||
sub get_type {
|
||||
my $self = shift;
|
||||
|
||||
return $self->{_type};
|
||||
}
|
||||
|
||||
our @Types = qw(
|
||||
REG_NONE
|
||||
REG_SZ
|
||||
REG_EXPAND_SZ
|
||||
REG_BINARY
|
||||
REG_DWORD
|
||||
REG_DWORD_BIG_ENDIAN
|
||||
REG_LINK
|
||||
REG_MULTI_SZ
|
||||
REG_RESOURCE_LIST
|
||||
REG_FULL_RESOURCE_DESCRIPTOR
|
||||
REG_RESOURCE_REQUIREMENTS_LIST
|
||||
REG_QWORD
|
||||
);
|
||||
|
||||
sub get_type_as_string {
|
||||
my $self = shift;
|
||||
|
||||
my $type = $self->get_type;
|
||||
if (exists $Types[$type]) {
|
||||
return $Types[$type];
|
||||
}
|
||||
else {
|
||||
# Return unrecognised types as REG_<number>
|
||||
# REGEDIT displays them as formatted hex numbers, e.g. 0x1f4
|
||||
return "REG_$type";
|
||||
}
|
||||
}
|
||||
|
||||
sub get_data_as_string {
|
||||
my $self = shift;
|
||||
|
||||
my $type = $self->get_type;
|
||||
my $data = $self->get_data;
|
||||
if (!defined($data)) {
|
||||
return '(invalid data)';
|
||||
}
|
||||
elsif (length($data) == 0) {
|
||||
return '(no data)';
|
||||
}
|
||||
elsif ($type == REG_SZ || $type == REG_EXPAND_SZ) {
|
||||
return $data;
|
||||
}
|
||||
elsif ($type == REG_MULTI_SZ) {
|
||||
my @data = $self->get_data;
|
||||
my $i = 0;
|
||||
return join(' ', map { "[" . $i++ . "] $_" } @data);
|
||||
}
|
||||
elsif ($type == REG_DWORD || $type == REG_DWORD_BIG_ENDIAN) {
|
||||
return sprintf '0x%08x (%u)', $data, $data;
|
||||
}
|
||||
else {
|
||||
return join(' ', unpack('(H2)*', $data));
|
||||
}
|
||||
}
|
||||
|
||||
sub get_raw_data {
|
||||
my $self = shift;
|
||||
|
||||
return $self->{_data};
|
||||
}
|
||||
|
||||
sub as_string {
|
||||
my $self = shift;
|
||||
|
||||
my $name = $self->get_name;
|
||||
$name = '(Default)' if $name eq '';
|
||||
my $type_as_string = $self->get_type_as_string;
|
||||
my $data_as_string = $self->get_data_as_string;
|
||||
return "$name ($type_as_string) = $data_as_string";
|
||||
}
|
||||
|
||||
sub print_summary {
|
||||
my $self = shift;
|
||||
|
||||
print $self->as_string, "\n";
|
||||
}
|
||||
|
||||
1;
|
540
thirdparty/rr/Parse/Win32Registry/Win95/File.pm
vendored
Normal file
540
thirdparty/rr/Parse/Win32Registry/Win95/File.pm
vendored
Normal file
@ -0,0 +1,540 @@
|
||||
package Parse::Win32Registry::Win95::File;
|
||||
|
||||
use strict;
|
||||
use warnings;
|
||||
|
||||
use base qw(Parse::Win32Registry::File);
|
||||
|
||||
use Carp;
|
||||
use File::Basename;
|
||||
use Parse::Win32Registry::Base qw(:all);
|
||||
use Parse::Win32Registry::Win95::Key;
|
||||
|
||||
use constant CREG_HEADER_LENGTH => 0x20;
|
||||
use constant OFFSET_TO_RGKN_BLOCK => 0x20;
|
||||
|
||||
sub new {
|
||||
my $class = shift;
|
||||
my $filename = shift or croak 'No filename specified';
|
||||
|
||||
open my $fh, '<', $filename or croak "Unable to open '$filename': $!";
|
||||
|
||||
# CREG Header
|
||||
# 0x00 dword = 'CREG' signature
|
||||
# 0x04
|
||||
# 0x08 dword = offset to first rgdb block
|
||||
# 0x0c
|
||||
# 0x10 word = number of rgdb blocks
|
||||
|
||||
my $bytes_read = sysread($fh, my $creg_header, CREG_HEADER_LENGTH);
|
||||
if ($bytes_read != CREG_HEADER_LENGTH) {
|
||||
warnf('Could not read registry file header');
|
||||
return;
|
||||
}
|
||||
|
||||
my ($creg_sig,
|
||||
$offset_to_first_rgdb_block,
|
||||
$num_rgdb_blocks) = unpack('a4x4Vx4v', $creg_header);
|
||||
|
||||
if ($creg_sig ne 'CREG') {
|
||||
warnf('Invalid registry file signature');
|
||||
return;
|
||||
}
|
||||
|
||||
my $self = {};
|
||||
$self->{_filehandle} = $fh;
|
||||
$self->{_filename} = $filename;
|
||||
$self->{_length} = (stat $fh)[7];
|
||||
$self->{_offset_to_first_rgdb_block} = $offset_to_first_rgdb_block;
|
||||
$self->{_num_rgdb_blocks} = $num_rgdb_blocks;
|
||||
bless $self, $class;
|
||||
|
||||
# get_rgkn will cache the rgkn block for subsequent calls
|
||||
my $rgkn_block = $self->get_rgkn;
|
||||
return if !defined $rgkn_block; # warning will already have been made
|
||||
|
||||
# Index the rgdb entries by id for faster look up
|
||||
$self->_index_rgdb_entries;
|
||||
|
||||
return $self;
|
||||
}
|
||||
|
||||
sub get_timestamp {
|
||||
return undef;
|
||||
}
|
||||
|
||||
sub get_timestamp_as_string {
|
||||
return iso8601(undef);
|
||||
}
|
||||
|
||||
sub get_embedded_filename {
|
||||
return undef;
|
||||
}
|
||||
|
||||
sub get_root_key {
|
||||
my $self = shift;
|
||||
|
||||
return $self->get_rgkn->get_root_key;
|
||||
}
|
||||
|
||||
sub get_virtual_root_key {
|
||||
my $self = shift;
|
||||
my $fake_root = shift;
|
||||
|
||||
my $root_key = $self->get_root_key;
|
||||
return if !defined $root_key;
|
||||
|
||||
if (!defined $fake_root) {
|
||||
# guess virtual root from filename
|
||||
my $filename = basename $self->{_filename};
|
||||
|
||||
if ($filename =~ /USER/i) {
|
||||
$fake_root = 'HKEY_USERS';
|
||||
}
|
||||
elsif ($filename =~ /SYSTEM/i) {
|
||||
$fake_root = 'HKEY_LOCAL_MACHINE';
|
||||
}
|
||||
else {
|
||||
$fake_root = 'HKEY_UNKNOWN';
|
||||
}
|
||||
}
|
||||
|
||||
$root_key->{_name} = $fake_root;
|
||||
$root_key->{_key_path} = $fake_root;
|
||||
|
||||
return $root_key;
|
||||
}
|
||||
|
||||
sub _index_rgdb_entries {
|
||||
my $self = shift;
|
||||
|
||||
my %index = ();
|
||||
|
||||
# Build index of rgdb key entries
|
||||
# Entries are only included if $key_block_num matches $rgdb_block_num
|
||||
my $rgdb_block_num = 0;
|
||||
my $rgdb_iter = $self->get_rgdb_iterator;
|
||||
while (my $rgdb = $rgdb_iter->()) {
|
||||
my $rgdb_key_iter = $rgdb->get_key_iterator;
|
||||
while (my $rgdb_key = $rgdb_key_iter->()) {
|
||||
my $key_id = $rgdb_key->{_id};
|
||||
my $key_block_num = $key_id >> 16;
|
||||
if ($rgdb_block_num == $key_block_num) {
|
||||
$index{$key_id} = $rgdb_key;
|
||||
}
|
||||
}
|
||||
$rgdb_block_num++;
|
||||
}
|
||||
|
||||
$self->{_rgdb_index} = \%index;
|
||||
}
|
||||
|
||||
sub _dump_rgdb_index {
|
||||
my $self = shift;
|
||||
|
||||
my $rgdb_index = $self->{_rgdb_index};
|
||||
|
||||
foreach my $key_id (sort { $a <=> $b } keys %$rgdb_index) {
|
||||
my $rgdb_key = $rgdb_index->{$key_id};
|
||||
printf qq{id=0x%x 0x%x,%d/%d "%s" vals=%d\n},
|
||||
$key_id,
|
||||
$rgdb_key->{_offset},
|
||||
$rgdb_key->{_length_used},
|
||||
$rgdb_key->{_length},
|
||||
$rgdb_key->{_name},
|
||||
$rgdb_key->{_num_values};
|
||||
}
|
||||
}
|
||||
|
||||
sub get_rgkn {
|
||||
my $self = shift;
|
||||
|
||||
# Return cached rgkn block if present
|
||||
if (defined $self->{_rgkn}) {
|
||||
return $self->{_rgkn};
|
||||
}
|
||||
|
||||
my $offset = OFFSET_TO_RGKN_BLOCK;
|
||||
my $rgkn_block = Parse::Win32Registry::Win95::RGKN->new($self, $offset);
|
||||
$self->{_rgkn} = $rgkn_block;
|
||||
return $rgkn_block;
|
||||
}
|
||||
|
||||
sub get_rgdb_iterator {
|
||||
my $self = shift;
|
||||
|
||||
my $offset_to_next_rgdb_block = $self->{_offset_to_first_rgdb_block};
|
||||
my $num_rgdb_blocks = $self->{_num_rgdb_blocks};
|
||||
|
||||
my $end_of_file = $self->{_length};
|
||||
|
||||
my $rgdb_block_num = 0;
|
||||
|
||||
return Parse::Win32Registry::Iterator->new(sub {
|
||||
if ($offset_to_next_rgdb_block > $end_of_file) {
|
||||
return; # no more rgdb blocks
|
||||
}
|
||||
if ($rgdb_block_num >= $num_rgdb_blocks) {
|
||||
return; # no more rgdb blocks
|
||||
}
|
||||
$rgdb_block_num++;
|
||||
if (my $rgdb_block = Parse::Win32Registry::Win95::RGDB->new($self,
|
||||
$offset_to_next_rgdb_block))
|
||||
{
|
||||
return unless $rgdb_block->get_length > 0;
|
||||
$offset_to_next_rgdb_block += $rgdb_block->get_length;
|
||||
return $rgdb_block;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
sub get_block_iterator {
|
||||
my $self = shift;
|
||||
|
||||
my $rgdb_iter;
|
||||
|
||||
return Parse::Win32Registry::Iterator->new(sub {
|
||||
if (!defined $rgdb_iter) {
|
||||
$rgdb_iter = $self->get_rgdb_iterator;
|
||||
return $self->get_rgkn;
|
||||
}
|
||||
return $rgdb_iter->();
|
||||
});
|
||||
}
|
||||
|
||||
*get_hbin_iterator = \&get_block_iterator;
|
||||
|
||||
|
||||
package Parse::Win32Registry::Win95::RGKN;
|
||||
|
||||
use strict;
|
||||
use warnings;
|
||||
|
||||
use base qw(Parse::Win32Registry::Entry);
|
||||
|
||||
use Carp;
|
||||
use Parse::Win32Registry::Base qw(:all);
|
||||
|
||||
use constant RGKN_HEADER_LENGTH => 0x20;
|
||||
use constant OFFSET_TO_RGKN_BLOCK => 0x20;
|
||||
|
||||
sub new {
|
||||
my $class = shift;
|
||||
my $regfile = shift;
|
||||
my $offset = shift || OFFSET_TO_RGKN_BLOCK;
|
||||
|
||||
croak 'Missing registry file' if !defined $regfile;
|
||||
croak 'Missing offset' if !defined $offset;
|
||||
|
||||
my $fh = $regfile->get_filehandle;
|
||||
|
||||
# RGKN Block Header
|
||||
# 0x0 dword = 'RGKN' signature
|
||||
# 0x4 dword = length of rgkn block
|
||||
# 0x8 dword = offset to root key entry (relative to start of rgkn block)
|
||||
|
||||
sysseek($fh, $offset, 0);
|
||||
my $bytes_read = sysread($fh, my $rgkn_header, RGKN_HEADER_LENGTH);
|
||||
if ($bytes_read != RGKN_HEADER_LENGTH) {
|
||||
warnf('Could not read RGKN header at 0x%x', $offset);
|
||||
return;
|
||||
}
|
||||
|
||||
my ($sig,
|
||||
$rgkn_block_length,
|
||||
$offset_to_root_key) = unpack('a4VV', $rgkn_header);
|
||||
|
||||
if ($sig ne 'RGKN') {
|
||||
warnf('Invalid RGKN block signature at 0x%x', $offset);
|
||||
return;
|
||||
}
|
||||
|
||||
$offset_to_root_key += $offset;
|
||||
|
||||
my $self = {};
|
||||
$self->{_regfile} = $regfile;
|
||||
$self->{_offset} = $offset;
|
||||
$self->{_length} = $rgkn_block_length;
|
||||
$self->{_header_length} = RGKN_HEADER_LENGTH;
|
||||
$self->{_allocated} = 1;
|
||||
$self->{_tag} = 'rgkn block';
|
||||
$self->{_offset_to_root_key} = $offset_to_root_key;
|
||||
bless $self, $class;
|
||||
|
||||
return $self;
|
||||
}
|
||||
|
||||
sub get_root_key {
|
||||
my $self = shift;
|
||||
|
||||
my $regfile = $self->{_regfile};
|
||||
my $offset_to_root_key = $self->{_offset_to_root_key};
|
||||
|
||||
my $root_key = Parse::Win32Registry::Win95::Key->new($regfile,
|
||||
$offset_to_root_key);
|
||||
return $root_key;
|
||||
}
|
||||
|
||||
sub get_entry_iterator {
|
||||
my $self = shift;
|
||||
|
||||
my $root_key = $self->get_root_key;
|
||||
|
||||
# In the unlikely event there is no root key, return an empty iterator
|
||||
if (defined $root_key) {
|
||||
return $root_key->get_subtree_iterator;
|
||||
}
|
||||
else {
|
||||
return Parse::Win32Registry::Iterator->new(sub {});
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
package Parse::Win32Registry::Win95::RGDB;
|
||||
|
||||
use base qw(Parse::Win32Registry::Entry);
|
||||
|
||||
use Carp;
|
||||
use Parse::Win32Registry::Base qw(:all);
|
||||
|
||||
use constant RGDB_HEADER_LENGTH => 0x20;
|
||||
|
||||
sub new {
|
||||
my $class = shift;
|
||||
my $regfile = shift;
|
||||
my $offset = shift;
|
||||
|
||||
croak 'Missing registry file' if !defined $regfile;
|
||||
croak 'Missing offset' if !defined $offset;
|
||||
|
||||
my $fh = $regfile->get_filehandle;
|
||||
|
||||
# RGDB Block Header
|
||||
# 0x0 dword = 'RDGB' signature
|
||||
# 0x4 dword = length of rgdb block
|
||||
|
||||
sysseek($fh, $offset, 0);
|
||||
my $bytes_read = sysread($fh, my $rgdb_header, RGDB_HEADER_LENGTH);
|
||||
if ($bytes_read != RGDB_HEADER_LENGTH) {
|
||||
return;
|
||||
}
|
||||
|
||||
my ($sig,
|
||||
$rgdb_block_length) = unpack('a4V', $rgdb_header);
|
||||
|
||||
if ($sig ne 'RGDB') {
|
||||
return;
|
||||
}
|
||||
|
||||
my $self = {};
|
||||
$self->{_regfile} = $regfile;
|
||||
$self->{_offset} = $offset;
|
||||
$self->{_length} = $rgdb_block_length;
|
||||
$self->{_header_length} = RGDB_HEADER_LENGTH;
|
||||
$self->{_allocated} = 1;
|
||||
$self->{_tag} = 'rgdb block';
|
||||
bless $self, $class;
|
||||
|
||||
return $self;
|
||||
}
|
||||
|
||||
sub get_key_iterator {
|
||||
my $self = shift;
|
||||
|
||||
my $regfile = $self->{_regfile};
|
||||
my $offset = $self->{_offset};
|
||||
my $length = $self->{_length};
|
||||
|
||||
my $offset_to_next_rgdb_key = $offset + RGDB_HEADER_LENGTH;
|
||||
my $end_of_rgdb_block = $offset + $length;
|
||||
|
||||
return Parse::Win32Registry::Iterator->new(sub {
|
||||
if ($offset_to_next_rgdb_key >= $end_of_rgdb_block) {
|
||||
return;
|
||||
}
|
||||
if (my $rgdb_key = Parse::Win32Registry::Win95::RGDBKey->new($regfile,
|
||||
$offset_to_next_rgdb_key))
|
||||
{
|
||||
return unless $rgdb_key->get_length > 0;
|
||||
$offset_to_next_rgdb_key += $rgdb_key->get_length;
|
||||
|
||||
# Check rgdb key has not run past end of rgdb block
|
||||
if ($offset_to_next_rgdb_key > $end_of_rgdb_block) {
|
||||
return;
|
||||
}
|
||||
return $rgdb_key;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
sub get_entry_iterator {
|
||||
my $self = shift;
|
||||
|
||||
my $value_iter;
|
||||
my $key_iter = $self->get_key_iterator;
|
||||
|
||||
return Parse::Win32Registry::Iterator->new(sub {
|
||||
if (defined $value_iter) {
|
||||
my $value = $value_iter->();
|
||||
if (defined $value) {
|
||||
return $value;
|
||||
}
|
||||
}
|
||||
|
||||
my $key = $key_iter->();
|
||||
if (!defined $key) {
|
||||
return; # key iterator finished
|
||||
}
|
||||
|
||||
$value_iter = $key->get_value_iterator;
|
||||
return $key;
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
package Parse::Win32Registry::Win95::RGDBKey;
|
||||
|
||||
use base qw(Parse::Win32Registry::Entry);
|
||||
|
||||
use Carp;
|
||||
use Encode;
|
||||
use Parse::Win32Registry::Base qw(:all);
|
||||
|
||||
use constant RGDB_ENTRY_HEADER_LENGTH => 0x14;
|
||||
|
||||
sub new {
|
||||
my $class = shift;
|
||||
my $regfile = shift;
|
||||
my $offset = shift;
|
||||
|
||||
croak 'Missing registry file' if !defined $regfile;
|
||||
croak 'Missing offset' if !defined $offset;
|
||||
|
||||
my $fh = $regfile->get_filehandle;
|
||||
|
||||
# RGDB Key Entry
|
||||
# 0x00 dword = length of rgdb entry / offset to next rgdb entry
|
||||
# (this length includes any following value entries)
|
||||
# 0x04 dword = id (top word = block num, bottom word = id)
|
||||
# 0x08 dword = bytes used (unpacked, but not used)
|
||||
# 0x0c word = key name length
|
||||
# 0x0e word = number of values
|
||||
# 0x10 dword
|
||||
# 0x14 = key name [for key name length bytes]
|
||||
# followed immediately by any RGDB Value Entries belonging to this key
|
||||
|
||||
sysseek($fh, $offset, 0);
|
||||
my $bytes_read = sysread($fh, my $rgdb_key_entry, RGDB_ENTRY_HEADER_LENGTH);
|
||||
if ($bytes_read != RGDB_ENTRY_HEADER_LENGTH) {
|
||||
return;
|
||||
}
|
||||
|
||||
my ($length,
|
||||
$key_id,
|
||||
$length_used,
|
||||
$name_length,
|
||||
$num_values) = unpack('VVVvv', $rgdb_key_entry);
|
||||
|
||||
$bytes_read = sysread($fh, my $name, $name_length);
|
||||
if ($bytes_read != $name_length) {
|
||||
return;
|
||||
}
|
||||
$name = decode($Parse::Win32Registry::Base::CODEPAGE, $name);
|
||||
|
||||
# Calculate the length of the entry's key header
|
||||
my $header_length = RGDB_ENTRY_HEADER_LENGTH + $name_length;
|
||||
|
||||
# Check for invalid/unused entries
|
||||
if ($key_id == 0xffffffff || $length_used == 0xffffffff
|
||||
|| $header_length > $length)
|
||||
{
|
||||
$name = '';
|
||||
$header_length = RGDB_ENTRY_HEADER_LENGTH;
|
||||
}
|
||||
|
||||
my $self = {};
|
||||
$self->{_regfile} = $regfile;
|
||||
$self->{_offset} = $offset;
|
||||
$self->{_length} = $length;
|
||||
$self->{_length_used} = $length_used;
|
||||
$self->{_header_length} = $header_length;
|
||||
$self->{_allocated} = 1;
|
||||
$self->{_tag} = 'rgdb key';
|
||||
$self->{_id} = $key_id;
|
||||
$self->{_name} = $name;
|
||||
$self->{_name_length} = $name_length;
|
||||
$self->{_num_values} = $num_values;
|
||||
bless $self, $class;
|
||||
|
||||
return $self;
|
||||
}
|
||||
|
||||
sub get_name {
|
||||
my $self = shift;
|
||||
|
||||
return $self->{_name};
|
||||
}
|
||||
|
||||
sub parse_info {
|
||||
my $self = shift;
|
||||
|
||||
my $info = sprintf '0x%x rgdb key len=0x%x/0x%x "%s" id=0x%x vals=%d',
|
||||
$self->{_offset},
|
||||
$self->{_length_used},
|
||||
$self->{_length},
|
||||
$self->{_name},
|
||||
$self->{_id},
|
||||
$self->{_num_values};
|
||||
|
||||
return $info;
|
||||
}
|
||||
|
||||
sub get_value_iterator {
|
||||
my $self = shift;
|
||||
|
||||
my $regfile = $self->{_regfile};
|
||||
|
||||
my $num_values_remaining = $self->{_num_values};
|
||||
|
||||
my $offset = $self->{_offset};
|
||||
|
||||
# offset_to_next_rgdb_value can only be set to a valid offset
|
||||
# if num_values_remaining > 0
|
||||
my $offset_to_next_rgdb_value = 0xffffffff;
|
||||
if ($num_values_remaining > 0) {
|
||||
$offset_to_next_rgdb_value = $offset
|
||||
+ $self->{_header_length};
|
||||
}
|
||||
|
||||
my $end_of_rgdb_key = $offset + $self->{_length};
|
||||
|
||||
# don't attempt to return values if id is invalid...
|
||||
if ($self->{_id} == 0xffffffff) {
|
||||
$num_values_remaining = 0;
|
||||
}
|
||||
|
||||
return Parse::Win32Registry::Iterator->new(sub {
|
||||
if ($num_values_remaining-- <= 0) {
|
||||
return;
|
||||
}
|
||||
if ($offset_to_next_rgdb_value == 0xffffffff) {
|
||||
return;
|
||||
}
|
||||
if ($offset_to_next_rgdb_value > $end_of_rgdb_key) {
|
||||
return;
|
||||
}
|
||||
if (my $value = Parse::Win32Registry::Win95::Value->new($regfile,
|
||||
$offset_to_next_rgdb_value))
|
||||
{
|
||||
return unless $value->get_length > 0;
|
||||
$offset_to_next_rgdb_value += $value->get_length;
|
||||
return $value;
|
||||
}
|
||||
else {
|
||||
return; # no more values
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
1;
|
||||
|
207
thirdparty/rr/Parse/Win32Registry/Win95/Key.pm
vendored
Normal file
207
thirdparty/rr/Parse/Win32Registry/Win95/Key.pm
vendored
Normal file
@ -0,0 +1,207 @@
|
||||
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;
|
177
thirdparty/rr/Parse/Win32Registry/Win95/Value.pm
vendored
Normal file
177
thirdparty/rr/Parse/Win32Registry/Win95/Value.pm
vendored
Normal file
@ -0,0 +1,177 @@
|
||||
package Parse::Win32Registry::Win95::Value;
|
||||
|
||||
use strict;
|
||||
use warnings;
|
||||
|
||||
use base qw(Parse::Win32Registry::Value);
|
||||
|
||||
use Carp;
|
||||
use Encode;
|
||||
use Parse::Win32Registry::Base qw(:all);
|
||||
|
||||
use constant RGDB_VALUE_HEADER_LENGTH => 0xc;
|
||||
|
||||
sub new {
|
||||
my $class = shift;
|
||||
my $regfile = shift;
|
||||
my $offset = shift; # offset to RGDB value entry
|
||||
|
||||
croak 'Missing registry file' if !defined $regfile;
|
||||
croak 'Missing offset' if !defined $offset;
|
||||
|
||||
my $fh = $regfile->get_filehandle;
|
||||
|
||||
# RGDB Value Entry
|
||||
# 0x00 dword = value type
|
||||
# 0x04
|
||||
# 0x08 word = value name length
|
||||
# 0x0a word = value data length
|
||||
# 0x0c = value name [for name length bytes]
|
||||
# + value data [for data length bytes]
|
||||
# Value type may just be a word, not a dword;
|
||||
# following word always appears to be zero.
|
||||
|
||||
sysseek($fh, $offset, 0);
|
||||
my $bytes_read = sysread($fh, my $rgdb_value_entry,
|
||||
RGDB_VALUE_HEADER_LENGTH);
|
||||
if ($bytes_read != RGDB_VALUE_HEADER_LENGTH) {
|
||||
warnf('Could not read RGDB value at 0x%x', $offset);
|
||||
return;
|
||||
}
|
||||
|
||||
my ($type,
|
||||
$name_length,
|
||||
$data_length) = unpack('Vx4vv', $rgdb_value_entry);
|
||||
|
||||
$bytes_read = sysread($fh, my $name, $name_length);
|
||||
if ($bytes_read != $name_length) {
|
||||
warnf('Could not read name for RGDB value at 0x%x', $offset);
|
||||
return;
|
||||
}
|
||||
$name = decode($Parse::Win32Registry::Base::CODEPAGE, $name);
|
||||
|
||||
$bytes_read = sysread($fh, my $data, $data_length);
|
||||
if ($bytes_read != $data_length) {
|
||||
warnf('Could not read data for RGDB value at 0x%x', $offset);
|
||||
return;
|
||||
}
|
||||
|
||||
my $self = {};
|
||||
$self->{_regfile} = $regfile;
|
||||
$self->{_offset} = $offset;
|
||||
$self->{_length} = RGDB_VALUE_HEADER_LENGTH + $name_length + $data_length;
|
||||
$self->{_allocated} = 1;
|
||||
$self->{_tag} = 'rgdb value';
|
||||
$self->{_name} = $name;
|
||||
$self->{_name_length} = $name_length;
|
||||
$self->{_type} = $type;
|
||||
$self->{_data} = $data;
|
||||
$self->{_data_length} = $data_length;
|
||||
bless $self, $class;
|
||||
|
||||
return $self;
|
||||
}
|
||||
|
||||
sub get_data {
|
||||
my $self = shift;
|
||||
|
||||
my $type = $self->get_type;
|
||||
|
||||
my $data = $self->{_data};
|
||||
return if !defined $data; # actually, Win95 value data is always defined
|
||||
|
||||
# apply decoding to appropriate data types
|
||||
if ($type == REG_DWORD) {
|
||||
if (length($data) == 4) {
|
||||
$data = unpack('V', $data);
|
||||
}
|
||||
else {
|
||||
# incorrect length for dword data
|
||||
$data = undef;
|
||||
}
|
||||
}
|
||||
elsif ($type == REG_DWORD_BIG_ENDIAN) {
|
||||
if (length($data) == 4) {
|
||||
$data = unpack('N', $data);
|
||||
}
|
||||
else {
|
||||
# incorrect length for dword data
|
||||
$data = undef;
|
||||
}
|
||||
}
|
||||
elsif ($type == REG_SZ || $type == REG_EXPAND_SZ) {
|
||||
# Snip off any terminating null.
|
||||
# Typically, REG_SZ values will not have a terminating null,
|
||||
# while REG_EXPAND_SZ values will have a terminating null
|
||||
chop $data if substr($data, -1, 1) eq "\0";
|
||||
}
|
||||
elsif ($type == REG_MULTI_SZ) {
|
||||
# Snip off any terminating nulls
|
||||
chop $data if substr($data, -1, 1) eq "\0";
|
||||
chop $data if substr($data, -1, 1) eq "\0";
|
||||
my @multi_sz = split("\0", $data, -1);
|
||||
# Make sure there is at least one empty string
|
||||
@multi_sz = ('') if @multi_sz == 0;
|
||||
return wantarray ? @multi_sz : join($", @multi_sz);
|
||||
}
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
sub as_regedit_export {
|
||||
my $self = shift;
|
||||
my $version = shift || 5;
|
||||
|
||||
my $name = $self->get_name;
|
||||
my $export = $name eq '' ? '@=' : '"' . $name . '"=';
|
||||
|
||||
my $type = $self->get_type;
|
||||
|
||||
# XXX
|
||||
# if (!defined $self->{_data}) {
|
||||
# $name = $name eq '' ? '@' : qq{"$name"};
|
||||
# return qq{; $name=(invalid data)\n};
|
||||
# }
|
||||
|
||||
if ($type == REG_SZ) {
|
||||
$export .= '"' . $self->get_data . '"';
|
||||
$export .= "\n";
|
||||
}
|
||||
elsif ($type == REG_BINARY) {
|
||||
$export .= 'hex:';
|
||||
$export .= format_octets($self->{_data}, length($export));
|
||||
}
|
||||
elsif ($type == REG_DWORD) {
|
||||
my $data = $self->get_data;
|
||||
$export .= defined($data)
|
||||
? sprintf("dword:%08x", $data)
|
||||
: "dword:";
|
||||
$export .= "\n";
|
||||
}
|
||||
elsif ($type == REG_EXPAND_SZ || $type == REG_MULTI_SZ) {
|
||||
my $data = $version == 4
|
||||
? $self->{_data} # raw data
|
||||
: encode("UCS-2LE", $self->{_data}); # ansi->unicode
|
||||
$export .= sprintf("hex(%x):", $type);
|
||||
$export .= format_octets($data, length($export));
|
||||
}
|
||||
else {
|
||||
$export .= sprintf("hex(%x):", $type);
|
||||
$export .= format_octets($self->{_data}, length($export));
|
||||
}
|
||||
return $export;
|
||||
}
|
||||
|
||||
sub parse_info {
|
||||
my $self = shift;
|
||||
|
||||
my $info = sprintf '0x%x rgdb value len=0x%x "%s" type=%d data,len=0x%x',
|
||||
$self->{_offset},
|
||||
$self->{_length},
|
||||
$self->{_name},
|
||||
$self->{_type},
|
||||
$self->{_data_length};
|
||||
return $info;
|
||||
}
|
||||
|
||||
1;
|
109
thirdparty/rr/Parse/Win32Registry/WinNT/Entry.pm
vendored
Normal file
109
thirdparty/rr/Parse/Win32Registry/WinNT/Entry.pm
vendored
Normal file
@ -0,0 +1,109 @@
|
||||
package Parse::Win32Registry::WinNT::Entry;
|
||||
|
||||
use strict;
|
||||
use warnings;
|
||||
|
||||
use base qw(Parse::Win32Registry::Entry);
|
||||
|
||||
use Carp;
|
||||
use Parse::Win32Registry::Base qw(:all);
|
||||
use Parse::Win32Registry::WinNT::Key;
|
||||
use Parse::Win32Registry::WinNT::Value;
|
||||
use Parse::Win32Registry::WinNT::Security;
|
||||
|
||||
sub new {
|
||||
my $class = shift;
|
||||
my $regfile = shift;
|
||||
my $offset = shift;
|
||||
|
||||
croak 'Missing registry file' if !defined $regfile;
|
||||
croak 'Missing offset' if !defined $offset;
|
||||
|
||||
my $fh = $regfile->get_filehandle;
|
||||
|
||||
sysseek($fh, $offset, 0);
|
||||
my $bytes_read = sysread($fh, my $entry_header, 8);
|
||||
if ($bytes_read != 8) {
|
||||
return;
|
||||
}
|
||||
|
||||
my ($length,
|
||||
$tag) = unpack('Va2', $entry_header);
|
||||
|
||||
my $allocated = 0;
|
||||
if ($length > 0x7fffffff) {
|
||||
$allocated = 1;
|
||||
$length = (0xffffffff - $length) + 1;
|
||||
}
|
||||
|
||||
$tag = '' if $tag !~ /(nk|vk|lh|lf|li|ri|sk)/;
|
||||
|
||||
if ($tag eq 'nk') {
|
||||
if (my $key = Parse::Win32Registry::WinNT::Key->new($regfile,
|
||||
$offset))
|
||||
{
|
||||
$key->regenerate_path;
|
||||
return $key;
|
||||
}
|
||||
}
|
||||
elsif ($tag eq 'vk') {
|
||||
if (my $value = Parse::Win32Registry::WinNT::Value->new($regfile,
|
||||
$offset))
|
||||
{
|
||||
return $value;
|
||||
}
|
||||
}
|
||||
elsif ($tag eq 'sk') {
|
||||
if (my $value = Parse::Win32Registry::WinNT::Security->new($regfile,
|
||||
$offset))
|
||||
{
|
||||
return $value;
|
||||
}
|
||||
}
|
||||
|
||||
my $self = {};
|
||||
$self->{_regfile} = $regfile,
|
||||
$self->{_offset} = $offset,
|
||||
$self->{_length} = $length,
|
||||
$self->{_tag} = $tag,
|
||||
$self->{_allocated} = $allocated,
|
||||
bless $self, $class;
|
||||
|
||||
return $self;
|
||||
}
|
||||
|
||||
sub as_string {
|
||||
my $self = shift;
|
||||
|
||||
my $tag = $self->{_tag};
|
||||
if ($tag eq 'nk') {
|
||||
return '(key entry)';
|
||||
}
|
||||
elsif ($tag eq 'vk') {
|
||||
return '(value entry)';
|
||||
}
|
||||
elsif ($tag eq 'sk') {
|
||||
return '(security entry)';
|
||||
}
|
||||
elsif ($tag =~ /(lh|lf|li|ri)/) {
|
||||
return '(subkey list entry)';
|
||||
}
|
||||
return '(unidentified entry)';
|
||||
}
|
||||
|
||||
sub parse_info {
|
||||
my $self = shift;
|
||||
|
||||
my $tag = $self->{_tag};
|
||||
$tag = defined($tag) && $tag ne ''
|
||||
? $tag . ' '
|
||||
: '.. ';
|
||||
my $info = sprintf '0x%x %slen=0x%x alloc=%d',
|
||||
$self->{_offset},
|
||||
$tag,
|
||||
$self->{_length},
|
||||
$self->{_allocated};
|
||||
return $info;
|
||||
}
|
||||
|
||||
1;
|
297
thirdparty/rr/Parse/Win32Registry/WinNT/File.pm
vendored
Normal file
297
thirdparty/rr/Parse/Win32Registry/WinNT/File.pm
vendored
Normal file
@ -0,0 +1,297 @@
|
||||
package Parse::Win32Registry::WinNT::File;
|
||||
|
||||
use strict;
|
||||
use warnings;
|
||||
|
||||
use base qw(Parse::Win32Registry::File);
|
||||
|
||||
use Carp;
|
||||
use Encode;
|
||||
use File::Basename;
|
||||
use Parse::Win32Registry::Base qw(:all);
|
||||
use Parse::Win32Registry::WinNT::Key;
|
||||
|
||||
use constant REGF_HEADER_LENGTH => 0x200;
|
||||
use constant OFFSET_TO_FIRST_HBIN => 0x1000;
|
||||
|
||||
sub new {
|
||||
my $class = shift;
|
||||
my $filename = shift or croak "No filename specified";
|
||||
|
||||
open my $fh, '<', $filename or croak "Unable to open '$filename': $!";
|
||||
|
||||
# 0x00 dword = 'regf' signature
|
||||
# 0x04 dword = seq1
|
||||
# 0x08 dword = seq2
|
||||
# 0x0c qword = timestamp
|
||||
# 0x14 dword = major version
|
||||
# 0x18 dword = minor version
|
||||
# 0x1c dword = type (0 = registry file, 1 = log file)
|
||||
# 0x20 dword = (1)
|
||||
# 0x24 dword = offset to root key
|
||||
# 0x28 dword = total length of all hbins (excludes header)
|
||||
# 0x2c dword = (1)
|
||||
# 0x30 = embedded filename
|
||||
|
||||
# Extracted offsets are always relative to first hbin
|
||||
|
||||
my $bytes_read = sysread($fh, my $regf_header, REGF_HEADER_LENGTH);
|
||||
if ($bytes_read != REGF_HEADER_LENGTH) {
|
||||
warnf('Could not read registry file header');
|
||||
return;
|
||||
}
|
||||
|
||||
my ($regf_sig,
|
||||
$seq1,
|
||||
$seq2,
|
||||
$timestamp,
|
||||
$major_version,
|
||||
$minor_version,
|
||||
$type,
|
||||
$offset_to_root_key,
|
||||
$total_hbin_length,
|
||||
$embedded_filename,
|
||||
) = unpack('a4VVa8VVVx4VVx4a64', $regf_header);
|
||||
|
||||
$offset_to_root_key += OFFSET_TO_FIRST_HBIN;
|
||||
|
||||
if ($regf_sig ne 'regf') {
|
||||
warnf('Invalid registry file signature');
|
||||
return;
|
||||
}
|
||||
|
||||
$embedded_filename = unpack('Z*', decode('UCS-2LE', $embedded_filename));
|
||||
|
||||
# The header checksum is the xor of the first 127 dwords.
|
||||
# The checksum is stored in the 128th dword, at offset 0x1fc (508).
|
||||
my $checksum = 0;
|
||||
foreach my $x (unpack('V127', $regf_header)) {
|
||||
$checksum ^= $x;
|
||||
}
|
||||
my $embedded_checksum = unpack('x508V', $regf_header);
|
||||
if ($checksum != $embedded_checksum) {
|
||||
warnf('Invalid checksum for registry file header');
|
||||
}
|
||||
|
||||
my $self = {};
|
||||
$self->{_filehandle} = $fh;
|
||||
$self->{_filename} = $filename;
|
||||
$self->{_length} = (stat $fh)[7];
|
||||
$self->{_offset_to_root_key} = $offset_to_root_key;
|
||||
$self->{_timestamp} = unpack_windows_time($timestamp);
|
||||
$self->{_embedded_filename} = $embedded_filename;
|
||||
$self->{_seq1} = $seq1;
|
||||
$self->{_seq2} = $seq2;
|
||||
$self->{_version} = "$major_version.$minor_version";
|
||||
$self->{_type} = $type;
|
||||
$self->{_total_hbin_length} = $total_hbin_length;
|
||||
$self->{_embedded_checksum} = $embedded_checksum;
|
||||
$self->{_security_cache} = {}; # comment out to disable cache
|
||||
bless $self, $class;
|
||||
|
||||
return $self;
|
||||
}
|
||||
|
||||
sub get_root_key {
|
||||
my $self = shift;
|
||||
|
||||
my $offset_to_root_key = $self->{_offset_to_root_key};
|
||||
|
||||
my $root_key = Parse::Win32Registry::WinNT::Key->new($self,
|
||||
$offset_to_root_key);
|
||||
return $root_key;
|
||||
}
|
||||
|
||||
sub get_virtual_root_key {
|
||||
my $self = shift;
|
||||
my $fake_root = shift;
|
||||
|
||||
my $root_key = $self->get_root_key;
|
||||
return if !defined $root_key;
|
||||
|
||||
if (!defined $fake_root) {
|
||||
# guess virtual root from filename
|
||||
my $filename = basename $self->{_filename};
|
||||
|
||||
if ($filename =~ /NTUSER/i) {
|
||||
$fake_root = 'HKEY_CURRENT_USER';
|
||||
}
|
||||
elsif ($filename =~ /USRCLASS/i) {
|
||||
$fake_root = 'HKEY_CLASSES_ROOT';
|
||||
}
|
||||
elsif ($filename =~ /SOFTWARE/i) {
|
||||
$fake_root = 'HKEY_LOCAL_MACHINE\SOFTWARE';
|
||||
}
|
||||
elsif ($filename =~ /SYSTEM/i) {
|
||||
$fake_root = 'HKEY_LOCAL_MACHINE\SYSTEM';
|
||||
}
|
||||
elsif ($filename =~ /SAM/i) {
|
||||
$fake_root = 'HKEY_LOCAL_MACHINE\SAM';
|
||||
}
|
||||
elsif ($filename =~ /SECURITY/i) {
|
||||
$fake_root = 'HKEY_LOCAL_MACHINE\SECURITY';
|
||||
}
|
||||
else {
|
||||
$fake_root = 'HKEY_UNKNOWN';
|
||||
}
|
||||
}
|
||||
|
||||
$root_key->{_name} = $fake_root;
|
||||
$root_key->{_key_path} = $fake_root;
|
||||
|
||||
return $root_key;
|
||||
}
|
||||
|
||||
sub get_timestamp {
|
||||
my $self = shift;
|
||||
|
||||
return $self->{_timestamp};
|
||||
}
|
||||
|
||||
sub get_timestamp_as_string {
|
||||
my $self = shift;
|
||||
|
||||
return iso8601($self->{_timestamp});
|
||||
}
|
||||
|
||||
sub get_embedded_filename {
|
||||
my $self = shift;
|
||||
|
||||
return $self->{_embedded_filename};
|
||||
}
|
||||
|
||||
sub get_block_iterator {
|
||||
my $self = shift;
|
||||
|
||||
my $offset_to_next_hbin = OFFSET_TO_FIRST_HBIN;
|
||||
my $end_of_file = $self->{_length};
|
||||
|
||||
return Parse::Win32Registry::Iterator->new(sub {
|
||||
if ($offset_to_next_hbin > $end_of_file) {
|
||||
return; # no more hbins
|
||||
}
|
||||
if (my $hbin = Parse::Win32Registry::WinNT::Hbin->new($self,
|
||||
$offset_to_next_hbin))
|
||||
{
|
||||
return unless $hbin->get_length > 0;
|
||||
$offset_to_next_hbin += $hbin->get_length;
|
||||
return $hbin;
|
||||
}
|
||||
else {
|
||||
return; # no more hbins
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
*get_hbin_iterator = \&get_block_iterator;
|
||||
|
||||
sub _dump_security_cache {
|
||||
my $self = shift;
|
||||
|
||||
if (defined(my $cache = $self->{_security_cache})) {
|
||||
foreach my $offset (sort { $a <=> $b } keys %$cache) {
|
||||
my $security = $cache->{$offset};
|
||||
printf '0x%x %s\n', $offset, $security->as_string;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
package Parse::Win32Registry::WinNT::Hbin;
|
||||
|
||||
use strict;
|
||||
use warnings;
|
||||
|
||||
use base qw(Parse::Win32Registry::Entry);
|
||||
|
||||
use Carp;
|
||||
use Parse::Win32Registry::Base qw(:all);
|
||||
use Parse::Win32Registry::WinNT::Entry;
|
||||
|
||||
use constant HBIN_HEADER_LENGTH => 0x20;
|
||||
|
||||
sub new {
|
||||
my $class = shift;
|
||||
my $regfile = shift;
|
||||
my $offset = shift;
|
||||
|
||||
croak 'Missing registry file' if !defined $regfile;
|
||||
croak 'Missing offset' if !defined $offset;
|
||||
|
||||
my $fh = $regfile->get_filehandle;
|
||||
|
||||
# 0x00 dword = 'hbin' signature
|
||||
# 0x04 dword = offset from first hbin to this hbin
|
||||
# 0x08 dword = length of this hbin / relative offset to next hbin
|
||||
# 0x14 qword = timestamp (first hbin only)
|
||||
|
||||
# Extracted offsets are always relative to first hbin
|
||||
|
||||
sysseek($fh, $offset, 0);
|
||||
my $bytes_read = sysread($fh, my $hbin_header, HBIN_HEADER_LENGTH);
|
||||
if ($bytes_read != HBIN_HEADER_LENGTH) {
|
||||
return;
|
||||
}
|
||||
|
||||
my ($sig,
|
||||
$offset_to_hbin,
|
||||
$length,
|
||||
$timestamp) = unpack('a4VVx8a8x4', $hbin_header);
|
||||
|
||||
if ($sig ne 'hbin') {
|
||||
return;
|
||||
}
|
||||
|
||||
my $self = {};
|
||||
$self->{_regfile} = $regfile;
|
||||
$self->{_offset} = $offset;
|
||||
$self->{_length} = $length;
|
||||
$self->{_header_length} = HBIN_HEADER_LENGTH;
|
||||
$self->{_allocated} = 1;
|
||||
$self->{_tag} = $sig;
|
||||
$self->{_timestamp} = unpack_windows_time($timestamp);
|
||||
bless $self, $class;
|
||||
|
||||
return $self;
|
||||
}
|
||||
|
||||
sub get_timestamp {
|
||||
my $self = shift;
|
||||
|
||||
return $self->{_timestamp};
|
||||
}
|
||||
|
||||
sub get_timestamp_as_string {
|
||||
my $self = shift;
|
||||
|
||||
return iso8601($self->{_timestamp});
|
||||
}
|
||||
|
||||
sub get_entry_iterator {
|
||||
my $self = shift;
|
||||
|
||||
my $regfile = $self->{_regfile};
|
||||
my $offset = $self->{_offset};
|
||||
my $length = $self->{_length};
|
||||
|
||||
my $offset_to_next_entry = $offset + HBIN_HEADER_LENGTH;
|
||||
my $end_of_hbin = $offset + $length;
|
||||
|
||||
return Parse::Win32Registry::Iterator->new(sub {
|
||||
if ($offset_to_next_entry >= $end_of_hbin) {
|
||||
return; # no more entries
|
||||
}
|
||||
if (my $entry = Parse::Win32Registry::WinNT::Entry->new($regfile,
|
||||
$offset_to_next_entry))
|
||||
{
|
||||
return unless $entry->get_length > 0;
|
||||
$offset_to_next_entry += $entry->get_length;
|
||||
return $entry;
|
||||
}
|
||||
else {
|
||||
return; # no more entries
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
1;
|
444
thirdparty/rr/Parse/Win32Registry/WinNT/Key.pm
vendored
Normal file
444
thirdparty/rr/Parse/Win32Registry/WinNT/Key.pm
vendored
Normal file
@ -0,0 +1,444 @@
|
||||
package Parse::Win32Registry::WinNT::Key;
|
||||
|
||||
use strict;
|
||||
use warnings;
|
||||
|
||||
use base qw(Parse::Win32Registry::Key);
|
||||
|
||||
use Carp;
|
||||
use Encode;
|
||||
use Parse::Win32Registry::Base qw(:all);
|
||||
use Parse::Win32Registry::WinNT::Value;
|
||||
use Parse::Win32Registry::WinNT::Security;
|
||||
|
||||
use constant NK_HEADER_LENGTH => 0x50;
|
||||
use constant OFFSET_TO_FIRST_HBIN => 0x1000;
|
||||
|
||||
sub new {
|
||||
my $class = shift;
|
||||
my $regfile = shift;
|
||||
my $offset = shift; # offset to nk record relative to start of file
|
||||
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;
|
||||
|
||||
# 0x00 dword = key length (negative = allocated)
|
||||
# 0x04 word = 'nk' signature
|
||||
# 0x06 word = flags
|
||||
# 0x08 qword = timestamp
|
||||
# 0x10
|
||||
# 0x14 dword = offset to parent
|
||||
# 0x18 dword = number of subkeys
|
||||
# 0x1c
|
||||
# 0x20 dword = offset to subkey list (lf, lh, ri, li)
|
||||
# 0x24
|
||||
# 0x28 dword = number of values
|
||||
# 0x2c dword = offset to value list
|
||||
# 0x30 dword = offset to security
|
||||
# 0x34 dword = offset to class name
|
||||
# 0x38 dword = max subkey name length
|
||||
# 0x3c dword = max class name length
|
||||
# 0x40 dword = max value name length
|
||||
# 0x44 dword = max value data length
|
||||
# 0x48
|
||||
# 0x4c word = key name length
|
||||
# 0x4e word = class name length
|
||||
# 0x50 = key name [for key name length bytes]
|
||||
|
||||
# Extracted offsets are always relative to first hbin
|
||||
|
||||
sysseek($fh, $offset, 0);
|
||||
my $bytes_read = sysread($fh, my $nk_header, NK_HEADER_LENGTH);
|
||||
if ($bytes_read != NK_HEADER_LENGTH) {
|
||||
warnf('Could not read key at 0x%x', $offset);
|
||||
return;
|
||||
}
|
||||
|
||||
my ($length,
|
||||
$sig,
|
||||
$flags,
|
||||
$timestamp,
|
||||
$offset_to_parent,
|
||||
$num_subkeys,
|
||||
$offset_to_subkey_list,
|
||||
$num_values,
|
||||
$offset_to_value_list,
|
||||
$offset_to_security,
|
||||
$offset_to_class_name,
|
||||
$name_length,
|
||||
$class_name_length,
|
||||
) = unpack('Va2va8x4VVx4Vx4VVVVx20vv', $nk_header);
|
||||
|
||||
$offset_to_parent += OFFSET_TO_FIRST_HBIN
|
||||
if $offset_to_parent != 0xffffffff;
|
||||
$offset_to_subkey_list += OFFSET_TO_FIRST_HBIN
|
||||
if $offset_to_subkey_list != 0xffffffff;
|
||||
$offset_to_value_list += OFFSET_TO_FIRST_HBIN
|
||||
if $offset_to_value_list != 0xffffffff;
|
||||
$offset_to_security += OFFSET_TO_FIRST_HBIN
|
||||
if $offset_to_security != 0xffffffff;
|
||||
$offset_to_class_name += OFFSET_TO_FIRST_HBIN
|
||||
if $offset_to_class_name != 0xffffffff;
|
||||
|
||||
my $allocated = 0;
|
||||
if ($length > 0x7fffffff) {
|
||||
$allocated = 1;
|
||||
$length = (0xffffffff - $length) + 1;
|
||||
}
|
||||
# allocated should be true
|
||||
|
||||
if ($length < NK_HEADER_LENGTH) {
|
||||
warnf('Invalid value entry length at 0x%x', $offset);
|
||||
return;
|
||||
}
|
||||
|
||||
if ($sig ne 'nk') {
|
||||
warnf('Invalid signature for key at 0x%x', $offset);
|
||||
return;
|
||||
}
|
||||
|
||||
$bytes_read = sysread($fh, my $name, $name_length);
|
||||
if ($bytes_read != $name_length) {
|
||||
warnf('Could not read name for key at 0x%x', $offset);
|
||||
return;
|
||||
}
|
||||
|
||||
if ($flags & 0x20) {
|
||||
$name = decode($Parse::Win32Registry::Base::CODEPAGE, $name);
|
||||
}
|
||||
else {
|
||||
$name = decode('UCS-2LE', $name);
|
||||
}
|
||||
|
||||
my $key_path = (defined $parent_key_path)
|
||||
? "$parent_key_path\\$name"
|
||||
: "$name";
|
||||
|
||||
my $class_name;
|
||||
if ($offset_to_class_name != 0xffffffff) {
|
||||
sysseek($fh, $offset_to_class_name + 4, 0);
|
||||
$bytes_read = sysread($fh, $class_name, $class_name_length);
|
||||
if ($bytes_read != $class_name_length) {
|
||||
warnf('Could not read class name at 0x%x', $offset_to_class_name);
|
||||
$class_name = undef;
|
||||
}
|
||||
else {
|
||||
$class_name = decode('UCS-2LE', $class_name);
|
||||
}
|
||||
}
|
||||
|
||||
my $self = {};
|
||||
$self->{_regfile} = $regfile;
|
||||
$self->{_offset} = $offset;
|
||||
$self->{_length} = $length;
|
||||
$self->{_allocated} = $allocated;
|
||||
$self->{_tag} = $sig;
|
||||
$self->{_name} = $name;
|
||||
$self->{_name_length} = $name_length;
|
||||
$self->{_key_path} = $key_path;
|
||||
$self->{_flags} = $flags;
|
||||
$self->{_offset_to_parent} = $offset_to_parent;
|
||||
$self->{_num_subkeys} = $num_subkeys;
|
||||
$self->{_offset_to_subkey_list} = $offset_to_subkey_list;
|
||||
$self->{_num_values} = $num_values;
|
||||
$self->{_offset_to_value_list} = $offset_to_value_list;
|
||||
$self->{_timestamp} = unpack_windows_time($timestamp);
|
||||
$self->{_offset_to_security} = $offset_to_security;
|
||||
$self->{_offset_to_class_name} = $offset_to_class_name;
|
||||
$self->{_class_name_length} = $class_name_length;
|
||||
$self->{_class_name} = $class_name;
|
||||
bless $self, $class;
|
||||
|
||||
return $self;
|
||||
}
|
||||
|
||||
sub get_timestamp {
|
||||
my $self = shift;
|
||||
|
||||
return $self->{_timestamp};
|
||||
}
|
||||
|
||||
sub get_timestamp_as_string {
|
||||
my $self = shift;
|
||||
|
||||
return iso8601($self->get_timestamp);
|
||||
}
|
||||
|
||||
sub get_class_name {
|
||||
my $self = shift;
|
||||
|
||||
return $self->{_class_name};
|
||||
}
|
||||
|
||||
sub is_root {
|
||||
my $self = shift;
|
||||
|
||||
my $flags = $self->{_flags};
|
||||
return $flags & 4 || $flags & 8;
|
||||
}
|
||||
|
||||
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::WinNT::Key->new($regfile,
|
||||
$offset_to_parent,
|
||||
$grandparent_key_path);
|
||||
}
|
||||
|
||||
sub get_security {
|
||||
my $self = shift;
|
||||
|
||||
my $regfile = $self->{_regfile};
|
||||
my $offset_to_security = $self->{_offset_to_security};
|
||||
my $key_path = $self->{_key_path};
|
||||
|
||||
if ($offset_to_security == 0xffffffff) {
|
||||
return;
|
||||
}
|
||||
|
||||
return Parse::Win32Registry::WinNT::Security->new($regfile,
|
||||
$offset_to_security,
|
||||
$key_path);
|
||||
}
|
||||
|
||||
sub as_string {
|
||||
my $self = shift;
|
||||
|
||||
my $string = $self->get_path . ' [' . $self->get_timestamp_as_string . ']';
|
||||
return $string;
|
||||
}
|
||||
|
||||
sub parse_info {
|
||||
my $self = shift;
|
||||
|
||||
my $info = sprintf '0x%x nk len=0x%x alloc=%d "%s" par=0x%x keys=%d,0x%x vals=%d,0x%x sec=0x%x class=0x%x',
|
||||
$self->{_offset},
|
||||
$self->{_length},
|
||||
$self->{_allocated},
|
||||
$self->{_name},
|
||||
$self->{_offset_to_parent},
|
||||
$self->{_num_subkeys}, $self->{_offset_to_subkey_list},
|
||||
$self->{_num_values}, $self->{_offset_to_value_list},
|
||||
$self->{_offset_to_security},
|
||||
$self->{_offset_to_class_name};
|
||||
if (defined $self->{_class_name}) {
|
||||
$info .= sprintf ',len=0x%x', $self->{_class_name_length};
|
||||
}
|
||||
return $info;
|
||||
}
|
||||
|
||||
sub _get_offsets_to_subkeys {
|
||||
my $self = shift;
|
||||
|
||||
# Offset is passed as a parameter for recursive lists such as 'ri'
|
||||
my $offset_to_subkey_list = shift || $self->{_offset_to_subkey_list};
|
||||
|
||||
my $regfile = $self->{_regfile};
|
||||
my $fh = $regfile->get_filehandle;
|
||||
|
||||
return if $offset_to_subkey_list == 0xffffffff
|
||||
|| $self->{_num_subkeys} == 0;
|
||||
|
||||
sysseek($fh, $offset_to_subkey_list, 0);
|
||||
my $bytes_read = sysread($fh, my $subkey_list_header, 8);
|
||||
if ($bytes_read != 8) {
|
||||
warnf('Could not read subkey list header at 0x%x',
|
||||
$offset_to_subkey_list);
|
||||
return;
|
||||
}
|
||||
|
||||
# 0x00 dword = subkey list length (negative = allocated)
|
||||
# 0x04 word = 'lf' signature
|
||||
# 0x06 word = number of entries
|
||||
# 0x08 dword = offset to 1st subkey
|
||||
# 0x0c dword = first four characters of the key name
|
||||
# 0x10 dword = offset to 2nd subkey
|
||||
# 0x14 dword = first four characters of the key name
|
||||
# ...
|
||||
|
||||
# 0x00 dword = subkey list length (negative = allocated)
|
||||
# 0x04 word = 'lh' signature
|
||||
# 0x06 word = number of entries
|
||||
# 0x08 dword = offset to 1st subkey
|
||||
# 0x0c dword = hash of the key name
|
||||
# 0x10 dword = offset to 2nd subkey
|
||||
# 0x14 dword = hash of the key name
|
||||
# ...
|
||||
|
||||
# 0x00 dword = subkey list length (negative = allocated)
|
||||
# 0x04 word = 'ri' signature
|
||||
# 0x06 word = number of entries in ri list
|
||||
# 0x08 dword = offset to 1st lf/lh/li list
|
||||
# 0x0c dword = offset to 2nd lf/lh/li list
|
||||
# 0x10 dword = offset to 3rd lf/lh/li list
|
||||
# ...
|
||||
|
||||
# 0x00 dword = subkey list length (negative = allocated)
|
||||
# 0x04 word = 'li' signature
|
||||
# 0x06 word = number of entries in li list
|
||||
# 0x08 dword = offset to 1st subkey
|
||||
# 0x0c dword = offset to 2nd subkey
|
||||
# ...
|
||||
|
||||
# Extracted offsets are always relative to first hbin
|
||||
|
||||
my @offsets_to_subkeys = ();
|
||||
|
||||
my ($length,
|
||||
$sig,
|
||||
$num_entries,
|
||||
) = unpack('Va2v', $subkey_list_header);
|
||||
|
||||
my $subkey_list_length;
|
||||
if ($sig eq 'lf' || $sig eq 'lh') {
|
||||
$subkey_list_length = 2 * 4 * $num_entries;
|
||||
}
|
||||
elsif ($sig eq 'ri' || $sig eq 'li') {
|
||||
$subkey_list_length = 4 * $num_entries;
|
||||
}
|
||||
else {
|
||||
warnf('Invalid signature for subkey list at 0x%x',
|
||||
$offset_to_subkey_list);
|
||||
return;
|
||||
}
|
||||
|
||||
$bytes_read = sysread($fh, my $subkey_list, $subkey_list_length);
|
||||
if ($bytes_read != $subkey_list_length) {
|
||||
warnf('Could not read subkey list at 0x%x',
|
||||
$offset_to_subkey_list);
|
||||
return;
|
||||
}
|
||||
|
||||
if ($sig eq 'lf') {
|
||||
foreach my $offset (unpack("(Vx4)$num_entries", $subkey_list)) {
|
||||
push @offsets_to_subkeys, OFFSET_TO_FIRST_HBIN + $offset;
|
||||
}
|
||||
}
|
||||
elsif ($sig eq 'lh') {
|
||||
foreach my $offset (unpack("(Vx4)$num_entries", $subkey_list)) {
|
||||
push @offsets_to_subkeys, OFFSET_TO_FIRST_HBIN + $offset;
|
||||
}
|
||||
}
|
||||
elsif ($sig eq 'ri') {
|
||||
foreach my $offset (unpack("V$num_entries", $subkey_list)) {
|
||||
my $offsets_ref =
|
||||
$self->_get_offsets_to_subkeys(OFFSET_TO_FIRST_HBIN + $offset);
|
||||
if (defined $offsets_ref && ref $offsets_ref eq 'ARRAY') {
|
||||
push @offsets_to_subkeys, @{ $offsets_ref };
|
||||
}
|
||||
}
|
||||
}
|
||||
elsif ($sig eq 'li') {
|
||||
foreach my $offset (unpack("V$num_entries", $subkey_list)) {
|
||||
push @offsets_to_subkeys, OFFSET_TO_FIRST_HBIN + $offset;
|
||||
}
|
||||
}
|
||||
|
||||
return \@offsets_to_subkeys;
|
||||
}
|
||||
|
||||
sub get_subkey_iterator {
|
||||
my $self = shift;
|
||||
|
||||
my $regfile = $self->{_regfile};
|
||||
my $key_path = $self->{_key_path};
|
||||
|
||||
my @offsets_to_subkeys = ();
|
||||
if ($self->{_num_subkeys} > 0) {
|
||||
my $offsets_to_subkeys_ref = $self->_get_offsets_to_subkeys;
|
||||
if (defined $offsets_to_subkeys_ref) {
|
||||
@offsets_to_subkeys = @{$self->_get_offsets_to_subkeys};
|
||||
}
|
||||
}
|
||||
|
||||
return Parse::Win32Registry::Iterator->new(sub {
|
||||
while (defined(my $offset_to_subkey = shift @offsets_to_subkeys)) {
|
||||
my $subkey = Parse::Win32Registry::WinNT::Key->new($regfile,
|
||||
$offset_to_subkey, $key_path);
|
||||
if (defined $subkey) {
|
||||
return $subkey;
|
||||
}
|
||||
}
|
||||
return; # no more offsets to subkeys
|
||||
});
|
||||
}
|
||||
|
||||
sub _get_offsets_to_values {
|
||||
my $self = shift;
|
||||
|
||||
my $regfile = $self->{_regfile};
|
||||
my $fh = $regfile->get_filehandle;
|
||||
my $offset_to_value_list = $self->{_offset_to_value_list};
|
||||
|
||||
my $num_values = $self->{_num_values};
|
||||
return if $num_values == 0;
|
||||
# Actually, this could probably just fall through
|
||||
# as unpack("x4V0", ...) would return an empty array.
|
||||
|
||||
my @offsets_to_values = ();
|
||||
|
||||
# 0x00 dword = value list length (negative = allocated)
|
||||
# 0x04 dword = 1st offset
|
||||
# 0x08 dword = 2nd offset
|
||||
# ...
|
||||
|
||||
# Extracted offsets are always relative to first hbin
|
||||
|
||||
sysseek($fh, $offset_to_value_list, 0);
|
||||
my $value_list_length = 0x4 + $num_values * 4;
|
||||
my $bytes_read = sysread($fh, my $value_list, $value_list_length);
|
||||
if ($bytes_read != $value_list_length) {
|
||||
warnf("Could not read value list at 0x%x",
|
||||
$offset_to_value_list);
|
||||
return;
|
||||
}
|
||||
|
||||
foreach my $offset (unpack("x4V$num_values", $value_list)) {
|
||||
push @offsets_to_values, OFFSET_TO_FIRST_HBIN + $offset;
|
||||
}
|
||||
|
||||
return \@offsets_to_values;
|
||||
}
|
||||
|
||||
sub get_value_iterator {
|
||||
my $self = shift;
|
||||
|
||||
my $regfile = $self->{_regfile};
|
||||
my $key_path = $self->{_key_path};
|
||||
|
||||
my @offsets_to_values = ();
|
||||
if ($self->{_num_values} > 0) {
|
||||
my $offsets_to_values_ref = $self->_get_offsets_to_values;
|
||||
if (defined $offsets_to_values_ref) {
|
||||
@offsets_to_values = @{$self->_get_offsets_to_values};
|
||||
}
|
||||
}
|
||||
|
||||
return Parse::Win32Registry::Iterator->new(sub {
|
||||
while (defined(my $offset_to_value = shift @offsets_to_values)) {
|
||||
my $value = Parse::Win32Registry::WinNT::Value->new($regfile,
|
||||
$offset_to_value);
|
||||
if (defined $value) {
|
||||
return $value;
|
||||
}
|
||||
}
|
||||
return; # no more offsets to values
|
||||
});
|
||||
}
|
||||
|
||||
1;
|
157
thirdparty/rr/Parse/Win32Registry/WinNT/Security.pm
vendored
Normal file
157
thirdparty/rr/Parse/Win32Registry/WinNT/Security.pm
vendored
Normal file
@ -0,0 +1,157 @@
|
||||
package Parse::Win32Registry::WinNT::Security;
|
||||
|
||||
use strict;
|
||||
use warnings;
|
||||
|
||||
use base qw(Parse::Win32Registry::Entry);
|
||||
|
||||
use Carp;
|
||||
use Parse::Win32Registry::Base qw(:all);
|
||||
|
||||
use constant SK_HEADER_LENGTH => 0x18;
|
||||
use constant OFFSET_TO_FIRST_HBIN => 0x1000;
|
||||
|
||||
sub new {
|
||||
my $class = shift;
|
||||
my $regfile = shift;
|
||||
my $offset = shift; # offset to sk record relative to start of file
|
||||
|
||||
croak 'Missing registry file' if !defined $regfile;
|
||||
croak 'Missing offset' if !defined $offset;
|
||||
|
||||
if (defined(my $cache = $regfile->{_security_cache})) {
|
||||
if (exists $cache->{$offset}) {
|
||||
return $cache->{$offset};
|
||||
}
|
||||
}
|
||||
|
||||
my $fh = $regfile->get_filehandle;
|
||||
|
||||
# 0x00 dword = security length (negative = allocated)
|
||||
# 0x04 word = 'sk' signature
|
||||
# 0x08 dword = offset to previous sk
|
||||
# 0x0c dword = offset to next sk
|
||||
# 0x10 dword = ref count
|
||||
# 0x14 dword = length of security descriptor
|
||||
# 0x18 = start of security descriptor
|
||||
|
||||
# Extracted offsets are always relative to first hbin
|
||||
|
||||
sysseek($fh, $offset, 0);
|
||||
my $bytes_read = sysread($fh, my $sk_header, SK_HEADER_LENGTH);
|
||||
if ($bytes_read != SK_HEADER_LENGTH) {
|
||||
warnf('Could not read security at 0x%x', $offset);
|
||||
return;
|
||||
}
|
||||
|
||||
my ($length,
|
||||
$sig,
|
||||
$offset_to_previous,
|
||||
$offset_to_next,
|
||||
$ref_count,
|
||||
$sd_length,
|
||||
) = unpack('Va2x2VVVV', $sk_header);
|
||||
|
||||
$offset_to_previous += OFFSET_TO_FIRST_HBIN
|
||||
if $offset_to_previous != 0xffffffff;
|
||||
$offset_to_next += OFFSET_TO_FIRST_HBIN
|
||||
if $offset_to_next != 0xffffffff;
|
||||
|
||||
my $allocated = 0;
|
||||
if ($length > 0x7fffffff) {
|
||||
$allocated = 1;
|
||||
$length = (0xffffffff - $length) + 1;
|
||||
}
|
||||
# allocated should be true
|
||||
|
||||
if ($sig ne 'sk') {
|
||||
warnf('Invalid signature for security at 0x%x', $offset);
|
||||
return;
|
||||
}
|
||||
|
||||
$bytes_read = sysread($fh, my $sd_data, $sd_length);
|
||||
if ($bytes_read != $sd_length) {
|
||||
warnf('Could not read security descriptor for security at 0x%x',
|
||||
$offset);
|
||||
return;
|
||||
}
|
||||
|
||||
my $sd = unpack_security_descriptor($sd_data);
|
||||
if (!defined $sd) {
|
||||
warnf('Invalid security descriptor for security at 0x%x',
|
||||
$offset);
|
||||
# Abandon security object if security descriptor is invalid
|
||||
return;
|
||||
}
|
||||
|
||||
my $self = {};
|
||||
$self->{_regfile} = $regfile;
|
||||
$self->{_offset} = $offset;
|
||||
$self->{_length} = $length;
|
||||
$self->{_allocated} = $allocated;
|
||||
$self->{_tag} = $sig;
|
||||
$self->{_offset_to_previous} = $offset_to_previous;
|
||||
$self->{_offset_to_next} = $offset_to_next;
|
||||
$self->{_ref_count} = $ref_count;
|
||||
$self->{_security_descriptor_length} = $sd_length;
|
||||
$self->{_security_descriptor} = $sd;
|
||||
bless $self, $class;
|
||||
|
||||
if (defined(my $cache = $regfile->{_security_cache})) {
|
||||
$cache->{$offset} = $self;
|
||||
}
|
||||
|
||||
return $self;
|
||||
}
|
||||
|
||||
sub get_previous {
|
||||
my $self = shift;
|
||||
my $regfile = $self->{_regfile};
|
||||
my $offset_to_previous = $self->{_offset_to_previous};
|
||||
|
||||
return Parse::Win32Registry::WinNT::Security->new($regfile,
|
||||
$offset_to_previous);
|
||||
}
|
||||
|
||||
sub get_next {
|
||||
my $self = shift;
|
||||
my $regfile = $self->{_regfile};
|
||||
my $offset_to_next = $self->{_offset_to_next};
|
||||
|
||||
return Parse::Win32Registry::WinNT::Security->new($regfile,
|
||||
$offset_to_next);
|
||||
}
|
||||
|
||||
sub get_reference_count {
|
||||
my $self = shift;
|
||||
|
||||
return $self->{_ref_count};
|
||||
}
|
||||
|
||||
sub get_security_descriptor {
|
||||
my $self = shift;
|
||||
|
||||
return $self->{_security_descriptor};
|
||||
}
|
||||
|
||||
sub as_string {
|
||||
my $self = shift;
|
||||
|
||||
return '(security entry)';
|
||||
}
|
||||
|
||||
sub parse_info {
|
||||
my $self = shift;
|
||||
|
||||
my $info = sprintf '0x%x sk len=0x%x alloc=%d prev=0x%x,next=0x%x refs=%d',
|
||||
$self->{_offset},
|
||||
$self->{_length},
|
||||
$self->{_allocated},
|
||||
$self->{_offset_to_previous},
|
||||
$self->{_offset_to_next},
|
||||
$self->{_ref_count};
|
||||
|
||||
return $info;
|
||||
}
|
||||
|
||||
1;
|
332
thirdparty/rr/Parse/Win32Registry/WinNT/Value.pm
vendored
Normal file
332
thirdparty/rr/Parse/Win32Registry/WinNT/Value.pm
vendored
Normal file
@ -0,0 +1,332 @@
|
||||
package Parse::Win32Registry::WinNT::Value;
|
||||
|
||||
use strict;
|
||||
use warnings;
|
||||
|
||||
use base qw(Parse::Win32Registry::Value);
|
||||
|
||||
use Carp;
|
||||
use Encode;
|
||||
use Parse::Win32Registry::Base qw(:all);
|
||||
|
||||
use constant VK_HEADER_LENGTH => 0x18;
|
||||
use constant OFFSET_TO_FIRST_HBIN => 0x1000;
|
||||
|
||||
sub new {
|
||||
my $class = shift;
|
||||
my $regfile = shift;
|
||||
my $offset = shift; # offset to vk record relative to first hbin
|
||||
|
||||
croak 'Missing registry file' if !defined $regfile;
|
||||
croak 'Missing offset' if !defined $offset;
|
||||
|
||||
my $fh = $regfile->get_filehandle;
|
||||
|
||||
# 0x00 dword = value length (negative = allocated)
|
||||
# 0x04 word = 'vk' signature
|
||||
# 0x06 word = value name length
|
||||
# 0x08 dword = value data length (bit 31 set => data stored inline)
|
||||
# 0x0c dword = offset to data/inline data
|
||||
# 0x10 dword = value type
|
||||
# 0x14 word = flags (bit 1 set => compressed name)
|
||||
# 0x16 word
|
||||
# 0x18 = value name [for value name length bytes]
|
||||
|
||||
# Extracted offsets are always relative to first hbin
|
||||
|
||||
sysseek($fh, $offset, 0);
|
||||
my $bytes_read = sysread($fh, my $vk_header, VK_HEADER_LENGTH);
|
||||
if ($bytes_read != VK_HEADER_LENGTH) {
|
||||
warnf('Could not read value at 0x%x', $offset);
|
||||
return;
|
||||
}
|
||||
|
||||
my ($length,
|
||||
$sig,
|
||||
$name_length,
|
||||
$data_length,
|
||||
$offset_to_data,
|
||||
$type,
|
||||
$flags,
|
||||
) = unpack('Va2vVVVv', $vk_header);
|
||||
|
||||
my $allocated = 0;
|
||||
if ($length > 0x7fffffff) {
|
||||
$allocated = 1;
|
||||
$length = (0xffffffff - $length) + 1;
|
||||
}
|
||||
# allocated should be true
|
||||
|
||||
if ($length < VK_HEADER_LENGTH) {
|
||||
warnf('Invalid value entry length at 0x%x', $offset);
|
||||
return;
|
||||
}
|
||||
|
||||
if ($sig ne 'vk') {
|
||||
warnf('Invalid signature for value at 0x%x', $offset);
|
||||
return;
|
||||
}
|
||||
|
||||
$bytes_read = sysread($fh, my $name, $name_length);
|
||||
if ($bytes_read != $name_length) {
|
||||
warnf('Could not read name for value at 0x%x', $offset);
|
||||
return;
|
||||
}
|
||||
|
||||
if ($flags & 1) {
|
||||
$name = decode($Parse::Win32Registry::Base::CODEPAGE, $name);
|
||||
}
|
||||
else {
|
||||
$name = decode('UCS-2LE', $name);
|
||||
};
|
||||
|
||||
# If the top bit of the data_length is set, then
|
||||
# the value is inline and stored in the offset to data field (at 0xc).
|
||||
my $data;
|
||||
my $data_inline = $data_length >> 31;
|
||||
if ($data_inline) {
|
||||
# REG_DWORDs are always inline, but I've also seen
|
||||
# REG_SZ, REG_BINARY, REG_EXPAND_SZ, and REG_NONE inline
|
||||
$data_length &= 0x7fffffff;
|
||||
if ($data_length > 4) {
|
||||
warnf("Invalid inline data length for value '%s' at 0x%x",
|
||||
$name, $offset);
|
||||
$data = undef;
|
||||
}
|
||||
else {
|
||||
# unpack inline data from header
|
||||
$data = substr($vk_header, 0xc, $data_length);
|
||||
}
|
||||
}
|
||||
else {
|
||||
if ($offset_to_data != 0 && $offset_to_data != 0xffffffff) {
|
||||
$offset_to_data += OFFSET_TO_FIRST_HBIN;
|
||||
if ($offset_to_data < ($regfile->get_length - $data_length)) {
|
||||
$data = _extract_data($fh, $offset_to_data, $data_length);
|
||||
}
|
||||
else {
|
||||
warnf("Invalid offset to data for value '%s' at 0x%x",
|
||||
$name, $offset);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
my $self = {};
|
||||
$self->{_regfile} = $regfile;
|
||||
$self->{_offset} = $offset;
|
||||
$self->{_length} = $length;
|
||||
$self->{_allocated} = $allocated;
|
||||
$self->{_tag} = $sig;
|
||||
$self->{_name} = $name;
|
||||
$self->{_name_length} = $name_length;
|
||||
$self->{_type} = $type;
|
||||
$self->{_data} = $data;
|
||||
$self->{_data_length} = $data_length;
|
||||
$self->{_data_inline} = $data_inline;
|
||||
$self->{_offset_to_data} = $offset_to_data;
|
||||
$self->{_flags} = $flags;
|
||||
bless $self, $class;
|
||||
|
||||
return $self;
|
||||
}
|
||||
|
||||
sub _extract_data {
|
||||
my $fh = shift;
|
||||
my $offset_to_data = shift;
|
||||
my $data_length = shift;
|
||||
|
||||
if ($offset_to_data == 0 || $offset_to_data == 0xffffffff) {
|
||||
return undef;
|
||||
}
|
||||
|
||||
sysseek($fh, $offset_to_data, 0);
|
||||
my $bytes_read = sysread($fh, my $data_header, 4);
|
||||
if ($bytes_read != 4) {
|
||||
warnf('Could not read data at 0x%x', $offset_to_data);
|
||||
return undef;
|
||||
}
|
||||
|
||||
my ($max_data_length) = unpack('V', $data_header);
|
||||
|
||||
my $data_allocated = 0;
|
||||
if ($max_data_length > 0x7fffffff) {
|
||||
$data_allocated = 1;
|
||||
$max_data_length = (0xffffffff - $max_data_length) + 1;
|
||||
}
|
||||
# data_allocated should be true
|
||||
|
||||
my $data;
|
||||
|
||||
if ($data_length > $max_data_length) {
|
||||
$bytes_read = sysread($fh, my $db_entry, 8);
|
||||
if ($bytes_read != 8) {
|
||||
warnf('Could not read data at 0x%x', $offset_to_data);
|
||||
return undef;
|
||||
}
|
||||
|
||||
my ($sig, $num_data_blocks, $offset_to_data_block_list)
|
||||
= unpack('a2vV', $db_entry);
|
||||
if ($sig ne 'db') {
|
||||
warnf('Invalid signature for big data at 0x%x', $offset_to_data);
|
||||
return undef;
|
||||
}
|
||||
$offset_to_data_block_list += OFFSET_TO_FIRST_HBIN;
|
||||
|
||||
sysseek($fh, $offset_to_data_block_list + 4, 0);
|
||||
$bytes_read = sysread($fh, my $data_block_list, $num_data_blocks * 4);
|
||||
if ($bytes_read != $num_data_blocks * 4) {
|
||||
warnf('Could not read data block list at 0x%x',
|
||||
$offset_to_data_block_list);
|
||||
return undef;
|
||||
}
|
||||
|
||||
$data = "";
|
||||
my @offsets = map { OFFSET_TO_FIRST_HBIN + $_ }
|
||||
unpack("V$num_data_blocks", $data_block_list);
|
||||
foreach my $offset (@offsets) {
|
||||
sysseek($fh, $offset, 0);
|
||||
$bytes_read = sysread($fh, my $block_header, 4);
|
||||
if ($bytes_read != 4) {
|
||||
warnf('Could not read data block at 0x%x', $offset);
|
||||
return undef;
|
||||
}
|
||||
my ($block_length) = unpack('V', $block_header);
|
||||
if ($block_length > 0x7fffffff) {
|
||||
$block_length = (0xffffffff - $block_length) + 1;
|
||||
}
|
||||
$bytes_read = sysread($fh, my $block_data, $block_length - 8);
|
||||
if ($bytes_read != $block_length - 8) {
|
||||
warnf('Could not read data block at 0x%x', $offset);
|
||||
return undef;
|
||||
}
|
||||
$data .= $block_data;
|
||||
}
|
||||
if (length($data) < $data_length) {
|
||||
warnf("Insufficient data blocks for data at 0x%x", $offset_to_data);
|
||||
return undef;
|
||||
}
|
||||
$data = substr($data, 0, $data_length);
|
||||
return $data;
|
||||
}
|
||||
else {
|
||||
$bytes_read = sysread($fh, $data, $data_length);
|
||||
if ($bytes_read != $data_length) {
|
||||
warnf("Could not read data at 0x%x", $offset_to_data);
|
||||
return undef;
|
||||
}
|
||||
}
|
||||
return $data;
|
||||
}
|
||||
|
||||
sub get_data {
|
||||
my $self = shift;
|
||||
|
||||
my $type = $self->get_type;
|
||||
|
||||
my $data = $self->{_data};
|
||||
return if !defined $data;
|
||||
|
||||
# apply decoding to appropriate data types
|
||||
if ($type == REG_DWORD) {
|
||||
if (length($data) == 4) {
|
||||
$data = unpack('V', $data);
|
||||
}
|
||||
else {
|
||||
# incorrect length for dword data
|
||||
$data = undef;
|
||||
}
|
||||
}
|
||||
elsif ($type == REG_DWORD_BIG_ENDIAN) {
|
||||
if (length($data) == 4) {
|
||||
$data = unpack('N', $data);
|
||||
}
|
||||
else {
|
||||
# incorrect length for dword data
|
||||
$data = undef;
|
||||
}
|
||||
}
|
||||
elsif ($type == REG_SZ || $type == REG_EXPAND_SZ) {
|
||||
$data = decode('UCS-2LE', $data);
|
||||
# snip off any terminating null
|
||||
chop $data if substr($data, -1, 1) eq "\0";
|
||||
}
|
||||
elsif ($type == REG_MULTI_SZ) {
|
||||
$data = decode('UCS-2LE', $data);
|
||||
# snip off any terminating nulls
|
||||
chop $data if substr($data, -1, 1) eq "\0";
|
||||
chop $data if substr($data, -1, 1) eq "\0";
|
||||
my @multi_sz = split("\0", $data, -1);
|
||||
# make sure there is at least one empty string
|
||||
@multi_sz = ('') if @multi_sz == 0;
|
||||
return wantarray ? @multi_sz : join($", @multi_sz);
|
||||
}
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
sub as_regedit_export {
|
||||
my $self = shift;
|
||||
my $version = shift || 5;
|
||||
|
||||
my $name = $self->get_name;
|
||||
my $export = $name eq '' ? '@=' : '"' . $name . '"=';
|
||||
|
||||
my $type = $self->get_type;
|
||||
|
||||
# XXX
|
||||
# if (!defined $self->{_data}) {
|
||||
# $name = $name eq '' ? '@' : qq{"$name"};
|
||||
# return qq{; $name=(invalid data)\n};
|
||||
# }
|
||||
|
||||
if ($type == REG_SZ) {
|
||||
$export .= '"' . $self->get_data . '"';
|
||||
$export .= "\n";
|
||||
}
|
||||
elsif ($type == REG_BINARY) {
|
||||
$export .= "hex:";
|
||||
$export .= format_octets($self->{_data}, length($export));
|
||||
}
|
||||
elsif ($type == REG_DWORD) {
|
||||
my $data = $self->get_data;
|
||||
$export .= defined($data)
|
||||
? sprintf("dword:%08x", $data)
|
||||
: "dword:";
|
||||
$export .= "\n";
|
||||
}
|
||||
elsif ($type == REG_EXPAND_SZ || $type == REG_MULTI_SZ) {
|
||||
my $data = $version == 4
|
||||
? encode("ascii", $self->{_data}) # unicode->ascii
|
||||
: $self->{_data}; # raw data
|
||||
$export .= sprintf("hex(%x):", $type);
|
||||
$export .= format_octets($data, length($export));
|
||||
}
|
||||
else {
|
||||
$export .= sprintf("hex(%x):", $type);
|
||||
$export .= format_octets($self->{_data}, length($export));
|
||||
}
|
||||
return $export;
|
||||
}
|
||||
|
||||
sub parse_info {
|
||||
my $self = shift;
|
||||
|
||||
my $info = sprintf '0x%x vk len=0x%x alloc=%d "%s" type=%d',
|
||||
$self->{_offset},
|
||||
$self->{_length},
|
||||
$self->{_allocated},
|
||||
$self->{_name},
|
||||
$self->{_type};
|
||||
if ($self->{_data_inline}) {
|
||||
$info .= sprintf ' data=inline,len=0x%x',
|
||||
$self->{_data_length};
|
||||
}
|
||||
else {
|
||||
$info .= sprintf ' data=0x%x,len=0x%x',
|
||||
$self->{_offset_to_data},
|
||||
$self->{_data_length};
|
||||
}
|
||||
return $info;
|
||||
}
|
||||
|
||||
1;
|
2
thirdparty/rr/rip.pl
vendored
2
thirdparty/rr/rip.pl
vendored
@ -1,4 +1,4 @@
|
||||
#! c:\perl\bin\perl.exe
|
||||
#! /usr/bin/perl
|
||||
#-------------------------------------------------------------------------
|
||||
# Rip - RegRipper, CLI version
|
||||
# Use this utility to run a plugins file or a single plugin against a Reg
|
||||
|
2
thirdparty/rr/rr.pl
vendored
2
thirdparty/rr/rr.pl
vendored
@ -1,4 +1,4 @@
|
||||
#! c:\perl\bin\perl.exe
|
||||
#!/usr/bin/perl
|
||||
#-----------------------------------------------------------
|
||||
# Registry Ripper
|
||||
# Parse a Registry hive file for data pertinent to an investigation
|
||||
|
Loading…
x
Reference in New Issue
Block a user