Currently 'hivexget' is a very clumsy command line tool which only lets you display a single registry key in a hive file, for example: $ hivexget SOFTWARE '\Microsoft\Windows\TabletPC\TabSetup\' "TabletSetup"=dword:00000000" This is inflexible and got really annoying as I was trying to explore these hive files in preparation for adding write support to hivex. The first patch implements a simple interactive shell which allows you to 'cd' around a hive and list out values and subkeys. For example: $ ./hivex/hivexsh SOFTWARE Welcome to hivexsh, the hivex interactive shell for examining Windows Registry binary hive files. Type: 'help' for help summary 'quit' to quit the shell SOFTWARE\> ls ATI Technologies Classes Clients Intel Microsoft ODBC Policies RegisteredApplications Sonic Wow6432Node SOFTWARE\> cd \Microsoft\Windows\TabletPC\TabSetup SOFTWARE\Microsoft\Windows\TabletPC\TabSetup> ls SOFTWARE\Microsoft\Windows\TabletPC\TabSetup> lsval "TabletSetup"=dword:00000000" SOFTWARE\Microsoft\Windows\TabletPC\TabSetup> cd .. SOFTWARE\Microsoft\Windows\TabletPC> ls Snipping Tool TabSetup The (later) write part will enhance this shell to allow hive modifications, but these patches do not contain that change yet. The second patch removes the old C-based hivexget and replaces it with a simple shell script based around hivexsh. Rich. -- Richard Jones, Virtualization Group, Red Hat http://people.redhat.com/~rjones virt-df lists disk usage of guests without needing to install any software inside the virtual machine. Supports Linux and Windows. http://et.redhat.com/~rjones/virt-df/
Richard W.M. Jones
2010-Jan-29 19:21 UTC
[Libguestfs] [PATCH 1/2] hivex: Add 'hivexsh' program (shell for navigating registry hives.
-- Richard Jones, Virtualization Group, Red Hat http://people.redhat.com/~rjones libguestfs lets you edit virtual machines. Supports shell scripting, bindings from many languages. http://et.redhat.com/~rjones/libguestfs/ See what it can do: http://et.redhat.com/~rjones/libguestfs/recipes.html -------------- next part -------------->From 15a5511e038f53ade23fa998985ecd4de39b60d5 Mon Sep 17 00:00:00 2001From: Richard Jones <rjones at redhat.com> Date: Fri, 29 Jan 2010 12:18:30 +0000 Subject: [PATCH 1/2] hivex: Add 'hivexsh' program (shell for navigating registry hives). --- .gitignore | 2 + hivex/Makefile.am | 35 ++- hivex/hivexget.c | 4 +- hivex/hivexget.pod | 1 + hivex/hivexml.pod | 1 + hivex/hivexsh.c | 779 ++++++++++++++++++++++++++++++++++++++++++++++++++++ hivex/hivexsh.pod | 179 ++++++++++++ po/POTFILES.in | 1 + tools/virt-win-reg | 1 + 9 files changed, 997 insertions(+), 6 deletions(-) create mode 100644 hivex/hivexsh.c create mode 100644 hivex/hivexsh.pod diff --git a/.gitignore b/.gitignore index 5b4d356..a80f1ec 100644 --- a/.gitignore +++ b/.gitignore @@ -83,12 +83,14 @@ haskell/Guestfs.hs hivex/*.1 hivex/*.3 hivex/hivexget +hivex/hivexsh hivex/hivexml html/guestfish.1.html html/guestfs.3.html html/guestmount.1.html html/hivex.3.html html/hivexget.1.html +html/hivexsh.1.html html/hivexml.1.html html/recipes.html html/virt-cat.1.html diff --git a/hivex/Makefile.am b/hivex/Makefile.am index 31275ea..90fd716 100644 --- a/hivex/Makefile.am +++ b/hivex/Makefile.am @@ -15,7 +15,7 @@ # along with this program; if not, write to the Free Software # Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. -EXTRA_DIST = hivex.pod hivexml.pod hivexget.pod LICENSE +EXTRA_DIST = hivex.pod hivexml.pod hivexget.pod hivexsh.pod LICENSE lib_LTLIBRARIES = libhivex.la @@ -27,7 +27,7 @@ libhivex_la_LDFLAGS = -version-info 0:0:0 libhivex_la_CFLAGS = \ $(WARN_CFLAGS) $(WERROR_CFLAGS) -bin_PROGRAMS = hivexml hivexget +bin_PROGRAMS = hivexml hivexget hivexsh hivexml_SOURCES = \ hivexml.c @@ -48,7 +48,17 @@ hivexget_CFLAGS = \ -DLOCALEBASEDIR=\""$(datadir)/locale"\" \ $(WARN_CFLAGS) $(WERROR_CFLAGS) -man_MANS = hivex.3 hivexml.1 hivexget.1 +hivexsh_SOURCES = \ + hivexsh.c + +hivexsh_LDADD = libhivex.la ../gnulib/lib/libgnu.la $(LIBREADLINE) +hivexsh_CFLAGS = \ + -I$(top_srcdir)/gnulib/lib \ + -I$(top_srcdir)/src \ + -DLOCALEBASEDIR=\""$(datadir)/locale"\" \ + $(WARN_CFLAGS) $(WERROR_CFLAGS) + +man_MANS = hivex.3 hivexml.1 hivexget.1 hivexsh.1 hivex.3: hivex.pod $(POD2MAN) \ @@ -74,10 +84,19 @@ hivexget.1: hivexget.pod --release "$(PACKAGE_NAME)-$(PACKAGE_VERSION)" \ $< > $@-t; mv $@-t $@ +hivexsh.1: hivexsh.pod + $(POD2MAN) \ + --section 1 \ + -c "Windows Registry" \ + --name "hivexsh" \ + --release "$(PACKAGE_NAME)-$(PACKAGE_VERSION)" \ + $< > $@-t; mv $@-t $@ + noinst_DATA = \ $(top_builddir)/html/hivex.3.html \ $(top_builddir)/html/hivexml.1.html \ - $(top_builddir)/html/hivexget.1.html + $(top_builddir)/html/hivexget.1.html \ + $(top_builddir)/html/hivexsh.1.html $(top_builddir)/html/hivex.3.html: hivex.pod mkdir -p $(top_builddir)/html @@ -102,3 +121,11 @@ $(top_builddir)/html/hivexget.1.html: hivexget.pod --htmldir html \ --outfile html/hivexget.1.html \ hivex/hivexget.pod + +$(top_builddir)/html/hivexsh.1.html: hivexsh.pod + mkdir -p $(top_builddir)/html + cd $(top_builddir) && pod2html \ + --css 'pod.css' \ + --htmldir html \ + --outfile html/hivexsh.1.html \ + hivex/hivexsh.pod diff --git a/hivex/hivexget.c b/hivex/hivexget.c index fd49293..4f2419c 100644 --- a/hivex/hivexget.c +++ b/hivex/hivexget.c @@ -60,7 +60,7 @@ main (int argc, char *argv[]) } if (path[1] == '\\') { doubled: - fprintf (stderr, _("hivexget: %s: \\ characters in path are doubled - are you escaping the path parameter correctly?\n"), path); + fprintf (stderr, _("%s: %s: \\ characters in path are doubled - are you escaping the path parameter correctly?\n"), "hivexget", path); exit (EXIT_FAILURE); } @@ -118,7 +118,7 @@ main (int argc, char *argv[]) if (errno) goto error; /* else key not found */ - fprintf (stderr, _("hivexget: %s: key not found\n"), key); + fprintf (stderr, _("%s: %s: key not found\n"), "hivexget", key); exit (EXIT_NOT_FOUND); } diff --git a/hivex/hivexget.pod b/hivex/hivexget.pod index fa390e0..4fbac13 100644 --- a/hivex/hivexget.pod +++ b/hivex/hivexget.pod @@ -65,6 +65,7 @@ If it's a numeric value, it is printed as a decimal number. L<hivex(3)>, L<hivexml(1)>, +L<hivexsh(1)>, L<virt-win-reg(1)>, L<guestfs(3)>, L<http://libguestfs.org/>, diff --git a/hivex/hivexml.pod b/hivex/hivexml.pod index 448c4f6..d6a87b4 100644 --- a/hivex/hivexml.pod +++ b/hivex/hivexml.pod @@ -35,6 +35,7 @@ skips over any parts of the Registry that we cannot read. L<hivex(3)>, L<hivexget(1)>, +L<hivexsh(1)>, L<virt-win-reg(1)>, L<guestfs(3)>, L<http://libguestfs.org/>, diff --git a/hivex/hivexsh.c b/hivex/hivexsh.c new file mode 100644 index 0000000..1cecaad --- /dev/null +++ b/hivex/hivexsh.c @@ -0,0 +1,779 @@ +/* hivexsh - Hive shell. + * Copyright (C) 2009 Red Hat Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include <config.h> + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <stdint.h> +#include <inttypes.h> +#include <fcntl.h> +#include <unistd.h> +#include <assert.h> +#include <errno.h> + +#ifdef HAVE_LIBREADLINE +#include <readline/readline.h> +#include <readline/history.h> +#endif + +#ifdef HAVE_GETTEXT +#include "gettext.h" +#define _(str) dgettext(PACKAGE, (str)) +//#define N_(str) dgettext(PACKAGE, (str)) +#else +#define _(str) str +//#define N_(str) str +#endif + +#define STREQ(a,b) (strcmp((a),(b)) == 0) +#define STRCASEEQ(a,b) (strcasecmp((a),(b)) == 0) +#define STRNEQ(a,b) (strcmp((a),(b)) != 0) +//#define STRCASENEQ(a,b) (strcasecmp((a),(b)) != 0) +//#define STREQLEN(a,b,n) (strncmp((a),(b),(n)) == 0) +//#define STRCASEEQLEN(a,b,n) (strncasecmp((a),(b),(n)) == 0) +//#define STRNEQLEN(a,b,n) (strncmp((a),(b),(n)) != 0) +//#define STRCASENEQLEN(a,b,n) (strncasecmp((a),(b),(n)) != 0) +//#define STRPREFIX(a,b) (strncmp((a),(b),strlen((b))) == 0) + +#include "c-ctype.h" + +#include "hivex.h" + +static int quit = 0; +static hive_h *h = NULL; +static char *prompt_string = NULL; /* Prompt string. */ +static char *loaded = NULL; /* Basename of loaded file, if any. */ +static hive_node_h cwd; /* Current node. */ +static int open_flags = 0; /* Flags used when loading a hive file. */ + +static void usage (void) __attribute__((noreturn)); +static void print_node_path (hive_node_h, FILE *); +static void set_prompt_string (void); +static void initialize_readline (void); +static void cleanup_readline (void); +static void add_history_line (const char *); +static char *rl_gets (int prompt); +static void sort_strings (char **strings, int len); +static int dispatch (char *cmd, char *args); +static int cmd_cd (char *path); +static int cmd_close (char *path); +static int cmd_help (char *args); +static int cmd_load (char *hivefile); +static int cmd_ls (char *args); +static int cmd_lsval (char *args); + +static void +usage (void) +{ + fprintf (stderr, "hivexsh [-df] [hivefile]\n"); + exit (EXIT_FAILURE); +} + +int +main (int argc, char *argv[]) +{ + setlocale (LC_ALL, ""); + bindtextdomain (PACKAGE, LOCALEBASEDIR); + textdomain (PACKAGE); + + int c; + const char *filename = NULL; + + set_prompt_string (); + + while ((c = getopt (argc, argv, "df")) != EOF) { + switch (c) { + case 'd': + open_flags |= HIVEX_OPEN_DEBUG; + break; + case 'f': + filename = optarg; + break; + default: + usage (); + } + } + + if (optind < argc) { + if (optind + 1 != argc) + usage (); + if (cmd_load (argv[optind]) == -1) + exit (EXIT_FAILURE); + } + + /* -f filename parameter */ + if (filename) { + close (0); + if (open (filename, O_RDONLY) == -1) { + perror (filename); + exit (EXIT_FAILURE); + } + } + + /* Main loop. */ + initialize_readline (); + int prompt = isatty (0); + + if (prompt) + printf (_( +"\n" +"Welcome to hivexsh, the hivex interactive shell for examining\n" +"Windows Registry binary hive files.\n" +"\n" +"Type: 'help' for help summary\n" +" 'quit' to quit the shell\n" +"\n")); + + while (!quit) { + char *buf = rl_gets (prompt); + if (!buf) { + quit = 1; + printf ("\n"); + break; + } + + while (*buf && c_isspace (*buf)) + buf++; + + /* Ignore blank line. */ + if (!*buf) continue; + + /* If the next character is '#' then this is a comment. */ + if (*buf == '#') continue; + + /* Parsing is very simple - much simpler than guestfish. This is + * because Registry keys often contain spaces, and we don't want + * to bother with quoting. Therefore here we just split at the + * first whitespace into "cmd<whitespace>arg(s)". We let the + * command decide how to deal with arg(s), if at all. + */ + size_t len = strcspn (buf, " \t"); + + if (len == 0) continue; + + char *cmd = buf; + char *args; + size_t i = 0; + + if (buf[len] == '\0') { + /* This is mostly safe. Although the cmd_* functions do sometimes + * modify args, then shouldn't do so when args is "". + */ + args = (char *) ""; + goto got_command; + } + + buf[len] = '\0'; + args = buf + len + 1 + strspn (&buf[len+1], " \t"); + + len = strlen (args); + while (len > 0 && c_isspace (args[len-1])) { + args[len-1] = '\0'; + len--; + } + + got_command: + /*printf ("command: '%s' args: '%s'\n", cmd, args)*/; + int r = dispatch (cmd, args); + if (!prompt && r == -1) + exit (EXIT_FAILURE); + } + + cleanup_readline (); + free (prompt_string); + free (loaded); + if (h) hivex_close (h); + exit (0); +} + +/* Set the prompt string. This is called whenever it could change, eg. + * after loading a file or changing directory. + */ +static void +set_prompt_string (void) +{ + free (prompt_string); + prompt_string = NULL; + + FILE *fp; + char *ptr; + size_t size; + fp = open_memstream (&ptr, &size); + if (fp == NULL) { + perror ("open_memstream"); + exit (1); + } + + if (h) { + assert (loaded != NULL); + assert (cwd != 0); + + fputs (loaded, fp); + print_node_path (cwd, fp); + } + + fprintf (fp, "> "); + fclose (fp); + prompt_string = ptr; +} + +/* Print the \full\path of a node. */ +static void +print_node_path (hive_node_h node, FILE *fp) +{ + hive_node_h root = hivex_root (h); + + if (node == root) { + fputc ('\\', fp); + return; + } + + hive_node_h parent = hivex_node_parent (h, node); + if (parent == 0) { + fprintf (stderr, _("hivexsh: error getting parent of node %zu\n"), node); + return; + } + print_node_path (parent, fp); + + if (parent != root) + fputc ('\\', fp); + + char *name = hivex_node_name (h, node); + if (name == NULL) { + fprintf (stderr, _("hivexsh: error getting node name of node %zx\n"), node); + return; + } + + fputs (name, fp); + free (name); +} + +static char *line_read = NULL; + +static char * +rl_gets (int prompt) +{ +#ifdef HAVE_LIBREADLINE + + if (prompt) { + if (line_read) { + free (line_read); + line_read = NULL; + } + + line_read = readline (prompt_string); + + if (line_read && *line_read) + add_history_line (line_read); + + return line_read; + } + +#endif /* HAVE_LIBREADLINE */ + + static char buf[8192]; + int len; + + if (prompt) + printf ("%s", prompt_string); + line_read = fgets (buf, sizeof buf, stdin); + + if (line_read) { + len = strlen (line_read); + if (len > 0 && buf[len-1] == '\n') buf[len-1] = '\0'; + } + + return line_read; +} + +#ifdef HAVE_LIBREADLINE +static char histfile[1024]; +static int nr_history_lines = 0; +#endif + +static void +initialize_readline (void) +{ +#ifdef HAVE_LIBREADLINE + const char *home; + + home = getenv ("HOME"); + if (home) { + snprintf (histfile, sizeof histfile, "%s/.hivexsh", home); + using_history (); + (void) read_history (histfile); + } + + rl_readline_name = "hivexsh"; +#endif +} + +static void +cleanup_readline (void) +{ +#ifdef HAVE_LIBREADLINE + int fd; + + if (histfile[0] != '\0') { + fd = open (histfile, O_WRONLY|O_CREAT, 0644); + if (fd == -1) { + perror (histfile); + return; + } + close (fd); + + (void) append_history (nr_history_lines, histfile); + } +#endif +} + +static void +add_history_line (const char *line) +{ +#ifdef HAVE_LIBREADLINE + add_history (line); + nr_history_lines++; +#endif +} + +static int +compare (const void *vp1, const void *vp2) +{ + char * const *p1 = (char * const *) vp1; + char * const *p2 = (char * const *) vp2; + return strcasecmp (*p1, *p2); +} + +static void +sort_strings (char **strings, int len) +{ + qsort (strings, len, sizeof (char *), compare); +} + +static int +dispatch (char *cmd, char *args) +{ + if (STRCASEEQ (cmd, "help")) + return cmd_help (args); + else if (STRCASEEQ (cmd, "load")) + return cmd_load (args); + else if (STRCASEEQ (cmd, "exit") || + STRCASEEQ (cmd, "q") || + STRCASEEQ (cmd, "quit")) { + quit = 1; + return 0; + } + + /* If no hive file is loaded (!h) then only the small selection of + * commands above will work. + */ + if (!h) { + fprintf (stderr, _("hivexsh: you must load a hive file first using 'load hivefile'\n")); + return -1; + } + + if (STRCASEEQ (cmd, "cd")) + return cmd_cd (args); + else if (STRCASEEQ (cmd, "close") || STRCASEEQ (cmd, "unload")) + return cmd_close (args); + else if (STRCASEEQ (cmd, "ls")) + return cmd_ls (args); + else if (STRCASEEQ (cmd, "lsval")) + return cmd_lsval (args); + else { + fprintf (stderr, _("hivexsh: unknown command '%s', use 'help' for help summary\n"), + cmd); + return -1; + } +} + +static int +cmd_load (char *hivefile) +{ + if (STREQ (hivefile, "")) { + fprintf (stderr, _("hivexsh: load: no hive file name given to load\n")); + return -1; + } + + if (h) hivex_close (h); + h = NULL; + + free (loaded); + loaded = NULL; + + cwd = 0; + + h = hivex_open (hivefile, open_flags); + if (h == NULL) { + fprintf (stderr, + _( +"hivexsh: failed to open hive file: %s: %m\n" +"\n" +"If you think this file is a valid Windows binary hive file (_not_\n" +"a regedit *.reg file) then please run this command again using the\n" +"hivexsh option '-d' and attach the complete output _and_ the hive file\n" +"which fails into a bug report at https://bugzilla.redhat.com/\n" +"\n"), + hivefile); + return -1; + } + + /* Get the basename of the file for the prompt. */ + char *p = strrchr (hivefile, '/'); + if (p) + loaded = strdup (p+1); + else + loaded = strdup (hivefile); + if (!loaded) { + perror ("strdup"); + exit (EXIT_FAILURE); + } + + cwd = hivex_root (h); + + set_prompt_string (); + + return 0; +} + +static int +cmd_close (char *args) +{ + if (STRNEQ (args, "")) { + fprintf (stderr, _("hivexsh: '%s' command should not be given arguments\n"), + "close"); + return -1; + } + + if (h) hivex_close (h); + h = NULL; + + free (loaded); + loaded = NULL; + + cwd = 0; + + set_prompt_string (); + + return 0; +} + +static int +cmd_cd (char *path) +{ + if (STREQ (path, "")) { + print_node_path (cwd, stdout); + fputc ('\n', stdout); + return 0; + } + + if (path[0] == '\\' && path[1] == '\\') { + fprintf (stderr, _("%s: %s: \\ characters in path are doubled - are you escaping the path parameter correctly?\n"), "hivexsh", path); + return -1; + } + + hive_node_h new_cwd = cwd; + hive_node_h root = hivex_root (h); + + if (path[0] == '\\') { + new_cwd = root; + path++; + } + + while (path[0]) { + size_t len = strcspn (path, "\\"); + if (len == 0) { + path++; + continue; + } + + char *elem = path; + path = path[len] == '\0' ? &path[len] : &path[len+1]; + elem[len] = '\0'; + + if (len == 1 && STREQ (elem, ".")) + continue; + + if (len == 2 && STREQ (elem, "..")) { + if (new_cwd != root) + new_cwd = hivex_node_parent (h, new_cwd); + continue; + } + + new_cwd = hivex_node_get_child (h, new_cwd, elem); + if (new_cwd == 0) { + fprintf (stderr, _("hivexsh: cd: subkey '%s' not found\n"), + elem); + return -1; + } + } + + if (new_cwd != cwd) { + cwd = new_cwd; + set_prompt_string (); + } + + return 0; +} + +static int +cmd_help (char *args) +{ + printf (_( +"Navigate through the hive's keys using the 'cd' command, as if it\n" +"contained a filesystem, and use 'ls' to list the subkeys of the\n" +"current key. Full documentation is in the hivexsh(1) manual page.\n")); + + return 0; +} + +static int +cmd_ls (char *args) +{ + if (STRNEQ (args, "")) { + fprintf (stderr, _("hivexsh: '%s' command should not be given arguments\n"), + "ls"); + return -1; + } + + /* Get the subkeys. */ + hive_node_h *children = hivex_node_children (h, cwd); + if (children == NULL) { + perror ("ls"); + return -1; + } + + /* Get names for each subkey. */ + size_t len; + for (len = 0; children[len] != 0; ++len) + ; + + char **names = calloc (len, sizeof (char *)); + if (names == NULL) { + perror ("malloc"); + exit (1); + } + + int ret = -1; + size_t i; + for (i = 0; i < len; ++i) { + names[i] = hivex_node_name (h, children[i]); + if (names[i] == NULL) { + perror ("hivex_node_name"); + goto error; + } + } + + /* Sort the names. */ + sort_strings (names, len); + + for (i = 0; i < len; ++i) + printf ("%s\n", names[i]); + + ret = 0; + error: + free (children); + for (i = 0; i < len; ++i) + free (names[i]); + free (names); + return ret; +} + +static int +cmd_lsval (char *key) +{ + if (STRNEQ (key, "")) { + hive_value_h value; + + errno = 0; + if (STREQ (key, "@")) /* default key written as "@" */ + value = hivex_node_get_value (h, cwd, ""); + else + value = hivex_node_get_value (h, cwd, key); + + if (value == 0) { + if (errno) + goto error; + /* else key not found */ + fprintf (stderr, _("%s: %s: key not found\n"), "hivexsh", key); + return -1; + } + + /* Print the value. */ + hive_type t; + size_t len; + if (hivex_value_type (h, value, &t, &len) == -1) + goto error; + + switch (t) { + case hive_t_string: + case hive_t_expand_string: + case hive_t_link: { + char *str = hivex_value_string (h, value); + if (!str) + goto error; + + puts (str); /* note: this adds a single \n character */ + free (str); + break; + } + + case hive_t_dword: + case hive_t_dword_be: { + int32_t j = hivex_value_dword (h, value); + printf ("%" PRIi32 "\n", j); + break; + } + + case hive_t_qword: { + int64_t j = hivex_value_qword (h, value); + printf ("%" PRIi64 "\n", j); + break; + } + + case hive_t_multiple_strings: { + char **strs = hivex_value_multiple_strings (h, value); + if (!strs) + goto error; + size_t j; + for (j = 0; strs[j] != NULL; ++j) { + puts (strs[j]); + free (strs[j]); + } + free (strs); + break; + } + + case hive_t_none: + case hive_t_binary: + case hive_t_resource_list: + case hive_t_full_resource_description: + case hive_t_resource_requirements_list: + default: { + char *data = hivex_value_value (h, value, &t, &len); + if (!data) + goto error; + + if (fwrite (data, 1, len, stdout) != len) + goto error; + + free (data); + break; + } + } /* switch */ + } else { + /* No key specified, so print all keys in this node. We do this + * in a format which looks like the output of regedit, although + * this isn't a particularly useful format. + */ + hive_value_h *values; + + values = hivex_node_values (h, cwd); + if (values == NULL) + goto error; + + size_t i; + for (i = 0; values[i] != 0; ++i) { + char *key = hivex_value_key (h, values[i]); + if (!key) goto error; + + if (*key) { + putchar ('"'); + size_t j; + for (j = 0; key[j] != 0; ++j) { + if (key[j] == '"' || key[j] == '\\') + putchar ('\\'); + putchar (key[j]); + } + putchar ('"'); + } else + printf ("\"@\""); /* default key in regedit files */ + putchar ('='); + free (key); + + hive_type t; + size_t len; + if (hivex_value_type (h, values[i], &t, &len) == -1) + goto error; + + switch (t) { + case hive_t_string: + case hive_t_expand_string: + case hive_t_link: { + char *str = hivex_value_string (h, values[i]); + if (!str) + goto error; + + if (t != hive_t_string) + printf ("str(%d):", t); + putchar ('"'); + size_t j; + for (j = 0; str[j] != 0; ++j) { + if (str[j] == '"' || str[j] == '\\') + putchar ('\\'); + putchar (str[j]); + } + putchar ('"'); + free (str); + break; + } + + case hive_t_dword: + case hive_t_dword_be: { + int32_t j = hivex_value_dword (h, values[i]); + printf ("dword:%08" PRIx32 "\"", j); + break; + } + + case hive_t_qword: /* sic */ + case hive_t_none: + case hive_t_binary: + case hive_t_multiple_strings: + case hive_t_resource_list: + case hive_t_full_resource_description: + case hive_t_resource_requirements_list: + default: { + char *data = hivex_value_value (h, values[i], &t, &len); + if (!data) + goto error; + + printf ("hex(%d):", t); + size_t j; + for (j = 0; j < len; ++j) { + if (j > 0) + putchar (','); + printf ("%02x", data[j]); + } + break; + } + } /* switch */ + + putchar ('\n'); + } /* for */ + + free (values); + } + + return 0; + + error: + perror ("hivexsh: lsval"); + return -1; +} diff --git a/hivex/hivexsh.pod b/hivex/hivexsh.pod new file mode 100644 index 0000000..d13c70b --- /dev/null +++ b/hivex/hivexsh.pod @@ -0,0 +1,179 @@ +=encoding utf8 + +=head1 NAME + +hivexsh - Windows Registry hive shell + +=head1 SYNOPSIS + + hivexsh [-options] [hivefile] + +=head1 DESCRIPTION + +This program provides a simple shell for navigating Windows Registry +'hive' files. It uses the hivex library for access to these binary +files. + +Firstly you will need to provide a hive file from a Windows operating +system. The hive files are usually located in +C<C:\Windows\System32\Config> and have names like C<software>, +C<system> etc (without any file extension). For more information +about hive files, read L<hivex(3)>. For information about downloading +files from virtual machines, read L<virt-cat(1)> and L<guestfish(1)>. + +You can provide the name of the hive file to examine on the command +line. For example: + + hivexsh software + +Or you can start C<hivexsh> without any arguments, and immediately use +the C<load> command to load a hive: + + $ hivexsh + + Welcome to hivexsh, the hivex interactive shell for examining + Windows Registry binary hive files. + + Type: 'help' for help with commands + 'quit' to quit the shell + + > load software + software\> + +Navigate through the hive's keys using the C<cd> command, as if it +contained a filesystem, and use C<ls> to list the subkeys of the +current key. Other commands are listed below. + +=head1 OPTIONS + +=over 4 + +=item B<-d> + +Enable lots of debug messages. If you find a Registry file that this +program cannot parse, please enable this option and post the complete +output I<and> the Registry hive file in your bug report. + +=item B<-f> filename + +Read commands from C<filename> instead of stdin. To write a hivexsh +script, use: + + #!/usr/bin/hivexsh -f + +=back + +=head1 COMMANDS + +=over 4 + +=item B<cd> path + +Change to the subkey C<path>. Use Windows-style backslashes to +separate path elements, and start with a backslash in order to start +from the root of the hive. For example: + + cd \Classes\* + +moves from the root node, to the C<Classes> node, to the C<*> node. +If you were already at the root node, you could do this instead: + + cd Classes\* + +or even: + + cd Classes + cd * + +Path elements (node names) are matched case insensitively, and +characters like space, C<*>, and C<?> have I<no> special significance. + +C<..> may be used to go to the parent directory. + +=item B<close> | B<unload> + +Close the currently loaded hive. + +=item B<exit> | B<quit> + +Exit the shell. + +=item B<load> hivefile + +Load the binary hive named C<hivefile>. The currently loaded hive, if +any, is closed. The current directory is changed back to the root +node. + +=item B<ls> + +List the subkeys of the current hive Registry key. Note this command +does not take any arguments. + +=item B<lsval> [key] + +List the (key, value) pairs of the current hive Registry key. If no +argument is given then all pairs are displayed. If C<key> is given, +then the value of the named key is displayed. If C<@> is given, then +the value of the default key is displayed. + +=back + +=head1 EXAMPLE + + $ guestfish --ro -i Windows7 + ><fs> download win:c:\windows\system32\config\software software + ><fs> quit + + $ hivexsh software + + Welcome to hivexsh, the hivex interactive shell for examining + Windows Registry binary hive files. + + Type: 'help' for help with commands + 'quit' to quit the shell + + software\> ls + ATI Technologies + Classes + Clients + Intel + Microsoft + ODBC + Policies + RegisteredApplications + Sonic + Wow6432Node + software\> quit + +=head1 SEE ALSO + +L<hivex(3)>, +L<hivexget(1)>, +L<hivexml(1)>, +L<virt-win-reg(1)>, +L<guestfs(3)>, +L<http://libguestfs.org/>, +L<virt-cat(1)>, +L<virt-edit(1)>. + +=head1 AUTHORS + +Richard W.M. Jones (C<rjones at redhat dot com>) + +=head1 COPYRIGHT + +Copyright (C) 2009-2010 Red Hat Inc. + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License along +with this program; if not, write to the Free Software Foundation, Inc., +51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. diff --git a/po/POTFILES.in b/po/POTFILES.in index 85892e8..3ac88a0 100644 --- a/po/POTFILES.in +++ b/po/POTFILES.in @@ -83,6 +83,7 @@ fuse/guestmount.c hivex/hivex.c hivex/hivexget.c hivex/hivexml.c +hivex/hivexsh.c inspector/virt-inspector java/com_redhat_et_libguestfs_GuestFS.c ocaml/guestfs_c.c diff --git a/tools/virt-win-reg b/tools/virt-win-reg index e11ac34..8f248d7 100755 --- a/tools/virt-win-reg +++ b/tools/virt-win-reg @@ -277,6 +277,7 @@ for ($i = 0; $i < @ARGV; ++$i) { L<hivex(3)>, L<hivexget(1)>, +L<hivexsh(1)>, L<guestfs(3)>, L<guestfish(1)>, L<virt-cat(1)>, -- 1.6.5.2
Richard W.M. Jones
2010-Jan-29 19:22 UTC
[Libguestfs] [PATCH 2/2] hivex: Reimplement hivexget as a simple shell script.
-- Richard Jones, Virtualization Group, Red Hat http://people.redhat.com/~rjones New in Fedora 11: Fedora Windows cross-compiler. Compile Windows programs, test, and build Windows installers. Over 70 libraries supprt'd http://fedoraproject.org/wiki/MinGW http://www.annexia.org/fedora_mingw -------------- next part -------------->From 73c587ff0f2f8f76af39b16dafb0949be7bf0883 Mon Sep 17 00:00:00 2001From: Richard Jones <rjones at redhat.com> Date: Fri, 29 Jan 2010 19:12:34 +0000 Subject: [PATCH 2/2] hivex: Reimplement hivexget as a simple shell script. hivexget is currently a large C program. Now that we have hivexsh (the shell) we can reimplement hivexget as a simple bash script that calls out to hivexsh. --- .gitignore | 1 - hivex/Makefile.am | 12 +-- hivex/hivexget | 43 ++++++++ hivex/hivexget.c | 284 ----------------------------------------------------- 4 files changed, 45 insertions(+), 295 deletions(-) create mode 100755 hivex/hivexget delete mode 100644 hivex/hivexget.c diff --git a/.gitignore b/.gitignore index a80f1ec..49daccd 100644 --- a/.gitignore +++ b/.gitignore @@ -82,7 +82,6 @@ haskell/Guestfs.hs *.hi hivex/*.1 hivex/*.3 -hivex/hivexget hivex/hivexsh hivex/hivexml html/guestfish.1.html diff --git a/hivex/Makefile.am b/hivex/Makefile.am index 90fd716..ae7dbac 100644 --- a/hivex/Makefile.am +++ b/hivex/Makefile.am @@ -27,7 +27,8 @@ libhivex_la_LDFLAGS = -version-info 0:0:0 libhivex_la_CFLAGS = \ $(WARN_CFLAGS) $(WERROR_CFLAGS) -bin_PROGRAMS = hivexml hivexget hivexsh +bin_PROGRAMS = hivexml hivexsh +bin_SCRIPTS = hivexget hivexml_SOURCES = \ hivexml.c @@ -39,15 +40,6 @@ hivexml_CFLAGS = \ $(LIBXML2_CFLAGS) \ $(WARN_CFLAGS) $(WERROR_CFLAGS) -hivexget_SOURCES = \ - hivexget.c - -hivexget_LDADD = libhivex.la -hivexget_CFLAGS = \ - -I$(top_srcdir)/src \ - -DLOCALEBASEDIR=\""$(datadir)/locale"\" \ - $(WARN_CFLAGS) $(WERROR_CFLAGS) - hivexsh_SOURCES = \ hivexsh.c diff --git a/hivex/hivexget b/hivex/hivexget new file mode 100755 index 0000000..f804d0d --- /dev/null +++ b/hivex/hivexget @@ -0,0 +1,43 @@ +#!/bin/bash - +# Copyright (C) 2009-2010 Red Hat Inc. +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +set -e + +if [ $# -lt 2 -o $# -gt 3 ]; then + echo "hivexget hivefile path [key]" + exit 1 +fi + +if [ $# -eq 2 ]; then + hivexsh <<EOF +load $1 +cd $2 +lsval +exit +EOF +else + key=$3 + if [ "$key" = "" ]; then + key="@" + fi + hivexsh <<EOF +load $1 +cd $2 +lsval $key +exit +EOF +fi diff --git a/hivex/hivexget.c b/hivex/hivexget.c deleted file mode 100644 index 4f2419c..0000000 --- a/hivex/hivexget.c +++ /dev/null @@ -1,284 +0,0 @@ -/* hivexget - Get single subkeys or values from a hive. - * Copyright (C) 2009 Red Hat Inc. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - */ - -#include <config.h> - -#include <stdio.h> -#include <stdlib.h> -#include <string.h> -#include <stdint.h> -#include <inttypes.h> -#include <errno.h> - -#include "hivex.h" - -#ifdef HAVE_GETTEXT -#include "gettext.h" -#define _(str) dgettext(PACKAGE, (str)) -//#define N_(str) dgettext(PACKAGE, (str)) -#else -#define _(str) str -//#define N_(str) str -#endif - -enum { EXIT_NOT_FOUND = 2 }; - -int -main (int argc, char *argv[]) -{ - setlocale (LC_ALL, ""); - bindtextdomain (PACKAGE, LOCALEBASEDIR); - textdomain (PACKAGE); - - if (argc < 3 || argc > 4) { - fprintf (stderr, _("hivexget regfile path [key]\n")); - exit (EXIT_FAILURE); - } - - char *file = argv[1]; - char *path = argv[2]; - char *key = argv[3]; /* could be NULL */ - - if (path[0] != '\\') { - fprintf (stderr, _("hivexget: path must start with a \\ character\n")); - exit (EXIT_FAILURE); - } - if (path[1] == '\\') { - doubled: - fprintf (stderr, _("%s: %s: \\ characters in path are doubled - are you escaping the path parameter correctly?\n"), "hivexget", path); - exit (EXIT_FAILURE); - } - - hive_h *h = hivex_open (file, 0); - if (h == NULL) { - error: - perror (file); - exit (EXIT_FAILURE); - } - - /* Navigate to the desired node. */ - hive_node_h node = hivex_root (h); - if (!node) - goto error; - - char *p = path+1, *pnext; - size_t len; - while (*p) { - len = strcspn (p, "\\"); - - if (len == 0) - goto doubled; - - if (p[len] == '\\') { - p[len] = '\0'; - pnext = p + len + 1; - } else - pnext = p + len; - - errno = 0; - node = hivex_node_get_child (h, node, p); - if (node == 0) { - if (errno) - goto error; - /* else node not found */ - fprintf (stderr, _("hivexget: %s: %s: path element not found\n"), - path, p); - exit (EXIT_NOT_FOUND); - } - - p = pnext; - } - - /* Get the desired key, or print all keys. */ - if (key) { - hive_value_h value; - - errno = 0; - if (key[0] == '@' && key[1] == '\0') /* default key written as "@" */ - value = hivex_node_get_value (h, node, ""); - else - value = hivex_node_get_value (h, node, key); - - if (value == 0) { - if (errno) - goto error; - /* else key not found */ - fprintf (stderr, _("%s: %s: key not found\n"), "hivexget", key); - exit (EXIT_NOT_FOUND); - } - - /* Print the value. */ - hive_type t; - size_t len; - if (hivex_value_type (h, value, &t, &len) == -1) - goto error; - - switch (t) { - case hive_t_string: - case hive_t_expand_string: - case hive_t_link: { - char *str = hivex_value_string (h, value); - if (!str) - goto error; - - puts (str); /* note: this adds a single \n character */ - free (str); - break; - } - - case hive_t_dword: - case hive_t_dword_be: { - int32_t j = hivex_value_dword (h, value); - printf ("%" PRIi32 "\n", j); - break; - } - - case hive_t_qword: { - int64_t j = hivex_value_qword (h, value); - printf ("%" PRIi64 "\n", j); - break; - } - - case hive_t_multiple_strings: { - char **strs = hivex_value_multiple_strings (h, value); - if (!strs) - goto error; - size_t j; - for (j = 0; strs[j] != NULL; ++j) { - puts (strs[j]); - free (strs[j]); - } - free (strs); - break; - } - - case hive_t_none: - case hive_t_binary: - case hive_t_resource_list: - case hive_t_full_resource_description: - case hive_t_resource_requirements_list: - default: { - char *data = hivex_value_value (h, value, &t, &len); - if (!data) - goto error; - - if (fwrite (data, 1, len, stdout) != len) - goto error; - - free (data); - break; - } - } /* switch */ - } else { - /* No key specified, so print all keys in this node. We do this - * in a format which looks like the output of regedit, although - * this isn't a particularly useful format. - */ - hive_value_h *values; - - values = hivex_node_values (h, node); - if (values == NULL) - goto error; - - size_t i; - for (i = 0; values[i] != 0; ++i) { - char *key = hivex_value_key (h, values[i]); - if (!key) goto error; - - if (*key) { - putchar ('"'); - size_t j; - for (j = 0; key[j] != 0; ++j) { - if (key[j] == '"' || key[j] == '\\') - putchar ('\\'); - putchar (key[j]); - } - putchar ('"'); - } else - printf ("\"@\""); /* default key in regedit files */ - putchar ('='); - free (key); - - hive_type t; - size_t len; - if (hivex_value_type (h, values[i], &t, &len) == -1) - goto error; - - switch (t) { - case hive_t_string: - case hive_t_expand_string: - case hive_t_link: { - char *str = hivex_value_string (h, values[i]); - if (!str) - goto error; - - if (t != hive_t_string) - printf ("str(%d):", t); - putchar ('"'); - size_t j; - for (j = 0; str[j] != 0; ++j) { - if (str[j] == '"' || str[j] == '\\') - putchar ('\\'); - putchar (str[j]); - } - putchar ('"'); - free (str); - break; - } - - case hive_t_dword: - case hive_t_dword_be: { - int32_t j = hivex_value_dword (h, values[i]); - printf ("dword:%08" PRIx32 "\"", j); - break; - } - - case hive_t_qword: /* sic */ - case hive_t_none: - case hive_t_binary: - case hive_t_multiple_strings: - case hive_t_resource_list: - case hive_t_full_resource_description: - case hive_t_resource_requirements_list: - default: { - char *data = hivex_value_value (h, values[i], &t, &len); - if (!data) - goto error; - - printf ("hex(%d):", t); - size_t j; - for (j = 0; j < len; ++j) { - if (j > 0) - putchar (','); - printf ("%02x", data[j]); - } - break; - } - } /* switch */ - - putchar ('\n'); - } /* for */ - - free (values); - } - - if (hivex_close (h) == -1) - goto error; - - exit (EXIT_SUCCESS); -} -- 1.6.5.2
Apparently Analagous Threads
- [PATCH 2/2] hivex: Make sure that pod2html finds the POD file when building out-of-tree
- hivex: pod2html complaints
- Hivex bug? Cannot access Windows 2003 x64 Software\Classes key
- [hivex PATCH 2/2] build: do not ignore pod2man error codes
- hivex: some issues (key encoding, ...) and suggested fixes