Listing 1: Excerpts from DumpStartups.pl use Getopt::Long; use Win32::API::Prototype; use Win32::TieRegistry; use Win32::Shortcut; use Win32::AdminMisc; use strict; use vars qw( %HIVES %PATHS %Config $Value $EXECUTABLE_EXTENSIONS $CSIDL_STARTUP $CSIDL_COMMON_STARTUP $SHGFP_TYPE_CURRENT ); %HIVES = ( CUser => "HKCU", LMachine => "HKLM", Drive => "Drive", ); BEGIN CALLOUT A ApiLink( "shell32", "HRESULT SHGetFolderPath( HWND hwndOwner, int nFolder, HANDLE hToken, DWORD dwFlags, LPTSTR pszPath )" ) || die; $CSIDL_STARTUP = 0x0007; # Start Menu\Programs\Startup $CSIDL_COMMON_STARTUP = 0x0018; # Users\Startup $SHGFP_TYPE_CURRENT = 1; # Current value (not default) END CALLOUT A BEGIN CALLOUT B BEGIN COMMENT LINE # If a PATHEXT environment variable doesn't exist, create one. END COMMENT LINE $ENV{PATHEXT} = ".cmd;.com;.bat;.vbs;" unless( "" ne $ENV{PATHEXT} ); $EXECUTABLE_EXTENSIONS = join( "|", ( split( ";", $ENV{PATHEXT} . ";.pif;.lnk" ) ) ); END CALLOUT B . . . BEGIN CALLOUT C foreach my $Path ( @{$PATHS{registry}} ) { push( @RunValues, ProcessKey( $Path ) ); } foreach my $Path ( @{$PATHS{file}} ) { push( @RunValues, ProcessDir( $Path ) ); } END CALLOUT C $~ = "DumpHeader"; write; $~ = "DumpData"; BEGIN CALLOUT D if( scalar @{$Config{remove}} ) { foreach my $Index ( @{$Config{remove}} ) { local $Value; $Value = $RunValues[ $Index ]; $Value->{_index} = $Index; Remove( $Value ); BEGIN COMMENT LINE # Write after calling Remove(). The write format might delete # some of the $Value hash reference members. END COMMENT LINE write; } } else { my $TotalIndex = scalar @RunValues; for( my $Index = 0; $Index < $TotalIndex; $Index++ ) { local $Value; $Value = $RunValues[ $Index ]; $Value->{_index} = $Index; write; } } END CALLOUT D sub ProcessKey { my( $Path ) = @_; my @ValueList = (); BEGIN CALLOUT E my( $Location ) = ( $Path =~ /\/([^\/]+)\// );\ BEGIN COMMENT LINE # Use the MAXIMUM_ALLOWED permission to open the registry subkey. END COMMENT LINE if( my $Key = $Registry->Open( $Path, {Access => 0x2000000} )) { foreach my $ValueName ( $Key->ValueNames() ) { # Assume all of these values are strings... my $Data = $Key->{ "/$ValueName" }; push( @ValueList, { _name => $ValueName, _path => $Path, _data => $Data, _location => $Location } ); } } return( @ValueList ); END CALLOUT E } sub ProcessDir { my( $Dir ) = @_; my @ValueList = (); my @DirList = (); if( opendir( STARTUP_DIR, $Dir ) ) { while( my $File = readdir( STARTUP_DIR ) ) { my $Path = "$Dir/$File"; next if( "." eq $File || ".." eq $File ); if( -d $Path ) { push( @DirList, $Path ); next; } if( $File =~ /\.lnk$/i ) { BEGIN CALLOUT F # Process Shortcut... my $Shortcut = new Win32::Shortcut( $Path ); my $Name = $Shortcut->{Description}; if( "" eq $Name ) { my %FileInfo; if( Win32::AdminMisc::GetFileInfo( $Shortcut->{Path}, \%FileInfo ) ) { $Name = $FileInfo{FileDescription} || $File; } } push( @ValueList, { _name => $Name, _path => $Path, _data => "\"$Shortcut->{Path}\" $Shortcut- >{Arguments}", _location => "Drive" } ); END CALLOUT F } elsif( $File =~ /($EXECUTABLE_EXTENSIONS)$/i ) { BEGIN CALLOUT G # Process other files... my $Name = $File; my %FileInfo; if( Win32::AdminMisc::GetFileInfo( $Path, \%FileInfo ) ) { $Name = $FileInfo{FileDescription} if( "" ne $FileInfo{FileDescription} ); } push( @ValueList, { _name => $Name, _path => $Path, _data => $Path, _location => "Drive" } ); } END CALLOUT G } closedir( STARTUP_DIR ); foreach my $Path ( @DirList ) { push( @ValueList, ProcessDir( $Path ) ); } } return( @ValueList ); } . . . BEGIN CALLOUT H if( "drive" eq lc $Item->{_location} ) { BEGIN COMMENT LINE # We have a location on a drive. END COMMENT LINE print "Deleting file: '$Item->{_path}'\n"; $Result = unlink( $Item->{_path} ); } else { BEGIN COMMENT LINE # We have a registry subkey. END COMMENT LINE my $Path = "$Item->{_path}/$Item->{_name}"; $Result = delete $Registry->{$Path}; print "Deleting reg key value: '$Path'\n"; } END CALLOUT H if( $Result ) { print "REMOVED!\n"; } else { print "FAILED to remove! Error: " . Win32::FormatMessage( Win32::GetLastError() ); } } else { print " WILL NOT REMOVE $Item->{_name}!\n"; } } BEGIN CALLOUT I sub GetSpecialDirectory { my( $FolderType ) = @_; my $pszPath = NewString( 1024 ); if( 0 == SHGetFolderPath( undef, $FolderType, undef, $SHGFP_TYPE_CURRENT, $pszPath ) ) { $pszPath =~ s/\x00//g; return( $pszPath ); } return( undef ); } END CALLOUT I