Click here to Skip to main content
15,889,838 members
Articles / Programming Languages / Shell

Process Kill

Rate me:
Please Sign up or sign in to vote.
4.86/5 (6 votes)
12 Mar 2012CPOL7 min read 51.6K   2.6K   22  
Describes some of the ways to kill a runaway process or malware
///////////////////////////////////////////////////////////////////////////////
//
//  Random.cs
//
//  By Philip R. Braica (HoshiKata@aol.com, VeryMadSci@gmail.com)
//
//  Distributed under the The Code Project Open License (CPOL)
//  http://www.codeproject.com/info/cpol10.aspx
///////////////////////////////////////////////////////////////////////////////

// Using.
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Diagnostics;
using System.Runtime.InteropServices;

// Namespace
namespace KillForms
{
    /// <summary>
    /// Just a simple form.
    /// </summary>
    public partial class Form1 : Form
    {
        /// <summary>
        /// Allow us to make a critical process non-critical.
        /// </summary>
        /// <param name="bNew"></param>
        /// <param name="pbOld"></param>
        /// <param name="bNeedScb"></param>
        /// <returns></returns>
        [DllImport("ntdll.dll", SetLastError = true)]
        unsafe public static extern int RtlSetProcessIsCritical(bool bNew, out bool pbOld, bool bNeedScb);

        /// <summary>
        /// You can use the unsupported thing to set the criticalness of another application.
        /// </summary>
        /// <param name="hProcess"></param>
        /// <param name="processInformationClass"></param>
        /// <param name="processInformation"></param>
        /// <param name="processInformationLength"></param>
        /// <returns></returns>
        [DllImport("ntdll.dll", SetLastError = true)]
        private static extern int NtSetInformationProcess(IntPtr hProcess, int processInformationClass, ref int processInformation, int processInformationLength);


        /// <summary>
        /// Open a thread.
        /// </summary>
        /// <param name="dwDesiredAccess"></param>
        /// <param name="bInheritHandle"></param>
        /// <param name="dwThreadId"></param>
        /// <returns></returns>
        [System.Runtime.InteropServices.DllImport("kernel32.dll")]
        static extern IntPtr OpenThread(uint dwDesiredAccess, bool bInheritHandle, uint dwThreadId);

        /// <summary>
        /// Kill the thread.
        /// </summary>
        /// <param name="hThread"></param>
        /// <param name="dwExitCode"></param>
        /// <returns></returns>
        [System.Runtime.InteropServices.DllImport("kernel32.dll")]
        static extern bool TerminateThread(IntPtr hThread, uint dwExitCode);

        /// <summary>
        /// Win32 call to kill.
        /// </summary>
        /// <param name="hProcess"></param>
        /// <param name="uExitCode"></param>
        /// <returns></returns>
        [DllImport("kernel32.dll", SetLastError = true)]
        [return: MarshalAs(UnmanagedType.Bool)]
        static extern bool TerminateProcess(IntPtr hProcess, uint uExitCode);

 
        /// <summary>
        /// Suspend a thread.
        /// </summary>
        /// <param name="hThread"></param>
        /// <returns></returns>
        [DllImport("kernel32.dll")]
        static extern uint SuspendThread(IntPtr hThread);

        /// <summary>
        /// Resume a thread.
        /// </summary>
        /// <param name="hThread"></param>
        /// <returns></returns>
        [DllImport("kernel32.dll")]
        static extern int ResumeThread(IntPtr hThread);


        /// <summary>
        /// Constructor.
        /// </summary>
        public Form1()
        {
            InitializeComponent();
        }

        /// <summary>
        /// Nuke the problem things.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void button1_Click(object sender, EventArgs e)
        {
            // Grab the value now not while executing.
            bool doAffinity = checkBox1.Checked;
            bool doPriority = checkBox2.Checked;
            bool usePath = checkBox3.Checked;
            bool atAllCosts = checkBox4.Checked;
            bool hibernateOnly = checkBox5.Checked;
            atAllCosts = hibernateOnly ? false : atAllCosts;
            int iterations = (int)numericUpDown1.Value;

            // Make a list of processes to kill.
            List<string> namesToDealWith = new List<string>();
            TextBox[] tb = { textBox1, textBox2, textBox3, textBox4 };
            for (int i = 0; i < tb.Length; i++)
            {
                if (tb[i].Text.Length == 0) continue;
                namesToDealWith.Add(tb[i].Text);
            }
            if (tb.Length == 0)
            {
                return;
            }

            // If the at all costs option is checked, make darn sure they mean it.
            if (atAllCosts)
            {
                DialogResult dr = MessageBox.Show(
                    "Are you sure?\r\nUsing at \"at all costs\" may lockup the PC, " + 
                    "rebooting may be a better option, only use if fighting off " +
                    "malware and there is no other choice. " + 
                    "\r\n\r\nCheck MSDN on " + 
                    "TerminateThread and for using NtSetInformationProcess " + 
                    "to make a process non-critical for more information." + 
                    "\r\n\r\nThis may crash your PC! At your own risk!",
                    "Just checking ...",
                    MessageBoxButtons.OKCancel,
                    MessageBoxIcon.Warning,
                    MessageBoxDefaultButton.Button2);
                if (dr == DialogResult.Cancel)
                {
                    return;
                }
            }


            // This give us some privlidges and some protection while 
            // making some of these calls.
            if (atAllCosts)
            {
                Process.EnterDebugMode();
            }

            // Iterate many times to make sure the process stays killed.
            for (int i = 0; i < iterations; i++)
            {
                // Get a list of processes.
                Process[] p = Process.GetProcesses();
                if (p == null) continue;

                for (int j = 0; j < p.Length; j++)
                {
                    // If null just continue.
                    if (p[j] == null) continue;

                    // See if p[j] is in the list.
                    // 
                    // Sometimes things have a unique name, sometimes 
                    // they borrow a common name to hide, sometimes they 
                    // have a MainModule, sometimes they don't, some have 
                    // file paths that use resource forks or non-text 
                    // enterable characters. For this reason they can use
                    // either the common name or the full path to executable.
                    string name = p[j].ProcessName;
                    if (usePath)
                    {
                        try
                        {
                            name = p[j].MainModule.FileName;
                        }
                        catch (Exception)
                        {
                            // don't care.
                        }
                    }
                    if (namesToDealWith.Contains(name) == false) continue;

                    // Affinity.
                    if (doAffinity)
                    {
                        try
                        {
                            p[j].ProcessorAffinity = (System.IntPtr)1;
                        }
                        catch (Exception)
                        {
                            // Don't care keep moving.
                        }
                    }

                    // Priority.
                    if (doPriority)
                    {
                        try
                        {
                            p[j].PriorityClass = ProcessPriorityClass.Idle;
                        }
                        catch (Exception)
                        {
                            // Don't care keep moving.
                        }
                        try
                        {
                            p[j].PriorityBoostEnabled = false;
                        }
                        catch (Exception)
                        {
                            // Don't care keep moving.
                        }
                    }

                    if (hibernateOnly)
                    {
                        // Only hibernate, method 3.
                        stopProcess(p[j], 3, false);
                    }
                    else
                    {
                        // Try the ith set of methods on this process.
                        stopProcessAllMethods(p[j], i, atAllCosts);
                    }
                }
            }
        }

        /// <summary>
        /// Try to kill based on the ith starting point attempt.
        /// </summary>
        /// <param name="p"></param>
        /// <param name="seed"></param>
        /// <param name="atAllCosts"></param>
        private void stopProcessAllMethods(Process p, int seed, bool atAllCosts)
        {
            // Try one stop method, then return, get process list again.
            //
            // Otherwise if we just do multiples against a process that doesn't exist,
            // under windows XP, the process may survive due to the efforts taken.
            // There are four normal methods and two "at all costs" methods.

            if (stopProcess(p, seed++, atAllCosts)) return;
            if (stopProcess(p, seed++, atAllCosts)) return;
            if (stopProcess(p, seed++, atAllCosts)) return;
            if (stopProcess(p, seed++, atAllCosts)) return;
            
            if (atAllCosts)
            {
                if (stopProcess(p, seed++, atAllCosts)) return;
                stopProcess(p, seed, atAllCosts);
            }
        }

        /// <summary>
        /// Stop a process p, with either 3 methods or 4 methods.
        /// </summary>
        /// <param name="p"></param>
        /// <param name="method">Executes method % (total allowed)</param>
        /// <param name="atAllCosts">Allows the fourth method.</param>
        /// <returns>True if likely killed the process.</returns>
        private bool stopProcess(Process p, int method, bool atAllCosts)
        {            
            // Limit to 0-4 if all costs, else 0-3.
            method = atAllCosts? method % 4 : method % 6;
            try
            {
                if (method == 0) p.CloseMainWindow();  // Close main window.
                if (method == 1) p.Kill();  // Kill.
                if (method == 2) TerminateProcess(p.Handle, 0); // Terminate win32.

                if (method == 3)
                {
                    // Suspend all it's threads, for fighting mal ware this may be 
                    // enough to make it killable.
                    for (int i = 0; i < p.Threads.Count; i++)
                    {
                        ProcessThread pt = p.Threads[i];
                        try
                        {
                            IntPtr ptrThread = OpenThread(2, false, (uint)pt.Id);
                            if (System.Threading.Thread.CurrentThread.ManagedThreadId != pt.Id)
                            {
                                SuspendThread(ptrThread);
                            }
                        }
                        catch (Exception ex)
                        {
                            Console.WriteLine(ex.Message);
                            return false;
                        }
                    }                    
                }

                // Extreme methods.
                if (!atAllCosts) return false;


                if (method == 4)
                {
                    int enable = 0; // disable, set to 1 to enable
                    NtSetInformationProcess(p.Handle, 29, ref enable, sizeof(int));
                    p.Kill();
                }
                if (method == 5)
                {
                    for (int i = 0; i < p.Threads.Count; i++)
                    {
                        ProcessThread pt = p.Threads[i];
                        try
                        {
                            IntPtr ptrThread = OpenThread(1, false, (uint)pt.Id);
                            if (System.Threading.Thread.CurrentThread.ManagedThreadId != pt.Id)
                            {
                                TerminateThread(ptrThread, 1);
                            }
                        }
                        catch (Exception ex)
                        {
                            Console.WriteLine(ex.Message);
                            return false;
                        }
                    }
                }
            }
            catch (Exception ex)
            {
                // Don't care keep moving.
                Console.WriteLine(ex.Message);
                return false;
            }
            return true;
        }
    }
}

By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Technical Lead
United States United States
Phil is a Principal Software developer focusing on weird yet practical algorithms that run the gamut of embedded and desktop (PID loops, Kalman filters, FFTs, client-server SOAP bindings, ASIC design, communication protocols, game engines, robotics).

In his personal life he is a part time mad scientist, full time dad, and studies small circle jujitsu, plays guitar and piano.

Comments and Discussions