[libsendmail-milter-perl] 07/20: Imported Upstream version 0.51
Hilko Bengen
bengen at moszumanska.debian.org
Sun Sep 27 18:32:52 UTC 2015
This is an automated email from the git hooks/post-receive script.
bengen pushed a commit to annotated tag debian/1.0-1
in repository libsendmail-milter-perl.
commit c46a09d28f38ccef8d1a97f6811218bbc15bb5ae
Author: Hilko Bengen <bengen at debian.org>
Date: Thu Dec 3 10:19:24 2009 +0100
Imported Upstream version 0.51
---
Changes | 27 +
MANIFEST | 4 +
META.yml | 2 +-
Makefile.PL | 5 +
bin/regcompare.pl | 448 ++++++++++--
bin/regmultidiff.pl | 10 +-
bin/regscope.pl | 1200 ++++++++++++++++++++++++++++++++
bin/regview.pl | 331 ++++++---
lib/Parse/Win32Registry.pm | 64 +-
lib/Parse/Win32Registry/Base.pm | 21 +-
lib/Parse/Win32Registry/Key.pm | 5 +-
lib/Parse/Win32Registry/Win95/Key.pm | 14 +
lib/Parse/Win32Registry/Win95/Value.pm | 10 +
lib/Parse/Win32Registry/WinNT/Key.pm | 32 +
lib/Parse/Win32Registry/WinNT/Value.pm | 14 +
t/iterator.t | 253 +++++++
t/use.t | 2 +-
t/win95_iter_tests.rf | Bin 0 -> 655 bytes
t/winnt_iter_tests.rf | Bin 0 -> 5376 bytes
19 files changed, 2280 insertions(+), 162 deletions(-)
diff --git a/Changes b/Changes
index 78c31a7..ab802cc 100644
--- a/Changes
+++ b/Changes
@@ -1,5 +1,32 @@
Revision history for Perl extension Parse::Win32Registry.
+** 0.51 2009-10-04
+
+Added new regscope.pl script, a GTK+ registry entry viewer that uses color
+to highlight different types of registry entries.
+
+Documented the get_name method of SID objects and the get_value_data
+method of Key objects. The as_string method of the ACE object and the
+as_stanza method of the SecurityDescriptor object now include the well
+known SID names (as returned each SID object's get_name method) for
+each SID object.
+
+Updated the regview.pl and regcompare.pl scripts: regview.pl and
+regcompare.pl can now select keys and/or values when searching,
+regview.pl can now sort columns (e.g. keys can be sorted by timestamp,
+values by type, etc), regcompare.pl can now bookmark keys or values,
+and regview.pl now has a basic report view.
+
+Fixed the get_subtree_iter and make_multiple_subkey_iterator methods
+to return the root key(s) of the subtree(s) as the documentation
+indicates. regview.pl, regmultidiff.pl, and regcompare.pl amended to
+accommodate these changes.
+
+Fixed redisplay problem closing dialogs using Escape in regview.pl and
+regcompare.pl.
+
+Makefile.pl now includes all scripts as exe_files.
+
** 0.50 2009-07-19
Security information is now extracted from Windows NT registry files.
diff --git a/MANIFEST b/MANIFEST
index 6878b71..3da569f 100644
--- a/MANIFEST
+++ b/MANIFEST
@@ -10,6 +10,7 @@ bin/regexport.pl
bin/regfind.pl
bin/regmultidiff.pl
bin/regscan.pl
+bin/regscope.pl
bin/regsecurity.pl
bin/regshell.pl
bin/regstats.pl
@@ -36,6 +37,7 @@ t/constants.t
t/entry.t
t/errors.t
t/file.t
+t/iterator.t
t/key.t
t/misc.t
t/security.t
@@ -57,9 +59,11 @@ t/invalid_regf_header.rf
t/invalid_rgkn_header.rf
t/missing_rgkn_header.rf
t/win95_error_tests.rf
+t/win95_iter_tests.rf
t/win95_key_tests.rf
t/win95_value_tests.rf
t/winnt_error_tests.rf
+t/winnt_iter_tests.rf
t/winnt_key_tests.rf
t/winnt_security_tests.rf
t/winnt_value_tests.rf
diff --git a/META.yml b/META.yml
index 871403f..6d9c8e1 100644
--- a/META.yml
+++ b/META.yml
@@ -1,6 +1,6 @@
--- #YAML:1.0
name: Parse-Win32Registry
-version: 0.50
+version: 0.51
abstract: Parse Windows Registry Files
license: ~
author:
diff --git a/Makefile.PL b/Makefile.PL
index 747baac..3ed244a 100644
--- a/Makefile.PL
+++ b/Makefile.PL
@@ -16,11 +16,16 @@ WriteMakefile(
AUTHOR => 'James Macfarlane',
EXE_FILES => [
'bin/regclassnames.pl',
+ 'bin/regcompare.pl',
'bin/regdiff.pl',
'bin/regdump.pl',
'bin/regexport.pl',
'bin/regfind.pl',
+ 'bin/regmultidiff.pl',
'bin/regscan.pl',
+ 'bin/regscope.pl',
+ 'bin/regsecurity.pl',
+ 'bin/regshell.pl',
'bin/regstats.pl',
'bin/regtimeline.pl',
'bin/regtree.pl',
diff --git a/bin/regcompare.pl b/bin/regcompare.pl
index b6e5ee6..05452b5 100755
--- a/bin/regcompare.pl
+++ b/bin/regcompare.pl
@@ -5,9 +5,15 @@ use warnings;
use Glib ':constants';
use Gtk2 -init;
+my $screen = Gtk2::Gdk::Screen->get_default;
+my $window_width = $screen->get_width * 0.9;
+my $window_height = $screen->get_height * 0.8;
+$window_width = 1100 if $window_width > 1100;
+$window_height = 900 if $window_height > 900;
+
use File::Basename;
use File::Spec;
-use Parse::Win32Registry 0.50 qw( make_multiple_subtree_iterator
+use Parse::Win32Registry 0.51 qw( make_multiple_subtree_iterator
make_multiple_subkey_iterator
make_multiple_value_iterator
compare_multiple_keys
@@ -176,6 +182,7 @@ my @actions = (
# name, stock id, label
['FileMenu', undef, '_File'],
['SearchMenu', undef, '_Search'],
+ ['BookmarksMenu', undef, '_Bookmarks'],
['ViewMenu', undef, '_View'],
['HelpMenu', undef, '_Help'],
# name, stock-id, label, accelerator, tooltip, callback
@@ -184,8 +191,11 @@ my @actions = (
['Quit', 'gtk-quit', '_Quit', '<control>Q', undef, \&quit],
['Find', 'gtk-find', '_Find', '<control>F', undef, \&find],
['FindNext', undef, 'Find Next', '<control>G', undef, \&find_next],
+ ['FindNext2', undef, 'Find Next', 'F3', undef, \&find_next],
['FindChange', 'gtk-find', 'Find _Change', '<control>N', undef, \&find_change],
['FindNextChange', undef, 'Find Next Change', '<control>M', undef, \&find_next_change],
+ ['AddBookmark', 'gtk-add', '_Add Bookmark', '<control>D', undef, \&add_bookmark],
+ ['EditBookmarks', undef, '_Edit Bookmarks', '<control>B', undef, \&edit_bookmarks],
['About', 'gtk-about', '_About', undef, undef, \&about],
);
@@ -194,11 +204,17 @@ $action_group->add_actions(\@actions, undef);
my @toggle_actions = (
# name, stock id, label, accelerator, tooltip, callback, active
- ['ShowDetail', undef, 'Show _Detail', '<control>X', undef, \&toggle_item_detail, TRUE],
+ ['ShowToolbar', undef, 'Show _Toolbar', '<control>T', undef, \&toggle_toolbar, TRUE],
+ ['ShowDetail', 'gtk-edit', 'Show _Detail', '<control>X', undef, \&toggle_item_detail, TRUE],
);
$action_group->add_toggle_actions(\@toggle_actions, undef);
+my $action_group2 = Gtk2::ActionGroup->new('actions2'); # bookmarks
+my $bookmarks_merge_id = $uimanager->new_merge_id;
+my $action_name = 1; # unique action name
+
$uimanager->insert_action_group($action_group, 0);
+$uimanager->insert_action_group($action_group2, 1);
my $ui_info = <<END_OF_UI;
<ui>
@@ -216,8 +232,15 @@ my $ui_info = <<END_OF_UI;
<menuitem action='FindChange'/>
<menuitem action='FindNextChange'/>
</menu>
+ <menu action='BookmarksMenu'>
+ <menuitem action='AddBookmark'/>
+ <menuitem action='EditBookmarks'/>
+ <separator/>
+ </menu>
<menu action='ViewMenu'>
+ <menuitem action='ShowToolbar'/>
<menuitem action='ShowDetail'/>
+ <separator/>
</menu>
<menu action='HelpMenu'>
<menuitem action='About'/>
@@ -232,6 +255,7 @@ my $ui_info = <<END_OF_UI;
<separator/>
<toolitem action='Quit'/>
</toolbar>
+ <accelerator action='FindNext2'/>
</ui>
END_OF_UI
@@ -239,6 +263,7 @@ $uimanager->add_ui_from_string($ui_info);
my $menubar = $uimanager->get_widget('/MenuBar');
my $toolbar = $uimanager->get_widget('/ToolBar');
+my $bookmarks_menu = $uimanager->get_widget('/MenuBar/BookmarksMenu')->get_submenu;
### STATUSBAR
@@ -246,7 +271,7 @@ my $statusbar = Gtk2::Statusbar->new;
### VBOX
-my $main_vbox = Gtk2::VBox->new;
+my $main_vbox = Gtk2::VBox->new(FALSE, 0);
$main_vbox->pack_start($menubar, FALSE, FALSE, 0);
$main_vbox->pack_start($toolbar, FALSE, FALSE, 0);
$main_vbox->pack_start($vpaned1, TRUE, TRUE, 0);
@@ -255,7 +280,7 @@ $main_vbox->pack_start($statusbar, FALSE, FALSE, 0);
### WINDOW
my $window = Gtk2::Window->new;
-$window->set_default_size(600, 400);
+$window->set_default_size($window_width, $window_height);
$window->set_position('center');
$window->signal_connect(destroy => sub { Gtk2->main_quit });
$window->add($main_vbox);
@@ -301,10 +326,14 @@ sub build_open_files_dialog {
'gtk-remove' => 50,
'gtk-ok' => 'ok',
);
- $dialog->set_size_request(-1, 400);
+ $dialog->set_size_request($window_width * 0.8, $window_height * 0.8);
$dialog->vbox->add($scrolled_file_view);
$dialog->set_default_response('ok');
+ $dialog->signal_connect(delete_event => sub {
+ $dialog->hide;
+ return TRUE;
+ });
$dialog->signal_connect(response => sub {
my ($dialog, $response) = @_;
if ($response eq '70') {
@@ -347,6 +376,119 @@ sub build_open_files_dialog {
my $open_files_dialog = build_open_files_dialog;
+### BOOKMARKS STORE
+
+use constant {
+ BMCOL_NAME => 0,
+ BMCOL_LOCATION => 1,
+ BMCOL_ICON => 2,
+};
+
+my $bookmark_store = Gtk2::ListStore->new(
+ 'Glib::String', 'Glib::Scalar', 'Glib::String',
+);
+
+sub build_bookmarks_dialog {
+ my $bookmark_view = Gtk2::TreeView->new($bookmark_store);
+ $bookmark_view->set_reorderable(TRUE);
+
+ my $bookmark_icon_cell = Gtk2::CellRendererPixbuf->new;
+ my $bookmark_name_cell = Gtk2::CellRendererText->new;
+ my $bookmark_column0 = Gtk2::TreeViewColumn->new;
+ $bookmark_column0->set_title('Bookmark');
+ $bookmark_column0->pack_start($bookmark_icon_cell, FALSE);
+ $bookmark_column0->pack_start($bookmark_name_cell, TRUE);
+ $bookmark_column0->set_attributes($bookmark_icon_cell,
+ 'stock-id', BMCOL_ICON);
+ $bookmark_column0->set_attributes($bookmark_name_cell,
+ 'text', BMCOL_NAME);
+ $bookmark_column0->set_resizable(TRUE);
+ $bookmark_view->append_column($bookmark_column0);
+
+ my $bookmark_location_cell = Gtk2::CellRendererText->new;
+ my $bookmark_column1 = $bookmark_view->insert_column_with_data_func(
+ 1, 'Location', $bookmark_location_cell,
+ sub {
+ my ($column, $cell, $model, $iter, $num) = @_;
+ my $location = $model->get($iter, BMCOL_LOCATION);
+ if (defined $location) {
+ my ($subkey_path, $value_name) = @$location;
+ my $string = $subkey_path;
+ if (defined $value_name) {
+ $value_name = '(Default)' if $value_name eq '';
+ $string .= ", $value_name";
+ }
+ $cell->set('text', $string);
+ }
+ else {
+ $cell->set('text', '?');
+ }
+ },
+ );
+ $bookmark_location_cell->set('ellipsize', 'end');
+
+ my $scrolled_bookmark_view = Gtk2::ScrolledWindow->new;
+ $scrolled_bookmark_view->set_policy('automatic', 'automatic');
+ $scrolled_bookmark_view->set_shadow_type('in');
+ $scrolled_bookmark_view->add($bookmark_view);
+
+ my $label = Gtk2::Label->new;
+ $label->set_markup('<i>Drag bookmarks to reorder them</i>');
+
+ my $dialog = Gtk2::Dialog->new('Edit Bookmarks', $window, 'modal',
+ 'gtk-remove' => 50,
+ 'gtk-ok' => 'ok',
+ );
+ $dialog->resize($window_width * 0.8, $window_height * 0.8);
+ $dialog->vbox->pack_start($scrolled_bookmark_view, TRUE, TRUE, 0);
+ $dialog->vbox->pack_start($label, FALSE, FALSE, 5);
+ $dialog->set_default_response('ok');
+
+ $dialog->signal_connect(delete_event => sub {
+ $dialog->hide;
+ return TRUE;
+ });
+ $dialog->signal_connect(response => sub {
+ my ($dialog, $response) = @_;
+ if ($response eq '50') {
+ # Remove selected bookmark
+ my $selection = $bookmark_view->get_selection;
+ my $iter = $selection->get_selected;
+ if (defined $iter) {
+ $bookmark_store->remove($iter);
+ }
+ }
+ else {
+ # Before exiting, move menuitems into current bookmark order
+ $uimanager->remove_ui($bookmarks_merge_id);
+ $uimanager->ensure_update;
+ foreach my $action ($action_group2->list_actions) {
+ $action_group2->remove_action($action);
+ }
+ $action_name = 1;
+ my $iter = $bookmark_store->get_iter_first;
+ while (defined $iter) {
+ my $bookmark_name = $bookmark_store->get($iter, BMCOL_NAME);
+ my $location = $bookmark_store->get($iter, BMCOL_LOCATION);
+ my $icon = $bookmark_store->get($iter, BMCOL_ICON);
+ my $display_name = $bookmark_name;
+ $display_name =~ s/_/__/g;
+ $action_group2->add_actions([
+ [$action_name, $icon, $display_name, undef, undef, \&go_to_bookmark],
+ ], $location);
+ $uimanager->add_ui($bookmarks_merge_id, '/MenuBar/BookmarksMenu', $action_name, $action_name, 'menuitem', FALSE);
+ $action_name++;
+ $iter = $bookmark_store->iter_next($iter);
+ }
+ $dialog->hide;
+ }
+ });
+
+ return $dialog;
+}
+
+my $bookmarks_dialog = build_bookmarks_dialog;
+
######################## GLOBAL SETUP
my @filenames = ();
@@ -354,7 +496,9 @@ my @root_keys = ();
my $last_dir;
-my $find_param;
+my $search_keys = TRUE;
+my $search_values = TRUE;
+my $find_param = '';
my $find_iter;
my $change_iter;
@@ -423,6 +567,16 @@ sub toggle_item_detail {
}
}
+sub toggle_toolbar {
+ my ($toggle_action) = @_;
+ if ($toggle_action->get_active) {
+ $toolbar->show;
+ }
+ else {
+ $toolbar->hide;
+ }
+}
+
sub tree_item_selected {
my ($tree_selection) = @_;
@@ -552,11 +706,12 @@ sub compare_files {
my $color = $model->get($iter, TREECOL_COLOR);
if (defined $changes) {
my $diff = substr($changes->[$num], 0, 1);
- $cell->set('text', $diff || '.');
+ $cell->set('text', $diff || "\x{00bb}");
$cell->set('foreground', $color);
}
else {
- $cell->set('text', '.');
+ $cell->set('text', "\x{00b7}");
+ $cell->set('foreground', $color);
}
},
$num, # additional data is passed to callback
@@ -607,11 +762,18 @@ sub add_children {
while (defined(my $subkeys = $subkeys_iter->get_next)) {
my @changes = compare_multiple_keys(@$subkeys);
+ my $num_changes = grep { $_ } @changes;
+ # insert a 'blank' change for missing subkeys
+ for (my $i = 0; $i < @changes; $i++) {
+ if ($changes[$i] eq '' && !defined $subkeys->[$i]) {
+ $changes[$i] = ' ';
+ }
+ }
my $any_subkey = (grep { defined } @$subkeys)[0];
my $iter = $model->append($parent_iter);
- my $num_changes = grep { $_ } @changes;
+
if ($num_changes > 0) {
$model->set($iter,
TREECOL_NAME, $any_subkey->get_name,
@@ -635,13 +797,20 @@ sub add_children {
while (defined(my $values = $values_iter->get_next)) {
my @changes = compare_multiple_values(@$values);
+ my $num_changes = grep { $_ } @changes;
+ # insert a 'blank' change for missing values
+ for (my $i = 0; $i < @changes; $i++) {
+ if ($changes[$i] eq '' && !defined $values->[$i]) {
+ $changes[$i] = " ";
+ }
+ }
my $any_value = (grep { defined } @$values)[0];
-
my $name = $any_value->get_name;
$name = "(Default)" if $name eq '';
+
my $iter = $model->append($parent_iter);
- my $num_changes = grep { $_ } @changes;
+
if ($num_changes > 0) {
$model->set($iter,
TREECOL_NAME, $name,
@@ -751,6 +920,7 @@ sub show_message {
'ok',
$message,
);
+ $dialog->set_title(ucfirst $type);
$dialog->run;
$dialog->destroy;
}
@@ -792,8 +962,8 @@ sub go_to_subkey_and_value {
? ($subkey_path)
: split(/\\/, $subkey_path, -1);
- my $root_iter = $tree_store->get_iter_first;
- my $iter = $root_iter;
+ my $iter = $tree_store->get_iter_first;
+ return if !defined $iter; # no registry loaded
while (defined(my $subkey_name = shift @path_components)) {
my $items = $tree_store->get($iter, TREECOL_ITEMS);
@@ -813,10 +983,12 @@ sub go_to_subkey_and_value {
if (!defined $iter) {
return; # no matching child iter
}
-
}
+ my $parent_iter = $tree_store->iter_parent($iter);
+ my $parent_path = $tree_store->get_path($parent_iter);
+ $tree_view->expand_to_path($parent_path);
my $tree_path = $tree_store->get_path($iter);
- $tree_view->expand_to_path($tree_path);
+# $tree_view->expand_to_path($tree_path);
$tree_view->scroll_to_cell($tree_path);
$tree_view->set_cursor($tree_path);
$window->set_focus($tree_view);
@@ -825,19 +997,33 @@ sub go_to_subkey_and_value {
}
}
+sub get_search_message {
+ my $message;
+ if ($search_keys && $search_values) {
+ $message = "Searching registry keys and values...";
+ }
+ elsif ($search_keys) {
+ $message = "Searching registry keys...";
+ }
+ elsif ($search_values) {
+ $message = "Searching registry values...";
+ }
+ return $message;
+}
+
sub find_next {
if (!defined $find_param || !defined $find_iter) {
return;
}
my $label = Gtk2::Label->new;
- $label->set_text("Searching registry...");
+ $label->set_text(get_search_message);
my $dialog = Gtk2::Dialog->new('Find',
$window,
'modal',
'gtk-cancel' => 'cancel',
);
- $dialog->vbox->pack_start($label, TRUE, TRUE, 10);
+ $dialog->vbox->pack_start($label, TRUE, TRUE, 5);
$dialog->set_default_response('cancel');
$dialog->show_all;
@@ -846,57 +1032,82 @@ sub find_next {
if (!defined $keys_ref) {
$dialog->response('ok');
- show_message('info', 'Finished searching.');
return FALSE; # stop searching
}
# Obtain the name and path from the first defined key
my $any_key = (grep { defined } @$keys_ref)[0];
my $subkey_path = (split(/\\/, $any_key->get_path, 2))[1];
+ if (!defined $subkey_path) {
+ return TRUE;
+ }
+ # Check values (if defined) for a match
if (defined $values_ref) {
- my $any_value = (grep { defined } @$values_ref)[0];
- my $value_name = $any_value->get_name;
- if (index(lc $value_name, lc $find_param) >= 0) {
- go_to_subkey_and_value($subkey_path, $value_name);
- $dialog->response('ok');
- return FALSE; # stop searching
- }
- else {
- return TRUE; # continue searching
+ if ($search_values) {
+ my $any_value = (grep { defined } @$values_ref)[0];
+ my $value_name = $any_value->get_name;
+ if (index(lc $value_name, lc $find_param) >= 0) {
+ go_to_subkey_and_value($subkey_path, $value_name);
+ $dialog->response(50);
+ return FALSE; # stop searching
+ }
}
+ return TRUE; # continue searching
}
- my $key_name = $any_key->get_name;
- if (index(lc $key_name, lc $find_param) >= 0) {
- go_to_subkey_and_value($subkey_path);
- $dialog->response('ok');
- return FALSE; # stop searching
- }
- else {
- return TRUE; # continue searching
+ # Check keys for a match
+ if ($search_keys) {
+ my $key_name = $any_key->get_name;
+ if (index(lc $key_name, lc $find_param) >= 0) {
+ go_to_subkey_and_value($subkey_path);
+ $dialog->response(50);
+ return FALSE; # stop searching
+ }
}
+ return TRUE; # continue searching
});
my $response = $dialog->run;
+ $dialog->destroy;
+
if ($response eq 'cancel' || $response eq 'delete-event') {
Glib::Source->remove($id);
}
- $dialog->destroy;
+ elsif ($response eq 'ok') {
+ show_message('info', 'Finished searching.');
+ }
}
sub find {
return if @root_keys == 0;
my $entry = Gtk2::Entry->new;
+ $entry->set_text($find_param);
$entry->set_activates_default(TRUE);
+ my $check1 = Gtk2::CheckButton->new('Search _Keys');
+ $check1->set_active($search_keys);
+ my $check2 = Gtk2::CheckButton->new('Search _Values');
+ $check2->set_active($search_values);
+ $check1->signal_connect(toggled => sub {
+ if (!$check1->get_active && !$check2->get_active) {
+ $check2->set_active(TRUE);
+ }
+ });
+ $check2->signal_connect(toggled => sub {
+ if (!$check1->get_active && !$check2->get_active) {
+ $check1->set_active(TRUE);
+ }
+ });
my $dialog = Gtk2::Dialog->new('Find',
$window,
'modal',
'gtk-cancel' => 'cancel',
'gtk-ok' => 'ok',
);
- $dialog->vbox->pack_start($entry, TRUE, TRUE, 10);
+ $dialog->vbox->pack_start($entry, TRUE, TRUE, 0);
+ $dialog->vbox->pack_start($check1, TRUE, TRUE, 0);
+ $dialog->vbox->pack_start($check2, TRUE, TRUE, 0);
$dialog->set_default_response('ok');
$dialog->show_all;
@@ -904,6 +1115,8 @@ sub find {
$dialog->destroy;
if ($response eq 'ok' && @root_keys > 0) {
+ $search_keys = $check1->get_active;
+ $search_values = $check2->get_active;
$find_param = $entry->get_text;
if ($find_param ne '') {
$find_iter = make_multiple_subtree_iterator(@root_keys);
@@ -918,13 +1131,13 @@ sub find_next_change {
}
my $label = Gtk2::Label->new;
- $label->set_text("Searching registry...");
+ $label->set_text(get_search_message);
my $dialog = Gtk2::Dialog->new('Find Change',
$window,
'modal',
'gtk-cancel' => 'cancel',
);
- $dialog->vbox->pack_start($label, TRUE, TRUE, 10);
+ $dialog->vbox->pack_start($label, TRUE, TRUE, 5);
$dialog->set_default_response('cancel');
$dialog->show_all;
@@ -933,48 +1146,54 @@ sub find_next_change {
if (!defined $keys_ref) {
$dialog->response('ok');
- show_message('info', 'Finished searching.');
return FALSE; # stop searching
}
# Obtain the name and path from the first defined key
my $any_key = (grep { defined } @$keys_ref)[0];
my $subkey_path = (split(/\\/, $any_key->get_path, 2))[1];
+ if (!defined $subkey_path) {
+ return TRUE;
+ }
+ # Check values (if defined) for changes
if (defined $values_ref) {
- my $any_value = (grep { defined } @$values_ref)[0];
- my $value_name = $any_value->get_name;
- my @changes = compare_multiple_values(@$values_ref);
+ if ($search_values) {
+ my $any_value = (grep { defined } @$values_ref)[0];
+ my $value_name = $any_value->get_name;
+ my @changes = compare_multiple_values(@$values_ref);
+ my $num_changes = grep { $_ } @changes;
+ if ($num_changes > 0) {
+ go_to_subkey_and_value($subkey_path, $value_name);
+ $dialog->response(50);
+ return FALSE; # stop searching
+ }
+ }
+ return TRUE; # continue searching
+ }
+
+ if ($search_keys) {
+ my $key_name = $any_key->get_name;
+ my @changes = compare_multiple_keys(@$keys_ref);
my $num_changes = grep { $_ } @changes;
if ($num_changes > 0) {
- go_to_subkey_and_value($subkey_path, $value_name);
- $dialog->response('ok');
+ go_to_subkey_and_value($subkey_path);
+ $dialog->response(50);
return FALSE; # stop searching
}
- else {
- return TRUE; # continue searching
- }
- }
-
- my $key_name = $any_key->get_name;
-
- my @changes = compare_multiple_keys(@$keys_ref);
- my $num_changes = grep { $_ } @changes;
- if ($num_changes > 0) {
- go_to_subkey_and_value($subkey_path);
- $dialog->response('ok');
- return FALSE; # stop searching
- }
- else {
- return TRUE; # continue searching
}
+ return TRUE; # continue searching
});
my $response = $dialog->run;
+ $dialog->destroy;
+
if ($response eq 'cancel' || $response eq 'delete-event') {
Glib::Source->remove($id);
}
- $dialog->destroy;
+ elsif ($response eq 'ok') {
+ show_message('info', 'Finished searching.');
+ }
}
sub find_change {
@@ -1006,14 +1225,30 @@ sub find_change {
my $key_path = $start_key->get_path;
my $label = Gtk2::Label->new;
- $label->set_text("Find changes starting from\n'$key_path'?");
+ $label->set_markup("Find changes starting from\n<b>$key_path</b>?");
+ my $check1 = Gtk2::CheckButton->new('Search _Keys');
+ $check1->set_active($search_keys);
+ my $check2 = Gtk2::CheckButton->new('Search _Values');
+ $check2->set_active($search_values);
+ $check1->signal_connect(toggled => sub {
+ if (!$check1->get_active && !$check2->get_active) {
+ $check2->set_active(TRUE);
+ }
+ });
+ $check2->signal_connect(toggled => sub {
+ if (!$check1->get_active && !$check2->get_active) {
+ $check1->set_active(TRUE);
+ }
+ });
my $dialog = Gtk2::Dialog->new('Find Change',
$window,
'modal',
'gtk-cancel' => 'cancel',
'gtk-ok' => 'ok',
);
- $dialog->vbox->pack_start($label, TRUE, TRUE, 10);
+ $dialog->vbox->pack_start($label, TRUE, TRUE, 5);
+ $dialog->vbox->pack_start($check1, TRUE, TRUE, 0);
+ $dialog->vbox->pack_start($check2, TRUE, TRUE, 0);
$dialog->set_default_response('ok');
$dialog->show_all;
@@ -1021,7 +1256,92 @@ sub find_change {
$dialog->destroy;
if ($response eq 'ok') {
+ $search_keys = $check1->get_active;
+ $search_values = $check2->get_active;
$change_iter = make_multiple_subtree_iterator(@start_keys);
+ $change_iter->get_next;
find_next_change;
}
}
+
+sub get_location {
+ my @start_keys;
+ my @start_values;
+ my ($keys_ref, $values_ref);
+ my ($model, $iter) = $tree_selection->get_selected;
+ if (defined $model && defined $iter) {
+ my $icon = $model->get($iter, TREECOL_ICON);
+ if ($icon eq 'gtk-directory') {
+ # Item is a key
+ $keys_ref = $model->get($iter, TREECOL_ITEMS);
+ }
+ else {
+ # Item is a value
+ $values_ref = $model->get($iter, TREECOL_ITEMS);
+
+ # Find parent key
+ $iter = $model->iter_parent($iter);
+ return if !defined $iter;
+
+ $keys_ref = $model->get($iter, TREECOL_ITEMS);
+ }
+ return ($keys_ref, $values_ref);
+ }
+ else {
+ return ($keys_ref, $values_ref);
+ }
+}
+
+sub add_bookmark {
+ my ($keys_ref, $values_ref) = get_location;
+ if (defined $keys_ref) {
+ my $any_key = (grep { defined } @$keys_ref)[0];
+ my $key_path = $any_key->get_path;
+ my $key_name = $any_key->get_name;
+
+ # Remove root key name to get subkey path
+ my $subkey_path = (split(/\\/, $key_path, 2))[1];
+ return if !defined $subkey_path;
+
+ my $bookmark_name;
+ my $location;
+ my $icon;
+ if (defined $values_ref) {
+ my $any_value = (grep { defined } @$values_ref)[0];
+ my $value_name = $any_value->get_name;
+ $location = [$subkey_path, $value_name];
+ $value_name = '(Default)' if $value_name eq '';
+ $bookmark_name = "$value_name [$key_name]";
+ $icon = 'gtk-file';
+ }
+ else {
+ $bookmark_name = $key_name;
+ $location = [$subkey_path];
+ $icon = 'gtk-directory';
+ }
+ my $display_name = $bookmark_name;
+ $display_name =~ s/_/__/g;
+ $action_group2->add_actions([
+ [$action_name, $icon, $display_name, undef, undef, \&go_to_bookmark],
+ ], $location);
+ $uimanager->add_ui($bookmarks_merge_id, '/MenuBar/BookmarksMenu', $action_name, $action_name, 'menuitem', FALSE);
+ $action_name++;
+ if (my $iter = $bookmark_store->append) {
+ $bookmark_store->set($iter,
+ BMCOL_NAME, $bookmark_name,
+ BMCOL_LOCATION, $location,
+ BMCOL_ICON, $icon,
+ );
+ }
+ }
+}
+
+sub edit_bookmarks {
+ $bookmarks_dialog->show_all;
+}
+
+sub go_to_bookmark {
+ my ($menuitem, $location) = @_;
+ my ($subkey_path, $value_name) = @$location;
+ go_to_subkey_and_value($subkey_path, $value_name);
+}
diff --git a/bin/regmultidiff.pl b/bin/regmultidiff.pl
index b51da65..66d39a3 100755
--- a/bin/regmultidiff.pl
+++ b/bin/regmultidiff.pl
@@ -4,7 +4,7 @@ use warnings;
use File::Basename;
use Getopt::Long;
-use Parse::Win32Registry 0.50 qw( make_multiple_subtree_iterator
+use Parse::Win32Registry 0.51 qw( make_multiple_subtree_iterator
compare_multiple_keys
compare_multiple_values
hexdump );
@@ -68,9 +68,10 @@ for (my $num = 0; $num < $batch_size; $num++) {
}
my $key_shown;
-my $keys_ref = \@start_keys;
-my $values_ref;
-do {
+#my $keys_ref = \@start_keys;
+#my $values_ref;
+
+while (my ($keys_ref, $values_ref) = $subtree_iter->get_next) {
my @keys = @$keys_ref;
my $any_key = (grep { defined } @keys)[0];
die "Unexpected error: no keys!" if !defined $any_key;
@@ -136,7 +137,6 @@ do {
}
}
}
-while (($keys_ref, $values_ref) = $subtree_iter->get_next);
sub usage {
my $script_name = basename $0;
diff --git a/bin/regscope.pl b/bin/regscope.pl
new file mode 100755
index 0000000..29d3a87
--- /dev/null
+++ b/bin/regscope.pl
@@ -0,0 +1,1200 @@
+#!/usr/bin/perl
+use strict;
+use warnings;
+
+use constant MAX_SCALE => 10; # maximum scale for hbin maps
+
+use Glib ':constants';
+use Gtk2 -init;
+
+my $screen = Gtk2::Gdk::Screen->get_default;
+my $window_width = $screen->get_width * 0.9;
+my $window_height = $screen->get_height * 0.8;
+$window_width = 1100 if $window_width > 1100;
+$window_height = 900 if $window_height > 900;
+
+use Encode;
+use File::Basename;
+use Parse::Win32Registry 0.51 qw(hexdump qquote :REG_);
+
+binmode(STDOUT, ':utf8');
+
+my $script_name = basename $0;
+
+### LIST VIEW
+
+use constant {
+ COLUMN_HBIN_OFFSET => 0,
+ COLUMN_HBIN_OBJECT => 1,
+};
+
+my $hbin_store = Gtk2::ListStore->new(
+ 'Glib::String', 'Glib::Scalar',
+);
+
+my $hbin_view = Gtk2::TreeView->new($hbin_store);
+$hbin_view->set_size_request(120, -1);
+
+my $hbin_column1 = Gtk2::TreeViewColumn->new_with_attributes(
+ 'Hbin', Gtk2::CellRendererText->new,
+ 'text', COLUMN_HBIN_OFFSET,
+);
+$hbin_view->append_column($hbin_column1);
+$hbin_column1->set_resizable(TRUE);
+
+my $hbin_selection = $hbin_view->get_selection;
+$hbin_selection->set_mode('browse');
+$hbin_selection->signal_connect('changed' => \&hbin_selection_changed);
+
+my $scrolled_hbin_view = Gtk2::ScrolledWindow->new;
+$scrolled_hbin_view->set_policy('automatic', 'automatic');
+$scrolled_hbin_view->set_shadow_type('in');
+$scrolled_hbin_view->add($hbin_view);
+
+### LIST VIEW FOR ENTRY
+
+use constant {
+ COLUMN_ENTRY_OFFSET => 0,
+ COLUMN_ENTRY_LENGTH => 1,
+ COLUMN_ENTRY_TAG => 2,
+ COLUMN_ENTRY_IN_USE => 3,
+ COLUMN_ENTRY_COLOR => 4,
+ COLUMN_ENTRY_OBJECT => 5,
+ COLUMN_ENTRY_USED_BY => 6,
+};
+
+my $entry_store = Gtk2::ListStore->new(
+ 'Glib::String', 'Glib::String', 'Glib::String',
+ 'Glib::String', 'Glib::String', 'Glib::Scalar',
+ 'Glib::String',
+);
+
+my $entry_view = Gtk2::TreeView->new($entry_store);
+
+my $entry_column0 = Gtk2::TreeViewColumn->new_with_attributes(
+ 'Entry', my $entry_cell0 = Gtk2::CellRendererText->new,
+ 'text', COLUMN_ENTRY_OFFSET,
+ 'background', COLUMN_ENTRY_COLOR,
+);
+$entry_view->append_column($entry_column0);
+$entry_column0->set_resizable(TRUE);
+
+my $entry_column1 = Gtk2::TreeViewColumn->new_with_attributes(
+ 'Tag', my $entry_cell1 = Gtk2::CellRendererText->new,
+ 'text', COLUMN_ENTRY_TAG,
+ 'background', COLUMN_ENTRY_COLOR,
+);
+$entry_view->append_column($entry_column1);
+$entry_column1->set_resizable(TRUE);
+
+my $entry_column2 = Gtk2::TreeViewColumn->new_with_attributes(
+ 'Alloc.', Gtk2::CellRendererText->new,
+ 'text', COLUMN_ENTRY_IN_USE,
+ 'background', COLUMN_ENTRY_COLOR,
+);
+$entry_view->append_column($entry_column2);
+$entry_column2->set_resizable(TRUE);
+
+my $entry_column3 = Gtk2::TreeViewColumn->new_with_attributes(
+ 'Length', Gtk2::CellRendererText->new,
+ 'text', COLUMN_ENTRY_LENGTH,
+ 'background', COLUMN_ENTRY_COLOR,
+);
+$entry_view->append_column($entry_column3);
+$entry_column3->set_resizable(TRUE);
+
+my $entry_column4 = Gtk2::TreeViewColumn->new_with_attributes(
+ 'Owner', my $entry_cell4 = Gtk2::CellRendererText->new,
+ 'text', COLUMN_ENTRY_USED_BY,
+ 'background', COLUMN_ENTRY_COLOR,
+);
+$entry_view->append_column($entry_column4);
+$entry_column4->set_resizable(TRUE);
+
+my $entry_selection = $entry_view->get_selection;
+$entry_selection->set_mode('browse');
+$entry_selection->signal_connect('changed' => \&entry_selection_changed);
+
+my $scrolled_entry_view = Gtk2::ScrolledWindow->new;
+$scrolled_entry_view->set_policy('automatic', 'automatic');
+$scrolled_entry_view->set_shadow_type('in');
+$scrolled_entry_view->add($entry_view);
+
+### TEXT VIEW
+
+my $text_view = Gtk2::TextView->new;
+$text_view->set_editable(FALSE);
+$text_view->modify_font(Gtk2::Pango::FontDescription->from_string('monospace'));
+
+my $text_buffer = $text_view->get_buffer;
+
+my $scrolled_text_view = Gtk2::ScrolledWindow->new;
+$scrolled_text_view->set_policy('automatic', 'automatic');
+$scrolled_text_view->set_shadow_type('in');
+$scrolled_text_view->add($text_view);
+
+### IMAGE
+
+my $hbin_image = Gtk2::Image->new;
+my $eventbox = Gtk2::EventBox->new;
+$eventbox->add($hbin_image);
+$eventbox->add_events(['button-press-mask']);
+
+$eventbox->signal_connect('button-press-event' => \&hbin_map_click);
+
+my $scrolled_hbin_image = Gtk2::ScrolledWindow->new;
+$scrolled_hbin_image->set_policy('automatic', 'automatic');
+$scrolled_hbin_image->set_shadow_type('in');
+$scrolled_hbin_image->add_with_viewport($eventbox);
+
+### NOTEBOOK
+
+my $notebook = Gtk2::Notebook->new;
+my $hbin_map_page = $notebook->append_page($scrolled_hbin_image,
+ Gtk2::Label->new_with_mnemonic("Hbin _Map"));
+my $info_page = $notebook->append_page($scrolled_text_view,
+ Gtk2::Label->new_with_mnemonic("_Info"));
+
+### HPANED
+
+my $hpaned = Gtk2::HPaned->new;
+$hpaned->add1($scrolled_entry_view);
+$hpaned->add2($notebook);
+$hpaned->set_position(320);
+
+### HBOX
+
+my $hbox = Gtk2::HBox->new;
+$hbox->pack_start($scrolled_hbin_view, FALSE, FALSE, 0);
+$hbox->pack_start($hpaned, TRUE, TRUE, 0);
+
+### UIMANAGER
+
+my $uimanager = Gtk2::UIManager->new;
+
+my @actions = (
+ # name, stock id, label
+ ['FileMenu', undef, '_File'],
+ ['SearchMenu', undef, '_Search'],
+ ['ViewMenu', undef, '_View'],
+ ['HelpMenu', undef, '_Help'],
+ # name, stock-id, label, accelerator, tooltip, callback
+ ['Open', 'gtk-open', '_Open', '<control>O', undef, \&open_file],
+ ['Quit', 'gtk-quit', '_Quit', '<control>Q', undef, \&quit],
+ ['About', 'gtk-about', '_About', undef, undef, \&about],
+);
+
+my $default_actions = Gtk2::ActionGroup->new('actions');
+$default_actions->add_actions(\@actions, undef);
+
+my @actions2 = (
+ # name, stock-id, label, accelerator, tooltip, callback
+ ['Close', 'gtk-close', '_Close', '<control>W', undef, \&close_file],
+ ['Find', 'gtk-find', '_Find', '<control>F', undef, \&find],
+ ['FindNext', undef, 'Find _Next', '<control>G', undef, \&find_next],
+ ['FindNext2', undef, undef, 'F3', undef, \&find_next],
+ ['Process1', undef, '_Scan Entries', undef, undef, \&scan_entries],
+ ['Process2', 'gtk-media-play', 'Identify _Entry Owners', '<control>E', undef, \&scan_tree],
+ ['GoTo', 'gtk-index', '_Go To Offset', '<control>I', undef, \&go_to_offset],
+);
+
+my $file_actions = Gtk2::ActionGroup->new('actions2');
+$file_actions->add_actions(\@actions2, undef);
+
+my @actions3 = (
+ # name, stock-id, label, accelerator, tooltip, callback
+ ['ZoomIn', 'gtk-zoom-in', 'Zoom Hbin Map _In', '<control>plus', undef, \&zoom_in],
+ ['ZoomIn2', undef, undef, '<control>equal', undef, \&zoom_in],
+ ['ZoomOut', 'gtk-zoom-out', 'Zoom Hbin Map _Out', '<control>minus', undef, \&zoom_out],
+ ['ZoomFit', 'gtk-zoom-fit', 'Zoom Hbin Map To _Fit', '<control>0', undef, \&zoom_fit],
+ ['SaveHbinMap', 'gtk-save', '_Save Hbin Map', '<control>S', undef, \&save_hbin_map],
+);
+
+my $hbin_actions = Gtk2::ActionGroup->new('actions3');
+$hbin_actions->add_actions(\@actions3, undef);
+
+my @actions4 = (
+ # name, stock-id, label, accelerator, tooltip, callback
+ ['Jump', 'gtk-jump-to', '_Jump To Owner', '<control>J', undef, \&jump_to_owner],
+ ['JumpBack', 'gtk-go-back', 'Jump _Back', 'BackSpace', undef, \&jump_back],
+);
+
+my $owner_actions = Gtk2::ActionGroup->new('actions4');
+$owner_actions->add_actions(\@actions4, undef);
+
+my @toggle_actions = (
+ # name, stock id, label, accelerator, tooltip, callback, active
+ ['ShowToolbar', undef, 'Show _Toolbar', '<control>T', undef, \&toggle_toolbar, TRUE],
+ ['ShowHbins', undef, 'Show _Hbins', undef, undef, \&toggle_hbins, TRUE],
+ ['ShowHbinMap', undef, 'Show Hbin _Map', undef, undef, \&toggle_hbin_map, TRUE],
+);
+$default_actions->add_toggle_actions(\@toggle_actions, undef);
+
+$uimanager->insert_action_group($default_actions, 0);
+$uimanager->insert_action_group($file_actions, 0);
+$uimanager->insert_action_group($hbin_actions, 0);
+$uimanager->insert_action_group($owner_actions, 0);
+
+$file_actions->set_sensitive(FALSE);
+$hbin_actions->set_sensitive(FALSE);
+$owner_actions->set_sensitive(FALSE);
+
+my $ui_info = <<END_OF_UI;
+<ui>
+ <menubar name='MenuBar'>
+ <menu action='FileMenu'>
+ <menuitem action='Open'/>
+ <menuitem action='SaveHbinMap'/>
+ <menuitem action='Close'/>
+ <separator/>
+ <menuitem action='Quit'/>
+ </menu>
+ <menu action='SearchMenu'>
+ <menuitem action='Find'/>
+ <menuitem action='FindNext'/>
+ <separator/>
+ <menuitem action='GoTo'/>
+ <separator/>
+ <menuitem action='Process2'/>
+ <menuitem action='Jump'/>
+ <menuitem action='JumpBack'/>
+ </menu>
+ <menu action='ViewMenu'>
+ <menuitem action='ShowToolbar'/>
+ <menuitem action='ShowHbins'/>
+ <menuitem action='ShowHbinMap'/>
+ <separator/>
+ <menuitem action='ZoomIn'/>
+ <menuitem action='ZoomOut'/>
+ <menuitem action='ZoomFit'/>
+ </menu>
+ <menu action='HelpMenu'>
+ <menuitem action='About'/>
+ </menu>
+ </menubar>
+ <toolbar name='ToolBar'>
+ <toolitem action='Open'/>
+ <toolitem action='Close'/>
+ <separator/>
+ <toolitem action='Find'/>
+ <toolitem action='GoTo'/>
+ <toolitem action='Jump'/>
+ <separator/>
+ <toolitem action='Quit'/>
+ </toolbar>
+ <accelerator action='FindNext2'/>
+ <accelerator action='ZoomIn2'/>
+</ui>
+END_OF_UI
+
+$uimanager->add_ui_from_string($ui_info);
+
+my $menubar = $uimanager->get_widget('/MenuBar');
+my $toolbar = $uimanager->get_widget('/ToolBar');
+
+### STATUSBAR
+
+my $statusbar = Gtk2::Statusbar->new;
+
+### VBOX
+
+my $main_vbox = Gtk2::VBox->new(FALSE, 0);
+$main_vbox->pack_start($menubar, FALSE, FALSE, 0);
+$main_vbox->pack_start($toolbar, FALSE, FALSE, 0);
+$main_vbox->pack_start($hbox, TRUE, TRUE, 0);
+$main_vbox->pack_start($statusbar, FALSE, FALSE, 0);
+
+### WINDOW
+
+my $window = Gtk2::Window->new;
+$window->set_default_size($window_width, $window_height);
+$window->set_position('center');
+$window->signal_connect(destroy => sub { Gtk2->main_quit });
+$window->add($main_vbox);
+$window->add_accel_group($uimanager->get_accel_group);
+$window->set_title($script_name);
+$window->show_all;
+
+my $filename = shift;
+if (defined $filename && -r $filename) {
+ load_file($filename);
+}
+
+### GLOBALS
+
+my $registry;
+
+my $last_dir;
+
+my $find_param = '';
+my $find_iter;
+my $find_hbin;
+my $find_hbin_iter;
+my $find_entry_iter;
+
+my $entry_source; # will be a registry for Win95, a hbin for WinNT
+
+my $map_width;
+my $map_height;
+my $map_pixbuf;
+my $map_scale = 6;
+
+my %owners = ();
+
+my @jump_history = ();
+
+Gtk2->main;
+
+###############################################################################
+
+sub load_entries {
+ return if !defined $entry_source;
+
+ $entry_store->clear;
+
+ # $entry_source is either a WinNT::Hbin or a Win95::File.
+ my $entry_iter = $entry_source->get_entry_iterator;
+ while (my $entry = $entry_iter->get_next) {
+ my $iter = $entry_store->append;
+
+ my $tag = $entry->get_tag;
+ my $offset = $entry->get_offset;
+
+ # colorize each row according to its tag (NT only)
+ # '#FF8080' red, sat 50%
+ # '#80FFFF' cyan, sat 50%
+ # '#80FF80' green, sat 50%
+ # '#FF80FF' magenta, sat 50%
+ my $color = '#E6E6E6';
+ if ($tag eq 'nk') {
+ $color = '#FF8080';
+ }
+ elsif ($tag eq 'sk') {
+ $color = '#80FFFF';
+ }
+ elsif ($tag eq 'vk') {
+ $color = '#80FF80';
+ }
+ elsif ($tag =~ /(lf|lh|li|ri)/) {
+ $color = '#FF80FF';
+ }
+
+ $entry_store->set($iter,
+ COLUMN_ENTRY_OFFSET, sprintf("0x%x", $offset),
+ COLUMN_ENTRY_LENGTH, $entry->get_length,
+ COLUMN_ENTRY_TAG, $tag,
+ COLUMN_ENTRY_IN_USE, $entry->is_allocated,
+ COLUMN_ENTRY_COLOR, $color,
+ COLUMN_ENTRY_OBJECT, $entry);
+
+ # If owners have been identified, add this to the Owner column
+ if (exists $owners{$offset}) {
+ my $desc = "";
+ my $num_referrers = @{$owners{$offset}};
+ if ($num_referrers == 1) {
+ my $rtype = $owners{$offset}[0]{type};
+ my $roffset = $owners{$offset}[0]{offset};
+ $desc = sprintf "$rtype @ 0x%x", $roffset;
+ if ($roffset == $offset) {
+ $desc = "Self";
+ }
+ }
+ else {
+ $desc = "$num_referrers referrers";
+ }
+ $entry_store->set($iter,
+ COLUMN_ENTRY_USED_BY, $desc);
+ }
+ }
+}
+
+sub hbin_selection_changed {
+ my ($model, $iter) = $hbin_selection->get_selected;
+ if (!defined $model || !defined $iter) {
+ return;
+ }
+
+ my $hbin = $model->get($iter, COLUMN_HBIN_OBJECT);
+ $entry_source = $hbin; # set global entry source
+
+ my $str = $hbin->parse_info . "\n";
+ $str .= $hbin->unparsed;
+
+ $text_buffer->set_text($str);
+
+ $statusbar->pop(0);
+ $statusbar->push(0, sprintf("Hbin @ 0x%x", $hbin->get_offset));
+
+ load_entries();
+
+ make_hbin_map();
+ zoom_fit();
+
+ $notebook->set_current_page($hbin_map_page);
+}
+
+sub entry_selection_changed {
+ my ($model, $iter) = $entry_selection->get_selected;
+ if (!defined $model || !defined $iter) {
+ return;
+ }
+
+ my $entry = $model->get($iter, COLUMN_ENTRY_OBJECT);
+ my $offset = $entry->get_offset;
+
+ my $desc;
+ if ($entry->looks_like_key) {
+ $desc = sprintf "Key @ 0x%x", $offset;
+ }
+ elsif ($entry->looks_like_value) {
+ $desc = sprintf "Value @ 0x%x", $offset;
+ }
+ elsif ($entry->looks_like_security) {
+ $desc = sprintf "Security @ 0x%x", $offset;
+ }
+ else {
+ $desc = sprintf "Entry @ 0x%x", $offset;
+ }
+
+ my $str = "$desc\n\n"
+ . $entry->parse_info . "\n"
+ . $entry->unparsed . "\n";
+
+ my $status = $desc;
+
+ if ($entry->looks_like_key) {
+ $str .= $entry->as_string . "\n\n";
+ $status .= ' "' . $entry->get_name . '"';
+ }
+ elsif ($entry->looks_like_value) {
+ my $name = $entry->get_name;
+ $name = '(Default)' if $name eq '';
+ my $type_as_string = $entry->get_type_as_string;
+
+ $str .= "$name ($type_as_string)\n\n";
+ $status .= ' "' . $entry->get_name . '"';
+ }
+
+ $text_buffer->set_text($str);
+ $notebook->set_current_page($info_page);
+
+ $statusbar->pop(0);
+ $statusbar->push(0, $status);
+}
+
+sub show_message {
+ my $type = shift;
+ my $message = shift;
+
+ my $dialog = Gtk2::MessageDialog->new(
+ $window,
+ 'destroy-with-parent',
+ $type,
+ 'ok',
+ $message,
+ );
+ $dialog->set_title(ucfirst $type);
+ $dialog->run;
+ $dialog->destroy;
+}
+
+sub load_file {
+ my $filename = shift;
+
+ my ($name, $path) = fileparse($filename);
+
+ close_file();
+
+ if (!-r $filename) {
+ show_message('error', "Unable to open '$name'.");
+ }
+ elsif ($registry = Parse::Win32Registry->new($filename)) {
+ if (my $root_key = $registry->get_root_key) {
+ $window->set_title("$name - $script_name");
+
+ my $hbin_iter = $registry->get_hbin_iterator;
+ if (defined $hbin_iter) { # WinNT
+ # load hbins
+ while (my $hbin = $hbin_iter->get_next) {
+ my $iter = $hbin_store->append;
+ $hbin_store->set($iter,
+ COLUMN_HBIN_OFFSET, sprintf("0x%x", $hbin->{_offset}),
+ COLUMN_HBIN_OBJECT, $hbin);
+ }
+ show_hbin_functions(TRUE);
+ }
+ else { # Win95
+ $entry_source = $registry;
+ load_entries();
+ show_hbin_functions(FALSE);
+ }
+ $file_actions->set_sensitive(TRUE);
+ }
+ }
+ else {
+ show_message('error', "'$name' is not a registry file.");
+ }
+}
+
+sub choose_file {
+ my ($title, $type, $suggested_name) = @_;
+
+ my $file_chooser = Gtk2::FileChooserDialog->new(
+ $title,
+ undef,
+ $type,
+ 'gtk-cancel' => 'cancel',
+ 'gtk-ok' => 'ok',
+ );
+ if ($type eq 'save') {
+ $file_chooser->set_current_name($suggested_name);
+ }
+ if (defined $last_dir) {
+ $file_chooser->set_current_folder($last_dir);
+ }
+ my $response = $file_chooser->run;
+
+ my $filename;
+ if ($response eq 'ok') {
+ $filename = $file_chooser->get_filename;
+ }
+ $last_dir = $file_chooser->get_current_folder;
+ $file_chooser->destroy;
+ return $filename;
+}
+
+sub open_file {
+ my $filename = choose_file('Select Registry File', 'open');
+ if (defined $filename) {
+ load_file($filename);
+ }
+}
+
+sub save_hbin_map {
+ return if !defined $map_pixbuf;
+
+ my ($model, $iter) = $hbin_selection->get_selected;
+ if (!defined $model || !defined $iter) {
+ return;
+ }
+
+ my $hbin = $model->get($iter, COLUMN_HBIN_OBJECT);
+
+ my $name = sprintf "hbin\@0x%x.jpg", $hbin->get_offset;
+
+ my $filename = choose_file('Save Hbin Map', 'save', $name);
+ if (defined $filename) {
+ my $pixbuf = $map_pixbuf->scale_simple($map_width * MAX_SCALE,
+ $map_height * MAX_SCALE,
+ 'tiles');
+ $pixbuf->save($filename, 'jpeg', quality => 100);
+ }
+}
+
+sub close_file {
+ $hbin_store->clear;
+ $entry_store->clear;
+ $registry = undef;
+ $entry_source = undef;
+ $hbin_image->clear;
+ $text_buffer->set_text('');
+ $statusbar->pop(0);
+ $map_pixbuf = undef;
+ %owners = ();
+ @jump_history = ();
+ $file_actions->set_sensitive(FALSE);
+ $hbin_actions->set_sensitive(FALSE);
+ $owner_actions->set_sensitive(FALSE);
+}
+
+sub quit {
+ $window->destroy;
+}
+
+sub about {
+ Gtk2->show_about_dialog(undef,
+ 'program-name' => $script_name,
+ 'version' => $Parse::Win32Registry::VERSION,
+ 'copyright' => 'Copyright (c) 2009 James Macfarlane',
+ 'comments' => 'GTK2 Registry Scope for the Parse::Win32Registry module',
+ );
+}
+
+sub toggle_hbins {
+ my ($toggle_action) = @_;
+ if ($toggle_action->get_active) {
+ $scrolled_hbin_view->show;
+ }
+ else {
+ $scrolled_hbin_view->hide;
+ }
+}
+
+sub toggle_hbin_map {
+ my ($toggle_action) = @_;
+ if ($toggle_action->get_active) {
+ $scrolled_hbin_image->show;
+ }
+ else {
+ $scrolled_hbin_image->hide;
+ }
+}
+
+sub toggle_toolbar {
+ my ($toggle_action) = @_;
+ if ($toggle_action->get_active) {
+ $toolbar->show;
+ }
+ else {
+ $toolbar->hide;
+ }
+}
+
+sub make_hbin_map {
+ my ($model, $iter) = $hbin_selection->get_selected;
+ if (!defined $model || !defined $iter) {
+ return;
+ }
+
+ my $hbin = $model->get($iter, COLUMN_HBIN_OBJECT);
+
+ my $hbin_length = $hbin->get_length;
+
+ # Find the nearest power of 2 larger than hbin length
+ my $n = $hbin_length;
+ if ($n > 0) {
+ $n--;
+ foreach (1..31) {
+ $n |= $n >> $_;
+ }
+ $n++;
+ }
+
+ # Find the squarest map dimensions
+ my $h = 1;
+ my $w = $n;
+ while ($h < $w) {
+ $h *= 2;
+ $w /= 2;
+ }
+
+ $map_width = $w;
+ $map_height = int($hbin_length / $w);
+
+ # Initialise the byte sequence with the hbin header
+ my $data = pack "C*",
+ map { ($_, $_, $_, 255) } unpack("C*", $hbin->get_raw_bytes);
+
+ # Build the hbin map using colorised byte sequences
+ my $entry_iter = $hbin->get_entry_iterator;
+ while (my $entry = $entry_iter->get_next) {
+ my $tag = $entry->get_tag;
+ if ($tag eq 'nk') {
+ $data .= pack "C*",
+ map { ($_, 0, 0, 255) } unpack("C*", $entry->get_raw_bytes);
+ }
+ elsif ($tag eq 'vk') {
+ $data .= pack "C*",
+ map { (0, $_, 0, 255) } unpack("C*", $entry->get_raw_bytes);
+ }
+ elsif ($tag eq 'sk') {
+ $data .= pack "C*",
+ map { (0, $_, $_, 255) } unpack("C*", $entry->get_raw_bytes);
+ }
+ elsif ($tag =~ /(lf|lh|ri|li)/) {
+ $data .= pack "C*",
+ map { ($_, 0, $_, 255) } unpack("C*", $entry->get_raw_bytes);
+ }
+ else {
+ $data .= pack "C*",
+ map { ($_, $_, $_, 255) } unpack("C*", $entry->get_raw_bytes);
+ }
+ }
+
+ my $padding = ($map_width * $map_height) - int(length($data) / 4);
+ $data .= pack "C*", (255, 255, 255, 128) x $padding;
+
+ $map_pixbuf = Gtk2::Gdk::Pixbuf->new_from_data(
+ $data, 'rgb', 1, 8, $map_width, $map_height, $map_width * 4);
+}
+
+sub hbin_map_click {
+ my ($widget, $event) = @_;
+
+ my ($x, $y) = ($event->x, $event->y);
+
+ my @alloc = $hbin_image->allocation->values;
+ my $alloc_width = $alloc[2];
+ my $alloc_height = $alloc[3];
+ my $map_x = $x - ($alloc_width - $map_width*$map_scale) / 2;
+ my $map_y = $y - ($alloc_height - $map_height*$map_scale) / 2;
+ $map_x /= $map_scale;
+ $map_y /= $map_scale;
+ $map_x = int($map_x);
+ $map_y = int($map_y);
+
+ if (($map_x >= 0 && $map_x < $map_width) &&
+ ($map_y >= 0 && $map_y < $map_height)) {
+ my $offset = ($map_y * $map_width) + $map_x;
+
+ my ($model, $iter) = $hbin_selection->get_selected;
+ if (!defined $model || !defined $iter) {
+ return;
+ }
+ my $hbin = $model->get($iter, COLUMN_HBIN_OBJECT);
+ $offset += $hbin->get_offset;
+
+ if ($offset < ($hbin->get_offset + 0x20)) {
+ # First 32 bytes comprise the hbin header
+ go_to_hbin($offset);
+ }
+ else {
+ go_to_entry($offset);
+ $notebook->set_current_page($hbin_map_page);
+ }
+ }
+}
+
+sub show_owner_functions {
+
+}
+
+sub show_hbin_functions {
+ my $state = shift;
+
+ my $show_hbins_toggle
+ = $uimanager->get_widget('/MenuBar/ViewMenu/ShowHbins');
+
+ my $show_hbin_map_toggle
+ = $uimanager->get_widget('/MenuBar/ViewMenu/ShowHbinMap');
+
+ if ($state) {
+ $hbin_actions->set_sensitive(TRUE);
+ $show_hbins_toggle->set_active(TRUE);
+ $show_hbin_map_toggle->set_active(TRUE);
+ }
+ else {
+ $hbin_actions->set_sensitive(FALSE);
+ $show_hbins_toggle->set_active(FALSE);
+ $show_hbin_map_toggle->set_active(FALSE);
+ }
+}
+
+sub draw_scaled_map {
+ return if !defined $map_pixbuf;
+
+ my $scale = shift || $map_scale; # optional override
+
+ my $pixbuf = $map_pixbuf->scale_simple($map_width * $scale,
+ $map_height * $scale,
+ 'tiles');
+ $hbin_image->set_from_pixbuf($pixbuf);
+}
+
+sub zoom_in {
+ return if !defined $map_pixbuf;
+
+ $map_scale++;
+ $map_scale = MAX_SCALE if $map_scale > MAX_SCALE;
+ draw_scaled_map();
+ $notebook->set_current_page($hbin_map_page);
+}
+
+sub zoom_out {
+ return if !defined $map_pixbuf;
+
+ $map_scale--;
+ $map_scale = 1 if $map_scale < 1;
+ draw_scaled_map();
+ $notebook->set_current_page($hbin_map_page);
+}
+
+sub zoom_fit {
+ return if !defined $map_pixbuf;
+
+ my $allocation = $scrolled_hbin_image->allocation;
+ my ($x, $y, $available_width, $available_height) = $allocation->values;
+
+ for (my $scale = MAX_SCALE; $scale > 0; $scale--) {
+ my $width = $map_width * $scale;
+ my $height = $map_height * $scale;
+
+ if ($width < $available_width && $height < $available_height) {
+ $map_scale = $scale;
+ last;
+ }
+ }
+ draw_scaled_map();
+ $notebook->set_current_page($hbin_map_page);
+}
+
+sub go_to_hbin {
+ my ($offset) = @_;
+
+ my $iter = $hbin_store->get_iter_first;
+ while (defined $iter) {
+ my $hbin = $hbin_store->get($iter, COLUMN_HBIN_OBJECT);
+ my $hbin_start = $hbin->get_offset;
+ my $hbin_end = $hbin_start + $hbin->get_length;
+ if ($offset >= $hbin_start && $offset < $hbin_end) {
+ my $tree_path = $hbin_store->get_path($iter);
+ $hbin_view->expand_to_path($tree_path);
+ $hbin_view->scroll_to_cell($tree_path);
+ $hbin_view->set_cursor($tree_path);
+ $window->set_focus($hbin_view);
+ return;
+ }
+ $iter = $hbin_store->iter_next($iter);
+ }
+}
+
+sub go_to_entry {
+ my ($offset) = @_;
+
+ my $iter = $entry_store->get_iter_first;
+ while (defined $iter) {
+ my $entry = $entry_store->get($iter, COLUMN_ENTRY_OBJECT);
+ my $entry_start = $entry->get_offset;
+ my $entry_end = $entry_start + $entry->get_length;
+ if ($offset >= $entry_start && $offset < $entry_end) {
+ my $tree_path = $entry_store->get_path($iter);
+ $entry_view->expand_to_path($tree_path);
+ $entry_view->scroll_to_cell($tree_path);
+ $entry_view->set_cursor($tree_path);
+ $window->set_focus($entry_view);
+ return;
+ }
+ $iter = $entry_store->iter_next($iter);
+ }
+}
+
+sub find_next {
+ if (!defined $find_param || !defined $find_entry_iter) {
+ return;
+ }
+
+ # Build find next dialog
+ my $label = Gtk2::Label->new;
+ $label->set_text("Searching registry entries...");
+ my $dialog = Gtk2::Dialog->new('Find',
+ $window,
+ 'modal',
+ 'gtk-cancel' => 'cancel',
+ );
+ $dialog->vbox->pack_start($label, TRUE, TRUE, 5);
+ $dialog->set_default_response('cancel');
+ $dialog->show_all;
+
+ my $id = Glib::Idle->add(sub {
+ while (1) {
+ my $entry = $find_entry_iter->get_next;
+ if (defined $entry) {
+ my $found = 0;
+
+ if (index($entry->get_raw_bytes, $find_param) > -1) {
+ $found = 1;
+ }
+ else {
+ my $uni_find_param = encode("UCS-2LE", $find_param);
+ if (index($entry->get_raw_bytes, $uni_find_param) > -1) {
+ $found = 1;
+ }
+ }
+
+ if ($found) {
+ if (defined $find_hbin) {
+ go_to_hbin($find_hbin->get_offset);
+ }
+ go_to_entry($entry->get_offset);
+
+ $dialog->response(50);
+ return FALSE;
+ }
+
+ return TRUE; # continue searching...
+ }
+ else { # no more entries...?
+ if (defined $find_hbin_iter) {
+ $find_hbin = $find_hbin_iter->get_next;
+ if (defined $find_hbin) {
+ $find_entry_iter = $find_hbin->get_entry_iterator;
+ if (!defined $find_entry_iter) {
+ last; # no entry iterator... (WinNT)
+ }
+ }
+ else {
+ last; # no more hbins... (WinNT)
+ }
+ }
+ else {
+ last; # no more entries... (Win95)
+ }
+ }
+ }
+
+ $dialog->response('ok');
+ return FALSE;
+
+ });
+
+ my $response = $dialog->run;
+ $dialog->destroy;
+
+ if ($response eq 'cancel' || $response eq 'delete-event') {
+ Glib::Source->remove($id);
+ }
+ elsif ($response eq 'ok') {
+ show_message('info', 'Finished searching.');
+ }
+}
+
+sub find {
+ return if !defined $registry;
+
+ my $entry = Gtk2::Entry->new;
+ $entry->set_text($find_param);
+ $entry->set_activates_default(TRUE);
+ my $dialog = Gtk2::Dialog->new('Find',
+ $window,
+ 'modal',
+ 'gtk-cancel' => 'cancel',
+ 'gtk-ok' => 'ok',
+ );
+ $dialog->vbox->pack_start($entry, TRUE, TRUE, 5);
+ $dialog->set_default_response('ok');
+ $dialog->show_all;
+
+ my $response = $dialog->run;
+ $dialog->destroy;
+
+ if ($response eq 'ok') {
+ $find_param = $entry->get_text;
+ if ($find_param ne '') {
+ # WinNT: initialise hbin_iter, hbin, entry_iter
+ # Win95: initialise entry_iter
+ $find_hbin_iter = $registry->get_hbin_iterator;
+ if (defined $find_hbin_iter) { # WinNT
+ $find_hbin = $find_hbin_iter->get_next;
+ $find_entry_iter = $find_hbin->get_entry_iterator;
+ }
+ else { # Win95
+ $find_entry_iter = $registry->get_entry_iterator;
+ }
+ find_next;
+ }
+ }
+}
+
+sub go_to_offset {
+ return if !defined $registry;
+
+ my $entry = Gtk2::Entry->new;
+ $entry->set_activates_default(TRUE);
+ my $dialog = Gtk2::Dialog->new('Go To Offset',
+ $window,
+ 'modal',
+ 'gtk-cancel' => 'cancel',
+ 'gtk-ok' => 'ok',
+ );
+ $dialog->vbox->pack_start($entry, TRUE, TRUE, 5);
+ $dialog->set_default_response('ok');
+ $dialog->show_all;
+
+ $entry->prepend_text("0x");
+ $entry->set_position(-1);
+
+ my $response = $dialog->run;
+ $dialog->destroy;
+
+ if ($response ne 'ok') {
+ return;
+ }
+
+ my $offset;
+ eval {
+ my $answer = $entry->get_text;
+ if ($answer =~ m/^0x[\da-fA-F]+\s*$/) {
+ $offset = int(eval $answer);
+ }
+ };
+
+ if (defined $offset && $offset < $registry->get_length) {
+ go_to_hbin($offset);
+ go_to_entry($offset);
+ }
+}
+
+sub jump_to_owner {
+ my ($model, $iter) = $entry_selection->get_selected;
+ if (!defined $model || !defined $iter) {
+ return;
+ }
+
+ if (!%owners) {
+ show_message('error', "'Identify Entry Owners' has not been run.");
+ return;
+ }
+
+ my $entry = $model->get($iter, COLUMN_ENTRY_OBJECT);
+ my $offset = $entry->get_offset;
+
+ if (exists $owners{$offset}) {
+ my $num_referrers = @{$owners{$offset}};
+ if ($num_referrers >= 1) {
+ my $roffset = $owners{$offset}[0]{offset};
+ if ($roffset != $offset) {
+ push @jump_history, $offset;
+ go_to_hbin($roffset);
+ go_to_entry($roffset);
+ }
+ }
+ }
+}
+
+sub jump_back {
+ if (@jump_history) {
+ my $offset = pop @jump_history;
+ go_to_hbin($offset);
+ go_to_entry($offset);
+ }
+}
+
+###############################################################################
+
+sub scan_entries {
+ return if !defined $registry;
+
+ my $label = Gtk2::Label->new;
+ $label->set_text("Searching registry...");
+ my $dialog = Gtk2::Dialog->new('Find',
+ $window,
+ 'modal',
+ 'gtk-cancel' => 'cancel',
+ );
+ $dialog->vbox->pack_start($label, TRUE, TRUE, 5);
+ $dialog->set_default_response('cancel');
+ $dialog->show_all;
+
+ my $entry_iter;
+ my $hbin_iter = $registry->get_hbin_iterator;
+ if (defined $hbin_iter) { # WinNT
+ my $hbin = $hbin_iter->get_next;
+ $entry_iter = $hbin->get_entry_iterator;
+ }
+ else { # Win95
+ $entry_iter = $registry->get_entry_iterator;
+ }
+
+ my $id = Glib::Idle->add(sub {
+ while (1) {
+ my $entry = $entry_iter->get_next;
+ if (defined $entry) {
+
+ # do something with entry...
+ printf "processing entry 0x%x...\n", $entry->get_offset;
+
+ return TRUE; # continue searching...
+ }
+ else { # no more entries...?
+ if (defined $hbin_iter) {
+ my $hbin = $hbin_iter->get_next;
+ if (defined $hbin) {
+ $entry_iter = $hbin->get_entry_iterator;
+ if (!defined $entry_iter) {
+ last; # no more entries... (WinNT)
+ }
+ }
+ else {
+ last; # no more hbins... (WinNT)
+ }
+ }
+ else {
+ last; # no more entries... (Win95)
+ }
+ }
+ }
+
+ $dialog->response('ok');
+ show_message('info', 'Finished long running process.');
+ return FALSE;
+ });
+
+ my $response = $dialog->run;
+ $dialog->destroy;
+
+ if ($response eq 'cancel' || $response eq 'delete-event') {
+ Glib::Source->remove($id);
+ }
+}
+
+sub scan_tree {
+ return if !defined $registry;
+
+ %owners = ();
+
+ my $label = Gtk2::Label->new;
+ $label->set_text("Scanning registry to identify entry owners...");
+ my $dialog = Gtk2::Dialog->new('Scanning',
+ $window,
+ 'modal',
+ 'gtk-cancel' => 'cancel',
+ );
+ $dialog->vbox->pack_start($label, TRUE, TRUE, 5);
+ $dialog->set_default_response('cancel');
+ $dialog->show_all;
+
+ my $root_key = $registry->get_root_key;
+ my $subtree_iter = $root_key->get_subtree_iterator;
+ my $value_iter;
+
+ my $id = Glib::Idle->add(sub {
+ if (defined $value_iter) {
+ my $value = $value_iter->get_next;
+ if (defined $value) {
+ my $name = $value->get_name;
+ $name = '(Default)' if $name eq '';
+
+ my $self_offset = $value->get_offset;
+ foreach my $offset ($value->get_associated_offsets) {
+ push @{$owners{$offset}},
+ { type => "Value", offset => $self_offset };
+ }
+
+ return TRUE; # continue processing
+ }
+ }
+ if (defined $subtree_iter) {
+ my $key = $subtree_iter->get_next;
+ if (defined $key) {
+ my $self_offset = $key->get_offset;
+ foreach my $offset ($key->get_associated_offsets) {
+ push @{$owners{$offset}},
+ { type => "Key", offset => $self_offset };
+ }
+
+ # Fetch new value iterator for new key
+ $value_iter = $key->get_value_iterator;
+ return TRUE; # continue processing
+ }
+ }
+
+ $dialog->response('ok');
+ return FALSE; # stop processing
+ });
+
+ my $response = $dialog->run;
+ $dialog->destroy;
+
+ if ($response eq 'cancel' || $response eq 'delete-event') {
+ Glib::Source->remove($id);
+ %owners = ();
+ }
+ elsif ($response eq 'ok') {
+ $entry_column4->set_visible(TRUE);
+ show_message('info', "Finished identifying entry owners.\n"
+ . "Check the Owner column for details.");
+ $owner_actions->set_sensitive(TRUE);
+ load_entries();
+ }
+}
+
diff --git a/bin/regview.pl b/bin/regview.pl
index e2cbc54..a5374ba 100755
--- a/bin/regview.pl
+++ b/bin/regview.pl
@@ -5,9 +5,15 @@ use warnings;
use Glib ':constants';
use Gtk2 -init;
+my $screen = Gtk2::Gdk::Screen->get_default;
+my $window_width = $screen->get_width * 0.9;
+my $window_height = $screen->get_height * 0.8;
+$window_width = 1100 if $window_width > 1100;
+$window_height = 900 if $window_height > 900;
+
use File::Basename;
use File::Spec;
-use Parse::Win32Registry 0.50 qw(hexdump);
+use Parse::Win32Registry 0.51 qw(hexdump);
binmode(STDOUT, ':utf8');
@@ -37,6 +43,15 @@ for (my $col = 0; $col < @list_column_names; $col++) {
'text', $col);
$list_view->append_column($column);
$column->set_resizable(TRUE);
+ $list_store->set_sort_func($col, sub {
+ my ($model, $itera, $iterb, $col) = @_;
+ my $a = $model->get($itera, $col);
+ my $b = $model->get($iterb, $col);
+ $a = '' if !defined $a;
+ $b = '' if !defined $b;
+ return $a cmp $b;
+ }, $col);
+ $column->set_sort_column_id($col);
}
$list_view->set_rules_hint(TRUE);
@@ -96,6 +111,15 @@ for (my $col = 0; $col < @tree_column_names; $col++) {
$column->set_resizable(TRUE);
$tree_view->append_column($column);
push @tree_columns, $column;
+ $tree_store->set_sort_func($col, sub {
+ my ($model, $itera, $iterb, $col) = @_;
+ my $a = $model->get($itera, $col);
+ my $b = $model->get($iterb, $col);
+ $a = '' if !defined $a;
+ $b = '' if !defined $b;
+ return $a cmp $b;
+ }, $col);
+ $column->set_sort_column_id($col);
}
$tree_view->set_rules_hint(TRUE);
@@ -120,7 +144,7 @@ my $hpaned = Gtk2::HPaned->new;
$hpaned->pack1($scrolled_tree_view, FALSE, FALSE);
$hpaned->pack2($vpaned, TRUE, FALSE);
-$hpaned->set_position(200);
+$hpaned->set_position($window_width * 0.3);
### MENU
@@ -133,15 +157,15 @@ my $accel_group = Gtk2::AccelGroup->new;
# File Menu
my $open_menuitem = Gtk2::MenuItem->new('_Open');
$open_menuitem->signal_connect('activate' => \&open_file);
-$open_menuitem->add_accelerator('activate' => $accel_group,
+$open_menuitem->add_accelerator('activate', $accel_group,
$Gtk2::Gdk::Keysyms{O}, ['control-mask'], ['visible', 'locked']);
my $close_menuitem = Gtk2::MenuItem->new('_Close');
$close_menuitem->signal_connect('activate' => \&close_file);
-$close_menuitem->add_accelerator('activate' => $accel_group,
+$close_menuitem->add_accelerator('activate', $accel_group,
$Gtk2::Gdk::Keysyms{W}, ['control-mask'], ['visible', 'locked']);
my $quit_menuitem = Gtk2::MenuItem->new('_Quit');
$quit_menuitem->signal_connect('activate' => \&quit);
-$quit_menuitem->add_accelerator('activate' => $accel_group,
+$quit_menuitem->add_accelerator('activate', $accel_group,
$Gtk2::Gdk::Keysyms{Q}, ['control-mask'], ['visible', 'locked']);
my $file_menu = Gtk2::Menu->new;
@@ -156,7 +180,7 @@ my $recent_separator; # placeholder, becomes separator for recent files
# Edit Menu
my $copy_menuitem = Gtk2::MenuItem->new('_Copy key path');
$copy_menuitem->signal_connect('activate' => \©_key_path);
-$copy_menuitem->add_accelerator('activate' => $accel_group,
+$copy_menuitem->add_accelerator('activate', $accel_group,
$Gtk2::Gdk::Keysyms{C}, ['control-mask'], ['visible', 'locked']);
my $edit_menu = Gtk2::Menu->new;
@@ -165,38 +189,27 @@ $edit_menu->append($copy_menuitem);
# Search Menu
my $find_menuitem = Gtk2::MenuItem->new('_Find');
$find_menuitem->signal_connect('activate' => \&find);
-$find_menuitem->add_accelerator('activate' => $accel_group,
+$find_menuitem->add_accelerator('activate', $accel_group,
$Gtk2::Gdk::Keysyms{F}, ['control-mask'], ['visible', 'locked']);
my $find_next_menuitem = Gtk2::MenuItem->new('Find Next');
$find_next_menuitem->signal_connect('activate' => \&find_next);
-$find_next_menuitem->add_accelerator('activate' => $accel_group,
+$find_next_menuitem->add_accelerator('activate', $accel_group,
$Gtk2::Gdk::Keysyms{G}, ['control-mask'], ['visible', 'locked']);
+$find_next_menuitem->add_accelerator('activate', $accel_group,
+ $Gtk2::Gdk::Keysyms{F3}, [], ['visible', 'locked']);
my $search_menu = Gtk2::Menu->new;
$search_menu->append($find_menuitem);
$search_menu->append($find_next_menuitem);
-# Test Menu
-my $dump_loaded_keys_menuitem = Gtk2::MenuItem->new('Dump loaded keys');
-$dump_loaded_keys_menuitem->signal_connect('activate' => \&dump_loaded_keys);
-my $dump_settings_menuitem = Gtk2::MenuItem->new('Dump settings');
-$dump_settings_menuitem->signal_connect('activate' => \&dump_settings);
-my $dump_bookmarks_menuitem = Gtk2::MenuItem->new('Dump bookmarks');
-$dump_bookmarks_menuitem->signal_connect('activate' => \&dump_bookmarks);
-
-my $test_menu = Gtk2::Menu->new;
-$test_menu->append($dump_loaded_keys_menuitem);
-$test_menu->append($dump_bookmarks_menuitem);
-$test_menu->append($dump_settings_menuitem);
-
# Bookmarks Menu
my $add_bookmark_menuitem = Gtk2::MenuItem->new('_Add Bookmark');
$add_bookmark_menuitem->signal_connect('activate' => \&add_bookmark);
-$add_bookmark_menuitem->add_accelerator('activate' => $accel_group,
+$add_bookmark_menuitem->add_accelerator('activate', $accel_group,
$Gtk2::Gdk::Keysyms{D}, ['control-mask'], ['visible', 'locked']);
my $edit_bookmarks_menuitem = Gtk2::MenuItem->new('_Edit Bookmarks');
$edit_bookmarks_menuitem->signal_connect('activate' => \&edit_bookmarks);
-$edit_bookmarks_menuitem->add_accelerator('activate' => $accel_group,
+$edit_bookmarks_menuitem->add_accelerator('activate', $accel_group,
$Gtk2::Gdk::Keysyms{B}, ['control-mask'], ['visible', 'locked']);
my $bookmarks_menu = Gtk2::Menu->new;
@@ -204,6 +217,21 @@ $bookmarks_menu->append($add_bookmark_menuitem);
$bookmarks_menu->append($edit_bookmarks_menuitem);
$bookmarks_menu->append(Gtk2::SeparatorMenuItem->new);
+# Reports Menu
+my $view_report_menuitem = Gtk2::MenuItem->new('View Bookmark Report');
+$view_report_menuitem->signal_connect('activate' => \&view_report);
+$view_report_menuitem->add_accelerator('activate', $accel_group,
+ $Gtk2::Gdk::Keysyms{R}, ['control-mask'], ['visible', 'locked']);
+#my $dump_loaded_keys_menuitem = Gtk2::MenuItem->new('Dump loaded keys');
+#$dump_loaded_keys_menuitem->signal_connect('activate' => \&dump_loaded_keys);
+#my $dump_settings_menuitem = Gtk2::MenuItem->new('Dump settings');
+#$dump_settings_menuitem->signal_connect('activate' => \&dump_settings);
+
+my $reports_menu = Gtk2::Menu->new;
+$reports_menu->append($view_report_menuitem);
+#$reports_menu->append($dump_loaded_keys_menuitem);
+#$reports_menu->append($dump_settings_menuitem);
+
# Help Menu
my $about_menuitem = Gtk2::MenuItem->new('_About');
$about_menuitem->signal_connect('activate' => \&about);
@@ -224,14 +252,14 @@ my $search_menuitem = Gtk2::MenuItem->new('_Search');
$search_menuitem->set_submenu($search_menu);
$menubar->append($search_menuitem);
-#my $test_menuitem = Gtk2::MenuItem->new('_Test');
-#$test_menuitem->set_submenu($test_menu);
-#$menubar->append($test_menuitem);
-
my $bookmarks_menuitem = Gtk2::MenuItem->new('_Bookmarks');
$bookmarks_menuitem->set_submenu($bookmarks_menu);
$menubar->append($bookmarks_menuitem);
+my $reports_menuitem = Gtk2::MenuItem->new('_Reports');
+$reports_menuitem->set_submenu($reports_menu);
+$menubar->append($reports_menuitem);
+
my $help_menuitem = Gtk2::MenuItem->new('_Help');
$help_menuitem->set_submenu($help_menu);
$menubar->append($help_menuitem);
@@ -242,7 +270,7 @@ my $statusbar = Gtk2::Statusbar->new;
### VBOX
-my $main_vbox = Gtk2::VBox->new;
+my $main_vbox = Gtk2::VBox->new(FALSE, 0);
$main_vbox->pack_start($menubar, FALSE, FALSE, 0);
$main_vbox->pack_start($hpaned, TRUE, TRUE, 0);
$main_vbox->pack_start($statusbar, FALSE, FALSE, 0);
@@ -250,7 +278,7 @@ $main_vbox->pack_start($statusbar, FALSE, FALSE, 0);
### WINDOW
my $window = Gtk2::Window->new;
-$window->set_default_size(600, 400);
+$window->set_default_size($window_width, $window_height);
$window->set_position('center');
$window->signal_connect(destroy => sub { Gtk2->main_quit });
$window->signal_connect(delete_event => sub { save_settings(); return FALSE; });
@@ -274,27 +302,37 @@ sub build_bookmarks_dialog {
my $bookmark_column0 = Gtk2::TreeViewColumn->new_with_attributes(
'Bookmark', Gtk2::CellRendererText->new, 'text', 0);
+ $bookmark_column0->set_resizable(TRUE);
$bookmark_view->append_column($bookmark_column0);
- $bookmark_column0->set_resizable(FALSE);
+ my $bookmark_location_cell = Gtk2::CellRendererText->new;
my $bookmark_column1 = Gtk2::TreeViewColumn->new_with_attributes(
- 'Location', Gtk2::CellRendererText->new, 'text', 1);
- $bookmark_view->append_column($bookmark_column1);
+ 'Location', $bookmark_location_cell, 'text', 1);
+ $bookmark_location_cell->set('ellipsize', 'end');
$bookmark_column1->set_resizable(FALSE);
+ $bookmark_view->append_column($bookmark_column1);
my $scrolled_bookmark_view = Gtk2::ScrolledWindow->new;
$scrolled_bookmark_view->set_policy('automatic', 'automatic');
$scrolled_bookmark_view->set_shadow_type('in');
$scrolled_bookmark_view->add($bookmark_view);
+ my $label = Gtk2::Label->new;
+ $label->set_markup('<i>Drag bookmarks to reorder them</i>');
+
my $dialog = Gtk2::Dialog->new('Edit Bookmarks', $window, 'modal',
'gtk-remove' => 50,
'gtk-ok' => 'ok',
);
- $dialog->resize(400, 200);
- $dialog->vbox->add($scrolled_bookmark_view);
+ $dialog->resize($window_width * 0.8, $window_height * 0.8);
+ $dialog->vbox->pack_start($scrolled_bookmark_view, TRUE, TRUE, 0);
+ $dialog->vbox->pack_start($label, FALSE, FALSE, 5);
$dialog->set_default_response('ok');
+ $dialog->signal_connect(delete_event => sub {
+ $dialog->hide;
+ return TRUE;
+ });
$dialog->signal_connect(response => sub {
my ($dialog, $response) = @_;
if ($response eq '50') {
@@ -325,9 +363,52 @@ sub build_bookmarks_dialog {
my $bookmarks_dialog = build_bookmarks_dialog;
+my $report_view;
+
+sub build_report_dialog {
+ $report_view = Gtk2::TextView->new;
+ $report_view->set_editable(FALSE);
+ $report_view->modify_font(Gtk2::Pango::FontDescription->from_string('monospace'));
+
+ my $text_buffer = $report_view->get_buffer;
+
+ my $scrolled_report_view = Gtk2::ScrolledWindow->new;
+ $scrolled_report_view->set_policy('automatic', 'automatic');
+ $scrolled_report_view->set_shadow_type('in');
+ $scrolled_report_view->add($report_view);
+
+ my $dialog = Gtk2::Dialog->new('Report', $window, 'modal',
+ 'gtk-save' => 50,
+ 'gtk-ok' => 'ok',
+ );
+ $dialog->resize($window_width * 0.8, $window_height * 0.8);
+ $dialog->vbox->add($scrolled_report_view);
+ $dialog->set_default_response('ok');
+
+ $dialog->signal_connect(delete_event => sub {
+ $dialog->hide;
+ return TRUE;
+ });
+ $dialog->signal_connect(response => sub {
+ my ($dialog, $response) = @_;
+ if ($response eq '50') {
+ save_report();
+ }
+ else {
+ $dialog->hide;
+ }
+ });
+
+ return $dialog;
+}
+
+my $report_dialog = build_report_dialog;
+
### GLOBALS
-my $find_param;
+my $search_keys = TRUE;
+my $search_values = TRUE;
+my $find_param = '';
my $find_iter;
my @recent = ();
@@ -564,29 +645,56 @@ sub load_recent {
load_file($filename);
}
-sub open_file {
+sub choose_file {
+ my ($title, $type, $suggested_name) = @_;
+
my $file_chooser = Gtk2::FileChooserDialog->new(
- 'Select Registry File',
+ $title,
undef,
- 'open',
+ $type,
'gtk-cancel' => 'cancel',
'gtk-ok' => 'ok',
);
+ if ($type eq 'save') {
+ $file_chooser->set_current_name($suggested_name);
+ }
if (defined $last_dir) {
$file_chooser->set_current_folder($last_dir);
}
- my $filename;
my $response = $file_chooser->run;
+
+ my $filename;
if ($response eq 'ok') {
$filename = $file_chooser->get_filename;
}
$last_dir = $file_chooser->get_current_folder;
$file_chooser->destroy;
+ return $filename;
+}
+
+sub open_file {
+ my $filename = choose_file('Select Registry File', 'open');
if ($filename) {
load_file($filename);
}
}
+sub save_report {
+ if (my $filename = choose_file('Save Log File As', 'save', "report.txt")) {
+ my $basename = basename $filename;
+ if (open my $fh, ">", $filename) {
+ my $text_buffer = $report_view->get_buffer;
+ my $start_iter = $text_buffer->get_start_iter;
+ my $end_iter = $text_buffer->get_end_iter;
+ print {$fh} $text_buffer->get_text($start_iter, $end_iter, 0);
+# show_message("info", "Report saved to '$basename'");
+ }
+ else {
+ show_message("error", "Error saving log to '$basename'");
+ }
+ }
+}
+
sub close_file {
$tree_store->clear;
$list_store->clear;
@@ -621,6 +729,7 @@ sub show_message {
'ok',
$message,
);
+ $dialog->set_title(ucfirst $type);
$dialog->run;
$dialog->destroy;
}
@@ -628,20 +737,23 @@ sub show_message {
sub create_bookmark_menuitem {
my ($name, $subkey_path) = @_;
- if (my $menuitem = Gtk2::MenuItem->new($name)) {
- $bookmarks_menu->append($menuitem);
- $bookmarks_menu->show_all;
- if (my $iter = $bookmark_store->append) {
- $bookmark_store->set($iter,
- 0, $name,
- 1, $subkey_path,
- 2, $menuitem,
- );
- }
- $menuitem->signal_connect('activate' => \&go_to_bookmark,
- $subkey_path);
+ my $display_name = $name;
+ $display_name =~ s/_/__/g;
+ if (my $menuitem = Gtk2::MenuItem->new($display_name)) {
+ $bookmarks_menu->append($menuitem);
+ $bookmarks_menu->show_all;
+ if (my $iter = $bookmark_store->append) {
+ $bookmark_store->set($iter,
+ 0, $name,
+ 1, $subkey_path,
+ 2, $menuitem,
+ );
}
+ $menuitem->signal_connect('activate' => \&go_to_bookmark,
+ $subkey_path);
+ }
}
+
sub add_bookmark {
my $iter = $tree_selection->get_selected;
return if !defined $iter;
@@ -755,8 +867,10 @@ sub go_to_subkey {
}
if (@path_components == 0) {
+ my $parent_iter = $tree_store->iter_parent($iter);
+ my $parent_path = $tree_store->get_path($parent_iter);
+ $tree_view->expand_to_path($parent_path);
my $tree_path = $tree_store->get_path($iter);
- $tree_view->expand_to_path($tree_path);
$tree_view->scroll_to_cell($tree_path);
$tree_view->set_cursor($tree_path);
$window->set_focus($tree_view);
@@ -765,20 +879,33 @@ sub go_to_subkey {
}
}
+sub get_search_message {
+ my $message;
+ if ($search_keys && $search_values) {
+ $message = "Searching registry keys and values...";
+ }
+ elsif ($search_keys) {
+ $message = "Searching registry keys...";
+ }
+ elsif ($search_values) {
+ $message = "Searching registry values...";
+ }
+ return $message;
+}
+
sub find_next {
if (!defined $find_param || !defined $find_iter) {
return;
}
- # Build find next dialog
my $label = Gtk2::Label->new;
- $label->set_text("Searching registry...");
+ $label->set_text(get_search_message);
my $dialog = Gtk2::Dialog->new('Find',
$window,
'modal',
'gtk-cancel' => 'cancel',
);
- $dialog->vbox->pack_start($label, TRUE, TRUE, 10);
+ $dialog->vbox->pack_start($label, TRUE, TRUE, 5);
$dialog->set_default_response('cancel');
$dialog->show_all;
@@ -787,41 +914,44 @@ sub find_next {
if (!defined $key) {
$dialog->response('ok');
- show_message('info', 'Finished searching.');
return FALSE; # stop searching
}
# Remove root key name to get subkey path
my $subkey_path = (split(/\\/, $key->get_path, 2))[1];
if (!defined $subkey_path) {
- return FALSE; # stop searching
- # (currently get_subtree_iterator never returns the root key)
+ # go_to_subkey locates keys based on the subkey path
+ # and does not support going to the root key.
+ # Therefore if the subkey path is not defined,
+ # the subtree iterator has returned the root key,
+ # so searching it should be skipped.
+ return TRUE; # continue searching
}
+ # Check value (if defined) for a match
if (defined $value) {
- # Check value for a match
- my $value_name = $value->get_name;
- if (index(lc $value_name, lc $find_param) >= 0) {
- go_to_subkey($subkey_path);
- go_to_value($value_name);
- $dialog->response('ok');
- return FALSE; # stop searching
- }
- else {
- return TRUE; # continue searching
+ if ($search_values) {
+ my $value_name = $value->get_name;
+ if (index(lc $value_name, lc $find_param) >= 0) {
+ go_to_subkey($subkey_path);
+ go_to_value($value_name);
+ $dialog->response(50);
+ return FALSE; # stop searching
+ }
}
+ return TRUE; # continue searching
}
# Check key for a match
- my $key_name = $key->get_name;
- if (index(lc $key_name, lc $find_param) >= 0) {
- go_to_subkey($subkey_path);
- $dialog->response('ok');
- return FALSE; # stop searching
- }
- else {
- return TRUE; # continue searching
+ if ($search_keys) {
+ my $key_name = $key->get_name;
+ if (index(lc $key_name, lc $find_param) >= 0) {
+ go_to_subkey($subkey_path);
+ $dialog->response(50);
+ return FALSE; # stop searching
+ }
}
+ return TRUE; # continue searching
});
my $response = $dialog->run;
@@ -830,19 +960,40 @@ sub find_next {
if ($response eq 'cancel' || $response eq 'delete-event') {
Glib::Source->remove($id);
}
+ elsif ($response eq 'ok') {
+ show_message('info', 'Finished searching.');
+ }
}
sub find {
- # Build find dialog
+ return if !defined $tree_store->get_iter_first;
+
my $entry = Gtk2::Entry->new;
+ $entry->set_text($find_param);
$entry->set_activates_default(TRUE);
+ my $check1 = Gtk2::CheckButton->new('Search _Keys');
+ $check1->set_active($search_keys);
+ my $check2 = Gtk2::CheckButton->new('Search _Values');
+ $check2->set_active($search_values);
+ $check1->signal_connect(toggled => sub {
+ if (!$check1->get_active && !$check2->get_active) {
+ $check2->set_active(TRUE);
+ }
+ });
+ $check2->signal_connect(toggled => sub {
+ if (!$check1->get_active && !$check2->get_active) {
+ $check1->set_active(TRUE);
+ }
+ });
my $dialog = Gtk2::Dialog->new('Find',
$window,
'modal',
'gtk-cancel' => 'cancel',
'gtk-ok' => 'ok',
);
- $dialog->vbox->pack_start($entry, TRUE, TRUE, 10);
+ $dialog->vbox->pack_start($entry, TRUE, TRUE, 0);
+ $dialog->vbox->pack_start($check1, TRUE, TRUE, 0);
+ $dialog->vbox->pack_start($check2, TRUE, TRUE, 0);
$dialog->set_default_response('ok');
$dialog->show_all;
@@ -856,6 +1007,8 @@ sub find {
return if !defined $root_key;
if ($response eq 'ok') {
+ $search_keys = $check1->get_active;
+ $search_values = $check2->get_active;
$find_param = $entry->get_text;
$find_iter = undef;
if ($find_param ne '') {
@@ -939,13 +1092,16 @@ sub dump_loaded_keys {
});
}
-sub dump_bookmarks {
- print "Dumping bookmarks:\n";
+sub view_report {
my $root_iter = $tree_store->get_iter_first;
if (!defined $root_iter) {
print "(no registry file loaded)\n";
return;
}
+
+ my $text_buffer = $report_view->get_buffer;
+ $text_buffer->set_text('');
+
my $root_key = $tree_store->get($root_iter, 3);
my $iter = $bookmark_store->get_iter_first;
while (defined $iter) {
@@ -953,8 +1109,21 @@ sub dump_bookmarks {
my $path = $bookmark_store->get($iter, 1);
if (my $key = $root_key->get_subkey($path)) {
- print $key->get_path, "\n";
+ my $str = $key->as_string . "\n";
+ $text_buffer->insert_at_cursor($str);
+ foreach my $value ($key->get_list_of_values) {
+ my $value_name = $value->get_name;
+ $value_name = "(Default)" if $value_name eq "";
+ my $value_type = $value->get_type_as_string;
+ my $str = "$value_name ($value_type):\n";
+ $str .= hexdump($value->get_raw_data);
+ $text_buffer->insert_at_cursor($str);
+ }
+ $text_buffer->insert_at_cursor("\n");
}
$iter = $bookmark_store->iter_next($iter);
}
+
+ $report_dialog->show_all;
}
+
diff --git a/lib/Parse/Win32Registry.pm b/lib/Parse/Win32Registry.pm
index 473d840..dc587ec 100644
--- a/lib/Parse/Win32Registry.pm
+++ b/lib/Parse/Win32Registry.pm
@@ -3,7 +3,7 @@ package Parse::Win32Registry;
use strict;
use warnings;
-our $VERSION = '0.50';
+our $VERSION = '0.51';
use base qw(Exporter);
@@ -359,6 +359,32 @@ The default value (displayed as '(Default)' by REGEDIT)
does not actually have a name. It can obtained by supplying
an empty string, e.g. $key->get_value('');
+=item $key->get_value_data( 'value name' )
+
+Returns the data for the specified value name.
+If either the value or the value's data does not exist,
+nothing will be returned.
+
+This is simply a shortcut for accessing the data of a value
+without creating an intermediate Value object.
+
+The following code:
+
+ my $value = $key->get_value('value name');
+ if (defined $value) {
+ my $data = $value->get_data;
+ if (defined $data) {
+ ...process data...
+ }
+ }
+
+can be replaced with:
+
+ my $data = $key->get_value_data('value name');
+ if (defined $data) {
+ ...process data...
+ }
+
=item $key->get_list_of_subkeys
Returns a list of Key objects representing the subkeys of the
@@ -610,7 +636,8 @@ REG_MULTI_SZ values will be returned as a list of strings when
called in a list context,
and as a string with each element separated by
the list separator $" when called in a scalar context.
-(The list separator defaults to the space character.)
+(The list separator defaults to the space character.
+See perlvar for further information.)
String data will be converted from Unicode (UCS-2LE) for Windows
NT based registry files.
@@ -829,6 +856,13 @@ A SID object represents a Security Identifier.
=over 4
+=item $sid->get_name
+
+Returns a string containing a name for the SID
+(e.g. "Administrators" for S-1-5-32-544)
+if it is a "well known" SID.
+See Microsoft Knowledge Base Article KB243330.
+
=item $sid->as_string
Returns a string containing the SID formatted for presentation.
@@ -1262,7 +1296,8 @@ but with something a little more helpful than a hex editor.
They are not designed for pulling data out of keys and values.
They are designed for providing technical information about keys and values.
-Most of these methods are demonstrated by the supplied regscan.pl script.
+Most of these methods are demonstrated by the supplied regscan.pl
+and regscope.pl scripts.
=head2 Registry Object Methods
@@ -1487,7 +1522,7 @@ Type regclassnames.pl on its own to see the help:
=head2 regcompare.pl
-regview.pl is a GTK+ program for comparing multiple registry files.
+regcompare.pl is a GTK+ program for comparing multiple registry files.
It displays a tree of the registry keys and values
highlighting the changed keys and values,
and a table detailing the actual changes.
@@ -1500,6 +1535,8 @@ Filenames of registry files to compare can be supplied on the command line:
regcompare.pl <filename1> <filename2> <filename3> ...
+You can of course use wildcards when running from a Unix shell.
+
=head2 regdump.pl
regdump.pl is used to display the keys and values of a registry file.
@@ -1621,6 +1658,23 @@ Type regscan.pl on its own to see the help:
-u or --unparsed show the unparsed on-disk entries as a hex dump
-w or --warnings display warnings of invalid keys and values
+=head2 regscope.pl
+
+regscope.pl is a GTK+ registry scanner.
+It presents all the entries in a registry file returned by the
+get_hbin_iterator and get_entry_iterator methods.
+When viewing Windows NT registry files, it uses color to highlight
+key, value, security, and subkey list entries, and presents the hbin
+as a colored map.
+
+It requires Gtk2-Perl to be installed.
+Links to Windows binaries can be found via the project home page at
+L<http://gtk2-perl.sourceforge.net/win32/>.
+
+A filename can also be supplied on the command line:
+
+ regscope.pl <filename>
+
=head2 regsecurity.pl
regsecurity.pl will display the security information
@@ -1708,7 +1762,7 @@ A filename can also be supplied on the command line:
This would not have been possible without the work of those people who have
analysed and documented the structure of Windows Registry files, namely:
the WINE Project (see misc/registry.c in older releases),
-the Samba Project (see utils/editreg.c and utils/profiles.c),
+the Samba Project (see utils/editreg.c and utils/profiles.c in older releases),
Petter Nordahl-Hagen (see chntpw's ntreg.h),
and B.D. (see WinReg.txt).
diff --git a/lib/Parse/Win32Registry/Base.pm b/lib/Parse/Win32Registry/Base.pm
index eab07cd..840bd7d 100644
--- a/lib/Parse/Win32Registry/Base.pm
+++ b/lib/Parse/Win32Registry/Base.pm
@@ -445,7 +445,11 @@ sub make_multiple_subtree_iterator {
croak "Usage: make_multiple_subtree_iterator\(\$key1, \$key2, ...\)";
}
- push my @subkey_iters, make_multiple_subkey_iterator(@keys);
+ my @subkeys_queue = (\@keys);
+ push my (@subkey_iters), Parse::Win32Registry::Iterator->new(sub {
+ return shift @subkeys_queue;
+ });
+# make_multiple_subkey_iterator(@keys);
my $value_iter;
my $subkeys;
@@ -881,11 +885,14 @@ sub get_trustee {
sub as_string {
my $self = shift;
+ my $sid = $self->{_trustee};
my $string = sprintf "%s 0x%02x 0x%08x %s",
_look_up_ace_type($self->{_type}),
$self->{_flags},
$self->{_mask},
- $self->{_trustee}->as_string;
+ $sid->as_string;
+ my $name = $sid->get_name;
+ $string .= " [$name]" if defined $name;
return $string;
}
@@ -1087,10 +1094,16 @@ sub as_stanza {
my $stanza = "";
if (defined(my $owner = $self->{_owner})) {
- $stanza .= "Owner SID: " . $owner->as_string . "\n";
+ $stanza .= "Owner SID: " . $owner->as_string;
+ my $name = $owner->get_name;
+ $stanza .= " [$name]" if defined $name;
+ $stanza .= "\n";
}
if (defined(my $group = $self->{_group})) {
- $stanza .= "Group SID: " . $group->as_string . "\n";
+ $stanza .= "Group SID: " . $group->as_string;
+ my $name = $group->get_name;
+ $stanza .= " [$name]" if defined $name;
+ $stanza .= "\n";
}
if (defined(my $sacl = $self->{_sacl})) {
foreach my $ace ($sacl->get_list_of_aces) {
diff --git a/lib/Parse/Win32Registry/Key.pm b/lib/Parse/Win32Registry/Key.pm
index c923dce..697c86c 100644
--- a/lib/Parse/Win32Registry/Key.pm
+++ b/lib/Parse/Win32Registry/Key.pm
@@ -172,7 +172,10 @@ sub get_list_of_values {
sub get_subtree_iterator {
my $self = shift;
- push my @subkey_iters, $self->get_subkey_iterator;
+ my @start_keys = ($self);
+ push my (@subkey_iters), Parse::Win32Registry::Iterator->new(sub {
+ return shift @start_keys;
+ });
my $value_iter;
my $key;
diff --git a/lib/Parse/Win32Registry/Win95/Key.pm b/lib/Parse/Win32Registry/Win95/Key.pm
index 719cfaf..3ee15bf 100644
--- a/lib/Parse/Win32Registry/Win95/Key.pm
+++ b/lib/Parse/Win32Registry/Win95/Key.pm
@@ -259,4 +259,18 @@ sub get_value_iterator {
});
}
+sub get_associated_offsets {
+ my $self = shift;
+
+ my @owners = ();
+
+ push @owners, $self->{_offset};
+
+ if (defined $self->{_offset_to_rgdb_entry}) {
+ push @owners, $self->{_offset_to_rgdb_entry};
+ }
+
+ return @owners;
+}
+
1;
diff --git a/lib/Parse/Win32Registry/Win95/Value.pm b/lib/Parse/Win32Registry/Win95/Value.pm
index a8a5f5d..de08361 100644
--- a/lib/Parse/Win32Registry/Win95/Value.pm
+++ b/lib/Parse/Win32Registry/Win95/Value.pm
@@ -177,4 +177,14 @@ sub parse_info {
return $info;
}
+sub get_associated_offsets {
+ my $self = shift;
+
+ my @owners = ();
+
+ push @owners, $self->{_offset};
+
+ return @owners;
+}
+
1;
diff --git a/lib/Parse/Win32Registry/WinNT/Key.pm b/lib/Parse/Win32Registry/WinNT/Key.pm
index dcffdfd..cbd7ec5 100644
--- a/lib/Parse/Win32Registry/WinNT/Key.pm
+++ b/lib/Parse/Win32Registry/WinNT/Key.pm
@@ -480,4 +480,36 @@ sub get_value_iterator {
});
}
+sub get_associated_offsets {
+ my $self = shift;
+
+ my @owners = ();
+
+ push @owners, $self->{_offset};
+
+ if ($self->{_offset_to_security}) {
+ push @owners, $self->{_offset_to_security};
+ }
+
+ if ($self->{_offset_to_class_name}) {
+ push @owners, $self->{_offset_to_class_name};
+ }
+
+ if ($self->{_num_subkeys}) {
+ push @owners, $self->{_offset_to_subkey_list};
+ }
+
+ # Indirect offsets must be added after _get_offsets_to_subkeys
+ # has been called (as this populates the _indirect_offsets field)
+ if ($self->{_indirect_offsets}) {
+ push @owners, keys %{ $self->{_indirect_offsets} };
+ }
+
+ if ($self->{_num_values}) {
+ push @owners, $self->{_offset_to_value_list};
+ }
+
+ return @owners;
+}
+
1;
diff --git a/lib/Parse/Win32Registry/WinNT/Value.pm b/lib/Parse/Win32Registry/WinNT/Value.pm
index e36b593..71e9056 100644
--- a/lib/Parse/Win32Registry/WinNT/Value.pm
+++ b/lib/Parse/Win32Registry/WinNT/Value.pm
@@ -237,4 +237,18 @@ sub parse_info {
return $info;
}
+sub get_associated_offsets {
+ my $self = shift;
+
+ my @owners = ();
+
+ push @owners, $self->{_offset};
+
+ if (!$self->{_data_inline}) {
+ push @owners, $self->{_offset_to_data};
+ }
+
+ return @owners;
+}
+
1;
diff --git a/t/iterator.t b/t/iterator.t
new file mode 100644
index 0000000..d464557
--- /dev/null
+++ b/t/iterator.t
@@ -0,0 +1,253 @@
+use strict;
+use warnings;
+
+use Test::More 'no_plan';
+use Data::Dumper;
+use Parse::Win32Registry 0.51 qw(:REG_ make_multiple_subtree_iterator);
+
+$Data::Dumper::Useqq = 1;
+$Data::Dumper::Terse = 1;
+$Data::Dumper::Indent = 0;
+
+Parse::Win32Registry::disable_warnings;
+
+sub find_file
+{
+ my $filename = shift;
+ return -d 't' ? "t/$filename" : $filename;
+}
+
+sub run_subtree_iterator_tests
+{
+ my $key = shift;
+ my @tests = @_;
+
+ my ($os) = ref($key) =~ /Win(NT|95)/;
+
+ # key+value tests
+
+ my $subtree_iter = $key->get_subtree_iterator;
+ ok(defined $subtree_iter, "$os get_subtree_iterator defined");
+ isa_ok($subtree_iter, "Parse::Win32Registry::Iterator");
+ for (my $i = 0; $i < @tests; $i++) {
+ my ($key_path, $value_name) = @{$tests[$i]};
+
+ my ($key, $value) = $subtree_iter->get_next;
+
+ my $desc = "$os (list) TEST" . ($i + 1);
+
+ ok(defined $key, "$desc key defined (valid key)");
+ is($key->get_path, $key_path,
+ "$desc key get_path eq " . Dumper($key_path));
+
+ if (defined $value_name) {
+ ok(defined $value, "$desc value defined (valid value)");
+ is($value->get_name, $value_name,
+ "$desc value get_name eq " . Dumper($value_name));
+ }
+ else {
+ ok(!defined $value, "$desc value undefined (no value)");
+ }
+ }
+ my @final = $subtree_iter->get_next;
+ is(@final, 0, "$os (list) iterator empty");
+
+ # key tests
+
+ @tests = grep { !defined $_->[1] } @tests;
+
+ $subtree_iter = $key->get_subtree_iterator;
+ ok(defined $subtree_iter, "$os get_subtree_iterator defined");
+ isa_ok($subtree_iter, "Parse::Win32Registry::Iterator");
+ for (my $i = 0; $i < @tests; $i++) {
+ my ($key_path, $value_name) = @{$tests[$i]};
+
+ my $key = $subtree_iter->get_next;
+
+ my $desc = "$os (scalar) TEST" . ($i + 1);
+
+ ok(defined $key, "$desc key defined (valid key)");
+ is($key->get_path, $key_path,
+ "$desc key get_path eq " . Dumper($key_path));
+ }
+ my $final = $subtree_iter->get_next;
+ ok(!defined $final, "$os (scalar) iterator empty");
+}
+
+sub run_multiple_subtree_iterator_tests {
+ my $key = shift;
+ my @tests = @_;
+
+ my ($os) = ref($key) =~ /Win(NT|95)/;
+
+ # key+value tests
+
+ my $subtree_iter = make_multiple_subtree_iterator($key);
+ ok(defined $subtree_iter,
+ "$os (list) make_multiple_subtree_iterator defined");
+ isa_ok($subtree_iter, "Parse::Win32Registry::Iterator");
+ for (my $i = 0; $i < @tests; $i++) {
+ my ($key_path, $value_name) = @{$tests[$i]};
+
+ my ($keys_ref, $values_ref) = $subtree_iter->get_next;
+
+ my $desc = "$os (list) TEST" . ($i + 1);
+
+ ok(defined $keys_ref, "$desc keys_ref defined");
+ is(ref $keys_ref, 'ARRAY', "$desc keys_ref array");
+ is($keys_ref->[0]->get_path, $key_path,
+ "$desc keys_ref->[0] get_path eq " . Dumper($key_path));
+
+ if (defined $value_name) {
+ ok(defined $values_ref, "$desc values_ref defined");
+ is(ref $values_ref, 'ARRAY', "$desc values_ref array");
+ is($values_ref->[0]->get_name, $value_name,
+ "$desc values_ref->[0] get_name eq " . Dumper($value_name));
+ }
+ else {
+ ok(!defined $values_ref, "$desc values_ref undefined");
+ }
+ }
+ my @final = $subtree_iter->get_next;
+ is(@final, 0, "$os (list) iterator empty");
+
+ # key tests
+
+ @tests = grep { !defined $_->[1] } @tests;
+
+ $subtree_iter = make_multiple_subtree_iterator($key);
+ ok(defined $subtree_iter,
+ "$os (scalar) make_multiple_subtree_iterator defined");
+ isa_ok($subtree_iter, "Parse::Win32Registry::Iterator");
+ for (my $i = 0; $i < @tests; $i++) {
+ my ($key_path, $value_name) = @{$tests[$i]};
+
+ my $keys_ref = $subtree_iter->get_next;
+
+ my $desc = "$os (scalar) TEST" . ($i + 1);
+
+ ok(defined $keys_ref, "$desc keys_ref defined");
+ is(ref $keys_ref, 'ARRAY', "$desc keys_ref array");
+ is($keys_ref->[0]->get_path, $key_path,
+ "$desc keys_ref->[0] get_path eq " . Dumper($key_path));
+ }
+ my $final = $subtree_iter->get_next;
+ ok(!defined $final, "$os (scalar) iterator empty");
+}
+
+{
+ my $filename = find_file('win95_iter_tests.rf');
+
+ my $registry = Parse::Win32Registry->new($filename);
+
+ my $root_key = $registry->get_root_key;
+
+ my @tests = (
+ [""],
+ ["\\key1"],
+ ["\\key1", "value1"],
+ ["\\key1", "value2"],
+ ["\\key1\\key3"],
+ ["\\key1\\key3", "value5"],
+ ["\\key1\\key3", "value6"],
+ ["\\key1\\key4"],
+ ["\\key1\\key4", "value7"],
+ ["\\key1\\key4", "value8"],
+ ["\\key2"],
+ ["\\key2", "value3"],
+ ["\\key2", "value4"],
+ ["\\key2\\key5"],
+ ["\\key2\\key5", "value9"],
+ ["\\key2\\key5", "value10"],
+ ["\\key2\\key6"],
+ ["\\key2\\key6", "value11"],
+ ["\\key2\\key6", "value12"],
+ );
+
+ run_subtree_iterator_tests($root_key, @tests);
+
+ @tests = (
+ [""],
+ ["\\key1"],
+ ["\\key1", "value1"],
+ ["\\key1", "value2"],
+ ["\\key1\\key3"],
+ ["\\key1\\key3", "value5"],
+ ["\\key1\\key3", "value6"],
+ ["\\key1\\key4"],
+ ["\\key1\\key4", "value7"],
+ ["\\key1\\key4", "value8"],
+ ["\\key2"],
+ ["\\key2", "value3"],
+ ["\\key2", "value4"],
+ ["\\key2\\key5"],
+ ["\\key2\\key5", "value10"],
+ ["\\key2\\key5", "value9"],
+ ["\\key2\\key6"],
+ ["\\key2\\key6", "value11"],
+ ["\\key2\\key6", "value12"],
+ );
+
+ run_multiple_subtree_iterator_tests($root_key, @tests);
+}
+
+
+{
+ my $filename = find_file('winnt_iter_tests.rf');
+
+ my $registry = Parse::Win32Registry->new($filename);
+
+ my $root_key = $registry->get_root_key;
+
+ my @tests = (
+ ["\$\$\$PROTO.HIV"],
+ ["\$\$\$PROTO.HIV", "value1"],
+ ["\$\$\$PROTO.HIV", "value2"],
+ ["\$\$\$PROTO.HIV\\key1"],
+ ["\$\$\$PROTO.HIV\\key1", "value3"],
+ ["\$\$\$PROTO.HIV\\key1", "value4"],
+ ["\$\$\$PROTO.HIV\\key1\\key3"],
+ ["\$\$\$PROTO.HIV\\key1\\key3", "value7"],
+ ["\$\$\$PROTO.HIV\\key1\\key3", "value8"],
+ ["\$\$\$PROTO.HIV\\key1\\key4"],
+ ["\$\$\$PROTO.HIV\\key1\\key4", "value9"],
+ ["\$\$\$PROTO.HIV\\key1\\key4", "value10"],
+ ["\$\$\$PROTO.HIV\\key2"],
+ ["\$\$\$PROTO.HIV\\key2", "value5"],
+ ["\$\$\$PROTO.HIV\\key2", "value6"],
+ ["\$\$\$PROTO.HIV\\key2\\key5"],
+ ["\$\$\$PROTO.HIV\\key2\\key5", "value11"],
+ ["\$\$\$PROTO.HIV\\key2\\key5", "value12"],
+ ["\$\$\$PROTO.HIV\\key2\\key6"],
+ ["\$\$\$PROTO.HIV\\key2\\key6", "value13"],
+ ["\$\$\$PROTO.HIV\\key2\\key6", "value14"],
+ );
+
+ run_subtree_iterator_tests($root_key, @tests);
+
+ @tests = (
+ ["\$\$\$PROTO.HIV"],
+ ["\$\$\$PROTO.HIV", "value1"],
+ ["\$\$\$PROTO.HIV", "value2"],
+ ["\$\$\$PROTO.HIV\\key1"],
+ ["\$\$\$PROTO.HIV\\key1", "value3"],
+ ["\$\$\$PROTO.HIV\\key1", "value4"],
+ ["\$\$\$PROTO.HIV\\key1\\key3"],
+ ["\$\$\$PROTO.HIV\\key1\\key3", "value7"],
+ ["\$\$\$PROTO.HIV\\key1\\key3", "value8"],
+ ["\$\$\$PROTO.HIV\\key1\\key4"],
+ ["\$\$\$PROTO.HIV\\key1\\key4", "value10"],
+ ["\$\$\$PROTO.HIV\\key1\\key4", "value9"],
+ ["\$\$\$PROTO.HIV\\key2"],
+ ["\$\$\$PROTO.HIV\\key2", "value5"],
+ ["\$\$\$PROTO.HIV\\key2", "value6"],
+ ["\$\$\$PROTO.HIV\\key2\\key5"],
+ ["\$\$\$PROTO.HIV\\key2\\key5", "value11"],
+ ["\$\$\$PROTO.HIV\\key2\\key5", "value12"],
+ ["\$\$\$PROTO.HIV\\key2\\key6"],
+ ["\$\$\$PROTO.HIV\\key2\\key6", "value13"],
+ ["\$\$\$PROTO.HIV\\key2\\key6", "value14"],
+ );
+
+ run_multiple_subtree_iterator_tests($root_key, @tests);
+}
diff --git a/t/use.t b/t/use.t
index cc20b7d..195c05f 100644
--- a/t/use.t
+++ b/t/use.t
@@ -5,7 +5,7 @@ use Test::More 'no_plan';
BEGIN { use_ok('Parse::Win32Registry') };
-is($Parse::Win32Registry::VERSION, '0.50', 'correct version');
+is($Parse::Win32Registry::VERSION, '0.51', 'correct version');
can_ok('Parse::Win32Registry', 'new');
can_ok('Parse::Win32Registry', 'convert_filetime_to_epoch_time');
can_ok('Parse::Win32Registry', 'iso8601');
diff --git a/t/win95_iter_tests.rf b/t/win95_iter_tests.rf
new file mode 100644
index 0000000..618e8da
Binary files /dev/null and b/t/win95_iter_tests.rf differ
diff --git a/t/winnt_iter_tests.rf b/t/winnt_iter_tests.rf
new file mode 100644
index 0000000..ac56a34
Binary files /dev/null and b/t/winnt_iter_tests.rf differ
--
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/pkg-perl/packages/libsendmail-milter-perl.git
More information about the Pkg-perl-cvs-commits
mailing list