Tip: List services in a Windows guest

The lengthy program after the fold uses libguestfs and hivex to list out the services from a Windows guest. You point it to a Windows guest and it will produce a rather long list, like this:

# ./services.pl WindowsGuest
[...]
SysMain:
  Path: %systemroot%\system32\svchost.exe -k LocalSystemNetworkRestricted
  Start flag: autoload
  Service type: Win32 service using svchost
  Error control: ignore
TabletInputService:
  Path: %SystemRoot%\System32\svchost.exe -k LocalSystemNetworkRestricted
  Group: PlugPlay
  Start flag: load on demand
  Service type: Win32 service using svchost
  Error control: normal
TapiSrv:
  Path: %SystemRoot%\System32\svchost.exe -k NetworkService
  Start flag: load on demand
  Service type: Win32 service using svchost
  Error control: normal
TBS:
  Path: %SystemRoot%\System32\svchost.exe -k LocalServiceAndNoImpersonation
  Start flag: load on demand
  Service type: Win32 service using svchost
  Error control: normal
Tcpip:
  Path: System32\drivers\tcpip.sys
  Group: PNP_TDI
  Start flag: boot loader
  Service type: kernel device driver
  Error control: normal
TCPIP6 (Microsoft IPv6 Protocol Driver):
  Microsoft IPv6 Protocol Driver
  Path: system32\DRIVERS\tcpip.sys
  Start flag: load on demand
  Service type: kernel device driver
  Error control: normal
tcpipreg (TCP/IP Registry Compatibility):
  Provides compatibility for legacy applications which interact with TCP/IP through the registry. If this service is stopped, certain applications may have impaired functionality.
  Path: System32\drivers\tcpipreg.sys
  Start flag: autoload
  Service type: kernel device driver
  Error control: normal
[...]

#!/usr/bin/perl -w
use strict;
use Sys::Guestfs;
use Win::Hivex;
use File::Temp qw(tempdir);

# Open the guest disk image.
my $guest = $ARGV[0] or die "Usage: $0 guestname\n";
my $g = Sys::Guestfs->new ();
#$g->set_trace (1);
$g->add_domain ($guest, readonly => 1);
$g->launch ();

# Look for a Windows operating system.
my @roots = $g->inspect_os ();
if (@roots == 0) {
    die "$0: $guest: no operating system found in the guest disk image\n"
}
my $root = $roots[0];
if ($g->inspect_get_type ($root) ne "windows") {
    die "$0: $guest: not a Windows guest\n"
}
$g->mount_ro ($root, "/");

# Download the HKLM\SYSTEM registry (hive file).
my $systemroot = $g->inspect_get_windows_systemroot ($root);
my $path = "$systemroot/system32/config/system";
$path = $g->case_sensitive_path ($path);
my $tmpdir = tempdir (CLEANUP => 1);
$g->download ($path, "$tmpdir/system");

# Use hivex to pull out registry keys of interest.
my $h = Win::Hivex->open ("$tmpdir/system");
my $root_node = $h->root ();

# Firstly get HKLM\SYSTEM\Select so we know which
# ControlSetNNN is in use.
my ($node, $val);
$node = $h->node_get_child ($root_node, "Select");
$val = $h->node_get_value ($node, "Current");
my $cset = sprintf ("ControlSet%03d", $h->value_dword ($val));

# Then get HKLM\SYSTEM\$cset\Services
$node = $h->node_get_child ($root_node, $cset);
my $services_node = $h->node_get_child ($node, "Services");

# Get all the child nodes of HKLM\SYSTEM\$cset\Services
# This is the list of services.
my @nodes = $h->node_children ($services_node);

# Iterate through the services, printing name and details.
# http://support.microsoft.com/kb/103000
foreach $node (@nodes) {
    my $name = $h->node_name ($node);

    my $start;
    eval {
        $val = $h->node_get_value ($node, "Start");
        $start = $h->value_dword ($val);
    };

    # Ignore registry keys which don't have "Start".
    if (defined $start) {
        my ($display_name, $description, $group,
            $tag, $type, $error_control, $image_path);

        eval {
            $val = $h->node_get_value ($node, "DisplayName");
            $display_name = $h->value_string ($val);
            # If the first character of DisplayName is
            # '@' then it seems to mean "refer to this
            # other DLL file for the name".
            if (substr ($display_name, 0, 1) eq '@') {
                $display_name = undef;
            }
        };
        eval {
            $val = $h->node_get_value ($node, "Description");
            $description = $h->value_string ($val);
            # Uses @ like DisplayName above.
            if (substr ($description, 0, 1) eq '@') {
                $description = undef;
            } else {
                chomp $description;
            }
        };
        eval {
            $val = $h->node_get_value ($node, "Group");
            $group = $h->value_string ($val);
        };
        eval {
            $val = $h->node_get_value ($node, "Tag");
            $tag = $h->value_dword ($val);
        };
        eval {
            $val = $h->node_get_value ($node, "Type");
            $type = $h->value_dword ($val);
        };
        eval {
            $val = $h->node_get_value ($node, "ErrorControl");
            $error_control = $h->value_dword ($val);
        };
        eval {
            $val = $h->node_get_value ($node, "ImagePath");
            $image_path = $h->value_string ($val);
        };

        print "$name";
        print " ($display_name)" if defined $display_name;
        print ":\n";

        print "  $description\n" if defined $description;
        print "  Path: $image_path\n" if defined $image_path;
        print "  Group: $group\n" if defined $group;
        print "  Start flag: ";
        if ($start == 0x0) {
            print "boot loader";
        } elsif ($start == 0x1) {
            print "I/O subsystem";
        } elsif ($start == 0x2) {
            print "autoload";
        } elsif ($start == 0x3) {
            print "load on demand";
        } elsif ($start == 0x4) {
            print "disabled";
        } else {
            printf "unknown (0x%x)", $start
        }
        print "\n";
        if (defined $type) {
            print "  Service type: ";
            if ($type == 0x1) {
                print "kernel device driver";
            } elsif ($type == 0x2) {
                print "filesystem driver";
            } elsif ($type == 0x4) {
                print "adapter arguments";
            } elsif ($type == 0x10) {
                print "Win32 service";
            } elsif ($type == 0x20) {
                print "Win32 service using svchost";
            } else {
                printf "unknown (0x%x)", $type
            }
            print "\n";
        }
        if (defined $error_control) {
            print "  Error control: ";
            if ($error_control == 0x0) {
                print "ignore";
            } elsif ($error_control == 0x1) {
                print "normal";
            } elsif ($error_control == 0x2) {
                print "severe";
            } elsif ($error_control == 0x3) {
                print "critical";
            } else {
                printf "unknown (0x%x)", $error_control;
            }
            print "\n";
        }
    }
}

2 Comments

Filed under Uncategorized

2 responses to “Tip: List services in a Windows guest

  1. anonymous

    Cool! You should add some Perl examples to http://libguestfs.org/ 🙂

Leave a comment

This site uses Akismet to reduce spam. Learn how your comment data is processed.