/*
* Process Hacker -
* static variables and user interface thread management
*
* Copyright (C) 2008-2009 wj32
*
* This file is part of Process Hacker.
*
* Process Hacker is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Process Hacker is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Process Hacker. If not, see .
*/
using System;
using System.Collections.Generic;
using System.Threading;
using System.Windows.Forms;
using System.Drawing;
using System.Security.Principal;
using Microsoft.Samples;
namespace ProcessHacker
{
public enum WindowsVersion
{
Unknown,
XP,
Vista
}
public static class Program
{
///
/// The main Process Hacker window instance
///
public static HackerWindow HackerWindow;
public static WindowsVersion WindowsVersion = WindowsVersion.Unknown;
public static Win32.PROCESS_RIGHTS MinProcessQueryRights = Win32.PROCESS_RIGHTS.PROCESS_QUERY_INFORMATION;
public static Win32.PROCESS_RIGHTS MinProcessReadMemoryRights = Win32.PROCESS_RIGHTS.PROCESS_VM_READ;
public static Win32.PROCESS_RIGHTS MinProcessWriteMemoryRights = Win32.PROCESS_RIGHTS.PROCESS_VM_WRITE | Win32.PROCESS_RIGHTS.PROCESS_VM_OPERATION;
public static Win32.THREAD_RIGHTS MinThreadQueryRights = Win32.THREAD_RIGHTS.THREAD_QUERY_INFORMATION;
public static int CurrentProcess;
public static int CurrentSessionId;
public static string CurrentUsername;
///
/// The Results Window ID Generator
///
public static IdGenerator ResultsIds = new IdGenerator();
public static Dictionary Structs = new Dictionary();
public const bool MemoryEditorsThreaded = true;
public static Dictionary MemoryEditors = new Dictionary();
public static Dictionary MemoryEditorsThreads = new Dictionary();
public const bool ResultsWindowsThreaded = true;
public static Dictionary ResultsWindows = new Dictionary();
public static Dictionary ResultsThreads = new Dictionary();
public const bool PEWindowsThreaded = false;
public static Dictionary PEWindows = new Dictionary();
public static Dictionary PEThreads = new Dictionary();
public const bool PWindowsThreaded = true;
public static Dictionary PWindows = new Dictionary();
public static Dictionary PThreads = new Dictionary();
public delegate void ResultsWindowInvokeAction(ResultsWindow f);
public delegate void MemoryEditorInvokeAction(MemoryEditor f);
public delegate void ThreadWindowInvokeAction(ThreadWindow f);
public delegate void PEWindowInvokeAction(PEWindow f);
public delegate void PWindowInvokeAction(ProcessWindow f);
public delegate void UpdateWindowAction(Form f);
public static System.Collections.Specialized.StringCollection ImposterNames =
new System.Collections.Specialized.StringCollection();
public static bool Aggressive = false;
public static bool StartHidden = false;
public static bool StartVisible = false;
public static bool ShowOptions = false;
public static string SelectTab = "Processes";
public static Win32.TOKEN_ELEVATION_TYPE ElevationType;
public static KProcessHacker KPH;
public static SharedThreadProvider SharedThreadProvider;
public static SharedThreadProvider SecondarySharedThreadProvider;
private static object CollectWorkerThreadsLock = new object();
///
/// The main entry point for the application.
///
[STAThread]
public static void Main(string[] args)
{
Dictionary pArgs = null;
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
try
{
pArgs = ParseArgs(args);
}
catch
{
MessageBox.Show(
"Usage: processhacker [-m]\n" +
"\t-m\tStarts Process Hacker hidden.\n" +
"\t-v\tStarts Process Hacker visible.\n" +
"\t-a\tAggressive mode.\n" +
"\t-o\tShows Options.\n" +
"\t-t n\tShows the specified tab. 0 is Processes, and 1 is Services.",
"Process Hacker", MessageBoxButtons.OK, MessageBoxIcon.Information);
pArgs = new Dictionary();
}
// In case the settings file is corrupt PH won't crash here - it will be dealt with later.
try
{
if (Properties.Settings.Default.AllowOnlyOneInstance && !pArgs.ContainsKey("-e"))
CheckForPreviousInstance();
}
catch
{ }
// Try to upgrade settings
try
{
if (Properties.Settings.Default.NeedsUpgrade)
{
try
{
Properties.Settings.Default.Upgrade();
}
catch
{
MessageBox.Show("Process Hacker could not upgrade its settings from a previous version.", "Process Hacker",
MessageBoxButtons.OK, MessageBoxIcon.Warning);
}
Properties.Settings.Default.NeedsUpgrade = false;
}
}
catch
{ }
if (Environment.Version.Major < 2)
{
MessageBox.Show("You must have .NET Framework 2.0 or higher to use Process Hacker.", "Process Hacker",
MessageBoxButtons.OK, MessageBoxIcon.Error);
Application.Exit();
}
if (IntPtr.Size == 8)
{
MessageBox.Show("Process Hacker cannot run on 64-bit versions of Windows.", "Process Hacker",
MessageBoxButtons.OK, MessageBoxIcon.Error);
Application.Exit();
}
//Asm.LockedBus = 1;
//Asm.Lowercase = true;
//Asm.ExtraSpace = true;
try
{
KPH = new KProcessHacker("KProcessHacker");
}
catch
{ }
Win32.CreateMutex(0, false, "Global\\ProcessHackerMutex");
if (Environment.OSVersion.Version.Major <= 5)
WindowsVersion = WindowsVersion.XP;
else if (Environment.OSVersion.Version.Major >= 6)
WindowsVersion = WindowsVersion.Vista;
if (WindowsVersion == WindowsVersion.Vista)
{
MinProcessQueryRights = Win32.PROCESS_RIGHTS.PROCESS_QUERY_LIMITED_INFORMATION;
MinThreadQueryRights = Win32.THREAD_RIGHTS.THREAD_QUERY_LIMITED_INFORMATION;
}
if (KPH != null && WindowsVersion == WindowsVersion.Vista)
{
MinProcessReadMemoryRights = MinProcessQueryRights;
MinProcessWriteMemoryRights = MinProcessQueryRights;
}
try
{
using (var thandle = new Win32.ProcessHandle(System.Diagnostics.Process.GetCurrentProcess().Id,
MinProcessQueryRights).GetToken())
{
try { thandle.SetPrivilege("SeDebugPrivilege", Win32.SE_PRIVILEGE_ATTRIBUTES.SE_PRIVILEGE_ENABLED); }
catch { }
try { thandle.SetPrivilege("SeShutdownPrivilege", Win32.SE_PRIVILEGE_ATTRIBUTES.SE_PRIVILEGE_ENABLED); }
catch { }
if (Program.WindowsVersion == WindowsVersion.Vista)
{
try { ElevationType = thandle.GetElevationType(); }
catch { ElevationType = Win32.TOKEN_ELEVATION_TYPE.TokenElevationTypeFull; }
if (ElevationType == Win32.TOKEN_ELEVATION_TYPE.TokenElevationTypeDefault &&
!(new WindowsPrincipal(WindowsIdentity.GetCurrent())).
IsInRole(WindowsBuiltInRole.Administrator))
ElevationType = Win32.TOKEN_ELEVATION_TYPE.TokenElevationTypeLimited;
else if (ElevationType == Win32.TOKEN_ELEVATION_TYPE.TokenElevationTypeDefault)
ElevationType = Win32.TOKEN_ELEVATION_TYPE.TokenElevationTypeFull;
}
else
{
ElevationType = Win32.TOKEN_ELEVATION_TYPE.TokenElevationTypeFull;
}
}
}
catch
{ }
CurrentUsername = System.Security.Principal.WindowsIdentity.GetCurrent().Name;
CurrentProcess = Win32.OpenProcess(
Win32.PROCESS_RIGHTS.PROCESS_ALL_ACCESS, 0,
System.Diagnostics.Process.GetCurrentProcess().Id);
if (CurrentProcess == 0)
CurrentProcess =
System.Diagnostics.Process.GetCurrentProcess().Handle.ToInt32();
try
{
CurrentSessionId = Win32.GetProcessSessionId(System.Diagnostics.Process.GetCurrentProcess().Id);
System.Threading.Thread.CurrentThread.Priority = ThreadPriority.Highest;
}
catch
{ }
{
if (pArgs.ContainsKey("-m"))
StartHidden = true;
if (pArgs.ContainsKey("-v"))
StartVisible = true;
if (pArgs.ContainsKey("-o"))
ShowOptions = true;
if (pArgs.ContainsKey("-a"))
{
Aggressive = true;
try
{
Unhook();
}
catch
{ }
}
if (pArgs.ContainsKey(""))
if (pArgs[""].Replace("\"", "").Trim().ToLower().EndsWith("taskmgr.exe"))
StartVisible = true;
if (pArgs.ContainsKey("-t"))
{
if (pArgs["-t"] == "0")
SelectTab = "Processes";
else if (pArgs["-t"] == "1")
SelectTab = "Services";
}
if (pArgs.ContainsKey("-e"))
{
try
{
ExtendedCmd.Run(pArgs);
}
catch (Exception ex)
{
MessageBox.Show(ex.Message, "Process Hacker", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
return;
}
}
#if DEBUG
#else
Application.ThreadException += new ThreadExceptionEventHandler(Application_ThreadException);
AppDomain.CurrentDomain.UnhandledException += new UnhandledExceptionEventHandler(CurrentDomain_UnhandledException);
Application.SetUnhandledExceptionMode(UnhandledExceptionMode.CatchException);
#endif
new HackerWindow();
Application.Run();
}
public static void Unhook()
{
PE.PEFile file = new ProcessHacker.PE.PEFile(Environment.SystemDirectory + "\\ntdll.dll");
System.IO.BinaryReader br = new System.IO.BinaryReader(
new System.IO.FileStream(Environment.SystemDirectory + "\\ntdll.dll", System.IO.FileMode.Open, System.IO.FileAccess.Read));
int ntdll = Win32.GetModuleHandle("ntdll.dll");
int old;
Win32.VirtualProtectEx(Win32.GetCurrentProcess(), ntdll, (int)file.COFFOptionalHeader.SizeOfCode, (int)Win32.MEMORY_PROTECTION.PAGE_EXECUTE_READWRITE, out old);
for (int i = 0; i < file.ExportData.ExportOrdinalTable.Count; i++)
{
ushort ordinal = file.ExportData.ExportOrdinalTable[i];
if (ordinal >= file.ExportData.ExportAddressTable.Count)
continue;
uint address = file.ExportData.ExportAddressTable[ordinal].ExportRVA;
int fileAddress = (int)file.RvaToVa(address);
string name = file.ExportData.ExportNameTable[i];
if (!name.StartsWith("Nt") || name.StartsWith("Ntdll"))
continue;
byte[] fileData = new byte[5];
br.BaseStream.Seek(fileAddress, System.IO.SeekOrigin.Begin);
for (int j = 0; j < 5; j++)
{
System.Runtime.InteropServices.Marshal.WriteByte(new IntPtr(ntdll + address + j), br.ReadByte());
}
}
br.Close();
Win32.VirtualProtectEx(Win32.GetCurrentProcess(), ntdll, (int)file.Sections[0].VirtualSize, old, out old);
}
private static void CheckForPreviousInstance()
{
bool found = false;
Win32.EnumWindows((hWnd, param) =>
{
System.Text.StringBuilder sb = new System.Text.StringBuilder(0x100);
int length = Win32.InternalGetWindowText(hWnd, sb, sb.Capacity);
if (sb.ToString().Contains("Process Hacker ["))
{
int result;
Win32.SendMessageTimeout(hWnd, (Win32.WindowMessage)0x9991, 0, 0,
Win32.SmtoFlags.Block, 5000, out result);
if (result == 0x1119)
{
Win32.SetForegroundWindow(hWnd);
found = true;
return false;
}
}
return true;
}, 0);
if (found)
Environment.Exit(0);
}
public static void StartProcessHackerAdmin()
{
StartProcessHackerAdmin("", null, IntPtr.Zero);
}
public static void StartProcessHackerAdmin(string args, MethodInvoker successAction)
{
StartProcessHackerAdmin(args, successAction, IntPtr.Zero);
}
public static void StartProcessHackerAdmin(string args, MethodInvoker successAction, IntPtr hWnd)
{
StartProgramAdmin(Win32.ProcessHandle.FromHandle(Program.CurrentProcess).GetMainModule().FileName,
args, successAction, Win32.ShowWindowType.Show, hWnd);
}
public static Win32.WaitResult StartProcessHackerAdminWait(string args, IntPtr hWnd, uint timeout)
{
Win32.SHELLEXECUTEINFO info = new Win32.SHELLEXECUTEINFO();
info.cbSize = System.Runtime.InteropServices.Marshal.SizeOf(typeof(Win32.SHELLEXECUTEINFO));
info.lpFile = Win32.ProcessHandle.FromHandle(Program.CurrentProcess).GetMainModule().FileName;
info.nShow = Win32.ShowWindowType.Show;
info.fMask = 0x40; // SEE_MASK_NOCLOSEPROCESS
info.lpVerb = "runas";
info.lpParameters = args;
info.hWnd = hWnd;
if (Win32.ShellExecuteEx(ref info))
{
var result = Win32.WaitForSingleObject(info.hProcess, timeout);
Win32.CloseHandle(info.hProcess);
return result;
}
else
{
// An error occured - the user probably canceled the elevation dialog.
return Win32.WaitResult.Abandoned;
}
}
public static void StartProgramAdmin(string program, string args,
MethodInvoker successAction, Win32.ShowWindowType showType, IntPtr hWnd)
{
Win32.SHELLEXECUTEINFO info = new Win32.SHELLEXECUTEINFO();
info.cbSize = System.Runtime.InteropServices.Marshal.SizeOf(typeof(Win32.SHELLEXECUTEINFO));
info.lpFile = program;
info.nShow = showType;
info.lpVerb = "runas";
info.lpParameters = args;
info.hWnd = hWnd;
if (Win32.ShellExecuteEx(ref info))
{
if (successAction != null)
successAction();
}
}
private static Dictionary ParseArgs(string[] args)
{
Dictionary dict = new Dictionary();
string argPending = null;
foreach (string s in args)
{
if (s.StartsWith("-"))
{
if (dict.ContainsKey(s))
throw new Exception("Option already specified.");
dict.Add(s, "");
argPending = s;
}
else
{
if (argPending != null)
{
dict[argPending] = s;
argPending = null;
}
else
{
if (dict.ContainsKey(""))
throw new Exception("Input file already specified.");
dict.Add("", s);
}
}
}
return dict;
}
public static void ApplyFont(Font font)
{
HackerWindow.BeginInvoke(new MethodInvoker(() => { HackerWindow.ApplyFont(font); }));
foreach (var processWindow in PWindows.Values)
{
processWindow.BeginInvoke(new MethodInvoker(() => { processWindow.ApplyFont(font); }));
}
}
public static void CollectGarbage()
{
/* Garbage collections */
GC.Collect();
GC.WaitForPendingFinalizers();
GC.Collect();
GC.WaitForPendingFinalizers();
GC.Collect();
/* Compact the native heaps */
int[] heaps = new int[128];
int count = Win32.GetProcessHeaps(heaps.Length, heaps);
if (count <= heaps.Length)
{
for (int i = 0; i < count; i++)
Win32.HeapCompact(heaps[i], false);
}
/* Terminate any unused threadpool threads */
CollectWorkerThreads();
}
public static void CollectWorkerThreads()
{
lock (CollectWorkerThreadsLock)
{
int workerThreads, completionPortThreads, maxWorkerThreads, maxCompletionPortThreads;
ThreadPool.GetMaxThreads(out maxWorkerThreads, out maxCompletionPortThreads);
ThreadPool.GetAvailableThreads(out workerThreads, out completionPortThreads);
workerThreads = maxWorkerThreads - workerThreads;
completionPortThreads = maxCompletionPortThreads - completionPortThreads;
ThreadPool.SetMaxThreads(0, 0);
ThreadPool.SetMaxThreads(workerThreads, completionPortThreads);
}
}
private static void CurrentDomain_UnhandledException(object sender, UnhandledExceptionEventArgs e)
{
UnhandledException(e.ExceptionObject as Exception);
}
private static void Application_ThreadException(object sender, ThreadExceptionEventArgs e)
{
UnhandledException(e.Exception);
}
private static void UnhandledException(Exception ex)
{
if (false)
{
TaskDialog td = new TaskDialog();
td.WindowTitle = "Process Hacker";
td.MainInstruction = "Process Hacker has encountered a problem";
td.Content = "An unhandled exception has occurred in Process Hacker.";
td.Buttons = new TaskDialogButton[]
{
new TaskDialogButton((int)DialogResult.Yes, "Continue\nIgnore the error and continue. This may cause Process Hacker to crash."),
new TaskDialogButton((int)DialogResult.No, "Close\nClose Process Hacker.")
};
td.UseCommandLinks = true;
try
{
if (Program.HackerWindow != null)
{
td.CustomMainIcon = ProcessHacker.Properties.Resources.Process;
}
}
catch
{ }
td.ExpandedInformation = "Please report this problem to " +
"http://sourceforge.net/projects/processhacker\r\n\r\n" + ex.ToString();
td.EnableHyperlinks = true;
td.ExpandFooterArea = true;
td.CollapsedControlText = "Show problem details";
td.ExpandedControlText = "Hide problem details";
td.Callback = (taskDialog, args, callbackData) =>
{
if (args.Notification == TaskDialogNotification.HyperlinkClicked)
{
if (args.Hyperlink == "report")
{
try
{
System.Diagnostics.Process.Start("http://sourceforge.net/tracker2/?group_id=242527");
}
catch
{ }
}
return true;
}
return false;
};
DialogResult result = (DialogResult)td.Show();
if (result == DialogResult.No)
{
try
{
Properties.Settings.Default.Save();
}
catch
{ }
Win32.ExitProcess(0);
}
}
else
{
ErrorDialog ed = new ErrorDialog(ex);
ed.ShowDialog();
}
}
///
/// Creates an instance of the memory editor form.
///
/// The PID of the process to edit
/// The address to start editing at
/// The length to edit
public static MemoryEditor GetMemoryEditor(int PID, int address, int length)
{
return GetMemoryEditor(PID, address, length, new MemoryEditorInvokeAction(delegate {}));
}
///
/// Creates an instance of the memory editor form and invokes an action on the memory editor's thread.
///
/// The PID of the process to edit
/// The address to start editing at
/// The length to edit
/// The action to be invoked on the memory editor's thread
/// Memory editor form
public static MemoryEditor GetMemoryEditor(int PID, int address, int length, MemoryEditorInvokeAction action)
{
MemoryEditor ed = null;
string id = PID.ToString() + "-" + address.ToString() + "-" + length.ToString();
if (MemoryEditors.ContainsKey(id))
{
ed = MemoryEditors[id];
ed.Invoke(action, ed);
return ed;
}
if (MemoryEditorsThreaded)
{
Thread t = new Thread(new ThreadStart(delegate
{
ed = new MemoryEditor(PID, address, length);
action(ed);
try
{
Application.Run(ed);
}
catch
{ }
Program.MemoryEditorsThreads.Remove(id);
}));
t.SetApartmentState(ApartmentState.STA);
t.Start();
Program.MemoryEditorsThreads.Add(id, t);
}
else
{
ed = new MemoryEditor(PID, address, length);
action(ed);
ed.Show();
}
return ed;
}
///
/// Creates an instance of the results window on a separate thread.
///
public static ResultsWindow GetResultsWindow(int PID)
{
return GetResultsWindow(PID, new ResultsWindowInvokeAction(delegate { }));
}
///
/// Creates an instance of the results window on a separate thread and invokes an action on that thread.
///
/// The action to be performed.
public static ResultsWindow GetResultsWindow(int PID, ResultsWindowInvokeAction action)
{
ResultsWindow rw = null;
string id = "";
if (ResultsWindowsThreaded)
{
Thread t = new Thread(new ThreadStart(delegate
{
rw = new ResultsWindow(PID);
id = rw.Id;
action(rw);
try
{
Application.Run(rw);
}
catch
{ }
Program.ResultsThreads.Remove(id);
}));
t.SetApartmentState(ApartmentState.STA);
t.Start();
while (id == "") Thread.Sleep(1);
Program.ResultsThreads.Add(id, t);
}
else
{
rw = new ResultsWindow(PID);
action(rw);
rw.Show();
}
return rw;
}
///
/// Creates an instance of the PE window on a separate thread.
///
public static PEWindow GetPEWindow(string path)
{
return GetPEWindow(path, new PEWindowInvokeAction(delegate { }));
}
///
/// Creates an instance of the thread window on a separate thread and invokes an action on that thread.
///
/// The action to be performed.
public static PEWindow GetPEWindow(string path, PEWindowInvokeAction action)
{
PEWindow pw = null;
if (PEWindows.ContainsKey(path))
{
pw = PEWindows[path];
pw.Invoke(action, pw);
return pw;
}
if (PEWindowsThreaded)
{
Thread t = new Thread(new ThreadStart(delegate
{
pw = new PEWindow(path);
action(pw);
try
{
Application.Run(pw);
}
catch
{ }
Program.PEThreads.Remove(path);
}));
t.SetApartmentState(ApartmentState.STA);
t.Start();
Program.PEThreads.Add(path, t);
}
else
{
pw = new PEWindow(path);
action(pw);
pw.Show();
}
return pw;
}
///
/// Creates an instance of the process window on a separate thread.
///
public static ProcessWindow GetProcessWindow(ProcessItem process)
{
return GetProcessWindow(process, new PWindowInvokeAction(delegate { }));
}
///
/// Creates an instance of the process window on a separate thread and invokes an action on that thread.
///
/// The action to be performed.
public static ProcessWindow GetProcessWindow(ProcessItem process, PWindowInvokeAction action)
{
ProcessWindow pw = null;
if (PWindows.ContainsKey(process.PID))
{
pw = PWindows[process.PID];
pw.Invoke(action, pw);
return pw;
}
if (PWindowsThreaded)
{
Thread t = new Thread(new ThreadStart(delegate
{
pw = new ProcessWindow(process);
action(pw);
try
{
Application.Run(pw);
}
catch
{ }
Program.PThreads.Remove(process.PID);
}));
t.SetApartmentState(ApartmentState.STA);
t.Start();
Program.PThreads.Add(process.PID, t);
}
else
{
pw = new ProcessWindow(process);
action(pw);
pw.Show();
}
return pw;
}
public static void FocusWindow(Form f)
{
if (f.InvokeRequired)
{
f.BeginInvoke(new MethodInvoker(delegate { Program.FocusWindow(f); }));
return;
}
f.Visible = true; // just in case it's hidden right now
if (f.WindowState == FormWindowState.Minimized)
f.WindowState = FormWindowState.Normal;
f.Activate();
}
public static void UpdateWindow(Form f)
{
if (f.InvokeRequired)
{
f.BeginInvoke(new UpdateWindowAction(UpdateWindow), f);
return;
}
MenuItem windowMenuItem = (MenuItem)f.GetType().GetProperty("WindowMenuItem").GetValue(f, null);
wyDay.Controls.VistaMenu vistaMenu =
(wyDay.Controls.VistaMenu)f.GetType().GetProperty("VistaMenu").GetValue(f, null);
MenuItem item;
lock (windowMenuItem)
{
WeakReference