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";
}
}
}

Cool! You should add some Perl examples to http://libguestfs.org/ 🙂
There’s a lot of real Perl code using the API, eg. here and here.