#  Kill.pl
#  Example: 7.18
#  ----------------------------------------
#  From "Win32 Perl Scripting: Administrators Handbook" by Dave Roth
#  Published by New Riders Publishing.
#  ISBN # 1-57870-215-1
#
#  This script illustrates how Perl's kill() function may not be enough to
#  end some processes.  This script uses an alternative technique to
#  end a process.  This technique works for almost all processes; the exceptions
#  are those that are secured by the system.

use Win32::API;

# Inform each process to return this value to it's parent
$PROC_TERMINATION_VALUE = 0;

if( 0 == scalar @ARGV )
{
    Syntax();
    exit;
}
Configure();
foreach my $Pid ( @ARGV )
{
    my $iResult = 0;
    
    # If the user passed in text values skip it.
    next unless( $Pid =~ /^\d+$/ );

    if( !( $iResult = kill( $PROC_TERMINATION_VALUE, $Pid ) ) )
    {
        $iResult = ForceKill( $Pid );
    }
    
    if( $iResult )
    {
        print "Process $Pid was successfully killed.\n";
    }
    else
    {
        my $Error = Win32::FormatMessage( Win32::GetLastError() );
        $Error =~ s/\r|\n//g;
        print "Process $Pid failed to terminate. ($Error)\n";
    }
}

sub ForceKill
{
    my( $Pid ) = @_;
    my $iResult = 0;
    my $phToken = pack( "L", 0 );
    # Fetch the process's token
    if( $OpenProcessToken->Call( $GetCurrentProcess->Call(), $TOKEN_ADJUST_PRIVILEGES | $TOKEN_QUERY, $phToken ) )
    {
        my $hToken = unpack( "L", $phToken );
        # Set the debug privilege on the token
        if( SetPrivilege( $hToken, $SE_DEBUG_NAME, 1 ) )
        {
            # Now that we have debug privileges on the process
            # open the process so we can mess with it.
            my $hProcess = $OpenProcess->Call( $PROCESS_TERMINATE, 0, $Pid );
            if( $hProcess )
            {
                # We no longer need the debug privilege since we have opened
                # the process so remove the privilege.
                SetPrivilege( $hToken, $SE_DEBUG_NAME, 0 );
                # Let's termiante the process
                $iResult = $TerminateProcess->Call( $hProcess, 0 );
                $CloseHandle->Call( $hProcess );    
            }
        }
        $CloseHandle->Call( $hToken );
    }
    return( $iResult );
}     

sub SetPrivilege
{
    my( $hToken, $pszPriv, $bSetFlag ) = @_;
    my $pLuid = pack( "Ll", 0, 0 );
    # Lookup the LIUD of the privilege
    if( $LookupPrivilegeValue->Call( "\x00\x00", $pszPriv, $pLuid ) )
    {
        # Unpack the LUID
        my $pPrivStruct = pack( "LLlL", 1, unpack( "Ll", $pLuid ), ( ( $bSetFlag )? $SE_PRIVILEGE_ENABLED : 0 ) );
        # Now modify the process's token to set the required privilege
        $iResult = ( 0 != $AdjustTokenPrivileges->Call( $hToken, 0,$pPrivStruct, length( $pPrivStruct ), 0, 0 ) );
    }
        return( $iResult );
}

sub Configure
{
    $TOKEN_QUERY                = 0x0008;
    $TOKEN_ADJUST_PRIVILEGES    = 0x0020;
    $SE_PRIVILEGE_ENABLED        = 0x02;
    $PROCESS_TERMINATE         = 0x0001;
    $SE_DEBUG_NAME             = "SeDebugPrivilege";

    # Prepare to use some specialized Win32 API calls
    $GetCurrentProcess = new Win32::API( 'Kernel32.dll', 'GetCurrentProcess', [], N ) || die;
    $OpenProcessToken = new Win32::API( 'AdvApi32.dll', 'OpenProcessToken', [N,N,P], I ) || die;
    $LookupPrivilegeValue = new Win32::API( 'AdvApi32.dll', 'LookupPrivilegeValue', [P,P,P], I ) || die;
    $AdjustTokenPrivileges = new Win32::API( 'AdvApi32.dll', 'AdjustTokenPrivileges', [N,I,P,N,P,P], I ) || die;
    $OpenProcess = new Win32::API( 'Kernel32.dll', 'OpenProcess', [N,I,N], N ) || die;
    $TerminateProcess = new Win32::API( 'Kernel32.dll', 'TerminateProcess', [N,I], I ) || die;
    $CloseHandle = new Win32::API( 'Kernel32.dll', 'CloseHandle', [N], I ) || die;
}

sub Syntax()
{
    my( $Script ) = ( $0 =~ /([^\\]*?)$/ );
    my $Line = "-" x length( $Script );
    print <<EOT;

$Script
$Line
    Syntax: 
    $Script Pid [ Pid2 [ Pid3 [ ... ] ] ]
EOT
}

